From 110ded690478b970cf24d1eb8dfcfa273621d917 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Fri, 27 Jan 2017 23:06:30 -0800 Subject: [PATCH] FRONTPANEL: sim_frontpanel API version 3 release Adds: 1) Simulator side execution of register update activities at an interval specified in usecs. Rates in excess of 1000Hz should be achievable for locally connected frontpanel applications. 2) New API for simulators to describe the register state available in the simulator. If all of a the state which is potentially interesting to front panel applications is always present in the variables described by simh REG structures, then frontpanel API access to that data can be more efficiently provided. --- Visual Studio Projects/FrontPanelTest.vcproj | 8 +- frontpanel/FrontPanelTest.c | 26 +- sim_console.c | 472 +++++++++++++++---- sim_console.h | 1 + sim_frontpanel.c | 418 +++++++++++----- sim_frontpanel.h | 22 +- 6 files changed, 726 insertions(+), 221 deletions(-) diff --git a/Visual Studio Projects/FrontPanelTest.vcproj b/Visual Studio Projects/FrontPanelTest.vcproj index e684a37d..95c97aaa 100644 --- a/Visual Studio Projects/FrontPanelTest.vcproj +++ b/Visual Studio Projects/FrontPanelTest.vcproj @@ -35,6 +35,9 @@ + @@ -116,6 +119,9 @@ + @@ -126,7 +132,7 @@ EnableIntrinsicFunctions="true" FavorSizeOrSpeed="1" OmitFramePointers="true" - WholeProgramOptimization="true" + WholeProgramOptimization="false" AdditionalIncludeDirectories="./;../;../VAX/;../pdp11/;"../../windows-build/winpcap/Wpdpack/Include";"../../windows-build/PCRE/include/";"../../windows-build/pthreads"" PreprocessorDefinitions="_CRT_NONSTDC_NO_WARNINGS;_CRT_SECURE_NO_WARNINGS;_WINSOCK_DEPRECATED_NO_WARNINGS;PTW32_STATIC_LIB" StringPooling="true" diff --git a/frontpanel/FrontPanelTest.c b/frontpanel/FrontPanelTest.c index 9537ada4..612b4313 100644 --- a/frontpanel/FrontPanelTest.c +++ b/frontpanel/FrontPanelTest.c @@ -148,11 +148,10 @@ if ((argc > 1) && ((!strcmp("-d", argv[1])) || (!strcmp("-D", argv[1])) || (!str if ((f = fopen (sim_config, "w"))) { if (debug) { fprintf (f, "set verbose\n"); - fprintf (f, "set log simulator.dbg\n"); - fprintf (f, "set debug -n -a log\n"); + fprintf (f, "set debug -n -a simulator.dbg\n"); fprintf (f, "set cpu conhalt\n"); fprintf (f, "set remote telnet=2226\n"); - fprintf (f, "set rem-con debug=XMT;RCV\n"); + fprintf (f, "set rem-con debug=XMT;RCV;MODE;REPEAT;CMD\n"); fprintf (f, "set remote notelnet\n"); } fprintf (f, "set cpu autoboot\n"); @@ -184,9 +183,8 @@ if (!panel) { } if (debug) { - sim_panel_set_debug_mode (panel, DBG_XMT|DBG_RCV); + sim_panel_set_debug_mode (panel, DBG_XMT|DBG_RCV|DBG_REQ|DBG_RSP); } - tape = sim_panel_add_device_panel (panel, "TAPE DRIVE"); if (!tape) { @@ -313,7 +311,7 @@ if (sim_panel_get_registers (panel, NULL)) { printf ("Error getting register data: %s\n", sim_panel_get_error()); goto Done; } -if (sim_panel_set_display_callback (panel, &DisplayCallback, NULL, 5)) { +if (sim_panel_set_display_callback_interval (panel, &DisplayCallback, NULL, 200000)) { printf ("Error setting automatic display callback: %s\n", sim_panel_get_error()); goto Done; } @@ -336,19 +334,27 @@ if (sim_panel_dismount (panel, "RL0")) { } remove ("TEST-RL.DSK"); if (sim_panel_break_set (panel, "400")) { - printf ("Unexpected establishing a breakpoint: %s\n", sim_panel_get_error()); + printf ("Unexpected error establishing a breakpoint: %s\n", sim_panel_get_error()); goto Done; } if (sim_panel_break_clear (panel, "400")) { - printf ("Unexpected clearing a breakpoint: %s\n", sim_panel_get_error()); + printf ("Unexpected error clearing a breakpoint: %s\n", sim_panel_get_error()); goto Done; } if (sim_panel_break_output_set (panel, "\"32..31..30\"")) { - printf ("Unexpected establishing an output breakpoint: %s\n", sim_panel_get_error()); + printf ("Unexpected error establishing an output breakpoint: %s\n", sim_panel_get_error()); goto Done; } if (sim_panel_break_output_clear (panel, "\"32..31..30\"")) { - printf ("Unexpected clearing an output breakpoint: %s\n", sim_panel_get_error()); + printf ("Unexpected error clearing an output breakpoint: %s\n", sim_panel_get_error()); + goto Done; + } +if (sim_panel_break_output_set (panel, "-P \"Normal operation not possible.\"")) { + printf ("Unexpected error establishing an output breakpoint: %s\n", sim_panel_get_error()); + goto Done; + } +if (sim_panel_break_output_set (panel, "-P \"Device? [XQA0]: \"")) { + printf ("Unexpected error establishing an output breakpoint: %s\n", sim_panel_get_error()); goto Done; } sim_panel_clear_error (); diff --git a/sim_console.c b/sim_console.c index 11f55c23..5553fa73 100644 --- a/sim_console.c +++ b/sim_console.c @@ -114,6 +114,9 @@ sim_ttisatty called to determine if running interactively sim_os_poll_kbd poll for keyboard input sim_os_putchar output character to console + sim_set_noconsole_port Enable automatic WRU console polling + sim_set_stable_registers_state Declare that all registers are always stable + The first group is OS-independent; the second group is OS-dependent. @@ -172,6 +175,7 @@ int32 sim_del_char = '\b'; /* delete character */ #else int32 sim_del_char = 0177; #endif +extern TMLN *sim_oline; /* global output socket */ static t_stat sim_con_poll_svc (UNIT *uptr); /* console connection poll routine */ static t_stat sim_con_reset (DEVICE *dptr); /* console reset routine */ @@ -243,6 +247,16 @@ sim_con_console_port = FALSE; return SCPE_OK; } +static t_bool sim_con_stable_registers = FALSE; + +/* Enable automatic WRU console polling */ + +t_stat sim_set_stable_registers_state (void) +{ +sim_con_stable_registers = TRUE; +return SCPE_OK; +} + /* Unit service for console connection polling */ static t_stat sim_con_poll_svc (UNIT *uptr) @@ -409,18 +423,28 @@ while (*cptr != 0) { return SCPE_OK; } +#define MAX_REMOTE_SESSIONS 40 /* Arbitrary Session Limit */ + t_stat sim_rem_con_poll_svc (UNIT *uptr); /* remote console connection poll routine */ t_stat sim_rem_con_data_svc (UNIT *uptr); /* remote console connection data routine */ +t_stat sim_rem_con_repeat_svc (UNIT *uptr); /* remote auto repeat command console timing routine */ t_stat sim_rem_con_reset (DEVICE *dptr); /* remote console reset routine */ -UNIT sim_rem_con_unit[2] = { +UNIT sim_rem_con_unit[2+MAX_REMOTE_SESSIONS] = { { UDATA (&sim_rem_con_poll_svc, UNIT_IDLE, 0) }, /* remote console connection polling unit */ { UDATA (&sim_rem_con_data_svc, UNIT_IDLE|UNIT_DIS, 0) }}; /* console data handling unit */ +#define DBG_MOD 0x00000004 /* Remote Console Mode activities */ +#define DBG_REP 0x00000008 /* Remote Console Repeat activities */ +#define DBG_CMD 0x00000010 /* Remote Console Command activities */ + DEBTAB sim_rem_con_debug[] = { {"TRC", DBG_TRC, "routine calls"}, {"XMT", DBG_XMT, "Transmitted Data"}, {"RCV", DBG_RCV, "Received Data"}, {"CON", DBG_CON, "connection activity"}, + {"CMD", DBG_CMD, "Remote Console Command activity"}, + {"MODE", DBG_MOD, "Remote Console Mode activity"}, + {"REPEAT", DBG_REP, "Remote Console Repeat activity"}, {0} }; @@ -439,10 +463,15 @@ DEVICE sim_remote_console = { NULL, NULL, sim_rem_con_reset, NULL, NULL, NULL, NULL, DEV_DEBUG | DEV_NOSAVE, 0, sim_rem_con_debug, NULL, NULL, NULL, NULL, NULL, sim_rem_con_description}; -#define MAX_REMOTE_SESSIONS 40 /* Arbitrary Session Limit */ static int32 *sim_rem_buf_size = NULL; static int32 *sim_rem_buf_ptr = NULL; static char **sim_rem_buf = NULL; +static char **sim_rem_act_buf = NULL; +static size_t *sim_rem_act_buf_size = NULL; +static char **sim_rem_act = NULL; +static uint32 *sim_rem_repeat_interval = NULL; +static t_bool *sim_rem_repeat_pending = NULL; +static char **sim_rem_repeat_action = NULL; static t_bool *sim_rem_single_mode = NULL; /* per line command mode (single command or must continue) */ 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 */ @@ -523,6 +552,10 @@ for (i=connections=0; itxbsz) - sim_os_ms_sleep (100); - } while (unwritten == lp->txbsz); - } } +sim_oline = NULL; +if ((sim_rem_act[line] == NULL) && + (!tmxr_input_pending_ln (lp))) { + int32 unwritten; + + do { + unwritten = tmxr_send_buffered_data (lp); + if (unwritten == lp->txbsz) + sim_os_ms_sleep (100); + } while (unwritten == lp->txbsz); + } + } void sim_remote_process_command (void) @@ -724,17 +777,152 @@ cptr = cbuf; cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */ sim_rem_active_command = find_cmd (gbuf); /* find command */ -sim_ttcmd (); /* restore console */ +if (!sim_processing_event) + sim_ttcmd (); /* restore console */ stat = sim_rem_active_command->action (sim_rem_active_command->arg, cptr);/* execute command */ if (stat != SCPE_OK) stat = _sim_rem_message (gbuf, stat); /* display results */ sim_last_cmd_stat = SCPE_BARE_STATUS(stat); -sim_ttrun (); /* set console mode */ -sim_cancel (&sim_rem_con_unit[1]); /* force immediate activation of sim_rem_con_data_svc */ -sim_activate (&sim_rem_con_unit[1], -1); +if (!sim_processing_event) { + sim_ttrun (); /* set console mode */ + sim_cancel (&sim_rem_con_unit[1]); /* force immediate activation of sim_rem_con_data_svc */ + sim_activate (&sim_rem_con_unit[1], -1); + } sim_switches = saved_switches; /* restore original switches */ } +/* Clear pending actions */ + +static char *sim_rem_clract (int32 line) +{ +TMLN *lp = &sim_rem_con_tmxr.ldsc[line]; + +tmxr_send_buffered_data (lp); /* flush any buffered data */ +return sim_rem_act[line] = NULL; +} + +/* Set up pending actions */ + +static void sim_rem_setact (int32 line, const char *action) +{ +if (action) { + size_t act_size = strlen (action) + 1; + + if (act_size > sim_rem_act_buf_size[line]) {/* expand buffer if necessary */ + sim_rem_act_buf[line] = (char *)realloc (sim_rem_act_buf[line], act_size); + sim_rem_act_buf_size[line] = act_size; + } + strcpy (sim_rem_act_buf[line], action); /* populate buffer */ + sim_rem_act[line] = sim_rem_act_buf[line]; /* start at beginning of buffer */ + } +else + sim_rem_clract (line); +} + +/* Get next pending action, if any */ + +static char *sim_rem_getact (int32 line, char *buf, int32 size) +{ +char *ep; +size_t lnt; + +if (sim_rem_act[line] == NULL) /* any action? */ + return NULL; +while (sim_isspace (*sim_rem_act[line])) /* skip spaces */ + sim_rem_act[line]++; +if (*sim_rem_act[line] == 0) /* now empty? */ + return sim_rem_clract (line); +if ((ep = strchr (sim_rem_act[line], ';'))) { /* cmd delimiter? */ + lnt = ep - sim_rem_act[line]; /* cmd length */ + memcpy (buf, sim_rem_act[line], lnt + 1); /* copy with ; */ + buf[lnt] = 0; /* erase ; */ + sim_rem_act[line] += lnt + 1; /* adv ptr */ + } +else { + strncpy (buf, sim_rem_act[line], size); /* copy action */ + sim_rem_act[line] += strlen (sim_rem_act[line]);/* adv ptr to end */ + } +return buf; +} + +/* + Parse and setup Remote Console REPEAT command: + REPEAT EVERY nnn USECS Command {; command...} + */ +static t_stat _rem_repeat_cmd_setup (int32 line, CONST char **iptr) +{ +char gbuf[CBUFSIZE]; +int32 val; +t_bool all_stop = FALSE; +t_stat stat = SCPE_OK; +CONST char *cptr = *iptr; + +sim_debug (DBG_REP, &sim_remote_console, "Repeat Setup: %s\n", cptr); +if (*cptr == 0) /* required argument? */ + stat = SCPE_2FARG; +else { + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + if (MATCH_CMD (gbuf, "EVERY") == 0) { + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + val = (int32) get_uint (gbuf, 10, INT_MAX, &stat); + if ((stat != SCPE_OK) || (val <= 0)) /* error? */ + stat = SCPE_ARG; + else { + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + if ((MATCH_CMD (gbuf, "USECS") != 0) || (*cptr == 0)) + stat = SCPE_ARG; + else + sim_rem_repeat_interval[line] = val; + } + } + else { + if (MATCH_CMD (gbuf, "STOP") == 0) { + if (*cptr) { /* more command arguments? */ + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + if ((MATCH_CMD (gbuf, "ALL") != 0) || /* */ + (*cptr != 0) || /* */ + (line != 0)) /* master line? */ + stat = SCPE_ARG; + else + all_stop = TRUE; + } + else + sim_rem_repeat_interval[line] = 0; + } + else + stat = SCPE_ARG; + } + } +if (stat == SCPE_OK) { + if (all_stop) { + for (line = 0; line < sim_rem_con_tmxr.lines; line++) { + free (sim_rem_repeat_action[line]); + sim_rem_repeat_action[line] = NULL; + sim_cancel (&sim_rem_con_unit[line + 2]); + sim_rem_repeat_pending[line] = FALSE; + sim_rem_clract (line); + } + } + else { + if (sim_rem_repeat_interval[line] != 0) { + sim_rem_repeat_action[line] = (char *)realloc (sim_rem_repeat_action[line], 1 + strlen (cptr)); + strcpy (sim_rem_repeat_action[line], cptr); + cptr += strlen (cptr); + stat = sim_activate_after (&sim_rem_con_unit[line + 2], sim_rem_repeat_interval[line]); + } + else { + free (sim_rem_repeat_action[line]); + sim_rem_repeat_action[line] = NULL; + sim_cancel (&sim_rem_con_unit[line + 2]); + } + sim_rem_repeat_pending[line] = FALSE; + sim_rem_clract (line); + } + } +*iptr = cptr; +return stat; +} + /* Unit service for remote console data polling */ t_stat sim_rem_con_data_svc (UNIT *uptr) @@ -760,8 +948,13 @@ for (i=(was_active_command ? sim_rem_cmd_active_line : 0); t_bool master_session = (sim_rem_master_mode && (i == 0)); lp = &sim_rem_con_tmxr.ldsc[i]; - if (!lp->conn) + if (!lp->conn) { + if (sim_rem_repeat_interval[i]) { /* was repeated enabled? */ + cptr = strcpy (gbuf, "STOP"); + _rem_repeat_cmd_setup (i, &cptr); /* make sure it is now disabled */ + } continue; + } if (master_session && !sim_rem_master_was_connected) { tmxr_linemsgf (lp, "\nMaster Mode Session\r\n"); tmxr_send_buffered_data (lp); /* flush any buffered data */ @@ -770,6 +963,7 @@ for (i=(was_active_command ? sim_rem_cmd_active_line : 0); stat = SCPE_OK; if ((was_active_command) || (master_session && !sim_rem_single_mode[i])) { + sim_debug (DBG_MOD, &sim_remote_console, "Session: %d %s %s\n", i, was_active_command ? "Was Active" : "", (master_session && !sim_rem_single_mode[i]) ? "master_session && !sim_rem_single_mode[i]" : ""); if (was_active_command) { sim_rem_cmd_active_line = -1; /* Done with active command */ if (!sim_rem_active_command) { /* STEP command? */ @@ -792,58 +986,59 @@ for (i=(was_active_command ? sim_rem_cmd_active_line : 0); tmxr_linemsgf (lpj, "\nRemote Master Console(%s) Entering Commands\n", lp->ipad); tmxr_send_buffered_data (lpj); /* flush any buffered data */ } - lp = &sim_rem_con_tmxr.ldsc[i]; } } else { - c = tmxr_getc_ln (lp); - if (!(TMXR_VALID & c)) - continue; - c = c & ~TMXR_VALID; - if (sim_rem_single_mode[i]) { - if (c == sim_int_char) { /* ^E (the interrupt character) must start continue mode console interaction */ - sim_rem_single_mode[i] = FALSE; /* enter multi command mode */ - sim_is_running = 0; - sim_stop_timer_services (); - stat = SCPE_STOP; - _sim_rem_message ("RUN", stat); - _sim_rem_log_out (lp); - for (j=0; j < sim_rem_con_tmxr.lines; j++) { - TMLN *lpj = &sim_rem_con_tmxr.ldsc[j]; - if ((i == j) || (!lpj->conn)) - continue; - tmxr_linemsgf (lpj, "\nRemote Console %d(%s) Entering Commands\n", i, lp->ipad); - tmxr_send_buffered_data (lpj); /* flush any buffered data */ - } - lp = &sim_rem_con_tmxr.ldsc[i]; - if (!master_session) - tmxr_linemsg (lp, "\r\nSimulator paused.\r\n"); - if (!master_session && sim_rem_read_timeouts[i]) { - tmxr_linemsgf (lp, "Simulation will resume automatically if input is not received in %d seconds\n", sim_rem_read_timeouts[i]); - tmxr_linemsgf (lp, "\r\n"); - tmxr_send_buffered_data (lp); /* flush any buffered data */ - } - } - else { - if ((sim_rem_buf_ptr[i] == 0) && /* At beginning of input line */ - ((c == '\n') || /* Ignore bare LF between commands (Microsoft Telnet bug) */ - (c == '\r'))) /* Ignore empty commands */ - continue; - if ((c == '\004') || (c == '\032')) { /* EOF character (^D or ^Z) ? */ - tmxr_linemsgf (lp, "\r\nGoodbye\r\n"); - tmxr_send_buffered_data (lp); /* flush any buffered data */ - tmxr_reset_ln (lp); - continue; - } - if (sim_rem_buf_ptr[i] == 0) { - /* we just picked up the first character on a command line */ + if ((!sim_rem_repeat_pending[i]) || (sim_rem_buf_ptr[i] != 0)) { + c = tmxr_getc_ln (lp); + if (!(TMXR_VALID & c)) + continue; + c = c & ~TMXR_VALID; + if (sim_rem_single_mode[i]) { + if (c == sim_int_char) { /* ^E (the interrupt character) must start continue mode console interaction */ + sim_rem_single_mode[i] = FALSE; /* enter multi command mode */ + sim_is_running = 0; + sim_stop_timer_services (); + stat = SCPE_STOP; + _sim_rem_message ("RUN", stat); + _sim_rem_log_out (lp); + for (j=0; j < sim_rem_con_tmxr.lines; j++) { + TMLN *lpj = &sim_rem_con_tmxr.ldsc[j]; + if ((i == j) || (!lpj->conn)) + continue; + tmxr_linemsgf (lpj, "\nRemote Console %d(%s) Entering Commands\n", i, lp->ipad); + tmxr_send_buffered_data (lpj); /* flush any buffered data */ + } + lp = &sim_rem_con_tmxr.ldsc[i]; if (!master_session) - tmxr_linemsgf (lp, "\r\n%s", sim_prompt); - else - tmxr_linemsgf (lp, "\r\n%s", sim_is_running ? "SIM> " : "sim> "); - sim_debug (DBG_XMT, &sim_remote_console, "Prompt Written: %s\n", sim_is_running ? "SIM> " : "sim> "); - if (!tmxr_input_pending_ln (lp)) - tmxr_send_buffered_data (lp); /* flush any buffered data */ + tmxr_linemsg (lp, "\r\nSimulator paused.\r\n"); + if (!master_session && sim_rem_read_timeouts[i]) { + tmxr_linemsgf (lp, "Simulation will resume automatically if input is not received in %d seconds\n", sim_rem_read_timeouts[i]); + tmxr_linemsgf (lp, "\r\n"); + tmxr_send_buffered_data (lp); /* flush any buffered data */ + } + } + else { + if ((sim_rem_buf_ptr[i] == 0) && /* At beginning of input line */ + ((c == '\n') || /* Ignore bare LF between commands (Microsoft Telnet bug) */ + (c == '\r'))) /* Ignore empty commands */ + continue; + if ((c == '\004') || (c == '\032')) { /* EOF character (^D or ^Z) ? */ + tmxr_linemsgf (lp, "\r\nGoodbye\r\n"); + tmxr_send_buffered_data (lp); /* flush any buffered data */ + tmxr_reset_ln (lp); + continue; + } + if (sim_rem_buf_ptr[i] == 0) { + /* we just picked up the first character on a command line */ + if (!master_session) + tmxr_linemsgf (lp, "\r\n%s", sim_prompt); + else + tmxr_linemsgf (lp, "\r\n%s", sim_is_running ? "SIM> " : "sim> "); + sim_debug (DBG_XMT, &sim_remote_console, "Prompt Written: %s\n", sim_is_running ? "SIM> " : "sim> "); + if ((sim_rem_act[i] == NULL) && (!tmxr_input_pending_ln (lp))) + tmxr_send_buffered_data (lp); /* flush any buffered data */ + } } } } @@ -861,6 +1056,31 @@ for (i=(was_active_command ? sim_rem_cmd_active_line : 0); tmxr_send_buffered_data (lp); /* flush any buffered data */ } do { + if (sim_rem_buf_ptr[i] == 0) { + if (sim_rem_getact (i, sim_rem_buf[i], sim_rem_buf_size[i])) { + if (!master_session) + tmxr_linemsgf (lp, "%s%s\n", sim_prompt, sim_rem_buf[i]); + else + tmxr_linemsgf (lp, "%s%s\n", sim_is_running ? "SIM> " : "sim> ", sim_rem_buf[i]); + sim_rem_buf_ptr[i] = strlen (sim_rem_repeat_action[i]); + got_command = TRUE; + break; + } + else { + if (sim_rem_repeat_pending[i]) { + sim_rem_repeat_pending[i] = FALSE; + sim_rem_setact (i, sim_rem_repeat_action[i]); + sim_rem_getact (i, sim_rem_buf[i], sim_rem_buf_size[i]); + if (!master_session) + tmxr_linemsgf (lp, "%s%s\n", sim_prompt, sim_rem_buf[i]); + else + tmxr_linemsgf (lp, "%s%s\n", sim_is_running ? "SIM> " : "sim> ", sim_rem_buf[i]); + sim_rem_buf_ptr[i] = strlen (sim_rem_repeat_action[i]); + got_command = TRUE; + break; + } + } + } if (!sim_rem_single_mode[i]) { c = tmxr_getc_ln (lp); if (!(TMXR_VALID & c)) { @@ -957,12 +1177,13 @@ for (i=(was_active_command ? sim_rem_cmd_active_line : 0); c = c & ~TMXR_VALID; } } while ((!got_command) && ((!sim_rem_single_mode[i]) || c)); - if (!tmxr_input_pending_ln (lp)) + if ((sim_rem_act[i] == NULL) && (!tmxr_input_pending_ln (lp))) tmxr_send_buffered_data (lp); /* flush any buffered data */ if ((sim_rem_single_mode[i]) && !got_command) { break; } - sim_printf ("Remote Console Command from %s> %s\r\n", lp->ipad, sim_rem_buf[i]); + if (!sim_rem_master_mode) + sim_printf ("Remote Console Command from %s> %s\r\n", lp->ipad, sim_rem_buf[i]); got_command = FALSE; if (strlen(sim_rem_buf[i]) >= sizeof(cbuf)) { sim_printf ("\r\nLine too long. Ignored. Continuing Simulator execution\r\n"); @@ -1000,6 +1221,8 @@ for (i=(was_active_command ? sim_rem_cmd_active_line : 0); } sim_rem_cmd_log_start = sim_ftell (sim_log); basecmdp = find_cmd (gbuf); /* validate basic command */ + if (basecmdp == NULL) + basecmdp = find_ctab (remote_only_cmds, gbuf);/* validate basic command */ if (basecmdp == NULL) { if ((gbuf[0] == ';') || (gbuf[0] == '#')) { /* ignore comment */ sim_rem_cmd_active_line = i; @@ -1013,39 +1236,64 @@ for (i=(was_active_command ? sim_rem_cmd_active_line : 0); } else { if ((cmdp = find_ctab (sim_rem_single_mode[i] ? allowed_single_remote_cmds : (master_session ? allowed_master_remote_cmds : allowed_remote_cmds), gbuf))) {/* lookup command */ - if (cmdp->action == &x_continue_cmd) + sim_debug (DBG_CMD, &sim_remote_console, "gbuf='%s', basecmd='%s', cmd='%s', action=%p, continue_cmd=%p, repeat_cmd=%p\n", gbuf, basecmdp->name, cmdp->name, cmdp->action, &x_continue_cmd, &x_repeat_cmd); + if (cmdp->action == &x_continue_cmd) { + sim_debug (DBG_CMD, &sim_remote_console, "continue_cmd executing\n"); stat = SCPE_OK; + } else { if (cmdp->action == &exit_cmd) return SCPE_EXIT; if (cmdp->action == &x_step_cmd) { + sim_debug (DBG_CMD, &sim_remote_console, "step_cmd executing\n"); steps = 1; /* default of 1 instruction */ stat = SCPE_OK; if (*cptr != 0) { /* argument? */ - cptr = get_glyph (cptr, gbuf, 0);/* get next glyph */ - if (*cptr != 0) /* should be end */ - stat = SCPE_2MARG; - else { - steps = (int32) get_uint (gbuf, 10, INT_MAX, &stat); - if ((stat != SCPE_OK) || (steps <= 0)) /* error? */ - stat = SCPE_ARG; - } - } + cptr = get_glyph (cptr, gbuf, 0);/* get next glyph */ + if (*cptr != 0) /* should be end */ + stat = SCPE_2MARG; + else { + steps = (int32) get_uint (gbuf, 10, INT_MAX, &stat); + if ((stat != SCPE_OK) || (steps <= 0)) /* error? */ + stat = SCPE_ARG; + } + } if (stat != SCPE_OK) cmdp = NULL; } else { if (cmdp->action == &x_run_cmd) { - sim_switches |= SIM_SW_HIDE;/* Request Setup only */ + sim_debug (DBG_CMD, &sim_remote_console, "run_cmd executing\n"); + if (sim_con_stable_registers && /* can we process command now? */ + sim_rem_master_mode) + sim_oline = lp; /* specify output socket */ + sim_switches |= SIM_SW_HIDE; /* Request Setup only */ stat = basecmdp->action (cmdp->arg, cptr); - sim_switches &= ~SIM_SW_HIDE;/* Done with Setup only mode */ + sim_switches &= ~SIM_SW_HIDE; /* Done with Setup only mode */ if (stat == SCPE_OK) { /* switch to CONTINUE after x_run_cmd() did RUN setup */ cmdp = find_ctab (allowed_master_remote_cmds, "CONTINUE"); } } - else - stat = SCPE_REMOTE; /* force processing outside of sim_instr() */ + else { + if (cmdp->action == &x_repeat_cmd) { + sim_debug (DBG_CMD, &sim_remote_console, "repeat_cmd executing\n"); + stat = _rem_repeat_cmd_setup (i, &cptr); + } + else { + if (sim_con_stable_registers && + sim_rem_master_mode) { /* can we process command now? */ + sim_debug (DBG_CMD, &sim_remote_console, "Processing Command directly\n"); + sim_oline = lp; /* specify output socket */ + sim_remote_process_command (); + stat = SCPE_OK; /* any message has already been emitted */ + } + else { + sim_debug (DBG_CMD, &sim_remote_console, "Processing Command via SCPE_REMOTE\n"); + stat = SCPE_REMOTE; /* force processing outside of sim_instr() */ + } + } + } } } } @@ -1136,14 +1384,36 @@ else return SCPE_OK; /* keep going */ } +t_stat sim_rem_con_repeat_svc (UNIT *uptr) +{ +int line = uptr - (sim_rem_con_unit + 2); + +sim_debug (DBG_REP, &sim_remote_console, "sim_rem_con_repeat_svc(line=%d) - interval=%d\n", line, sim_rem_repeat_interval[line]); +if (sim_rem_repeat_interval[line]) { + sim_rem_repeat_pending[line] = TRUE; + sim_activate_after (uptr, sim_rem_repeat_interval[line]); /* reschedule */ + sim_activate_abs (&sim_rem_con_unit[1], -1); /* wake up to process */ + } +return SCPE_OK; +} + t_stat sim_rem_con_reset (DEVICE *dptr) { if (sim_rem_con_tmxr.lines) { int32 i; - for (i=0; iunits[1], 100000); /* continue polling for open sessions */ return sim_rem_con_poll_svc (&dptr->units[0]); /* establish polling as needed */ @@ -1208,10 +1478,22 @@ sim_rem_con_tmxr.ldsc = (TMLN *)realloc (sim_rem_con_tmxr.ldsc, sizeof(*sim_rem_ memset (sim_rem_con_tmxr.ldsc, 0, sizeof(*sim_rem_con_tmxr.ldsc)*lines); sim_rem_buf = (char **)realloc (sim_rem_buf, sizeof(*sim_rem_buf)*lines); memset (sim_rem_buf, 0, sizeof(*sim_rem_buf)*lines); +sim_rem_act_buf = (char **)realloc (sim_rem_act_buf, sizeof(*sim_rem_act_buf)*lines); +memset (sim_rem_act_buf, 0, sizeof(*sim_rem_act_buf)*lines); +sim_rem_act_buf_size = (size_t *)realloc (sim_rem_act_buf_size, sizeof(*sim_rem_act_buf_size)*lines); +memset (sim_rem_act_buf_size, 0, sizeof(*sim_rem_act_buf_size)*lines); +sim_rem_act = (char **)realloc (sim_rem_act, sizeof(*sim_rem_act)*lines); +memset (sim_rem_act, 0, sizeof(*sim_rem_act)*lines); sim_rem_buf_size = (int32 *)realloc (sim_rem_buf_size, sizeof(*sim_rem_buf_size)*lines); memset (sim_rem_buf_size, 0, sizeof(*sim_rem_buf_size)*lines); sim_rem_buf_ptr = (int32 *)realloc (sim_rem_buf_ptr, sizeof(*sim_rem_buf_ptr)*lines); memset (sim_rem_buf_ptr, 0, sizeof(*sim_rem_buf_ptr)*lines); +sim_rem_repeat_interval = (uint32 *)realloc (sim_rem_repeat_interval, sizeof(*sim_rem_repeat_interval)*lines); +memset (sim_rem_repeat_interval, 0, sizeof(*sim_rem_repeat_interval)*lines); +sim_rem_repeat_pending = (t_bool *)realloc (sim_rem_repeat_pending, sizeof(*sim_rem_repeat_pending)*lines); +memset (sim_rem_repeat_pending, 0, sizeof(*sim_rem_repeat_pending)*lines); +sim_rem_repeat_action = (char **)realloc (sim_rem_repeat_action, sizeof(*sim_rem_repeat_action)*lines); +memset (sim_rem_repeat_action, 0, sizeof(*sim_rem_repeat_action)*lines); sim_rem_single_mode = (t_bool *)realloc (sim_rem_single_mode, sizeof(*sim_rem_single_mode)*lines); memset (sim_rem_single_mode, 0, sizeof(*sim_rem_single_mode)*lines); sim_rem_read_timeouts = (uint32 *)realloc (sim_rem_read_timeouts, sizeof(*sim_rem_read_timeouts)*lines); @@ -1346,7 +1628,8 @@ t_stat sim_show_kmap (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST char { if (sim_devices[0]->dradix == 16) fprintf (st, "%s = %X\n", show_con_tab[flag].name, *(cons_kmap[flag & KMAP_MASK])); -else fprintf (st, "%s = %o\n", show_con_tab[flag].name, *(cons_kmap[flag & KMAP_MASK])); +else + fprintf (st, "%s = %o\n", show_con_tab[flag].name, *(cons_kmap[flag & KMAP_MASK])); return SCPE_OK; } @@ -1376,7 +1659,8 @@ t_stat sim_show_pchar (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, CONST cha { if (sim_devices[0]->dradix == 16) fprintf (st, "pchar mask = %X", sim_tt_pchar); -else fprintf (st, "pchar mask = %o", sim_tt_pchar); +else + fprintf (st, "pchar mask = %o", sim_tt_pchar); if (sim_tt_pchar) { static const char *pchars[] = {"NUL(^@)", "SOH(^A)", "STX(^B)", "ETX(^C)", "EOT(^D)", "ENQ(^E)", "ACK(^F)", "BEL(^G)", "BS(^H)" , "HT(^I)", "LF(^J)", "VT(^K)", "FF(^L)", "CR(^M)", "SO(^N)", "SI(^O)", @@ -1433,7 +1717,7 @@ r = sim_open_logfile (gbuf, FALSE, &sim_log, &sim_log_ref); /* open log */ if (r != SCPE_OK) /* error? */ return r; if (!sim_quiet) - printf ("Logging to file \"%s\"\n", + fprintf (stdout, "Logging to file \"%s\"\n", sim_logfile_name (sim_log, sim_log_ref)); fprintf (sim_log, "Logging to file \"%s\"\n", sim_logfile_name (sim_log, sim_log_ref)); /* start of log */ @@ -1451,7 +1735,7 @@ if (cptr && (*cptr != 0)) /* now eol? */ if (sim_log == NULL) /* no log? */ return SCPE_OK; if (!sim_quiet) - printf ("Log file closed\n"); + fprintf (stdout, "Log file closed\n"); fprintf (sim_log, "Log file closed\n"); sim_close_logfile (&sim_log_ref); /* close log */ sim_log = NULL; @@ -1467,7 +1751,8 @@ if (cptr && (*cptr != 0)) if (sim_log) fprintf (st, "Logging enabled to \"%s\"\n", sim_logfile_name (sim_log, sim_log_ref)); -else fprintf (st, "Logging disabled\n"); +else + fprintf (st, "Logging disabled\n"); return SCPE_OK; } @@ -1591,7 +1876,8 @@ if (sim_deb) { } } } -else fprintf (st, "Debug output disabled\n"); +else + fprintf (st, "Debug output disabled\n"); return SCPE_OK; } diff --git a/sim_console.h b/sim_console.h index a36d452e..7f2d4901 100644 --- a/sim_console.h +++ b/sim_console.h @@ -113,6 +113,7 @@ 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, CONST char *cptr); t_stat sim_set_noconsole_port (void); +t_stat sim_set_stable_registers_state (void); t_stat sim_poll_kbd (void); t_stat sim_putchar (int32 c); t_stat sim_putchar_s (int32 c); diff --git a/sim_frontpanel.c b/sim_frontpanel.c index f8ff5aab..fde13a2f 100644 --- a/sim_frontpanel.c +++ b/sim_frontpanel.c @@ -134,6 +134,7 @@ struct PANEL { size_t reg_count; REG *regs; char *reg_query; + int new_register; size_t reg_query_size; unsigned long long array_element_data; OperationalState State; @@ -143,6 +144,8 @@ struct PANEL { int io_thread_running; pthread_mutex_t io_lock; pthread_mutex_t io_send_lock; + pthread_mutex_t io_command_lock; + int command_count; int io_reg_query_pending; int io_waiting; char *io_response; @@ -154,7 +157,7 @@ struct PANEL { pthread_t callback_thread; int callback_thread_running; void *callback_context; - int callbacks_per_second; + int usecs_between_callbacks; int debug; char *simulator_version; int radix; @@ -167,10 +170,16 @@ struct PANEL { }; static const char *sim_prompt = "sim> "; +static const char *register_repeat_prefix = "repeat every "; +static const char *register_repeat_stop = "repeat stop"; +static const char *register_repeat_stop_all = "repeat stop all"; +static const char *register_repeat_units = " usecs "; static const char *register_get_prefix = "show time"; static const char *register_get_echo = "# REGISTERS-DONE"; +static const char *register_repeat_echo = "# REGISTERS-REPEAT-DONE"; static const char *register_dev_echo = "# REGISTERS-FOR-DEVICE:"; static const char *register_ind_echo = "# REGISTER-INDIRECT:"; +static const char *command_status = "ECHO Status:%STATUS%-%TSTATUS%"; static const char *command_done_echo = "# COMMAND-DONE"; static int little_endian; static void *_panel_reader(void *arg); @@ -209,7 +218,18 @@ if (p == NULL) return p; } -static void _panel_debug (PANEL *p, int dbits, const char *fmt, const char *buf, int bufsize, ...) +/* Allow compiler to help validate printf style format arguments */ +#if !defined __GNUC__ +#define GCC_FMT_ATTR(n, m) +#endif +#if !defined(GCC_FMT_ATTR) +#define GCC_FMT_ATTR(n, m) __attribute__ ((format (__printf__, n, m))) +#endif + +static void __panel_debug (PANEL *p, int dbits, const char *fmt, const char *buf, int bufsize, ...) GCC_FMT_ATTR(3, 6); +#define _panel_debug(p, dbits, fmt, buf, bufsize, ...) do { if (p && p->Debug && (dbits & p->debug)) __panel_debug (p, dbits, fmt, buf, bufsize, ##__VA_ARGS__);} while (0) + +static void __panel_debug (PANEL *p, int dbits, const char *fmt, const char *buf, int bufsize, ...) { if (p && p->Debug && (dbits & p->debug)) { int i; @@ -300,6 +320,7 @@ sim_panel_set_debug_file (PANEL *panel, const char *debug_file) if (!panel) return; panel->Debug = fopen(debug_file, "w"); +setvbuf (panel->Debug, NULL, _IOFBF, 65536); } void @@ -348,13 +369,13 @@ return sent; } static int -_panel_sendf (PANEL *p, int wait_for_completion, char **response, const char *fmt, ...); +_panel_sendf (PANEL *p, int *completion_status, char **response, const char *fmt, ...); static int _panel_register_query_string (PANEL *panel, char **buf, size_t *buf_size) { size_t i, j, buf_data, buf_needed = 0; -char *dev; +const char *dev; pthread_mutex_lock (&panel->io_lock); buf_needed = 2 + strlen (register_get_prefix); /* SHOW TIME */ @@ -381,7 +402,7 @@ sprintf (*buf + buf_data, "%s\r", register_get_prefix); buf_data += strlen (*buf + buf_data); dev = ""; for (i=j=0; ireg_count; i++) { - char *reg_dev = panel->regs[i].device_name ? panel->regs[i].device_name : ""; + const char *reg_dev = panel->regs[i].device_name ? panel->regs[i].device_name : ""; if (panel->regs[i].indirect) continue; @@ -412,9 +433,9 @@ for (i=j=0; ireg_count; i++) { } else { if (j == 0) - sprintf (*buf + buf_data, "E -H %s %s[0:%d]", dev, panel->regs[i].name, panel->regs[i].element_count-1); + sprintf (*buf + buf_data, "E -H %s %s[0:%d]", dev, panel->regs[i].name, (int)(panel->regs[i].element_count-1)); else - sprintf (*buf + buf_data, ",%s[0:%d]", panel->regs[i].name, panel->regs[i].element_count-1); + sprintf (*buf + buf_data, ",%s[0:%d]", panel->regs[i].name, (int)(panel->regs[i].element_count-1)); } ++j; buf_data += strlen (*buf + buf_data); @@ -424,7 +445,7 @@ if (buf_data && ((*buf)[buf_data-1] != '\r')) { buf_data += strlen (*buf + buf_data); } for (i=j=0; ireg_count; i++) { - char *reg_dev = panel->regs[i].device_name ? panel->regs[i].device_name : ""; + const char *reg_dev = panel->regs[i].device_name ? panel->regs[i].device_name : ""; if (!panel->regs[i].indirect) continue; @@ -495,6 +516,7 @@ FILE *fOut = NULL; struct stat statb; char *buf = NULL; int port; +int cmd_stat; size_t i, device_num; char hostport[64]; union {int i; char c[sizeof (int)]; } end_test; @@ -540,7 +562,6 @@ else { } else break; - } if (stat (sim_config, &statb) < 0) { sim_panel_set_error ("Can't stat simulator configuration '%s': %s", sim_config, strerror(errno)); @@ -599,6 +620,30 @@ if (debug_file) { sim_panel_set_debug_file (p, debug_file); sim_panel_set_debug_mode (p, DBG_XMT|DBG_RCV); _panel_debug (p, DBG_XMT|DBG_RCV, "Creating Simulator Process %s\n", NULL, 0, sim_path); + + if (stat (p->temp_config, &statb) < 0) { + sim_panel_set_error ("Can't stat temporary simulator configuration '%s': %s", p->temp_config, strerror(errno)); + goto Error_Return; + } + buf = (char *)_panel_malloc (statb.st_size+1); + if (buf == NULL) + goto Error_Return; + buf[statb.st_size] = '\0'; + fIn = fopen (p->temp_config, "r"); + if (fIn == NULL) { + sim_panel_set_error ("Can't open temporary configuration file '%s': %s", p->temp_config, strerror(errno)); + goto Error_Return; + } + _panel_debug (p, DBG_XMT|DBG_RCV, "Using Temporary Configuration File '%s' containing:", NULL, 0, p->temp_config); + i = 0; + while (fgets (buf, statb.st_size, fIn)) { + ++i; + buf[strlen(buf) - 1] = '\0'; + _panel_debug (p, DBG_XMT|DBG_RCV, "Line %2d: %s", NULL, 0, (int)i, buf); + } + free (buf); + buf = NULL; + fclose (fIn); } if (!simulator_panel) { #if defined(_WIN32) @@ -659,9 +704,10 @@ if (p->sock == INVALID_SOCKET) { } goto Error_Return; } -_panel_debug (p, DBG_XMT|DBG_RCV, "Connected to simulator at %s after %dms\n", NULL, 0, p->hostport, i*100); +_panel_debug (p, DBG_XMT|DBG_RCV, "Connected to simulator on %s after %dms\n", NULL, 0, p->hostport, (int)i*100); pthread_mutex_init (&p->io_lock, NULL); pthread_mutex_init (&p->io_send_lock, NULL); +pthread_mutex_init (&p->io_command_lock, NULL); pthread_cond_init (&p->io_done, NULL); pthread_cond_init (&p->startup_cond, NULL); if (sizeof(mantra) != _panel_send (p, (char *)mantra, sizeof(mantra))) { @@ -696,7 +742,7 @@ else { if (p->State == Error) goto Error_Return; /* Validate sim_frontpanel API version */ - if (_panel_sendf (p, 1, &p->simulator_version, "SHOW VERSION\r")) + if (_panel_sendf (p, &cmd_stat, &p->simulator_version, "SHOW VERSION\r")) goto Error_Return; if (1) { int api_version = 0; @@ -712,7 +758,7 @@ else { if (1) { char *radix = NULL; - if (_panel_sendf (p, 1, &radix, "SHOW %s RADIX\r", p->device_name ? p->device_name : "")) { + if (_panel_sendf (p, &cmd_stat, &radix, "SHOW %s RADIX\r", p->device_name ? p->device_name : "")) { free (radix); goto Error_Return; } @@ -807,18 +853,23 @@ if (panel) { int wait_count; /* First, wind down the automatic register queries */ - sim_panel_set_display_callback (panel, NULL, NULL, 0); - /* Next, attempt a simulator shutdown */ - _panel_send (panel, "\005\rEXIT\r", 7); + sim_panel_set_display_callback_interval (panel, NULL, NULL, 0); + /* Next, attempt a simulator shutdown only with the master panel */ + if (panel->parent == NULL) { + if (panel->State == Run) + _panel_send (panel, "\005\r", 2); + _panel_send (panel, "EXIT\r", 5); + } /* Wait for up to 2 seconds for a graceful shutdown */ for (wait_count=0; panel->io_thread_running && (wait_count<20); ++wait_count) msleep (100); - /* Now close the socket which should stop a pending read which hasn't completed */ + /* Now close the socket which should stop a pending read that hasn't completed */ panel->sock = INVALID_SOCKET; sim_close_sock (sock); pthread_join (panel->io_thread, NULL); pthread_mutex_destroy (&panel->io_lock); pthread_mutex_destroy (&panel->io_send_lock); + pthread_mutex_destroy (&panel->io_command_lock); pthread_cond_destroy (&panel->io_done); } #if defined(_WIN32) @@ -881,11 +932,16 @@ _panel_add_register (PANEL *panel, REG *regs, *reg; char *response = NULL; size_t i; +int cmd_stat; if (!panel || (panel->State == Error)) { sim_panel_set_error ("Invalid Panel"); return -1; } +if (panel->State == Run) { + sim_panel_set_error ("Not Halted"); + return -1; + } regs = (REG *)_panel_malloc ((1 + panel->reg_count)*sizeof(*regs)); if (regs == NULL) { panel->State = Error; @@ -953,7 +1009,7 @@ reg->size = size; reg->element_count = element_count; pthread_mutex_unlock (&panel->io_lock); /* Validate existence of requested register/array */ -if (_panel_sendf (panel, 1, &response, "EXAMINE %s %s%s\r", device_name? device_name : "", name, (element_count > 0) ? "[0]" : "")) { +if (_panel_sendf (panel, &cmd_stat, &response, "EXAMINE %s %s%s\r", device_name? device_name : "", name, (element_count > 0) ? "[0]" : "")) { free (reg->name); free (reg->device_name); free (regs); @@ -969,7 +1025,7 @@ if (!strcmp ("Invalid argument\r\n", response)) { } free (response); if (element_count > 0) { - if (_panel_sendf (panel, 1, &response, "EXAMINE %s %s[%d]\r", device_name? device_name : "", name, element_count-1)) { + if (_panel_sendf (panel, &cmd_stat, &response, "EXAMINE %s %s[%d]\r", device_name? device_name : "", name, element_count-1)) { free (reg->name); free (reg->device_name); free (regs); @@ -989,6 +1045,7 @@ pthread_mutex_lock (&panel->io_lock); ++panel->reg_count; free (panel->regs); panel->regs = regs; +panel->new_register = 1; pthread_mutex_unlock (&panel->io_lock); /* Now build the register query string for the whole register list */ if (_panel_register_query_string (panel, &panel->reg_query, &panel->reg_query_size)) @@ -1028,14 +1085,14 @@ sim_panel_add_register_indirect (PANEL *panel, return _panel_add_register (panel, name, device_name, size, addr, 1, 0); } -int -sim_panel_get_registers (PANEL *panel, unsigned long long *simulation_time) +static int +_panel_get_registers (PANEL *panel, int calledback, unsigned long long *simulation_time) { if ((!panel) || (panel->State == Error)) { sim_panel_set_error ("Invalid Panel"); return -1; } -if (panel->callback) { +if ((!calledback) && (panel->callback)) { sim_panel_set_error ("Callback provides register data"); return -1; } @@ -1043,11 +1100,18 @@ if (!panel->reg_count) { sim_panel_set_error ("No registers specified"); return -1; } +pthread_mutex_lock (&panel->io_command_lock); pthread_mutex_lock (&panel->io_lock); if (panel->reg_query_size != _panel_send (panel, panel->reg_query, panel->reg_query_size)) { pthread_mutex_unlock (&panel->io_lock); + pthread_mutex_unlock (&panel->io_command_lock); return -1; } +while (panel->io_reg_query_pending != 0) { + pthread_mutex_unlock (&panel->io_lock); + msleep (100); + pthread_mutex_lock (&panel->io_lock); + } ++panel->io_reg_query_pending; panel->io_waiting = 1; while (panel->io_waiting) @@ -1055,14 +1119,21 @@ while (panel->io_waiting) if (simulation_time) *simulation_time = panel->simulation_time; pthread_mutex_unlock (&panel->io_lock); +pthread_mutex_unlock (&panel->io_command_lock); return 0; } int -sim_panel_set_display_callback (PANEL *panel, - PANEL_DISPLAY_PCALLBACK callback, - void *context, - int callbacks_per_second) +sim_panel_get_registers (PANEL *panel, unsigned long long *simulation_time) +{ +return _panel_get_registers (panel, 0, simulation_time); +} + +int +sim_panel_set_display_callback_interval (PANEL *panel, + PANEL_DISPLAY_PCALLBACK callback, + void *context, + int usecs_between_callbacks) { if (!panel) { sim_panel_set_error ("Invalid Panel"); @@ -1071,10 +1142,10 @@ if (!panel) { pthread_mutex_lock (&panel->io_lock); panel->callback = callback; panel->callback_context = context; -if (callbacks_per_second && (0 == panel->callbacks_per_second)) { /* Need to start callbacks */ +if (usecs_between_callbacks && (0 == panel->usecs_between_callbacks)) { /* Need to start/enable callbacks */ pthread_attr_t attr; - panel->callbacks_per_second = callbacks_per_second; + panel->usecs_between_callbacks = usecs_between_callbacks; pthread_cond_init (&panel->startup_cond, NULL); pthread_attr_init(&attr); pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); @@ -1084,8 +1155,8 @@ if (callbacks_per_second && (0 == panel->callbacks_per_second)) { /* Need to sta pthread_cond_wait (&panel->startup_cond, &panel->io_lock); /* Wait for thread to stabilize */ pthread_cond_destroy (&panel->startup_cond); } -if ((callbacks_per_second == 0) && panel->callbacks_per_second) { /* Need to stop callbacks */ - panel->callbacks_per_second = 0; +if ((usecs_between_callbacks == 0) && panel->usecs_between_callbacks) { /* Need to stop callbacks */ + panel->usecs_between_callbacks = 0; pthread_mutex_unlock (&panel->io_lock); pthread_join (panel->callback_thread, NULL); pthread_mutex_lock (&panel->io_lock); @@ -1127,7 +1198,7 @@ if (panel->State == Run) { sim_panel_set_error ("Not Halted"); return -1; } -if (_panel_sendf (panel, 0, NULL, "BOOT %s\r", device)) +if (_panel_sendf (panel, NULL, NULL, "BOOT %s\r", device)) return -1; panel->State = Run; return 0; @@ -1148,7 +1219,7 @@ if (panel->State == Run) { sim_panel_set_error ("Not Halted"); return -1; } -if (_panel_sendf (panel, 0, NULL, "CONT\r", 5)) +if (_panel_sendf (panel, NULL, NULL, "CONT\r", 5)) return -1; panel->State = Run; return 0; @@ -1179,6 +1250,9 @@ return 0; int sim_panel_break_set (PANEL *panel, const char *condition) { +char *response = NULL; +int cmd_stat; + if (!panel || (panel->State == Error)) { sim_panel_set_error ("Invalid Panel"); return -1; @@ -1192,14 +1266,22 @@ if (panel->State == Run) { return -1; } -if (_panel_sendf (panel, 1, NULL, "BREAK %s\r", condition)) +if ((_panel_sendf (panel, &cmd_stat, &response, "BREAK %s\r", condition)) || + (*response)) { + sim_panel_set_error ("Error establishing breakpoint at '%s': %s", condition, response ? response : ""); + free (response); return -1; + } +free (response); return 0; } int sim_panel_break_clear (PANEL *panel, const char *condition) { +char *response = NULL; +int cmd_stat; + if (!panel || (panel->State == Error)) { sim_panel_set_error ("Invalid Panel"); return -1; @@ -1213,14 +1295,22 @@ if (panel->State == Run) { return -1; } -if (_panel_sendf (panel, 1, NULL, "NOBREAK %s\r", condition)) +if ((_panel_sendf (panel, &cmd_stat, &response, "NOBREAK %s\r", condition)) || + (*response)) { + sim_panel_set_error ("Error clearing breakpoint at '%s': %s", condition, response ? response : ""); + free (response); return -1; + } +free (response); return 0; } int sim_panel_break_output_set (PANEL *panel, const char *condition) { +char *response = NULL; +int cmd_stat; + if (!panel || (panel->State == Error)) { sim_panel_set_error ("Invalid Panel"); return -1; @@ -1234,14 +1324,22 @@ if (panel->State == Run) { return -1; } -if (_panel_sendf (panel, 1, NULL, "EXPECT %s\r", condition)) +if ((_panel_sendf (panel, &cmd_stat, &response, "EXPECT %s\r", condition)) || + (*response)) { + sim_panel_set_error ("Error establishing output breakpoint for '%s': %s", condition, response ? response : ""); + free (response); return -1; + } +free (response); return 0; } int sim_panel_break_output_clear (PANEL *panel, const char *condition) { +char *response = NULL; +int cmd_stat; + if (!panel || (panel->State == Error)) { sim_panel_set_error ("Invalid Panel"); return -1; @@ -1255,8 +1353,13 @@ if (panel->State == Run) { return -1; } -if (_panel_sendf (panel, 1, NULL, "NOEXPECT %s\r", condition)) +if ((_panel_sendf (panel, &cmd_stat, &response, "NOEXPECT %s\r", condition)) || + (*response)) { + sim_panel_set_error ("Error clearing output breakpoint for '%s': %s", condition, response ? response : ""); + free (response); return -1; + } +free (response); return 0; } @@ -1279,6 +1382,7 @@ sim_panel_gen_examine (PANEL *panel, { char *response = NULL, *c; unsigned long long data = 0; +int cmd_stat; if (!panel || (panel->State == Error)) { sim_panel_set_error ("Invalid Panel"); @@ -1288,7 +1392,7 @@ if (panel->State == Run) { sim_panel_set_error ("Not Halted"); return -1; } -if (_panel_sendf (panel, 1, &response, "EXAMINE -H %s", name_or_addr)) { +if (_panel_sendf (panel, &cmd_stat, &response, "EXAMINE -H %s", name_or_addr)) { free (response); return -1; } @@ -1325,6 +1429,7 @@ sim_panel_gen_deposit (PANEL *panel, const void *value) { unsigned long long data = 0; +int cmd_stat; if (!panel || (panel->State == Error)) { sim_panel_set_error ("Invalid Panel"); @@ -1338,7 +1443,7 @@ if (little_endian) memcpy (&data, value, size); else memcpy (((char *)&data) + sizeof(data)-size, value, size); -if (_panel_sendf (panel, 1, NULL, "DEPOSIT -H %s %llx", name_or_addr, data)) +if (_panel_sendf (panel, &cmd_stat, NULL, "DEPOSIT -H %s %llx", name_or_addr, data)) return -1; return 0; } @@ -1367,6 +1472,7 @@ sim_panel_mem_examine (PANEL *panel, { char *response = NULL, *c; unsigned long long data = 0, address = 0; +int cmd_stat; if (!panel || (panel->State == Error)) { sim_panel_set_error ("Invalid Panel"); @@ -1380,7 +1486,7 @@ if (little_endian) memcpy (&address, addr, addr_size); else memcpy (((char *)&address) + sizeof(address)-addr_size, addr, addr_size); -if (_panel_sendf (panel, 1, &response, (panel->radix == 16) ? "EXAMINE -H %llx" : "EXAMINE -H %llo", address)) { +if (_panel_sendf (panel, &cmd_stat, &response, (panel->radix == 16) ? "EXAMINE -H %llx" : "EXAMINE -H %llo", address)) { free (response); return -1; } @@ -1422,6 +1528,7 @@ sim_panel_mem_deposit (PANEL *panel, const void *value) { unsigned long long data = 0, address = 0; +int cmd_stat; if (!panel || (panel->State == Error)) { sim_panel_set_error ("Invalid Panel"); @@ -1439,7 +1546,7 @@ else { memcpy (((char *)&data) + sizeof(data)-value_size, value, value_size); memcpy (((char *)&address) + sizeof(address)-addr_size, addr, addr_size); } -if (_panel_sendf (panel, 1, NULL, (panel->radix == 16) ? "DEPOSIT -H %llx %llx" : "DEPOSIT -H %llo %llx", address, data)) +if (_panel_sendf (panel, &cmd_stat, NULL, (panel->radix == 16) ? "DEPOSIT -H %llx %llx" : "DEPOSIT -H %llo %llx", address, data)) return -1; return 0; } @@ -1460,6 +1567,8 @@ sim_panel_set_register_value (PANEL *panel, const char *name, const char *value) { +int cmd_stat; + if (!panel || (panel->State == Error)) { sim_panel_set_error ("Invalid Panel"); return -1; @@ -1468,7 +1577,7 @@ if (panel->State == Run) { sim_panel_set_error ("Not Halted"); return -1; } -if (_panel_sendf (panel, 1, NULL, "DEPOSIT %s %s", name, value)) +if (_panel_sendf (panel, &cmd_stat, NULL, "DEPOSIT %s %s", name, value)) return -1; return 0; } @@ -1487,30 +1596,42 @@ sim_panel_mount (PANEL *panel, const char *switches, const char *path) { -char *response = NULL, *status = NULL; +char *response = NULL; +OperationalState OrigState; +int stat = 0; +int cmd_stat; if (!panel || (panel->State == Error)) { sim_panel_set_error ("Invalid Panel"); return -1; } -if (_panel_sendf (panel, 1, &response, "ATTACH %s %s %s", switches, device, path)) { - free (response); - return -1; - } -if (_panel_sendf (panel, 1, &status, "ECHO %%STATUS%%")) { - free (response); - free (status); - return -1; - } -if (!status || (strcmp (status, "00000000\r\n"))) { - sim_panel_set_error (response); - free (response); - free (status); - return -1; +pthread_mutex_lock (&panel->io_lock); +OrigState = panel->State; +if (OrigState == Run) { + sim_panel_exec_halt (panel); + while (panel->State == Run) { + pthread_mutex_unlock (&panel->io_lock); + msleep (100); + pthread_mutex_lock (&panel->io_lock); + } } +pthread_mutex_unlock (&panel->io_lock); +do { + if (_panel_sendf (panel, &cmd_stat, &response, "ATTACH %s %s %s", switches, device, path)) { + stat = -1; + break; + } + if (cmd_stat) { + sim_panel_set_error (response); + stat = -1; + } + } while (0); +pthread_mutex_lock (&panel->io_lock); +if (OrigState == Run) + sim_panel_exec_run (panel); +pthread_mutex_unlock (&panel->io_lock); free (response); -free (status); -return 0; +return stat; } /** @@ -1523,30 +1644,42 @@ int sim_panel_dismount (PANEL *panel, const char *device) { -char *response = NULL, *status = NULL; +char *response = NULL; +OperationalState OrigState; +int stat = 0; +int cmd_stat; if (!panel || (panel->State == Error)) { sim_panel_set_error ("Invalid Panel"); return -1; } -if (_panel_sendf (panel, 1, &response, "DETACH %s", device)) { - free (response); - return -1; - } -if (_panel_sendf (panel, 1, &status, "ECHO %%STATUS%%")) { - free (response); - free (status); - return -1; - } -if (!status || (strcmp (status, "00000000\r\n"))) { - sim_panel_set_error (response); - free (response); - free (status); - return -1; +pthread_mutex_lock (&panel->io_lock); +OrigState = panel->State; +if (OrigState == Run) { + sim_panel_exec_halt (panel); + while (panel->State == Run) { + pthread_mutex_unlock (&panel->io_lock); + msleep (100); + pthread_mutex_lock (&panel->io_lock); + } } +pthread_mutex_unlock (&panel->io_lock); +do { + if (_panel_sendf (panel, &cmd_stat, &response, "DETACH %s", device)) { + stat = -1; + break; + } + if (cmd_stat) { + sim_panel_set_error (response); + stat = -1; + } + } while (0); +pthread_mutex_lock (&panel->io_lock); +if (OrigState == Run) + sim_panel_exec_run (panel); +pthread_mutex_unlock (&panel->io_lock); free (response); -free (status); -return 0; +return stat; } @@ -1683,8 +1816,7 @@ while ((p->sock != INVALID_SOCKET) && else { size_t name_len = strlen (p->regs[i].name); - if ((0 == memcmp (p->regs[i].name, s, name_len), s) && - (s[name_len] == '[')) { + if ((0 == memcmp (p->regs[i].name, s, name_len)) && (s[name_len] == '[')) { size_t array_index = (size_t)atoi (s + name_len + 1); size_t end_index = array_index; char *end = strchr (s + name_len + 1, '['); @@ -1714,22 +1846,23 @@ while ((p->sock != INVALID_SOCKET) && *e = ':'; /* Unexpected Register Data Found (or other output containing a : character) */ } - if (!strcmp (s + strlen (sim_prompt), register_get_echo)) { - pthread_mutex_lock (&p->io_lock); - --p->io_reg_query_pending; + if (!strcmp (s + strlen (sim_prompt), register_repeat_echo)) { if (p->callback) { pthread_mutex_unlock (&p->io_lock); p->callback (p, p->simulation_time, p->callback_context); } - else { - p->io_waiting = 0; - pthread_cond_signal (&p->io_done); - pthread_mutex_unlock (&p->io_lock); - } + } + if (!strcmp (s + strlen (sim_prompt), register_get_echo)) { + pthread_mutex_lock (&p->io_lock); + --p->io_reg_query_pending; + p->io_waiting = 0; + pthread_cond_signal (&p->io_done); + pthread_mutex_unlock (&p->io_lock); } else { pthread_mutex_lock (&p->io_lock); if (!strcmp (s + strlen (sim_prompt), command_done_echo)) { + _panel_debug (p, DBG_RCV, "Received Command Complete", NULL, 0); p->io_waiting = 0; pthread_cond_signal (&p->io_done); } @@ -1751,11 +1884,14 @@ while ((p->sock != INVALID_SOCKET) && p->io_response = t; p->io_response_size = p->io_response_data + strlen (s) + 3; } + _panel_debug (p, DBG_RCV, "Receive Data Accumulated: '%s'", NULL, 0, s); strcpy (p->io_response + p->io_response_data, s); p->io_response_data += strlen(s); strcpy (p->io_response + p->io_response_data, "\r\n"); p->io_response_data += 2; } + else + _panel_debug (p, DBG_RCV, "Receive Data Discarded: '%s'", NULL, 0, s); } pthread_mutex_unlock (&p->io_lock); } @@ -1794,6 +1930,7 @@ struct sched_param sched_priority; char *buf = NULL; size_t buf_data = 0; unsigned int callback_count = 0; +int cmd_stat; /* Boost Priority for timer thread so it doesn't compete @@ -1810,31 +1947,78 @@ pthread_cond_signal (&p->startup_cond); /* Signal we're ready to go */ msleep (100); pthread_mutex_lock (&p->io_lock); while ((p->sock != INVALID_SOCKET) && - (p->callbacks_per_second) && + (p->usecs_between_callbacks) && (p->State != Error)) { - int rate = p->callbacks_per_second; + int interval = p->usecs_between_callbacks; + int new_register = p->new_register; + p->new_register = 0; pthread_mutex_unlock (&p->io_lock); - ++callback_count; - if (1 == callback_count%rate) { /* once a second update the query string */ + if (new_register) /* need to get and send updated register info */ _panel_register_query_string (p, &buf, &buf_data); - } - msleep (1000/rate); + + /* twice a second activities: */ + /* 1) update the query string if it has changed */ + /* (only really happens at startup) */ + /* 2) update register state by polling if the simulator is halted */ + msleep (500); pthread_mutex_lock (&p->io_lock); - if (((p->State == Run) || ((p->State == Halt) && (0 == callback_count%(5*rate)))) && - (p->io_reg_query_pending == 0)) { - ++p->io_reg_query_pending; + if (new_register) { + if (p->io_reg_query_pending == 0) { + size_t repeat_data = strlen (register_repeat_prefix) + /* prefix */ + 20 + /* max int width */ + strlen (register_repeat_units) + /* units and spacing */ + buf_data + /* command contents */ + 1 + /* carriage return */ + strlen (register_repeat_echo) + /* auto repeat completion */ + 1 + /* carriage return */ + 1; /* NUL */ + char *repeat = (char *)malloc (repeat_data); + char *c; + + sprintf (repeat, "%s%d%s%*.*s", register_repeat_prefix, + p->usecs_between_callbacks, + register_repeat_units, + (int)buf_data, (int)buf_data, buf); + pthread_mutex_unlock (&p->io_lock); + for (c = strchr (repeat, '\r'); c != NULL; c = strchr (c, '\r')) + *c = ';'; /* replace carriage returns with semicolons */ + c = strstr (repeat, register_get_echo); /* remove register_done_echo string and */ + strcpy (c, register_repeat_echo); /* replace it with the register_repeat_echo string */ + if (_panel_sendf (p, &cmd_stat, NULL, "%s", repeat)) { + pthread_mutex_lock (&p->io_lock); + free (repeat); + break; + } + pthread_mutex_lock (&p->io_lock); + free (repeat); + } + else { /* already busy */ + p->new_register = 1; /* retry later */ + _panel_debug (p, DBG_XMT, "Waiting on prior command completion before specifying repeat interval", NULL, 0); + } + } + /* when halted, we directly poll the halted system to get updated */ + /* register state which may have changed due to panel activities */ + if (p->State == Halt) { pthread_mutex_unlock (&p->io_lock); - if (buf_data != _panel_send (p, buf, buf_data)) { + if (_panel_get_registers (p, 1, NULL)) { pthread_mutex_lock (&p->io_lock); break; } pthread_mutex_lock (&p->io_lock); } - else - _panel_debug (p, DBG_XMT, "Waiting for prior register query completion", NULL, 0); } +pthread_mutex_unlock (&p->io_lock); +/* stop any established repeating activity in the simulator */ +if (p->parent == NULL) /* Top level panel? */ + _panel_sendf (p, &cmd_stat, NULL, "%s", register_repeat_stop_all); +else { + if (p->State == Run) + _panel_sendf (p, &cmd_stat, NULL, "%s", register_repeat_stop); + } +pthread_mutex_lock (&p->io_lock); p->callback_thread_running = 0; pthread_mutex_unlock (&p->io_lock); free (buf); @@ -1905,13 +2089,13 @@ return; } static int -_panel_sendf (PANEL *p, int wait_for_completion, char **response, const char *fmt, ...) +_panel_sendf (PANEL *p, int *completion_status, char **response, const char *fmt, ...) { char stackbuf[1024]; int bufsize = sizeof(stackbuf); char *buf = stackbuf; -int len; -int post_fix_len = wait_for_completion ? 5 + strlen (command_done_echo): 1; +int len, status_echo_len = 0; +int post_fix_len = completion_status ? 7 + sizeof (command_done_echo) + sizeof (command_status) : 1; va_list arglist; int ret; @@ -1938,20 +2122,23 @@ while (1) { /* format passed string, arg } if (len && (buf[len-1] != '\r')) { - strcat (buf, "\r"); /* Make sure command line is terminated */ + strcpy (&buf[len], "\r"); /* Make sure command line is terminated */ ++len; } -if (wait_for_completion) { - strcat (buf, command_done_echo); - strcat (buf, "\r"); +pthread_mutex_lock (&p->io_command_lock); +++p->command_count; +if (completion_status) { + sprintf (&buf[len], "%s\r%s\r", command_status, command_done_echo); + status_echo_len = strlen (&buf[len]); pthread_mutex_lock (&p->io_lock); p->io_response_data = 0; } -ret = (strlen (buf) == _panel_send (p, buf, strlen (buf))) ? 0 : -1; +_panel_debug (p, DBG_REQ, "Command %d Request%s: %*.*s", NULL, 0, p->command_count, completion_status ? " (with response)" : "", len, len, buf); +ret = ((len + status_echo_len) == _panel_send (p, buf, len + status_echo_len)) ? 0 : -1; -if (wait_for_completion) { +if (completion_status) { if (!ret) { /* Sent OK? */ p->io_waiting = 1; while (p->io_waiting) @@ -1959,16 +2146,33 @@ if (wait_for_completion) { if (response) { *response = (char *)_panel_malloc (p->io_response_data + 1); if (0 == memcmp (buf, p->io_response + strlen (sim_prompt), len)) { + char *eol, *status; memcpy (*response, p->io_response + strlen (sim_prompt) + len + 1, p->io_response_data + 1 - (strlen (sim_prompt) + len + 1)); + *completion_status = -1; + status = strstr (*response, command_status); + if (status) { + *(status - strlen (sim_prompt)) = '\0'; + status += strlen (command_status) + 2; + eol = strchr (status, '\r'); + if (eol) + *eol = '\0'; + sscanf (status, "Status:%08X-", completion_status); + } } else memcpy (*response, p->io_response, p->io_response_data + 1); + _panel_debug (p, DBG_RSP, "Command %d Response(Status=%d): '%s'", NULL, 0, p->command_count, *completion_status, *response); + } + else { + if (p->io_response_data) + _panel_debug (p, DBG_RSP, "Discarded Unwanted Command %d Response Data:", p->io_response, p->io_response_data, p->command_count); } - p->io_response_data = 0; - p->io_response[0] = '\0'; } + p->io_response_data = 0; + p->io_response[0] = '\0'; pthread_mutex_unlock (&p->io_lock); } +pthread_mutex_unlock (&p->io_command_lock); if (buf != stackbuf) free (buf); diff --git a/sim_frontpanel.h b/sim_frontpanel.h index c01cc5b4..86aa4d6d 100644 --- a/sim_frontpanel.h +++ b/sim_frontpanel.h @@ -57,7 +57,7 @@ extern "C" { #if !defined(__VAX) /* Unsupported platform */ -#define SIM_FRONTPANEL_VERSION 2 +#define SIM_FRONTPANEL_VERSION 3 /** @@ -128,7 +128,7 @@ sim_panel_destroy (PANEL *panel); simulator. The registers that a particular frontpanel application mught need - access to are described by the application by calling: + access to are described by the application when it calls: sim_panel_add_register sim_panel_add_register_array @@ -175,10 +175,10 @@ sim_panel_add_register_indirect (PANEL *panel, 1) The values can be polled (when ever it is desired) by calling sim_panel_get_registers(). - 2) The panel can call sim_panel_set_display_callback() to specify a - callback routine and a periodic rate that the callback routine - should be called. The panel API will make a best effort to deliver - the current register state at the desired rate. + 2) The panel can call sim_panel_set_display_callback_interval() to + specify a callback routine and a periodic rate that the callback + routine should be called. The panel API will make a best effort + to deliver the current register state at the desired rate. Note 1: The buffers described in a panel's register set will be @@ -203,10 +203,10 @@ typedef void (*PANEL_DISPLAY_PCALLBACK)(PANEL *panel, void *context); int -sim_panel_set_display_callback (PANEL *panel, - PANEL_DISPLAY_PCALLBACK callback, - void *context, - int callbacks_per_second); +sim_panel_set_display_callback_interval (PANEL *panel, + PANEL_DISPLAY_PCALLBACK callback, + void *context, + int usecs_between_callbacks); /** @@ -450,6 +450,8 @@ sim_panel_set_debug_file (PANEL *panel, const char *debug_file); #define DBG_XMT 1 /* Transmit Data */ #define DBG_RCV 2 /* Receive Data */ +#define DBG_REQ 4 /* Request Data */ +#define DBG_RSP 8 /* Response Data */ void sim_panel_set_debug_mode (PANEL *panel, int debug_bits);