From 454b706a11c19a13476acbe8a49905f1571cfa9b Mon Sep 17 00:00:00 2001 From: Mark Pizzolato Date: Fri, 15 Apr 2011 08:43:07 -0700 Subject: [PATCH] Added Buffered Console Capabilities MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit A key capability needed to support simulators running with background execution is the ability to have a Telnet connected console which isn’t continuously connected. This feature is called a Buffered Telnet console. Absolutely nothing changes for someone not interested in using the feature. However, if someone is interested in the feature, the following SCP commands are available: sim> SET CONSOLE TELNET=port ! Unchanged sim> SET CONSOLE TELNET=BUFFERED{=buffersize} ! buffersize defaults to 32K sim> SET CONSOLE TELNET=UNBUFFERED ! restores normal behavior sim> SET CONSOLE TELNET=NOBUFFERED ! synonym for UNBUFFERED sim> SET CONSOLE TELNET=LOG=logdestination ! enables logging of Telnet session traffic (potentially separate from destination specified by SET CONSOLE LOG=conlogdest) sim> SET CONSOLE TELNET=NOLOG ! turns off specific logging of Telnet session sim> SET CONSOLE TELNET=LOG=conlogdest ! turns on logging all console traffic (unless the telnet session logging is also specified). The logdestinations for any of the logging/debugging commands can now be: LOG, DEBUG, STDOUT, STDERR or any file specification (caps are not required, but these names are reserved to indicate the current destination of the specified file handles (sim_log, sim_deb, stdout, stderr). When a Console Telnet session is Buffered, a simulator will start (via BOOT CPU or whatever is appropriate for a particular simulator) without needing to have an active telnet connection. When a Telnet connection comes along for the telnet port, the contents of the saved buffer (which wraps on overflow) are presented on the telnet session as output before session traffic. This allows the connecting telnet client to see what happened before he connected since the likely reason he might be connecting to the console of a background simulator is to troubleshoot unusual behavior. The current structure has the optional ability to log the Telnet session separately from the simulator output (i.e. ini file command execution) is potentially useful when you need to review what the simulator may have output which may be difficult to find in and amongst the possibly verbose Operating system console output. If someone doesn’t use “SET CONSOLE TELNET=LOG=logdestination”, then the original strategy of logging all output to the target specified by “SET CONSOLE LOG=logdestination” is preserved. Looking at the isolated console output might be more interesting if/when control flow scp commands are ever implemented (ON,GOTO, RETURN, etc.) --- scp.c | 3 +- sim_console.c | 412 +++++++++++++++++++++++++++++++++++++++++--------- sim_console.h | 10 ++ sim_defs.h | 9 ++ sim_tmxr.c | 225 ++++++++++++++++++++------- sim_tmxr.h | 9 +- 6 files changed, 540 insertions(+), 128 deletions(-) diff --git a/scp.c b/scp.c index f64196a2..02d1c02e 100644 --- a/scp.c +++ b/scp.c @@ -377,9 +377,10 @@ static uint32 sim_rtime; static int32 noqueue_time; volatile int32 stop_cpu = 0; t_value *sim_eval = NULL; -int32 sim_deb_close = 0; /* 1 = close debug */ FILE *sim_log = NULL; /* log file */ +FILEREF *sim_log_ref = NULL; /* log file file reference */ FILE *sim_deb = NULL; /* debug file */ +FILEREF *sim_deb_ref = NULL; /* debug file file reference */ static SCHTAB sim_stab; static UNIT sim_step_unit = { UDATA (&step_svc, 0, 0) }; diff --git a/sim_console.c b/sim_console.c index 497c8e6d..045eb41c 100644 --- a/sim_console.c +++ b/sim_console.c @@ -23,6 +23,27 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 20-Jan-11 MP Fixed support for BREAK key on Windows to account + for/ignore other keyboard Meta characters. + 18-Jan-11 MP Added log file reference count support + 17-Jan-11 MP Added support for a "Buffered" behaviors which include: + - If Buffering is enabled and Telnet is enabled, a + telnet connection is not required for simulator + operation (instruction execution). + - If Buffering is enabled, all console output is + written to the buffer at all times (deleting the + oldest buffer contents on overflow). + - when a connection is established on the console + telnet port, the whole contents of the Buffer is + presented on the telnet session and connection + will then proceed as if the connection had always + been there. + This concept allows a simulator to run in the background + and when needed a console session to be established. + The "when needed" case usually will be interested in + what already happened before looking to address what + to do, hence the buffer contents being presented. + 28-Dec-10 MP Added support for BREAK key on Windows 30-Sep-06 RMS Fixed non-printable characters in KSR mode 22-Jun-06 RMS Implemented SET/SHOW PCHAR 31-May-06 JDB Fixed bug if SET CONSOLE DEBUG with no argument @@ -62,6 +83,12 @@ sim_putchar_s - output character to console, stall if congested sim_set_console - set console parameters sim_show_console - show console parameters + sim_set_cons_buff - set console buffered + sim_set_cons_unbuff -set console unbuffered + sim_set_cons_log - set console log + sim_set_cons_nolog - set console nolog + sim_show_cons_buff - show console buffered + sim_show_cons_log - show console log sim_tt_inpcvt - convert input character per mode sim_tt_outcvt - convert output character per mode @@ -104,8 +131,9 @@ TMLN sim_con_ldsc = { 0 }; /* console line descr */ TMXR sim_con_tmxr = { 1, 0, 0, &sim_con_ldsc }; /* console line mux */ extern volatile int32 stop_cpu; -extern int32 sim_quiet, sim_deb_close; +extern int32 sim_quiet; extern FILE *sim_log, *sim_deb; +extern FILEREF *sim_log_ref, *sim_deb_ref; extern DEVICE *sim_devices[]; /* Set/show data structures */ @@ -129,9 +157,19 @@ static SHTAB show_con_tab[] = { { "BRK", &sim_show_kmap, KMAP_BRK }, { "DEL", &sim_show_kmap, KMAP_DEL }, { "PCHAR", &sim_show_pchar, 0 }, - { "LOG", &sim_show_log, 0 }, + { "LOG", &sim_show_cons_log, 0 }, { "TELNET", &sim_show_telnet, 0 }, { "DEBUG", &sim_show_debug, 0 }, + { "BUFFERED", &sim_show_cons_buff, 0 }, + { NULL, NULL, 0 } + }; + +static CTAB set_con_telnet_tab[] = { + { "LOG", &sim_set_cons_log, 0 }, + { "NOLOG", &sim_set_cons_nolog, 0 }, + { "BUFFERED", &sim_set_cons_buff, 0 }, + { "NOBUFFERED", &sim_set_cons_unbuff, 0 }, + { "UNBUFFERED", &sim_set_cons_unbuff, 0 }, { NULL, NULL, 0 } }; @@ -261,6 +299,7 @@ return SCPE_OK; t_stat sim_set_logon (int32 flag, char *cptr) { char gbuf[CBUFSIZE]; +t_stat r; if ((cptr == NULL) || (*cptr == 0)) /* need arg */ return SCPE_2FARG; @@ -268,12 +307,14 @@ cptr = get_glyph_nc (cptr, gbuf, 0); /* get file name */ if (*cptr != 0) /* now eol? */ return SCPE_2MARG; sim_set_logoff (0, NULL); /* close cur log */ -sim_log = sim_fopen (gbuf, "a"); /* open log */ -if (sim_log == NULL) /* error? */ - return SCPE_OPENERR; +r = sim_open_logfile (gbuf, FALSE, &sim_log, &sim_log_ref); /* open log */ +if (r != SCPE_OK) /* error? */ + return r; if (!sim_quiet) - printf ("Logging to file \"%s\"\n", gbuf); -fprintf (sim_log, "Logging to file \"%s\"\n", gbuf); /* start of log */ + printf ("Logging to file \"%s\"\n", + sim_logfile_name (sim_log, sim_log_ref)); +fprintf (sim_log, "Logging to file \"%s\"\n", + sim_logfile_name (sim_log, sim_log_ref)); /* start of log */ return SCPE_OK; } @@ -287,8 +328,8 @@ if (sim_log == NULL) /* no log? */ return SCPE_OK; if (!sim_quiet) printf ("Log file closed\n"); -fprintf (sim_log, "Log file closed\n"); /* close log */ -fclose (sim_log); +fprintf (sim_log, "Log file closed\n"); +sim_close_logfile (&sim_log_ref); /* close log */ sim_log = NULL; return SCPE_OK; } @@ -300,8 +341,8 @@ t_stat sim_show_log (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) if (cptr && (*cptr != 0)) return SCPE_2MARG; if (sim_log) - fputs ("Logging enabled\n", st); -else fputs ("Logging disabled\n", st); + fprintf (st, "Logging enabled to %s\n", sim_logfile_name (sim_log, sim_log_ref)); +else fprintf (st, "Logging disabled\n"); return SCPE_OK; } @@ -309,34 +350,24 @@ return SCPE_OK; t_stat sim_set_debon (int32 flag, char *cptr) { -char *tptr, gbuf[CBUFSIZE]; +char gbuf[CBUFSIZE]; +t_stat r; -if ((cptr == NULL) || (*cptr == 0)) /* too few arguments? */ +if ((cptr == NULL) || (*cptr == 0)) /* need arg */ return SCPE_2FARG; -tptr = get_glyph (cptr, gbuf, 0); /* get file name */ -if (*tptr != 0) /* now eol? */ +cptr = get_glyph_nc (cptr, gbuf, 0); /* get file name */ +if (*cptr != 0) /* now eol? */ return SCPE_2MARG; -sim_set_deboff (0, NULL); /* close cur debug */ -if (strcmp (gbuf, "LOG") == 0) { /* debug to log? */ - if (sim_log == NULL) /* any log? */ - return SCPE_ARG; - sim_deb = sim_log; - } -else if (strcmp (gbuf, "STDOUT") == 0) /* debug to stdout? */ - sim_deb = stdout; -else if (strcmp (gbuf, "STDERR") == 0) /* debug to stderr? */ - sim_deb = stderr; -else { - cptr = get_glyph_nc (cptr, gbuf, 0); /* reparse */ - sim_deb = sim_fopen (gbuf, "a"); /* open debug */ - if (sim_deb == NULL) /* error? */ - return SCPE_OPENERR; - sim_deb_close = 1; /* need close */ - } +r = sim_open_logfile (gbuf, FALSE, &sim_deb, &sim_deb_ref); + +if (r != SCPE_OK) + return r; if (!sim_quiet) - printf ("Debug output to \"%s\"\n", gbuf); + printf ("Debug output to \"%s\"\n", + sim_logfile_name (sim_deb, sim_deb_ref)); if (sim_log) - fprintf (sim_log, "Debug output to \"%s\"\n", gbuf); + fprintf (sim_log, "Debug output to \"%s\"\n", + sim_logfile_name (sim_deb, sim_deb_ref)); return SCPE_OK; } @@ -344,18 +375,18 @@ return SCPE_OK; t_stat sim_set_deboff (int32 flag, char *cptr) { +t_stat r; + if (cptr && (*cptr != 0)) /* now eol? */ return SCPE_2MARG; if (sim_deb == NULL) /* no log? */ return SCPE_OK; +r = sim_close_logfile (&sim_deb_ref); +sim_deb = NULL; if (!sim_quiet) printf ("Debug output disabled\n"); if (sim_log) fprintf (sim_log, "Debug output disabled\n"); -if (sim_deb_close) /* close if needed */ - fclose (sim_deb); -sim_deb_close = 0; -sim_deb = NULL; return SCPE_OK; } @@ -366,20 +397,41 @@ t_stat sim_show_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cpt if (cptr && (*cptr != 0)) return SCPE_2MARG; if (sim_deb) - fputs ("Debug output enabled\n", st); -else fputs ("Debug output disabled\n", st); + fprintf (st, "Debug output enabled\n", sim_logfile_name (sim_deb, sim_deb_ref)); +else fprintf (st, "Debug output disabled\n"); return SCPE_OK; } -/* Set console to Telnet port */ +/* SET CONSOLE command */ -t_stat sim_set_telnet (int32 flg, char *cptr) +/* Set console to Telnet port (and parameters) */ + +t_stat sim_set_telnet (int32 flag, char *cptr) { -if ((cptr == NULL) || (*cptr == 0)) /* too few arguments? */ +char *cvptr, gbuf[CBUFSIZE]; +CTAB *ctptr; +t_stat r; + +if ((cptr == NULL) || (*cptr == 0)) return SCPE_2FARG; -if (sim_con_tmxr.master) /* already open? */ - return SCPE_ALATT; -return tmxr_open_master (&sim_con_tmxr, cptr); /* open master socket */ +while (*cptr != 0) { /* do all mods */ + cptr = get_glyph_nc (cptr, gbuf, ','); /* get modifier */ + if (cvptr = strchr (gbuf, '=')) /* = value? */ + *cvptr++ = 0; + get_glyph (gbuf, gbuf, 0); /* modifier to UC */ + if (isdigit (*gbuf)) { + if (sim_con_tmxr.master) return SCPE_ALATT; /* already open? */ + return tmxr_open_master (&sim_con_tmxr, gbuf); /* open master socket */ + } + else + if (ctptr = find_ctab (set_con_telnet_tab, gbuf)) { /* match? */ + r = ctptr->action (ctptr->arg, cvptr); /* do the rest */ + if (r != SCPE_OK) + return r; + } + else return SCPE_NOPARAM; + } +return SCPE_OK; } /* Close console Telnet port */ @@ -401,17 +453,167 @@ if (cptr && (*cptr != 0)) return SCPE_2MARG; if (sim_con_tmxr.master == 0) fprintf (st, "Connected to console window\n"); -else if (sim_con_ldsc.conn == 0) - fprintf (st, "Listening on port %d\n", sim_con_tmxr.port); else { - fprintf (st, "Listening on port %d, connected to socket %d\n", - sim_con_tmxr.port, sim_con_ldsc.conn); - tmxr_fconns (st, &sim_con_ldsc, -1); + if (sim_con_ldsc.conn == 0) + fprintf (st, "Listening on port %d\n", sim_con_tmxr.port); + else { + fprintf (st, "Listening on port %d, connected to socket %d\n", + sim_con_tmxr.port, sim_con_ldsc.conn); + tmxr_fconns (st, &sim_con_ldsc, -1); + } tmxr_fstats (st, &sim_con_ldsc, -1); } return SCPE_OK; } + +/* Set console to Buffering */ + +t_stat sim_set_cons_buff (int32 flg, char *cptr) +{ +char cmdbuf[CBUFSIZE]; + +sprintf(cmdbuf, "BUFFERED%c%s", cptr ? '=' : '\0', cptr ? cptr : ""); +return tmxr_open_master (&sim_con_tmxr, cmdbuf); /* open master socket */ +} + +/* Set console to NoBuffering */ + +t_stat sim_set_cons_unbuff (int32 flg, char *cptr) +{ +char cmdbuf[CBUFSIZE]; + +sprintf(cmdbuf, "UNBUFFERED%c%s", cptr ? '=' : '\0', cptr ? cptr : ""); +return tmxr_open_master (&sim_con_tmxr, cmdbuf); /* open master socket */ +} + +/* Set console to Logging */ + +t_stat sim_set_cons_log (int32 flg, char *cptr) +{ +char cmdbuf[CBUFSIZE]; + +sprintf(cmdbuf, "LOG%c%s", cptr ? '=' : '\0', cptr ? cptr : ""); +return tmxr_open_master (&sim_con_tmxr, cmdbuf); /* open master socket */ +} + +/* Set console to NoLogging */ + +t_stat sim_set_cons_nolog (int32 flg, char *cptr) +{ +char cmdbuf[CBUFSIZE]; + +sprintf(cmdbuf, "NOLOG%c%s", cptr ? '=' : '\0', cptr ? cptr : ""); +return tmxr_open_master (&sim_con_tmxr, cmdbuf); /* open master socket */ +} + +t_stat sim_show_cons_log (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, char *cptr) +{ +if (cptr && (*cptr != 0)) + return SCPE_2MARG; +if (sim_con_tmxr.ldsc->txlog) + fprintf (st, "Log File being written to %s\n", sim_con_tmxr.ldsc->txlogname); +else + fprintf (st, "No Logging\n"); +return SCPE_OK; +} + +t_stat sim_show_cons_buff (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, char *cptr) +{ +if (cptr && (*cptr != 0)) + return SCPE_2MARG; +if (!sim_con_tmxr.buffered) + fprintf (st, "Unbuffered\n"); +else + fprintf (st, "Buffer Size = %d\n", sim_con_tmxr.buffered); +return SCPE_OK; +} + + +/* Log File Open/Close/Show Support */ + +/* Open log file */ + +t_stat sim_open_logfile (char *filename, t_bool binary, FILE **pf, FILEREF **pref) +{ +char *tptr, gbuf[CBUFSIZE]; + +*pref = NULL; +if ((filename == NULL) || (*filename == 0)) /* too few arguments? */ + return SCPE_2FARG; +tptr = get_glyph (filename, gbuf, 0); +if (*tptr != 0) /* now eol? */ + return SCPE_2MARG; +sim_close_logfile (pref); +*pf = NULL; +if (strcmp (gbuf, "LOG") == 0) { /* output to log? */ + if (sim_log == NULL) /* any log? */ + return SCPE_ARG; + *pf = sim_log; + *pref = sim_log_ref; + if (*pref) + ++(*pref)->refcount; + } +else if (strcmp (gbuf, "DEBUG") == 0) { /* output to debug? */ + if (sim_debug == NULL) /* any debug? */ + return SCPE_ARG; + *pf = sim_deb; + *pref = sim_deb_ref; + if (*pref) + ++(*pref)->refcount; + } +else if (strcmp (gbuf, "STDOUT") == 0) /* output to stdout? */ + *pf = stdout; +else if (strcmp (gbuf, "STDERR") == 0) /* output to stderr? */ + *pf = stderr; +else { + *pref = calloc (1, sizeof(**pref)); + if (!*pref) + return SCPE_MEM; + get_glyph_nc (filename, gbuf, 0); /* reparse */ + strncpy ((*pref)->name, gbuf, sizeof((*pref)->name)-1); + *pf = sim_fopen (gbuf, (binary ? "ab" : "a")); /* open file */ + if (*pf == NULL) { /* error? */ + free (*pref); + *pref = NULL; + return SCPE_OPENERR; + } + (*pref)->file = *pf; + (*pref)->refcount = 1; /* need close */ + } +return SCPE_OK; +} + +/* Close log file */ + +t_stat sim_close_logfile (FILEREF **pref) +{ +if (NULL == *pref) + return SCPE_OK; +(*pref)->refcount = (*pref)->refcount - 1; +if ((*pref)->refcount > 0) + return SCPE_OK; +fclose ((*pref)->file); +free (*pref); +*pref = NULL; +return SCPE_OK; +} + +/* Show logfile support routine */ + +const char *sim_logfile_name (FILE *st, FILEREF *ref) +{ +if (!st) + return ""; +if (st == stdout) + return "STDOUT"; +if (st == stderr) + return "STDERR"; +if (!ref) + return ""; +return ref->name; +} + /* Check connection before executing */ t_stat sim_check_console (int32 sec) @@ -420,10 +622,15 @@ int32 c, i; if (sim_con_tmxr.master == 0) /* not Telnet? done */ return SCPE_OK; -if (sim_con_ldsc.conn) { /* connected? */ +if (sim_con_ldsc.conn || sim_con_ldsc.txbfd) { /* connected or buffered ? */ tmxr_poll_rx (&sim_con_tmxr); /* poll (check disconn) */ - if (sim_con_ldsc.conn) /* still connected? */ + if (sim_con_ldsc.conn || sim_con_ldsc.txbfd) { /* still connected? */ + if (!sim_con_ldsc.conn) { + printf ("Running with Buffered Console\n"); /* print transition */ + fflush (stdout); + } return SCPE_OK; + } } for (i = 0; i < sec; i++) { /* loop */ if (tmxr_poll_conn (&sim_con_tmxr) >= 0) { /* poll connect */ @@ -455,8 +662,14 @@ int32 c; c = sim_os_poll_kbd (); /* get character */ if ((c == SCPE_STOP) || (sim_con_tmxr.master == 0)) /* ^E or not Telnet? */ return c; /* in-window */ -if (sim_con_ldsc.conn == 0) /* no Telnet conn? */ - return SCPE_LOST; +if (sim_con_ldsc.conn == 0) { /* no Telnet conn? */ + if (!sim_con_ldsc.txbfd) /* unbuffered? */ + return SCPE_LOST; /* connection lost */ + if (tmxr_poll_conn (&sim_con_tmxr) >= 0) /* poll connect */ + sim_con_ldsc.rcve = 1; /* rcv enabled */ + else /* fall through to poll reception */ + return SCPE_OK; /* unconnected and buffered - nothing to receive */ + } tmxr_poll_rx (&sim_con_tmxr); /* poll for input */ if (c = tmxr_getc_ln (&sim_con_ldsc)) /* any char? */ return (c & (SCPE_BREAK | 0377)) | SCPE_KFLAG; @@ -467,12 +680,19 @@ return SCPE_OK; t_stat sim_putchar (int32 c) { -if (sim_log) /* log file? */ - fputc (c, sim_log); -if (sim_con_tmxr.master == 0) /* not Telnet? */ +if (sim_con_tmxr.master == 0) { /* not Telnet? */ + if (sim_log) /* log file? */ + fputc (c, sim_log); return sim_os_putchar (c); /* in-window version */ -if (sim_con_ldsc.conn == 0) /* no Telnet conn? */ - return SCPE_LOST; + } +if (sim_log && !sim_con_ldsc.txlog) /* log file, but no line log? */ + fputc (c, sim_log); +if (sim_con_ldsc.conn == 0) { /* no Telnet conn? */ + if (!sim_con_ldsc.txbfd) /* unbuffered? */ + return SCPE_LOST; /* connection lost */ + if (tmxr_poll_conn (&sim_con_tmxr) >= 0) /* poll connect */ + sim_con_ldsc.rcve = 1; /* rcv enabled */ + } tmxr_putc_ln (&sim_con_ldsc, c); /* output char */ tmxr_poll_tx (&sim_con_tmxr); /* poll xmt */ return SCPE_OK; @@ -485,8 +705,12 @@ t_stat r; if (sim_log) fputc (c, sim_log); /* log file? */ if (sim_con_tmxr.master == 0) /* not Telnet? */ return sim_os_putchar (c); /* in-window version */ -if (sim_con_ldsc.conn == 0) /* no Telnet conn? */ - return SCPE_LOST; +if (sim_con_ldsc.conn == 0) { /* no Telnet conn? */ + if (!sim_con_ldsc.txbfd) /* non-buffered Telnet conn? */ + return SCPE_LOST; /* lost */ + if (tmxr_poll_conn (&sim_con_tmxr) >= 0) /* poll connect */ + sim_con_ldsc.rcve = 1; /* rcv enabled */ + } if (sim_con_ldsc.xmte == 0) /* xmt disabled? */ r = SCPE_STALL; else r = tmxr_putc_ln (&sim_con_ldsc, c); /* no, Telnet output */ @@ -662,17 +886,42 @@ return SCPE_OK; #elif defined (_WIN32) -#include #include #include #include #define RAW_MODE 0 static HANDLE std_input; +static HANDLE std_output; static DWORD saved_mode; +static BOOL WINAPI +ControlHandler(DWORD dwCtrlType) + { + DWORD Mode; + extern void int_handler (int sig); + + switch (dwCtrlType) + { + case CTRL_BREAK_EVENT: // Use CTRL-Break or CTRL-C to simulate + case CTRL_C_EVENT: // SERVICE_CONTROL_STOP in debug mode + int_handler(0); + return TRUE; + case CTRL_CLOSE_EVENT: // Window is Closing + case CTRL_LOGOFF_EVENT: // User is logging off + if (!GetConsoleMode(GetStdHandle(STD_INPUT_HANDLE), &Mode)) + return TRUE; // Not our User, so ignore + case CTRL_SHUTDOWN_EVENT: // System is shutting down + int_handler(0); + return TRUE; + } + return FALSE; + } + t_stat sim_ttinit (void) { +SetConsoleCtrlHandler( ControlHandler, TRUE ); std_input = GetStdHandle (STD_INPUT_HANDLE); +std_output = GetStdHandle (STD_OUTPUT_HANDLE); if ((std_input == INVALID_HANDLE_VALUE) || !GetConsoleMode (std_input, &saved_mode)) return SCPE_TTYERR; @@ -710,24 +959,49 @@ return SCPE_OK; t_stat sim_os_poll_kbd (void) { -int c; +int c = -1; +DWORD nkbevents, nkbevent; +INPUT_RECORD rec; +extern int32 sim_switches; -if (!_kbhit ()) - return SCPE_OK; -c = _getch (); +if (!GetNumberOfConsoleInputEvents(std_input, &nkbevents)) + return SCPE_TTYERR; +while (c == -1) { + if (0 == nkbevents) + return SCPE_OK; + if (!ReadConsoleInput(std_input, &rec, 1, &nkbevent)) + return SCPE_TTYERR; + if (0 == nkbevent) + return SCPE_OK; + --nkbevents; + if (rec.EventType == KEY_EVENT) { + if (rec.Event.KeyEvent.bKeyDown) { + if (0 == rec.Event.KeyEvent.uChar.UnicodeChar) { /* Special Character/Keys? */ + if (rec.Event.KeyEvent.wVirtualKeyCode == VK_PAUSE) /* Pause/Break Key */ + c = sim_brk_char | SCPE_BREAK; + else + if (rec.Event.KeyEvent.wVirtualKeyCode == '2') /* ^@ */ + c = 0; /* return NUL */ + } else + c = rec.Event.KeyEvent.uChar.AsciiChar; + } + } + } if ((c & 0177) == sim_del_char) c = 0177; if ((c & 0177) == sim_int_char) return SCPE_STOP; -if (sim_brk_char && ((c & 0177) == sim_brk_char)) +if ((sim_brk_char && ((c & 0177) == sim_brk_char)) || (c & SCPE_BREAK)) return SCPE_BREAK; return c | SCPE_KFLAG; } t_stat sim_os_putchar (int32 c) { +DWORD unused; + if (c != 0177) - _putch (c); + WriteConsoleA(std_output, &c, 1, &unused, NULL); return SCPE_OK; } diff --git a/sim_console.h b/sim_console.h index 0b4818a7..6c64dd31 100644 --- a/sim_console.h +++ b/sim_console.h @@ -23,6 +23,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 17-Jan-11 MP Added buffered line capabilities 22-Jun-06 RMS Implemented SET/SHOW PCHAR 22-Nov-05 RMS Added central input/output conversion support 05-Nov-04 RMS Moved SET/SHOW DEBUG under CONSOLE hierarchy @@ -57,6 +58,10 @@ t_stat sim_set_notelnet (int32 flag, char *cptr); t_stat sim_set_logon (int32 flag, char *cptr); t_stat sim_set_logoff (int32 flag, char *cptr); t_stat sim_set_debon (int32 flag, char *cptr); +t_stat sim_set_cons_buff (int32 flg, char *cptr); +t_stat sim_set_cons_unbuff (int32 flg, char *cptr); +t_stat sim_set_cons_log (int32 flg, char *cptr); +t_stat sim_set_cons_nolog (int32 flg, char *cptr); t_stat sim_set_deboff (int32 flag, char *cptr); t_stat sim_set_pchar (int32 flag, char *cptr); t_stat sim_show_console (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); @@ -65,7 +70,12 @@ t_stat sim_show_telnet (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cp t_stat sim_show_log (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat sim_show_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat sim_show_pchar (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat sim_show_cons_buff (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +t_stat sim_show_cons_log (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat sim_check_console (int32 sec); +t_stat sim_open_logfile (char *filename, t_bool binary, FILE **pf, FILEREF **pref); +t_stat sim_close_logfile (FILEREF **pref); +const char *sim_logfile_name (FILE *st, FILEREF *ref); t_stat sim_poll_kbd (void); t_stat sim_putchar (int32 c); t_stat sim_putchar_s (int32 c); diff --git a/sim_defs.h b/sim_defs.h index 66f67d44..90f89f8b 100644 --- a/sim_defs.h +++ b/sim_defs.h @@ -23,6 +23,7 @@ used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from Robert M Supnik. + 18-Jan-11 MP Added log file reference count support 21-Jul-08 RMS Removed inlining support 28-May-08 RMS Added inlining support 28-Jun-07 RMS Added IA64 VMS support (from Norm Lastovica) @@ -484,6 +485,13 @@ struct sim_debtab { #define DEBUG_PRI(d,m) (sim_deb && (d.dctrl & (m))) #define DEBUG_PRJ(d,m) (sim_deb && (d->dctrl & (m))) +/* File Reference */ +struct sim_fileref { + char name[CBUFSIZE]; /* file name */ + FILE *file; /* file handle */ + int32 refcount; /* reference count */ + }; + /* The following macros define structure contents */ #define UDATA(act,fl,cap) NULL,act,NULL,NULL,NULL,0,0,(fl),(cap),0,0 @@ -520,6 +528,7 @@ typedef struct sim_mtab MTAB; typedef struct sim_schtab SCHTAB; typedef struct sim_brktab BRKTAB; typedef struct sim_debtab DEBTAB; +typedef struct sim_fileref FILEREF; /* Function prototypes */ diff --git a/sim_tmxr.c b/sim_tmxr.c index b7ff0fdd..0e35b1b0 100644 --- a/sim_tmxr.c +++ b/sim_tmxr.c @@ -26,6 +26,7 @@ Based on the original DZ11 simulator by Thord Nilson, as updated by Arthur Krewat. + 17-Jan-11 MP Added Buffered line capabilities 20-Nov-08 RMS Added three new standardized SHOW routines 30-Sep-08 JDB Reverted tmxr_find_ldsc to original implementation 27-May-08 JDB Added line connection order to tmxr_poll_conn, @@ -137,8 +138,12 @@ int32 tmxr_poll_conn (TMXR *mp) SOCKET newsock; TMLN *lp; int32 *op; -int32 i, j; +int32 i, j, psave; uint32 ipaddr; +char cmsg[80]; +char dmsg[80] = ""; +char lmsg[80] = ""; +char msgbuf[256]; static char mantra[] = { TN_IAC, TN_WILL, TN_LINE, TN_IAC, TN_WILL, TN_SGA, @@ -171,34 +176,38 @@ if (newsock != INVALID_SOCKET) { /* got a live one? */ lp = mp->ldsc + i; /* get line desc */ lp->conn = newsock; /* record connection */ lp->ipad = ipaddr; /* ip address */ + sim_write_sock (newsock, mantra, sizeof(mantra)); + sprintf (cmsg, "\n\r\nConnected to the %s simulator ", sim_name); + + if (mp->dptr) { /* device defined? */ + sprintf (dmsg, "%s device", /* report device name */ + sim_dname (mp->dptr)); + + if (mp->lines > 1) /* more than one line? */ + sprintf (lmsg, ", line %d", i); /* report the line number */ + } + + sprintf (msgbuf, "%s%s%s\r\n\n", cmsg, dmsg, lmsg); lp->cnms = sim_os_msec (); /* time of conn */ - lp->rxbpr = lp->rxbpi = 0; /* init buf pointers */ - lp->txbpr = lp->txbpi = 0; - lp->rxcnt = lp->txcnt = 0; /* init counters */ + if (!mp->buffered) { + lp->txbpi = 0; /* init buf pointers */ + lp->txbpr = lp->txbsz - strlen (msgbuf); + lp->rxcnt = lp->txcnt = lp->txdrp = 0; /* init counters */ + } + else + if (lp->txcnt > lp->txbsz) + lp->txbpr = (lp->txbpi + 1) % lp->txbsz; + else + lp->txbpr = lp->txbsz - strlen (msgbuf); lp->tsta = 0; /* init telnet state */ lp->xmte = 1; /* enable transmit */ lp->dstb = 0; /* default bin mode */ - sim_write_sock (newsock, mantra, 15); - tmxr_linemsg (lp, "\n\r\nConnected to the "); - tmxr_linemsg (lp, sim_name); - tmxr_linemsg (lp, " simulator "); - - if (mp->dptr) { /* device defined? */ - tmxr_linemsg (lp, sim_dname (mp->dptr)); /* report device name */ - tmxr_linemsg (lp, " device"); - - if (mp->lines > 1) { /* more than one line? */ - char line[20]; - - tmxr_linemsg (lp, ", line "); /* report the line number */ - sprintf (line, "%i", i); - tmxr_linemsg (lp, line); - } - } - - tmxr_linemsg (lp, "\r\n\n"); - + psave = lp->txbpi; /* save insertion pointer */ + lp->txbpi = lp->txbpr; /* insert connection message */ + tmxr_linemsg (lp, msgbuf); /* beginning of buffer */ + lp->txbpi = psave; /* restore insertion pointer */ tmxr_poll_tx (mp); /* flush output */ + lp->txcnt -= strlen (msgbuf); /* adjust statistics */ return i; } } /* end if newsock */ @@ -215,7 +224,8 @@ tmxr_send_buffered_data (lp); /* send buffered data */ sim_close_sock (lp->conn, 0); /* reset conn */ lp->conn = lp->tsta = 0; /* reset state */ lp->rxbpr = lp->rxbpi = 0; -lp->txbpr = lp->txbpi = 0; +if (!lp->txbfd) + lp->txbpr = lp->txbpi = 0; lp->xmte = 1; lp->dstb = 0; return; @@ -369,7 +379,7 @@ return; int32 tmxr_rqln (TMLN *lp) { -return (lp->rxbpi - lp->rxbpr); +return (lp->rxbpi - lp->rxbpr + ((lp->rxbpi < lp->rxbpr)? TMXR_MAXBUF: 0)); } /* Remove character p (and matching status) from line l input buffer */ @@ -397,24 +407,29 @@ t_stat tmxr_putc_ln (TMLN *lp, int32 chr) { if (lp->txlog) /* log if available */ fputc (chr, lp->txlog); -if (lp->conn == 0) /* no conn? lost */ - return SCPE_LOST; -if (tmxr_tqln (lp) < (TMXR_MAXBUF - 1)) { /* room for char (+ IAC)? */ - lp->txb[lp->txbpi] = (char) chr; /* buffer char */ - lp->txbpi = lp->txbpi + 1; /* adv pointer */ - if (lp->txbpi >= TMXR_MAXBUF) /* wrap? */ - lp->txbpi = 0; - if ((char) chr == TN_IAC) { /* IAC? */ - lp->txb[lp->txbpi] = (char) chr; /* IAC + IAC */ - lp->txbpi = lp->txbpi + 1; /* adv pointer */ - if (lp->txbpi >= TMXR_MAXBUF) /* wrap? */ - lp->txbpi = 0; +if ((lp->conn == 0) && (!lp->txbfd)) /* no conn & not buffered? */ + if (lp->txlog) /* if it was logged, we got it */ + return SCPE_OK; + else { + ++lp->txdrp; /* lost */ + return SCPE_LOST; } - if (tmxr_tqln (lp) > (TMXR_MAXBUF - TMXR_GUARD)) /* near full? */ +#define TXBUF_AVAIL(lp) (lp->txbsz - tmxr_tqln (lp)) +#define TXBUF_CHAR(lp, c) { \ + lp->txb[lp->txbpi++] = (char)(c); \ + lp->txbpi %= lp->txbsz; \ + if (lp->txbpi == lp->txbpr) \ + lp->txbpr = (1+lp->txbpr)%lp->txbsz, ++lp->txdrp; \ + } +if ((lp->txbfd) || (TXBUF_AVAIL(lp) > 1)) { /* room for char (+ IAC)? */ + if (TN_IAC == (char) chr) /* char == IAC ? */ + TXBUF_CHAR (lp, TN_IAC); /* stuff extra IAC char */ + TXBUF_CHAR (lp, chr); /* buffer char & adv pointer */ + if ((!lp->txbfd) && (TXBUF_AVAIL (lp) <= TMXR_GUARD))/* near full? */ lp->xmte = 0; /* disable line */ return SCPE_OK; /* char sent */ } -lp->xmte = 0; /* no room, dsbl line */ +++lp->txdrp; lp->xmte = 0; /* no room, dsbl line */ return SCPE_STALL; /* char not sent */ } @@ -460,10 +475,10 @@ if (nbytes) { /* >0? write */ sbytes = sim_write_sock (lp->conn, /* write all data */ &(lp->txb[lp->txbpr]), nbytes); else sbytes = sim_write_sock (lp->conn, /* write to end buf */ - &(lp->txb[lp->txbpr]), TMXR_MAXBUF - lp->txbpr); + &(lp->txb[lp->txbpr]), lp->txbsz - lp->txbpr); if (sbytes != SOCKET_ERROR) { /* ok? */ lp->txbpr = (lp->txbpr + sbytes); /* update remove ptr */ - if (lp->txbpr >= TMXR_MAXBUF) /* wrap? */ + if (lp->txbpr >= lp->txbsz) /* wrap? */ lp->txbpr = 0; lp->txcnt = lp->txcnt + sbytes; /* update counts */ nbytes = nbytes - sbytes; @@ -472,7 +487,7 @@ if (nbytes) { /* >0? write */ sbytes = sim_write_sock (lp->conn, lp->txb, nbytes); if (sbytes != SOCKET_ERROR) { /* ok */ lp->txbpr = (lp->txbpr + sbytes); /* update remove ptr */ - if (lp->txbpr >= TMXR_MAXBUF) /* wrap? */ + if (lp->txbpr >= lp->txbsz) /* wrap? */ lp->txbpr = 0; lp->txcnt = lp->txcnt + sbytes; /* update counts */ nbytes = nbytes - sbytes; @@ -486,7 +501,7 @@ return nbytes; int32 tmxr_tqln (TMLN *lp) { -return (lp->txbpi - lp->txbpr + ((lp->txbpi < lp->txbpr)? TMXR_MAXBUF: 0)); +return (lp->txbpi - lp->txbpr + ((lp->txbpi < lp->txbpr)? lp->txbsz: 0)); } /* Open master socket */ @@ -498,6 +513,81 @@ SOCKET sock; TMLN *lp; t_stat r; +if (!isdigit(*cptr)) { + char gbuf[CBUFSIZE]; + cptr = get_glyph (cptr, gbuf, '='); + if (0 == MATCH_CMD (gbuf, "LOG")) { + if ((NULL == cptr) || ('\0' == *cptr)) + return SCPE_2FARG; + strncpy(mp->logfiletmpl, cptr, sizeof(mp->logfiletmpl)-1); + for (i = 0; i < mp->lines; i++) { + lp = mp->ldsc + i; + sim_close_logfile (&lp->txlogref); + lp->txlog = NULL; + lp->txlogname = realloc(lp->txlogname, CBUFSIZE); + if (mp->lines > 1) + sprintf(lp->txlogname, "%s_%d", mp->logfiletmpl, i); + else + strcpy(lp->txlogname, mp->logfiletmpl); + r = sim_open_logfile (lp->txlogname, TRUE, &lp->txlog, &lp->txlogref); + if (r == SCPE_OK) + setvbuf(lp->txlog, NULL, _IOFBF, 65536); + else { + free (lp->txlogname); + lp->txlogname = NULL; + break; + } + } + return r; + } + if ((0 == MATCH_CMD (gbuf, "NOBUFFERED")) || + (0 == MATCH_CMD (gbuf, "UNBUFFERED"))) { + if (mp->buffered) { + mp->buffered = 0; + for (i = 0; i < mp->lines; i++) { /* default line buffers */ + lp = mp->ldsc + i; + lp->txbsz = TMXR_MAXBUF; + lp->txb = (char *)realloc(lp->txb, lp->txbsz); + lp->txbfd = lp->txbpi = lp->txbpr = 0; + } + } + return SCPE_OK; + } + if (0 == MATCH_CMD (gbuf, "BUFFERED")) { + if ((NULL == cptr) || ('\0' == *cptr)) + mp->buffered = 32768; + else { + i = (int32) get_uint (cptr, 10, 1024*1024, &r); + if ((r != SCPE_OK) || (i == 0)) + return SCPE_ARG; + mp->buffered = i; + } + for (i = 0; i < mp->lines; i++) { /* initialize line buffers */ + lp = mp->ldsc + i; + lp->txbsz = mp->buffered; + lp->txbfd = 1; + lp->txb = (char *)realloc(lp->txb, lp->txbsz); + lp->txbpi = lp->txbpr = 0; + } + return SCPE_OK; + } + if (0 == MATCH_CMD (gbuf, "NOLOG")) { + if ((NULL != cptr) && ('\0' != *cptr)) + return SCPE_2MARG; + mp->logfiletmpl[0] = '\0'; + for (i = 0; i < mp->lines; i++) { /* close line logs */ + lp = mp->ldsc + i; + free(lp->txlogname); + lp->txlogname = NULL; + if (lp->txlog) { + sim_close_logfile (&lp->txlogref); + lp->txlog = NULL; + } + } + return SCPE_OK; + } + return SCPE_ARG; + } port = (int32) get_uint (cptr, 10, 65535, &r); /* get port */ if ((r != SCPE_OK) || (port == 0)) return SCPE_ARG; @@ -514,7 +604,12 @@ for (i = 0; i < mp->lines; i++) { /* initialize lines */ lp->conn = lp->tsta = 0; lp->rxbpi = lp->rxbpr = 0; lp->txbpi = lp->txbpr = 0; - lp->rxcnt = lp->txcnt = 0; + if (!mp->buffered) { + lp->txbfd = lp->txbpi = lp->txbpr = 0; + lp->txbsz = TMXR_MAXBUF; + lp->txb = (char *)realloc(lp->txb, lp->txbsz); + } + lp->rxcnt = lp->txcnt = lp->txdrp = 0; lp->xmte = 1; lp->dstb = 0; } @@ -527,8 +622,11 @@ t_stat tmxr_attach (TMXR *mp, UNIT *uptr, char *cptr) { char* tptr; t_stat r; +char pmsg[20], bmsg[32] = "", lmsg[64+PATH_MAX] = ""; -tptr = (char *) malloc (strlen (cptr) + 1); /* get string buf */ +tptr = (char *) malloc (strlen (cptr) + /* get string buf */ + sizeof(pmsg) + + sizeof(bmsg) + sizeof(lmsg)); if (tptr == NULL) /* no more mem? */ return SCPE_MEM; r = tmxr_open_master (mp, cptr); /* open master socket */ @@ -536,7 +634,12 @@ if (r != SCPE_OK) { /* error? */ free (tptr); /* release buf */ return SCPE_OPENERR; } -strcpy (tptr, cptr); /* copy port */ +sprintf (pmsg, "%d", mp->port); /* copy port */ +if (mp->buffered) + sprintf (bmsg, ", buffered=%d", mp->buffered); /* buffer info */ +if (mp->logfiletmpl[0]) + sprintf (lmsg, ", log=%s", mp->logfiletmpl); /* logfile info */ +sprintf (tptr, "%s%s%s", pmsg, bmsg, lmsg); /* assemble all */ uptr->filename = tptr; /* save */ uptr->flags = uptr->flags | UNIT_ATT; /* no more errors */ @@ -646,16 +749,24 @@ static const char *enab = "on"; static const char *dsab = "off"; if (ln >= 0) - fprintf (st, "line %d: ", ln); -if (lp->conn) { - fprintf (st, "input (%s) queued/total = %d/%d, ", + fprintf (st, "line %d:\b", ln); +if (!lp->conn) + fprintf (st, "line disconnected\n"); +if (lp->rxcnt) + fprintf (st, " input (%s) queued/total = %d/%d\n", (lp->rcve? enab: dsab), - lp->rxbpi - lp->rxbpr, lp->rxcnt); - fprintf (st, "output (%s) queued/total = %d/%d\n", + tmxr_rqln (lp), lp->rxcnt); +if (lp->txcnt || lp->txbpi) + fprintf (st, " output (%s) queued/total = %d/%d\n", (lp->xmte? enab: dsab), - lp->txbpi - lp->txbpr, lp->txcnt); - } -else fprintf (st, "line disconnected\n"); + tmxr_tqln (lp), lp->txcnt); +if (lp->txbfd) + fprintf (st, " output buffer size = %d\n", lp->txbsz); +if (lp->txcnt || lp->txbpi) + fprintf (st, " bytes in buffer = %d\n", + ((lp->txcnt > 0) && (lp->txcnt > lp->txbsz)) ? lp->txbsz : lp->txbpi); +if (lp->txdrp) + fprintf (st, " dropped = %d\n", lp->txdrp); return; } @@ -708,7 +819,7 @@ lp->txlogname = (char *) calloc (CBUFSIZE, sizeof (char)); /* alloc namebuf */ if (lp->txlogname == NULL) /* can't? */ return SCPE_MEM; strncpy (lp->txlogname, cptr, CBUFSIZE); /* save file name */ -lp->txlog = fopen (cptr, "ab"); /* open log */ +sim_open_logfile (cptr, TRUE, &lp->txlog, &lp->txlogref);/* open log */ if (lp->txlog == NULL) { /* error? */ free (lp->txlogname); /* free buffer */ return SCPE_OPENERR; @@ -729,7 +840,7 @@ lp = tmxr_find_ldsc (uptr, val, mp); /* find line desc */ if (lp == NULL) return SCPE_IERR; if (lp->txlog) { /* logging? */ - fclose (lp->txlog); /* close log */ + sim_close_logfile (&lp->txlogref); /* close log */ free (lp->txlogname); /* free namebuf */ lp->txlog = NULL; lp->txlogname = NULL; diff --git a/sim_tmxr.h b/sim_tmxr.h index 55399840..0dc26fb1 100644 --- a/sim_tmxr.h +++ b/sim_tmxr.h @@ -26,6 +26,7 @@ Based on the original DZ11 simulator by Thord Nilson, as updated by Arthur Krewat. + 17-Jan-11 MP Added buffered line capabilities 20-Nov-08 RMS Added three new standardized SHOW routines 27-May-08 JDB Added lnorder to TMXR structure, added tmxr_set_lnorder and tmxr_set_lnorder @@ -62,11 +63,15 @@ struct tmln { int32 txbpr; /* xmt buf remove */ int32 txbpi; /* xmt buf insert */ int32 txcnt; /* xmt count */ + int32 txdrp; /* xmt drop count */ + int32 txbsz; /* xmt buffer size */ + int32 txbfd; /* xmt buffered flag */ FILE *txlog; /* xmt log file */ + FILEREF *txlogref; /* xmt log file reference */ char *txlogname; /* xmt log file name */ char rxb[TMXR_MAXBUF]; /* rcv buffer */ char rbr[TMXR_MAXBUF]; /* rcv break */ - char txb[TMXR_MAXBUF]; /* xmt buffer */ + char *txb; /* xmt buffer */ }; typedef struct tmln TMLN; @@ -78,6 +83,8 @@ struct tmxr { TMLN *ldsc; /* line descriptors */ int32 *lnorder; /* line connection order */ DEVICE *dptr; /* multiplexer device */ + char logfiletmpl[FILENAME_MAX]; /* template logfile name */ + int32 buffered; /* Buffered Line Behavior and Buffer Size Flag */ }; typedef struct tmxr TMXR;