From 8735538d721b5b4173d2b101745819053f58e952 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Mon, 29 Apr 2013 05:39:22 -0700 Subject: [PATCH 01/23] Fix building under Visual Studio on Windows XP. --- Visual Studio Projects/ALTAIR.vcproj | 4 +- Visual Studio Projects/AltairZ80.vcproj | 4 +- Visual Studio Projects/BuildAll.cmd | 2 +- Visual Studio Projects/ECLIPSE.vcproj | 4 +- Visual Studio Projects/GRI.vcproj | 4 +- Visual Studio Projects/H316.vcproj | 4 +- Visual Studio Projects/HP2100.vcproj | 4 +- Visual Studio Projects/I1401.vcproj | 4 +- Visual Studio Projects/I1620.vcproj | 4 +- Visual Studio Projects/I7094.vcproj | 4 +- Visual Studio Projects/IBM1130.vcproj | 4 +- Visual Studio Projects/ID16.vcproj | 4 +- Visual Studio Projects/ID32.vcproj | 4 +- Visual Studio Projects/NOVA.vcproj | 4 +- Visual Studio Projects/PDP1.vcproj | 4 +- Visual Studio Projects/PDP10.vcproj | 4 +- Visual Studio Projects/PDP11.vcproj | 4 +- Visual Studio Projects/PDP15.vcproj | 4 +- Visual Studio Projects/PDP4.vcproj | 4 +- Visual Studio Projects/PDP7.vcproj | 4 +- Visual Studio Projects/PDP8.vcproj | 4 +- Visual Studio Projects/PDP9.vcproj | 4 +- Visual Studio Projects/Pre-Build-Event.cmd | 93 +++++++++++++++++++++ Visual Studio Projects/S3.vcproj | 4 +- Visual Studio Projects/SDS.vcproj | 4 +- Visual Studio Projects/TX-0.vcproj | 4 +- Visual Studio Projects/VAX.vcproj | 6 +- Visual Studio Projects/VAX610.vcproj | 4 +- Visual Studio Projects/VAX620.vcproj | 4 +- Visual Studio Projects/VAX630.vcproj | 4 +- Visual Studio Projects/VAX730.vcproj | 4 +- Visual Studio Projects/VAX750.vcproj | 4 +- Visual Studio Projects/VAX780.vcproj | 4 +- Visual Studio Projects/VAX8600.vcproj | 4 +- Visual Studio Projects/lgp.vcproj | 4 +- Visual Studio Projects/swtp6800mp-a.vcproj | 4 +- Visual Studio Projects/swtp6800mp-a2.vcproj | 4 +- makefile | 3 + 38 files changed, 168 insertions(+), 72 deletions(-) create mode 100644 Visual Studio Projects/Pre-Build-Event.cmd diff --git a/Visual Studio Projects/ALTAIR.vcproj b/Visual Studio Projects/ALTAIR.vcproj index 28bda3c4..30204e21 100644 --- a/Visual Studio Projects/ALTAIR.vcproj +++ b/Visual Studio Projects/ALTAIR.vcproj @@ -27,7 +27,7 @@ NUL +if not exist ../../windows-build/winpcap/Wpdpack/Include/pcap.h goto _notice +if not exist ../../windows-build/pthreads/pthread.h goto _notice +goto _done_build +:_notice +echo **************************************************** +echo **************************************************** +echo ** The required build support is not available. ** +echo **************************************************** +echo **************************************************** +type 0ReadMe_Projects.txt +exit 1 +:_done_build + +if not exist ..\.git goto _done_hooks +if exist ..\.git\hooks\post-commit goto _done_hooks +echo ***************************************************** +echo ***************************************************** +echo ** Installing git hooks in newly cloned repository ** +echo ***************************************************** +echo ***************************************************** +copy git-hooks\post* ..\.git\hooks\ +:_done_hooks + +:_SetId +SET GIT_COMMIT_ID= +if not exist ..\.git-commit-id goto _NoId +for /F %%i in (..\.git-commit-id) do SET GIT_COMMIT_ID=%%i +:_NoId +SET OLD_GIT_COMMIT_ID= +if not exist .git-commit-id.h echo.>.git-commit-id.h +for /F "tokens=3" %%i in (.git-commit-id.h) do SET OLD_GIT_COMMIT_ID=%%i +if "%GIT_COMMIT_ID%" equ "%OLD_GIT_COMMIT_ID%" goto _IdGood +echo #define SIM_GIT_COMMIT_ID %GIT_COMMIT_ID% >.git-commit-id.h +:_IdGood +:_done_id \ No newline at end of file diff --git a/Visual Studio Projects/S3.vcproj b/Visual Studio Projects/S3.vcproj index c06bf470..74c4f754 100644 --- a/Visual Studio Projects/S3.vcproj +++ b/Visual Studio Projects/S3.vcproj @@ -27,7 +27,7 @@ NUL) + endif GCC_VERSION = $(word 3,$(shell $(GCC) --version)) COMPILER_NAME = GCC Version: $(GCC_VERSION) LTO_EXCLUDE_VERSIONS = 4.5.2 From b1e31a7034244e07190152d72cd89b5365cf194b Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Tue, 30 Apr 2013 08:37:11 -0700 Subject: [PATCH 02/23] Fixed Remote Console to tolerate bare LF as a line terminator and enhanced the tmxr debug output for special characters --- sim_console.c | 3 +++ sim_tmxr.c | 19 +++++++++++++++++-- 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/sim_console.c b/sim_console.c index aa359d59..a151e64f 100644 --- a/sim_console.c +++ b/sim_console.c @@ -598,6 +598,9 @@ for (i=(was_stepping ? sim_rem_step_line : 0); --sim_rem_buf_ptr[i]; } break; + case '\n': + if (sim_rem_buf_ptr[i] == 0) + break; case '\r': tmxr_linemsg (lp, "\r\n"); if (sim_rem_buf_ptr[i]+1 >= sim_rem_buf_size[i]) { diff --git a/sim_tmxr.c b/sim_tmxr.c index 4d34b9b2..6c377301 100644 --- a/sim_tmxr.c +++ b/sim_tmxr.c @@ -3550,10 +3550,25 @@ if ((lp->mp->dptr) && (dbits & lp->mp->dptr->dctrl)) { for (i=0; i= 1) && (buf[i] <= 26)) { + tmxr_buf_debug_char ('^'); + tmxr_buf_debug_char ('A' + buf[i] - 1); + } + else { + char octal[8]; + + sprintf(octal, "\\%03o", (u_char)buf[i]); + tmxr_buf_debug_string (octal); + } + tmxr_buf_debug_char ('_'); + } break; } - if (buf[i] == tn_chars[j].value) { + if ((u_char)buf[i] == tn_chars[j].value) { tmxr_buf_debug_char ('_'); tmxr_buf_debug_string (tn_chars[j].name); tmxr_buf_debug_char ('_'); From 67712d00955c73941aad616f0e7aec17c5d58eb0 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Wed, 1 May 2013 13:43:10 -0700 Subject: [PATCH 03/23] Added more extensive debug output of tmxr telnet parameter and data --- sim_tmxr.c | 161 +++++++++++++++++++++++++++++++++++++++++++++++------ 1 file changed, 143 insertions(+), 18 deletions(-) diff --git a/sim_tmxr.c b/sim_tmxr.c index 6c377301..90ddcff6 100644 --- a/sim_tmxr.c +++ b/sim_tmxr.c @@ -353,10 +353,44 @@ /* Options */ -#define TN_BIN 0 /* bin */ -#define TN_ECHO 1 /* echo */ -#define TN_SGA 3 /* sga */ -#define TN_LINE 34 /* line mode */ +#define TN_BIN 0 /* bin */ +#define TN_ECHO 1 /* echo */ +#define TN_SGA 3 /* sga */ +#define TN_STATUS 5 /* option status query */ +#define TN_TIMING 6 /* Timing Mark */ +#define TN_NAOCRD 10 /* Output Carriage-Return Disposition */ +#define TN_NAOHTS 11 /* Output Horizontal Tab Stops */ +#define TN_NAOHTD 12 /* Output Horizontal Tab Stop Disposition */ +#define TN_NAOFFD 13 /* Output Forfeed Disposition */ +#define TN_NAOVTS 14 /* Output Vertical Tab Stop */ +#define TN_NAOVTD 15 /* Output Vertical Tab Stop Disposition */ +#define TN_NAOLFD 16 /* Output Linefeed Disposition */ +#define TN_EXTEND 17 /* Extended Ascii */ +#define TN_LOGOUT 18 /* Logout */ +#define TN_BM 19 /* Byte Macro */ +#define TN_DET 20 /* Data Entry Terminal */ +#define TN_SENDLO 23 /* Send Location */ +#define TN_TERMTY 24 /* Terminal Type */ +#define TN_ENDREC 25 /* Terminal Type */ +#define TN_TUID 26 /* TACACS User Identification */ +#define TN_OUTMRK 27 /* Output Marking */ +#define TN_TTYLOC 28 /* Terminal Location Number */ +#define TN_3270 29 /* 3270 Regime */ +#define TN_X3PAD 30 /* X.3 PAD */ +#define TN_NAWS 31 /* Negotiate About Window Size */ +#define TN_TERMSP 32 /* Terminal Speed */ +#define TN_TOGFLO 33 /* Remote Flow Control */ +#define TN_LINE 34 /* line mode */ +#define TN_XDISPL 35 /* X Display Location */ +#define TN_ENVIRO 36 /* Environment */ +#define TN_AUTH 37 /* Authentication */ +#define TN_ENCRYP 38 /* Data Encryption */ +#define TN_NEWENV 39 /* New Environment */ +#define TN_TN3270 40 /* TN3270 Enhancements */ +#define TN_CHARST 42 /* CHARSET */ +#define TN_COMPRT 44 /* Com Port Control */ +#define TN_KERMIT 47 /* KERMIT */ + #define TN_CR 015 /* carriage return */ #define TN_LF 012 /* line feed */ #define TN_NUL 000 /* null */ @@ -1280,9 +1314,12 @@ for (i = 0; i < mp->lines; i++) { /* loop thru lines */ case TNS_WILL: case TNS_WONT: /* IAC+WILL/WONT prev */ if (tmp == TN_BIN) { /* BIN? */ - if (lp->tsta == TNS_WILL) + if (lp->tsta == TNS_WILL) { lp->dstb = 0; - else lp->dstb = 1; + } + else { + lp->dstb = 1; + } } tmxr_rmvrc (lp, j); /* remove it */ lp->tsta = TNS_NORM; /* next normal */ @@ -3514,9 +3551,40 @@ static struct { {TN_BIN, "TN_BIN"}, /* bin */ {TN_ECHO, "TN_ECHO"}, /* echo */ {TN_SGA, "TN_SGA"}, /* sga */ + {TN_STATUS, "TN_STATUS"}, /* option status query */ + {TN_TIMING, "TN_TIMING"}, /* Timing Mark */ + {TN_NAOCRD, "TN_NAOCRD"}, /* Output Carriage-Return Disposition */ + {TN_NAOHTS, "TN_NAOHTS"}, /* Output Horizontal Tab Stops */ + {TN_NAOHTD, "TN_NAOHTD"}, /* Output Horizontal Tab Stop Disposition */ + {TN_NAOFFD, "TN_NAOFFD"}, /* Output Forfeed Disposition */ + {TN_NAOVTS, "TN_NAOVTS"}, /* Output Vertical Tab Stop */ + {TN_NAOVTD, "TN_NAOVTD"}, /* Output Vertical Tab Stop Disposition */ + {TN_NAOLFD, "TN_NAOLFD"}, /* Output Linefeed Disposition */ + {TN_EXTEND, "TN_EXTEND"}, /* Extended Ascii */ + {TN_LOGOUT, "TN_LOGOUT"}, /* Logout */ + {TN_BM, "TN_BM"}, /* Byte Macro */ + {TN_DET, "TN_DET"}, /* Data Entry Terminal */ + {TN_SENDLO, "TN_SENDLO"}, /* Send Location */ + {TN_TERMTY, "TN_TERMTY"}, /* Terminal Type */ + {TN_ENDREC, "TN_ENDREC"}, /* Terminal Type */ + {TN_TUID, "TN_TUID"}, /* TACACS User Identification */ + {TN_OUTMRK, "TN_OUTMRK"}, /* Output Marking */ + {TN_TTYLOC, "TN_TTYLOC"}, /* Terminal Location Number */ + {TN_3270, "TN_3270"}, /* 3270 Regime */ + {TN_X3PAD, "TN_X3PAD"}, /* X.3 PAD */ + {TN_NAWS, "TN_NAWS"}, /* Negotiate About Window Size */ + {TN_TERMSP, "TN_TERMSP"}, /* Terminal Speed */ + {TN_TOGFLO, "TN_TOGFLO"}, /* Remote Flow Control */ {TN_LINE, "TN_LINE"}, /* line mode */ - {TN_CR, "TN_CR"}, /* carriage return */ - {TN_LF, "TN_LF"}, /* line feed */ + {TN_XDISPL, "TN_XDISPL"}, /* X Display Location */ + {TN_ENVIRO, "TN_ENVIRO"}, /* Environment */ + {TN_AUTH, "TN_AUTH"}, /* Authentication */ + {TN_ENCRYP, "TN_ENCRYP"}, /* Data Encryption */ + {TN_NEWENV, "TN_NEWENV"}, /* New Environment */ + {TN_TN3270, "TN_TN3270"}, /* TN3270 Enhancements */ + {TN_CHARST, "TN_CHARST"}, /* CHARSET */ + {TN_COMPRT, "TN_COMPRT"}, /* Com Port Control */ + {TN_KERMIT, "TN_KERMIT"}, /* KERMIT */ {0, NULL}}; static char *tmxr_debug_buf = NULL; @@ -3539,17 +3607,81 @@ while (*string) tmxr_buf_debug_char (*string++); } +static void tmxr_buf_debug_telnet_option (u_char chr) +{ +int j; + +for (j=0; 1; ++j) { + if (NULL == tn_chars[j].name) { + if (isprint(chr)) + tmxr_buf_debug_char (chr); + else { + tmxr_buf_debug_char ('_'); + if ((chr >= 1) && (chr <= 26)) { + tmxr_buf_debug_char ('^'); + tmxr_buf_debug_char ('A' + chr - 1); + } + else { + char octal[8]; + + sprintf(octal, "\\%03o", (u_char)chr); + tmxr_buf_debug_string (octal); + } + tmxr_buf_debug_char ('_'); + } + break; + } + if ((u_char)chr == tn_chars[j].value) { + tmxr_buf_debug_char ('_'); + tmxr_buf_debug_string (tn_chars[j].name); + tmxr_buf_debug_char ('_'); + break; + } + } +} + +static int tmxr_buf_debug_telnet_options (char *buf, int bufsize) +{ +int optsize = 2; + +tmxr_buf_debug_telnet_option ((u_char)buf[0]); +tmxr_buf_debug_telnet_option ((u_char)buf[1]); +switch ((u_char)buf[1]) { + case TN_IAC: + default: + return optsize; + break; + case TN_WILL: + case TN_WONT: + case TN_DO: + case TN_DONT: + ++optsize; + tmxr_buf_debug_telnet_option ((u_char)buf[2]); + break; + } +return optsize; +} + void _tmxr_debug (uint32 dbits, TMLN *lp, const char *msg, char *buf, int bufsize) { if ((lp->mp->dptr) && (dbits & lp->mp->dptr->dctrl)) { - int i, j; + int i; tmxr_debug_buf_used = 0; if (tmxr_debug_buf) tmxr_debug_buf[tmxr_debug_buf_used] = '\0'; for (i=0; imp->dptr) && (dbits & lp->mp->dptr->dctrl)) { tmxr_buf_debug_char ('_'); } break; - } - if ((u_char)buf[i] == tn_chars[j].value) { - tmxr_buf_debug_char ('_'); - tmxr_buf_debug_string (tn_chars[j].name); - tmxr_buf_debug_char ('_'); - break; - } } } sim_debug (dbits, lp->mp->dptr, "%s %d bytes '%s'\n", msg, bufsize, tmxr_debug_buf); From 242103e79fad1c90b520696f7f4ac69671d649b1 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Wed, 1 May 2013 16:48:55 -0700 Subject: [PATCH 04/23] Added remote console support when a persistent console log is not enabled. --- sim_console.c | 33 ++++++++++++++++++++++----------- 1 file changed, 22 insertions(+), 11 deletions(-) diff --git a/sim_console.c b/sim_console.c index a151e64f..407f531b 100644 --- a/sim_console.c +++ b/sim_console.c @@ -349,6 +349,8 @@ static char **sim_rem_buf = NULL; static TMXR sim_rem_con_tmxr = { 0, 0, 0, NULL, NULL, &sim_remote_console };/* remote console line mux */ static uint32 sim_rem_read_timeout = 30; /* seconds before automatic continue */ static int32 sim_rem_step_line = -1; /* step in progress on line # */ +static t_bool sim_log_temp = FALSE; /* temporary log file active */ +#define SIM_REMOTE_TEMP_LOG "sim_remote_console.log" /* SET REMOTE CONSOLE command */ @@ -628,7 +630,9 @@ for (i=(was_stepping ? sim_rem_step_line : 0); if (sim_log) fprintf (sim_log, "Remote Console Command from %s> %s\n", lp->ipad, sim_rem_buf[i]); if (strlen(sim_rem_buf[i]) >= sizeof(cbuf)) { - printf ("\r\nLine too long. Ignored. Continuing Simulator execution\r\n"); + printf ("\nLine too long. Ignored. Continuing Simulator execution\n"); + if (sim_log) + fprintf (sim_log, "\r\nLine too long. Ignored. Continuing Simulator execution\r\n"); tmxr_linemsgf (lp, "\nLine too long. Ignored. Continuing Simulator execution\n"); tmxr_send_buffered_data (lp); /* try to flush any buffered data */ break; @@ -642,6 +646,14 @@ for (i=(was_stepping ? sim_rem_step_line : 0); cptr = cbuf; cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */ sim_switches = 0; /* init switches */ + if (!sim_log) { /* Not currently logging? */ + int32 save_quiet = sim_quiet; + + sim_quiet = 0; + sim_set_logon (0, SIM_REMOTE_TEMP_LOG); + sim_quiet = save_quiet; + sim_log_temp = TRUE; + } cmd_log_start = sim_ftell (sim_log); if (!find_cmd (gbuf)) /* validate command */ stat = SCPE_UNK; @@ -696,6 +708,15 @@ for (i=(was_stepping ? sim_rem_step_line : 0); } if (cmdp && (cmdp->action == &x_continue_cmd)) { sim_rem_step_line = -1; /* Not stepping */ + if (sim_log_temp) { /* If we setup a temporary log, clean it now */ + int32 save_quiet = sim_quiet; + + sim_quiet = 0; + sim_set_logoff (0, NULL); + sim_quiet = save_quiet; + remove (SIM_REMOTE_TEMP_LOG); + sim_log_temp = FALSE; + } tmxr_linemsg (lp, "Simulator Running..."); tmxr_send_buffered_data (lp); for (j=0; j < sim_rem_con_tmxr.lines; j++) { @@ -734,10 +755,6 @@ t_stat r; if (flag) { r = sim_parse_addr (cptr, NULL, 0, NULL, NULL, 0, NULL, NULL); if (r == SCPE_OK) { - if (!sim_log) { - printf ("Logging must be enabled to activate Remote Console support\n"); - return SCPE_ARG; - } if (sim_rem_con_tmxr.master) /* already open? */ sim_set_rem_telnet (0, NULL); /* close first */ if (sim_rem_con_tmxr.lines == 0) /* Ir no connection limit set */ @@ -890,12 +907,6 @@ if (cptr && (*cptr != 0)) /* now eol? */ return SCPE_2MARG; if (sim_log == NULL) /* no log? */ return SCPE_OK; -if (sim_rem_con_tmxr.master) { - if (!sim_quiet) - printf ("Can't close log, Remote Console is enabled\n"); - fprintf (sim_log, "Can't close log, Remote Console is enabled\n"); - return SCPE_ARG; - } if (!sim_quiet) printf ("Log file closed\n"); fprintf (sim_log, "Log file closed\n"); From cc8c79eb0f095ba2a476cf033ec209ed6237f1b5 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Thu, 2 May 2013 07:46:07 -0700 Subject: [PATCH 05/23] Added a more unique naming paradigm for remote console temporary log files to avoid potential collisions when multiple simulators may be running concurrently --- scp.c | 2 ++ sim_console.c | 7 ++++--- sim_defs.h | 1 + 3 files changed, 7 insertions(+), 3 deletions(-) diff --git a/scp.c b/scp.c index b7cac770..f76cbe04 100644 --- a/scp.c +++ b/scp.c @@ -3146,6 +3146,8 @@ return SCPE_OK; t_stat set_default_cmd (int32 flg, char *cptr) { +if (sim_is_running) + return SCPE_INVREM; if ((!cptr) || (*cptr == 0)) return SCPE_2FARG; sim_trim_endspc(cptr); diff --git a/sim_console.c b/sim_console.c index 407f531b..d5ceb23e 100644 --- a/sim_console.c +++ b/sim_console.c @@ -350,7 +350,7 @@ static TMXR sim_rem_con_tmxr = { 0, 0, 0, NULL, NULL, &sim_remote_console };/* r static uint32 sim_rem_read_timeout = 30; /* seconds before automatic continue */ static int32 sim_rem_step_line = -1; /* step in progress on line # */ static t_bool sim_log_temp = FALSE; /* temporary log file active */ -#define SIM_REMOTE_TEMP_LOG "sim_remote_console.log" +static char sim_rem_con_temp_name[PATH_MAX+1]; /* SET REMOTE CONSOLE command */ @@ -650,7 +650,8 @@ for (i=(was_stepping ? sim_rem_step_line : 0); int32 save_quiet = sim_quiet; sim_quiet = 0; - sim_set_logon (0, SIM_REMOTE_TEMP_LOG); + sprintf (sim_rem_con_temp_name, "sim_remote_console_%d.temporary_log", (int)getpid()); + sim_set_logon (0, sim_rem_con_temp_name); sim_quiet = save_quiet; sim_log_temp = TRUE; } @@ -714,7 +715,7 @@ for (i=(was_stepping ? sim_rem_step_line : 0); sim_quiet = 0; sim_set_logoff (0, NULL); sim_quiet = save_quiet; - remove (SIM_REMOTE_TEMP_LOG); + remove (sim_rem_con_temp_name); sim_log_temp = FALSE; } tmxr_linemsg (lp, "Simulator Running..."); diff --git a/sim_defs.h b/sim_defs.h index 2ee9229a..38539103 100644 --- a/sim_defs.h +++ b/sim_defs.h @@ -118,6 +118,7 @@ #undef PACKED /* avoid macro name collision */ #undef ERROR /* avoid macro name collision */ #undef MEM_MAPPED /* avoid macro name collision */ +#include #endif /* avoid macro names collisions */ From b30211c2a4d08c8b6cb30e3b59295ea10bfda2b5 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Thu, 2 May 2013 09:27:02 -0700 Subject: [PATCH 06/23] Added documentation for the Remote Console capabilities --- doc/simh_doc.doc | Bin 192000 -> 192512 bytes 1 file changed, 0 insertions(+), 0 deletions(-) diff --git a/doc/simh_doc.doc b/doc/simh_doc.doc index 1cd7c44315c20d4aa52bcf0e738353613b111149..480365e79f339b327cf3a8df338a07d7b9ee37ef 100644 GIT binary patch delta 34275 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 Date: Sat, 4 May 2013 12:22:50 -0700 Subject: [PATCH 07/23] Fixed compile and operational behaviors when running with SIM_ASYNCH_CLOCKS enabled --- sim_defs.h | 53 +++++++++++++++++++++++++++++++++++++++++++++++++++++ sim_timer.c | 28 ++++++++++++++++++---------- 2 files changed, 71 insertions(+), 10 deletions(-) diff --git a/sim_defs.h b/sim_defs.h index 38539103..ca63e5f5 100644 --- a/sim_defs.h +++ b/sim_defs.h @@ -763,6 +763,57 @@ extern int32 sim_asynch_inst_latency; else \ (void)0 #endif /* !defined(SIM_ASYNCH_MUX) && !defined(SIM_ASYNCH_CLOCKS) */ +#if !defined(SIM_ASYNCH_MUX) && defined(SIM_ASYNCH_CLOCKS) +#define AIO_CANCEL(uptr) \ + if ((uptr)->a_cancel) \ + (uptr)->a_cancel (uptr); \ + else { \ + if (AIO_IS_ACTIVE (uptr)) { \ + UNIT *cptr, *nptr; \ + AIO_UPDATE_QUEUE; \ + pthread_mutex_lock (&sim_timer_lock); \ + nptr = QUEUE_LIST_END; \ + if ((uptr) == sim_wallclock_queue) { \ + sim_wallclock_queue = (uptr)->next; \ + (uptr)->next = NULL; \ + } \ + else \ + for (cptr = sim_wallclock_queue; \ + (cptr != QUEUE_LIST_END); \ + cptr = cptr->next) \ + if (cptr->next == (uptr)) { \ + cptr->next = (uptr)->next; \ + nptr = cptr; \ + (uptr)->next = NULL; \ + break; \ + } \ + if (nptr == QUEUE_LIST_END) { \ + sim_timer_event_canceled = TRUE; \ + pthread_cond_signal (&sim_timer_wake); \ + } \ + if ((uptr)->next == NULL) \ + (uptr)->a_due_time = (uptr)->a_usec_delay = 0; \ + else { \ + nptr = QUEUE_LIST_END; \ + if ((uptr) == sim_clock_cosched_queue) { \ + sim_clock_cosched_queue = (uptr)->next; \ + (uptr)->next = NULL; \ + } \ + else \ + for (cptr = sim_clock_cosched_queue; \ + (cptr != QUEUE_LIST_END); \ + cptr = cptr->next) \ + if (cptr->next == (uptr)) { \ + cptr->next = (uptr)->next; \ + nptr = cptr; \ + (uptr)->next = NULL; \ + break; \ + } \ + } \ + pthread_mutex_unlock (&sim_timer_lock); \ + } \ + } +#endif #if defined(SIM_ASYNCH_MUX) && !defined(SIM_ASYNCH_CLOCKS) #define AIO_CANCEL(uptr) \ if ((uptr)->a_cancel) \ @@ -832,6 +883,8 @@ extern int32 sim_asynch_inst_latency; pthread_mutex_unlock (&sim_timer_lock); \ } \ } +#endif +#if defined(SIM_ASYNCH_CLOCKS) #define AIO_RETURN_TIME(uptr) \ if (1) { \ pthread_mutex_lock (&sim_timer_lock); \ diff --git a/sim_timer.c b/sim_timer.c index eca1b741..bddf7911 100644 --- a/sim_timer.c +++ b/sim_timer.c @@ -1427,8 +1427,8 @@ if (1) { } pthread_mutex_lock (&sim_timer_lock); sim_wallclock_entry = uptr; -pthread_mutex_unlock (&sim_timer_lock); pthread_cond_signal (&sim_timer_wake); /* wake the timer thread to deal with it */ +pthread_mutex_unlock (&sim_timer_lock); return SCPE_OK; #else return _sim_activate (uptr, inst_delay); /* queue it now */ @@ -1451,18 +1451,26 @@ else if (sim_asynch_enabled && sim_asynch_timer) { if (!sim_is_active (uptr)) { /* already active? */ #if defined(SIM_ASYNCH_IO) && defined(SIM_ASYNCH_CLOCKS) - sim_debug (DBG_TIM, &sim_timer_dev, "sim_clock_coschedule() - queueing %s for clock co-schedule\n", sim_uname (uptr)); - pthread_mutex_lock (&sim_timer_lock); - uptr->next = sim_clock_cosched_queue; - sim_clock_cosched_queue = uptr; - pthread_mutex_unlock (&sim_timer_lock); + if ((sim_calb_tmr != -1) && + (rtc_elapsed[sim_calb_tmr ] >= sim_idle_stable)) { + sim_debug (DBG_TIM, &sim_timer_dev, "sim_clock_coschedule() - queueing %s for clock co-schedule\n", sim_uname (uptr)); + pthread_mutex_lock (&sim_timer_lock); + uptr->next = sim_clock_cosched_queue; + sim_clock_cosched_queue = uptr; + pthread_mutex_unlock (&sim_timer_lock); + return SCPE_OK; + } + else { #else - int32 t; - - t = sim_activate_time (sim_clock_unit); - return sim_activate (uptr, t? t - 1: interval); + if (1) { #endif + int32 t; + + t = sim_activate_time (sim_clock_unit); + return sim_activate (uptr, t? t - 1: interval); + } } + sim_debug (DBG_TIM, &sim_timer_dev, "sim_clock_coschedule() - %s is already active\n", sim_uname (uptr)); return SCPE_OK; } else { From 9faef6ea89fcc01291cc32e0de59bef7054cacf8 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Mon, 6 May 2013 07:50:09 -0700 Subject: [PATCH 08/23] Added separate debugging ability to trace line connect/disconnect activities to better debug virtual null modem cable activities. --- PDP11/pdp11_dz.c | 2 ++ PDP11/pdp11_vh.c | 4 ++++ sim_sock.c | 4 ++++ sim_tmxr.c | 43 ++++++++++++++++++++++++++++++++++--------- sim_tmxr.h | 7 +++++-- 5 files changed, 49 insertions(+), 11 deletions(-) diff --git a/PDP11/pdp11_dz.c b/PDP11/pdp11_dz.c index efeb5998..e09aa377 100644 --- a/PDP11/pdp11_dz.c +++ b/PDP11/pdp11_dz.c @@ -242,6 +242,7 @@ TMXR dz_desc = { DZ_MUXES * DZ_LINES, 0, 0, NULL }; /* mux descriptor */ #define DBG_XMT TMXR_DBG_XMT /* display Transmitted Data */ #define DBG_RCV TMXR_DBG_RCV /* display Received Data */ #define DBG_MDM TMXR_DBG_MDM /* display Modem Signals */ +#define DBG_CON TMXR_DBG_CON /* display connection activities */ #define DBG_TRC TMXR_DBG_TRC /* display trace routine calls */ #define DBG_ASY TMXR_DBG_ASY /* display Asynchronous Activities */ @@ -251,6 +252,7 @@ DEBTAB dz_debug[] = { {"XMT", DBG_XMT}, {"RCV", DBG_RCV}, {"MDM", DBG_MDM}, + {"CON", DBG_CON}, {"TRC", DBG_TRC}, {"ASY", DBG_ASY}, {0} diff --git a/PDP11/pdp11_vh.c b/PDP11/pdp11_vh.c index 13e30131..b2172987 100644 --- a/PDP11/pdp11_vh.c +++ b/PDP11/pdp11_vh.c @@ -305,6 +305,8 @@ static TMLX vh_parm[VH_MUXES * VH_LINES_ALLOC] = { { 0 } }; #define DBG_TRC TMXR_DBG_TRC /* trace routine calls */ #define DBG_XMT TMXR_DBG_XMT /* display Transmitted Data */ #define DBG_RCV TMXR_DBG_RCV /* display Received Data */ +#define DBG_MDM TMXR_DBG_MDM /* display Modem Signals */ +#define DBG_CON TMXR_DBG_CON /* display connection activities */ #define DBG_TRC TMXR_DBG_TRC /* display trace routine calls */ #define DBG_ASY TMXR_DBG_ASY /* display Asynchronous Activities */ @@ -314,6 +316,8 @@ DEBTAB vh_debug[] = { {"TRC", DBG_TRC}, {"XMT", DBG_XMT}, {"RCV", DBG_RCV}, + {"MDM", DBG_MDM}, + {"CON", DBG_CON}, {"TRC", DBG_TRC}, {"ASY", DBG_ASY}, {0} diff --git a/sim_sock.c b/sim_sock.c index 111e29c3..5f3b78ce 100644 --- a/sim_sock.c +++ b/sim_sock.c @@ -939,6 +939,10 @@ if (rbytes == SOCKET_ERROR) { err = WSAGetLastError (); if (err == WSAEWOULDBLOCK) /* no data */ return 0; +#if defined(EAGAIN) + if (err == EAGAIN) /* no data */ + return 0; +#endif if ((err != WSAETIMEDOUT) && /* expected errors after a connect failure */ (err != WSAEHOSTUNREACH) && (err != WSAECONNREFUSED)) diff --git a/sim_tmxr.c b/sim_tmxr.c index 90ddcff6..27fa6acb 100644 --- a/sim_tmxr.c +++ b/sim_tmxr.c @@ -816,7 +816,7 @@ if (mp->master) { if (newsock != INVALID_SOCKET) { /* got a live one? */ sprintf (msg, "tmxr_poll_conn() - Connection from %s", address); - tmxr_debug_trace (mp, msg); + tmxr_debug_connect (mp, msg); op = mp->lnorder; /* get line connection order list pointer */ i = mp->lines; /* play it safe in case lines == 0 */ ++mp->sessions; /* count the new session */ @@ -836,7 +836,7 @@ if (mp->master) { if (i >= mp->lines) { /* all busy? */ tmxr_msg (newsock, "All connections busy\r\n"); - tmxr_debug_trace (mp, "tmxr_poll_conn() - All connections busy"); + tmxr_debug_connect (mp, "tmxr_poll_conn() - All connections busy"); sim_close_sock (newsock, 0); free (address); } @@ -872,8 +872,12 @@ for (i = 0; i < mp->lines; i++) { /* check each line in se lp->ipad = realloc (lp->ipad, 1+strlen (lp->destination)); strcpy (lp->ipad, lp->destination); lp->cnms = sim_os_msec (); + sprintf (msg, "tmxr_poll_conn() - Line Connection to %s established", lp->destination); + tmxr_debug_connect_line (lp, msg); break; case -1: /* failed connection */ + sprintf (msg, "tmxr_poll_conn() - Line Connection to %s failed", lp->destination); + tmxr_debug_connect_line (lp, msg); tmxr_reset_ln (lp); /* retry */ break; } @@ -887,7 +891,7 @@ for (i = 0; i < mp->lines; i++) { /* check each line in se if (newsock != INVALID_SOCKET) { /* got a live one? */ sprintf (msg, "tmxr_poll_conn() - Line Connection from %s", address); - tmxr_debug_trace_line (lp, msg); + tmxr_debug_connect_line (lp, msg); ++mp->sessions; /* count the new session */ if (lp->destination) { /* Virtual Null Modem Cable? */ @@ -896,12 +900,14 @@ for (i = 0; i < mp->lines; i++) { /* check each line in se if (sim_parse_addr (lp->destination, host, sizeof(host), NULL, NULL, 0, NULL, address)) { tmxr_msg (newsock, "Rejecting connection from unexpected source\r\n"); sprintf (msg, "tmxr_poll_conn() - Rejecting line connection from: %s, Expected: %s", address, host); - tmxr_debug_trace_line (lp, msg); + tmxr_debug_connect_line (lp, msg); sim_close_sock (newsock, 0); free (address); continue; /* Move on to next line */ } if (lp->connecting) { + sprintf (msg, "tmxr_poll_conn() - aborting outgoing line connection attempt to: %s", lp->destination); + tmxr_debug_connect_line (lp, msg); sim_close_sock (lp->connecting, 0); /* abort our as yet unconnnected socket */ lp->connecting = 0; } @@ -921,7 +927,7 @@ for (i = 0; i < mp->lines; i++) { /* check each line in se } else { tmxr_msg (newsock, "Line connection busy\r\n"); - tmxr_debug_trace_line (lp, "tmxr_poll_conn() - Line connection busy"); + tmxr_debug_connect_line (lp, "tmxr_poll_conn() - Line connection busy"); sim_close_sock (newsock, 0); free (address); } @@ -939,8 +945,11 @@ for (i = 0; i < mp->lines; i++) { /* check each line in se /* Check for needed outgoing connection initiation */ if (lp->destination && (!lp->sock) && (!lp->connecting) && (!lp->serport) && - (!mp->modem_control || (lp->modembits & TMXR_MDM_DTR))) + (!mp->modem_control || (lp->modembits & TMXR_MDM_DTR))) { + sprintf (msg, "tmxr_poll_conn() - establishing outgoing connection to: %s", lp->destination); + tmxr_debug_connect_line (lp, msg); lp->connecting = sim_connect_sock (lp->destination, "localhost", NULL); + } } @@ -958,6 +967,8 @@ return -1; /* no new connections ma static t_stat tmxr_reset_ln_ex (TMLN *lp, t_bool closeserial) { +char msg[512]; + tmxr_debug_trace_line (lp, "tmxr_reset_ln_ex)"); if (lp->txlog) @@ -965,6 +976,9 @@ if (lp->txlog) tmxr_send_buffered_data (lp); /* send any buffered data */ +sprintf (msg, "tmxr_reset_ln_ex(%s)", closeserial ? "TRUE" : "FALSE"); +tmxr_debug_connect_line (lp, msg); + if (lp->serport) { if (closeserial) { sim_close_serial (lp->serport); @@ -995,10 +1009,15 @@ else /* Telnet connection */ free(lp->ipad); lp->ipad = NULL; if ((lp->destination) && (!lp->serport)) { - if (lp->connecting) + if (lp->connecting) { sim_close_sock (lp->connecting, 0); - if ((!lp->mp->modem_control) || (lp->modembits & TMXR_MDM_DTR)) + lp->connecting = 0; + } + if ((!lp->mp->modem_control) || (lp->modembits & TMXR_MDM_DTR)) { + sprintf (msg, "tmxr_reset_ln_ex() - connecting to %s", lp->destination); + tmxr_debug_connect_line (lp, msg); lp->connecting = sim_connect_sock (lp->destination, "localhost", NULL); + } } tmxr_init_line (lp); /* initialize line state */ /* Revise the unit's connect string to reflect the current attachments */ @@ -1012,6 +1031,7 @@ return SCPE_OK; t_stat tmxr_close_ln (TMLN *lp) { tmxr_debug_trace_line (lp, "tmxr_close_ln()"); +tmxr_debug_connect_line (lp, "tmxr_close_ln()"); return tmxr_reset_ln_ex (lp, TRUE); } @@ -1144,8 +1164,13 @@ if (lp->mp && lp->mp->modem_control) { /* This API ONLY works on mo else { if ((lp->destination) && /* Virtual Null Modem Cable */ ((bits_to_set ^ before_modem_bits) & /* and DTR being Raised */ - TMXR_MDM_DTR)) + TMXR_MDM_DTR)) { + char msg[512]; + + sprintf (msg, "tmxr_set_get_modem_bits() - establishing outgoing connection to: %s", lp->destination); + tmxr_debug_connect_line (lp, msg); lp->connecting = sim_connect_sock (lp->destination, "localhost", NULL); + } } } return SCPE_OK; diff --git a/sim_tmxr.h b/sim_tmxr.h index 147234fc..d1c26a08 100644 --- a/sim_tmxr.h +++ b/sim_tmxr.h @@ -69,8 +69,9 @@ typedef int SERHANDLE; #define TMXR_DBG_XMT 0x010000 /* Debug Transmit Data */ #define TMXR_DBG_RCV 0x020000 /* Debug Received Data */ #define TMXR_DBG_MDM 0x040000 /* Debug Modem Signals */ -#define TMXR_DBG_ASY 0x080000 /* Debug Asynchronous Activities */ -#define TMXR_DBG_TRC 0x100000 /* Debug trace routine calls */ +#define TMXR_DBG_CON 0x080000 /* Debug Connection Activities */ +#define TMXR_DBG_ASY 0x100000 /* Debug Asynchronous Activities */ +#define TMXR_DBG_TRC 0x200000 /* Debug trace routine calls */ /* Modem Control Bits */ @@ -202,6 +203,8 @@ extern FILE *sim_deb; /* debug file */ #define tmxr_debug(dbits, lp, msg, buf, bufsize) if (sim_deb && (lp)->mp->dptr && ((dbits) & (lp)->mp->dptr->dctrl)) _tmxr_debug (dbits, lp, msg, buf, bufsize); else (void)0 #define tmxr_debug_trace(mp, msg) if (sim_deb && (mp)->dptr && (TMXR_DBG_TRC & (mp)->dptr->dctrl)) sim_debug (TMXR_DBG_TRC, mp->dptr, "%s\n", (msg)); else (void)0 #define tmxr_debug_trace_line(lp, msg) if (sim_deb && (lp)->mp && (lp)->mp->dptr && (TMXR_DBG_TRC & (lp)->mp->dptr->dctrl)) sim_debug (TMXR_DBG_TRC, (lp)->mp->dptr, "%s\n", (msg)); else (void)0 +#define tmxr_debug_connect(mp, msg) if (sim_deb && (mp)->dptr && (TMXR_DBG_CON & (mp)->dptr->dctrl)) sim_debug (TMXR_DBG_CON, mp->dptr, "%s\n", (msg)); else (void)0 +#define tmxr_debug_connect_line(lp, msg) if (sim_deb && (lp)->mp && (lp)->mp->dptr && (TMXR_DBG_CON & (lp)->mp->dptr->dctrl)) sim_debug (TMXR_DBG_CON, (lp)->mp->dptr, "%s\n", (msg)); else (void)0 #if defined(SIM_ASYNCH_IO) && defined(SIM_ASYNCH_MUX) #define tmxr_attach(mp, uptr, cptr) tmxr_attach_ex(mp, uptr, cptr, TRUE) From 6cf54e83414c1cf0e58fd8c423ec7de001aa0220 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Tue, 7 May 2013 11:22:29 -0700 Subject: [PATCH 09/23] Fixes for stable operation with SIM_ASYNCH_CLOCKS defined --- VAX/vax_sysdev.c | 4 +- scp.c | 28 +++++++--- sim_console.c | 2 + sim_defs.h | 140 ++++++++++++++++++++++++++++++++++++++--------- sim_timer.c | 40 +++++++++----- sim_timer.h | 1 + 6 files changed, 167 insertions(+), 48 deletions(-) diff --git a/VAX/vax_sysdev.c b/VAX/vax_sysdev.c index dda6a00b..98dbdbef 100644 --- a/VAX/vax_sysdev.c +++ b/VAX/vax_sysdev.c @@ -1580,8 +1580,10 @@ if ((tmr_inc[tmr] == TMR_INC) && (tmr_time > clk_time)) { tmr_inc[tmr] = (uint32) (((double) clk_time * TMR_INC) / tmr_poll); tmr_time = clk_time; + sim_clock_coschedule (&sysd_unit[tmr], tmr_time); } -sim_activate (&sysd_unit[tmr], tmr_time); +else + sim_activate (&sysd_unit[tmr], tmr_time); return; } diff --git a/scp.c b/scp.c index f76cbe04..9d49196c 100644 --- a/scp.c +++ b/scp.c @@ -2934,8 +2934,8 @@ else { } } if (sim_clock_cosched_queue != QUEUE_LIST_END) { - fprintf (st, "%s clock co-schedule event queue status, time = %.0f\n", - sim_name, sim_time); + fprintf (st, "%s clock (%s) co-schedule event queue status, time = %.0f\n", + sim_name, sim_uname(sim_clock_unit), sim_time); for (uptr = sim_clock_cosched_queue; uptr != QUEUE_LIST_END; uptr = uptr->next) { if ((dptr = find_dev_from_unit (uptr)) != NULL) { fprintf (st, " %s", sim_dname (dptr)); @@ -3934,6 +3934,8 @@ char *sim_uname (UNIT *uptr) DEVICE *d = find_dev_from_unit(uptr); static AIO_TLS char uname[CBUFSIZE]; +if (!d) + return ""; if (d->numunits == 1) return sim_dname (d); sprintf (uname, "%s%d", sim_dname (d), (int)(uptr-d->units)); @@ -6336,28 +6338,39 @@ AIO_CANCEL(uptr); AIO_UPDATE_QUEUE; if (sim_clock_queue == QUEUE_LIST_END) return SCPE_OK; +sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Event for %s\n", sim_uname(uptr)); UPDATE_SIM_TIME; /* update sim time */ if (!sim_is_active (uptr)) return SCPE_OK; nptr = QUEUE_LIST_END; -if (sim_clock_queue == uptr) +if (sim_clock_queue == uptr) { nptr = sim_clock_queue = uptr->next; + uptr->next = NULL; /* hygiene */ + } else { for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) { if (cptr->next == uptr) { nptr = cptr->next = uptr->next; + uptr->next = NULL; /* hygiene */ break; /* end queue scan */ } } } if (nptr != QUEUE_LIST_END) - nptr->time = nptr->time + uptr->time; -uptr->next = NULL; /* hygiene */ -uptr->time = 0; + nptr->time += (uptr->next) ? 0 : uptr->time; +if (!uptr->next) + uptr->time = 0; if (sim_clock_queue != QUEUE_LIST_END) sim_interval = sim_clock_queue->time; else sim_interval = noqueue_time = NOQUEUE_WAIT; +if (sim_is_active(uptr)) { + if (sim_deb) { + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Cancel failed for %s\n", sim_uname(uptr)); + fclose(sim_deb); + } + abort (); + } return SCPE_OK; } @@ -6413,7 +6426,8 @@ return 0; double sim_gtime (void) { -UPDATE_SIM_TIME; +if (AIO_MAIN_THREAD) + UPDATE_SIM_TIME; return sim_time; } diff --git a/sim_console.c b/sim_console.c index d5ceb23e..c0bc8d43 100644 --- a/sim_console.c +++ b/sim_console.c @@ -541,6 +541,7 @@ for (i=(was_stepping ? sim_rem_step_line : 0); c = c & ~TMXR_VALID; if (c != sim_int_char) continue; /* ^E (the interrupt character) must start console interaction */ + sim_stop_timer_services (); for (j=0; j < sim_rem_con_tmxr.lines; j++) { lp = &sim_rem_con_tmxr.ldsc[j]; if ((i == j) || (!lp->conn)) @@ -727,6 +728,7 @@ for (i=(was_stepping ? sim_rem_step_line : 0); tmxr_linemsg (lp, "Simulator Running..."); tmxr_send_buffered_data (lp); } + sim_start_timer_services (); break; } if (cmdp && (cmdp->action == &x_step_cmd)) { diff --git a/sim_defs.h b/sim_defs.h index ca63e5f5..6e66bbb2 100644 --- a/sim_defs.h +++ b/sim_defs.h @@ -749,7 +749,24 @@ extern int32 sim_asynch_inst_latency; /* It is primarily used only used in debugging messages */ #define AIO_TLS #endif - +#define AIO_QUEUE_CHECK(que, lock) \ + if (1) { \ + UNIT *_cptr; \ + if (lock) \ + pthread_mutex_lock (lock); \ + for (_cptr = que; \ + (_cptr != QUEUE_LIST_END); \ + _cptr = _cptr->next) \ + if (!_cptr->next) { \ + if (sim_deb) { \ + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Queue Corruption detected\n");\ + fclose(sim_deb); \ + } \ + abort(); \ + } \ + if (lock) \ + pthread_mutex_unlock (lock); \ + } else (void)0 #define AIO_MAIN_THREAD (pthread_equal ( pthread_self(), sim_asynch_main_threadid )) #define AIO_LOCK \ pthread_mutex_lock(&sim_asynch_lock) @@ -768,14 +785,16 @@ extern int32 sim_asynch_inst_latency; if ((uptr)->a_cancel) \ (uptr)->a_cancel (uptr); \ else { \ - if (AIO_IS_ACTIVE (uptr)) { \ - UNIT *cptr, *nptr; \ + if ((uptr)->next) { \ + UNIT *cptr; \ AIO_UPDATE_QUEUE; \ pthread_mutex_lock (&sim_timer_lock); \ - nptr = QUEUE_LIST_END; \ if ((uptr) == sim_wallclock_queue) { \ sim_wallclock_queue = (uptr)->next; \ (uptr)->next = NULL; \ + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Timer Event for %s\n", sim_uname(uptr));\ + sim_timer_event_canceled = TRUE; \ + pthread_cond_signal (&sim_timer_wake); \ } \ else \ for (cptr = sim_wallclock_queue; \ @@ -783,18 +802,13 @@ extern int32 sim_asynch_inst_latency; cptr = cptr->next) \ if (cptr->next == (uptr)) { \ cptr->next = (uptr)->next; \ - nptr = cptr; \ (uptr)->next = NULL; \ + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Timer Event for %s\n", sim_uname(uptr));\ break; \ } \ - if (nptr == QUEUE_LIST_END) { \ - sim_timer_event_canceled = TRUE; \ - pthread_cond_signal (&sim_timer_wake); \ - } \ if ((uptr)->next == NULL) \ (uptr)->a_due_time = (uptr)->a_usec_delay = 0; \ else { \ - nptr = QUEUE_LIST_END; \ if ((uptr) == sim_clock_cosched_queue) { \ sim_clock_cosched_queue = (uptr)->next; \ (uptr)->next = NULL; \ @@ -805,10 +819,18 @@ extern int32 sim_asynch_inst_latency; cptr = cptr->next) \ if (cptr->next == (uptr)) { \ cptr->next = (uptr)->next; \ - nptr = cptr; \ (uptr)->next = NULL; \ break; \ } \ + if ((uptr)->next == NULL) { \ + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Clock Coscheduling Event for %s\n", sim_uname(uptr));\ + } \ + } \ + while (sim_timer_event_canceled) { \ + pthread_mutex_unlock (&sim_timer_lock); \ + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Waiting for Timer Event cancelation for %s\n", sim_uname(uptr));\ + sim_os_ms_sleep (0); \ + pthread_mutex_lock (&sim_timer_lock); \ } \ pthread_mutex_unlock (&sim_timer_lock); \ } \ @@ -838,14 +860,16 @@ extern int32 sim_asynch_inst_latency; sim_tmxr_poll_count -= (uptr)->a_poll_waiter_count; \ (uptr)->a_poll_waiter_count = 0; \ } \ - if (AIO_IS_ACTIVE (uptr)) { \ - UNIT *cptr, *nptr; \ + if ((uptr)->next) { \ + UNIT *cptr; \ AIO_UPDATE_QUEUE; \ pthread_mutex_lock (&sim_timer_lock); \ - nptr = QUEUE_LIST_END; \ if ((uptr) == sim_wallclock_queue) { \ sim_wallclock_queue = (uptr)->next; \ (uptr)->next = NULL; \ + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Timer Event for %s\n", sim_uname(uptr));\ + sim_timer_event_canceled = TRUE; \ + pthread_cond_signal (&sim_timer_wake); \ } \ else \ for (cptr = sim_wallclock_queue; \ @@ -853,18 +877,13 @@ extern int32 sim_asynch_inst_latency; cptr = cptr->next) \ if (cptr->next == (uptr)) { \ cptr->next = (uptr)->next; \ - nptr = cptr; \ (uptr)->next = NULL; \ + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Timer Event for %s\n", sim_uname(uptr));\ break; \ } \ - if (nptr == QUEUE_LIST_END) { \ - sim_timer_event_canceled = TRUE; \ - pthread_cond_signal (&sim_timer_wake); \ - } \ if ((uptr)->next == NULL) \ (uptr)->a_due_time = (uptr)->a_usec_delay = 0; \ else { \ - nptr = QUEUE_LIST_END; \ if ((uptr) == sim_clock_cosched_queue) { \ sim_clock_cosched_queue = (uptr)->next; \ (uptr)->next = NULL; \ @@ -875,10 +894,18 @@ extern int32 sim_asynch_inst_latency; cptr = cptr->next) \ if (cptr->next == (uptr)) { \ cptr->next = (uptr)->next; \ - nptr = cptr; \ (uptr)->next = NULL; \ break; \ } \ + if ((uptr)->next == NULL) { \ + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Clock Coscheduling Event for %s\n", sim_uname(uptr));\ + } \ + } \ + while (sim_timer_event_canceled) { \ + pthread_mutex_unlock (&sim_timer_lock); \ + sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Waiting for Timer Event cancelation for %s\n", sim_uname(uptr));\ + sim_os_ms_sleep (0); \ + pthread_mutex_lock (&sim_timer_lock); \ } \ pthread_mutex_unlock (&sim_timer_lock); \ } \ @@ -988,8 +1015,8 @@ extern int32 sim_asynch_inst_latency; do \ q = AIO_QUEUE_VAL; \ while (q != AIO_QUEUE_SET(QUEUE_LIST_END, q)); \ - sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Found Asynch event for %s after %d instructions\n", sim_uname(q), q->a_event_time);\ while (q != QUEUE_LIST_END) { /* List !Empty */ \ + sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Migrating Asynch event for %s after %d instructions\n", sim_uname(q), q->a_event_time);\ uptr = q; \ q = q->a_next; \ uptr->a_next = NULL; /* hygiene */ \ @@ -1014,8 +1041,8 @@ extern int32 sim_asynch_inst_latency; } else { \ UNIT *q, *qe; \ ouptr->a_event_time = event_time; \ - uptr->a_activate_call = caller; \ - uptr->a_next = QUEUE_LIST_END; /* Mark as on list */ \ + ouptr->a_activate_call = caller; \ + ouptr->a_next = QUEUE_LIST_END; /* Mark as on list */ \ do { \ do \ q = AIO_QUEUE_VAL; \ @@ -1035,6 +1062,39 @@ extern int32 sim_asynch_inst_latency; } \ return SCPE_OK; \ } else (void)0 +#define AIO_ACTIVATE_LIST(caller, list, event_time) \ + if (1) { \ + UNIT *ouptr, *q, *qe; \ + sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Queueing Asynch events for %s after %d instructions\n", sim_uname(list), event_time);\ + for (qe=(list); qe->next != QUEUE_LIST_END;) { \ + qe->a_event_time = event_time; \ + qe->a_activate_call = caller; \ + qe->a_next = qe->next; \ + qe->next = NULL; \ + qe = qe->a_next; \ + } \ + qe->a_event_time = event_time; \ + qe->a_activate_call = caller; \ + qe->a_next = QUEUE_LIST_END; \ + qe->next = NULL; \ + ouptr = (list); \ + do { \ + do \ + q = AIO_QUEUE_VAL; \ + while (q != AIO_QUEUE_SET(QUEUE_LIST_END, q));/* Grab current list */ \ + for (qe = ouptr; qe->a_next != QUEUE_LIST_END; qe = qe->a_next); \ + qe->a_next = q; /* append current list */ \ + do \ + q = AIO_QUEUE_VAL; \ + while (q != AIO_QUEUE_SET(ouptr, q)); \ + ouptr = q; \ + } while (ouptr != QUEUE_LIST_END); \ + sim_asynch_check = 0; /* try to force check */ \ + if (sim_idle_wait) { \ + sim_debug (TIMER_DBG_IDLE, &sim_timer_dev, "waking due to event on %s after %d instructions\n", sim_uname(ouptr), event_time);\ + pthread_cond_signal (&sim_asynch_wake); \ + } \ + } else (void)0 #else /* !USE_AIO_INTRINSICS */ /* This approach uses a pthread mutex to manage access to the link list */ /* head sim_asynch_queue. It will always work, but may be slower than the */ @@ -1098,8 +1158,8 @@ extern int32 sim_asynch_inst_latency; } else (void)0 #define AIO_ACTIVATE(caller, uptr, event_time) \ if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) { \ - sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "queueing asynch event for %s after %d instructions\n", sim_uname(uptr), event_time);\ - AIO_UNLOCK; \ + sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Queueing Asynch event for %s after %d instructions\n", sim_uname(uptr), event_time);\ + AIO_LOCK; \ if (uptr->a_next) { /* already queued? */ \ uptr->a_activate_call = sim_activate_abs; \ } else { \ @@ -1108,12 +1168,38 @@ extern int32 sim_asynch_inst_latency; uptr->a_activate_call = caller; \ sim_asynch_queue = uptr; \ } \ - if (sim_idle_wait) \ + if (sim_idle_wait) { \ + sim_debug (TIMER_DBG_IDLE, &sim_timer_dev, "waking due to event on %s after %d instructions\n", sim_uname(uptr), event_time);\ pthread_cond_signal (&sim_asynch_wake); \ + } \ AIO_UNLOCK; \ sim_asynch_check = 0; \ return SCPE_OK; \ } else (void)0 +#define AIO_ACTIVATE_LIST(caller, list, event_time) \ + if (1) { \ + UNIT *qe; \ + sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Queueing Asynch events for %s after %d instructions\n", sim_uname(list), event_time);\ + for (qe=list; qe->next != QUEUE_LIST_END;) { \ + qe->a_event_time = event_time; \ + qe->a_activate_call = caller; \ + qe->a_next = qe->next; \ + qe->next = NULL; \ + qe = qe->a_next; \ + } \ + qe->a_event_time = event_time; \ + qe->a_activate_call = caller; \ + qe->next = NULL; \ + AIO_LOCK; \ + qe->a_next = sim_asynch_queue; \ + sim_asynch_queue = list; \ + sim_asynch_check = 0; /* try to force check */ \ + if (sim_idle_wait) { \ + sim_debug (TIMER_DBG_IDLE, &sim_timer_dev, "waking due to event on %s after %d instructions\n", sim_uname(list), event_time);\ + pthread_cond_signal (&sim_asynch_wake); \ + } \ + AIO_UNLOCK; \ + } else (void)0 #endif /* USE_AIO_INTRINSICS */ #define AIO_VALIDATE if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) abort() #define AIO_CHECK_EVENT \ diff --git a/sim_timer.c b/sim_timer.c index bddf7911..f5b0f9bb 100644 --- a/sim_timer.c +++ b/sim_timer.c @@ -101,7 +101,7 @@ static uint32 sim_throt_val = 0; static uint32 sim_throt_state = 0; static uint32 sim_throt_sleep_time = 0; static int32 sim_throt_wait = 0; -static UNIT *sim_clock_unit = NULL; +UNIT *sim_clock_unit = NULL; t_bool sim_asynch_timer = #if defined (SIM_ASYNCH_CLOCKS) TRUE; @@ -1216,7 +1216,7 @@ while (sim_asynch_enabled && sim_asynch_timer && sim_is_running) { if (sim_wallclock_queue == QUEUE_LIST_END) sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - waiting forever\n"); else - sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - waiting for %.0f usecs until %.6f\n", wait_usec, sim_wallclock_queue->a_due_time); + sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - waiting for %.0f usecs until %.6f for %s\n", wait_usec, sim_wallclock_queue->a_due_time, sim_uname(sim_wallclock_queue)); if ((wait_usec <= 0.0) || (0 != pthread_cond_timedwait (&sim_timer_wake, &sim_timer_lock, &due_time))) { if (sim_wallclock_queue == QUEUE_LIST_END) /* queue empty? */ @@ -1238,21 +1238,28 @@ while (sim_asynch_enabled && sim_asynch_timer && sim_is_running) { } sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - slept %.0fms - activating(%s,%d)\n", 1000.0*(_timespec_to_double (&stop_time)-_timespec_to_double (&start_time)), sim_uname(uptr), inst_delay); - sim_activate (uptr, inst_delay); - if (sim_clock_unit == uptr) - while (sim_clock_cosched_queue != QUEUE_LIST_END) { - uptr = sim_clock_cosched_queue; - sim_clock_cosched_queue = uptr->next; - uptr->next = NULL; - sim_activate (uptr, inst_delay); - } + if (sim_clock_unit == uptr) { + /* + * Some devices may depend on executing during the same instruction as the + * clock tick event. We link the clock coschedule queue to the clock tick + * and then insert that list in the asynch event queue in a single operation + */ + uptr->next = sim_clock_cosched_queue; + sim_clock_cosched_queue = QUEUE_LIST_END; + AIO_ACTIVATE_LIST(sim_activate, uptr, inst_delay); + } + else + sim_activate (uptr, inst_delay); + continue; } - else /* Something wants to adjust the queue */ + else /* Something wants to adjust the queue since the wait condition was signaled */ if (sim_timer_event_canceled) sim_timer_event_canceled = FALSE; /* reset flag and continue */ else - if (sim_wallclock_entry == NULL) /* nothing to insert? */ + if (sim_wallclock_entry == NULL) { /* nothing to insert? */ + sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - condition wakeup without new entry\n"); break; /* stop processing entries */ + } } pthread_mutex_unlock (&sim_timer_lock); @@ -1426,9 +1433,16 @@ if (1) { sim_uname(uptr), uptr->a_due_time); } pthread_mutex_lock (&sim_timer_lock); +while (sim_wallclock_entry) { + sim_debug (DBG_TIM, &sim_timer_dev, "sim_timer_activate_after() - queue insert entry %s busy waiting for 1ms\n", + sim_uname(sim_wallclock_entry)); + pthread_mutex_unlock (&sim_timer_lock); + sim_os_ms_sleep (1); + pthread_mutex_lock (&sim_timer_lock); + } sim_wallclock_entry = uptr; -pthread_cond_signal (&sim_timer_wake); /* wake the timer thread to deal with it */ pthread_mutex_unlock (&sim_timer_lock); +pthread_cond_signal (&sim_timer_wake); /* wake the timer thread to deal with it */ return SCPE_OK; #else return _sim_activate (uptr, inst_delay); /* queue it now */ diff --git a/sim_timer.h b/sim_timer.h index d5f7981c..4448567f 100644 --- a/sim_timer.h +++ b/sim_timer.h @@ -119,6 +119,7 @@ uint32 sim_timer_idle_capable (uint32 *hoat_tick_ms); extern t_bool sim_idle_enab; /* idle enabled flag */ extern volatile t_bool sim_idle_wait; /* idle waiting flag */ +extern UNIT *sim_clock_unit; extern t_bool sim_asynch_timer; extern DEVICE sim_timer_dev; From 6c6d6410341f31b652e0f6f747768aa7d34fae62 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Tue, 7 May 2013 11:40:07 -0700 Subject: [PATCH 10/23] Tolerate race condition which results in spurious timer thread wakeups which were causing the timer thread to exit. (SIM_ASYNCH_CLOCKS) --- sim_console.c | 2 ++ sim_timer.c | 9 ++------- 2 files changed, 4 insertions(+), 7 deletions(-) diff --git a/sim_console.c b/sim_console.c index c0bc8d43..4df66594 100644 --- a/sim_console.c +++ b/sim_console.c @@ -541,6 +541,7 @@ for (i=(was_stepping ? sim_rem_step_line : 0); c = c & ~TMXR_VALID; if (c != sim_int_char) continue; /* ^E (the interrupt character) must start console interaction */ + sim_is_running = 0; sim_stop_timer_services (); for (j=0; j < sim_rem_con_tmxr.lines; j++) { lp = &sim_rem_con_tmxr.ldsc[j]; @@ -728,6 +729,7 @@ for (i=(was_stepping ? sim_rem_step_line : 0); tmxr_linemsg (lp, "Simulator Running..."); tmxr_send_buffered_data (lp); } + sim_is_running = 1; sim_start_timer_services (); break; } diff --git a/sim_timer.c b/sim_timer.c index f5b0f9bb..0cdef0c5 100644 --- a/sim_timer.c +++ b/sim_timer.c @@ -1250,16 +1250,11 @@ while (sim_asynch_enabled && sim_asynch_timer && sim_is_running) { } else sim_activate (uptr, inst_delay); - continue; } - else /* Something wants to adjust the queue since the wait condition was signaled */ + else {/* Something wants to adjust the queue since the wait condition was signaled */ if (sim_timer_event_canceled) sim_timer_event_canceled = FALSE; /* reset flag and continue */ - else - if (sim_wallclock_entry == NULL) { /* nothing to insert? */ - sim_debug (DBG_TIM, &sim_timer_dev, "_timer_thread() - condition wakeup without new entry\n"); - break; /* stop processing entries */ - } + } } pthread_mutex_unlock (&sim_timer_lock); From 141b6e834aa98defe500d50174477809419a1123 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Wed, 8 May 2013 04:17:55 -0700 Subject: [PATCH 11/23] Fixed formatting error --- scp.c | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/scp.c b/scp.c index 9d49196c..7d8fc19c 100644 --- a/scp.c +++ b/scp.c @@ -2930,7 +2930,9 @@ else { fprintf (st, " unit %d", (int32) (uptr - dptr->units)); } else fprintf (st, " Unknown"); - fprintf (st, " after %d usec\n", uptr->a_usec_delay); + fprintf (st, " after "); + fprint_val (st, (t_value)uptr->a_usec_delay, 10, 0, PV_RCOMMA); + fprintf (st, " usec\n"); } } if (sim_clock_cosched_queue != QUEUE_LIST_END) { @@ -6100,7 +6102,7 @@ switch (format) { ndigits = MAX_WIDTH - digit; commas = (ndigits - 1)/3; for (digit=0; digit 0) + d = MAX_WIDTH - width; break; case PV_RZRO: case PV_RSPC: From ddb10425e72281355d30b519353a52ceec11210b Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Wed, 8 May 2013 09:34:53 -0700 Subject: [PATCH 12/23] Reworked all asynch queues to always link using the unit a_next field instead of the next field. --- scp.c | 4 +-- sim_defs.h | 92 +++++++++++++++++++++++++---------------------------- sim_timer.c | 29 +++++++++-------- 3 files changed, 61 insertions(+), 64 deletions(-) diff --git a/scp.c b/scp.c index 7d8fc19c..cde6982c 100644 --- a/scp.c +++ b/scp.c @@ -2923,7 +2923,7 @@ if (sim_wallclock_queue == QUEUE_LIST_END) else { fprintf (st, "%s wall clock event queue status, time = %.0f\n", sim_name, sim_time); - for (uptr = sim_wallclock_queue; uptr != QUEUE_LIST_END; uptr = uptr->next) { + for (uptr = sim_wallclock_queue; uptr != QUEUE_LIST_END; uptr = uptr->a_next) { if ((dptr = find_dev_from_unit (uptr)) != NULL) { fprintf (st, " %s", sim_dname (dptr)); if (dptr->numunits > 1) @@ -2938,7 +2938,7 @@ else { if (sim_clock_cosched_queue != QUEUE_LIST_END) { fprintf (st, "%s clock (%s) co-schedule event queue status, time = %.0f\n", sim_name, sim_uname(sim_clock_unit), sim_time); - for (uptr = sim_clock_cosched_queue; uptr != QUEUE_LIST_END; uptr = uptr->next) { + for (uptr = sim_clock_cosched_queue; uptr != QUEUE_LIST_END; uptr = uptr->a_next) { if ((dptr = find_dev_from_unit (uptr)) != NULL) { fprintf (st, " %s", sim_dname (dptr)); if (dptr->numunits > 1) diff --git a/sim_defs.h b/sim_defs.h index 6e66bbb2..de97ad74 100644 --- a/sim_defs.h +++ b/sim_defs.h @@ -785,13 +785,13 @@ extern int32 sim_asynch_inst_latency; if ((uptr)->a_cancel) \ (uptr)->a_cancel (uptr); \ else { \ - if ((uptr)->next) { \ + AIO_UPDATE_QUEUE; \ + if ((uptr)->a_next) { \ UNIT *cptr; \ - AIO_UPDATE_QUEUE; \ pthread_mutex_lock (&sim_timer_lock); \ if ((uptr) == sim_wallclock_queue) { \ - sim_wallclock_queue = (uptr)->next; \ - (uptr)->next = NULL; \ + sim_wallclock_queue = (uptr)->a_next; \ + (uptr)->a_next = NULL; \ sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Timer Event for %s\n", sim_uname(uptr));\ sim_timer_event_canceled = TRUE; \ pthread_cond_signal (&sim_timer_wake); \ @@ -799,30 +799,30 @@ extern int32 sim_asynch_inst_latency; else \ for (cptr = sim_wallclock_queue; \ (cptr != QUEUE_LIST_END); \ - cptr = cptr->next) \ - if (cptr->next == (uptr)) { \ - cptr->next = (uptr)->next; \ - (uptr)->next = NULL; \ + cptr = cptr->a_next) \ + if (cptr->a_next == (uptr)) { \ + cptr->a_next = (uptr)->a_next; \ + (uptr)->a_next = NULL; \ sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Timer Event for %s\n", sim_uname(uptr));\ break; \ } \ - if ((uptr)->next == NULL) \ + if ((uptr)->a_next == NULL) \ (uptr)->a_due_time = (uptr)->a_usec_delay = 0; \ else { \ if ((uptr) == sim_clock_cosched_queue) { \ - sim_clock_cosched_queue = (uptr)->next; \ - (uptr)->next = NULL; \ + sim_clock_cosched_queue = (uptr)->a_next; \ + (uptr)->a_next = NULL; \ } \ else \ for (cptr = sim_clock_cosched_queue; \ (cptr != QUEUE_LIST_END); \ - cptr = cptr->next) \ - if (cptr->next == (uptr)) { \ - cptr->next = (uptr)->next; \ - (uptr)->next = NULL; \ + cptr = cptr->a_next) \ + if (cptr->a_next == (uptr)) { \ + cptr->a_next = (uptr)->a_next; \ + (uptr)->a_next = NULL; \ break; \ } \ - if ((uptr)->next == NULL) { \ + if ((uptr)->a_next == NULL) { \ sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Clock Coscheduling Event for %s\n", sim_uname(uptr));\ } \ } \ @@ -854,19 +854,19 @@ extern int32 sim_asynch_inst_latency; if ((uptr)->a_cancel) \ (uptr)->a_cancel (uptr); \ else { \ + AIO_UPDATE_QUEUE; \ if (((uptr)->dynflags & UNIT_TM_POLL) && \ !((uptr)->next) && !((uptr)->a_next)) { \ (uptr)->a_polling_now = FALSE; \ sim_tmxr_poll_count -= (uptr)->a_poll_waiter_count; \ (uptr)->a_poll_waiter_count = 0; \ } \ - if ((uptr)->next) { \ + if ((uptr)->a_next) { \ UNIT *cptr; \ - AIO_UPDATE_QUEUE; \ pthread_mutex_lock (&sim_timer_lock); \ if ((uptr) == sim_wallclock_queue) { \ - sim_wallclock_queue = (uptr)->next; \ - (uptr)->next = NULL; \ + sim_wallclock_queue = (uptr)->a_next; \ + (uptr)->a_next = NULL; \ sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Timer Event for %s\n", sim_uname(uptr));\ sim_timer_event_canceled = TRUE; \ pthread_cond_signal (&sim_timer_wake); \ @@ -874,30 +874,30 @@ extern int32 sim_asynch_inst_latency; else \ for (cptr = sim_wallclock_queue; \ (cptr != QUEUE_LIST_END); \ - cptr = cptr->next) \ - if (cptr->next == (uptr)) { \ - cptr->next = (uptr)->next; \ - (uptr)->next = NULL; \ + cptr = cptr->a_next) \ + if (cptr->a_next == (uptr)) { \ + cptr->a_next = (uptr)->a_next; \ + (uptr)->a_next = NULL; \ sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Timer Event for %s\n", sim_uname(uptr));\ break; \ } \ - if ((uptr)->next == NULL) \ + if ((uptr)->a_next == NULL) \ (uptr)->a_due_time = (uptr)->a_usec_delay = 0; \ else { \ if ((uptr) == sim_clock_cosched_queue) { \ - sim_clock_cosched_queue = (uptr)->next; \ - (uptr)->next = NULL; \ + sim_clock_cosched_queue = (uptr)->a_next; \ + (uptr)->a_next = NULL; \ } \ else \ for (cptr = sim_clock_cosched_queue; \ (cptr != QUEUE_LIST_END); \ - cptr = cptr->next) \ - if (cptr->next == (uptr)) { \ - cptr->next = (uptr)->next; \ - (uptr)->next = NULL; \ + cptr = cptr->a_next) \ + if (cptr->a_next == (uptr)) { \ + cptr->a_next = (uptr)->a_next; \ + (uptr)->a_next = NULL; \ break; \ } \ - if ((uptr)->next == NULL) { \ + if ((uptr)->a_next == NULL) { \ sim_debug (SIM_DBG_EVENT, sim_dflt_dev, "Canceling Clock Coscheduling Event for %s\n", sim_uname(uptr));\ } \ } \ @@ -917,7 +917,7 @@ extern int32 sim_asynch_inst_latency; pthread_mutex_lock (&sim_timer_lock); \ for (cptr = sim_wallclock_queue; \ cptr != QUEUE_LIST_END; \ - cptr = cptr->next) \ + cptr = cptr->a_next) \ if ((uptr) == cptr) { \ double inst_per_sec = sim_timer_inst_per_sec (); \ int32 result; \ @@ -1028,8 +1028,10 @@ extern int32 sim_asynch_inst_latency; else \ a_event_time = uptr->a_event_time; \ uptr->a_activate_call (uptr, a_event_time); \ - if (uptr->a_check_completion) \ + if (uptr->a_check_completion) { \ + sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Calling Completion Check for asynch event on %s\n", sim_uname(uptr));\ uptr->a_check_completion (uptr); \ + } \ } \ } else (void)0 #define AIO_ACTIVATE(caller, uptr, event_time) \ @@ -1063,20 +1065,16 @@ extern int32 sim_asynch_inst_latency; return SCPE_OK; \ } else (void)0 #define AIO_ACTIVATE_LIST(caller, list, event_time) \ - if (1) { \ + if (list) { \ UNIT *ouptr, *q, *qe; \ sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Queueing Asynch events for %s after %d instructions\n", sim_uname(list), event_time);\ - for (qe=(list); qe->next != QUEUE_LIST_END;) { \ + for (qe=(list); qe->a_next != QUEUE_LIST_END;) { \ qe->a_event_time = event_time; \ qe->a_activate_call = caller; \ - qe->a_next = qe->next; \ - qe->next = NULL; \ qe = qe->a_next; \ } \ qe->a_event_time = event_time; \ qe->a_activate_call = caller; \ - qe->a_next = QUEUE_LIST_END; \ - qe->next = NULL; \ ouptr = (list); \ do { \ do \ @@ -1137,7 +1135,7 @@ extern int32 sim_asynch_inst_latency; while (sim_asynch_queue != QUEUE_LIST_END) { /* List !Empty */ \ int32 a_event_time; \ uptr = sim_asynch_queue; \ - sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "found asynch event for %s after %d instructions\n", sim_uname(uptr), uptr->a_event_time);\ + sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Migrating Asynch event for %s after %d instructions\n", sim_uname(uptr), uptr->a_event_time);\ sim_asynch_queue = uptr->a_next; \ uptr->a_next = NULL; /* hygiene */ \ if (uptr->a_activate_call != &sim_activate_notbefore) { \ @@ -1148,10 +1146,11 @@ extern int32 sim_asynch_inst_latency; else \ a_event_time = uptr->a_event_time; \ AIO_UNLOCK; \ - sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "calling completion check for asynch event on %s\n", sim_uname(uptr));\ uptr->a_activate_call (uptr, a_event_time); \ - if (uptr->a_check_completion) \ + if (uptr->a_check_completion) { \ + sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Calling Completion Check for asynch event on %s\n", sim_uname(uptr));\ uptr->a_check_completion (uptr); \ + } \ AIO_LOCK; \ } \ AIO_UNLOCK; \ @@ -1177,19 +1176,16 @@ extern int32 sim_asynch_inst_latency; return SCPE_OK; \ } else (void)0 #define AIO_ACTIVATE_LIST(caller, list, event_time) \ - if (1) { \ + if (list) { \ UNIT *qe; \ sim_debug (SIM_DBG_AIO_QUEUE, sim_dflt_dev, "Queueing Asynch events for %s after %d instructions\n", sim_uname(list), event_time);\ - for (qe=list; qe->next != QUEUE_LIST_END;) { \ + for (qe=list; qe->a_next != QUEUE_LIST_END;) { \ qe->a_event_time = event_time; \ qe->a_activate_call = caller; \ - qe->a_next = qe->next; \ - qe->next = NULL; \ qe = qe->a_next; \ } \ qe->a_event_time = event_time; \ qe->a_activate_call = caller; \ - qe->next = NULL; \ AIO_LOCK; \ qe->a_next = sim_asynch_queue; \ sim_asynch_queue = list; \ diff --git a/sim_timer.c b/sim_timer.c index 0cdef0c5..87d9799d 100644 --- a/sim_timer.c +++ b/sim_timer.c @@ -1185,18 +1185,18 @@ while (sim_asynch_enabled && sim_asynch_timer && sim_is_running) { sim_wallclock_entry = NULL; prvptr = NULL; - for (cptr = sim_wallclock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) { + for (cptr = sim_wallclock_queue; cptr != QUEUE_LIST_END; cptr = cptr->a_next) { if (uptr->a_due_time < cptr->a_due_time) break; prvptr = cptr; } if (prvptr == NULL) { /* insert at head */ - cptr = uptr->next = sim_wallclock_queue; + cptr = uptr->a_next = sim_wallclock_queue; sim_wallclock_queue = uptr; } else { - cptr = uptr->next = prvptr->next; /* insert at prvptr */ - prvptr->next = uptr; + cptr = uptr->a_next = prvptr->a_next; /* insert at prvptr */ + prvptr->a_next = uptr; } } @@ -1224,8 +1224,8 @@ while (sim_asynch_enabled && sim_asynch_timer && sim_is_running) { inst_per_sec = sim_timer_inst_per_sec (); uptr = sim_wallclock_queue; - sim_wallclock_queue = uptr->next; - uptr->next = NULL; /* hygiene */ + sim_wallclock_queue = uptr->a_next; + uptr->a_next = NULL; /* hygiene */ clock_gettime(CLOCK_REALTIME, &stop_time); if (1 != sim_timespec_compare (&due_time, &stop_time)) { @@ -1240,11 +1240,12 @@ while (sim_asynch_enabled && sim_asynch_timer && sim_is_running) { 1000.0*(_timespec_to_double (&stop_time)-_timespec_to_double (&start_time)), sim_uname(uptr), inst_delay); if (sim_clock_unit == uptr) { /* - * Some devices may depend on executing during the same instruction as the - * clock tick event. We link the clock coschedule queue to the clock tick - * and then insert that list in the asynch event queue in a single operation + * Some devices may depend on executing during the same instruction or immediately + * after the clock tick event. To satisfy this, we link the clock unit to the head + * of the clock coschedule queue and then insert that list in the asynch event + * queue in a single operation */ - uptr->next = sim_clock_cosched_queue; + uptr->a_next = sim_clock_cosched_queue; sim_clock_cosched_queue = QUEUE_LIST_END; AIO_ACTIVATE_LIST(sim_activate, uptr, inst_delay); } @@ -1277,7 +1278,7 @@ if (sim_asynch_enabled && sim_asynch_timer) { /* when restarting after being manually stopped the due times for all */ /* timer events needs to slide so they fire in the future. (clock ticks */ /* don't accumulate when the simulator is stopped) */ - for (cptr = sim_wallclock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) { + for (cptr = sim_wallclock_queue; cptr != QUEUE_LIST_END; cptr = cptr->a_next) { if (cptr == sim_wallclock_queue) { /* Handle first entry */ struct timespec now; double due_time; @@ -1332,9 +1333,9 @@ else { uptr = sim_wallclock_queue; if (uptr == QUEUE_LIST_END) break; - sim_wallclock_queue = uptr->next; + sim_wallclock_queue = uptr->a_next; accum += uptr->time; - uptr->next = NULL; + uptr->a_next = NULL; uptr->a_due_time = 0; uptr->a_usec_delay = 0; sim_activate_after (uptr, accum); @@ -1464,7 +1465,7 @@ else (rtc_elapsed[sim_calb_tmr ] >= sim_idle_stable)) { sim_debug (DBG_TIM, &sim_timer_dev, "sim_clock_coschedule() - queueing %s for clock co-schedule\n", sim_uname (uptr)); pthread_mutex_lock (&sim_timer_lock); - uptr->next = sim_clock_cosched_queue; + uptr->a_next = sim_clock_cosched_queue; sim_clock_cosched_queue = uptr; pthread_mutex_unlock (&sim_timer_lock); return SCPE_OK; From d964df4eeae953ecc0a78e724ab628830e163f7b Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Thu, 9 May 2013 14:02:48 -0700 Subject: [PATCH 13/23] Compiler suggested cleanups --- PDP11/pdp11_cr.c | 2 +- PDP11/pdp11_rc.c | 2 +- PDP11/pdp11_vh.c | 2 +- S3/s3_cpu.c | 2 +- sim_serial.c | 24 ++++++++++++------------ 5 files changed, 16 insertions(+), 16 deletions(-) diff --git a/PDP11/pdp11_cr.c b/PDP11/pdp11_cr.c index 467b03f7..28e6c1bf 100644 --- a/PDP11/pdp11_cr.c +++ b/PDP11/pdp11_cr.c @@ -1372,7 +1372,7 @@ t_stat cr_show_trans ( FILE *st, return (SCPE_OK); } -t_stat cr_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +static t_stat cr_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) { #if defined(VM_PDP11) char *devtype = "CR11/CD11"; diff --git a/PDP11/pdp11_rc.c b/PDP11/pdp11_rc.c index dd77b426..c1103334 100644 --- a/PDP11/pdp11_rc.c +++ b/PDP11/pdp11_rc.c @@ -591,7 +591,7 @@ static t_stat rc_set_size (UNIT *uptr, int32 val, char *cptr, void *desc) return (SCPE_OK); } -char *rc_description (DEVICE *dptr) +static char *rc_description (DEVICE *dptr) { return "RC11/RS64 fixed head disk controller"; } diff --git a/PDP11/pdp11_vh.c b/PDP11/pdp11_vh.c index b2172987..5ddb9b5e 100644 --- a/PDP11/pdp11_vh.c +++ b/PDP11/pdp11_vh.c @@ -1386,7 +1386,7 @@ static t_stat vh_detach ( UNIT *uptr ) return (tmxr_detach (&vh_desc, uptr)); } -t_stat vh_show_vec (FILE *st, UNIT *uptr, int32 arg, void *desc) +static t_stat vh_show_vec (FILE *st, UNIT *uptr, int32 arg, void *desc) { TMXR *mp = (TMXR *) desc; diff --git a/S3/s3_cpu.c b/S3/s3_cpu.c index 6906b7e2..7d3c8a52 100644 --- a/S3/s3_cpu.c +++ b/S3/s3_cpu.c @@ -1315,7 +1315,7 @@ int32 PutMem(int32 addr, int32 data) /* Check the condition register against the qbyte and return 1 if true */ -int32 condition(int32 qbyte) +static int32 condition(int32 qbyte) { int32 r = 0, t, q; t = (qbyte & 0xf0) >> 4; diff --git a/sim_serial.c b/sim_serial.c index 14678e95..15dee496 100644 --- a/sim_serial.c +++ b/sim_serial.c @@ -583,7 +583,7 @@ return ports; interest to a DCB retrieved from a call to "GetCommState". */ -SERHANDLE sim_open_os_serial (char *name) +static SERHANDLE sim_open_os_serial (char *name) { SERHANDLE port; DCB dcb; @@ -675,7 +675,7 @@ return port; /* return port handle on 1.5 stop bits. */ -t_stat sim_config_os_serial (SERHANDLE port, SERCONFIG config) +static t_stat sim_config_os_serial (SERHANDLE port, SERCONFIG config) { static const struct { char parity; @@ -868,7 +868,7 @@ else The serial port is closed. Errors are ignored. */ -void sim_close_os_serial (SERHANDLE port) +static void sim_close_os_serial (SERHANDLE port) { CloseHandle (port); /* close the port */ return; @@ -1000,7 +1000,7 @@ return ports; reading. */ -SERHANDLE sim_open_os_serial (char *name) +static SERHANDLE sim_open_os_serial (char *name) { static const tcflag_t i_clear = IGNBRK | /* ignore BREAK */ BRKINT | /* signal on BREAK */ @@ -1118,7 +1118,7 @@ return port; /* return port fd for su */ -t_stat sim_config_os_serial (SERHANDLE port, SERCONFIG config) +static t_stat sim_config_os_serial (SERHANDLE port, SERCONFIG config) { struct termios tio; int32 i; @@ -1339,7 +1339,7 @@ return (int32) written; /* return number of The serial port is closed. Errors are ignored. */ -void sim_close_os_serial (SERHANDLE port) +static void sim_close_os_serial (SERHANDLE port) { close (port); /* close the port */ return; @@ -1484,7 +1484,7 @@ return ports; */ -SERHANDLE sim_open_os_serial (char *name) +static SERHANDLE sim_open_os_serial (char *name) { uint32 status; uint32 chan = 0; @@ -1541,7 +1541,7 @@ return chan; /* return channel for su */ -t_stat sim_config_os_serial (SERHANDLE port, SERCONFIG config) +static t_stat sim_config_os_serial (SERHANDLE port, SERCONFIG config) { int32 i; SENSE_BUF sense; @@ -1780,7 +1780,7 @@ return (int32)iosb.count; /* return number of char The serial port is closed. Errors are ignored. */ -void sim_close_os_serial (SERHANDLE port) +static void sim_close_os_serial (SERHANDLE port) { sys$dassgn (port); /* close the port */ return; @@ -1800,7 +1800,7 @@ return 0; /* Open a serial port */ -SERHANDLE sim_open_os_serial (char *name) +static SERHANDLE sim_open_os_serial (char *name) { return INVALID_HANDLE; } @@ -1808,7 +1808,7 @@ return INVALID_HANDLE; /* Configure a serial port */ -t_stat sim_config_os_serial (SERHANDLE port, SERCONFIG config) +static t_stat sim_config_os_serial (SERHANDLE port, SERCONFIG config) { return SCPE_IERR; } @@ -1840,7 +1840,7 @@ return -1; /* Close a serial port */ -void sim_close_os_serial (SERHANDLE port) +static void sim_close_os_serial (SERHANDLE port) { return; } From 52ec325e68dd41b9f6b9d88dd5367a6daea8a318 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Thu, 9 May 2013 16:43:29 -0700 Subject: [PATCH 14/23] Compiler suggested fix. --- S3/s3_cpu.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/S3/s3_cpu.c b/S3/s3_cpu.c index 7d3c8a52..b732cbe1 100644 --- a/S3/s3_cpu.c +++ b/S3/s3_cpu.c @@ -1345,7 +1345,7 @@ return (r); condition register initial state in parameter 3 */ -int32 compare(int32 byte1, int32 byte2, int32 cond) +static int32 compare(int32 byte1, int32 byte2, int32 cond) { int32 r; From 5fda4b628c70f9cfe254ad02392a4ff6b7f50a1a Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Thu, 9 May 2013 17:18:05 -0700 Subject: [PATCH 15/23] Cleanup for build support on HP-UX for both 32bit and 64bit builds with gcc and the HP C compilers. --- makefile | 30 +++++++++++++++++++++++------- 1 file changed, 23 insertions(+), 7 deletions(-) diff --git a/makefile b/makefile index 13bfcd8b..869ab5a9 100644 --- a/makefile +++ b/makefile @@ -83,6 +83,11 @@ ifeq ($(WIN32),) #*nix Environments (&& cygwin) COMPILER_NAME = $(wordlist 2,10,$(SUNC_VERSION)) endif endif + ifeq (HP-UX,$(OSTYPE)) + ifneq (,$(shell what `which $(firstword $(GCC))` | grep -i compiler)) + COMPILER_NAME = $(strip $(shell what `which $(firstword $(GCC))` | grep -i compiler)) + endif + endif endif else ifeq (Apple,$(shell $(GCC) -v /dev/null 2>&1 | grep 'Apple' | awk '{ print $$1 }')) @@ -167,15 +172,25 @@ ifeq ($(WIN32),) #*nix Environments (&& cygwin) LIBEXT = a else ifeq (,$(findstring NetBSD,$(OSTYPE))) - LDSEARCH :=$(shell ldconfig -r | grep 'search directories' | awk '{print $$3}' | sed 's/:/ /g') + ifneq (no ldconfig,$(wordlist 1,2,$(shell which ldconfig))) + LDSEARCH :=$(shell ldconfig -r | grep 'search directories' | awk '{print $$3}' | sed 's/:/ /g') + endif ifneq (,$(LDSEARCH)) LIBPATH := $(LDSEARCH) else - $(info *** Warning ***) - $(info *** Warning *** The library search path on your $(OSTYPE) platform can't be) - $(info *** Warning *** determined. This should be resolved before you can expect) - $(info *** Warning *** to have fully working simulators.) - $(info *** Warning ***) + ifeq (,$(strip $(LPATH))) + $(info *** Warning ***) + $(info *** Warning *** The library search path on your $(OSTYPE) platform can't be) + $(info *** Warning *** determined. This should be resolved before you can expect) + $(info *** Warning *** to have fully working simulators.) + $(info *** Warning ***) + $(info *** Warning *** You can specify your library paths via the LPATH environment) + $(info *** Warning *** variable.) + $(info *** Warning ***) + else + LIBPATH = $(subst :, ,$(LPATH)) + OS_LDFLAGS += $(patsubst %,-L%,$(LIBPATH)) + endif endif endif ifeq (usrpkglib,$(shell if $(TEST) -d /usr/pkg/lib; then echo usrpkglib; fi)) @@ -192,6 +207,7 @@ ifeq ($(WIN32),) #*nix Environments (&& cygwin) LIBEXT = sl endif OS_CCDEFS += -D_HPUX_SOURCE -D_LARGEFILE64_SOURCE + OS_LDFLAGS += -Wl,+b: NO_LTO = 1 else LIBEXT = a @@ -306,7 +322,7 @@ ifeq ($(WIN32),) #*nix Environments (&& cygwin) NETWORK_LDFLAGS = -L$(dir $(call find_lib,$(PCAPLIB))) -Wl,-R,$(dir $(call find_lib,$(PCAPLIB))) -l$(PCAPLIB) NETWORK_FEATURES = - static networking support using libpcap components located in the cygwin directories else - NETWORK_CCDEFS := -DUSE_NETWORK -isystem $(dir $(call find_include,pcap)) $(call find_lib,$(PCAPLIB)) + NETWORK_CCDEFS := -DUSE_NETWORK -isystem -I$(dir $(call find_include,pcap)) $(call find_lib,$(PCAPLIB)) NETWORK_FEATURES = - networking support using libpcap components from www.tcpdump.org $(info *** Warning ***) $(info *** Warning *** $(BUILD_SINGLE)Simulator$(BUILD_MULTIPLE) being built with networking support using) From 281c18142f657930bd85f4f161c607efb0b42b9d Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Thu, 9 May 2013 17:33:47 -0700 Subject: [PATCH 16/23] Describe the proper build command for HP-UX --- 0readme_ethernet.txt | 9 ++++++++- makefile | 7 +++++++ 2 files changed, 15 insertions(+), 1 deletion(-) diff --git a/0readme_ethernet.txt b/0readme_ethernet.txt index 9a74e0f1..bfe3abe2 100644 --- a/0readme_ethernet.txt +++ b/0readme_ethernet.txt @@ -304,8 +304,15 @@ Building on Linux, {Free|Net|Open}BSD, OS/X, Solaris, other *nix: typing 'make USE_NETWORK=1'. You must build with gcc to do this. There is no observable benefit to statically linking against libpcap and as such support for this ia deprecated. + + 4. Some platforms (HP-UX in particular) may not have vendor supplied libpcap + components available and installed with the operating system. The packages + which are available for this platform install include and library files in + user specified locations. When building on these platforms the library + path must be specified on the make command line. This can be done with: + 'make LPATH=/usr/lib:/usr/local/lib' - 4. Build it! + 5. Build it! ------------------------------------------------------------------------------- diff --git a/makefile b/makefile index 869ab5a9..2a3ec370 100644 --- a/makefile +++ b/makefile @@ -22,6 +22,13 @@ # libpcap can be enabled if GNU make is invoked with USE_NETWORK=1 on the # command line. # +# Some platforms may not have vendor supplied libpcap available. HP-UX is +# one such example. The packages which are available for this platform +# install include files and libraries in user specified directories. In +# order for this makefile to locate where these components may have been +# installed, gmake should be invoked with LPATH=/usr/lib:/usr/local/lib +# defined (adjusted as needed depending on where they may be installed). +# # The default build will build compiler optimized binaries. # If debugging is desired, then GNU make can be invoked with # DEBUG=1 on the command line. From 782bec605d7f37a1a73aadbb6fccb37d6b18af03 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Fri, 10 May 2013 05:02:07 -0700 Subject: [PATCH 17/23] Fix potential remote console memory leak --- sim_console.c | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/sim_console.c b/sim_console.c index 4df66594..ccf0b45d 100644 --- a/sim_console.c +++ b/sim_console.c @@ -773,8 +773,17 @@ if (flag) { return SCPE_NOPARAM; } else { - if (sim_rem_con_tmxr.master) + if (sim_rem_con_tmxr.master) { + int32 i; + tmxr_detach (&sim_rem_con_tmxr, &sim_rem_con_unit[0]); + for (i=0; i Date: Fri, 10 May 2013 16:10:07 -0700 Subject: [PATCH 18/23] Enhanced Remote Console support to allow two separate command processing modes. 1) Single Command Mode: commands are processed between simulated instructions once CR is received. 2) Multiple Command Mode: commands are initiated with the WRU character (usually *E) which suspends simulation execution and commands are processed until a CONTINUE command is entered (or automatically provided if the SET REMOTE TIMEOUT=seconds interval has elapsed). --- README.md | 8 ++- doc/simh_doc.doc | Bin 192512 -> 196608 bytes sim_console.c | 150 ++++++++++++++++++++++++++++++----------------- 3 files changed, 102 insertions(+), 56 deletions(-) diff --git a/README.md b/README.md index 791dad6f..0964bbee 100644 --- a/README.md +++ b/README.md @@ -23,7 +23,13 @@ #### Remote Console Facility A new capability has been added which allows a TELNET Connection to a user designated port so that some out of band commands can be entered to manipulate and/or adjust a running simulator. The commands which enable and control this capability are SET REMOTE TELNET=port, SET REMOTE CONNECTIONS=n, SET REMOTE TIMEOUT=seconds, and SHOW REMOTE. -A subset of normal simh commands are available for use in remote console sessions. These are: EXAMINE, IEXAMINE, DEPOSIT, EVALUATE, ATTACH, DETACH, ASSIGN, DEASSIGN, STEP, CONTINUE, PWD, SAVE, SET, SHOW, DIR, LS, ECHO, HELP +The remote console facility has two modes of operation: 1) single command mode. and 2) multiple command mode. +In single command mode you enter one command at a time and aren't concerned about what the simulated system is doing while you enter that command. The command is executed once you've hit return. +In multiple command mode you initiate your activities by entering the WRU character (usually ^E). This will suspend the current simulator execution. You then enter commands as needed and when you are done you enter a CONTINUE command. While entering Multiple Command commands, if you fail to enter a complete command before the timeout (specified by "SET REMOTE TIMEOUT=seconds"), a CONTINUE command is automatically processed and simulation proceeds. + +A subset of normal simh commands are available for use in remote console sessions. +The Single Command Mode commands are: ATTACH, DETACH, PWD, SHOW, DIR, LS, ECHO, HELP +The Multiple Command Mode commands are: EXAMINE, IEXAMINE, DEPOSIT, EVALUATE, ATTACH, DETACH, ASSIGN, DEASSIGN, STEP, CONTINUE, PWD, SAVE, SET, SHOW, DIR, LS, ECHO, HELP #### VAX/PDP11 Enhancements RQ has new disk types: RC25, RCF25, RA80 diff --git a/doc/simh_doc.doc b/doc/simh_doc.doc index 480365e79f339b327cf3a8df338a07d7b9ee37ef..02cec35ba569e91ce2d84d30f9d6b38c1e6ca14c 100644 GIT binary patch delta 26429 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 0) { /* poll connect */ sim_activate_after(uptr+1, 1000000); /* start data poll after 100ms */ lp->rcve = 1; /* rcv enabled */ sim_rem_buf_ptr[c] = 0; /* start with empty command buffer */ - tmxr_linemsgf (lp, "%s Remote Console\r\nSimulator Running...", sim_name); + tmxr_linemsgf (lp, "%s Remote Console\r\n" + "Enter single commands or to enter multiple command mode enter the WRU character\r\n" + "Simulator Running...", sim_name); tmxr_send_buffered_data (lp); /* flush buffered data */ } sim_activate_after(uptr, 1000000); /* check again in 1 second */ @@ -465,6 +468,18 @@ static CTAB allowed_remote_cmds[] = { { NULL, NULL } }; +static CTAB allowed_single_remote_cmds[] = { + { "ATTACH", &attach_cmd, 0 }, + { "DETACH", &detach_cmd, 0 }, + { "PWD", &pwd_cmd, 0 }, + { "DIR", &dir_cmd, 0 }, + { "LS", &dir_cmd, 0 }, + { "ECHO", &echo_cmd, 0 }, + { "SHOW", &show_cmd, 0 }, + { "HELP", &x_help_cmd, 0 }, + { NULL, NULL } + }; + static t_stat x_help_cmd (int32 flag, char *cptr) { CTAB *cmdp, *cmdph; @@ -539,52 +554,63 @@ for (i=(was_stepping ? sim_rem_step_line : 0); if (!(TMXR_VALID & c)) continue; c = c & ~TMXR_VALID; - if (c != sim_int_char) - continue; /* ^E (the interrupt character) must start console interaction */ - sim_is_running = 0; - sim_stop_timer_services (); - for (j=0; j < sim_rem_con_tmxr.lines; j++) { - lp = &sim_rem_con_tmxr.ldsc[j]; - if ((i == j) || (!lp->conn)) - continue; - tmxr_linemsgf (lp, "\nRemote Console(%s) Entering Commands\n", lp->ipad); + if (!sim_rem_single_mode[i]) { + if (c == sim_int_char) { /* ^E (the interrupt character) must start continue mode console interaction */ + sim_is_running = 0; + sim_stop_timer_services (); + for (j=0; j < sim_rem_con_tmxr.lines; j++) { + lp = &sim_rem_con_tmxr.ldsc[j]; + if ((i == j) || (!lp->conn)) + continue; + tmxr_linemsgf (lp, "\nRemote Console(%s) Entering Commands\n", lp->ipad); + tmxr_send_buffered_data (lp); /* flush any buffered data */ + } + lp = &sim_rem_con_tmxr.ldsc[i]; + tmxr_linemsg (lp, "\r\nSimulator paused.\r\n"); + if (sim_rem_read_timeout) + tmxr_linemsgf (lp, "Simulation will resume automatically if input is not received in %d seconds\n", sim_rem_read_timeout); + } + else { + sim_rem_single_mode[i] = TRUE; + tmxr_linemsg (lp, "\r\nsim> "); + tmxr_send_buffered_data (lp); /* flush any buffered data */ + } + } + } + got_command = FALSE; + while (1) { + if (!sim_rem_single_mode[i]) { + read_start_time = sim_os_msec(); + tmxr_linemsg (lp, "sim> "); tmxr_send_buffered_data (lp); /* flush any buffered data */ } - lp = &sim_rem_con_tmxr.ldsc[i]; - tmxr_linemsg (lp, "\r\nSimulator paused.\r\n"); - if (sim_rem_read_timeout) - tmxr_linemsgf (lp, "Simulation will resume automatically if input is not received in %d seconds\n", sim_rem_read_timeout); - } - while (1) { - read_start_time = sim_os_msec(); - tmxr_linemsg (lp, "sim> "); - tmxr_send_buffered_data (lp); /* flush any buffered data */ - got_command = FALSE; - while (!got_command) { - c = tmxr_getc_ln (lp); - if (!(TMXR_VALID & c)) { - tmxr_send_buffered_data (lp); /* flush any buffered data */ - if (sim_rem_read_timeout && - ((sim_os_msec() - read_start_time)/1000 >= sim_rem_read_timeout)) { - while (sim_rem_buf_ptr[i] > 0) { /* Erase current input line */ - tmxr_linemsg (lp, "\b \b"); - --sim_rem_buf_ptr[i]; + do { + if (!sim_rem_single_mode[i]) { + c = tmxr_getc_ln (lp); + if (!(TMXR_VALID & c)) { + tmxr_send_buffered_data (lp); /* flush any buffered data */ + if (sim_rem_read_timeout && + ((sim_os_msec() - read_start_time)/1000 >= sim_rem_read_timeout)) { + while (sim_rem_buf_ptr[i] > 0) { /* Erase current input line */ + tmxr_linemsg (lp, "\b \b"); + --sim_rem_buf_ptr[i]; + } + if (sim_rem_buf_ptr[i]+80 >= sim_rem_buf_size[i]) { + sim_rem_buf_size[i] += 1024; + sim_rem_buf[i] = realloc (sim_rem_buf[i], sim_rem_buf_size[i]); + } + strcpy (sim_rem_buf[i], "CONTINUE ! Automatic continue due to timeout"); + tmxr_linemsgf (lp, "%s\n", sim_rem_buf[i]); + got_command = TRUE; + break; } - if (sim_rem_buf_ptr[i]+80 >= sim_rem_buf_size[i]) { - sim_rem_buf_size[i] += 1024; - sim_rem_buf[i] = realloc (sim_rem_buf[i], sim_rem_buf_size[i]); - } - strcpy (sim_rem_buf[i], "CONTINUE ! Automatic continue due to timeout"); - tmxr_linemsgf (lp, "%s\n", sim_rem_buf[i]); - got_command = TRUE; - break; + sim_os_ms_sleep (100); + tmxr_poll_rx (&sim_rem_con_tmxr);/* poll input */ + continue; } - sim_os_ms_sleep (100); - tmxr_poll_rx (&sim_rem_con_tmxr);/* poll input */ - continue; + read_start_time = sim_os_msec(); + c = c & ~TMXR_VALID; } - read_start_time = sim_os_msec(); - c = c & ~TMXR_VALID; switch (c) { case 0: /* no data */ break; @@ -626,8 +652,10 @@ for (i=(was_stepping ? sim_rem_step_line : 0); got_command = TRUE; /* command too long */ break; } - } + } while ((!got_command) && (!sim_rem_single_mode[i])); tmxr_send_buffered_data (lp); /* flush any buffered data */ + if ((sim_rem_single_mode[i]) && !got_command) + break; printf ("Remote Console Command from %s> %s\r\n", lp->ipad, sim_rem_buf[i]); if (sim_log) fprintf (sim_log, "Remote Console Command from %s> %s\n", lp->ipad, sim_rem_buf[i]); @@ -643,7 +671,12 @@ for (i=(was_stepping ? sim_rem_step_line : 0); sim_rem_buf_ptr[i] = 0; sim_rem_buf[i][sim_rem_buf_ptr[i]] = '\0'; if (cbuf[0] == '\0') - continue; + if (sim_rem_single_mode[i]) { + sim_rem_single_mode[i] = FALSE; + break; + } + else + continue; sim_sub_args (cbuf, sizeof(cbuf), argv); cptr = cbuf; cptr = get_glyph (cptr, gbuf, 0); /* get command glyph */ @@ -661,7 +694,7 @@ for (i=(was_stepping ? sim_rem_step_line : 0); if (!find_cmd (gbuf)) /* validate command */ stat = SCPE_UNK; else { - if ((cmdp = find_ctab (allowed_remote_cmds, gbuf))) {/* lookup command */ + if ((cmdp = find_ctab (sim_rem_single_mode[i] ? allowed_single_remote_cmds : allowed_remote_cmds, gbuf))) {/* lookup command */ if (cmdp->action == &x_continue_cmd) stat = SCPE_OK; else { @@ -709,28 +742,32 @@ for (i=(was_stepping ? sim_rem_step_line : 0); tmxr_linemsgf (lp, "%s", cbuf); tmxr_send_buffered_data (lp); } - if (cmdp && (cmdp->action == &x_continue_cmd)) { + if ((cmdp && (cmdp->action == &x_continue_cmd)) || + (sim_rem_single_mode[i])) { sim_rem_step_line = -1; /* Not stepping */ if (sim_log_temp) { /* If we setup a temporary log, clean it now */ int32 save_quiet = sim_quiet; - sim_quiet = 0; + sim_quiet = 1; sim_set_logoff (0, NULL); sim_quiet = save_quiet; remove (sim_rem_con_temp_name); sim_log_temp = FALSE; } - tmxr_linemsg (lp, "Simulator Running..."); - tmxr_send_buffered_data (lp); - for (j=0; j < sim_rem_con_tmxr.lines; j++) { - lp = &sim_rem_con_tmxr.ldsc[j]; - if ((i == j) || (!lp->conn)) - continue; + if (!sim_rem_single_mode[i]) { tmxr_linemsg (lp, "Simulator Running..."); tmxr_send_buffered_data (lp); + for (j=0; j < sim_rem_con_tmxr.lines; j++) { + lp = &sim_rem_con_tmxr.ldsc[j]; + if ((i == j) || (!lp->conn)) + continue; + tmxr_linemsg (lp, "Simulator Running..."); + tmxr_send_buffered_data (lp); + } + sim_is_running = 1; + sim_start_timer_services (); } - sim_is_running = 1; - sim_start_timer_services (); + sim_rem_single_mode[i] = FALSE; break; } if (cmdp && (cmdp->action == &x_step_cmd)) { @@ -782,6 +819,7 @@ else { sim_rem_buf[i] = NULL; sim_rem_buf_size[i] = 0; sim_rem_buf_ptr[i] = 0; + sim_rem_single_mode[i] = FALSE; } } } @@ -812,6 +850,8 @@ sim_rem_buf_size = realloc (sim_rem_buf_size, sizeof(*sim_rem_buf_size)*lines); memset (sim_rem_buf_size, 0, sizeof(*sim_rem_buf_size)*lines); sim_rem_buf_ptr = realloc (sim_rem_buf_ptr, sizeof(*sim_rem_buf_ptr)*lines); memset (sim_rem_buf_ptr, 0, sizeof(*sim_rem_buf_ptr)*lines); +sim_rem_single_mode = realloc (sim_rem_single_mode, sizeof(*sim_rem_single_mode)*lines); +memset (sim_rem_single_mode, 0, sizeof(*sim_rem_single_mode)*lines); return SCPE_OK; } From 7eb02bfd2b3721a82dc6717b667e774915e62c53 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Fri, 10 May 2013 16:15:42 -0700 Subject: [PATCH 19/23] Avoid noisy log messages in Remote Console activities. --- sim_console.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/sim_console.c b/sim_console.c index e804bf8c..df573adc 100644 --- a/sim_console.c +++ b/sim_console.c @@ -684,7 +684,7 @@ for (i=(was_stepping ? sim_rem_step_line : 0); if (!sim_log) { /* Not currently logging? */ int32 save_quiet = sim_quiet; - sim_quiet = 0; + sim_quiet = 1; sprintf (sim_rem_con_temp_name, "sim_remote_console_%d.temporary_log", (int)getpid()); sim_set_logon (0, sim_rem_con_temp_name); sim_quiet = save_quiet; From 671644ff41fe7bc367adc7556b81eef39821ff15 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Sun, 12 May 2013 11:58:34 -0700 Subject: [PATCH 20/23] Avoid passing negative values (i.e. signed characters) to isprint() which is not happy with doing this on some platforms. --- sim_tmxr.c | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/sim_tmxr.c b/sim_tmxr.c index 27fa6acb..25536e53 100644 --- a/sim_tmxr.c +++ b/sim_tmxr.c @@ -3665,7 +3665,7 @@ for (j=0; 1; ++j) { } } -static int tmxr_buf_debug_telnet_options (char *buf, int bufsize) +static int tmxr_buf_debug_telnet_options (u_char *buf, int bufsize) { int optsize = 2; @@ -3698,7 +3698,7 @@ if ((lp->mp->dptr) && (dbits & lp->mp->dptr->dctrl)) { for (i=0; imp->dptr) && (dbits & lp->mp->dptr->dctrl)) { tmxr_buf_debug_string ("_TN_LF_"); break; default: - if (isprint(buf[i])) + if (isprint((u_char)buf[i])) tmxr_buf_debug_char (buf[i]); else { tmxr_buf_debug_char ('_'); From 3d27f015c1e2e87f69e95b9684b6738d647b5a34 Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Sun, 12 May 2013 12:11:18 -0700 Subject: [PATCH 21/23] Fix to avoid remote console looping in Multi-Command mode (Fix to issue #50). --- sim_console.c | 1 + 1 file changed, 1 insertion(+) diff --git a/sim_console.c b/sim_console.c index df573adc..e18bacf1 100644 --- a/sim_console.c +++ b/sim_console.c @@ -659,6 +659,7 @@ for (i=(was_stepping ? sim_rem_step_line : 0); printf ("Remote Console Command from %s> %s\r\n", lp->ipad, sim_rem_buf[i]); if (sim_log) fprintf (sim_log, "Remote Console Command from %s> %s\n", lp->ipad, sim_rem_buf[i]); + got_command = FALSE; if (strlen(sim_rem_buf[i]) >= sizeof(cbuf)) { printf ("\nLine too long. Ignored. Continuing Simulator execution\n"); if (sim_log) From d86680013b27b8cef5e80e69bf03609acb0a224b Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Mon, 13 May 2013 08:13:27 -0700 Subject: [PATCH 22/23] Enhanced remote console to display the simulator's prompt string in the remote console sessions and changed startup message to more clearly describe how to get into multiple command mode. --- sim_console.c | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) diff --git a/sim_console.c b/sim_console.c index e18bacf1..6f072a3d 100644 --- a/sim_console.c +++ b/sim_console.c @@ -416,13 +416,21 @@ int32 c; c = tmxr_poll_conn (&sim_rem_con_tmxr); if (c >= 0) { /* poll connect */ TMLN *lp = &sim_rem_con_tmxr.ldsc[c]; + char wru_name[8]; sim_activate_after(uptr+1, 1000000); /* start data poll after 100ms */ lp->rcve = 1; /* rcv enabled */ sim_rem_buf_ptr[c] = 0; /* start with empty command buffer */ + if (isprint(sim_int_char&0xFF)) + sprintf(wru_name, "'%c'", sim_int_char&0xFF); + else + if (sim_int_char <= 26) + sprintf(wru_name, "^%c", '@' + (sim_int_char&0xFF)); + else + sprintf(wru_name, "'\\%03o'", sim_int_char&0xFF); tmxr_linemsgf (lp, "%s Remote Console\r\n" - "Enter single commands or to enter multiple command mode enter the WRU character\r\n" - "Simulator Running...", sim_name); + "Enter single commands or to enter multiple command mode enter the %s character\r\n" + "Simulator Running...", sim_name, wru_name); tmxr_send_buffered_data (lp); /* flush buffered data */ } sim_activate_after(uptr, 1000000); /* check again in 1 second */ @@ -572,7 +580,7 @@ for (i=(was_stepping ? sim_rem_step_line : 0); } else { sim_rem_single_mode[i] = TRUE; - tmxr_linemsg (lp, "\r\nsim> "); + tmxr_linemsgf (lp, "\r\n%s", sim_prompt); tmxr_send_buffered_data (lp); /* flush any buffered data */ } } @@ -581,7 +589,7 @@ for (i=(was_stepping ? sim_rem_step_line : 0); while (1) { if (!sim_rem_single_mode[i]) { read_start_time = sim_os_msec(); - tmxr_linemsg (lp, "sim> "); + tmxr_linemsg (lp, sim_prompt); tmxr_send_buffered_data (lp); /* flush any buffered data */ } do { @@ -740,8 +748,14 @@ for (i=(was_stepping ? sim_rem_step_line : 0); sim_fseeko (sim_log, cmd_log_start, SEEK_SET); cbuf[sizeof(cbuf)-1] = '\0'; while (fgets (cbuf, sizeof(cbuf)-1, sim_log)) { + int32 unwritten; + tmxr_linemsgf (lp, "%s", cbuf); - tmxr_send_buffered_data (lp); + do { + unwritten = tmxr_send_buffered_data (lp); + if (unwritten == lp->txbsz) + sim_os_ms_sleep (100); + } while (unwritten == lp->txbsz); } if ((cmdp && (cmdp->action == &x_continue_cmd)) || (sim_rem_single_mode[i])) { From 3448edf608d6dfa562e20b5e9ba7cca6bd755a2d Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Mon, 13 May 2013 08:16:25 -0700 Subject: [PATCH 23/23] enhanced the relatively new tmxr_linemsgf api to dynamically flush a completely full transmit buffer as needed. --- sim_tmxr.c | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/sim_tmxr.c b/sim_tmxr.c index 25536e53..a368d87b 100644 --- a/sim_tmxr.c +++ b/sim_tmxr.c @@ -3104,11 +3104,13 @@ while (1) { /* format passed string, arg for (i = 0; i < len; ++i) { if ('\n' == buf[i]) { - tmxr_putc_ln (lp, '\r'); - tmxr_putc_ln (lp, buf[i]); + while (SCPE_STALL == tmxr_putc_ln (lp, '\r')) + if (lp->txbsz == tmxr_send_buffered_data (lp)) + sim_os_ms_sleep (10); } - else - tmxr_putc_ln (lp, buf[i]); + while (SCPE_STALL == tmxr_putc_ln (lp, buf[i])) + if (lp->txbsz == tmxr_send_buffered_data (lp)) + sim_os_ms_sleep (10); } if (buf != stackbuf) free (buf);