diff --git a/scp.c b/scp.c index f2cc4909..7d678bae 100644 --- a/scp.c +++ b/scp.c @@ -375,6 +375,8 @@ t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) t_stat show_default (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_break (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_on (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat sim_show_send (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat sim_show_expect (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat show_device (FILE *st, DEVICE *dptr, int32 flag); t_stat show_unit (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag); t_stat show_all_mods (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flg, int32 *toks); @@ -400,7 +402,7 @@ FILE *stdnul; SCHTAB *get_search (char *cptr, int32 radix, SCHTAB *schptr); int32 test_search (t_value val, SCHTAB *schptr); -static const char *get_glyph_gen (const char *iptr, char *optr, char mchar, t_bool uc, t_bool quote); +static const char *get_glyph_gen (const char *iptr, char *optr, char mchar, t_bool uc, t_bool quote, char escape_char); int32 get_switches (char *cptr); char *get_sim_sw (char *cptr); t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr); @@ -438,6 +440,7 @@ t_stat dep_addr (int32 flag, char *cptr, t_addr addr, DEVICE *dptr, UNIT *uptr, int32 dfltinc); void fprint_fields (FILE *stream, t_value before, t_value after, BITFIELD* bitdefs); t_stat step_svc (UNIT *ptr); +t_stat expect_svc (UNIT *ptr); t_stat shift_args (char *do_arg[], size_t arg_count); t_stat set_on (int32 flag, char *cptr); t_stat set_verify (int32 flag, char *cptr); @@ -507,6 +510,7 @@ static t_stat sim_last_cmd_stat; /* Command Status */ static SCHTAB sim_stab; static UNIT sim_step_unit = { UDATA (&step_svc, 0, 0) }; +static UNIT sim_expect_unit = { UDATA (&expect_svc, 0, 0) }; #if defined USE_INT64 static const char *sim_si64 = "64b data"; #else @@ -572,6 +576,7 @@ const struct scp_error { {"AFAIL", "Assertion failed"}, {"INVREM", "Invalid remote console command"}, {"NOTATT", "Not attached"}, + {"EXPECT", "Expect matched"}, }; const size_t size_map[] = { sizeof (int8), @@ -1112,6 +1117,8 @@ static const char simh_help[] = #define HLP_SHOW_MULTIPLEXER "*Commands SHOW" #define HLP_SHOW_CLOCKS "*Commands SHOW" #define HLP_SHOW_ON "*Commands SHOW" +#define HLP_SHOW_SEND "*Commands SHOW" +#define HLP_SHOW_EXPECT "*Commands SHOW" #define HLP_HELP "*Commands HELP" /***************** 80 character line width template *************************/ "2HELP\n" @@ -1364,6 +1371,80 @@ ASSERT failure have several different actions: " If there is no argument, ECHO prints a blank line on the console. This\n" " may be used to provide spacing in the console display or log.\n" /***************** 80 character line width template *************************/ +#define HLP_SEND "*Commands Executing_Command_Files Injecting_Console_Input" + /***************** 80 character line width template *************************/ + "3Injecting Console Input\n" + " The SEND command provides a way to insert input into the console device of\n" + " a simulated system as if it was entered by a user.\n\n" + "++SEND {delay=nn,}\"\" send input string into console\n\n" + " The string argument must be delimited by quote characters. Quotes may\n" + " be either single or double but the opening and closing quote characters\n" + " must match. Data in the string may contain escaped character strings.\n\n" + " The SEND command can also insert input into any serial device on a\n" + " simulated system as if it was entered by a user.\n\n" + "++SEND :line {delay=nn,}\"\"\n\n" + "4Delay\n" + " Specifies a positive integer representing a minimal instruction delay\n" + " before and between characdters being sent. The value specified in a delay\n" + " argument persists across SEND commands. The delay parameter can be set by\n" + " itself with SEND DELAY=n\n" + "4Escaping String Data\n" + " The following character escapes are explicitly supported:\n" + " ++\\r Sends the ASCII Carriage Return character (Decimal value 13)\n" + " ++\\n Sends the ASCII Linefeed character (Decimal value 10)\n" + " ++\\f Sends the ASCII Formfeed character (Decimal value 12)\n" + " ++\\t Sends the ASCII Horizontal Tab character (Decimal value 9)\n" + " ++\\v Sends the ASCII Vertical Tab character (Decimal value 11)\n" + " ++\\b Sends the ASCII Backspace character (Decimal value 8)\n" + " ++\\\\ Sends the ASCII Backslash character (Decimal value 92)\n" + " ++\\' Sends the ASCII Single Quote character (Decimal value 39)\n" + " ++\\\" Sends the ASCII Double Quote character (Decimal value 34)\n" + " ++\\? Sends the ASCII Question Mark character (Decimal value 63)\n" + " ++\\e Sends the ASCII Escape character (Decimal value 27)\n" + " as well as octal character values of the form:\n" + " ++\\n{n{n}} where each n is an octal digit (0-7)\n" + " and hext character values of the form:\n" + " ++\\xh{h} where each h is a hex digit (0-9A-Fa-f)\n" + /***************** 80 character line width template *************************/ +#define HLP_EXPECT "*Commands Executing_Command_Files Reacting_To_Console_Output" + /***************** 80 character line width template *************************/ + "3Reacting To Console Output\n" + " The EXPECT command provides a way to stop execution and take actions\n" + " when specific output has been generated by the simulated system.\n" + " a simulated system as if it was entered by a user.\n\n" + "++EXPECT {dev:line} \"\" {actioncommand {; actioncommand}...}\n\n" + " The string argument must be delimited by quote characters. Quotes may\n" + " be either single or double but the opening and closing quote characters\n" + " must match. Data in the string may contain escaped character strings.\n" + /***************** 80 character line width template *************************/ + "4Switches\n" + " Switches can be used to influence the behavior of EXPECT rules\n\n" + "5-p\n" + " EXPECT rules default to be one shot activities. That is a rule is\n" + " automatically removed when it matches unless it is designated as a\n" + " persistent rule by using a -p switch when the rule is defined.\n" + "5-c\n" + " If an expect rule is defined with the -c switch, it will cause all\n" + " pending expect rules on the current device to be cleared when the rule\n" + " matches data in the device output stream.\n" + "4Escaping String Data\n" + " The following character escapes are explicitly supported:\n" + " ++\\r Sends the ASCII Carriage Return character (Decimal value 13)\n" + " ++\\n Sends the ASCII Linefeed character (Decimal value 10)\n" + " ++\\f Sends the ASCII Formfeed character (Decimal value 12)\n" + " ++\\t Sends the ASCII Horizontal Tab character (Decimal value 9)\n" + " ++\\v Sends the ASCII Vertical Tab character (Decimal value 11)\n" + " ++\\b Sends the ASCII Backspace character (Decimal value 8)\n" + " ++\\\\ Sends the ASCII Backslash character (Decimal value 92)\n" + " ++\\' Sends the ASCII Single Quote character (Decimal value 39)\n" + " ++\\\" Sends the ASCII Double Quote character (Decimal value 34)\n" + " ++\\? Sends the ASCII Question Mark character (Decimal value 63)\n" + " ++\\e Sends the ASCII Escape character (Decimal value 27)\n" + " as well as octal character values of the form:\n" + " ++\\n{n{n}} where each n is an octal digit (0-7)\n" + " and hext character values of the form:\n" + " ++\\xh{h} where each h is a hex digit (0-9A-Fa-f)\n" + /***************** 80 character line width template *************************/ #define HLP_ASSERT "*Commands Executing_Command_Files Testing_Simulator_State" "3Testing Simulator State\n" " The ASSERT command tests a simulator state condition and halts command\n" @@ -1459,6 +1540,9 @@ static CTAB cmd_table[] = { { "IGNORE", &noop_cmd, 0, HLP_IGNORE }, { "ECHO", &echo_cmd, 0, HLP_ECHO }, { "ASSERT", &assert_cmd, 0, HLP_ASSERT }, + { "SEND", &send_cmd, 0, HLP_SEND }, + { "EXPECT", &expect_cmd, 1, HLP_EXPECT }, + { "NOEXPECT", &expect_cmd, 0, HLP_EXPECT }, { "!", &spawn_cmd, 0, HLP_SPAWN }, { "HELP", &help_cmd, 0, HLP_HELP }, { NULL, NULL, 0 } @@ -1536,6 +1620,8 @@ static SHTAB show_glob_tab[] = { { "MULTIPLEXER", &tmxr_show_open_devices, 0, HLP_SHOW_MULTIPLEXER }, { "MUX", &tmxr_show_open_devices, 0, HLP_SHOW_MULTIPLEXER }, { "CLOCKS", &sim_show_timers, 0, HLP_SHOW_CLOCKS }, + { "SEND", &sim_show_send, 0, HLP_SHOW_SEND }, + { "EXPECT", &sim_show_expect, 0, HLP_SHOW_EXPECT }, { "ON", &show_on, 0, HLP_SHOW_ON }, { NULL, NULL, 0 } }; @@ -1755,7 +1841,7 @@ char gbuf[CBUFSIZE]; if ((!cptr) || (*cptr == '\0')) return SCPE_ARG; -cptr = get_glyph_nc (cptr, gbuf, '"'); /* get quote delimted token */ +cptr = get_glyph_nc (cptr, gbuf, '"'); /* get quote delimited token */ if (gbuf[0] == '\0') { /* Token started with quote */ gbuf[sizeof (gbuf)-1] = '\0'; strncpy (gbuf, cptr, sizeof (gbuf)-1); @@ -2472,11 +2558,8 @@ do { } if (*cptr == 0) /* ignore blank */ continue; - if (echo) { /* echo if -v */ - printf("%s> %s\n", do_position(), cptr); - if (sim_log) - fprintf (sim_log, "%s> %s\n", do_position(), cptr); - } + if (echo) /* echo if -v */ + sim_printf("%s> %s\n", do_position(), cptr); if (*cptr == ':') /* ignore label */ continue; cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */ @@ -2528,20 +2611,15 @@ do { if (!echo && !sim_quiet && /* report if not echoing */ !stat_nomessage && /* and not suppressing messages */ !(cmdp && cmdp->message)) { /* and not handling them specially */ - printf("%s> %s\n", do_position(), ocptr); - if (sim_log) - fprintf (sim_log, "%s> %s\n", do_position(), ocptr); + sim_printf("%s> %s\n", do_position(), ocptr); } } if (!stat_nomessage) { /* report error if not suppressed */ if (cmdp && cmdp->message) /* special message handler */ cmdp->message ((!echo && !sim_quiet) ? ocptr : NULL, stat); else - if (stat >= SCPE_BASE) { /* report error if not suppressed */ - printf ("%s\n", sim_error_text (stat)); - if (sim_log) - fprintf (sim_log, "%s\n", sim_error_text (stat)); - } + if (stat >= SCPE_BASE) /* report error if not suppressed */ + sim_printf ("%s\n", sim_error_text (stat)); } if (staying && (sim_on_check[sim_do_depth]) && @@ -2903,6 +2981,141 @@ if (test_search (val, &sim_stab)) /* test condition */ return SCPE_AFAIL; /* condition fails */ } +/* Send command + + Syntax: SEND {Delay=n},"string-to-send" + + Delay - is a positive integer representing a minimal instruction delay + before and between characters being sent. The value specified + in a delay argument persists across SEND commands. The delay + parameter can be set by itself with SEND DELAY=n,"" + String - must be quoted. Quotes may be either single or double but the + opening anc closing quote characters must match. Within quotes + C style character escapes are allowed. + The following character escapes are explicitly supported: + \r Sends the ASCII Carriage Return character (Decimal value 13) + \n Sends the ASCII Linefeed character (Decimal value 10) + \f Sends the ASCII Formfeed character (Decimal value 12) + \t Sends the ASCII Horizontal Tab character (Decimal value 9) + \v Sends the ASCII Vertical Tab character (Decimal value 11) + \b Sends the ASCII Backspace character (Decimal value 8) + \\ Sends the ASCII Backslash character (Decimal value 92) + \' Sends the ASCII Single Quote character (Decimal value 39) + \" Sends the ASCII Double Quote character (Decimal value 34) + \? Sends the ASCII Question Mark character (Decimal value 63) + \e Sends the ASCII Escape character (Decimal value 27) + as well as octal character values of the form: + \n{n{n}} where each n is an octal digit (0-7) + and hext character values of the form: + \xh{h} where each h is a hex digit (0-9A-Fa-f) + */ + +t_stat send_cmd (int32 flag, char *cptr) +{ +char gbuf[CBUFSIZE], *gptr = gbuf, *tptr; +uint8 dbuf[CBUFSIZE], *dptr = dbuf; +uint32 dsize = 0; +uint32 delay = 0; +t_stat r; +SEND *snd; + +tptr = get_glyph (cptr, gbuf, ','); +if (isalpha(gbuf[0]) && (strchr (gbuf, ':'))) { + r = tmxr_locate_line_send (gbuf, &snd); + if (r != SCPE_OK) + return r; + tptr = get_glyph (tptr, gbuf, ','); + } +else + snd = sim_cons_get_send (); + +if ((!strncmp(gbuf, "DELAY=", 6)) && (gbuf[6])) { + delay = (uint32)get_uint (&gbuf[6], 10, 10000000, &r); + if (r != SCPE_OK) + return SCPE_ARG; + cptr = tptr; + } +if (*cptr) { + if ((*cptr != '"') && (*cptr != '\'')) + return SCPE_ARG; /* String must be quote delimited */ + cptr = get_glyph_quoted (cptr, gbuf, 0); + if (*cptr != '\0') + return SCPE_2MARG; /* No more arguments */ + + if (SCPE_OK != sim_decode_quoted_string (gbuf, dbuf, &dsize)) + return SCPE_ARG; + } +if ((dsize == 0) && (delay == 0)) + return SCPE_2FARG; +return sim_send_input (snd, dbuf, dsize, delay); +} + +t_stat sim_show_send (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +char gbuf[CBUFSIZE], *tptr; +t_stat r; +SEND *snd; + +tptr = get_glyph (cptr, gbuf, ','); +if (isalpha(gbuf[0]) && (strchr (gbuf, ':'))) { + r = tmxr_locate_line_send (gbuf, &snd); + if (r != SCPE_OK) + return r; + cptr = tptr; + } +else + snd = sim_cons_get_send (); +if (*cptr) + return SCPE_2MARG; +return sim_show_send_input (st, snd); +} + +t_stat expect_cmd (int32 flag, char *cptr) +{ +char gbuf[CBUFSIZE], *tptr; +t_stat r; +EXPECT *exp; + +tptr = get_glyph (cptr, gbuf, ','); +if (isalpha(gbuf[0]) && (strchr (gbuf, ':'))) { + r = tmxr_locate_line_expect (gbuf, &exp); + if (r != SCPE_OK) + return r; + cptr = tptr; + } +else + exp = sim_cons_get_expect (); +if (flag) + return sim_set_expect (exp, cptr); +else + return sim_set_noexpect (exp, cptr); +} + +t_stat sim_show_expect (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +char gbuf[CBUFSIZE], *tptr; +t_stat r; +EXPECT *exp; + +tptr = get_glyph (cptr, gbuf, ','); +if (isalpha(gbuf[0]) && (strchr (gbuf, ':'))) { + r = tmxr_locate_line_expect (gbuf, &exp); + if (r != SCPE_OK) + return r; + cptr = tptr; + } +else + exp = sim_cons_get_expect (); +if (*cptr && (*cptr != '"') && (*cptr != '\'')) + return SCPE_ARG; /* String must be quote delimited */ +tptr = get_glyph_quoted (cptr, gbuf, 0); +if (*tptr != '\0') + return SCPE_2MARG; /* No more arguments */ +if (*cptr && (cptr[strlen(cptr)-1] != '"') && (cptr[strlen(cptr)-1] != '\'')) + return SCPE_ARG; /* String must be quote delimited */ +return sim_exp_show (st, exp, gbuf); +} + /* Goto command */ @@ -2933,9 +3146,7 @@ while (1) { sim_brk_clract (); /* goto defangs current actions */ sim_do_echo = saved_do_echo; /* restore echo mode */ if (sim_do_echo) /* echo if -v */ - printf("%s> %s\n", do_position(), cbuf); - if (sim_do_echo && sim_log) - fprintf (sim_log, "%s> %s\n", do_position(), cbuf); + sim_printf("%s> %s\n", do_position(), cbuf); return SCPE_OK; } } @@ -3804,12 +4015,17 @@ else { for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = uptr->next) { if (uptr == &sim_step_unit) fprintf (st, " Step timer"); - else if ((dptr = find_dev_from_unit (uptr)) != NULL) { - fprintf (st, " %s", sim_dname (dptr)); - if (dptr->numunits > 1) - fprintf (st, " unit %d", (int32) (uptr - dptr->units)); - } - else fprintf (st, " Unknown"); + else + if (uptr == &sim_expect_unit) + fprintf (st, " Expect fired"); + else + if ((dptr = find_dev_from_unit (uptr)) != NULL) { + fprintf (st, " %s", sim_dname (dptr)); + if (dptr->numunits > 1) + fprintf (st, " unit %d", (int32) (uptr - dptr->units)); + } + else + fprintf (st, " Unknown"); fprintf (st, " at %d\n", accum + uptr->time); accum = accum + uptr->time; } @@ -5261,9 +5477,7 @@ else if ((flag == RU_STEP) || static t_bool not_implemented_message = FALSE; if ((!not_implemented_message) && (flag == RU_NEXT)) { - printf ("This simulator does not have subroutine call detection.\nPerforming a STEP instead\n"); - if (sim_log) - fprintf (sim_log, "This simulator does not have subroutine call detection.\nPerforming a STEP instead\n"); + sim_printf ("This simulator does not have subroutine call detection.\nPerforming a STEP instead\n"); not_implemented_message = TRUE; flag = RU_STEP; } @@ -5453,11 +5667,8 @@ run_cmd_message (const char *unechoed_cmdline, t_stat r) #if defined (VMS) printf ("\n"); #endif -if (unechoed_cmdline) { - printf("%s> %s\n", do_position(), unechoed_cmdline); - if (sim_log) - fprintf (sim_log, "%s> %s\n", do_position(), unechoed_cmdline); - } +if (unechoed_cmdline) + sim_printf("%s> %s\n", do_position(), unechoed_cmdline); fprint_stopped (stdout, r); /* print msg */ if (sim_log) /* log if enabled */ fprint_stopped (sim_log, r); @@ -5531,6 +5742,14 @@ t_stat step_svc (UNIT *uptr) return SCPE_STEP; } +/* Unit service to facilitate expect matching to stop simulation. + Return expect SCP code, will cause simulation to stop */ + +t_stat expect_svc (UNIT *uptr) +{ +return SCPE_EXPECT | (sim_do_echo ? 0 : SCPE_NOMESSAGE); +} + /* Cancel scheduled step service */ t_stat sim_cancel_step (void) @@ -6273,9 +6492,7 @@ while (isspace (*cptr)) /* trim leading spc */ cptr++; if (*cptr == ';') { /* ignore comment */ if (sim_do_echo) /* echo comments if -v */ - printf("%s> %s\n", do_position(), cptr); - if (sim_do_echo && sim_log) - fprintf (sim_log, "%s> %s\n", do_position(), cptr); + sim_printf("%s> %s\n", do_position(), cptr); *cptr = 0; } @@ -6293,26 +6510,36 @@ return cptr; get_glyph_gen get next glyph (general case) Inputs: - iptr = pointer to input string - optr = pointer to output string - mchar = optional end of glyph character - uc = TRUE for convert to upper case (_gen only) - quote = TRUE to allow quote enclosing values (_gen only) + iptr = pointer to input string + optr = pointer to output string + mchar = optional end of glyph character + uc = TRUE for convert to upper case (_gen only) + quote = TRUE to allow quote enclosing values (_gen only) + escape_char = optional escape character within quoted strings (_gen only) + Outputs - result = pointer to next character in input string + result = pointer to next character in input string */ -static const char *get_glyph_gen (const char *iptr, char *optr, char mchar, t_bool uc, t_bool quote) +static const char *get_glyph_gen (const char *iptr, char *optr, char mchar, t_bool uc, t_bool quote, char escape_char) { t_bool quoting = FALSE; +t_bool escaping = FALSE; char quote_char = 0; while ((*iptr != 0) && ((quote && quoting) || ((isspace (*iptr) == 0) && (*iptr != mchar)))) { if (quote) { if (quoting) { - if (*iptr == quote_char) - quoting = FALSE; + if (!escaping) { + if (*iptr == escape_char) + escaping = TRUE; + else + if (*iptr == quote_char) + quoting = FALSE; + } + else + escaping = FALSE; } else { if ((*iptr == '"') || (*iptr == '\'')) { @@ -6336,17 +6563,17 @@ return iptr; char *get_glyph (const char *iptr, char *optr, char mchar) { -return (char *)get_glyph_gen (iptr, optr, mchar, TRUE, FALSE); +return (char *)get_glyph_gen (iptr, optr, mchar, TRUE, FALSE, 0); } char *get_glyph_nc (const char *iptr, char *optr, char mchar) { -return (char *)get_glyph_gen (iptr, optr, mchar, FALSE, FALSE); +return (char *)get_glyph_gen (iptr, optr, mchar, FALSE, FALSE, 0); } char *get_glyph_quoted (const char *iptr, char *optr, char mchar) { -return (char *)get_glyph_gen (iptr, optr, mchar, FALSE, TRUE); +return (char *)get_glyph_gen (iptr, optr, mchar, FALSE, TRUE, '\\'); } /* Trim trailing spaces from a string @@ -6400,7 +6627,7 @@ return FALSE; val = value */ -t_value get_uint (char *cptr, uint32 radix, t_value max, t_stat *status) +t_value get_uint (const char *cptr, uint32 radix, t_value max, t_stat *status) { t_value val; char *tptr; @@ -6472,6 +6699,236 @@ if (term && (*tptr++ != term)) return tptr; } +/* sim_decode_quoted_string + + Inputs: + iptr = pointer to input string + optr = pointer to output buffer + the output buffer must be allocated by the caller + and to avoid overrunat it must be at least as big + as the input string. + + Outputs + result = status of decode SCPE_OK when good, SCPE_ARG otherwise + osize = size of the data in the optr buffer + + The input string must be quoted. Quotes may be either single or + double but the opening anc closing quote characters must match. + Within quotes C style character escapes are allowed. + + The following character escapes are explicitly supported: + \r ASCII Carriage Return character (Decimal value 13) + \n ASCII Linefeed character (Decimal value 10) + \f ASCII Formfeed character (Decimal value 12) + \t ASCII Horizontal Tab character (Decimal value 9) + \v ASCII Vertical Tab character (Decimal value 11) + \b ASCII Backspace character (Decimal value 8) + \\ ASCII Backslash character (Decimal value 92) + \' ASCII Single Quote character (Decimal value 39) + \" ASCII Double Quote character (Decimal value 34) + \? ASCII Question Mark character (Decimal value 63) + \e ASCII Escape character (Decimal value 27) + as well as octal character values of the form: + \n{n{n}} where each n is an octal digit (0-7) + and hext character values of the form: + \xh{h} where each h is a hex digit (0-9A-Fa-f) + +*/ + +t_stat sim_decode_quoted_string (const char *iptr, uint8 *optr, uint32 *osize) +{ +char quote_char; +uint8 *ostart = optr; + +*osize = 0; +if ((strlen(iptr) == 1) || + ((iptr[strlen(iptr)-1] != '"') && (iptr[strlen(iptr)-1] != '\''))) + return SCPE_ARG; /* String must be quote delimited */ +quote_char = *iptr++; /* Save quote character */ +while (iptr[1]) { /* Skip trailing quote */ + if (*iptr != '\\') { + if (*iptr == quote_char) + return SCPE_ARG; /* Imbedded quotes must be escaped */ + *(optr++) = (uint8)(*(iptr++)); + continue; + } + ++iptr; /* Skip backslash */ + switch (*iptr) { + case 'r': /* ASCII Carriage Return character (Decimal value 13) */ + *(optr++) = 13; ++iptr; + break; + case 'n': /* ASCII Linefeed character (Decimal value 10) */ + *(optr++) = 10; ++iptr; + break; + case 'f': /* ASCII Formfeed character (Decimal value 12) */ + *(optr++) = 12; ++iptr; + break; + case 't': /* ASCII Horizontal Tab character (Decimal value 9) */ + *(optr++) = 9; ++iptr; + break; + case 'v': /* ASCII Vertical Tab character (Decimal value 11) */ + *(optr++) = 11; ++iptr; + break; + case 'b': /* ASCII Backspace character (Decimal value 8) */ + *(optr++) = 8; ++iptr; + break; + case '\\': /* ASCII Backslash character (Decimal value 92) */ + *(optr++) = 92; ++iptr; + break; + case 'e': /* ASCII Escape character (Decimal value 27) */ + *(optr++) = 27; ++iptr; + break; + case '\'': /* ASCII Single Quote character (Decimal value 39) */ + *(optr++) = 39; ++iptr; + break; + case '"': /* ASCII Double Quote character (Decimal value 34) */ + *(optr++) = 34; ++iptr; + break; + case '?': /* ASCII Question Mark character (Decimal value 63) */ + *(optr++) = 63; ++iptr; + break; + case '0': case '1': case '2': case '3': case '4': case '5': case '6': case '7': + *optr = *(iptr++) - '0'; + if ((*iptr >= '0') && (*iptr <= '7')) + *optr = ((*optr)<<3) + (*(iptr++) - '0'); + if ((*iptr >= '0') && (*iptr <= '7')) + *optr = ((*optr)<<3) + (*(iptr++) - '0'); + ++optr; + break; + case 'x': + if (1) { + static char *hex_digits = "0123456789ABCDEF"; + const char *c; + + ++iptr; + *optr = 0; + c = strchr (hex_digits, toupper(*iptr)); + if (c) { + *optr = ((*optr)<<4) + (uint8)(c-hex_digits); + ++iptr; + } + c = strchr (hex_digits, toupper(*iptr)); + if (c) { + *optr = ((*optr)<<4) + (uint8)(c-hex_digits); + ++iptr; + } + ++optr; + } + break; + default: + return SCPE_ARG; /* Invalid escape */ + } + } +*optr = '\0'; +*osize = (uint32)(optr-ostart); +return SCPE_OK; +} + +/* sim_decode_quoted_string + + Inputs: + iptr = pointer to input string + optr = pointer to output buffer + the output buffer must be allocated by the caller + and to avoid overrunat it must be at least as big + as the input string. + + Outputs + result = status of decode SCPE_OK when good, SCPE_ARG otherwise + osize = size of the data in the optr buffer + + The input string must be quoted. Quotes may be either single or + double but the opening anc closing quote characters must match. + Within quotes C style character escapes are allowed. + + The following character escapes are explicitly supported: + \r ASCII Carriage Return character (Decimal value 13) + \n ASCII Linefeed character (Decimal value 10) + \f ASCII Formfeed character (Decimal value 12) + \t ASCII Horizontal Tab character (Decimal value 9) + \v ASCII Vertical Tab character (Decimal value 11) + \b ASCII Backspace character (Decimal value 8) + \\ ASCII Backslash character (Decimal value 92) + \' ASCII Single Quote character (Decimal value 39) + \" ASCII Double Quote character (Decimal value 34) + \? ASCII Question Mark character (Decimal value 63) + \e ASCII Escape character (Decimal value 27) + as well as octal character values of the form: + \n{n{n}} where each n is an octal digit (0-7) + and hext character values of the form: + \xh{h} where each h is a hex digit (0-9A-Fa-f) + +*/ + +char *sim_encode_quoted_string (const uint8 *iptr, uint32 size) +{ +uint32 i; +t_bool double_quote_found = FALSE; +t_bool single_quote_found = FALSE; +char quote = '"'; +char *tptr, *optr; + +optr = malloc (4*size + 3); +if (optr == NULL) + return NULL; +tptr = optr; +for (i=0; ityp |= sw; /* set type */ bp->cnt = ncnt; /* set count */ if ((!(sw & BRK_TYP_DYN_ALL)) && /* Not Dynamic and */ @@ -7762,6 +8219,363 @@ if (spc < SIM_BKPT_N_SPC) { return; } +/* Expect package. This code provides a mechanism to stop and control simulator + execution based on traffic coming out of simulated ports and as well as a means + to inject data into those ports. It can conceptually viewed as a string + breakpoint package. + + Expect rules are stored in tables associated with each port which can use this + facility. An expect rule consists of a four entry structure: + + match the expect match string + size the number of bytes in the match string + cnt number of iterations before match is declared + action command string to be executed when match occurs + + An expect rule is contained in an expect match context structure. + + rules the match rules + size the count of match rules + buf the buffer of output data which has produced + buf_ins the buffer insertion point for the next output data + buf_size the buffer size + + The package contains the following public routines: + + sim_set_expect expect command parser and intializer + sim_set_noexpect noexpect command parser + sim_exp_init initialize an expect context + sim_exp_set set or add an expect rule + sim_exp_clr clear or delete an expect rule + sim_exp_clrall clear all expect rules + sim_exp_show show an expect rule + sim_exp_showall show all expect rules + sim_exp_check test for rule match +*/ + +/* Initialize an expect context. */ + +t_stat sim_exp_init (EXPECT *exp) +{ +memset (exp, 0, sizeof(*exp)); +return SCPE_OK; +} + +/* Set expect */ + +t_stat sim_set_expect (EXPECT *exp, char *cptr) +{ +char gbuf[CBUFSIZE], *c1ptr; +int32 cnt = 0; + +if ((cptr == NULL) || (*cptr == 0)) + return SCPE_2FARG; +if (*cptr == '[') { + cnt = (int32) strtotv (cptr + 1, &c1ptr, 10); + if ((cptr == c1ptr) || (*c1ptr != ']')) + return SCPE_ARG; + cptr = c1ptr + 1; + while (isspace(*cptr)) + ++cptr; + } +if ((*cptr != '"') && (*cptr != '\'')) + return SCPE_ARG; /* Expect string must be quote delimited */ +cptr = get_glyph_quoted (cptr, gbuf, 0); +return sim_exp_set (exp, gbuf, cnt, sim_switches, cptr); +} + +/* Clear expect */ + +t_stat sim_set_noexpect (EXPECT *exp, char *cptr) +{ +if (!cptr || !*cptr) + return sim_exp_clrall (exp); /* clear all rules */ +return sim_exp_clr (exp, cptr); /* clear one rule */ +} + +/* Search for an expect rule in an expect context */ + +EXPTAB *sim_exp_fnd (EXPECT *exp, const char *match) +{ +int32 i; +uint8 *match_buf; +uint32 match_size; + +if (!exp->rules) + return NULL; +match_buf = (uint8 *)malloc (strlen (match) + 1); +if (!match_buf) + return NULL; +if (SCPE_OK != sim_decode_quoted_string (match, match_buf, &match_size)) { + free (match_buf); + return NULL; + } +for (i=0; isize; i++) + if ((match_size == exp->rules[i].size) && + (0 == memcmp (exp->rules[i].match, match_buf, match_size))) { + free (match_buf); + return &exp->rules[i]; + } +free (match_buf); +return NULL; +} + +/* Add an expecct rule */ + +EXPTAB *sim_exp_new (EXPECT *exp, const char *match) +{ +uint8 *match_buf; +uint32 match_size; +EXPTAB *ep; +int32 i; + +ep = sim_exp_fnd (exp, match); +if (ep) + return ep; +match_buf = (uint8 *)malloc (strlen (match) + 1); +if (!match_buf) + return NULL; +if (SCPE_OK != sim_decode_quoted_string (match, match_buf, &match_size)) { + free (match_buf); + return NULL; + } +exp->rules = (EXPTAB *) realloc (exp->rules, sizeof (*exp->rules)*(exp->size + 1)); +ep = &exp->rules[exp->size]; +exp->size += 1; +memset (ep, 0, sizeof(*ep)); +ep->match = match_buf; +ep->size = match_size; +/* Make sure that the production buffer is large enough to detect a match for all rules */ +for (i=0; isize; i++) { + if (exp->rules[i].size > exp->buf_size) { + free (exp->buf); + exp->buf = (uint8 *)calloc (exp->rules[i].size, sizeof(*exp->buf)); + exp->buf_size = exp->rules[i].size; + exp->buf_ins = 0; + } + } +return ep; +} + +/* Set a expect rule */ + +t_stat sim_exp_set (EXPECT *exp, const char *match, int32 cnt, int32 switches, char *act) +{ +EXPTAB *ep; +uint8 *match_buf; +uint32 match_size; + +/* Validate the match string */ +match_buf = (uint8 *)malloc (strlen (match) + 1); +if (!match_buf) + return SCPE_MEM; +if (SCPE_OK != sim_decode_quoted_string (match, match_buf, &match_size)) { + free (match_buf); + return SCPE_ARG; + } +free (match_buf); +ep = sim_exp_fnd (exp, match); /* present? */ +if (!ep) /* no, allocate */ + ep = sim_exp_new (exp, match); +if (!ep) /* still no? mem err */ + return SCPE_MEM; +ep->cnt = cnt; /* set proceed count */ +ep->switches = switches; /* set switches */ +if (ep->act) { /* replace old action? */ + free (ep->act); /* deallocate */ + ep->act = NULL; /* now no action */ + } +if (act) while (isspace(*act)) ++act; /* skip leading spaces in action string */ +if ((act != NULL) && (*act != 0)) { /* new action? */ + char *newp = (char *) calloc (strlen (act)+1, sizeof (*act)); /* alloc buf */ + if (newp == NULL) /* mem err? */ + return SCPE_MEM; + strcpy (newp, act); /* copy action */ + ep->act = newp; /* set pointer */ + } +return SCPE_OK; +} + +/* Clear (delete) an expect rule */ + +t_stat sim_exp_clr_tab (EXPECT *exp, EXPTAB *ep) +{ +int32 i; + +if (!ep) /* not there? ok */ + return SCPE_OK; +free (ep->match); /* deallocate match string */ +free (ep->act); /* deallocate action */ +for (i=ep-exp->rules; isize; i++) /* shuffle up remaining rules */ + exp->rules[i] = exp->rules[i+1]; +exp->size -= 1; /* decrement count */ +if (exp->size == 0) { /* No rules left? */ + free (exp->rules); + exp->rules = NULL; + } +return SCPE_OK; +} + +t_stat sim_exp_clr (EXPECT *exp, const char *match) +{ +return sim_exp_clr_tab (exp, sim_exp_fnd (exp, match)); +} + +/* Clear all expect rules */ + +t_stat sim_exp_clrall (EXPECT *exp) +{ +int32 i; + +for (i=0; isize; i++) { + free (exp->rules[i].match); /* deallocate match string */ + free (exp->rules[i].act); /* deallocate action */ + } +free (exp->rules); +exp->rules = NULL; +exp->size = 0; +free (exp->buf); +exp->buf = NULL; +exp->buf_size = 0; +return SCPE_OK; +} + +/* Show an expect rule */ + +t_stat sim_exp_show_tab (FILE *st, EXPECT *exp, EXPTAB *ep) +{ +if (!ep) + return SCPE_OK; +fprintf (st, "EXPECT "); +fprint_buffer_string (st, ep->match, ep->size); +if (ep->cnt > 0) + fprintf (st, " [%d]", ep->cnt); +if (ep->act) + fprintf (st, " %s", ep->act); +fprintf (st, "\n"); +return SCPE_OK; +} + +t_stat sim_exp_show (FILE *st, EXPECT *exp, const char *match) +{ +EXPTAB *ep = sim_exp_fnd (exp, match); + +if (!*match) + return sim_exp_showall (st, exp); +if (!ep) + return SCPE_ARG; +return sim_exp_show_tab (st, exp, ep); +} + +/* Show all expect rules */ + +t_stat sim_exp_showall (FILE *st, EXPECT *exp) +{ +int32 i; + +for (i=0; i < exp->size; i++) + sim_exp_show_tab (st, exp, &exp->rules[i]); +return SCPE_OK; +} + +/* Test for expect match */ + +t_stat sim_exp_check (EXPECT *exp, uint8 data) +{ +int32 i; +EXPTAB *ep; + +if ((!exp) || (!exp->rules)) /* Anying to check? */ + return SCPE_OK; + +exp->buf[exp->buf_ins++] = data; /* Save new data */ + +for (i=0; i < exp->size; i++) { + ep = &exp->rules[i]; + if (exp->buf_ins < ep->size) { + if (memcmp (exp->buf, &ep->match[ep->size-exp->buf_ins], exp->buf_ins)) + continue; + if (memcmp (&exp->buf[exp->buf_size-(ep->size-exp->buf_ins)], ep->match, ep->size-exp->buf_ins)) + continue; + break; + } + else { + if (memcmp (&exp->buf[exp->buf_ins-ep->size], ep->match, ep->size)) + continue; + break; + } + } +if (exp->buf_ins == exp->buf_size) /* At end of match buffer? */ + exp->buf_ins = 0; /* wrap around to beginning */ +if (i != exp->size) { /* Found? */ + if (ep->cnt > 0) + ep->cnt -= 1; + else { + sim_brk_setact (ep->act); /* set up actions */ + if (!(ep->switches & EXP_TYP_PERSIST)) /* One shot expect rule? */ + sim_exp_clr_tab (exp, ep); /* delete it */ + if (ep->switches & EXP_TYP_CLEARALL) /* One shot expect rule? */ + sim_exp_clrall (exp); /* delete all rules */ + sim_activate (&sim_expect_unit, 0); /* schedule simulation stop asap */ + } + } +return SCPE_OK; +} + +/* Queue input data for sending */ + +t_stat sim_send_input (SEND *snd, uint8 *data, size_t size, uint32 delay) +{ +if (snd->extoff != 0) { + if ((snd->insoff-snd->extoff) > 0) + memmove(snd->buffer, snd->buffer+snd->extoff, snd->insoff-snd->extoff); + snd->insoff -= snd->extoff; + snd->extoff -= 0; + } +if (snd->insoff+size > snd->bufsize) { + snd->bufsize = snd->insoff+size; + snd->buffer = realloc(snd->buffer, snd->bufsize); + } +memcpy(snd->buffer+snd->insoff, data, size); +snd->insoff += size; +if (delay) + snd->delay = delay; +snd->next_time = sim_gtime() + snd->delay; +return SCPE_OK; +} + +/* Display console Queued input data status */ + +t_stat sim_show_send_input (FILE *st, SEND *snd) +{ +if (snd->extoff < snd->insoff) { + 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, "Pending Input Delay=%d instructions per character\n", snd->delay); +return SCPE_OK; +} + +/* Poll for Queued input data */ + +t_bool sim_send_poll_data (SEND *snd, t_stat *stat) +{ +if (snd && (snd->extoff < snd->insoff)) { /* pending input characters available? */ + if (sim_gtime() < snd->next_time) /* too soon? */ + *stat = SCPE_OK; + else { + *stat = snd->buffer[snd->extoff++] | SCPE_KFLAG;/* get one */ + snd->next_time = sim_gtime() + snd->delay; + } + return TRUE; + } +return FALSE; +} + + /* Message Text */ const char *sim_error_text (t_stat stat) diff --git a/scp.h b/scp.h index 83a57379..dd93744f 100644 --- a/scp.h +++ b/scp.h @@ -82,6 +82,8 @@ t_stat call_cmd (int32 flag, char *ptr); t_stat on_cmd (int32 flag, char *ptr); t_stat noop_cmd (int32 flag, char *ptr); t_stat assert_cmd (int32 flag, char *ptr); +t_stat send_cmd (int32 flag, char *ptr); +t_stat expect_cmd (int32 flag, char *ptr); t_stat help_cmd (int32 flag, char *ptr); t_stat spawn_cmd (int32 flag, char *ptr); t_stat echo_cmd (int32 flag, char *ptr); @@ -114,9 +116,12 @@ char *get_sim_opt (int32 opt, char *cptr, t_stat *st); char *get_glyph (const char *iptr, char *optr, char mchar); char *get_glyph_nc (const char *iptr, char *optr, char mchar); char *get_glyph_quoted (const char *iptr, char *optr, char mchar); -t_value get_uint (char *cptr, uint32 radix, t_value max, t_stat *status); +t_value get_uint (const char *cptr, uint32 radix, t_value max, t_stat *status); char *get_range (DEVICE *dptr, char *cptr, t_addr *lo, t_addr *hi, uint32 rdx, t_addr max, char term); +t_stat sim_decode_quoted_string (const char *iptr, uint8 *optr, uint32 *osize); +char *sim_encode_quoted_string (const uint8 *iptr, uint32 size); +void fprint_buffer_string (FILE *st, const uint8 *buf, uint32 size); t_value strtotv (const char *cptr, char **endptr, uint32 radix); t_stat fprint_val (FILE *stream, t_value val, uint32 rdx, uint32 wid, uint32 fmt); t_stat sim_print_val (t_value val, uint32 radix, uint32 width, uint32 format); @@ -140,6 +145,17 @@ uint32 sim_brk_test (t_addr bloc, uint32 btyp); void sim_brk_clrspc (uint32 spc); char *sim_brk_clract (void); void sim_brk_setact (const char *action); +t_stat sim_send_input (SEND *snd, uint8 *data, size_t size, uint32 delay); +t_stat sim_show_send_input (FILE *st, SEND *snd); +t_bool sim_send_poll_data (SEND *snd, t_stat *stat); +t_stat sim_set_expect (EXPECT *exp, char *cptr); +t_stat sim_set_noexpect (EXPECT *exp, char *cptr); +t_stat sim_exp_set (EXPECT *exp, const char *match, int32 cnt, int32 switches, char *act); +t_stat sim_exp_clr (EXPECT *exp, const char *match); +t_stat sim_exp_clrall (EXPECT *exp); +t_stat sim_exp_show (FILE *st, EXPECT *exp, const char *match); +t_stat sim_exp_showall (FILE *st, EXPECT *exp); +t_stat sim_exp_check (EXPECT *exp, uint8 data); char *match_ext (char *fnam, char *ext); t_stat show_version (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); diff --git a/sim_console.c b/sim_console.c index 7363a08b..8f86d1ed 100644 --- a/sim_console.c +++ b/sim_console.c @@ -103,7 +103,10 @@ sim_show_cons_log show console log sim_tt_inpcvt convert input character per mode sim_tt_outcvt convert output character per mode - + sim_cons_get_send get console send structure address + sim_cons_get_expect get console expect structure address + sim_show_cons_send_input show pending input data + sim_show_cons_expect show expect rules and state sim_ttinit called once to get initial terminal state sim_ttrun called to put terminal into run state sim_ttcmd called to return terminal to command state @@ -160,6 +163,9 @@ int32 sim_del_char = '\b'; /* delete character */ #else int32 sim_del_char = 0177; #endif +SEND sim_con_send = {SEND_DEFAULT_DELAY}; +EXPECT sim_con_expect; + static t_stat sim_con_poll_svc (UNIT *uptr); /* console connection poll routine */ static t_stat sim_con_reset (DEVICE *dptr); /* console reset routine */ UNIT sim_con_unit = { UDATA (&sim_con_poll_svc, 0, 0) }; /* console connection unit */ @@ -247,6 +253,8 @@ static SHTAB show_con_tab[] = { { "TELNET", &sim_show_telnet, 0 }, { "DEBUG", &sim_show_cons_debug, 0 }, { "BUFFERED", &sim_show_cons_buff, 0 }, + { "EXPECT", &sim_show_cons_expect, 0 }, + { "INPUT", &sim_show_cons_send_input, 0 }, { NULL, NULL, 0 } }; @@ -295,7 +303,7 @@ while (*cptr != 0) { /* do all mods */ *cvptr++ = 0; get_glyph (gbuf, gbuf, 0); /* modifier to UC */ if ((ctptr = find_ctab (set_con_tab, gbuf))) { /* match? */ - r = ctptr->action (ctptr->arg, cvptr); /* do the rest */ + r = ctptr->action (ctptr->arg, cvptr); /* do the rest */ if (r != SCPE_OK) return r; } @@ -1369,6 +1377,13 @@ if (sim_con_ldsc.serport == 0) /* ignore if already closed */ return tmxr_close_master (&sim_con_tmxr); /* close master socket */ } +/* Show the console expect rules and state */ + +t_stat sim_show_cons_expect (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, char *cptr) +{ +return sim_exp_show (st, &sim_con_expect, cptr); +} + /* Log File Open/Close/Show Support */ /* Open log file */ @@ -1511,12 +1526,35 @@ for (i = 0; i < sec; i++) { /* loop */ return SCPE_TTMO; /* timed out */ } +/* Get Send object address for console */ + +SEND *sim_cons_get_send (void) +{ +return &sim_con_send; +} + +/* Get Expect object address for console */ + +EXPECT *sim_cons_get_expect (void) +{ +return &sim_con_expect; +} + +/* Display console Queued input data status */ + +t_stat sim_show_cons_send_input (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +{ +return sim_show_send_input (st, &sim_con_send); +} + /* Poll for character */ t_stat sim_poll_kbd (void) { int32 c; +if (sim_send_poll_data (&sim_con_send, &c)) /* injected input characters available? */ + return c; c = sim_os_poll_kbd (); /* get character */ if ((c == SCPE_STOP) || /* ^E or not Telnet? */ ((sim_con_tmxr.master == 0) && /* and not serial? */ @@ -1540,6 +1578,7 @@ return SCPE_OK; t_stat sim_putchar (int32 c) { +sim_exp_check (&sim_con_expect, c); if ((sim_con_tmxr.master == 0) && /* not Telnet? */ (sim_con_ldsc.serport == 0)) { /* and not serial port */ if (sim_log) /* log file? */ @@ -1563,6 +1602,7 @@ t_stat sim_putchar_s (int32 c) { t_stat r; +sim_exp_check (&sim_con_expect, c); if ((sim_con_tmxr.master == 0) && /* not Telnet? */ (sim_con_ldsc.serport == 0)) { /* and not serial port */ if (sim_log) /* log file? */ diff --git a/sim_console.h b/sim_console.h index a23068a3..03c95d1f 100644 --- a/sim_console.h +++ b/sim_console.h @@ -68,6 +68,8 @@ t_stat sim_set_cons_unbuff (int32 flg, char *cptr); t_stat sim_set_cons_log (int32 flg, char *cptr); t_stat sim_set_cons_nolog (int32 flg, char *cptr); t_stat sim_set_deboff (int32 flag, char *cptr); +t_stat sim_set_cons_expect (int32 flg, char *cptr); +t_stat sim_set_cons_noexpect (int32 flg, char *cptr); t_stat sim_debug_flush (void); t_stat sim_set_pchar (int32 flag, char *cptr); t_stat sim_show_console (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); @@ -80,10 +82,14 @@ t_stat sim_show_pchar (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cpt t_stat sim_show_cons_buff (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat sim_show_cons_log (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat sim_show_cons_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat sim_show_cons_expect (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat sim_check_console (int32 sec); t_stat sim_open_logfile (char *filename, t_bool binary, FILE **pf, FILEREF **pref); t_stat sim_close_logfile (FILEREF **pref); const char *sim_logfile_name (FILE *st, FILEREF *ref); +SEND *sim_cons_get_send (void); +EXPECT *sim_cons_get_expect (void); +t_stat sim_show_cons_send_input (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat sim_poll_kbd (void); t_stat sim_putchar (int32 c); t_stat sim_putchar_s (int32 c); diff --git a/sim_defs.h b/sim_defs.h index c3f500d8..123c4910 100644 --- a/sim_defs.h +++ b/sim_defs.h @@ -313,8 +313,9 @@ typedef uint32 t_addr; #define SCPE_AFAIL (SCPE_BASE + 42) /* assert failed */ #define SCPE_INVREM (SCPE_BASE + 43) /* invalid remote console command */ #define SCPE_NOTATT (SCPE_BASE + 44) /* not attached */ +#define SCPE_EXPECT (SCPE_BASE + 45) /* expect matched */ -#define SCPE_MAX_ERR (SCPE_BASE + 45) /* Maximum SCPE Error Value */ +#define SCPE_MAX_ERR (SCPE_BASE + 46) /* Maximum SCPE Error Value */ #define SCPE_KFLAG 0x1000 /* tti data flag */ #define SCPE_BREAK 0x2000 /* tti break flag */ #define SCPE_NOMESSAGE 0x10000000 /* message display supression flag */ @@ -632,6 +633,40 @@ struct sim_brktab { char *act; /* action string */ }; +/* Expect rule */ + +struct sim_exptab { + uint8 *match; /* match string */ + uint32 size; /* match string size */ + int32 cnt; /* proceed count */ + int32 switches; /* flags */ +#define EXP_TYP_PERSIST (SWMASK ('P')) /* rule persists after match, default is once a rule matches, it is removed */ +#define EXP_TYP_CLEARALL (SWMASK ('C')) /* clear all rules after matching this rule, default is to once a rule matches, it is removed */ + char *act; /* action string */ + }; + +/* Expect Context */ + +struct sim_expect { + struct sim_exptab *rules; /* match rules */ + int32 size; /* count of match rules */ + uint8 *buf; /* buffer of output data which has produced */ + uint32 buf_ins; /* buffer insertion point for the next output data */ + uint32 buf_size; /* buffer size */ + }; + +/* Send Context */ + +struct sim_send { + uint32 delay; /* instruction delay before/between sent data */ +#define SEND_DEFAULT_DELAY 1000 /* default delay instruction count */ + double next_time; /* execution time when next data can be sent */ + uint8 *buffer; /* buffer */ + size_t bufsize; /* buffer size */ + int32 insoff; /* insert offset */ + int32 extoff; /* extra offset */ + }; + /* Debug table */ struct sim_debtab { @@ -745,6 +780,9 @@ typedef struct sim_shtab SHTAB; typedef struct sim_mtab MTAB; typedef struct sim_schtab SCHTAB; typedef struct sim_brktab BRKTAB; +typedef struct sim_exptab EXPTAB; +typedef struct sim_expect EXPECT; +typedef struct sim_send SEND; typedef struct sim_debtab DEBTAB; typedef struct sim_fileref FILEREF; typedef struct sim_bitfield BITFIELD; diff --git a/sim_tmxr.c b/sim_tmxr.c index da76ba20..3869dd45 100644 --- a/sim_tmxr.c +++ b/sim_tmxr.c @@ -1530,15 +1530,17 @@ uint32 tmp; tmxr_debug_trace_line (lp, "tmxr_getc_ln()"); if (lp->conn && lp->rcve) { /* conn & enb? */ - j = lp->rxbpi - lp->rxbpr; /* # input chrs */ - if (j) { /* any? */ - tmp = lp->rxb[lp->rxbpr]; /* get char */ - val = TMXR_VALID | (tmp & 0377); /* valid + chr */ - if (lp->rbr[lp->rxbpr]) { /* break? */ - lp->rbr[lp->rxbpr] = 0; /* clear status */ - val = val | SCPE_BREAK; /* indicate to caller */ + if (!sim_send_poll_data (&lp->send, &val)) { /* injected input characters available? */ + j = lp->rxbpi - lp->rxbpr; /* # input chrs */ + if (j) { /* any? */ + tmp = lp->rxb[lp->rxbpr]; /* get char */ + val = TMXR_VALID | (tmp & 0377); /* valid + chr */ + if (lp->rbr[lp->rxbpr]) { /* break? */ + lp->rbr[lp->rxbpr] = 0; /* clear status */ + val = val | SCPE_BREAK; /* indicate to caller */ + } + lp->rxbpr = lp->rxbpr + 1; /* adv pointer */ } - lp->rxbpr = lp->rxbpr + 1; /* adv pointer */ } } /* end if conn */ if (lp->rxbpi == lp->rxbpr) /* empty? zero ptrs */ @@ -1816,6 +1818,7 @@ if ((lp->txbfd && !lp->notelnet) || (TXBUF_AVAIL(lp) > 1)) {/* room for char (+ lp->xmte = 0; /* disable line */ if (lp->txlog) /* log if available */ fputc (chr, lp->txlog); + sim_exp_check (&lp->expect, chr); /* process expect rules as needed */ return SCPE_OK; /* char sent */ } ++lp->txdrp; lp->xmte = 0; /* no room, dsbl line */ @@ -3198,6 +3201,9 @@ for (i=0; ilines; i++) + if (0 == mux->ldsc[i].send.delay) + mux->ldsc[i].send.delay = SEND_DEFAULT_DELAY; } #if defined(SIM_ASYNCH_IO) && defined(SIM_ASYNCH_MUX) pthread_mutex_unlock (&sim_tmxr_poll_lock); @@ -3226,6 +3232,46 @@ pthread_mutex_unlock (&sim_tmxr_poll_lock); #endif } +static t_stat _tmxr_locate_line_send_expect (const char *cptr, SEND **snd, EXPECT **exp) +{ +char gbuf[CBUFSIZE]; +DEVICE *dptr; +int i; +t_stat r; + +if (snd) + *snd = NULL; +if (exp) + *exp = NULL; +cptr = get_glyph(cptr, gbuf, ':'); +dptr = find_dev (gbuf); /* device match? */ +if (!dptr) + return SCPE_ARG; + +for (i=0; idptr == dptr) { + int line = (int)get_uint (cptr, 10, tmxr_open_devices[i]->lines, &r); + if (r != SCPE_OK) + return r; + if (snd) + *snd = &tmxr_open_devices[i]->ldsc[line].send; + if (exp) + *exp = &tmxr_open_devices[i]->ldsc[line].expect; + return SCPE_OK; + } +return SCPE_ARG; +} + +t_stat tmxr_locate_line_send (const char *cptr, SEND **snd) +{ +return _tmxr_locate_line_send_expect (cptr, snd, NULL); +} + +t_stat tmxr_locate_line_expect (const char *cptr, EXPECT **exp) +{ +return _tmxr_locate_line_send_expect (cptr, NULL, exp); +} + t_stat tmxr_change_async (void) { #if defined(SIM_ASYNCH_IO) @@ -3812,6 +3858,10 @@ if (lp->modem_control) { if ((lp->serport == 0) && (lp->sock) && (!lp->datagram)) fprintf (st, " %s\n", (lp->notelnet) ? "Telnet disabled (RAW data)" : "Telnet protocol"); +if (lp->send.buffer) + sim_show_send_input (st, &lp->send); +if (lp->expect.buf) + sim_exp_showall (st, &lp->expect); if (lp->txlog) fprintf (st, " Logging to %s\n", lp->txlogname); return; diff --git a/sim_tmxr.h b/sim_tmxr.h index 208f539f..30188753 100644 --- a/sim_tmxr.h +++ b/sim_tmxr.h @@ -161,6 +161,8 @@ struct tmln { UNIT *uptr; /* input polling unit (default to mp->uptr) */ UNIT *o_uptr; /* output polling unit (default to lp->uptr)*/ DEVICE *dptr; /* line specific device */ + EXPECT expect; /* Expect rules */ + SEND send; /* Send input state */ }; struct tmxr { @@ -238,6 +240,8 @@ t_stat tmxr_activate (UNIT *uptr, int32 interval); t_stat tmxr_activate_after (UNIT *uptr, int32 usecs_walltime); t_stat tmxr_clock_coschedule (UNIT *uptr, int32 interval); t_stat tmxr_change_async (void); +t_stat tmxr_locate_line_send (const char *dev_line, SEND **snd); +t_stat tmxr_locate_line_expect (const char *dev_line, EXPECT **exp); t_stat tmxr_startup (void); t_stat tmxr_shutdown (void); t_stat tmxr_start_poll (void);