diff --git a/PDP11/pdp11_tq.c b/PDP11/pdp11_tq.c index 61397f2f..d33a2dbc 100644 --- a/PDP11/pdp11_tq.c +++ b/PDP11/pdp11_tq.c @@ -1209,8 +1209,9 @@ if ((uptr = tq_getucb (lu))) { /* unit exist? */ if (sts == ST_SUC) { /* ok? */ uptr->cpkt = pkt; /* op in progress */ if ((tq_pkt[pkt].d[CMD_MOD] & MD_RWD) && /* rewind? */ - (!(tq_pkt[pkt].d[CMD_MOD] & MD_IMM))) /* !immediate? */ - sim_activate_after (uptr, 2000000); /* use 2 sec rewind execute time */ + (!(tq_pkt[pkt].d[CMD_MOD] & MD_IMM))) { /* !immediate? */ + sim_activate_after (uptr, tq_rwtime); /* use 2 sec rewind execute time */ + } else { /* otherwise */ uptr->iostarttime = sim_grtime(); sim_activate (uptr, 0); /* use normal execute time */ diff --git a/README.md b/README.md index 9f735f9a..af1a664f 100644 --- a/README.md +++ b/README.md @@ -229,6 +229,7 @@ Host platforms which have libSDL available can leverage this functionality. #### Tape Extensions AWS format tape support TAR format tape support + ANSIFILES format tape support #### Embedded ROM support Simulators which have boot commands which load constant files as part of diff --git a/doc/simh_doc.doc b/doc/simh_doc.doc index a89cc2ab..7f636454 100644 Binary files a/doc/simh_doc.doc and b/doc/simh_doc.doc differ diff --git a/doc/simh_faq.doc b/doc/simh_faq.doc index cb505e5b..60478201 100644 Binary files a/doc/simh_faq.doc and b/doc/simh_faq.doc differ diff --git a/sim_tape.c b/sim_tape.c index e720c7ac..2c1cdcfa 100644 --- a/sim_tape.c +++ b/sim_tape.c @@ -108,13 +108,14 @@ struct sim_tape_fmt { }; static struct sim_tape_fmt fmts[MTUF_N_FMT] = { - { "SIMH", 0, sizeof (t_mtrlnt) - 1, sizeof (t_mtrlnt)}, - { "E11", 0, sizeof (t_mtrlnt) - 1, sizeof (t_mtrlnt) }, + { "SIMH", 0, sizeof (t_mtrlnt) - 1, sizeof (t_mtrlnt) }, + { "E11", 0, sizeof (t_mtrlnt) - 1, sizeof (t_mtrlnt) }, { "TPC", UNIT_RO, sizeof (t_tpclnt) - 1, sizeof (t_tpclnt) }, - { "P7B", 0, 0, 0 }, - { "AWS", 0, 0, 0 }, - { "TAR", 0, 0, 0 }, - { NULL, 0, 0 } + { "P7B", 0, 0, 0 }, + { "AWS", 0, 0, 0 }, + { "TAR", 0, 0, 0 }, + { "ANSIFILES", 0, 0, 0 }, + { NULL, 0, 0, 0 } }; static const uint32 bpi [] = { /* tape density table, indexed by MT_DENS constants */ @@ -375,6 +376,89 @@ return FALSE; (_callback) (uptr, r); #endif +typedef struct VOL1 { + char type[3]; /* VOL */ + char num; /* 1 */ + char ident[6]; /* characters blank padded > */ + char accessibity; /* blank */ + char reserved1[13]; /* */ + char implement[13]; /* */ + char owner[14]; /* */ + char reserved2[28]; /* */ + char standard; /* 1,3 or 4 */ + } VOL1; + +typedef struct HDR1 { /* Also EOF1, EOV1 */ + char type[3]; /* HDR|EOF|EOV */ + char num; /* 1 */ + char file_ident[17]; /* filename */ + char file_set[6]; /* label ident */ + char file_section[4]; /* 0001 */ + char file_sequence[4]; /* 0001 */ + char generation_number[4]; /* 0001 */ + char version_number[2]; /* 00 */ + char creation_date[6]; /* cyyddd */ + char expiration_date[6]; + char accessibility; /* space */ + char block_count[6]; /* 000000 */ + char system_code[13]; /* */ + char reserved[7]; /* blank */ + } HDR1; + +typedef struct HDR2 { /* Also EOF2, EOV2 */ + char type[3]; /* HDR */ + char num; /* 2 */ + char record_format; /* F(fixed)|D(variable)|S(spanned) */ + char block_length[5]; /* label ident */ + char record_length[5]; /* */ + char reserved_os[35]; /* */ + char buffer_offset[2]; /* */ + char reserved_std[28]; /* */ + } HDR2; + +typedef struct HDR3 { /* Also EOF3, EOV3 */ + char type[3]; /* HDR */ + char num; /* 2 */ + char record_format; /* F(fixed)|D(variable)|S(spanned) */ + char block_length[5]; /* label ident */ + char record_length[5]; /* */ + char reserved_os[35]; /* */ + char buffer_offset[2]; /* */ + char reserved_std[28]; /* */ + } HDR3; + +typedef struct HDR4 { /* Also EOF4, EOV4 */ + char type[3]; /* HDR */ + char num; /* 4 */ + char blank; /* blank */ + char extra_name[62]; /* */ + char extra_name_used[2]; /* 99 */ + char unused[11]; + } HDR4; + +typedef struct TAPE_RECORD { + uint32 size; + uint8 data[1]; + } TAPE_RECORD; + +typedef struct ANSI_TAPE { + uint32 file_count; + uint32 record_count; + uint32 array_size; + uint32 block_size; + TAPE_RECORD **records; + VOL1 vol1; + } ANSI_TAPE; + +static ANSI_TAPE *ansi_create_tape (const char *label, uint32 block_size); +static int ansi_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); + /* Enable asynchronous operation */ @@ -448,7 +532,8 @@ sim_tape_clr_async (uptr); if (sim_asynch_enabled) sim_tape_set_async (uptr, ctx->asynch_io_latency); #endif -fflush (uptr->fileref); +if (MT_GET_FMT (uptr) != MTUF_F_ANSI) + fflush (uptr->fileref); } /* Attach tape unit */ @@ -502,9 +587,36 @@ if (MT_GET_FMT (uptr) == MTUF_F_TAR) { uptr->recsize = TAR_DFLT_RECSIZE; } if ((MT_GET_FMT (uptr) == MTUF_F_TPC) || - (MT_GET_FMT (uptr) == MTUF_F_TAR)) - sim_switches |= SWMASK ('R'); /* Force ReadOnly attach for TPC and TAR tapes */ -r = attach_unit (uptr, (CONST char *)cptr); /* attach unit */ + (MT_GET_FMT (uptr) == MTUF_F_TAR) || + (MT_GET_FMT (uptr) == MTUF_F_ANSI)) + sim_switches |= SWMASK ('R'); /* Force ReadOnly attach for TPC, TAR and ANSIFILES tapes */ +if (MT_GET_FMT (uptr) == MTUF_F_ANSI) { + const char *ocptr = cptr; + char label[CBUFSIZE] = "simh"; + + uptr->fileref = (FILE *)ansi_create_tape (label, 2048); + 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); + 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; + } + } +else + r = attach_unit (uptr, (CONST char *)cptr); /* attach unit */ if (r != SCPE_OK) /* error? */ return sim_messagef (r, "Can't open tape image: %s\n", cptr); @@ -591,7 +703,13 @@ if (ctx) sim_tape_clr_async (uptr); MT_CLR_INMRK (uptr); /* Not within an AWS or TAR tapemark */ -r = detach_unit (uptr); /* detach unit */ +if (MT_GET_FMT (uptr) == MTUF_F_ANSI) { + r = ansi_free_tape ((void *)uptr->fileref); + uptr->fileref = NULL; + uptr->flags &= ~UNIT_ATT; + } +else + r = detach_unit (uptr); /* detach unit */ if (r != SCPE_OK) return r; switch (f) { /* case on format */ @@ -639,7 +757,7 @@ 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 and TAR)\n"); +fprintf (st, " is SIMH, alternatives are E11, TPC, P7B, AWS, TAR and ANSIFILES)\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"); @@ -650,7 +768,13 @@ fprintf (st, " -L Display detailed record size counts observed durin 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"); +fprintf (st, " while the tape image is scanned.\n\n"); +fprintf (st, "Notes: ANSIFILES format allows one or several files to be presented to as a\n"); +fprintf (st, " read only ANSI Level 3 labeled tape with file labels that make each\n"); +fprintf (st, " individual file accessible directly as files on the tape.\n\n"); +fprintf (st, "Examples:\n\n"); +fprintf (st, " sim> ATTACH -F %s ANSIFILES Hobbyist-USE-ONLY-VA.TXT\n\n", dptr->name); +fprintf (st, " sim> ATTACH -F %s ANSIFILES *.TXT,*.ini,*.exe\n", dptr->name); return SCPE_OK; } @@ -664,6 +788,20 @@ if (sim_deb && ((uptr->dctrl | ctx->dptr->dctrl) & reason)) sim_data_trace(ctx->dptr, uptr, (detail ? data : NULL), "", len, txt, reason); } +static int sim_tape_seek (UNIT *uptr, t_addr pos) +{ +if (MT_GET_FMT (uptr) != MTUF_F_ANSI) + return sim_fseek (uptr->fileref, pos, SEEK_SET); +return 0; +} + +static t_offset sim_tape_size (UNIT *uptr) +{ +if (MT_GET_FMT (uptr) != MTUF_F_ANSI) + return sim_fsize_ex (uptr->fileref); +return uptr->tape_eom; +} + /* Read record length forward (internal routine). Inputs: @@ -782,7 +920,7 @@ if ((uptr->tape_eom) && return MTSE_EOM; /* and quit with I/O error status */ } -if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) { /* set the initial tape position; if it fails */ +if (sim_tape_seek (uptr, uptr->pos)) { /* set the initial tape position; if it fails */ MT_SET_PNU (uptr); /* then set position not updated */ return sim_tape_ioerr (uptr); /* and quit with I/O error status */ } @@ -878,8 +1016,8 @@ switch (f) { /* otherwise the read method else if (*bc == MTR_FHGAP) { /* otherwise if the value if a half gap */ uptr->pos = uptr->pos - sizeof (t_mtrlnt) / 2; /* then back up and resync */ - if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) { /* set the tape position; if it fails */ - status = sim_tape_ioerr (uptr); /* then quit with I/O error status */ + if (sim_tape_seek (uptr, uptr->pos)) { /* set the tape position; if it fails */ + status = sim_tape_ioerr (uptr); /* then quit with I/O error status */ break; } @@ -904,7 +1042,7 @@ switch (f) { /* otherwise the read method if (status == MTSE_OK) { /* Validate the reverse record size for data records */ t_mtrlnt rev_lnt; - if (sim_fseek (uptr->fileref, uptr->pos - sizeof (t_mtrlnt), SEEK_SET)){ /* then seek to the end of record size; if it fails */ + if (sim_tape_seek (uptr, uptr->pos - sizeof (t_mtrlnt))) { /* then seek to the end of record size; if it fails */ status = sim_tape_ioerr (uptr); /* then quit with I/O error status */ break; } @@ -924,7 +1062,7 @@ switch (f) { /* otherwise the read method MT_SET_PNU (uptr); /* pos not upd */ break; } - if (sim_fseek (uptr->fileref, saved_pos, SEEK_SET)) /* then seek back to the beginning of the data; if it fails */ + if (sim_tape_seek (uptr, saved_pos)) /* then seek back to the beginning of the data; if it fails */ status = sim_tape_ioerr (uptr); /* then quit with I/O error status */ } break; /* otherwise the operation succeeded */ @@ -976,7 +1114,7 @@ switch (f) { /* otherwise the read method if (status == MTSE_OK) { *bc = sbc; /* save rec lnt */ - (void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */ + (void)sim_tape_seek (uptr, uptr->pos); /* for read */ uptr->pos = uptr->pos + sbc; /* spc over record */ if (all_eof) { /* tape mark? */ status = MTSE_TMK; @@ -1018,7 +1156,7 @@ switch (f) { /* otherwise the read method uptr->pos += awshdr.nxtlen; /* spc over record */ memset (&awshdr, 0, sizeof (t_awslnt)); saved_pos = (t_addr)sim_ftell (uptr->fileref); - (void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */ + (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 */ @@ -1028,7 +1166,7 @@ switch (f) { /* otherwise the read method MT_CLR_INMRK (uptr); /* not within an AWS tapemark */ } else - (void)sim_fseek (uptr->fileref, saved_pos, SEEK_SET); /* Move back to the data */ + (void)sim_tape_seek (uptr, saved_pos); /* Move back to the data */ } break; @@ -1038,7 +1176,7 @@ switch (f) { /* otherwise the read method *bc = (t_mtrlnt)uptr->recsize; /* TAR record size */ else *bc = (t_mtrlnt)(uptr->hwmark - uptr->pos); /* TAR remnant last record */ - (void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); + (void)sim_tape_seek (uptr, uptr->pos); uptr->pos += *bc; MT_CLR_INMRK (uptr); } @@ -1052,6 +1190,22 @@ switch (f) { /* otherwise the read method } break; + case MTUF_F_ANSI: + if (1) { + ANSI_TAPE *tape = (ANSI_TAPE *)uptr->fileref; + + if (uptr->pos >= tape->record_count) + status = MTSE_EOM; + else { + if (tape->records[uptr->pos]->size == 0) + status = MTSE_TMK; + else + *bc = tape->records[uptr->pos]->size; + ++uptr->pos; + } + } + break; + default: status = MTSE_FMT; } @@ -1171,10 +1325,10 @@ else switch (f) { /* otherwise the read me bufcap = sizeof (buffer) /* to the full size of the buffer */ / sizeof (buffer [0]); - if (sim_fseek (uptr->fileref, /* seek back to the location */ - uptr->pos - bufcap * sizeof (t_mtrlnt), /* corresponding to the start */ - SEEK_SET)) { /* of the buffer; if it fails */ - status = sim_tape_ioerr (uptr); /* and fail with I/O error status */ + 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 */ break; } @@ -1213,9 +1367,8 @@ else switch (f) { /* otherwise the read me uptr->pos = uptr->pos - sizeof (t_mtrlnt) /* position to the start */ - (f == MTUF_F_STD ? (sbc + 1) & ~1 : sbc); /* of the record */ - if (sim_fseek (uptr->fileref, /* seek to the start of the data area; if it fails */ - uptr->pos + sizeof (t_mtrlnt), /* then return with I/O error status */ - SEEK_SET)) { + 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; } @@ -1230,7 +1383,7 @@ else switch (f) { /* otherwise the read me case MTUF_F_TPC: ppos = sim_tape_tpc_fnd (uptr, (t_addr *) uptr->filebuf); /* find prev rec */ - (void)sim_fseek (uptr->fileref, ppos, SEEK_SET);/* position */ + (void)sim_tape_seek (uptr, ppos); /* position */ (void)sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref); *bc = (t_mtrlnt)tpcbc; /* save rec lnt */ @@ -1243,7 +1396,7 @@ else switch (f) { /* otherwise the read me if (*bc == MTR_TMK) /* tape mark? */ status = MTSE_TMK; else - (void)sim_fseek (uptr->fileref, uptr->pos + sizeof (t_tpclnt), SEEK_SET); + (void)sim_tape_seek (uptr, uptr->pos + sizeof (t_tpclnt)); } break; @@ -1265,7 +1418,7 @@ else switch (f) { /* otherwise the read me buf_offset = uptr->pos - (sbc - 1 + BUF_SZ); read_size = BUF_SZ; } - (void)sim_fseek (uptr->fileref, buf_offset, SEEK_SET); + (void)sim_tape_seek (uptr, buf_offset); bytes_in_buf = sim_fread (buf, sizeof (uint8), read_size, uptr->fileref); if (ferror (uptr->fileref)) { /* error? */ status = sim_tape_ioerr (uptr); @@ -1286,7 +1439,7 @@ 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_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for next read */ + (void)sim_tape_seek (uptr, uptr->pos); /* for next read */ if (all_eof) /* tape mark? */ status = MTSE_TMK; } @@ -1300,7 +1453,7 @@ else switch (f) { /* otherwise the read me status = MTSE_BOT; /* then we're done */ break; } - (void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET);/* position */ + (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? */ @@ -1336,9 +1489,8 @@ else switch (f) { /* otherwise the read me uptr->pos -= sizeof (t_awshdr); /* position to the start of the record */ uptr->pos -= awshdr.prelen; /* Including the data length */ - if (sim_fseek (uptr->fileref, /* seek to the start of the data area; if it fails */ - uptr->pos + sizeof (t_awshdr), - SEEK_SET)) { + 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; } @@ -1368,10 +1520,22 @@ else switch (f) { /* otherwise the read me *bc = (t_mtrlnt)uptr->recsize; if (*bc) { uptr->pos -= *bc; - (void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); + (void)sim_tape_seek (uptr, uptr->pos); } break; + case MTUF_F_ANSI: + if (1) { + ANSI_TAPE *tape = (ANSI_TAPE *)uptr->fileref; + + --uptr->pos; + if (tape->records[uptr->pos]->size == 0) + status = MTSE_TMK; + else + *bc = tape->records[uptr->pos]->size; + } + break; + default: status = MTSE_FMT; } @@ -1439,11 +1603,19 @@ if (rbc > max) { /* rec out of range? */ uptr->pos = opos; return MTSE_INVRL; } -i = (t_mtrlnt) sim_fread (buf, sizeof (uint8), rbc, uptr->fileref); /* read record */ -if (ferror (uptr->fileref)) { /* error? */ - MT_SET_PNU (uptr); - uptr->pos = opos; - return sim_tape_ioerr (uptr); +if (f != MTUF_F_ANSI) { + i = (t_mtrlnt) sim_fread (buf, sizeof (uint8), rbc, uptr->fileref); /* read record */ + if (ferror (uptr->fileref)) { /* error? */ + MT_SET_PNU (uptr); + uptr->pos = opos; + return sim_tape_ioerr (uptr); + } + } +else { + ANSI_TAPE *tape = (ANSI_TAPE *)uptr->fileref; + + memcpy (buf, tape->records[uptr->pos - 1]->data, rbc); + i = rbc; } for ( ; i < rbc; i++) /* fill with 0's */ buf[i] = 0; @@ -1504,9 +1676,17 @@ if (st != MTSE_OK) { *bc = rbc = MTR_L (tbc); /* strip error flag */ if (rbc > max) /* rec out of range? */ return MTSE_INVRL; -i = (t_mtrlnt) sim_fread (buf, sizeof (uint8), rbc, uptr->fileref); /* read record */ -if (ferror (uptr->fileref)) /* error? */ - return sim_tape_ioerr (uptr); +if (f != MTUF_F_ANSI) { + i = (t_mtrlnt) sim_fread (buf, sizeof (uint8), rbc, uptr->fileref); /* read record */ + if (ferror (uptr->fileref)) /* error? */ + return sim_tape_ioerr (uptr); + } +else { + ANSI_TAPE *tape = (ANSI_TAPE *)uptr->fileref; + + memcpy (buf, tape->records[uptr->pos]->data, rbc); + i = rbc; + } for ( ; i < rbc; i++) /* fill with 0's */ buf[i] = 0; if (f == MTUF_F_P7B) /* p7b? strip SOR */ @@ -1561,7 +1741,7 @@ if (sim_tape_wrp (uptr)) /* write prot? */ return MTSE_WRP; if (sbc == 0) /* nothing to do? */ return MTSE_OK; -if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) /* set pos */ +if (sim_tape_seek (uptr, uptr->pos)) /* set pos */ return MTSE_IOERR; switch (f) { /* case on format */ @@ -1619,7 +1799,7 @@ size_t rdcnt; t_bool replacing_record; memset (&awshdr, 0, sizeof (t_awshdr)); -if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) /* set pos */ +if (sim_tape_seek (uptr, uptr->pos)) /* set pos */ return MTSE_IOERR; rdcnt = sim_fread (&awshdr, sizeof (t_awslnt), 3, uptr->fileref); if (ferror (uptr->fileref)) { /* error? */ @@ -1632,7 +1812,7 @@ if ((!sim_tape_bot (uptr)) && MT_SET_PNU (uptr); /* pos not upd */ return MTSE_INVRL; } -if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) /* set pos */ +if (sim_tape_seek (uptr, uptr->pos)) /* set pos */ return MTSE_IOERR; replacing_record = (awshdr.nxtlen == (t_awslnt)bc) && (awshdr.rectyp == (bc ? AWS_REC : AWS_TMK)); awshdr.nxtlen = (t_awslnt)bc; @@ -1667,7 +1847,7 @@ if (ctx == NULL) /* if not properly attac return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ if (sim_tape_wrp (uptr)) /* write prot? */ return MTSE_WRP; -(void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set pos */ +(void)sim_tape_seek (uptr, uptr->pos); /* set pos */ (void)sim_fwrite (&dat, sizeof (t_mtrlnt), 1, uptr->fileref); if (ferror (uptr->fileref)) { /* error? */ MT_SET_PNU (uptr); @@ -1889,7 +2069,7 @@ else if (gap_size == 0 || format != MTUF_F_STD) /* otherwise if zero len file_size = sim_fsize (uptr->fileref); /* get the file size */ -if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) { /* position the tape; if it fails */ +if (sim_tape_seek (uptr, uptr->pos)) { /* position the tape; if it fails */ MT_SET_PNU (uptr); /* then set position not updated */ return sim_tape_ioerr (uptr); /* and quit with I/O error status */ } @@ -1943,8 +2123,8 @@ do { else if (meta == MTR_FHGAP) { /* half gap? */ uptr->pos = uptr->pos - meta_size / 2; /* backup to resync */ - if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) /* position the tape; if it fails */ - return sim_tape_ioerr (uptr); /* then quit with I/O error status */ + if (sim_tape_seek (uptr, uptr->pos)) /* position the tape; if it fails */ + return sim_tape_ioerr (uptr); /* then quit with I/O error status */ gap_alloc = gap_alloc + meta_size / 2; /* allocate marker space */ gap_needed = gap_needed - meta_size / 2; /* reduce requirement */ @@ -1969,8 +2149,8 @@ do { if (rec_size < gap_needed + min_rec_size) { /* rec too small? */ uptr->pos = uptr->pos - meta_size + rec_size; /* position past record */ - if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) /* position the tape; if it fails */ - return sim_tape_ioerr (uptr); /* then quit with I/O error status */ + if (sim_tape_seek (uptr, uptr->pos)) /* position the tape; if it fails */ + return sim_tape_ioerr (uptr); /* then quit with I/O error status */ gap_alloc = gap_alloc + rec_size; /* allocate record */ gap_needed = gap_needed - rec_size; /* reduce requirement */ @@ -2095,7 +2275,7 @@ if (gap_size == meta_size) { /* if the request is for else /* otherwise */ uptr->pos -= meta_size; /* back up the file pointer */ - if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) /* position the tape; if it fails */ + if (sim_tape_seek (uptr, uptr->pos)) /* position the tape; if it fails */ return sim_tape_ioerr (uptr); /* then quit with I/O error status */ (void)sim_fread (&metadatum, meta_size, 1, uptr->fileref);/* read a metadatum */ @@ -2104,7 +2284,7 @@ if (gap_size == meta_size) { /* if the request is for return sim_tape_ioerr (uptr); /* then report the error and quit */ else if (metadatum == MTR_TMK) /* otherwise if a tape mark is present */ - if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) /* then reposition the tape; if it fails */ + if (sim_tape_seek (uptr, uptr->pos)) /* then reposition the tape; if it fails */ return sim_tape_ioerr (uptr); /* then quit with I/O error status */ else { /* otherwise */ @@ -2665,8 +2845,9 @@ if (uptr->flags & UNIT_ATT) { sim_debug_unit (ctx->dbit, uptr, "sim_tape_rewind(unit=%d)\n", (int)(uptr-ctx->dptr->units)); } uptr->pos = 0; -if (uptr->flags & UNIT_ATT) - (void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); +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 */ return MTSE_OK; @@ -2851,7 +3032,7 @@ recbuf = (uint8 *)malloc (65536); tape_size = (t_addr)sim_fsize (uptr->fileref); sim_debug_unit (MTSE_DBG_STR, uptr, "tpc_map: tape_size: %" T_ADDR_FMT "u\n", tape_size); for (objc = 0, sizec = 0, tpos = 0;; ) { - (void)sim_fseek (uptr->fileref, tpos, SEEK_SET); + (void)sim_tape_seek (uptr, tpos); i = sim_fread (&bc, sizeof (t_tpclnt), 1, uptr->fileref); if (i == 0) /* past or at eof? */ break; @@ -3015,7 +3196,7 @@ while (r == SCPE_OK) { break; } memset (buf_f, 0, bc_f); - memset (buf_r, 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); break; @@ -3049,10 +3230,10 @@ while (r == SCPE_OK) { uptr->tape_eom = uptr->pos; if ((!stop_cpu) && ((r != MTSE_EOM) || (sim_switches & SWMASK ('V')) || (sim_switches & SWMASK ('L')) || - ((uint32)(sim_fsize_ex (uptr->fileref) - (t_offset)uptr->pos) > fmts[MT_GET_FMT (uptr)].eom_remnant) || + ((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_fsize_ex (uptr->fileref) - (t_offset)uptr->tape_eom); - sim_printf ("Tape Image '%s' scanned as %s format.\n", uptr->filename, fmts[MT_GET_FMT (uptr)].name); + 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, fmts[MT_GET_FMT (uptr)].name); if (r != MTSE_EOM) sim_printf ("After processing "); else @@ -3081,7 +3262,7 @@ free (buf_f); free (buf_r); free (rec_sizes); uptr->pos = saved_pos; -(void)sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); +(void)sim_tape_seek (uptr, uptr->pos); return SCPE_OK; } @@ -3580,3 +3761,342 @@ SIM_TEST(sim_tape_test_remove_tape_files (dptr->units, "TapeTestFile1")); return SCPE_OK; } + +const char HDR3_RMS_STREAM[] = "HDR3020002040000" + "0000000100000000" + "0000000002000000" + "0000000000000000" + "0000 "; +const char HDR3_RMS_STMLF[] = "HDR3020002050000" + "0000000100000000" + "0000000002000000" + "0000000000000000" + "0000 "; +const char HDR3_RMS_VAR[] = "HDR3005C02020000" + "0000000100000000" + "0000000000000000" + "0000000000000000" + "0000 "; +const char HDR3_RMS_FIXED[] = "HDR3020000010000" + "0000000100000000" + "0000000002000000" + "0000000000000000" + "0000 "; + +static void ansi_date (time_t datetime, char date[6]) + { + struct tm *lt; + char buf[20]; + + lt = localtime (&datetime); + sprintf (buf, "%c%02d%03d", (lt->tm_year < 100) ? ' ' : '0' + (lt->tm_year/100 - 1), + lt->tm_year % 100, + lt->tm_yday + 1); + memcpy (date, buf, 6); + } + +static void to_ansi_a (char *out, const char *in, size_t size) + { + memset (out, ' ', size); + while (size--) { + if (isupper (*in) || isdigit (*in)) + *(out++) = *in++; + else { + if (*in == '\0') + break; + if (islower (*in)) { + *(out++) = toupper (*in); + ++in; + } + else { + if (strchr ("-.$_", *in)) + *(out++) = *in++; + else + ++in; + } + } + } + } + +static void ansi_make_VOL1 (VOL1 *vol, const char *ident) + { + memset (vol, ' ', sizeof (*vol)); + memcpy (vol->type, "VOL", 3); + vol->num = '1'; + vol->standard = '3'; + to_ansi_a (vol->ident, ident, sizeof (vol->ident)); + } + +static void ansi_make_HDR1 (HDR1 *hdr1, VOL1 *vol, HDR4 *hdr4, const char *filename) + { + const char *fn; + struct stat statb; + char extra_name_used[3] = "00"; + char *fn_cpy, *c, *ext; + + stat (filename, &statb); + if (!(fn = strrchr (filename, '/')) && !(fn = strrchr (filename, '\\'))) + fn = filename; + else + ++fn; /* skip over slash or backslash */ + fn_cpy = (char *)malloc (strlen (fn) + 1); + strcpy (fn_cpy, fn); + fn = fn_cpy; + ext = strrchr (fn_cpy, '.'); + if (ext) { + while ((c = strchr (fn_cpy, '.')) != ext) + *c = '_'; /* translate extra .'s to _ */ + } + memset (hdr1, ' ', sizeof (*hdr1)); + memcpy (hdr1->type, "HDR", 3); + hdr1->num = '1'; + memset (hdr4, ' ', sizeof (*hdr1)); + memcpy (hdr4->type, "HDR", 3); + hdr4->num = '4'; + to_ansi_a (hdr1->file_ident, fn, sizeof (hdr1->file_ident)); + if (strlen (fn) > 17) { + to_ansi_a (hdr4->extra_name, fn + 17, sizeof (hdr4->extra_name)); + sprintf (extra_name_used, "%02d", (int)(strlen (fn) - 17)); + } + memcpy (hdr4->extra_name_used, extra_name_used, 2); + memcpy (hdr1->file_set, vol->ident, sizeof (hdr1->file_set)); + memcpy (hdr1->file_section, "0001", 4); + memcpy (hdr1->file_sequence, "0001", 4); + memcpy (hdr1->generation_number, "0001", 4); /* generation_number and version_number */ + memcpy (hdr1->version_number, "00", 2); /* combine to produce VMS version # ;1 here */ + ansi_date (statb.st_mtime, hdr1->creation_date); + memcpy (hdr1->expiration_date, " 00000", 6); + memcpy (hdr1->block_count, "000000", 6); + to_ansi_a (hdr1->system_code, "DECFILE11A", sizeof (hdr1->system_code)); + free (fn_cpy); + } + +static void ansi_make_HDR2 (HDR2 *hdr, t_bool fixed_record, size_t block_size, size_t record_size) + { + char size[12]; + + memset (hdr, ' ', sizeof (*hdr)); + memcpy (hdr->type, "HDR", 3); + hdr->num = '2'; + hdr->record_format = (fixed_record ? 'F' : 'D'); + sprintf (size, "%05d", (int)block_size); + memcpy (hdr->block_length, size, sizeof (hdr->block_length)); + sprintf (size, "%05d", (int)record_size); + memcpy (hdr->record_length, size, sizeof (hdr->record_length)); + memcpy (hdr->buffer_offset, "00", 2); + } + +static void ansi_fill_text_buffer (FILE *f, char *buf, size_t bufsize) + { + long start; + char *tmp = (char *)calloc (1 + bufsize, sizeof (*buf)); + size_t offset = 0; + + while (1) { + int rec_size; + + start = ftell (f); + if (!fgets (tmp, bufsize, f)) + break; + rec_size = strlen (tmp); + if ((rec_size + 4) > (int)(bufsize - offset)) { /* room for record? */ + fseek (f, start, SEEK_SET); + break; + } + sprintf (buf + offset, "%04d%*.*s", rec_size + 4, rec_size, rec_size, tmp); + offset += 4 + rec_size; + } + if (bufsize > offset) + memset (buf + offset, '^', bufsize - offset); + free (tmp); + } + +static t_bool ansi_tape_add_block (ANSI_TAPE *tape, uint8 *block, uint32 size) +{ +TAPE_RECORD *rec; + +if (tape->array_size <= tape->record_count) { + TAPE_RECORD **new_records; + new_records = (TAPE_RECORD **)realloc (tape->records, (tape->array_size + 1000) * sizeof (*tape->records)); + if (new_records == NULL) + return TRUE; /* no memory error */ + tape->records = new_records; + memset (tape->records + tape->array_size, 0, 1000 * sizeof (*tape->records)); + tape->array_size += 1000; + } +rec = (TAPE_RECORD *)malloc (sizeof (*rec) + size); +if (rec == NULL) + return TRUE; /* no memory error */ +rec->size = size; +memcpy (rec->data, block, size); +tape->records[tape->record_count++] = rec; +return FALSE; +} + +static int ansi_free_tape (void *vtape) +{ +uint32 i; +ANSI_TAPE *tape = (ANSI_TAPE *)vtape; + +for (i=0; irecord_count; i++) { + free (tape->records[i]); + tape->records[i] = NULL; + } +free (tape->records); +free (tape); +return 0; +} + +ANSI_TAPE *ansi_create_tape (const char *label, uint32 block_size) +{ +ANSI_TAPE *tape = (ANSI_TAPE *)calloc (1, sizeof (*tape)); + +tape->block_size = block_size; +ansi_make_VOL1 (&tape->vol1, label); +ansi_tape_add_block (tape, (uint8 *)&tape->vol1, sizeof (tape->vol1)); +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) +{ +long pos = -1; +long last_cr = -1; +long last_lf = -1; +long line_start = 0; +int chr; +long non_print_chars = 0; +long lf_lines = 0; +long crlf_lines = 0; + +*max_record_size = 0; +*lf_line_endings = FALSE; +*crlf_line_endings = FALSE; +rewind (f); +while (EOF != (chr = fgetc (f))) { + ++pos; + if (!isprint (chr) && (!(chr == '\r')) && (!(chr == '\n'))) + ++non_print_chars; + if (chr == '\r') + last_cr = pos; + if (chr == '\n') { + long line_size; + + if (last_cr == (pos - 1)) { + ++crlf_lines; + line_size = (pos - (line_start - 2)); + } + else { + ++lf_lines; + line_size = (pos - (line_start - 1)); + } + if ((line_size + 4) > (long)(*max_record_size + 4)) + *max_record_size = line_size + 4; + line_start = pos + 1; + last_lf = pos; + } + } +rewind (f); +if (non_print_chars) + *max_record_size = 512; +else { + if ((crlf_lines > 0) && (lf_lines == 0)) { + *lf_line_endings = FALSE; + *crlf_line_endings = TRUE; + } + else { + if ((lf_lines > 0) && (crlf_lines == 0)) { + *lf_line_endings = TRUE; + *crlf_line_endings = FALSE; + } + } + } +return 0; +} + +static int ansi_add_file_to_tape (ANSI_TAPE *tape, const char *filename) +{ +FILE *f; +uint8 *block = NULL; +size_t max_record_size; +t_bool lf_line_endings; +t_bool crlf_line_endings; +char file_sequence[5]; +int block_count = 0; +char block_count_string[17]; +int error = FALSE; +HDR1 hdr1; +HDR2 hdr2; +HDR3 hdr3; +HDR4 hdr4; + +f = fopen (filename, "rb"); +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); +ansi_make_HDR1 (&hdr1, &tape->vol1, &hdr4, filename); +sprintf (file_sequence, "%04d", 1 + tape->file_count); +memcpy (hdr1.file_sequence, file_sequence, sizeof (hdr1.file_sequence)); +ansi_make_HDR2 (&hdr2, !lf_line_endings && !crlf_line_endings, tape->block_size, max_record_size); +if (!lf_line_endings && !crlf_line_endings) + memcpy (&hdr3, HDR3_RMS_FIXED, sizeof (hdr3)); +else { + if (lf_line_endings) + memcpy (&hdr3, HDR3_RMS_STMLF, sizeof (hdr3)); + else + memcpy (&hdr3, HDR3_RMS_STREAM, sizeof (hdr3)); + } +ansi_tape_add_block (tape, (uint8 *)&hdr1, sizeof (hdr1)); +ansi_tape_add_block (tape, (uint8 *)&hdr2, sizeof (hdr2)); +ansi_tape_add_block (tape, (uint8 *)&hdr3, sizeof (hdr3)); +ansi_tape_add_block (tape, (uint8 *)&hdr4, sizeof (hdr4)); +ansi_tape_add_block (tape, NULL, 0); +rewind (f); +block = (uint8 *)malloc (tape->block_size); +while (!feof(f) && !error) { + size_t data_read = tape->block_size; + + if (lf_line_endings || crlf_line_endings) + ansi_fill_text_buffer (f, (char *)block, tape->block_size); + else + data_read = fread (block, 1, tape->block_size, f); + if (data_read > 0) + error = ansi_tape_add_block (tape, block, data_read); + if (!error) + ++block_count; + } +fclose (f); +free (block); +ansi_tape_add_block (tape, NULL, 0); +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)); +ansi_tape_add_block (tape, (uint8 *)&hdr2, sizeof (hdr2)); +ansi_tape_add_block (tape, (uint8 *)&hdr3, sizeof (hdr3)); +ansi_tape_add_block (tape, (uint8 *)&hdr4, sizeof (hdr4)); +ansi_tape_add_block (tape, NULL, 0); +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; +return error; +} + +static void sim_tape_add_ansi_entry (const char *directory, + const char *filename, + t_offset FileSize, + const struct stat *filestat, + void *context) +{ +ANSI_TAPE *tape = (ANSI_TAPE *)context; +char FullPath[PATH_MAX + 1]; + +sprintf (FullPath, "%s%s", directory, filename); + +(void)ansi_add_file_to_tape (tape, FullPath); +} + diff --git a/sim_tape.h b/sim_tape.h index 402d2246..cbf40b94 100644 --- a/sim_tape.h +++ b/sim_tape.h @@ -102,6 +102,7 @@ typedef struct { #define MTUF_F_P7B 3 /* P7B format */ #define MTUF_F_AWS 4 /* AWS format */ #define MTUF_F_TAR 5 /* TAR format */ +#define MTUF_F_ANSI 6 /* ANSIFILES format */ #define MTUF_V_UF (MTUF_V_FMT + MTUF_W_FMT) #define MTUF_PNU (1u << MTUF_V_PNU) #define MTUF_WLK (1u << MTUF_V_WLK)