From 42df8ef82bb7776719446bed993c93796043b3d3 Mon Sep 17 00:00:00 2001 From: Richard Cornwell Date: Thu, 8 Jun 2017 20:51:14 -0400 Subject: [PATCH] SCP: Updated to current version --- README.md | 10 +- scp.c | 175 +++++++++---- scp.h | 8 + sim_console.c | 21 +- sim_defs.h | 2 +- sim_ether.c | 17 +- sim_ether.h | 3 + sim_fio.c | 2 +- sim_serial.c | 4 +- sim_tape.c | 705 +++++++++++++++++++++++++++++++++++--------------- sim_timer.c | 11 +- sim_tmxr.c | 5 + sim_video.c | 26 +- sim_video.h | 5 - 14 files changed, 678 insertions(+), 316 deletions(-) diff --git a/README.md b/README.md index b01c2c9..961782d 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,6 @@ + +[![Coverity Scan Build Status](https://scan.coverity.com/projects/12020/badge.svg)](https://scan.coverity.com/projects/rcornwell-sims) + # Development Simulators. This is a working copy of my Burroughs B5500, KA10, KI10 and IBM7000 series @@ -14,7 +17,8 @@ These are still in the testing stages. Please report any issues that you find in The KA10 sim has successfully run Tops 10 5.03 and Tops 10 6.03. The KI10 sim has successfully run Tops 10 6.03 -Started adding support for KA10 to boot ITS +Support for ITS (untested). +Support for BBN pager (untested). Disk * RC10 RD10/RM10 @@ -38,6 +42,10 @@ and transfering to the loaded value. RC10, RH10, TM10 support readin mode. Possibly add in support for KL10A. (No extended addressing). +To do: + * Add support for IBM 3330 drive to run Waits. + * Add support for DC76 for ITS. + # IBM 7000 simulators. Latest status for I7000 Cpus: diff --git a/scp.c b/scp.c index 0cda26d..f62dcd0 100644 --- a/scp.c +++ b/scp.c @@ -1161,10 +1161,19 @@ static const char simh_help[] = "3Asynch\n" "+set asynch enable asynchronous I/O\n" "+set noasynch disable asynchronous I/O\n" -#define HLP_SET_ENVIRON "*Commands SET Asynch" +#define HLP_SET_ENVIRON "*Commands SET Environment" "3Environment\n" + "4Explicitily Changing A Variable\n" "+set environment name=val set environment variable\n" "+set environment name clear environment variable\n" + "4Gathering Input From A User\n" + " Input from a user can be obtained by:\n\n" + "+set environment -p \"Prompt String\" name=default\n\n" + " The -p switch indicates that the user should be prompted\n" + " with the indicated prompt string and the input provided\n" + " will be saved in the environment variable 'name'. If no\n" + " input is provided, the value specified as 'default' will be\n" + " used.\n" #define HLP_SET_ON "*Commands SET Command_Status_Trap_Dispatching" "3Command Status Trap Dispatching\n" "+set on enables error checking after command\n" @@ -1705,6 +1714,8 @@ ASSERT failure have several different actions: " CPU's \"A\" register for the expected value:\n\n" "++; OS bootstrap command file\n" "++;\n" + "++IF EXIST \"os.disk\" echo os.disk exists\n" + "++IF NOT EXIST os.disk echo os.disk not existing\n" "++ATTACH DS0 os.disk\n" "++BOOT DS\n" "++; A register contains error code; 0 = good boot\n" @@ -1720,7 +1731,7 @@ ASSERT failure have several different actions: "4IF\n" " The IF command tests a simulator state condition and executes additional\n" " commands if the condition is true:\n\n" - "++IF commandtoprocess{; additionalcommandtoprocess}...\n\n" + "++IF commandtoprocess{; additionalcommandtoprocess}...\n\n" "5Examples:\n" " A command file might be used to bootstrap an operating system that\n" " halts after the initial load from disk. The ASSERT command is then\n" @@ -1728,6 +1739,8 @@ ASSERT failure have several different actions: " CPU's \"A\" register for the expected value:\n\n" "++; OS bootstrap command file\n" "++;\n" + "++IF EXIST \"os.disk\" echo os.disk exists\n" + "++IF NOT EXIST os.disk echo os.disk not existing\n" "++ATTACH DS0 os.disk\n" "++BOOT DS\n" "++; A register contains error code; 0 = good boot\n" @@ -1742,7 +1755,7 @@ ASSERT failure have several different actions: " failed\" message. Otherwise, the command file will continue to bring up\n" " the operating system.\n" "4Conditional Expressions\n" - " The IF and ASSERT commands evaluate two different forms of conditional\n" + " The IF and ASSERT commands evaluate three different forms of conditional\n" " expressions.:\n\n" "5Simulator State Expressions\n" " The values of simulator registers can be evaluated with:\n\n" @@ -1783,6 +1796,11 @@ ASSERT failure have several different actions: " comprised of all numeric digits, then the strings are converted to numbers\n" " and a numeric comparison is performed. For example: \"+1\" EQU \"1\" will be\n" " true.\n" + "5File Existence Expressions\n" + " File existence can be determined with:\n" + "++{NOT} EXIST \"\"\n\n" + "++{NOT} EXIST \n\n" + " Specifies a true (false {NOT}) condition if the file exists.\n" /***************** 80 character line width template *************************/ #define HLP_EXIT "*Commands Exiting_The_Simulator" "2Exiting The Simulator\n" @@ -2037,7 +2055,7 @@ for (i = 1; i < argc; i++) { /* loop thru args */ return 0; } if (*cbuf) /* concat args */ - sim_strlcat (cbuf, " ", sizeof(cbuf)); + strlcat (cbuf, " ", sizeof(cbuf)); sprintf(&cbuf[strlen(cbuf)], "%s%s%s", strchr(argv[i], ' ') ? "\"" : "", argv[i], strchr(argv[i], ' ') ? "\"" : ""); lookswitch = FALSE; /* no more switches */ } @@ -2441,7 +2459,7 @@ if (dptr->flags & DEV_DISABLE) { sprintf (buf, "set %s DISABLE", sim_dname (dptr)); fprintf (st, "%-30s\tDisables device %s\n", buf, sim_dname (dptr)); } -if (dptr->flags & DEV_DEBUG) { +if ((dptr->flags & DEV_DEBUG) || (dptr->debflags)) { fprint_header (st, &found, header); sprintf (buf, "set %s DEBUG", sim_dname (dptr)); fprintf (st, "%-30s\tEnables debugging for device %s\n", buf, sim_dname (dptr)); @@ -2519,7 +2537,7 @@ if (dptr->modifiers) { fprintf (st, "%-30s\t%s\n", buf, mptr->help ? mptr->help : ""); } } -if (dptr->flags & DEV_DEBUG) { +if ((dptr->flags & DEV_DEBUG) || (dptr->debflags)) { fprint_header (st, &found, header); sprintf (buf, "show %s DEBUG", sim_dname (dptr)); fprintf (st, "%-30s\tDisplays debugging status for device %s\n", buf, sim_dname (dptr)); @@ -2948,8 +2966,8 @@ if (flag >= 0) { /* Only bump nesting fro } } -sim_strlcpy( sim_do_filename[sim_do_depth], do_arg[0], - sizeof (sim_do_filename[sim_do_depth])); /* stash away do file name for possible use by 'call' command */ +strlcpy( sim_do_filename[sim_do_depth], do_arg[0], + sizeof (sim_do_filename[sim_do_depth])); /* stash away do file name for possible use by 'call' command */ sim_do_label[sim_do_depth] = label; /* stash away do label for possible use in messages */ sim_goto_line[sim_do_depth] = 0; if (label) { @@ -3441,6 +3459,7 @@ uint32 idx; t_value val; t_stat r; t_bool Not = FALSE; +t_bool Exist = FALSE; t_bool result; t_addr addr; t_stat reason; @@ -3454,8 +3473,13 @@ tptr = get_glyph (cptr, gbuf, 0); /* get token */ if (!strcmp (gbuf, "NOT")) { /* Conditional Inversion? */ Not = TRUE; /* remember that, and */ cptr = (CONST char *)tptr; + tptr = get_glyph (cptr, gbuf, 0); /* get next token */ } -if (*cptr == '"') { /* quoted string comparison? */ +if (!strcmp (gbuf, "EXIST")) { /* File Exist Test? */ + Exist = TRUE; /* remember that, and */ + cptr = (CONST char *)tptr; + } +if (Exist || (*cptr == '"')) { /* quoted string comparison? */ char op[CBUFSIZE]; static struct { const char *op; @@ -3485,29 +3509,37 @@ if (*cptr == '"') { /* quoted string compari cptr += strlen (gbuf); while (sim_isspace (*cptr)) /* skip spaces */ ++cptr; - get_glyph (cptr, op, '"'); - for (optr = compare_ops; optr->op; optr++) - if (0 == strcmp (op, optr->op)) - break; - if (!optr->op) - return sim_messagef (SCPE_ARG, "Invalid operator: %s\n", op); - cptr += strlen (op); - while (sim_isspace (*cptr)) /* skip spaces */ - ++cptr; - cptr = (CONST char *)get_glyph_gen (cptr, gbuf2, 0, (sim_switches & SWMASK ('I')), TRUE, '\\'); - /* get second string */ - if (*cptr) { /* more? */ - if (flag) /* ASSERT has no more args */ - return SCPE_2MARG; + if (!Exist) { + get_glyph (cptr, op, '"'); + for (optr = compare_ops; optr->op; optr++) + if (0 == strcmp (op, optr->op)) + break; + if (!optr->op) + return sim_messagef (SCPE_ARG, "Invalid operator: %s\n", op); + cptr += strlen (op); + while (sim_isspace (*cptr)) /* skip spaces */ + ++cptr; + cptr = (CONST char *)get_glyph_gen (cptr, gbuf2, 0, (sim_switches & SWMASK ('I')), TRUE, '\\'); + /* get second string */ + if (*cptr) { /* more? */ + if (flag) /* ASSERT has no more args */ + return SCPE_2MARG; + } + else { + if (!flag) + return SCPE_2FARG; /* IF needs actions! */ + } + result = sim_cmp_string (gbuf, gbuf2); + result = ((result == optr->aval) || (result == optr->bval)); + if (optr->invert) + result = !result; } else { - if (!flag) - return SCPE_2FARG; /* IF needs actions! */ + FILE *f = fopen (gbuf, "r"); + if (f) + fclose (f); + result = (f != NULL); } - result = sim_cmp_string (gbuf, gbuf2); - result = ((result == optr->aval) || (result == optr->bval)); - if (optr->invert) - result = !result; } else { cptr = get_glyph (cptr, gbuf, 0); /* get register */ @@ -3996,11 +4028,42 @@ return SCPE_OK; t_stat sim_set_environment (int32 flag, CONST char *cptr) { -char varname[CBUFSIZE]; +char varname[CBUFSIZE], prompt[CBUFSIZE], cbuf[CBUFSIZE]; if ((!cptr) || (*cptr == 0)) /* now eol? */ return SCPE_2FARG; -cptr = get_glyph (cptr, varname, '='); /* get environment variable name */ +if (sim_switches & SWMASK ('P')) { + CONST char *deflt = NULL; + + cptr = get_glyph_quoted (cptr, prompt, 0); /* get prompt */ + if (prompt[0] == '\0') + return sim_messagef (SCPE_2FARG, "Missing Prompt and Environment Variable Name\n"); + if ((prompt[0] == '"') || (prompt[0] == '\'')) { + prompt[strlen (prompt) - 1] = '\0'; + memmove (prompt, prompt + 1, strlen (prompt)); + } + deflt = get_glyph (cptr, varname, '='); /* get environment variable name */ + if (deflt == NULL) + deflt = ""; + if (*deflt) { + strlcat (prompt, " [", sizeof (prompt)); + strlcat (prompt, deflt, sizeof (prompt)); + strlcat (prompt, "] ", sizeof (prompt)); + } + else + strlcat (prompt, " ", sizeof (prompt)); + if (sim_rem_cmd_active_line == -1) { + cptr = read_line_p (prompt, cbuf, sizeof(cbuf), stdin); + if ((cptr == NULL) || (*cptr == 0)) + cptr = deflt; + else + cptr = cbuf; + } + else + cptr = deflt; + } +else + cptr = get_glyph (cptr, varname, '='); /* get environment variable name */ setenv(varname, cptr, 1); return SCPE_OK; } @@ -4841,7 +4904,7 @@ t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST cha int32 any = 0; DEBTAB *dep; -if (dptr->flags & DEV_DEBUG) { +if ((dptr->flags & DEV_DEBUG) || (dptr->debflags)) { if (dptr->dctrl == 0) fputs ("Debugging disabled", st); else if (dptr->debflags == NULL) @@ -5090,7 +5153,7 @@ char DirName[PATH_MAX + 1], WholeName[PATH_MAX + 1], WildName[PATH_MAX + 1]; memset (DirName, 0, sizeof(DirName)); memset (WholeName, 0, sizeof(WholeName)); -sim_strlcpy (WildName, cptr, sizeof(WildName)); +strlcpy (WildName, cptr, sizeof(WildName)); cptr = WildName; sim_trim_endspc (WildName); if ((!stat (WildName, &filestat)) && (filestat.st_mode & S_IFDIR)) @@ -5106,7 +5169,7 @@ if ((*cptr != '/') || (0 == memcmp (cptr, "./", 2)) || (0 == memcmp (cptr, "../" sim_trim_endspc (WholeName); } else - sim_strlcpy (WholeName, cptr, sizeof(WholeName)); + strlcpy (WholeName, cptr, sizeof(WholeName)); while ((c = strstr (WholeName, "/./"))) memmove (c + 1, c + 3, 1 + strlen (c + 3)); while ((c = strstr (WholeName, "//"))) @@ -5370,7 +5433,7 @@ char FullPath[PATH_MAX + 1]; char dname[CBUFSIZE];\ t_stat st; -sim_strlcpy (dname, ctx->destname, sizeof (dname)); +strlcpy (dname, ctx->destname, sizeof (dname)); sprintf (FullPath, "%s%s", directory, filename); @@ -7031,7 +7094,7 @@ for (gptr = gbuf, reason = SCPE_OK; return SCPE_ARG; reason = exdep_reg_loop (ofile, sim_schrptr, flag, cptr, lowr, highr, (uint32) low, (uint32) high); - if ((!sim_oline) && (sim_log && (ofile == stdout))) + if ((flag & EX_E) && (!sim_oline) && (sim_log && (ofile == stdout))) exdep_reg_loop (sim_log, sim_schrptr, EX_E, cptr, lowr, highr, (uint32) low, (uint32) high); continue; @@ -7046,7 +7109,7 @@ for (gptr = gbuf, reason = SCPE_OK; return SCPE_ARG; reason = exdep_addr_loop (ofile, sim_schaptr, flag, cptr, low, high, sim_dfdev, sim_dfunit); - if ((!sim_oline) && (sim_log && (ofile == stdout))) + if ((flag & EX_E) && (!sim_oline) && (sim_log && (ofile == stdout))) exdep_addr_loop (sim_log, sim_schaptr, EX_E, cptr, low, high, sim_dfdev, sim_dfunit); } /* end for */ @@ -10441,7 +10504,7 @@ t_stat sim_exp_show_tab (FILE *st, const EXPECT *exp, const EXPTAB *ep) { if (!ep) return SCPE_OK; -fprintf (st, "EXPECT"); +fprintf (st, " EXPECT"); if (ep->switches & EXP_TYP_PERSIST) fprintf (st, " -p"); if (ep->switches & EXP_TYP_CLEARALL) @@ -10466,20 +10529,20 @@ CONST EXPTAB *ep = (CONST EXPTAB *)sim_exp_fnd (exp, match, 0); if (exp->buf_size) { char *bstr = sim_encode_quoted_string (exp->buf, exp->buf_ins); - fprintf (st, "Match Buffer Size: %d\n", exp->buf_size); - fprintf (st, "Buffer Insert Offset: %d\n", exp->buf_ins); - fprintf (st, "Buffer Contents: %s\n", bstr); + fprintf (st, " Match Buffer Size: %d\n", exp->buf_size); + fprintf (st, " Buffer Insert Offset: %d\n", exp->buf_ins); + fprintf (st, " Buffer Contents: %s\n", bstr); free (bstr); } if (exp->after) - fprintf (st, "Halt After: %d instructions\n", exp->after); -if (exp->dptr && exp->dbit) - fprintf (st, "Debugging via: SET %s DEBUG%s%s\n", sim_dname(exp->dptr), exp->dptr->debflags ? "=" : "", exp->dptr->debflags ? get_dbg_verb (exp->dbit, exp->dptr) : ""); -fprintf (st, "Match Rules:\n"); + fprintf (st, " Halt After: %d instructions\n", exp->after); +if (exp->dptr && (exp->dbit & exp->dptr->dctrl)) + fprintf (st, " Expect Debugging via: SET %s DEBUG%s%s\n", sim_dname(exp->dptr), exp->dptr->debflags ? "=" : "", exp->dptr->debflags ? get_dbg_verb (exp->dbit, exp->dptr) : ""); +fprintf (st, " Match Rules:\n"); if (!*match) return sim_exp_showall (st, exp); if (!ep) { - fprintf (st, "No Rules match '%s'\n", match); + fprintf (st, " No Rules match '%s'\n", match); return SCPE_ARG; } do { @@ -10708,25 +10771,25 @@ return SCPE_OK; t_stat sim_show_send_input (FILE *st, const SEND *snd) { if (snd->extoff < snd->insoff) { - fprintf (st, "%d bytes of pending input Data:\n ", snd->insoff-snd->extoff); + fprintf (st, " %d bytes of pending input Data:\n ", snd->insoff-snd->extoff); fprint_buffer_string (st, snd->buffer+snd->extoff, snd->insoff-snd->extoff); fprintf (st, "\n"); } else - fprintf (st, "No Pending Input Data\n"); + fprintf (st, " No Pending Input Data\n"); if ((snd->next_time - sim_gtime()) > 0) { - if ((snd->next_time - sim_gtime()) > (sim_timer_inst_per_sec()/1000000.0)) - fprintf (st, "Minimum of %d instructions (%d microseconds) before sending first character\n", (int)(snd->next_time - sim_gtime()), + if (((snd->next_time - sim_gtime()) > (sim_timer_inst_per_sec()/1000000.0)) && ((sim_timer_inst_per_sec()/1000000.0) > 0.0)) + fprintf (st, " Minimum of %d instructions (%d microseconds) before sending first character\n", (int)(snd->next_time - sim_gtime()), (int)((snd->next_time - sim_gtime())/(sim_timer_inst_per_sec()/1000000.0))); else - fprintf (st, "Minimum of %d instructions before sending first character\n", (int)(snd->next_time - sim_gtime())); + fprintf (st, " Minimum of %d instructions before sending first character\n", (int)(snd->next_time - sim_gtime())); } -if (snd->delay > (sim_timer_inst_per_sec()/1000000.0)) - fprintf (st, "Minimum of %d instructions (%d microseconds) between characters\n", (int)snd->delay, (int)(snd->delay/(sim_timer_inst_per_sec()/1000000.0))); +if ((snd->delay > (sim_timer_inst_per_sec()/1000000.0)) && ((sim_timer_inst_per_sec()/1000000.0) > 0.0)) + fprintf (st, " Minimum of %d instructions (%d microseconds) between characters\n", (int)snd->delay, (int)(snd->delay/(sim_timer_inst_per_sec()/1000000.0))); else - fprintf (st, "Minimum of %d instructions between characters\n", (int)snd->delay); -if (snd->dptr && snd->dbit) - fprintf (st, "Debugging via: SET %s DEBUG%s%s\n", sim_dname(snd->dptr), snd->dptr->debflags ? "=" : "", snd->dptr->debflags ? get_dbg_verb (snd->dbit, snd->dptr) : ""); + fprintf (st, " Minimum of %d instructions between characters\n", (int)snd->delay); +if (snd->dptr && (snd->dbit & snd->dptr->dctrl)) + fprintf (st, " Send Debugging via: SET %s DEBUG%s%s\n", sim_dname(snd->dptr), snd->dptr->debflags ? "=" : "", snd->dptr->debflags ? get_dbg_verb (snd->dbit, snd->dptr) : ""); return SCPE_OK; } diff --git a/scp.h b/scp.h index 0b3228c..ef91c44 100644 --- a/scp.h +++ b/scp.h @@ -160,10 +160,18 @@ int sim_strncasecmp (const char *string1, const char *string2, size_t len); int sim_strcasecmp (const char *string1, const char *string2); size_t sim_strlcat (char *dst, const char *src, size_t size); size_t sim_strlcpy (char *dst, const char *src, size_t size); +#ifndef strlcpy #define strlcpy(dst, src, size) sim_strlcpy((dst), (src), (size)) +#endif +#ifndef strlcat #define strlcat(dst, src, size) sim_strlcat((dst), (src), (size)) +#endif +#ifndef strncasecmp #define strncasecmp(str1, str2, len) sim_strncasecmp((str1), (str2), (len)) +#endif +#ifndef strcasecmp #define strcasecmp(str1, str2) sim_strcasecmp ((str1), (str2)) +#endif CONST char *get_sim_opt (int32 opt, CONST char *cptr, t_stat *st); const char *put_switches (char *buf, size_t bufsize, uint32 sw); CONST char *get_glyph (const char *iptr, char *optr, char mchar); diff --git a/sim_console.c b/sim_console.c index 5bb132a..3d4eb65 100644 --- a/sim_console.c +++ b/sim_console.c @@ -340,10 +340,10 @@ static SHTAB show_con_tab[] = { { "DEBUG", &sim_show_cons_debug, 0 }, { "BUFFERED", &sim_show_cons_buff, 0 }, { "EXPECT", &sim_show_cons_expect, 0 }, - { "HALT", &sim_show_cons_expect, 0 }, + { "HALT", &sim_show_cons_expect, -1 }, { "INPUT", &sim_show_cons_send_input, 0 }, - { "RESPONSE", &sim_show_cons_send_input, 0 }, - { "DELAY", &sim_show_cons_expect, 0 }, + { "RESPONSE", &sim_show_cons_send_input, -1 }, + { "DELAY", &sim_show_cons_expect, -1 }, { NULL, NULL, 0 } }; @@ -411,7 +411,8 @@ int32 i; if (*cptr == 0) { /* show all */ for (i = 0; show_con_tab[i].name; i++) - show_con_tab[i].action (st, dptr, uptr, show_con_tab[i].arg, cptr); + if (show_con_tab[i].arg != -1) + show_con_tab[i].action (st, dptr, uptr, show_con_tab[i].arg, cptr); return SCPE_OK; } while (*cptr != 0) { @@ -509,7 +510,6 @@ REMOTE *sim_rem_consoles = NULL; static TMXR sim_rem_con_tmxr = { 0, 0, 0, NULL, NULL, &sim_remote_console };/* remote console line mux */ static uint32 sim_rem_read_timeout = 30; /* seconds before automatic continue */ -static uint32 *sim_rem_read_timeouts = NULL;/* per line read timeout (default from sim_rem_read_timeout) */ static int32 sim_rem_active_number = -1; /* -1 - not active, >= 0 is index of active console */ int32 sim_rem_cmd_active_line = -1; /* step in progress on line # */ static CTAB *sim_rem_active_command = NULL; /* active command */ @@ -1349,7 +1349,7 @@ for (i=(was_active_command ? sim_rem_cmd_active_line : 0); if (!master_session) tmxr_linemsg (lp, "\r\nSimulator paused.\r\n"); if (!master_session && rem->read_timeout) { - tmxr_linemsgf (lp, "Simulation will resume automatically if input is not received in %d seconds\n", sim_rem_read_timeouts[i]); + tmxr_linemsgf (lp, "Simulation will resume automatically if input is not received in %d seconds\n", rem->read_timeout); tmxr_linemsgf (lp, "\r\n"); tmxr_send_buffered_data (lp); /* flush any buffered data */ } @@ -2206,7 +2206,7 @@ if (sim_deb) { fprintf (st, " Debug messages display time of day as seconds.msec%s\n", sim_deb_switches & SWMASK ('R') ? " relative to the start of debugging" : ""); for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { if (!(dptr->flags & DEV_DIS) && - (dptr->flags & DEV_DEBUG) && + ((dptr->flags & DEV_DEBUG) || (dptr->debflags)) && (dptr->dctrl)) { fprintf (st, "Device: %-6s ", dptr->name); show_dev_debug (st, dptr, NULL, 0, NULL); @@ -2214,7 +2214,7 @@ if (sim_deb) { } for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i) { if (!(dptr->flags & DEV_DIS) && - (dptr->flags & DEV_DEBUG) && + ((dptr->flags & DEV_DEBUG) || (dptr->debflags)) && (dptr->dctrl)) { fprintf (st, "Device: %-6s ", dptr->name); show_dev_debug (st, dptr, NULL, 0, NULL); @@ -2433,6 +2433,7 @@ return tmxr_close_master (&sim_con_tmxr); /* close master socket * t_stat sim_show_cons_expect (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, CONST char *cptr) { +fprintf (st, "Console Expect processing:\n"); return sim_exp_show (st, &sim_con_expect, cptr); } @@ -2628,6 +2629,7 @@ return &sim_con_expect; t_stat sim_show_cons_send_input (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char *cptr) { +fprintf (st, "Console Send processing:\n"); return sim_show_send_input (st, &sim_con_send); } @@ -3229,7 +3231,8 @@ if ((std_input) && /* If Not Background pro if ((std_output) && /* If Not Background process? */ (std_output != INVALID_HANDLE_VALUE)) { if (GetConsoleMode(std_output, &saved_output_mode)) - SetConsoleMode(std_output, ENABLE_VIRTUAL_TERMINAL_PROCESSING|ENABLE_PROCESSED_OUTPUT); + if (!SetConsoleMode(std_output, ENABLE_VIRTUAL_TERMINAL_PROCESSING|ENABLE_PROCESSED_OUTPUT)) + SetConsoleMode(std_output, ENABLE_PROCESSED_OUTPUT); } if (sim_log) { fflush (sim_log); diff --git a/sim_defs.h b/sim_defs.h index 6037ffd..761a110 100644 --- a/sim_defs.h +++ b/sim_defs.h @@ -409,7 +409,7 @@ typedef uint32 t_addr; /* String match - at least one character required */ -#define MATCH_CMD(ptr,cmd) ((NULL == (ptr)) || (!*(ptr)) || sim_strncasecmp ((ptr), (cmd), strlen (ptr))) +#define MATCH_CMD(ptr,cmd) ((NULL == (ptr)) || (!*(ptr)) || strncasecmp ((ptr), (cmd), strlen (ptr))) /* End of Linked List/Queue value */ /* Chosen for 2 reasons: */ diff --git a/sim_ether.c b/sim_ether.c index 47cf978..046765a 100644 --- a/sim_ether.c +++ b/sim_ether.c @@ -711,7 +711,7 @@ char* eth_getname_byname(const char* name, char* temp, char *desc) n = strlen(name); for (i=0; i - static void * _eth_reader(void *arg) { @@ -3617,7 +3615,6 @@ t_stat eth_filter_hash(ETH_DEV* dev, int addr_count, ETH_MAC* const addresses, { int i; char buf[116+66*ETH_FILTER_MAX]; -char errbuf[PCAP_ERRBUF_SIZE]; char mac[20]; char* buf2; t_stat status; @@ -3762,6 +3759,7 @@ sim_debug(dev->dbit, dev->dptr, "BPF string is: |%s|\n", buf); #ifdef USE_BPF if (dev->eth_api == ETH_API_PCAP) { + char errbuf[PCAP_ERRBUF_SIZE]; bpf_u_int32 bpf_subnet, bpf_netmask; if (pcap_lookupnet(dev->name, &bpf_subnet, &bpf_netmask, errbuf)<0) @@ -3824,11 +3822,12 @@ int eth_host_devices(int used, int max, ETH_LIST* list) { pcap_t* conn = NULL; int i, j, datalink = 0; -char errbuf[PCAP_ERRBUF_SIZE]; for (i=0; i #if defined (USE_SETNONBLOCK) #undef USE_SETNONBLOCK #endif /* USE_SETNONBLOCK */ @@ -112,6 +113,8 @@ extern "C" { #if (!defined (xBSD) && !defined(_WIN32) && !defined(VMS) && !defined(__CYGWIN__)) || defined (HAVE_TAP_NETWORK) || defined (HAVE_VDE_NETWORK) #define MUST_DO_SELECT 1 #endif +#else +#include #endif /* USE_READER_THREAD */ /* give priority to USE_NETWORK over USE_SHARED */ diff --git a/sim_fio.c b/sim_fio.c index 38a1926..2f77b3d 100644 --- a/sim_fio.c +++ b/sim_fio.c @@ -390,7 +390,7 @@ dwStatus = FormatMessageA (FORMAT_MESSAGE_FROM_SYSTEM| sizeof (szMsgBuffer) -1, // __in DWORD nSize, NULL); // __in_opt va_list *Arguments if (0 == dwStatus) - snprintf(szMsgBuffer, sizeof(szMsgBuffer) - 1, "Error Code: 0x%X", dwError); + snprintf(szMsgBuffer, sizeof(szMsgBuffer) - 1, "Error Code: 0x%lX", dwError); while (sim_isspace (szMsgBuffer[strlen (szMsgBuffer)-1])) szMsgBuffer[strlen (szMsgBuffer) - 1] = '\0'; return szMsgBuffer; diff --git a/sim_serial.c b/sim_serial.c index f006888..167c04f 100644 --- a/sim_serial.c +++ b/sim_serial.c @@ -302,7 +302,7 @@ found = 0; n = strlen(name); for (i=0; idbit, ctx->dptr, "_tape_completion_dispatch(unit=%d, top=%d, cal if (ctx->io_top != TOP_DONE) abort(); /* horribly wrong, stop */ -if (ctx->callback && ctx->io_top == TOP_DONE) { +if (ctx->asynch_io) + pthread_mutex_lock (&ctx->io_lock); + +if (ctx->callback) { ctx->callback = NULL; + if (ctx->asynch_io) + pthread_mutex_unlock (&ctx->io_lock); callback (uptr, ctx->io_status); } +else { + if (ctx->asynch_io) + pthread_mutex_unlock (&ctx->io_lock); + } } static t_bool _tape_is_active (UNIT *uptr) @@ -446,7 +457,7 @@ DEVICE *dptr; if ((dptr = find_dev_from_unit (uptr)) == NULL) return SCPE_NOATT; -return sim_tape_attach_ex (uptr, cptr, (dptr->flags & DEV_DEBUG) ? 0xFFFFFFFF : 0, 0); +return sim_tape_attach_ex (uptr, cptr, ((dptr->flags & DEV_DEBUG) || (dptr->debflags)) ? 0xFFFFFFFF : 0, 0); } t_stat sim_tape_attach_ex (UNIT *uptr, const char *cptr, uint32 dbit, int completion_delay) @@ -607,7 +618,7 @@ if (sim_deb && (ctx->dptr->dctrl & reason)) sim_data_trace(ctx->dptr, uptr, (detail ? data : NULL), "", len, txt, reason); } -/* Read record length forward (internal routine) +/* Read record length forward (internal routine). Inputs: uptr = pointer to tape unit @@ -630,7 +641,7 @@ if (sim_deb && (ctx->dptr->dctrl & reason)) giving the reason that the operation did not succeed and the tape position is as indicated above. - The ANSI standards for magnetic tape recording (X3.32, X3.39, and X3.54) and + The ANSI standards for magnetic tape recording (X3.22, X3.39, and X3.54) and the equivalent ECMA standard (ECMA-62) specify a maximum erase gap length of 25 feet (7.6 meters). While gaps of any length may be written, gaps longer than this are non-standard and may indicate that an unrecorded or erased tape @@ -641,7 +652,7 @@ if (sim_deb && (ctx->dptr->dctrl & reason)) reaches 25 feet, motion is terminated, and MTSE_RUNAWAY status is returned. Runaway status is also returned if an end-of-medium marker or the physical end of file is encountered while spacing over a gap; however, MTSE_EOM is - returned if the tape is positioned at the EOM on entry. + returned if the tape is positioned at the EOM or EOF on entry. If the density has not been set, then a gap of any length is skipped, and MTSE_RUNAWAY status is never returned. In effect, erase gaps present in the @@ -656,13 +667,14 @@ if (sim_deb && (ctx->dptr->dctrl & reason)) read is of a single metadatum marker. If that is a gap marker, then additional buffered reads are performed. - See the notes at "sim_tape_wrgap" regarding the erase gap implementation. + See the notes at "tape_erase_fwd" regarding the erase gap implementation. + Implementation notes: 1. For programming convenience, erase gap processing is performed for both SIMH standard and E11 tape formats, although the latter will never - contain erase gaps, as the "sim_tape_wrgap" call takes no action for the + contain erase gaps, as the "tape_erase_fwd" call takes no action for the E11 format. 2. The "feof" call cannot return a non-zero value on the first pass through @@ -670,7 +682,11 @@ if (sim_deb && (ctx->dptr->dctrl & reason)) indicator. Subsequent passes only occur if an erase gap is present, so a non-zero return indicates an EOF was seen while reading through a gap. - 3. The dynamic start/stop test of the HP 3000 magnetic tape diagnostic + 3. The "runaway_counter" cannot decrement to zero (or below) in the presence + of an error that terminates the gap-search loop. Therefore, the test + after the loop exit need not check for error status. + + 4. The dynamic start/stop test of the HP 3000 magnetic tape diagnostic heavily exercises the erase gap scanning code. Sample test execution times for various buffer sizes on a 2 GHz host platform are: @@ -684,7 +700,7 @@ if (sim_deb && (ctx->dptr->dctrl & reason)) 512 186 1024 171 - 4. Because an erase gap may precede the logical end-of-medium, represented + 5. Because an erase gap may precede the logical end-of-medium, represented either by the physical end-of-file or by an EOM marker, the "position not updated" flag is set only if the tape is positioned at the EOM when the routine is entered. If at least one gap marker precedes the EOM, then @@ -694,27 +710,27 @@ if (sim_deb && (ctx->dptr->dctrl & reason)) static t_stat sim_tape_rdlntf (UNIT *uptr, t_mtrlnt *bc) { -struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; -uint8 c; -t_bool all_eof; -uint32 f = MT_GET_FMT (uptr); +uint8 c; +t_bool all_eof; +uint32 f = MT_GET_FMT (uptr); t_mtrlnt sbc; t_tpclnt tpcbc; t_mtrlnt buffer [256]; /* local tape buffer */ -uint32 bufcntr, bufcap; /* buffer counter and capacity */ -int32 runaway_counter, sizeof_gap; /* bytes remaining before runaway and bytes per gap */ -t_stat r = MTSE_OK; +uint32 bufcntr, bufcap; /* buffer counter and capacity */ +int32 runaway_counter, sizeof_gap; /* bytes remaining before runaway and bytes per gap */ +t_stat status = MTSE_OK; MT_CLR_PNU (uptr); /* clear the position-not-updated flag */ if ((uptr->flags & UNIT_ATT) == 0) /* if the unit is not attached */ return MTSE_UNATT; /* then quit with an error */ -if (ctx == NULL) /* if not properly attached? */ - return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ -sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* set the initial tape position */ +if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) { /* set the initial tape position; if it fails */ + MT_SET_PNU (uptr); /* then set position not updated */ + status = sim_tape_ioerr (uptr); /* and quit with I/O error status */ + } -switch (f) { /* the read method depends on the tape format */ +else switch (f) { /* otherwise the read method depends on the tape format */ case MTUF_F_STD: case MTUF_F_E11: @@ -724,6 +740,7 @@ switch (f) { /* the read method depen sizeof_gap = 0; /* then disable runaway detection */ runaway_counter = INT_MAX; /* to allow gaps of any size */ } + else /* otherwise */ sizeof_gap = sizeof (t_mtrlnt); /* set the size of the gap */ @@ -734,9 +751,9 @@ switch (f) { /* the read method depen if (bufcntr == bufcap) { /* if the buffer is empty then refill it */ if (feof (uptr->fileref)) { /* if we hit the EOF while reading a gap */ if (sizeof_gap > 0) /* then if detection is enabled */ - r = MTSE_RUNAWAY; /* then report a tape runaway */ + status = MTSE_RUNAWAY; /* then report a tape runaway */ else /* otherwise report the physical EOF */ - r = MTSE_EOM; /* as the end-of-medium */ + status = MTSE_EOM; /* as the end-of-medium */ break; } @@ -756,7 +773,7 @@ switch (f) { /* the read method depen if (bufcntr == 0) /* then if this is the initial read */ MT_SET_PNU (uptr); /* then set position not updated */ - r = sim_tape_ioerr (uptr); /* report the error and quit */ + status = sim_tape_ioerr (uptr); /* report the error and quit */ break; } @@ -764,15 +781,15 @@ switch (f) { /* the read method depen || buffer [0] == MTR_EOM) /* or at the logical EOM */ if (bufcntr == 0) { /* then if this is the initial read */ MT_SET_PNU (uptr); /* then set position not updated */ - r = MTSE_EOM; /* and report the end-of-medium and quit */ + status = MTSE_EOM; /* and report the end-of-medium and quit */ break; } else { /* otherwise some gap has already been skipped */ if (sizeof_gap > 0) /* so if detection is enabled */ - r = MTSE_RUNAWAY; /* then report a tape runaway */ + status = MTSE_RUNAWAY; /* then report a tape runaway */ else /* otherwise report the physical EOF */ - r = MTSE_EOM; /* as the end-of-medium */ + status = MTSE_EOM; /* as the end-of-medium */ break; } @@ -784,16 +801,16 @@ switch (f) { /* the read method depen if (*bc == MTR_EOM) { /* if an end-of-medium marker is seen */ if (sizeof_gap > 0) /* then if detection is enabled */ - r = MTSE_RUNAWAY; /* then report a tape runaway */ + status = MTSE_RUNAWAY; /* then report a tape runaway */ else /* otherwise report the physical EOF */ - r = MTSE_EOM; /* as the end-of-medium */ + status = MTSE_EOM; /* as the end-of-medium */ break; } uptr->pos = uptr->pos + sizeof (t_mtrlnt); /* space over the marker */ if (*bc == MTR_TMK) { /* if the value is a tape mark */ - r = MTSE_TMK; /* then quit with tape mark status */ + status = MTSE_TMK; /* then quit with tape mark status */ break; } @@ -801,80 +818,111 @@ switch (f) { /* the read method depen runaway_counter -= sizeof_gap; /* then decrement the gap counter */ else if (*bc == MTR_FHGAP) { /* otherwise if the value if a half gap */ - uptr->pos = uptr->pos - sizeof (t_mtrlnt) / 2; /* then back up */ - sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* to resync */ - bufcntr = bufcap; /* mark the buffer as invalid to force a read */ + uptr->pos = uptr->pos - sizeof (t_mtrlnt) / 2; /* then back up and resync */ - *bc = MTR_GAP; /* reset the marker */ - runaway_counter -= sizeof_gap / 2; /* and decrement the gap counter */ + 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 */ + break; + } + + bufcntr = bufcap; /* mark the buffer as invalid to force a read */ + + *bc = MTR_GAP; /* reset the marker */ + runaway_counter -= sizeof_gap / 2; /* and decrement the gap counter */ } - else { /* otherwise it's a record marker */ - if (bufcntr < bufcap) /* if the position is within the buffer */ - sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* then seek to the data area */ + else { /* otherwise it's a record marker */ + if (bufcntr < bufcap /* if the position is within the buffer */ + && sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) { /* then seek to the data area; if it fails */ + status = sim_tape_ioerr (uptr); /* then quit with I/O error status */ + break; + } - 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 */ + 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 */ } } while (*bc == MTR_GAP && runaway_counter > 0); /* continue until data or runaway occurs */ - if (r == MTSE_OK && runaway_counter <= 0) /* if a tape runaway occurred */ - r = MTSE_RUNAWAY; /* then report it */ + if (runaway_counter <= 0) /* if a tape runaway occurred */ + status = MTSE_RUNAWAY; /* then report it */ break; /* otherwise the operation succeeded */ case MTUF_F_TPC: sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref); *bc = tpcbc; /* save rec lnt */ + if (ferror (uptr->fileref)) { /* error? */ MT_SET_PNU (uptr); /* pos not upd */ - return sim_tape_ioerr (uptr); + status = sim_tape_ioerr (uptr); } - if (feof (uptr->fileref)) { /* eof? */ + else if (feof (uptr->fileref)) { /* eof? */ MT_SET_PNU (uptr); /* pos not upd */ - r = MTSE_EOM; - break; + status = MTSE_EOM; + } + else { + uptr->pos = uptr->pos + sizeof (t_tpclnt); /* spc over reclnt */ + if (tpcbc == TPC_TMK) /* tape mark? */ + status = MTSE_TMK; + else + uptr->pos = uptr->pos + ((tpcbc + 1) & ~1); /* spc over record */ } - uptr->pos = uptr->pos + sizeof (t_tpclnt); /* spc over reclnt */ - if (tpcbc == TPC_TMK) /* tape mark? */ - r = MTSE_TMK; - uptr->pos = uptr->pos + ((tpcbc + 1) & ~1); /* spc over record */ break; case MTUF_F_P7B: for (sbc = 0, all_eof = 1; ; sbc++) { /* loop thru record */ sim_fread (&c, sizeof (uint8), 1, uptr->fileref); + if (ferror (uptr->fileref)) { /* error? */ MT_SET_PNU (uptr); /* pos not upd */ - return sim_tape_ioerr (uptr); + status = sim_tape_ioerr (uptr); + break; } - if (feof (uptr->fileref)) { /* eof? */ + else if (feof (uptr->fileref)) { /* eof? */ if (sbc == 0) /* no data? eom */ - return MTSE_EOM; + status = MTSE_EOM; break; /* treat like eor */ } - if ((sbc != 0) && (c & P7B_SOR)) /* next record? */ + else if ((sbc != 0) && (c & P7B_SOR)) /* next record? */ break; - if ((c & P7B_DPAR) != P7B_EOF) + else if ((c & P7B_DPAR) != P7B_EOF) all_eof = 0; } - *bc = sbc; /* save rec lnt */ - sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */ - uptr->pos = uptr->pos + sbc; /* spc over record */ - if (all_eof) /* tape mark? */ - r = MTSE_TMK; + + if (status == MTSE_OK) { + *bc = sbc; /* save rec lnt */ + sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* for read */ + uptr->pos = uptr->pos + sbc; /* spc over record */ + if (all_eof) /* tape mark? */ + status = MTSE_TMK; + } break; default: - return MTSE_FMT; - } -sim_debug (MTSE_DBG_STR, ctx->dptr, "rd_lnt: st: %d, lnt: %d, pos: %" T_ADDR_FMT "u\n", r, *bc, uptr->pos); -return r; + status = MTSE_FMT; + } + +return status; } -/* Read record length reverse (internal routine) +static t_stat sim_tape_rdrlfwd (UNIT *uptr, t_mtrlnt *bc) +{ +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; +t_stat status; + +if (ctx == NULL) /* if not properly attached? */ + return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ + +status = sim_tape_rdlntf (uptr, bc); /* read the record length */ + +sim_debug (MTSE_DBG_STR, ctx->dptr, "rd_lnt: st: %d, lnt: %d, pos: %" T_ADDR_FMT "u\n", status, *bc, uptr->pos); + +return status; +} + +/* Read record length reverse (internal routine). Inputs: uptr = pointer to tape unit @@ -899,39 +947,45 @@ return r; giving the reason that the operation did not succeed and the tape position is as indicated above. - See the notes at "sim_tape_rdlntf" and "sim_tape_wrgap" regarding tape - runaway and the erase gap implementation, respectively. + + Implementation notes: + + 1. The "sim_fread" call cannot return 0 in the absence of an error + condition. The preceding "sim_tape_bot" test ensures that "pos" >= 4, so + "sim_fseek" will back up at least that far, so "sim_fread" will read at + least one element. If the call returns zero, an error must have + occurred, so the "ferror" call must succeed. + + 2. See the notes at "sim_tape_rdlntf" and "tape_erase_fwd" regarding tape + runaway and the erase gap implementation, respectively. */ static t_stat sim_tape_rdlntr (UNIT *uptr, t_mtrlnt *bc) { -struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; -uint8 c; -t_bool all_eof; -uint32 f = MT_GET_FMT (uptr); -t_addr ppos; +uint8 c; +t_bool all_eof; +uint32 f = MT_GET_FMT (uptr); +t_addr ppos; t_mtrlnt sbc; t_tpclnt tpcbc; t_mtrlnt buffer [256]; /* local tape buffer */ -uint32 bufcntr, bufcap; /* buffer counter and capacity */ -int32 runaway_counter, sizeof_gap; /* bytes remaining before runaway and bytes per gap */ -t_stat r = MTSE_OK; +uint32 bufcntr, bufcap; /* buffer counter and capacity */ +int32 runaway_counter, sizeof_gap; /* bytes remaining before runaway and bytes per gap */ +t_stat status = MTSE_OK; MT_CLR_PNU (uptr); /* clear the position-not-updated flag */ if ((uptr->flags & UNIT_ATT) == 0) /* if the unit is not attached */ return MTSE_UNATT; /* then quit with an error */ -if (ctx == NULL) /* if not properly attached? */ - return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ if (sim_tape_bot (uptr)) /* if the unit is positioned at the BOT */ - return MTSE_BOT; /* then reading backward is not possible */ + status = MTSE_BOT; /* then reading backward is not possible */ -switch (f) { /* the read method depends on the tape format */ +else switch (f) { /* otherwise the read method depends on the tape format */ case MTUF_F_STD: case MTUF_F_E11: - runaway_counter = 25 * 12 * bpi [MT_DENS (uptr->dynflags)]; /* set largest legal gap size in bytes */ + runaway_counter = 25 * 12 * bpi [MT_DENS (uptr->dynflags)]; /* set the largest legal gap size in bytes */ if (runaway_counter == 0) { /* if tape density has not been not set */ sizeof_gap = 0; /* then disable runaway detection */ @@ -942,35 +996,40 @@ switch (f) { /* the read method depen sizeof_gap = sizeof (t_mtrlnt); /* set the size of the gap */ bufcntr = 0; /* force an initial read */ - bufcap = 1; /* but of just one metadata marker */ + bufcap = 0; /* but of just one metadata marker */ - do { /* loop until a record, gap, or error seen */ + do { /* loop until a record, gap, or error is seen */ if (bufcntr == 0) { /* if the buffer is empty then refill it */ if (sim_tape_bot (uptr)) { /* if the search has backed into the BOT */ - r = MTSE_BOT; /* then quit with an error */ + status = MTSE_BOT; /* then quit with an error */ break; } - else if (uptr->pos < sizeof (buffer)) /* if less than a full buffer remains */ + else if (bufcap == 0) /* otherwise if this is the initial read */ + bufcap = 1; /* then start with just one marker */ + + else if (uptr->pos < sizeof (buffer)) /* otherwise if less than a full buffer remains */ bufcap = (uint32) uptr->pos /* then reduce the capacity accordingly */ / sizeof (t_mtrlnt); - sim_fseek (uptr->fileref, /* seek back to the location */ - uptr->pos - bufcap * sizeof (t_mtrlnt), /* corresponding to the start */ - SEEK_SET); /* of the buffer */ - - bufcntr = sim_fread (buffer, sizeof (t_mtrlnt), /* fill the buffer */ - bufcap, uptr->fileref); /* with tape metadata */ - - if (ferror (uptr->fileref)) { /* if a file I/O error occurred */ - MT_SET_PNU (uptr); /* then set position not updated */ - r = sim_tape_ioerr (uptr); /* report the error and quit */ - break; - } - else /* otherwise reset the capacity */ 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 */ + break; + } + + bufcntr = sim_fread (buffer, sizeof (t_mtrlnt), /* fill the buffer */ + bufcap, uptr->fileref); /* with tape metadata */ + + if (ferror (uptr->fileref)) { /* if a file I/O error occurred */ + status = sim_tape_ioerr (uptr); /* then report the error and quit */ + break; + } } *bc = buffer [--bufcntr]; /* store the metadata marker value */ @@ -978,7 +1037,7 @@ switch (f) { /* the read method depen uptr->pos = uptr->pos - sizeof (t_mtrlnt); /* backspace over the marker */ if (*bc == MTR_TMK) { /* if the marker is a tape mark */ - r = MTSE_TMK; /* then quit with tape mark status */ + status = MTSE_TMK; /* then quit with tape mark status */ break; } @@ -998,14 +1057,19 @@ switch (f) { /* the read method depen 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 */ - sim_fseek (uptr->fileref, /* seek to the data area */ - uptr->pos + sizeof (t_mtrlnt), SEEK_SET); + + 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)) { + status = sim_tape_ioerr (uptr); + break; + } } } while (*bc == MTR_GAP && runaway_counter > 0); /* continue until data or runaway occurs */ - if (r == MTSE_OK && runaway_counter <= 0) /* if a tape runaway occurred */ - r = MTSE_RUNAWAY; /* then report it */ + if (runaway_counter <= 0) /* if a tape runaway occurred */ + status = MTSE_RUNAWAY; /* then report it */ break; /* otherwise the operation succeeded */ @@ -1014,47 +1078,70 @@ switch (f) { /* the read method depen sim_fseek (uptr->fileref, ppos, SEEK_SET); /* position */ sim_fread (&tpcbc, sizeof (t_tpclnt), 1, uptr->fileref); *bc = tpcbc; /* save rec lnt */ + if (ferror (uptr->fileref)) /* error? */ - return sim_tape_ioerr (uptr); - if (feof (uptr->fileref)) { /* eof? */ - r = MTSE_EOM; - break; + status = sim_tape_ioerr (uptr); + else if (feof (uptr->fileref)) /* eof? */ + status = MTSE_EOM; + else { + uptr->pos = ppos; /* spc over record */ + if (*bc == MTR_TMK) /* tape mark? */ + status = MTSE_TMK; + else + sim_fseek (uptr->fileref, uptr->pos + sizeof (t_tpclnt), SEEK_SET); } - uptr->pos = ppos; /* spc over record */ - if (*bc == MTR_TMK) { /* tape mark? */ - r = MTSE_TMK; - break; - } - sim_fseek (uptr->fileref, uptr->pos + sizeof (t_tpclnt), SEEK_SET); break; case MTUF_F_P7B: for (sbc = 1, all_eof = 1; (t_addr) sbc <= uptr->pos ; sbc++) { sim_fseek (uptr->fileref, uptr->pos - sbc, SEEK_SET); sim_fread (&c, sizeof (uint8), 1, uptr->fileref); - if (ferror (uptr->fileref)) /* error? */ - return sim_tape_ioerr (uptr); - if (feof (uptr->fileref)) { /* eof? */ - r = MTSE_EOM; + + if (ferror (uptr->fileref)) { /* error? */ + status = sim_tape_ioerr (uptr); break; } - if ((c & P7B_DPAR) != P7B_EOF) - all_eof = 0; - if (c & P7B_SOR) /* start of record? */ + else if (feof (uptr->fileref)) { /* eof? */ + status = MTSE_EOM; break; + } + else { + if ((c & P7B_DPAR) != P7B_EOF) + all_eof = 0; + if (c & P7B_SOR) /* start of record? */ + break; + } + } + + if (status == MTSE_OK) { + 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) /* tape mark? */ + status = MTSE_TMK; } - 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) /* tape mark? */ - r = MTSE_TMK; break; default: - return MTSE_FMT; + status = MTSE_FMT; } -sim_debug (MTSE_DBG_STR, ctx->dptr, "rd_lnt: st: %d, lnt: %d, pos: %" T_ADDR_FMT "u\n", r, *bc, uptr->pos); -return r; + +return status; +} + +static t_stat sim_tape_rdrlrev (UNIT *uptr, t_mtrlnt *bc) +{ +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; +t_stat status; + +if (ctx == NULL) /* if not properly attached? */ + return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ + +status = sim_tape_rdlntr (uptr, bc); /* read the record length */ + +sim_debug (MTSE_DBG_STR, ctx->dptr, "rd_lnt: st: %d, lnt: %d, pos: %" T_ADDR_FMT "u\n", status, *bc, uptr->pos); + +return status; } /* Read record forward @@ -1091,7 +1178,8 @@ if (ctx == NULL) /* if not properly attac sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rdrecf(unit=%d, buf=%p, max=%d)\n", (int)(uptr-ctx->dptr->units), buf, max); opos = uptr->pos; /* old position */ -if (MTSE_OK != (st = sim_tape_rdlntf (uptr, &tbc))) /* read rec lnt */ +st = sim_tape_rdrlfwd (uptr, &tbc); /* read rec lnt */ +if (st != MTSE_OK) return st; *bc = rbc = MTR_L (tbc); /* strip error flag */ if (rbc > max) { /* rec out of range? */ @@ -1099,7 +1187,7 @@ 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 */ +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; @@ -1156,12 +1244,13 @@ if (ctx == NULL) /* if not properly attac return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_rdrecr(unit=%d, buf=%p, max=%d)\n", (int)(uptr-ctx->dptr->units), buf, max); -if (MTSE_OK != (st = sim_tape_rdlntr (uptr, &tbc))) /* read rec lnt */ +st = sim_tape_rdrlrev (uptr, &tbc); /* read rec lnt */ +if (st != MTSE_OK) return st; *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 */ +i = (t_mtrlnt) sim_fread (buf, sizeof (uint8), rbc, uptr->fileref); /* read record */ if (ferror (uptr->fileref)) /* error? */ return sim_tape_ioerr (uptr); for ( ; i < rbc; i++) /* fill with 0's */ @@ -1363,31 +1452,33 @@ AIO_CALL(TOP_WEMR, NULL, NULL, NULL, 0, 0, 0, 0, NULL, callback); return r; } -/* Write erase gap - Inputs: - uptr = pointer to tape unit - gaplen = length of gap in tenths of an inch +/* Erase a gap in the forward direction (internal routine). - Outputs: - status = operation status + An erase gap is written in the forward direction on the tape unit specified + by "uptr" for the number of bytes specified by "bc". The status of the + operation is returned, and the file position is altered as follows: - 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 + Exit Condition File Position + ------------------ ------------------ + unit unattached unchanged + unsupported format unchanged + write protected unchanged + read error unchanged, PNU set + write error unchanged, PNU set + gap written updated + If the requested byte count equals the metadatum size, then the routine + succeeds only if it can overlay a single metadatum (i.e., a tape mark, an + end-of-medium marker, or an existing erase gap marker); otherwise, the file + position is not altered, PNU is set, and MTSE_INVRL (invalid record length) + status is returned. 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 currently - supported only in SIMH (MTUF_F_STD) tape format. + value repeated throughout the gap. The 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. 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 @@ -1397,14 +1488,6 @@ return r; 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 tape - density in bits per inch 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 @@ -1418,8 +1501,7 @@ return r; 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 by rewriting the leading and trailing length words - appropriately. + truncated by rewriting the leading and trailing length words 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 @@ -1452,34 +1534,31 @@ return r; 0xFFFF0000:0xFFFF00FF - reserved (indicates half-gap in reverse reads) 0xFFFF8000:0xFFFF80FF - reserved (indicates half-gap in reverse reads) - If the tape density has been set via a previous sim_tape_set_dens call, and - the tape format is set to SIMH format, then this routine will write a gap of - the appropriate size. If the density has not been set, then no action will - be taken, and either MTSE_IOERR or MTSE_OK status will be returned, depending - on whether SIMH or another format is selected, respectively. A simulator - that calls this routine must set the density beforehand; failure to do so is - an error. However, calling while another format is enabled is OK and is - treated as a no-operation. This allows a device simulator that supports - writing erase gaps to use the same code without worrying about the tape - format currently selected by the user. + If the current tape format supports erase gaps, then this routine will write + a gap of the requested size. If the format does not, then no action will be + taken, and MTSE_OK status will be returned. This allows a device simulator + that supports writing erase gaps to use the same code without worrying about + the tape format currently selected by the user. A request for an erase gap + of zero length also succeeds with no action taken. + + + Implementation notes: + + 1. Erase gaps are currently supported only in SIMH (MTUF_F_STD) tape format. */ -t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen) +static t_stat tape_erase_fwd (UNIT *uptr, t_mtrlnt gap_size) { -struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; -t_stat st; +size_t xfer; +t_stat st; t_mtrlnt meta, sbc, new_len, rec_size; -t_addr gap_pos = uptr->pos; -uint32 file_size, marker_count, tape_density; -int32 gap_needed; -uint32 gap_alloc = 0; /* gap currently allocated from the tape */ -const uint32 format = MT_GET_FMT (uptr); /* tape format */ -const uint32 meta_size = sizeof (t_mtrlnt); /* bytes per metadatum */ -const uint32 min_rec_size = 2 + sizeof (t_mtrlnt) * 2; /* smallest data record size */ - -if (ctx == NULL) /* if not properly attached? */ - return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ -sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrgap(unit=%d, gaplen=%u)\n", (int)(uptr-ctx->dptr->units), gaplen); +uint32 file_size, marker_count; +int32 gap_needed = (int32) gap_size; /* the gap remaining to be allocated from the tape */ +uint32 gap_alloc = 0; /* the gap currently allocated from the tape */ +const t_addr gap_pos = uptr->pos; /* the file position where the gap will start */ +const uint32 format = MT_GET_FMT (uptr); /* the tape format */ +const uint32 meta_size = sizeof (t_mtrlnt); /* the number of bytes per metadatum */ +const uint32 min_rec_size = 2 + sizeof (t_mtrlnt) * 2; /* the smallest data record size */ MT_CLR_PNU (uptr); @@ -1489,39 +1568,45 @@ 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 */ -tape_density = bpi [MT_DENS (uptr->dynflags)]; /* get the density of the tape */ - -if (format != MTUF_F_STD) /* if erase gaps aren't supported by the format */ +else if (gap_size == 0 || format != MTUF_F_STD) /* otherwise if zero length or gaps aren't supported */ return MTSE_OK; /* then take no action */ -else if (tape_density == 0) /* otherwise if the density is not set */ - return MTSE_IOERR; /* then report an I/O error */ -else /* otherwise */ - gap_needed = (gaplen * tape_density) / 10; /* determine the gap size needed in bytes */ -file_size = sim_fsize (uptr->fileref); /* get file size */ -sim_fseek (uptr->fileref, uptr->pos, SEEK_SET); /* position tape */ +file_size = sim_fsize (uptr->fileref); /* get the file size */ -/* Read tape records and allocate to gap until amount required is consumed. +if (sim_fseek (uptr->fileref, uptr->pos, SEEK_SET)) { /* 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 */ + } - Read next metadatum from tape: +/* Read tape records and allocate them to the gap until the amount required is + consumed. + + Read the 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. + Loop until the bytes needed = 0. */ do { - sim_fread (&meta, meta_size, 1, uptr->fileref); /* read metadatum */ + xfer = sim_fread (&meta, meta_size, 1, uptr->fileref); /* read a 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 */ + + else if (xfer != 1 && feof (uptr->fileref) == 0) { /* otherwise if a partial metadatum was read */ + uptr->pos = gap_pos; /* then restore the original position */ + MT_SET_PNU (uptr); /* set the position-not-updated flag */ + return MTSE_INVRL; /* and return an invalid record length error */ + } + + else /* otherwise we had a good read */ + uptr->pos = uptr->pos + meta_size; /* so move the tape over the datum */ if (feof (uptr->fileref) || (meta == MTR_EOM)) { /* at eof or eom? */ gap_alloc = gap_alloc + gap_needed; /* allocate remainder */ @@ -1533,17 +1618,25 @@ do { gap_needed = gap_needed - meta_size; /* reduce requirement */ } + else if (gap_size == meta_size) { /* otherwise if the request is for a single metadatum */ + uptr->pos = gap_pos; /* then restore the original position */ + MT_SET_PNU (uptr); /* set the position-not-updated flag */ + return MTSE_INVRL; /* and return an invalid record length error */ + } + 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 */ + + 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 */ + 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 */ + 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: @@ -1552,13 +1645,17 @@ do { 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 */ + + 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 */ + gap_alloc = gap_alloc + rec_size; /* allocate record */ gap_needed = gap_needed - rec_size; /* reduce requirement */ } @@ -1586,16 +1683,18 @@ do { } } } -while (gap_needed > 0); +while (gap_needed > 0); /* loop until all of the gap has been allocated */ 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 */ } @@ -1604,6 +1703,7 @@ 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 */ @@ -1614,6 +1714,169 @@ while (--marker_count > 0); return MTSE_OK; } +/* Erase a gap in the reverse direction (internal routine). + + An erase gap is written in the reverse direction on the tape unit specified + by "uptr" for the number of bytes specified by "bc". The status of the + operation is returned, and the file position is altered as follows: + + Exit Condition File Position + ------------------ ------------------ + unit unattached unchanged + unsupported format unchanged + write protected unchanged + read error unchanged, PNU set + write error unchanged, PNU set + gap written updated + + If the requested byte count equals the metadatum size, then the routine + succeeds only if it can overlay a single metadatum (i.e., a tape mark or an + existing erase gap marker); otherwise, the file position is not altered, and + MTSE_INVRL (invalid record length) status is returned. + + + Implementation notes: + + 1. Erase gaps are currently supported only in SIMH (MTUF_F_STD) tape format. + + 2. Erasing a record in the reverse direction currently succeeds only if the + gap requested occupies the same space as the record located immediately + before the current file position. This limitation may be lifted in a + future update. + + 3. The "sim_fread" call cannot return 0 in the absence of an error + condition. The preceding "sim_tape_bot" test ensures that "pos" >= 4, so + "sim_fseek" will back up at least that far, so "sim_fread" will read at + least one element. If the call returns zero, an error must have + occurred, so the "ferror" call must succeed. +*/ + +static t_stat tape_erase_rev (UNIT *uptr, t_mtrlnt gap_size) +{ +const uint32 format = MT_GET_FMT (uptr); /* the tape format */ +const uint32 meta_size = sizeof (t_mtrlnt); /* the number of bytes per metadatum */ +t_stat status; +t_mtrlnt rec_size, metadatum; +t_addr gap_pos; +size_t xfer; + +MT_CLR_PNU (uptr); /* clear the position-not-updated flag */ + +if ((uptr->flags & UNIT_ATT) == 0) /* if the unit is not attached */ + return MTSE_UNATT; /* then we cannot proceed */ + +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 */ + return MTSE_OK; /* then take no action */ + +gap_pos = uptr->pos; /* save the starting position */ + +if (gap_size == meta_size) { /* if the request is for a single metadatum */ + if (sim_tape_bot (uptr)) /* then if the unit is positioned at the BOT */ + return MTSE_BOT; /* then erasing backward is not possible */ + 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 */ + return sim_tape_ioerr (uptr); /* then quit with I/O error status */ + + sim_fread (&metadatum, meta_size, 1, uptr->fileref); /* read a metadatum */ + + if (ferror (uptr->fileref)) /* if a file I/O error occurred */ + 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 */ + return sim_tape_ioerr (uptr); /* then quit with I/O error status */ + + else { /* otherwise */ + metadatum = MTR_GAP; /* replace it with an erase gap marker */ + + 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 */ + return sim_tape_ioerr (uptr); /* report the error and quit */ + else /* otherwise the write succeeded */ + status = MTSE_OK; /* so return success */ + } + + else if (metadatum == MTR_GAP) /* otherwise if a gap already exists */ + status = MTSE_OK; /* then take no additional action */ + + else { /* otherwise a data record is present */ + uptr->pos = gap_pos; /* so restore the starting position */ + return MTSE_INVRL; /* and fail with invalid record length status */ + } + } + +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 */ + gap_pos = uptr->pos; /* then save the gap start position */ + + status = tape_erase_fwd (uptr, gap_size); /* erase the record */ + + if (status == MTSE_OK) /* if the gap write succeeded */ + uptr->pos = gap_pos; /* the reposition back to the start of the gap */ + } + + else { /* otherwise the read failed or is the wrong size */ + uptr->pos = gap_pos; /* so restore the starting position */ + + if (status != MTSE_OK) /* if the record was not found */ + return status; /* then return the failure reason */ + else /* otherwise the record is the wrong size */ + return MTSE_INVRL; /* so report an invalid record length */ + } + } + +return status; /* return the status of the erase operation */ +} + +/* Write an erase gap. + + An erase gap is written in on the tape unit specified by "uptr" for the + length specified by "gap_size" in tenths of an inch, and the status of the + operation is returned. The tape density must have been set via a previous + sim_tape_set_dens call; if it has not, then no action is taken, and + MTSE_IOERR is returned. + + If the requested gap length is zero, or the tape format currently selected + does not support erase gaps, the call succeeds with no action taken. This + allows a device simulator that supports writing erase gaps to use the same + code without worrying about the tape format currently selected by the user. + + Because SIMH tape images do not carry physical parameters (e.g., recording + density), overwriting a tape image file containing a gap 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 tape density in bits per inch used + during writing. +*/ + +t_stat sim_tape_wrgap (UNIT *uptr, uint32 gaplen) +{ +struct tape_context *ctx = (struct tape_context *)uptr->tape_ctx; +const uint32 density = bpi [MT_DENS (uptr->dynflags)]; /* the tape density in bits per inch */ +const uint32 byte_length = (gaplen * density) / 10; /* the size of the requested gap in bytes */ + +if (ctx == NULL) /* if not properly attached? */ + return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ + +sim_debug (ctx->dbit, ctx->dptr, "sim_tape_wrgap(unit=%d, gaplen=%u)\n", (int)(uptr-ctx->dptr->units), gaplen); + +if (density == 0) /* if the density has not been set */ + return MTSE_IOERR; /* then report an I/O error */ +else /* otherwise */ + return tape_erase_fwd (uptr, byte_length); /* erase the requested gap size in bytes */ +} + t_stat sim_tape_wrgap_a (UNIT *uptr, uint32 gaplen, TAPE_PCALLBACK callback) { t_stat r = MTSE_OK; @@ -1632,6 +1895,11 @@ return r; metadata. This function may be used to erase a record of length "n" in place by requesting a gap of length "n". After erasure, the tape will be positioned at the end of the gap. + + If a length of 0 is specified, then the metadatum marker at the current tape + position will be erased. If the tape is not positioned at a metadatum + marker, the routine fails with MTSE_INVRL, and the tape position is + unchanged. */ t_stat sim_tape_errecf (UNIT *uptr, t_mtrlnt bc) @@ -1639,7 +1907,10 @@ t_stat sim_tape_errecf (UNIT *uptr, t_mtrlnt bc) const t_mtrlnt meta_size = sizeof (t_mtrlnt); /* the number of bytes per metadatum */ const t_mtrlnt gap_size = bc + 2 * meta_size; /* the requested gap size in bytes */ -return MTSE_IOERR; /* stub return */ +if (bc == 0) /* if a zero-length erase is requested */ + return tape_erase_fwd (uptr, meta_size); /* then erase a metadatum marker */ +else /* otherwise */ + return tape_erase_fwd (uptr, gap_size); /* erase the requested gap */ } /* Erase a record reverse. @@ -1651,6 +1922,11 @@ return MTSE_IOERR; /* stub return */ metadata. This function may be used to erase a record of length "n" in place by requesting a gap of length "n". After erasure, the tape will be positioned at the start of the gap. + + If a length of 0 is specified, then the metadatum marker preceding the + current tape position will be erased. If the tape is not positioned after a + metadatum marker, the routine fails with MTSE_INVRL, and the tape position is + unchanged. */ t_stat sim_tape_errecr (UNIT *uptr, t_mtrlnt bc) @@ -1658,7 +1934,10 @@ t_stat sim_tape_errecr (UNIT *uptr, t_mtrlnt bc) const t_mtrlnt meta_size = sizeof (t_mtrlnt); /* the number of bytes per metadatum */ const t_mtrlnt gap_size = bc + 2 * meta_size; /* the requested gap size in bytes */ -return MTSE_IOERR; /* stub return */ +if (bc == 0) /* if a zero-length erase is requested */ + return tape_erase_rev (uptr, meta_size); /* then erase a metadatum marker */ +else /* otherwise */ + return tape_erase_rev (uptr, gap_size); /* erase the requested gap */ } /* Space record forward @@ -1688,7 +1967,7 @@ if (ctx == NULL) /* if not properly attac return sim_messagef (SCPE_IERR, "Bad Attach\n"); /* that's a problem */ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_sprecf(unit=%d)\n", (int)(uptr-ctx->dptr->units)); -st = sim_tape_rdlntf (uptr, bc); /* get record length */ +st = sim_tape_rdrlfwd (uptr, bc); /* get record length */ *bc = MTR_L (*bc); return st; } @@ -1783,7 +2062,7 @@ if (MT_TST_PNU (uptr)) { *bc = 0; return MTSE_OK; } -st = sim_tape_rdlntr (uptr, bc); /* get record length */ +st = sim_tape_rdrlrev (uptr, bc); /* get record length */ *bc = MTR_L (*bc); return st; } @@ -1881,10 +2160,10 @@ sim_debug (ctx->dbit, ctx->dptr, "sim_tape_spfilebyrecf(unit=%d, count=%d, check if (check_leot) { t_mtrlnt rbc; - st = sim_tape_rdlntr (uptr, &rbc); + st = sim_tape_rdrlrev (uptr, &rbc); last_tapemark = (MTSE_TMK == st); if ((st == MTSE_OK) || (st == MTSE_TMK)) - sim_tape_rdlntf (uptr, &rbc); + sim_tape_rdrlfwd (uptr, &rbc); } *skipped = 0; *recsskipped = 0; @@ -2396,7 +2675,7 @@ return SCPE_OK; Set the density of the specified tape unit either to the value supplied or to the value represented by the supplied character string. - + If "desc" is NULL, then "val" must be set to one of the MT_DENS_* constants in sim_tape.h other than MT_DENS_NONE; the supplied value is used as the tape density, and the character string is ignored. Otherwise, "desc" must point diff --git a/sim_timer.c b/sim_timer.c index 99674c2..41c06ed 100644 --- a/sim_timer.c +++ b/sim_timer.c @@ -1602,6 +1602,8 @@ else { sim_throt_val = (uint32) (val * (1 + (sim_idle_rate_ms / val2))); } } + sim_throt_state = SIM_THROT_STATE_THROTTLE; /* force state */ + sim_throt_wait = sim_throt_val; } } sim_register_internal_device (&sim_throttle_dev); /* Register Throttle Device */ @@ -1635,7 +1637,8 @@ else { break; case SIM_THROT_SPC: - fprintf (st, "Throttle: sleep %d ms every %d cycles\n", sim_throt_sleep_time, sim_throt_val); + fprintf (st, "Throttle: %d/%d\n", sim_throt_val, sim_throt_sleep_time); + fprintf (st, "Throttling by sleeping for: %d ms every %d cycles\n", sim_throt_sleep_time, sim_throt_val); break; default: @@ -1684,7 +1687,7 @@ if (sim_throt_type == SIM_THROT_SPC) { /* Non dynamic? */ switch (sim_throt_state) { case SIM_THROT_STATE_INIT: /* take initial reading */ - sim_idle_ms_sleep (sim_idle_rate_ms); /* start on a tick boundart to calibrate */ + sim_idle_ms_sleep (sim_idle_rate_ms); /* start on a tick boundary to calibrate */ sim_throt_ms_start = sim_os_msec (); sim_throt_inst_start = sim_gtime(); sim_throt_wait = SIM_THROT_WST; @@ -2221,8 +2224,8 @@ for (tmr=0; tmr<=SIM_NTIMERS; tmr++) { if (clock_time < 0) clock_time = 0; /* Stop clock assist unit and make sure the clock unit has a tick queued */ - sim_cancel (&sim_timer_units[tmr]); - if (rtc_hz[tmr]) { + if (sim_is_active (&sim_timer_units[tmr])) { + sim_cancel (&sim_timer_units[tmr]); sim_debug (DBG_QUE, &sim_timer_dev, "sim_stop_timer_services() - tmr=%d scheduling %s after %d\n", tmr, sim_uname (sim_clock_unit[tmr]), clock_time); _sim_activate (sim_clock_unit[tmr], clock_time); } diff --git a/sim_tmxr.c b/sim_tmxr.c index 67b3501..b4004f6 100644 --- a/sim_tmxr.c +++ b/sim_tmxr.c @@ -2056,6 +2056,11 @@ if ((lp->txbfd && !lp->notelnet) || (TXBUF_AVAIL(lp) > 1)) {/* room for char (+ sim_oline = save_oline; /* resture output socket */ } sim_exp_check (&lp->expect, chr); /* process expect rules as needed */ + if ((sim_interval > 0) && /* not called within sim_process_event? */ + (lp->txbps) && (lp->txdelta > 1000)) { /* and rate limiting output slower than 1000 cps */ + tmxr_send_buffered_data (lp); /* put data on wire */ + sim_os_ms_sleep((lp->txdelta - 1000) / 1000); /* wait an approximate character delay */ + } return SCPE_OK; /* char sent */ } ++lp->txdrp; lp->xmte = 0; /* no room, dsbl line */ diff --git a/sim_video.c b/sim_video.c index 3dcd05e..b726bf4 100644 --- a/sim_video.c +++ b/sim_video.c @@ -31,8 +31,6 @@ #include "scp.h" t_bool vid_active = FALSE; -int32 vid_mouse_xrel; -int32 vid_mouse_yrel; int32 vid_cursor_x; int32 vid_cursor_y; t_bool vid_mouse_b1 = FALSE; @@ -96,7 +94,7 @@ static char tmp_key_name[40]; * via SDL_GetError(). */ #define SDL_SavePNG(surface, file) \ - SDL_SavePNG_RW(surface, SDL_RWFromFile(file, "wb"), 1) + SDL_SavePNG_RW(surface, SDL_RWFromFile(file, "wb"), 1) /* * SDL_SavePNG -- libpng-based SDL_Surface writer. @@ -523,8 +521,6 @@ if (!vid_active) { vid_height = height; vid_mouse_captured = FALSE; vid_cursor_visible = (vid_flags & SIM_VID_INPUTCAPTURED); - vid_mouse_xrel = 0; - vid_mouse_yrel = 0; vid_key_events.head = 0; vid_key_events.tail = 0; @@ -717,6 +713,9 @@ void vid_set_cursor_position (int32 x, int32 y) int32 x_delta = vid_cursor_x - x; int32 y_delta = vid_cursor_y - y; +if (vid_flags & SIM_VID_INPUTCAPTURED) + return; + if ((x_delta) || (y_delta)) { sim_debug (SIM_VID_DBG_CURSOR, vid_dev, "vid_set_cursor_position(%d, %d) - Cursor position changed\n", x, y); /* Any queued mouse motion events need to have their relative @@ -728,12 +727,8 @@ if ((x_delta) || (y_delta)) { for (i=0; ix_rel, ev->y_rel, ev->x_rel + x_delta, ev->y_rel + y_delta); - vid_mouse_xrel -= ev->x_rel; /* remove previously accumulated relative position */ - vid_mouse_yrel -= ev->y_rel; ev->x_rel += x_delta; ev->y_rel += y_delta; - vid_mouse_xrel += ev->x_rel; /* update cumulative x & y rel */ - vid_mouse_yrel += ev->y_rel; } if (SDL_SemPost (vid_mouse_events.sem)) sim_printf ("%s: vid_set_cursor_position(): SDL_SemPost error: %s\n", vid_dev ? sim_dname(vid_dev) : "Video Device", SDL_GetError()); @@ -1250,13 +1245,11 @@ if (SDL_SemWait (vid_mouse_events.sem) == 0) { event->xrel = (event->x - vid_cursor_x); event->yrel = (event->y - vid_cursor_y); } - vid_mouse_xrel += event->xrel; /* update cumulative x rel */ - vid_mouse_yrel += event->yrel; /* update cumulative y rel */ vid_mouse_b1 = (event->state & SDL_BUTTON(SDL_BUTTON_LEFT)) ? TRUE : FALSE; vid_mouse_b2 = (event->state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) ? TRUE : FALSE; vid_mouse_b3 = (event->state & SDL_BUTTON(SDL_BUTTON_RIGHT)) ? TRUE : FALSE; - sim_debug (SIM_VID_DBG_MOUSE, vid_dev, "Mouse Move Event: pos:(%d,%d) rel:(%d,%d) buttons:(%d,%d,%d) - Count: %d vid_mouse_rel:(%d,%d), vid_cursor:(%d,%d)\n", - event->x, event->y, event->xrel, event->yrel, (event->state & SDL_BUTTON(SDL_BUTTON_LEFT)) ? 1 : 0, (event->state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) ? 1 : 0, (event->state & SDL_BUTTON(SDL_BUTTON_RIGHT)) ? 1 : 0, vid_mouse_events.count, vid_mouse_xrel, vid_mouse_yrel, vid_cursor_x, vid_cursor_y); + sim_debug (SIM_VID_DBG_MOUSE, vid_dev, "Mouse Move Event: pos:(%d,%d) rel:(%d,%d) buttons:(%d,%d,%d) - Count: %d vid_cursor:(%d,%d)\n", + event->x, event->y, event->xrel, event->yrel, (event->state & SDL_BUTTON(SDL_BUTTON_LEFT)) ? 1 : 0, (event->state & SDL_BUTTON(SDL_BUTTON_MIDDLE)) ? 1 : 0, (event->state & SDL_BUTTON(SDL_BUTTON_RIGHT)) ? 1 : 0, vid_mouse_events.count, vid_cursor_x, vid_cursor_y); if (vid_mouse_events.count < MAX_EVENTS) { SIM_MOUSE_EVENT *tail = &vid_mouse_events.events[(vid_mouse_events.tail+MAX_EVENTS-1)%MAX_EVENTS]; @@ -1275,8 +1268,8 @@ if (SDL_SemWait (vid_mouse_events.sem) == 0) { tail->y_rel += ev.y_rel; tail->x_pos = ev.x_pos; tail->y_pos = ev.y_pos; - sim_debug (SIM_VID_DBG_MOUSE, vid_dev, "Mouse Move Event: Coalesced into pending event: (%d,%d) vid_mouse_rel:(%d,%d)\n", - tail->x_rel, tail->y_rel, vid_mouse_xrel, vid_mouse_yrel); + sim_debug (SIM_VID_DBG_MOUSE, vid_dev, "Mouse Move Event: Coalesced into pending event: (%d,%d)\n", + tail->x_rel, tail->y_rel); } else { /* Add a new event */ vid_mouse_events.events[vid_mouse_events.tail++] = ev; @@ -1705,6 +1698,9 @@ while (vid_active) { if (vid_flags & SIM_VID_INPUTCAPTURED) SDL_WarpMouseInWindow (NULL, vid_width/2, vid_height/2); /* center position */ break; + case SDL_WINDOWEVENT_EXPOSED: + vid_update (); + break; } } break; diff --git a/sim_video.h b/sim_video.h index b8ede97..384f1a4 100644 --- a/sim_video.h +++ b/sim_video.h @@ -197,11 +197,6 @@ t_stat vid_screenshot (const char *filename); extern t_bool vid_active; extern uint32 vid_mono_palette[2]; -extern int32 vid_mouse_xrel; /* mouse cumulative x rel */ -extern int32 vid_mouse_yrel; /* mouse cumulative y rel */ -extern t_bool vid_mouse_b1; /* mouse button 1 state */ -extern t_bool vid_mouse_b2; /* mouse button 2 state */ -extern t_bool vid_mouse_b3; /* mouse button 3 state */ void vid_set_cursor_position (int32 x, int32 y); /* cursor position (set by calling code) */ #define SIM_VID_DBG_MOUSE 0x01000000