| /* sim_frontpanel.c: simulator frontpanel API | |
| Copyright (c) 2015, Mark Pizzolato | |
| Permission is hereby granted, free of charge, to any person obtaining a | |
| copy of this software and associated documentation files (the "Software"), | |
| to deal in the Software without restriction, including without limitation | |
| the rights to use, copy, modify, merge, publish, distribute, sublicense, | |
| and/or sell copies of the Software, and to permit persons to whom the | |
| Software is furnished to do so, subject to the following conditions: | |
| The above copyright notice and this permission notice shall be included in | |
| all copies or substantial portions of the Software. | |
| THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
| IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
| FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
| MARK PIZZOLATO BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER | |
| IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN | |
| CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. | |
| Except as contained in this notice, the name of Mark Pizzolato shall not be | |
| used in advertising or otherwise to promote the sale, use or other dealings | |
| in this Software without prior written authorization from Mark Pizzolato. | |
| 05-Feb-15 MP Initial implementation | |
| 01-Apr-15 MP Added register indirect, mem_examine and mem_deposit | |
| 03-Apr-15 MP Added logic to pass simulator startup messages in | |
| panel error text if the connection to the simulator | |
| shuts down while it is starting. | |
| 04-Apr-15 MP Added mount and dismount routines to connect and | |
| disconnect removable media | |
| This module provides interface between a front panel application and a simh | |
| simulator. Facilities provide ways to gather information from and to | |
| observe and control the state of a simulator. | |
| The details of the 'wire protocol' are internal to the API interfaces | |
| provided here and described in sim_frontpanel.h. These details are subject | |
| to change from one sim_frontpanel version to the next, while all efforts | |
| will be made to retain any prior sim_frontpanel API interfaces. | |
| */ | |
| #ifdef __cplusplus | |
| extern "C" { | |
| #endif | |
| #include "sim_frontpanel.h" | |
| #include <stdio.h> | |
| #include <stdarg.h> | |
| #include <string.h> | |
| #include <ctype.h> | |
| #include <stdlib.h> | |
| #include <sys/stat.h> | |
| #include <errno.h> | |
| #include <signal.h> | |
| #include <pthread.h> | |
| #include "sim_sock.h" | |
| #if defined(_WIN32) | |
| #include <process.h> | |
| #include <windows.h> | |
| #include <winerror.h> | |
| #define sleep(n) Sleep(n*1000) | |
| #define msleep(n) Sleep(n) | |
| #define strtoull _strtoui64 | |
| #define CLOCK_REALTIME 0 | |
| int clock_gettime(int clk_id, struct timespec *tp) | |
| { | |
| unsigned long long now, unixbase; | |
| unixbase = 116444736; | |
| unixbase *= 1000000000; | |
| GetSystemTimeAsFileTime((FILETIME*)&now); | |
| now -= unixbase; | |
| tp->tv_sec = (long)(now/10000000); | |
| tp->tv_nsec = (now%10000000)*100; | |
| return 0; | |
| } | |
| #else /* NOT _WIN32 */ | |
| #include <unistd.h> | |
| #define msleep(n) usleep(1000*n) | |
| #include <sys/wait.h> | |
| #if defined (__APPLE__) | |
| #define HAVE_STRUCT_TIMESPEC 1 /* OSX defined the structure but doesn't tell us */ | |
| #endif | |
| /* on HP-UX, CLOCK_REALTIME is enum, not preprocessor define */ | |
| #if !defined(CLOCK_REALTIME) && !defined(__hpux) | |
| #define CLOCK_REALTIME 1 | |
| #define NEED_CLOCK_GETTIME 1 | |
| #if !defined(HAVE_STRUCT_TIMESPEC) | |
| #define HAVE_STRUCT_TIMESPEC | |
| #if !defined(_TIMESPEC_DEFINED) | |
| #define _TIMESPEC_DEFINED | |
| struct timespec { | |
| long tv_sec; | |
| long tv_nsec; | |
| }; | |
| #endif /* _TIMESPEC_DEFINED */ | |
| #endif /* HAVE_STRUCT_TIMESPEC */ | |
| #if defined(NEED_CLOCK_GETTIME) | |
| int clock_gettime(int clk_id, struct timespec *tp) | |
| { | |
| struct timeval cur; | |
| struct timezone foo; | |
| gettimeofday (&cur, &foo); | |
| tp->tv_sec = cur.tv_sec; | |
| tp->tv_nsec = cur.tv_usec*1000; | |
| return 0; | |
| } | |
| #endif /* defined(NEED_CLOCK_GETTIME) */ | |
| #endif /* !defined(CLOCK_REALTIME) && !defined(__hpux) */ | |
| #endif /* NOT _WIN32 */ | |
| typedef struct { | |
| char *name; | |
| char *device_name; | |
| void *addr; | |
| size_t size; | |
| int indirect; | |
| size_t element_count; | |
| int *bits; | |
| size_t bit_count; | |
| } REG; | |
| struct PANEL { | |
| PANEL *parent; /* Device Panels can have parent panels */ | |
| char *path; /* simulator path */ | |
| char *config; | |
| char *device_name; /* device name */ | |
| char *temp_config; | |
| char hostport[64]; | |
| size_t device_count; | |
| PANEL **devices; | |
| SOCKET sock; | |
| size_t reg_count; | |
| REG *regs; | |
| char *reg_query; | |
| int new_register; | |
| size_t reg_query_size; | |
| unsigned long long array_element_data; | |
| volatile OperationalState State; | |
| unsigned long long simulation_time; | |
| unsigned long long simulation_time_base; | |
| pthread_t io_thread; | |
| 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_waiting; | |
| char *io_response; | |
| char *halt_reason; | |
| size_t io_response_data; | |
| size_t io_response_size; | |
| const char *completion_string; | |
| pthread_cond_t io_done; | |
| pthread_cond_t startup_done; | |
| PANEL_DISPLAY_PCALLBACK callback; | |
| pthread_t callback_thread; | |
| int callback_thread_running; | |
| void *callback_context; | |
| int usecs_between_callbacks; | |
| pthread_t debugflush_thread; | |
| int debugflush_thread_running; | |
| unsigned int sample_frequency; | |
| unsigned int sample_dither_pct; | |
| unsigned int sample_depth; | |
| int debug; | |
| char *simulator_version; | |
| int radix; | |
| FILE *Debug; | |
| #if defined(_WIN32) | |
| HANDLE hProcess; | |
| DWORD dwProcessId; | |
| #else | |
| pid_t pidProcess; | |
| #endif | |
| }; | |
| /* | |
| * Thread synchronization model: | |
| * | |
| * Mutex: Role: | |
| * io_lock Serialize access to panel state variables | |
| * acquired and released in application threads: | |
| * _panel_register_query_string, | |
| * _panel_establish_register_bits_collection, | |
| * _panel_sendf | |
| * acquired and released in internal threads: | |
| * _panel_callback | |
| * _panel_reader | |
| * io_send_lock Serializes writes to a panel's sockets so that complete | |
| * command/request data can be delivered before another | |
| * thread attempts to write to the socket. | |
| * acquired and released in: _panel_send | |
| * io_command_lock To serialize frontpanel application command requests | |
| * acquired and released in: _panel_get_registers, | |
| * _panel_sendf_completion | |
| * | |
| * Condition Var: Sync Mutex: Purpose & Duration: | |
| * io_done io_lock | |
| * startup_done io_lock Indicate background thread setup is complete. | |
| * Once signaled, it is immediately destroyed. | |
| */ | |
| 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_collect_prefix = "collect "; | |
| static const char *register_collect_mid1 = " samples every "; | |
| static const char *register_collect_mid2 = " cycles dither "; | |
| static const char *register_collect_mid3 = " percent "; | |
| static const char *register_get_postfix = "sampleout"; | |
| static const char *register_get_start = "# REGISTERS-START"; | |
| static const char *register_get_end = "# REGISTERS-DONE"; | |
| static const char *register_repeat_start = "# REGISTERS-REPEAT-START"; | |
| static const char *register_repeat_end = "# 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); | |
| static void *_panel_callback(void *arg); | |
| static void *_panel_debugflusher(void *arg); | |
| static int sim_panel_set_error (PANEL *p, const char *fmt, ...); | |
| static pthread_key_t panel_thread_id; | |
| #define TN_IAC 0xFFu /* -1 */ /* protocol delim */ | |
| #define TN_DONT 0xFEu /* -2 */ /* dont */ | |
| #define TN_DO 0xFDu /* -3 */ /* do */ | |
| #define TN_WONT 0xFCu /* -4 */ /* wont */ | |
| #define TN_WILL 0xFBu /* -5 */ /* will */ | |
| #define TN_BIN 0 /* bin */ | |
| #define TN_ECHO 1 /* echo */ | |
| #define TN_SGA 3 /* sga */ | |
| #define TN_CR 015 /* carriage return */ | |
| #define TN_LF 012 /* line feed */ | |
| #define TN_LINE 34 /* line mode */ | |
| static unsigned char mantra[] = { | |
| TN_IAC, TN_WILL, TN_LINE, | |
| TN_IAC, TN_WILL, TN_SGA, | |
| TN_IAC, TN_WILL, TN_ECHO, | |
| TN_IAC, TN_WILL, TN_BIN, | |
| TN_IAC, TN_DO, TN_BIN | |
| }; | |
| static void * | |
| _panel_malloc (size_t size) | |
| { | |
| void *p = malloc (size); | |
| if (p == NULL) | |
| sim_panel_set_error (NULL, "Out of Memory"); | |
| return p; | |
| } | |
| /* 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_vdebug (PANEL *p, int dbits, const char *fmt, const char *buf, int bufsize, va_list arglist) | |
| { | |
| size_t obufsize = 10240 + 9*bufsize; | |
| while (p && p->Debug && (dbits & p->debug)) { | |
| int i, len; | |
| struct timespec time_now; | |
| char timestamp[32]; | |
| char threadname[50]; | |
| char *obuf = (char *)_panel_malloc (obufsize); | |
| clock_gettime(CLOCK_REALTIME, &time_now); | |
| sprintf (timestamp, "%lld.%03d ", (long long)(time_now.tv_sec), (int)(time_now.tv_nsec/1000000)); | |
| sprintf (threadname, "%s:%s ", p->parent ? p->device_name : "CPU", (pthread_getspecific (panel_thread_id)) ? (char *)pthread_getspecific (panel_thread_id) : ""); | |
| obuf[obufsize - 1] = '\0'; | |
| len = vsnprintf (obuf, obufsize - 1, fmt, arglist); | |
| if (len < 0) | |
| return; | |
| /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */ | |
| if (len >= (int)(obufsize - 9*bufsize)) { | |
| obufsize = len + 1 + 9*bufsize; | |
| free (obuf); | |
| continue; | |
| } | |
| for (i=0; i<bufsize; ++i) { | |
| switch ((unsigned char)buf[i]) { | |
| case TN_CR: | |
| sprintf (&obuf[strlen (obuf)], "_TN_CR_"); | |
| break; | |
| case TN_LF: | |
| sprintf (&obuf[strlen (obuf)], "_TN_LF_"); | |
| break; | |
| case TN_IAC: | |
| sprintf (&obuf[strlen (obuf)], "_TN_IAC_"); | |
| switch ((unsigned char)buf[i+1]) { | |
| case TN_IAC: | |
| sprintf (&obuf[strlen (obuf)], "_TN_IAC_"); ++i; | |
| break; | |
| case TN_DONT: | |
| sprintf (&obuf[strlen (obuf)], "_TN_DONT_"); ++i; | |
| break; | |
| case TN_DO: | |
| sprintf (&obuf[strlen (obuf)], "_TN_DO_"); ++i; | |
| break; | |
| case TN_WONT: | |
| sprintf (&obuf[strlen (obuf)], "_TN_WONT_"); ++i; | |
| break; | |
| case TN_WILL: | |
| sprintf (&obuf[strlen (obuf)], "_TN_WILL_"); ++i; | |
| break; | |
| default: | |
| sprintf (&obuf[strlen (obuf)], "_0x%02X_", (unsigned char)buf[i+1]); ++i; | |
| break; | |
| } | |
| switch ((unsigned char)buf[i+1]) { | |
| case TN_BIN: | |
| sprintf (&obuf[strlen (obuf)], "_TN_BIN_"); ++i; | |
| break; | |
| case TN_ECHO: | |
| sprintf (&obuf[strlen (obuf)], "_TN_ECHO_"); ++i; | |
| break; | |
| case TN_SGA: | |
| sprintf (&obuf[strlen (obuf)], "_TN_SGA_"); ++i; | |
| break; | |
| case TN_LINE: | |
| sprintf (&obuf[strlen (obuf)], "_TN_LINE_"); ++i; | |
| break; | |
| default: | |
| sprintf (&obuf[strlen (obuf)], "_0x%02X_", (unsigned char)buf[i+1]); ++i; | |
| break; | |
| } | |
| break; | |
| default: | |
| if (isprint((u_char)buf[i])) | |
| sprintf (&obuf[strlen (obuf)], "%c", buf[i]); | |
| else { | |
| sprintf (&obuf[strlen (obuf)], "_"); | |
| if ((buf[i] >= 1) && (buf[i] <= 26)) | |
| sprintf (&obuf[strlen (obuf)], "^%c", 'A' + buf[i] - 1); | |
| else | |
| sprintf (&obuf[strlen (obuf)], "\\%03o", (u_char)buf[i]); | |
| sprintf (&obuf[strlen (obuf)], "_"); | |
| } | |
| break; | |
| } | |
| } | |
| fprintf(p->Debug, "%s%s%s\n", timestamp, threadname, obuf); | |
| free (obuf); | |
| break; | |
| } | |
| } | |
| static void __panel_debug (PANEL *p, int dbits, const char *fmt, const char *buf, int bufsize, ...) | |
| { | |
| va_list arglist; | |
| va_start (arglist, bufsize); | |
| __panel_vdebug (p, dbits, fmt, buf, bufsize, arglist); | |
| va_end (arglist); | |
| } | |
| void | |
| sim_panel_debug (PANEL *panel, const char *fmt, ...) | |
| { | |
| va_list arglist; | |
| va_start (arglist, fmt); | |
| __panel_vdebug (panel, DBG_APP, fmt, NULL, 0, arglist); | |
| va_end (arglist); | |
| } | |
| static void * | |
| _panel_debugflusher(void *arg) | |
| { | |
| PANEL *p = (PANEL*)arg; | |
| int flush_interval = 15; | |
| int sleeps = 0; | |
| pthread_setspecific (panel_thread_id, "debugflush"); | |
| pthread_mutex_lock (&p->io_lock); | |
| p->debugflush_thread_running = 1; | |
| pthread_mutex_unlock (&p->io_lock); | |
| pthread_cond_signal (&p->startup_done); /* Signal we're ready to go */ | |
| msleep (100); | |
| pthread_mutex_lock (&p->io_lock); | |
| while (p->sock != INVALID_SOCKET) { | |
| pthread_mutex_unlock (&p->io_lock); | |
| msleep (1000); | |
| pthread_mutex_lock (&p->io_lock); | |
| if (0 == (sleeps++)%flush_interval) | |
| sim_panel_flush_debug (p); | |
| } | |
| pthread_mutex_unlock (&p->io_lock); | |
| pthread_mutex_lock (&p->io_lock); | |
| pthread_setspecific (panel_thread_id, NULL); | |
| p->debugflush_thread_running = 0; | |
| pthread_mutex_unlock (&p->io_lock); | |
| return NULL; | |
| } | |
| static void | |
| _set_debug_file (PANEL *panel, const char *debug_file) | |
| { | |
| if (!panel) | |
| return; | |
| panel->Debug = fopen(debug_file, "w"); | |
| if (panel->Debug) | |
| setvbuf (panel->Debug, NULL, _IOFBF, 65536); | |
| } | |
| void | |
| sim_panel_set_debug_mode (PANEL *panel, int debug_bits) | |
| { | |
| if (panel) | |
| panel->debug = debug_bits; | |
| } | |
| void | |
| sim_panel_flush_debug (PANEL *panel) | |
| { | |
| if (!panel) | |
| return; | |
| if (panel->Debug) | |
| fflush (panel->Debug); | |
| } | |
| static int | |
| _panel_send (PANEL *p, const char *msg, int len) | |
| { | |
| int sent = 0; | |
| if (p->sock == INVALID_SOCKET) | |
| return sim_panel_set_error (p, "Invalid Socket for write"); | |
| pthread_mutex_lock (&p->io_send_lock); | |
| while (len) { | |
| int bsent = sim_write_sock (p->sock, msg, len); | |
| if (bsent < 0) { | |
| pthread_mutex_unlock (&p->io_send_lock); | |
| return sim_panel_set_error (p, "%s", sim_get_err_sock("Error writing to socket")); | |
| } | |
| _panel_debug (p, DBG_XMT, "Sent %d bytes: ", msg, bsent, bsent); | |
| len -= bsent; | |
| msg += bsent; | |
| sent += bsent; | |
| } | |
| pthread_mutex_unlock (&p->io_send_lock); | |
| return sent; | |
| } | |
| static int | |
| _panel_sendf (PANEL *p, int *completion_status, char **response, const char *fmt, ...); | |
| static int | |
| _panel_sendf_completion (PANEL *p, char **response, const char *completion, 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, reg_count = 0, bit_reg_count = 0; | |
| const char *dev; | |
| pthread_mutex_lock (&panel->io_lock); | |
| buf_needed = 3 + 7 + /* EXECUTE */ | |
| strlen (register_get_start) + /* # REGISTERS-START */ | |
| strlen (register_get_prefix); /* SHOW TIME */ | |
| for (i=0; i<panel->reg_count; i++) { | |
| if (panel->regs[i].bits) | |
| ++bit_reg_count; | |
| else { | |
| ++reg_count; | |
| buf_needed += 10 + strlen (panel->regs[i].name) + (panel->regs[i].device_name ? strlen (panel->regs[i].device_name) : 0); | |
| if (panel->regs[i].element_count > 0) | |
| buf_needed += 4 + 6 /* 6 digit register array index */; | |
| if (panel->regs[i].indirect) | |
| buf_needed += 12 + strlen (register_ind_echo) + strlen (panel->regs[i].name); | |
| } | |
| } | |
| if (bit_reg_count) | |
| buf_needed += 2 + strlen (register_get_postfix); | |
| buf_needed += 10 + strlen (register_get_end); /* # REGISTERS-DONE */ | |
| if (buf_needed > *buf_size) { | |
| free (*buf); | |
| *buf = (char *)_panel_malloc (buf_needed); | |
| if (!*buf) { | |
| panel->State = Error; | |
| pthread_mutex_unlock (&panel->io_lock); | |
| return -1; | |
| } | |
| *buf_size = buf_needed; | |
| } | |
| buf_data = 0; | |
| if (reg_count) { | |
| sprintf (*buf + buf_data, "EXECUTE %s;%s;", register_get_start, register_get_prefix); | |
| buf_data += strlen (*buf + buf_data); | |
| } | |
| dev = ""; | |
| for (i=j=0; i<panel->reg_count; i++) { | |
| const char *reg_dev = panel->regs[i].device_name ? panel->regs[i].device_name : ""; | |
| if ((panel->regs[i].indirect) || (panel->regs[i].bits)) | |
| continue; | |
| if (strcmp (dev, reg_dev)) {/* devices are different */ | |
| char *tbuf; | |
| buf_needed += 4 + strlen (register_dev_echo) + strlen (reg_dev); /* # REGISTERS-for-DEVICE:XXX */ | |
| tbuf = (char *)_panel_malloc (buf_needed); | |
| if (tbuf == NULL) { | |
| panel->State = Error; | |
| pthread_mutex_unlock (&panel->io_lock); | |
| return -1; | |
| } | |
| strcpy (tbuf, *buf); | |
| free (*buf); | |
| *buf = tbuf; | |
| sprintf (*buf + buf_data, "%s%s%s;", (i == 0)? "" : ";", register_dev_echo, reg_dev); | |
| buf_data += strlen (*buf + buf_data); | |
| dev = reg_dev; | |
| j = 0; | |
| *buf_size = buf_needed; | |
| } | |
| if (panel->regs[i].element_count == 0) { | |
| if (j == 0) | |
| sprintf (*buf + buf_data, "E -16 %s %s", dev, panel->regs[i].name); | |
| else | |
| sprintf (*buf + buf_data, ",%s", panel->regs[i].name); | |
| } | |
| else { | |
| if (j == 0) | |
| sprintf (*buf + buf_data, "E -16 %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, (int)(panel->regs[i].element_count-1)); | |
| } | |
| ++j; | |
| buf_data += strlen (*buf + buf_data); | |
| } | |
| if (buf_data && ((*buf)[buf_data-1] != ';')) { | |
| strcpy (*buf + buf_data, ";"); | |
| buf_data += strlen (*buf + buf_data); | |
| } | |
| for (i=j=0; i<panel->reg_count; i++) { | |
| const char *reg_dev = panel->regs[i].device_name ? panel->regs[i].device_name : ""; | |
| if ((!panel->regs[i].indirect) || (panel->regs[i].bits)) | |
| continue; | |
| sprintf (*buf + buf_data, "%s%s;E -16 %s %s,$;", register_ind_echo, panel->regs[i].name, reg_dev, panel->regs[i].name); | |
| buf_data += strlen (*buf + buf_data); | |
| } | |
| if (bit_reg_count) { | |
| strcpy (*buf + buf_data, register_get_postfix); | |
| buf_data += strlen (*buf + buf_data); | |
| strcpy (*buf + buf_data, ";"); | |
| buf_data += strlen (*buf + buf_data); | |
| } | |
| strcpy (*buf + buf_data, register_get_end); | |
| buf_data += strlen (*buf + buf_data); | |
| strcpy (*buf + buf_data, "\r"); | |
| buf_data += strlen (*buf + buf_data); | |
| *buf_size = buf_data; | |
| pthread_mutex_unlock (&panel->io_lock); | |
| return 0; | |
| } | |
| static int | |
| _panel_establish_register_bits_collection (PANEL *panel) | |
| { | |
| size_t i, buf_data, buf_needed = 0, reg_count = 0, bit_reg_count = 0; | |
| int cmd_stat, bits_count = 0; | |
| char *buf, *response = NULL; | |
| pthread_mutex_lock (&panel->io_lock); | |
| for (i=0; i<panel->reg_count; i++) { | |
| if (panel->regs[i].bits) | |
| buf_needed += 9 + strlen (panel->regs[i].name) + (panel->regs[i].device_name ? strlen (panel->regs[i].device_name) : 0); | |
| } | |
| buf = (char *)_panel_malloc (buf_needed); | |
| if (!buf) { | |
| panel->State = Error; | |
| pthread_mutex_unlock (&panel->io_lock); | |
| return -1; | |
| } | |
| *buf = '\0'; | |
| buf_data = 0; | |
| for (i=0; i<panel->reg_count; i++) { | |
| if (panel->regs[i].bits) { | |
| ++bits_count; | |
| sprintf (buf + buf_data, "%s%s", (bits_count != 1) ? "," : "", panel->regs[i].indirect ? "-I " : ""); | |
| buf_data += strlen (buf + buf_data); | |
| if (panel->regs[i].device_name) { | |
| sprintf (buf + buf_data, "%s ", panel->regs[i].device_name); | |
| buf_data += strlen (buf + buf_data); | |
| } | |
| sprintf (buf + buf_data, "%s", panel->regs[i].name); | |
| buf_data += strlen (buf + buf_data); | |
| } | |
| } | |
| pthread_mutex_unlock (&panel->io_lock); | |
| if (_panel_sendf (panel, &cmd_stat, &response, "%s%u%s%u%s%u%s%s\r", register_collect_prefix, panel->sample_depth, | |
| register_collect_mid1, panel->sample_frequency, | |
| register_collect_mid2, panel->sample_dither_pct, | |
| register_collect_mid3, buf)) { | |
| sim_panel_set_error (NULL, "Error establishing bit data collection:%s", response); | |
| free (response); | |
| free (buf); | |
| return -1; | |
| } | |
| free (response); | |
| free (buf); | |
| return 0; | |
| } | |
| static PANEL **panels = NULL; | |
| static int panel_count = 0; | |
| static char *sim_panel_error_buf = NULL; | |
| static size_t sim_panel_error_bufsize = 0; | |
| static void | |
| _panel_cleanup (void) | |
| { | |
| while (panel_count) | |
| sim_panel_destroy (*panels); | |
| } | |
| static void | |
| _panel_register_panel (PANEL *p) | |
| { | |
| if (panel_count == 0) | |
| pthread_key_create (&panel_thread_id, free); | |
| if (!pthread_getspecific (panel_thread_id)) | |
| pthread_setspecific (panel_thread_id, p->device_name ? p->device_name : "PanelCreator"); | |
| ++panel_count; | |
| panels = (PANEL **)realloc (panels, sizeof(*panels)*panel_count); | |
| panels[panel_count-1] = p; | |
| if (panel_count == 1) | |
| atexit (&_panel_cleanup); | |
| } | |
| static void | |
| _panel_deregister_panel (PANEL *p) | |
| { | |
| int i; | |
| for (i=0; i<panel_count; i++) { | |
| if (panels[i] == p) { | |
| int j; | |
| for (j=i+1; j<panel_count; j++) | |
| panels[j-1] = panels[j]; | |
| --panel_count; | |
| if (panel_count == 0) { | |
| free (panels); | |
| panels = NULL; | |
| pthread_setspecific (panel_thread_id, NULL); | |
| pthread_key_delete (panel_thread_id); | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| static PANEL * | |
| _sim_panel_create (const char *sim_path, | |
| const char *sim_config, | |
| size_t device_panel_count, | |
| PANEL *simulator_panel, | |
| const char *device_name, | |
| const char *debug_file) | |
| { | |
| PANEL *p = NULL; | |
| FILE *fIn = NULL; | |
| 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; | |
| if (sim_panel_error_buf == NULL) { /* Preallocate an error message buffer */ | |
| sim_panel_error_bufsize = 2048; | |
| sim_panel_error_buf = (char *) malloc (sim_panel_error_bufsize); | |
| if (sim_panel_error_buf == NULL) { | |
| sim_panel_error_buf = (char *)"sim_panel_set_error(): Out of Memory\n"; | |
| sim_panel_error_bufsize = 0; | |
| return NULL; | |
| } | |
| } | |
| if (simulator_panel) { | |
| for (device_num=0; device_num < simulator_panel->device_count; ++device_num) | |
| if (simulator_panel->devices[device_num] == NULL) | |
| break; | |
| if (device_num == simulator_panel->device_count) { | |
| sim_panel_set_error (NULL, "No free panel devices slots available %s simulator. All %d slots are used.", simulator_panel->path, (int)simulator_panel->device_count); | |
| return NULL; | |
| } | |
| p = (PANEL *)_panel_malloc (sizeof(*p)); | |
| if (p == NULL) | |
| goto Error_Return; | |
| memset (p, 0, sizeof(*p)); | |
| _panel_register_panel (p); | |
| p->device_name = (char *)_panel_malloc (1 + strlen (device_name)); | |
| if (p->device_name == NULL) | |
| goto Error_Return; | |
| strcpy (p->device_name, device_name); | |
| p->parent = simulator_panel; | |
| p->Debug = p->parent->Debug; | |
| strcpy (p->hostport, simulator_panel->hostport); | |
| p->sock = INVALID_SOCKET; | |
| } | |
| else { | |
| end_test.i = 1; /* test endian-ness */ | |
| little_endian = (end_test.c[0] != 0); | |
| sim_init_sock (); | |
| for (port=1024; port < 2048; port++) { | |
| SOCKET sock; | |
| sprintf (hostport, "%d", port); | |
| sock = sim_connect_sock_ex (NULL, hostport, NULL, NULL, SIM_SOCK_OPT_NODELAY | SIM_SOCK_OPT_BLOCKING); | |
| if (sock != INVALID_SOCKET) { | |
| int sta = 0; | |
| while (!sta) { | |
| msleep (10); | |
| sta = sim_check_conn (sock, 1); | |
| } | |
| sim_close_sock (sock); | |
| if (sta == -1) | |
| break; | |
| } | |
| else | |
| break; | |
| } | |
| if (stat (sim_config, &statb) < 0) { | |
| sim_panel_set_error (NULL, "Can't stat simulator configuration '%s': %s", sim_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'; | |
| p = (PANEL *)_panel_malloc (sizeof(*p)); | |
| if (p == NULL) | |
| goto Error_Return; | |
| memset (p, 0, sizeof(*p)); | |
| _panel_register_panel (p); | |
| p->sock = INVALID_SOCKET; | |
| p->path = (char *)_panel_malloc (strlen (sim_path) + 1); | |
| if (p->path == NULL) | |
| goto Error_Return; | |
| strcpy (p->path, sim_path); | |
| p->config = (char *)_panel_malloc (strlen (sim_config) + 1); | |
| if (p->config == NULL) | |
| goto Error_Return; | |
| strcpy (p->config, sim_config); | |
| fIn = fopen (sim_config, "r"); | |
| if (fIn == NULL) { | |
| sim_panel_set_error (NULL, "Can't open configuration file '%s': %s", sim_config, strerror(errno)); | |
| goto Error_Return; | |
| } | |
| p->temp_config = (char *)_panel_malloc (strlen (sim_config) + 40); | |
| if (p->temp_config == NULL) | |
| goto Error_Return; | |
| sprintf (p->temp_config, "%s-Panel-%d", sim_config, getpid()); | |
| fOut = fopen (p->temp_config, "w"); | |
| if (fOut == NULL) { | |
| sim_panel_set_error (NULL, "Can't create temporary configuration file '%s': %s", p->temp_config, strerror(errno)); | |
| goto Error_Return; | |
| } | |
| fprintf (fOut, "# Temporary FrontPanel generated simh configuration file\n"); | |
| fprintf (fOut, "# Original Configuration File: %s\n", p->config); | |
| fprintf (fOut, "# Simulator Path: %s\n", sim_path); | |
| while (fgets (buf, statb.st_size, fIn)) | |
| fputs (buf, fOut); | |
| free (buf); | |
| buf = NULL; | |
| fclose (fIn); | |
| fIn = NULL; | |
| fprintf (fOut, "set remote notelnet\n"); | |
| if (device_panel_count) | |
| fprintf (fOut, "set remote connections=%d\n", (int)device_panel_count+1); | |
| fprintf (fOut, "set remote -u telnet=%s\n", hostport); | |
| fprintf (fOut, "set remote master\n"); | |
| fprintf (fOut, "exit\n"); | |
| fclose (fOut); | |
| fOut = NULL; | |
| } | |
| if (debug_file) { | |
| _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 (NULL, "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 (NULL, "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); | |
| fIn = NULL; | |
| } | |
| if (!simulator_panel) { | |
| #if defined(_WIN32) | |
| char cmd[2048]; | |
| PROCESS_INFORMATION ProcessInfo; | |
| STARTUPINFO StartupInfo; | |
| sprintf(cmd, "%s%s%s %s%s%s", strchr (sim_path, ' ') ? "\"" : "", sim_path, strchr (sim_path, ' ') ? "\"" : "", strchr (p->temp_config, ' ') ? "\"" : "", p->temp_config, strchr (p->temp_config, ' ') ? "\"" : ""); | |
| memset (&ProcessInfo, 0, sizeof(ProcessInfo)); | |
| memset (&StartupInfo, 0, sizeof(StartupInfo)); | |
| StartupInfo.dwFlags = STARTF_USESTDHANDLES; | |
| StartupInfo.hStdInput = INVALID_HANDLE_VALUE; | |
| StartupInfo.hStdOutput = INVALID_HANDLE_VALUE; | |
| StartupInfo.hStdError = INVALID_HANDLE_VALUE; | |
| if (CreateProcessA(NULL, cmd, NULL, NULL, FALSE, CREATE_NO_WINDOW, NULL, NULL, &StartupInfo, &ProcessInfo)) { | |
| CloseHandle (ProcessInfo.hThread); | |
| p->hProcess = ProcessInfo.hProcess; | |
| p->dwProcessId = ProcessInfo.dwProcessId; | |
| } | |
| else { /* Creation Problem */ | |
| sim_panel_set_error (NULL, "CreateProcess Error: %d", GetLastError()); | |
| goto Error_Return; | |
| } | |
| #else | |
| p->pidProcess = fork(); | |
| if (p->pidProcess == 0) { | |
| close (0); close (1); close (2); /* make sure not to pass the open standard handles */ | |
| dup (dup (open ("/dev/null", O_RDWR))); /* open standard handles to /dev/null */ | |
| if (execlp (sim_path, sim_path, p->temp_config, NULL, NULL)) { | |
| perror ("execl"); | |
| exit(errno); | |
| } | |
| } | |
| if (p->pidProcess < 0) { | |
| p->pidProcess = 0; | |
| sim_panel_set_error (NULL, "fork() Error: %s", strerror(errno)); | |
| goto Error_Return; | |
| } | |
| #endif | |
| strcpy (p->hostport, hostport); | |
| } | |
| for (i=0; i<100; i++) { /* Allow up to 10 seconds waiting for simulator to start up */ | |
| p->sock = sim_connect_sock_ex (NULL, p->hostport, NULL, NULL, SIM_SOCK_OPT_NODELAY | SIM_SOCK_OPT_BLOCKING); | |
| if (p->sock == INVALID_SOCKET) | |
| msleep (100); | |
| else | |
| break; | |
| } | |
| if (p->sock == INVALID_SOCKET) { | |
| if (simulator_panel) { | |
| sim_panel_set_error (NULL, "Can't connect to simulator Remote Console on port %s", p->hostport); | |
| } | |
| else { | |
| if (stat (sim_path, &statb) < 0) | |
| sim_panel_set_error (NULL, "Can't stat simulator '%s': %s", sim_path, strerror(errno)); | |
| else | |
| sim_panel_set_error (NULL, "Can't connect to the %s simulator Remote Console on port %s, the simulator process may not have started or the simulator binary can't be found", sim_path, p->hostport); | |
| } | |
| goto Error_Return; | |
| } | |
| _panel_debug (p, DBG_XMT|DBG_RCV, "Connected to simulator on %s after %dms", 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_done, NULL); | |
| if (sizeof(mantra) != _panel_send (p, (char *)mantra, sizeof(mantra))) { | |
| sim_panel_set_error (NULL, "Error sending Telnet mantra (options): %s", sim_get_err_sock ("send")); | |
| goto Error_Return; | |
| } | |
| if (1) { | |
| pthread_attr_t attr; | |
| pthread_attr_init(&attr); | |
| pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); | |
| pthread_mutex_lock (&p->io_lock); | |
| p->io_thread_running = 0; | |
| pthread_create (&p->io_thread, &attr, _panel_reader, (void *)p); | |
| while (!p->io_thread_running) | |
| pthread_cond_wait (&p->startup_done, &p->io_lock); /* Wait for thread to stabilize */ | |
| if ((p->Debug) && (p->parent == NULL)) { | |
| p->debugflush_thread_running = 0; | |
| pthread_create (&p->debugflush_thread, &attr, _panel_debugflusher, (void *)p); | |
| while (!p->debugflush_thread_running) | |
| pthread_cond_wait (&p->startup_done, &p->io_lock); /* Wait for thread to stabilize */ | |
| } | |
| pthread_mutex_unlock (&p->io_lock); | |
| pthread_attr_destroy(&attr); | |
| pthread_cond_destroy (&p->startup_done); | |
| } | |
| if (simulator_panel) { | |
| simulator_panel->devices[device_num] = p; | |
| } | |
| else { | |
| if (device_panel_count) { | |
| p->devices = (PANEL **)_panel_malloc (device_panel_count*sizeof(*p->devices)); | |
| if (p->devices == NULL) | |
| goto Error_Return; | |
| memset (p->devices, 0, device_panel_count*sizeof(*p->devices)); | |
| p->device_count = device_panel_count; | |
| } | |
| if (p->State == Error) | |
| goto Error_Return; | |
| /* Validate sim_frontpanel API version */ | |
| if (_panel_sendf (p, &cmd_stat, &p->simulator_version, "SHOW VERSION\r")) | |
| goto Error_Return; | |
| if (1) { | |
| int api_version = 0; | |
| char *c = strstr (p->simulator_version, "FrontPanel API Version"); | |
| if ((!c) || | |
| (1 != sscanf (c, "FrontPanel API Version %d", &api_version)) || | |
| (api_version != SIM_FRONTPANEL_VERSION)) { | |
| sim_panel_set_error (NULL, "Inconsistent sim_frontpanel API version %d in simulator. Version %d needed.-", api_version, SIM_FRONTPANEL_VERSION); | |
| goto Error_Return; | |
| } | |
| } | |
| if (1) { | |
| char *radix = NULL; | |
| if (_panel_sendf (p, &cmd_stat, &radix, "SHOW %s RADIX\r", p->device_name ? p->device_name : "")) { | |
| free (radix); | |
| goto Error_Return; | |
| } | |
| sscanf (radix, "Radix=%d", &p->radix); | |
| free (radix); | |
| if ((p->radix != 16) && (p->radix != 8)) { | |
| sim_panel_set_error (NULL, "Unsupported Radix: %d%s%s.", p->radix, p->device_name ? " on device " : "", p->device_name ? p->device_name : ""); | |
| goto Error_Return; | |
| } | |
| } | |
| } | |
| return p; | |
| Error_Return: | |
| if (fIn) | |
| fclose (fIn); | |
| if (fOut) { | |
| fclose (fOut); | |
| (void)remove (p->temp_config); | |
| } | |
| if (buf) | |
| free (buf); | |
| if (1) { | |
| const char *err = sim_panel_get_error(); | |
| char *errbuf = (char *)_panel_malloc (1 + strlen (err)); | |
| strcpy (errbuf, err); /* preserve error info while closing */ | |
| sim_panel_destroy (p); | |
| sim_panel_set_error (NULL, "%s", errbuf); | |
| free (errbuf); | |
| } | |
| if (!simulator_panel) | |
| sim_cleanup_sock(); | |
| return NULL; | |
| } | |
| PANEL * | |
| sim_panel_start_simulator_debug (const char *sim_path, | |
| const char *sim_config, | |
| size_t device_panel_count, | |
| const char *debug_file) | |
| { | |
| return _sim_panel_create (sim_path, sim_config, device_panel_count, NULL, NULL, debug_file); | |
| } | |
| PANEL * | |
| sim_panel_start_simulator (const char *sim_path, | |
| const char *sim_config, | |
| size_t device_panel_count) | |
| { | |
| return sim_panel_start_simulator_debug (sim_path, sim_config, device_panel_count, NULL); | |
| } | |
| PANEL * | |
| sim_panel_add_device_panel_debug (PANEL *simulator_panel, | |
| const char *device_name, | |
| const char *debug_file) | |
| { | |
| return _sim_panel_create (NULL, NULL, 0, simulator_panel, device_name, debug_file); | |
| } | |
| PANEL * | |
| sim_panel_add_device_panel (PANEL *simulator_panel, | |
| const char *device_name) | |
| { | |
| return sim_panel_add_device_panel_debug (simulator_panel, device_name, NULL); | |
| } | |
| int | |
| sim_panel_destroy (PANEL *panel) | |
| { | |
| REG *reg; | |
| if (panel) { | |
| _panel_debug (panel, DBG_XMT|DBG_RCV, "Closing Panel %s", NULL, 0, panel->device_name? panel->device_name : panel->path); | |
| if (panel->devices) { | |
| size_t i; | |
| for (i=0; i<panel->device_count; i++) { | |
| if (panel->devices[i]) { | |
| sim_panel_destroy (panel->devices[i]); | |
| panel->devices[i] = NULL; | |
| } | |
| } | |
| free (panel->devices); | |
| panel->devices = NULL; | |
| } | |
| if (panel->sock != INVALID_SOCKET) { | |
| SOCKET sock = panel->sock; | |
| int wait_count; | |
| /* First, wind down the automatic register queries */ | |
| 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) | |
| sim_panel_exec_halt (panel); | |
| _panel_send (panel, "EXIT\r", 5); | |
| } | |
| /* Wait for up to 2 seconds for a graceful shutdown */ | |
| panel->sock = INVALID_SOCKET; | |
| 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 that hasn't completed */ | |
| sim_close_sock (sock); | |
| pthread_join (panel->io_thread, NULL); | |
| } | |
| if ((panel->Debug) && (panel->parent == NULL)) | |
| pthread_join (panel->debugflush_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) | |
| if (panel->hProcess) { | |
| GenerateConsoleCtrlEvent (CTRL_BREAK_EVENT, panel->dwProcessId); | |
| msleep (200); | |
| TerminateProcess (panel->hProcess, 0); | |
| WaitForSingleObject (panel->hProcess, INFINITE); | |
| CloseHandle (panel->hProcess); | |
| } | |
| #else | |
| if (panel->pidProcess) { | |
| int status; | |
| if (!kill (panel->pidProcess, 0)) { | |
| kill (panel->pidProcess, SIGTERM); | |
| msleep (200); | |
| if (!kill (panel->pidProcess, 0)) | |
| kill (panel->pidProcess, SIGKILL); | |
| } | |
| waitpid (panel->pidProcess, &status, 0); | |
| } | |
| #endif | |
| free (panel->path); | |
| free (panel->device_name); | |
| free (panel->config); | |
| if (panel->temp_config) | |
| (void)remove (panel->temp_config); | |
| free (panel->temp_config); | |
| reg = panel->regs; | |
| while (panel->reg_count--) { | |
| free (reg->name); | |
| free (reg->device_name); | |
| reg++; | |
| } | |
| free (panel->regs); | |
| free (panel->reg_query); | |
| free (panel->io_response); | |
| free (panel->halt_reason); | |
| free (panel->simulator_version); | |
| if ((panel->Debug) && (!panel->parent)) | |
| fclose (panel->Debug); | |
| if (!panel->parent) | |
| sim_cleanup_sock (); | |
| _panel_deregister_panel (panel); | |
| free (panel); | |
| } | |
| return 0; | |
| } | |
| OperationalState | |
| sim_panel_get_state (PANEL *panel) | |
| { | |
| if (!panel) | |
| return Halt; | |
| return panel->State; | |
| } | |
| static int | |
| _panel_add_register (PANEL *panel, | |
| const char *name, | |
| const char *device_name, | |
| size_t size, | |
| void *addr, | |
| int indirect, | |
| size_t element_count, | |
| int *bits, | |
| size_t bit_count) | |
| { | |
| REG *regs, *reg; | |
| char *response = NULL, *c; | |
| unsigned long long data; | |
| size_t i; | |
| int cmd_stat; | |
| if (!panel || (panel->State == Error)) { | |
| sim_panel_set_error (NULL, "Invalid Panel"); | |
| return -1; | |
| } | |
| if (panel->State == Run) { | |
| sim_panel_set_error (NULL, "Not Halted"); | |
| return -1; | |
| } | |
| if ((bit_count != 0) && (panel->sample_depth == 0)) { | |
| sim_panel_set_error (NULL, "sim_panel_set_sampling_parameters() must be called first"); | |
| return -1; | |
| } | |
| regs = (REG *)_panel_malloc ((1 + panel->reg_count)*sizeof(*regs)); | |
| if (regs == NULL) | |
| return sim_panel_set_error (panel, "_panel_add_register(): Out of Memory\n"); | |
| pthread_mutex_lock (&panel->io_lock); | |
| memcpy (regs, panel->regs, panel->reg_count*sizeof(*regs)); | |
| reg = ®s[panel->reg_count]; | |
| memset (reg, 0, sizeof(*regs)); | |
| reg->name = (char *)_panel_malloc (1 + strlen (name)); | |
| if (reg->name == NULL) { | |
| panel->State = Error; | |
| pthread_mutex_unlock (&panel->io_lock); | |
| sim_panel_set_error (NULL, "_panel_add_register(): Out of Memory\n"); | |
| free (regs); | |
| return -1; | |
| } | |
| strcpy (reg->name, name); | |
| reg->indirect = indirect; | |
| reg->addr = addr; | |
| reg->size = size; | |
| reg->element_count = element_count; | |
| reg->bits = bits; | |
| reg->bit_count = bit_count; | |
| for (i=0; i<strlen (reg->name); i++) { | |
| if (islower (reg->name[i])) | |
| reg->name[i] = toupper (reg->name[i]); | |
| } | |
| if (device_name) { | |
| reg->device_name = (char *)_panel_malloc (1 + strlen (device_name)); | |
| if (reg->device_name == NULL) { | |
| free (reg->name); | |
| free (regs); | |
| pthread_mutex_unlock (&panel->io_lock); | |
| return sim_panel_set_error (panel, "_panel_add_register(): Out of Memory\n"); | |
| } | |
| strcpy (reg->device_name, device_name); | |
| for (i=0; i<strlen (reg->device_name); i++) { | |
| if (islower (reg->device_name[i])) | |
| reg->device_name[i] = toupper (reg->device_name[i]); | |
| } | |
| } | |
| for (i=0; i<panel->reg_count; i++) { | |
| char *t1 = (char *)_panel_malloc (2 + strlen (regs[i].name) + (regs[i].device_name? strlen (regs[i].device_name) : 0)); | |
| char *t2 = (char *)_panel_malloc (2 + strlen (reg->name) + (reg->device_name? strlen (reg->device_name) : 0)); | |
| if ((t1 == NULL) || (t2 == NULL)) { | |
| free (t1); | |
| free (t2); | |
| free (reg->name); | |
| free (reg->device_name); | |
| panel->State = Error; | |
| free (regs); | |
| pthread_mutex_unlock (&panel->io_lock); | |
| return sim_panel_set_error (NULL, "_panel_add_register(): Out of Memory\n"); | |
| } | |
| sprintf (t1, "%s %s", regs[i].device_name ? regs[i].device_name : "", regs[i].name); | |
| sprintf (t2, "%s %s", reg->device_name ? reg->device_name : "", reg->name); | |
| if ((!strcmp (t1, t2)) && | |
| (reg->indirect == regs[i].indirect) && | |
| ((reg->bits == NULL) == (regs[i].bits == NULL))) { | |
| pthread_mutex_unlock (&panel->io_lock); | |
| sim_panel_set_error (NULL, "Duplicate Register Declaration"); | |
| free (t1); | |
| free (t2); | |
| free (reg->name); | |
| free (reg->device_name); | |
| free (regs); | |
| return -1; | |
| } | |
| free (t1); | |
| free (t2); | |
| } | |
| pthread_mutex_unlock (&panel->io_lock); | |
| /* Validate existence of requested register/array */ | |
| if (_panel_sendf (panel, &cmd_stat, &response, "EXAMINE -H %s %s%s\r", device_name? device_name : "", name, (element_count > 0) ? "[0]" : "")) { | |
| free (reg->name); | |
| free (reg->device_name); | |
| free (regs); | |
| return -1; | |
| } | |
| c = strchr (response, ':'); | |
| if ((!strcmp ("Invalid argument\r\n", response)) || (!c)) { | |
| sim_panel_set_error (NULL, "Invalid Register: %s %s", device_name? device_name : "", name); | |
| free (response); | |
| free (reg->name); | |
| free (reg->device_name); | |
| free (regs); | |
| return -1; | |
| } | |
| data = strtoull (c + 1, NULL, 16); | |
| free (response); | |
| if (element_count > 0) { | |
| 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); | |
| return -1; | |
| } | |
| if (!strcmp ("Subscript out of range\r\n", response)) { | |
| sim_panel_set_error (NULL, "Invalid Register Array Dimension: %s %s[%d]", device_name? device_name : "", name, element_count-1); | |
| free (response); | |
| free (reg->name); | |
| free (reg->device_name); | |
| free (regs); | |
| return -1; | |
| } | |
| free (response); | |
| } | |
| 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)) | |
| return -1; | |
| if (bits) { | |
| for (i=0; i<bit_count; i++) | |
| bits[i] = (data & (1LL<<i)) ? panel->sample_depth : 0; | |
| if (_panel_establish_register_bits_collection (panel)) | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| int | |
| sim_panel_add_register (PANEL *panel, | |
| const char *name, | |
| const char *device_name, | |
| size_t size, | |
| void *addr) | |
| { | |
| return _panel_add_register (panel, name, device_name, size, addr, 0, 0, NULL, 0); | |
| } | |
| int | |
| sim_panel_add_register_bits (PANEL *panel, | |
| const char *name, | |
| const char *device_name, | |
| size_t bit_width, | |
| int *bits) | |
| { | |
| return _panel_add_register (panel, name, device_name, 0, NULL, 0, 0, bits, bit_width); | |
| } | |
| int | |
| sim_panel_add_register_array (PANEL *panel, | |
| const char *name, | |
| const char *device_name, | |
| size_t element_count, | |
| size_t size, | |
| void *addr) | |
| { | |
| return _panel_add_register (panel, name, device_name, size, addr, 0, element_count, NULL, 0); | |
| } | |
| int | |
| sim_panel_add_register_indirect (PANEL *panel, | |
| const char *name, | |
| const char *device_name, | |
| size_t size, | |
| void *addr) | |
| { | |
| return _panel_add_register (panel, name, device_name, size, addr, 1, 0, NULL, 0); | |
| } | |
| int | |
| sim_panel_add_register_indirect_bits (PANEL *panel, | |
| const char *name, | |
| const char *device_name, | |
| size_t bit_width, | |
| int *bits) | |
| { | |
| return _panel_add_register (panel, name, device_name, 0, NULL, 1, 0, bits, bit_width); | |
| } | |
| static int | |
| _panel_get_registers (PANEL *panel, int calledback, unsigned long long *simulation_time) | |
| { | |
| if ((!panel) || (panel->State == Error)) { | |
| sim_panel_set_error (NULL, "Invalid Panel"); | |
| return -1; | |
| } | |
| if ((!calledback) && (panel->callback)) { | |
| sim_panel_set_error (NULL, "Callback provides register data"); | |
| return -1; | |
| } | |
| if (!panel->reg_count) { | |
| sim_panel_set_error (NULL, "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; | |
| } | |
| if (panel->io_response_data) | |
| _panel_debug (panel, DBG_RCV, "Receive Data Discarded: ", panel->io_response, panel->io_response_data); | |
| panel->io_response_data = 0; | |
| panel->io_waiting = 1; | |
| while (panel->io_waiting) | |
| pthread_cond_wait (&panel->io_done, &panel->io_lock); | |
| 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_get_registers (PANEL *panel, unsigned long long *simulation_time) | |
| { | |
| return _panel_get_registers (panel, (panel->State == Halt), 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 (NULL, "Invalid Panel"); | |
| return -1; | |
| } | |
| pthread_mutex_lock (&panel->io_lock); /* acquire access */ | |
| panel->callback = callback; | |
| panel->callback_context = context; | |
| if (usecs_between_callbacks && (0 == panel->usecs_between_callbacks)) { /* Need to start/enable callbacks */ | |
| pthread_attr_t attr; | |
| _panel_debug (panel, DBG_THR, "Starting callback thread, Interval: %d usecs", NULL, 0, usecs_between_callbacks); | |
| panel->usecs_between_callbacks = usecs_between_callbacks; | |
| pthread_cond_init (&panel->startup_done, NULL); | |
| pthread_attr_init(&attr); | |
| pthread_attr_setscope(&attr, PTHREAD_SCOPE_SYSTEM); | |
| pthread_create (&panel->callback_thread, &attr, _panel_callback, (void *)panel); | |
| pthread_attr_destroy(&attr); | |
| while (!panel->callback_thread_running) | |
| pthread_cond_wait (&panel->startup_done, &panel->io_lock); /* Wait for thread to stabilize */ | |
| pthread_cond_destroy (&panel->startup_done); | |
| } | |
| if ((usecs_between_callbacks == 0) && panel->usecs_between_callbacks) { /* Need to stop callbacks */ | |
| _panel_debug (panel, DBG_THR, "Shutting down callback thread", NULL, 0); | |
| panel->usecs_between_callbacks = 0; /* flag disabled */ | |
| pthread_mutex_unlock (&panel->io_lock); /* allow access */ | |
| pthread_join (panel->callback_thread, NULL); /* synchronize with thread rundown */ | |
| pthread_mutex_lock (&panel->io_lock); /* reacquire access */ | |
| } | |
| pthread_mutex_unlock (&panel->io_lock); | |
| return 0; | |
| } | |
| int | |
| sim_panel_set_sampling_parameters_ex (PANEL *panel, | |
| unsigned int sample_frequency, | |
| unsigned int sample_dither_pct, | |
| unsigned int sample_depth) | |
| { | |
| if (sample_frequency == 0) { | |
| sim_panel_set_error (NULL, "Invalid sample frequency value: %u", sample_frequency); | |
| return -1; | |
| } | |
| if (sample_dither_pct > 25) { | |
| sim_panel_set_error (NULL, "Invalid sample dither percentage value: %u", sample_dither_pct); | |
| return -1; | |
| } | |
| if (sample_depth == 0) { | |
| sim_panel_set_error (NULL, "Invalid sample depth value: %u", sample_depth); | |
| return -1; | |
| } | |
| panel->sample_frequency = sample_frequency; | |
| panel->sample_dither_pct = sample_dither_pct; | |
| panel->sample_depth = sample_depth; | |
| return 0; | |
| } | |
| int | |
| sim_panel_set_sampling_parameters (PANEL *panel, | |
| unsigned int sample_frequency, | |
| unsigned int sample_depth) | |
| { | |
| return sim_panel_set_sampling_parameters_ex (panel, | |
| sample_frequency, | |
| 5, | |
| sample_depth); | |
| } | |
| int | |
| sim_panel_exec_halt (PANEL *panel) | |
| { | |
| if (!panel || (panel->State == Error)) { | |
| sim_panel_set_error (NULL, "Invalid Panel"); | |
| return -1; | |
| } | |
| if (panel->parent) { | |
| sim_panel_set_error (NULL, "Can't HALT simulator from device front panel"); | |
| return -1; | |
| } | |
| if (panel->State == Run) { | |
| if (_panel_sendf_completion (panel, NULL, sim_prompt, "\005")) { | |
| _panel_debug (panel, DBG_THR, "Error trying to HALT running simulator: %s", NULL, 0, sim_panel_get_error ()); | |
| return -1; | |
| } | |
| if (panel->State == Run) { | |
| _panel_debug (panel, DBG_THR, "Unable to HALT running simulator", NULL, 0); | |
| return -1; | |
| } | |
| } | |
| return 0; | |
| } | |
| const char * | |
| sim_panel_halt_text (PANEL *panel) | |
| { | |
| if (!panel || !panel->halt_reason) | |
| return ""; | |
| return panel->halt_reason; | |
| } | |
| int | |
| sim_panel_exec_boot (PANEL *panel, const char *device) | |
| { | |
| int cmd_stat; | |
| char *response, *simtime; | |
| if (!panel || (panel->State == Error)) { | |
| sim_panel_set_error (NULL, "Invalid Panel"); | |
| return -1; | |
| } | |
| if (panel->parent) { | |
| sim_panel_set_error (NULL, "Can't BOOT simulator from device front panel"); | |
| return -1; | |
| } | |
| if (panel->State == Run) { | |
| sim_panel_set_error (NULL, "Not Halted"); | |
| return -1; | |
| } | |
| /* A BOOT or RUN command will restart the simulator's time base. */ | |
| /* We account for that so that the frontpanel application sees ever */ | |
| /* increasing time values when register data is delivered. */ | |
| if (_panel_sendf (panel, &cmd_stat, &response, "SHOW TIME\r")) | |
| return -1; | |
| if ((simtime = strstr (response, "Time:"))) { | |
| panel->simulation_time = strtoull (simtime + 5, NULL, 10); | |
| panel->simulation_time_base += panel->simulation_time; | |
| } | |
| free (response); | |
| if (_panel_sendf_completion (panel, NULL, "Simulator Running...", "BOOT %s\r", device)) { | |
| _panel_debug (panel, DBG_THR, "Unable to BOOT simulator: %s", NULL, 0, sim_panel_get_error()); | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| int | |
| sim_panel_exec_start (PANEL *panel) | |
| { | |
| int cmd_stat; | |
| char *response, *simtime; | |
| if (!panel || (panel->State == Error)) { | |
| sim_panel_set_error (NULL, "Invalid Panel"); | |
| return -1; | |
| } | |
| if (panel->parent) { | |
| sim_panel_set_error (NULL, "Can't RUN simulator from device front panel"); | |
| return -1; | |
| } | |
| if (panel->State == Run) { | |
| sim_panel_set_error (NULL, "Not Halted"); | |
| return -1; | |
| } | |
| /* A BOOT or RUN command will restart the simulator's time base. */ | |
| /* We account for that so that the frontpanel application sees ever */ | |
| /* increasing time values when register data is delivered. */ | |
| if (_panel_sendf (panel, &cmd_stat, &response, "SHOW TIME\r")) { | |
| _panel_debug (panel, DBG_THR, "Unable to send SHOW TIME command while starting simulator: %s", NULL, 0, sim_panel_get_error()); | |
| return -1; | |
| } | |
| if ((simtime = strstr (response, "Time:"))) { | |
| panel->simulation_time = strtoull (simtime + 5, NULL, 10); | |
| panel->simulation_time_base += panel->simulation_time; | |
| } | |
| free (response); | |
| panel->simulation_time_base += panel->simulation_time; | |
| if (_panel_sendf_completion (panel, NULL, "Simulator Running...", "RUN\r", 5)) { | |
| _panel_debug (panel, DBG_THR, "Unable to start simulator: %s", NULL, 0, sim_panel_get_error()); | |
| return -1; | |
| } | |
| return 0; | |
| } | |
| int | |
| sim_panel_exec_run (PANEL *panel) | |
| { | |
| if (!panel || (panel->State == Error)) { | |
| sim_panel_set_error (NULL, "Invalid Panel"); | |
| return -1; | |
| } | |
| if (panel->parent) { | |
| sim_panel_set_error (NULL, "Can't CONT simulator from device front panel"); | |
| return -1; | |
| } | |
| if (panel->State == Run) { | |
| sim_panel_set_error (NULL, "Not Halted"); | |
| return -1; | |
| } | |
| if (_panel_sendf_completion (panel, NULL, "Simulator Running...", "CONT\r")) | |
| return -1; | |
| return 0; | |
| } | |
| int | |
| sim_panel_exec_step (PANEL *panel) | |
| { | |
| if (!panel || (panel->State == Error)) { | |
| sim_panel_set_error (NULL, "Invalid Panel"); | |
| return -1; | |
| } | |
| if (panel->parent) { | |
| sim_panel_set_error (NULL, "Can't STEP simulator from device front panel"); | |
| return -1; | |
| } | |
| if (panel->State == Run) { | |
| sim_panel_set_error (NULL, "Not Halted"); | |
| return -1; | |
| } | |
| if (_panel_sendf_completion (panel, NULL, sim_prompt, "STEP")) { | |
| _panel_debug (panel, DBG_THR, "Error trying to STEP running simulator: %s", NULL, 0, sim_panel_get_error ()); | |
| return -1; | |
| } | |
| 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 (NULL, "Invalid Panel"); | |
| return -1; | |
| } | |
| if (panel->parent) { | |
| sim_panel_set_error (NULL, "Can't establish a breakpoint from device front panel"); | |
| return -1; | |
| } | |
| if ((_panel_sendf (panel, &cmd_stat, &response, "BREAK %s\r", condition)) || | |
| (*response)) { | |
| sim_panel_set_error (NULL, "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 (NULL, "Invalid Panel"); | |
| return -1; | |
| } | |
| if (panel->parent) { | |
| sim_panel_set_error (NULL, "Can't clear a breakpoint from device front panel"); | |
| return -1; | |
| } | |
| if ((_panel_sendf (panel, &cmd_stat, &response, "NOBREAK %s\r", condition)) || | |
| (*response)) { | |
| sim_panel_set_error (NULL, "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 (NULL, "Invalid Panel"); | |
| return -1; | |
| } | |
| if (panel->parent) { | |
| sim_panel_set_error (NULL, "Can't establish an output breakpoint from device front panel"); | |
| return -1; | |
| } | |
| if ((_panel_sendf (panel, &cmd_stat, &response, "EXPECT %s\r", condition)) || | |
| (*response)) { | |
| sim_panel_set_error (NULL, "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 (NULL, "Invalid Panel"); | |
| return -1; | |
| } | |
| if (panel->parent) { | |
| sim_panel_set_error (NULL, "Can't clear an output breakpoint from device front panel"); | |
| return -1; | |
| } | |
| if ((_panel_sendf (panel, &cmd_stat, &response, "NOEXPECT %s\r", condition)) || | |
| (*response)) { | |
| sim_panel_set_error (NULL, "Error clearing output breakpoint for '%s': %s", condition, response ? response : ""); | |
| free (response); | |
| return -1; | |
| } | |
| free (response); | |
| return 0; | |
| } | |
| /** | |
| sim_panel_gen_examine | |
| name_or_addr the name the simulator knows this register by | |
| size the size (in local storage) of the buffer which will | |
| receive the data returned when examining the simulator | |
| value a pointer to the buffer which will be loaded with the | |
| data returned when examining the simulator | |
| */ | |
| int | |
| sim_panel_gen_examine (PANEL *panel, | |
| const char *name_or_addr, | |
| size_t size, | |
| void *value) | |
| { | |
| char *response = NULL, *c; | |
| unsigned long long data = 0; | |
| int cmd_stat; | |
| if (!panel || (panel->State == Error)) { | |
| sim_panel_set_error (NULL, "Invalid Panel"); | |
| return -1; | |
| } | |
| if (panel->State == Run) { | |
| sim_panel_set_error (NULL, "Not Halted"); | |
| return -1; | |
| } | |
| if (_panel_sendf (panel, &cmd_stat, &response, "EXAMINE -H %s", name_or_addr)) { | |
| free (response); | |
| return -1; | |
| } | |
| c = strchr (response, ':'); | |
| if (!c) { | |
| sim_panel_set_error (NULL, "response: %s", response); | |
| free (response); | |
| return -1; | |
| } | |
| data = strtoull (c + 1, NULL, 16); | |
| if (little_endian) | |
| memcpy (value, &data, size); | |
| else | |
| memcpy (value, ((char *)&data) + sizeof(data)-size, size); | |
| free (response); | |
| return 0; | |
| } | |
| /** | |
| sim_panel_get_history | |
| count the number of instructions to return | |
| size the size (in local storage) of the buffer which will | |
| receive the data returned when examining the simulator | |
| buffer a pointer to the buffer which will be loaded with the | |
| instruction history returned from the simulator | |
| */ | |
| int | |
| sim_panel_get_history (PANEL *panel, | |
| int count, | |
| size_t size, | |
| char *buffer) | |
| { | |
| char *response = NULL; | |
| int cmd_stat; | |
| if (!panel || (panel->State == Error)) { | |
| sim_panel_set_error (NULL, "Invalid Panel"); | |
| return -1; | |
| } | |
| if (panel->State == Run) { | |
| sim_panel_set_error (NULL, "Not Halted"); | |
| return -1; | |
| } | |
| if (_panel_sendf (panel, &cmd_stat, &response, "SHOW HISTORY=%d", count)) { | |
| free (response); | |
| return -1; | |
| } | |
| strncpy (buffer, response, size); | |
| free (response); | |
| return 0; | |
| } | |
| int | |
| sim_panel_device_debug_mode (PANEL *panel, | |
| const char *device, | |
| int set_unset, | |
| const char *mode_bits) | |
| { | |
| char *response = NULL; | |
| int cmd_stat; | |
| if (!panel || (panel->State == Error)) { | |
| sim_panel_set_error (NULL, "Invalid Panel"); | |
| return -1; | |
| } | |
| if ((device != NULL) && | |
| ((_panel_sendf (panel, &cmd_stat, &response, "SHOW %s", device) || | |
| (cmd_stat)))) { | |
| sim_panel_set_error (NULL, "Can't %s Debug Mode: '%s' on Device '%s': %s", | |
| set_unset ? "Enable" : "Disable", mode_bits ? mode_bits : "", device, response); | |
| free (response); | |
| return -1; | |
| } | |
| free (response); | |
| response = NULL; | |
| if (_panel_sendf (panel, &cmd_stat, &response, "%sDEBUG %s %s", | |
| set_unset ? "" : "NO", device ? device : "", mode_bits ? mode_bits : "") || | |
| (cmd_stat)) { | |
| sim_panel_set_error (NULL, "Can't %s Debug Mode: '%s' on Device '%s': %s", | |
| set_unset ? "Enable" : "Disable", mode_bits ? mode_bits : "", device, response); | |
| free (response); | |
| return -1; | |
| } | |
| free (response); | |
| return 0; | |
| } | |
| /** | |
| sim_panel_gen_deposit | |
| name_or_addr the name the simulator knows this register by | |
| size the size (in local storage) of the buffer which | |
| contains the data to be deposited into the simulator | |
| value a pointer to the buffer which contains the data to | |
| be deposited into the simulator | |
| */ | |
| int | |
| sim_panel_gen_deposit (PANEL *panel, | |
| const char *name_or_addr, | |
| size_t size, | |
| const void *value) | |
| { | |
| unsigned long long data = 0; | |
| int cmd_stat; | |
| if (!panel || (panel->State == Error)) { | |
| sim_panel_set_error (NULL, "Invalid Panel"); | |
| return -1; | |
| } | |
| if (panel->State == Run) { | |
| sim_panel_set_error (NULL, "Not Halted"); | |
| return -1; | |
| } | |
| if (little_endian) | |
| memcpy (&data, value, size); | |
| else | |
| memcpy (((char *)&data) + sizeof(data)-size, value, size); | |
| if (_panel_sendf (panel, &cmd_stat, NULL, "DEPOSIT -H %s %llx", name_or_addr, data)) | |
| return -1; | |
| return 0; | |
| } | |
| /** | |
| sim_panel_mem_examine | |
| addr_size the size (in local storage) of the buffer which | |
| contains the memory address of the data to be examined | |
| in the simulator | |
| addr a pointer to the buffer containing the memory address | |
| of the data to be examined in the simulator | |
| value_size the size (in local storage) of the buffer which will | |
| receive the data returned when examining the simulator | |
| value a pointer to the buffer which will be loaded with the | |
| data returned when examining the simulator | |
| */ | |
| int | |
| sim_panel_mem_examine (PANEL *panel, | |
| size_t addr_size, | |
| const void *addr, | |
| size_t value_size, | |
| void *value) | |
| { | |
| char *response = NULL, *c; | |
| unsigned long long data = 0, address = 0; | |
| int cmd_stat; | |
| if (!panel || (panel->State == Error)) { | |
| sim_panel_set_error (NULL, "Invalid Panel"); | |
| return -1; | |
| } | |
| if (panel->State == Run) { | |
| sim_panel_set_error (NULL, "Not Halted"); | |
| return -1; | |
| } | |
| if (little_endian) | |
| memcpy (&address, addr, addr_size); | |
| else | |
| memcpy (((char *)&address) + sizeof(address)-addr_size, addr, addr_size); | |
| if (_panel_sendf (panel, &cmd_stat, &response, (panel->radix == 16) ? "EXAMINE -H %llx" : "EXAMINE -H %llo", address)) { | |
| free (response); | |
| return -1; | |
| } | |
| c = strchr (response, ':'); | |
| if (!c) { | |
| sim_panel_set_error (NULL, "%s", response); | |
| free (response); | |
| return -1; | |
| } | |
| data = strtoull (c + 1, NULL, 16); | |
| if (little_endian) | |
| memcpy (value, &data, value_size); | |
| else | |
| memcpy (value, ((char *)&data) + sizeof(data)-value_size, value_size); | |
| free (response); | |
| return 0; | |
| } | |
| /** | |
| sim_panel_mem_deposit | |
| addr_size the size (in local storage) of the buffer which | |
| contains the memory address of the data to be deposited | |
| into the simulator | |
| addr a pointer to the buffer containing the memory address | |
| of the data to be deposited into the simulator | |
| value_size the size (in local storage) of the buffer which will | |
| contains the data to be deposited into the simulator | |
| value a pointer to the buffer which contains the data to be | |
| deposited into the simulator | |
| */ | |
| int | |
| sim_panel_mem_deposit (PANEL *panel, | |
| size_t addr_size, | |
| const void *addr, | |
| size_t value_size, | |
| const void *value) | |
| { | |
| unsigned long long data = 0, address = 0; | |
| int cmd_stat; | |
| if (!panel || (panel->State == Error)) { | |
| sim_panel_set_error (NULL, "Invalid Panel"); | |
| return -1; | |
| } | |
| if (panel->State == Run) { | |
| sim_panel_set_error (NULL, "Not Halted"); | |
| return -1; | |
| } | |
| if (little_endian) { | |
| memcpy (&data, value, value_size); | |
| memcpy (&address, addr, addr_size); | |
| } | |
| 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, &cmd_stat, NULL, (panel->radix == 16) ? "DEPOSIT -H %llx %llx" : "DEPOSIT -H %llo %llx", address, data)) | |
| return -1; | |
| return 0; | |
| } | |
| /** | |
| sim_panel_mem_deposit_instruction | |
| addr_size the size (in local storage) of the buffer which | |
| contains the memory address of the data to be deposited | |
| into the simulator | |
| addr a pointer to the buffer containing the memory address | |
| of the data to be deposited into the simulator | |
| instruction a pointer to the buffer that contains the mnemonic | |
| instruction to be deposited at the indicated address | |
| */ | |
| int | |
| sim_panel_mem_deposit_instruction (PANEL *panel, | |
| size_t addr_size, | |
| const void *addr, | |
| const char *instruction) | |
| { | |
| unsigned long long address = 0; | |
| int cmd_stat; | |
| if (!panel || (panel->State == Error)) { | |
| sim_panel_set_error (NULL, "Invalid Panel"); | |
| return -1; | |
| } | |
| if (panel->State == Run) { | |
| sim_panel_set_error (NULL, "Not Halted"); | |
| return -1; | |
| } | |
| if (little_endian) | |
| memcpy (&address, addr, addr_size); | |
| else | |
| memcpy (((char *)&address) + sizeof(address)-addr_size, addr, addr_size); | |
| if (_panel_sendf (panel, &cmd_stat, NULL, (panel->radix == 16) ? "DEPOSIT -H %llx %s" : "DEPOSIT -H %llo %s", address, instruction)) | |
| return -1; | |
| return 0; | |
| } | |
| /** | |
| sim_panel_set_register_value | |
| name the name of a simulator register or a memory address | |
| which is to receive a new value | |
| value the new value in character string form. The string | |
| must be in the native/natural radix that the simulator | |
| uses when referencing that register | |
| */ | |
| int | |
| 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 (NULL, "Invalid Panel"); | |
| return -1; | |
| } | |
| if (panel->State == Run) { | |
| sim_panel_set_error (NULL, "Not Halted"); | |
| return -1; | |
| } | |
| if (_panel_sendf (panel, &cmd_stat, NULL, "DEPOSIT %s %s", name, value)) | |
| return -1; | |
| return 0; | |
| } | |
| /** | |
| sim_panel_mount | |
| device the name of a simulator device/unit | |
| switches any switches appropriate for the desire attach | |
| path the path on the local system to be attached | |
| */ | |
| int | |
| sim_panel_mount (PANEL *panel, | |
| const char *device, | |
| const char *switches, | |
| const char *path) | |
| { | |
| char *response = NULL; | |
| OperationalState OrigState; | |
| int stat = 0; | |
| int cmd_stat; | |
| if (!panel || (panel->State == Error)) { | |
| sim_panel_set_error (NULL, "Invalid Panel"); | |
| return -1; | |
| } | |
| OrigState = panel->State; | |
| if (OrigState == Run) | |
| sim_panel_exec_halt (panel); | |
| 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 (NULL, response); | |
| stat = -1; | |
| } | |
| } while (0); | |
| if (OrigState == Run) | |
| sim_panel_exec_run (panel); | |
| free (response); | |
| return stat; | |
| } | |
| /** | |
| sim_panel_dismount | |
| device the name of a simulator device/unit | |
| */ | |
| int | |
| sim_panel_dismount (PANEL *panel, | |
| const char *device) | |
| { | |
| char *response = NULL; | |
| OperationalState OrigState; | |
| int stat = 0; | |
| int cmd_stat; | |
| if (!panel || (panel->State == Error)) { | |
| sim_panel_set_error (NULL, "Invalid Panel"); | |
| return -1; | |
| } | |
| OrigState = panel->State; | |
| if (OrigState == Run) | |
| sim_panel_exec_halt (panel); | |
| do { | |
| if (_panel_sendf (panel, &cmd_stat, &response, "DETACH %s", device)) { | |
| stat = -1; | |
| break; | |
| } | |
| if (cmd_stat) { | |
| sim_panel_set_error (NULL, "%s", response); | |
| stat = -1; | |
| } | |
| } while (0); | |
| if (OrigState == Run) | |
| sim_panel_exec_run (panel); | |
| free (response); | |
| return stat; | |
| } | |
| static void * | |
| _panel_reader(void *arg) | |
| { | |
| PANEL *p = (PANEL*)arg; | |
| REG *r = NULL; | |
| int sched_policy; | |
| struct sched_param sched_priority; | |
| char buf[4096]; | |
| int buf_data = 0; | |
| int processing_register_output = 0; | |
| int io_wait_done = 0; | |
| /* | |
| Boost Priority for this response processing thread to quickly digest | |
| arriving data. | |
| */ | |
| pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority); | |
| ++sched_priority.sched_priority; | |
| pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); | |
| pthread_setspecific (panel_thread_id, "reader"); | |
| _panel_debug (p, DBG_THR, "Starting", NULL, 0); | |
| buf[buf_data] = '\0'; | |
| pthread_mutex_lock (&p->io_lock); | |
| if (!p->parent) { | |
| while (1) { | |
| int new_data = sim_read_sock (p->sock, &buf[buf_data], sizeof(buf)-(buf_data+1)); | |
| if (new_data <= 0) { | |
| sim_panel_set_error (NULL, "%s after reading %d bytes: %s", sim_get_err_sock("Unexpected socket read"), buf_data, buf); | |
| _panel_debug (p, DBG_RCV, "%s", NULL, 0, sim_panel_get_error()); | |
| p->State = Error; | |
| break; | |
| } | |
| _panel_debug (p, DBG_RCV, "Startup receive of %d bytes: ", &buf[buf_data], new_data, new_data); | |
| buf_data += new_data; | |
| buf[buf_data] = '\0'; | |
| if (!memcmp (mantra, buf, sizeof (mantra))) { /* strip initial telnet mantra from input stream */ | |
| memmove (buf, buf + sizeof (mantra), 1 + buf_data - sizeof (mantra)); | |
| buf_data -= sizeof (mantra); | |
| } | |
| if ((size_t)buf_data < strlen (sim_prompt)) | |
| continue; | |
| if (!strcmp (sim_prompt, &buf[buf_data - strlen (sim_prompt)])) { | |
| memmove (buf, &buf[buf_data - strlen (sim_prompt)], strlen (sim_prompt) + 1); | |
| buf_data = strlen (sim_prompt); | |
| break; | |
| } | |
| } | |
| } | |
| p->io_thread_running = 1; | |
| pthread_mutex_unlock (&p->io_lock); | |
| pthread_cond_signal (&p->startup_done); /* Signal we're ready to go */ | |
| msleep (100); | |
| pthread_mutex_lock (&p->io_lock); | |
| while ((p->sock != INVALID_SOCKET) && | |
| (p->State != Error)) { | |
| int new_data; | |
| char *s, *e, *eol; | |
| if (NULL == strchr (buf, '\n')) { | |
| pthread_mutex_unlock (&p->io_lock); | |
| new_data = sim_read_sock (p->sock, &buf[buf_data], sizeof(buf)-(buf_data+1)); | |
| pthread_mutex_lock (&p->io_lock); | |
| if (new_data <= 0) { | |
| sim_panel_set_error (NULL, "%s", sim_get_err_sock("Unexpected socket read")); | |
| _panel_debug (p, DBG_RCV, "%s", NULL, 0, sim_panel_get_error()); | |
| p->State = Error; | |
| break; | |
| } | |
| _panel_debug (p, DBG_RCV, "Received %d bytes: ", &buf[buf_data], new_data, new_data); | |
| buf_data += new_data; | |
| buf[buf_data] = '\0'; | |
| } | |
| s = buf; | |
| while ((eol = strchr (s, '\n'))) { | |
| /* Line to process */ | |
| *eol++ = '\0'; | |
| while ((*s) && (s[strlen(s)-1] == '\r')) | |
| s[strlen(s)-1] = '\0'; | |
| if (processing_register_output) { | |
| e = strchr (s, ':'); | |
| if (e) { | |
| size_t i; | |
| char smp_dev[32], smp_reg[32], smp_ind[32]; | |
| unsigned int bit; | |
| *e++ = '\0'; | |
| if (!strcmp("Time", s)) { | |
| p->simulation_time = strtoull (e, NULL, 10); | |
| s = eol; | |
| while (isspace(0xFF & (*s))) | |
| ++s; | |
| continue; /* process next line */ | |
| } | |
| if ((*s == '}') && | |
| (3 == sscanf (s, "}%s %s %s", smp_dev, smp_reg, smp_ind))) { /* Register bit Sample Data? */ | |
| r = NULL; | |
| for (i=0; i<p->reg_count; i++) { | |
| if (p->regs[i].bits == NULL) | |
| continue; | |
| if ((!strcmp (smp_reg, p->regs[i].name)) && | |
| ((!p->device_name) || (!strcmp (smp_dev, p->device_name)))) { | |
| r = &p->regs[i]; | |
| break; | |
| } | |
| } | |
| if (r) { | |
| for (bit = 0; bit < r->bit_count; bit++) { | |
| int val = (int)strtol (e, &e, 10); | |
| r->bits[bit] = val; | |
| if (*e == ',') | |
| ++e; | |
| else | |
| break; | |
| } | |
| s = eol; | |
| } | |
| while (isspace(0xFF & (*s))) | |
| ++s; | |
| r = NULL; | |
| continue; /* process next line */ | |
| } | |
| if (!strncmp (s + strlen (sim_prompt), register_ind_echo, strlen (register_ind_echo) - 1)) { | |
| e = s + strlen (sim_prompt) + strlen (register_ind_echo); | |
| r = NULL; | |
| for (i=0; i<p->reg_count; i++) { | |
| if (p->regs[i].indirect && (!strcmp(p->regs[i].name, e))) { | |
| r = &p->regs[i]; | |
| break; | |
| } | |
| } | |
| s = eol; | |
| while (isspace(0xFF & (*s))) | |
| ++s; | |
| if (r) | |
| continue; /* process next line */ | |
| } | |
| if (r) { | |
| if (strcmp (s, r->name)) { | |
| unsigned long long data; | |
| data = strtoull (e, NULL, 16); | |
| if (little_endian) | |
| memcpy (r->addr, &data, r->size); | |
| else | |
| memcpy (r->addr, ((char *)&data) + sizeof(data)-r->size, r->size); | |
| r = NULL; | |
| } | |
| s = eol; | |
| while (isspace(0xFF & (*s))) | |
| ++s; | |
| continue; /* process next line */ | |
| } | |
| for (i=0; i<p->reg_count; i++) { | |
| if (p->regs[i].element_count == 0) { | |
| if (!strcmp(p->regs[i].name, s)) { | |
| unsigned long long data; | |
| data = strtoull (e, NULL, 16); | |
| if (little_endian) | |
| memcpy (p->regs[i].addr, &data, p->regs[i].size); | |
| else | |
| memcpy (p->regs[i].addr, ((char *)&data) + sizeof(data)-p->regs[i].size, p->regs[i].size); | |
| break; | |
| } | |
| } | |
| else { | |
| size_t name_len = strlen (p->regs[i].name); | |
| 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, '['); | |
| if (end) | |
| end_index = (size_t)atoi (end + 1); | |
| if (strcmp (e, " same as above")) | |
| p->array_element_data = strtoull (e, NULL, 16); | |
| while (array_index <= end_index) { | |
| if (little_endian) | |
| memcpy ((char *)(p->regs[i].addr) + (array_index * p->regs[i].size), &p->array_element_data, p->regs[i].size); | |
| else | |
| memcpy ((char *)(p->regs[i].addr) + (array_index * p->regs[i].size), ((char *)&p->array_element_data) + sizeof(p->array_element_data)-p->regs[i].size, p->regs[i].size); | |
| ++array_index; | |
| } | |
| break; | |
| } | |
| } | |
| } | |
| if (i != p->reg_count) { | |
| s = eol; | |
| while (isspace(0xFF & (*s))) | |
| ++s; | |
| continue; | |
| } | |
| --e; | |
| *e = ':'; | |
| /* Unexpected Register Data Found (or other output containing a : character) */ | |
| } | |
| } | |
| if ((strlen (s) > strlen (sim_prompt)) && (!strcmp (s + strlen (sim_prompt), register_repeat_end))) { | |
| _panel_debug (p, DBG_RCV, "*Repeat Block Complete (Accumulated Data = %d)", NULL, 0, (int)p->io_response_data); | |
| if (p->callback) { | |
| pthread_mutex_unlock (&p->io_lock); | |
| p->callback (p, p->simulation_time_base + p->simulation_time, p->callback_context); | |
| pthread_mutex_lock (&p->io_lock); | |
| } | |
| processing_register_output = 0; | |
| p->io_response_data = 0; | |
| p->io_response[p->io_response_data] = '\0'; | |
| goto Start_Next_Line; | |
| } | |
| if ((strlen (s) > strlen (sim_prompt)) && | |
| ((!strcmp (s + strlen (sim_prompt), register_repeat_start)) || | |
| (!strcmp (s + strlen (sim_prompt), register_get_start)))) { | |
| _panel_debug (p, DBG_RCV, "*Repeat/Register Block Starting", NULL, 0); | |
| processing_register_output = 1; | |
| goto Start_Next_Line; | |
| } | |
| if ((strlen (s) > strlen (sim_prompt)) && | |
| (!strcmp (s + strlen (sim_prompt), register_get_end))) { | |
| _panel_debug (p, DBG_RCV, "*Register Block Complete", NULL, 0); | |
| p->io_waiting = 0; | |
| processing_register_output = 0; | |
| pthread_cond_signal (&p->io_done); | |
| goto Start_Next_Line; | |
| } | |
| if ((strlen (s) > strlen (sim_prompt)) && (!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); | |
| goto Start_Next_Line; | |
| } | |
| /* Non Register Data Found (echo of EXAMINE or other commands and/or command output) */ | |
| if (p->io_response_data + strlen (s) + 3 > p->io_response_size) { | |
| char *t = (char *)_panel_malloc (p->io_response_data + strlen (s) + 3); | |
| if (t == NULL) { | |
| _panel_debug (p, DBG_RCV, "%s", NULL, 0, sim_panel_get_error()); | |
| p->State = Error; | |
| break; | |
| } | |
| memcpy (t, p->io_response, p->io_response_data); | |
| free (p->io_response); | |
| 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; | |
| if ((!p->parent) && | |
| (p->completion_string) && | |
| (!memcmp (s, p->completion_string, strlen (p->completion_string)))) { | |
| _panel_debug (p, DBG_RCV, "Match with potentially coalesced additional data: '%s'", NULL, 0, p->completion_string); | |
| if (eol < &buf[buf_data]) | |
| memset (s + strlen (s), ' ', eol - (s + strlen (s))); | |
| break; | |
| } | |
| Start_Next_Line: | |
| s = eol; | |
| while (isspace(0xFF & (*s))) | |
| ++s; | |
| } | |
| memmove (buf, s, buf_data - (s - buf) + 1); | |
| buf_data = strlen (buf); | |
| if (buf_data) | |
| _panel_debug (p, DBG_RSP, "Remnant Buffer Contents: '%s'", NULL, 0, buf); | |
| if ((!p->parent) && | |
| (p->completion_string) && | |
| (!memcmp (buf, p->completion_string, strlen (p->completion_string)))) { | |
| _panel_debug (p, DBG_RCV, "*Received Command Complete - Match: '%s'", NULL, 0, p->completion_string); | |
| io_wait_done = 1; | |
| } | |
| if (!memcmp ("Simulator Running...", buf, 20)) { | |
| _panel_debug (p, DBG_RSP, "State transitioning to Run", NULL, 0); | |
| p->State = Run; | |
| buf_data -= 20; | |
| if (buf_data) { | |
| memmove (buf, buf + 20, buf_data + 1); | |
| _panel_debug (p, DBG_RSP, "Remnant Buffer Contents: '%s'", NULL, 0, buf); | |
| } | |
| else | |
| buf[buf_data] = '\0'; | |
| if (io_wait_done) { /* someone waiting for this? */ | |
| _panel_debug (p, DBG_RCV, "*Match Command Complete - Match signaling waiting thread", NULL, 0); | |
| io_wait_done = 0; | |
| p->io_waiting = 0; | |
| p->completion_string = NULL; | |
| pthread_cond_signal (&p->io_done); | |
| /* Let this state transition propagate to the interested thread(s) */ | |
| /* before processing remaining buffered data */ | |
| pthread_mutex_unlock (&p->io_lock); | |
| msleep (100); | |
| pthread_mutex_lock (&p->io_lock); | |
| } | |
| } | |
| if ((p->State == Run) && (!strcmp (buf, sim_prompt))) { | |
| _panel_debug (p, DBG_RSP, "State transitioning to Halt: io_wait_done: %d", NULL, 0, io_wait_done); | |
| p->State = Halt; | |
| free (p->halt_reason); | |
| p->halt_reason = (char *)_panel_malloc (1 + strlen (p->io_response)); | |
| if (p->halt_reason == NULL) { | |
| _panel_debug (p, DBG_RCV, "%s", NULL, 0, sim_panel_get_error()); | |
| p->State = Error; | |
| break; | |
| } | |
| strcpy (p->halt_reason, p->io_response); | |
| } | |
| if (io_wait_done) { | |
| _panel_debug (p, DBG_RCV, "*Match Command Complete - Match signaling waiting thread", NULL, 0); | |
| io_wait_done = 0; | |
| p->io_waiting = 0; | |
| p->completion_string = NULL; | |
| pthread_cond_signal (&p->io_done); | |
| } | |
| } | |
| if (p->io_waiting) { | |
| _panel_debug (p, DBG_THR, "Receive: restarting waiting thread while exiting", NULL, 0); | |
| p->io_waiting = 0; | |
| pthread_cond_signal (&p->io_done); | |
| } | |
| _panel_debug (p, DBG_THR, "Exiting", NULL, 0); | |
| pthread_setspecific (panel_thread_id, NULL); | |
| p->io_thread_running = 0; | |
| pthread_mutex_unlock (&p->io_lock); | |
| return NULL; | |
| } | |
| static void * | |
| _panel_callback(void *arg) | |
| { | |
| PANEL *p = (PANEL*)arg; | |
| int sched_policy; | |
| 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 | |
| with compute bound activities. | |
| */ | |
| pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority); | |
| ++sched_priority.sched_priority; | |
| pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); | |
| pthread_setspecific (panel_thread_id, "callback"); | |
| _panel_debug (p, DBG_THR, "Starting", NULL, 0); | |
| pthread_mutex_lock (&p->io_lock); | |
| p->callback_thread_running = 1; | |
| pthread_mutex_unlock (&p->io_lock); | |
| pthread_cond_signal (&p->startup_done); /* Signal we're ready to go */ | |
| msleep (100); | |
| pthread_mutex_lock (&p->io_lock); | |
| while ((p->sock != INVALID_SOCKET) && | |
| (p->usecs_between_callbacks) && | |
| (p->State != Error)) { | |
| int interval = p->usecs_between_callbacks; | |
| int new_register = p->new_register; | |
| p->new_register = 0; | |
| pthread_mutex_unlock (&p->io_lock); | |
| if (new_register) /* need to get and send updated register info */ | |
| _panel_register_query_string (p, &buf, &buf_data); | |
| /* 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 (new_register) { | |
| 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 + /* ; */ | |
| strlen (register_repeat_start) + /* auto repeat begin */ | |
| 1 + /* ; */ | |
| strlen (register_repeat_end) + /* auto repeat completion */ | |
| 1 + /* carriage return */ | |
| 1; /* NUL */ | |
| char *repeat = (char *)malloc (repeat_data); | |
| char *c; | |
| c = strstr (buf, register_get_start); /* remove register_get_start string and anything before it */ | |
| if (c) { /* always true */ | |
| buf_data -= (c - buf) + strlen (register_get_start); | |
| c += strlen (register_get_start); | |
| } | |
| sprintf (repeat, "%s%d%s%s%*.*s", register_repeat_prefix, | |
| p->usecs_between_callbacks, | |
| register_repeat_units, | |
| register_repeat_start, | |
| (int)buf_data, (int)buf_data, c); | |
| pthread_mutex_unlock (&p->io_lock); | |
| c = strstr (repeat, register_get_end); /* remove register_done_echo string and */ | |
| if (c) /* always true */ | |
| strcpy (c, register_repeat_end); /* replace it with the register_repeat_end 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); | |
| } | |
| /* 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 (_panel_get_registers (p, 1, NULL)) { | |
| pthread_mutex_lock (&p->io_lock); | |
| break; | |
| } | |
| if (p->callback) | |
| p->callback (p, p->simulation_time_base + p->simulation_time, p->callback_context); | |
| pthread_mutex_lock (&p->io_lock); | |
| } | |
| } | |
| pthread_mutex_unlock (&p->io_lock); | |
| /* stop any established repeating activity in the simulator */ | |
| if (p->parent == NULL) { /* Top level panel? */ | |
| _panel_debug (p, DBG_THR, "Stopping All Repeats before exiting", NULL, 0); | |
| _panel_sendf (p, &cmd_stat, NULL, "%s", register_repeat_stop_all); | |
| } | |
| else { | |
| _panel_debug (p, DBG_THR, "Stopping Repeats before exiting", NULL, 0); | |
| _panel_sendf (p, &cmd_stat, NULL, "%s", register_repeat_stop); | |
| } | |
| pthread_mutex_lock (&p->io_lock); | |
| _panel_debug (p, DBG_THR, "Exiting", NULL, 0); | |
| pthread_setspecific (panel_thread_id, NULL); | |
| p->callback_thread_running = 0; | |
| pthread_mutex_unlock (&p->io_lock); | |
| free (buf); | |
| return NULL; | |
| } | |
| const char *sim_panel_get_error (void) | |
| { | |
| return (sim_panel_error_buf ? sim_panel_error_buf : ""); | |
| } | |
| void sim_panel_clear_error (void) | |
| { | |
| if (sim_panel_error_bufsize) | |
| free (sim_panel_error_buf); | |
| sim_panel_error_buf = NULL; | |
| sim_panel_error_bufsize = 0; | |
| } | |
| #if defined (_WIN32) | |
| #define vsnprintf _vsnprintf | |
| #endif | |
| static int sim_panel_set_error (PANEL *p, const char *fmt, ...) | |
| { | |
| va_list arglist; | |
| int len; | |
| if (p) { | |
| pthread_mutex_lock (&p->io_lock); | |
| p->State = Error; | |
| pthread_mutex_unlock (&p->io_lock); | |
| } | |
| if (sim_panel_error_bufsize == 0) { | |
| sim_panel_error_bufsize = 2048; | |
| sim_panel_error_buf = (char *) malloc (sim_panel_error_bufsize); | |
| if (sim_panel_error_buf == NULL) { | |
| sim_panel_error_buf = (char *)"sim_panel_set_error(): Out of Memory\n"; | |
| sim_panel_error_bufsize = 0; | |
| return -1; | |
| } | |
| } | |
| sim_panel_error_buf[sim_panel_error_bufsize-1] = '\0'; | |
| while (1) { /* format passed string, args */ | |
| va_start (arglist, fmt); | |
| len = vsnprintf (sim_panel_error_buf, sim_panel_error_bufsize-1, fmt, arglist); | |
| va_end (arglist); | |
| if (len < 0) /* Format encoding error? */ | |
| break; | |
| /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */ | |
| if (len >= (int)(sim_panel_error_bufsize-1)) { | |
| free (sim_panel_error_buf); | |
| sim_panel_error_bufsize = sim_panel_error_bufsize * 2; | |
| while ((int)sim_panel_error_bufsize < len + 2) | |
| sim_panel_error_bufsize = sim_panel_error_bufsize * 2; | |
| sim_panel_error_buf = (char *) malloc (sim_panel_error_bufsize); | |
| if (sim_panel_error_buf == NULL) { | |
| sim_panel_error_buf = (char *)"sim_panel_set_error(): Out of Memory\n"; | |
| sim_panel_error_bufsize = 0; | |
| return -1; | |
| } | |
| sim_panel_error_buf[sim_panel_error_bufsize-1] = '\0'; | |
| continue; | |
| } | |
| break; | |
| } | |
| return -1; | |
| } | |
| static int | |
| _panel_vsendf_completion (PANEL *p, int *completion_status, char **response, const char *completion_string, const char *fmt, va_list arglist) | |
| { | |
| char stackbuf[1024]; | |
| int bufsize = sizeof(stackbuf); | |
| char *buf = stackbuf; | |
| int len, status_echo_len = 0, sent_len; | |
| int post_fix_len = completion_status ? 7 + sizeof (command_done_echo) + sizeof (command_status) : 1; | |
| int ret; | |
| if (completion_status && completion_string) /* one or the other, but */ | |
| return -1; /* not both */ | |
| while (1) { /* format passed string, args */ | |
| len = vsnprintf (buf, bufsize-1, fmt, arglist); | |
| if (len < 0) | |
| return sim_panel_set_error (NULL, "Format encoding error while processing '%s'", fmt); | |
| /* If the formatted result didn't fit into the buffer, then grow the buffer and try again */ | |
| if ((len + post_fix_len) >= bufsize-1) { | |
| if (buf != stackbuf) | |
| free (buf); | |
| bufsize = bufsize * 2; | |
| if (bufsize < (len + post_fix_len + 2)) | |
| bufsize = len + post_fix_len + 2; | |
| buf = (char *) _panel_malloc (bufsize); | |
| if (buf == NULL) | |
| return -1; | |
| buf[bufsize-1] = '\0'; | |
| continue; | |
| } | |
| break; | |
| } | |
| if (len && (buf[len-1] != '\r')) { | |
| strcpy (&buf[len], "\r"); /* Make sure command line is terminated */ | |
| ++len; | |
| } | |
| pthread_mutex_lock (&p->io_command_lock); | |
| ++p->command_count; | |
| if (completion_status || completion_string) { | |
| 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->completion_string = completion_string; | |
| if (p->io_response_data) | |
| _panel_debug (p, DBG_RCV, "Receive Data Discarded: ", p->io_response, p->io_response_data); | |
| p->io_response_data = 0; | |
| p->io_waiting = 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) == (sent_len = _panel_send (p, buf, len + status_echo_len))) ? 0 : -1; | |
| if (completion_status || completion_string) { | |
| if (ret) { /* Send failed? */ | |
| p->completion_string = NULL; | |
| p->io_waiting = 0; | |
| } | |
| else { /* Sent OK? */ | |
| char *tresponse = NULL; | |
| while (p->io_waiting) | |
| pthread_cond_wait (&p->io_done, &p->io_lock); /* Wait for completion */ | |
| tresponse = (char *)_panel_malloc (p->io_response_data + 1); | |
| if (0 == memcmp (buf, p->io_response + strlen (sim_prompt), len)) { | |
| char *eol, *status; | |
| memcpy (tresponse, p->io_response + strlen (sim_prompt) + len + 1, p->io_response_data + 1 - (strlen (sim_prompt) + len + 1)); | |
| if (completion_status) { | |
| *completion_status = -1; | |
| status = strstr (tresponse, 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 (tresponse, p->io_response, p->io_response_data + 1); | |
| if (response) { | |
| *response = tresponse; | |
| if (completion_status) | |
| _panel_debug (p, DBG_RSP, "Command %d Response(Status=%d): '%s'", NULL, 0, p->command_count, *completion_status, *response); | |
| else | |
| _panel_debug (p, DBG_RSP, "Command %d Response - Match '%s': '%s'", NULL, 0, p->command_count, completion_string, *response); | |
| } | |
| else { | |
| free (tresponse); | |
| if (p->io_response_data) { | |
| if (completion_status) | |
| _panel_debug (p, DBG_RSP, "Discarded Unwanted Command %d Response Data(Status=%d):", p->io_response, p->io_response_data, p->command_count, *completion_status); | |
| else | |
| _panel_debug (p, DBG_RSP, "Discarded Unwanted Command %d Response Data - Match '%s':", p->io_response, p->io_response_data, p->command_count, completion_string); | |
| } | |
| } | |
| } | |
| p->completion_string = NULL; | |
| p->io_response_data = 0; | |
| p->io_response[0] = '\0'; | |
| pthread_mutex_unlock (&p->io_lock); | |
| } | |
| else { | |
| if (ret) | |
| sim_panel_set_error (p, "Unexpected send length: %d, expected: %d", sent_len, len + status_echo_len); | |
| } | |
| pthread_mutex_unlock (&p->io_command_lock); | |
| if (buf != stackbuf) | |
| free (buf); | |
| return ret; | |
| } | |
| static int | |
| _panel_sendf (PANEL *p, int *completion_status, char **response, const char *fmt, ...) | |
| { | |
| va_list arglist; | |
| int status; | |
| va_start (arglist, fmt); | |
| status = _panel_vsendf_completion (p, completion_status, response, NULL, fmt, arglist); | |
| va_end (arglist); | |
| return status; | |
| } | |
| static int | |
| _panel_sendf_completion (PANEL *p, char **response, const char *completion, const char *fmt, ...) | |
| { | |
| va_list arglist; | |
| int status; | |
| va_start (arglist, fmt); | |
| status = _panel_vsendf_completion (p, NULL, response, completion, fmt, arglist); | |
| va_end (arglist); | |
| return status; | |
| } | |
| #ifdef __cplusplus | |
| } | |
| #endif |