diff --git a/scp.c b/scp.c index 565ea5ee..f0d8ef3f 100644 --- a/scp.c +++ b/scp.c @@ -395,10 +395,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 FILE *sim_gotofile; static int32 sim_do_depth = 0; diff --git a/sim_console.c b/sim_console.c index 0d1d19ee..8b35cf66 100644 --- a/sim_console.c +++ b/sim_console.c @@ -23,7 +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 Added support for BREAK key on Windows + 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 @@ -63,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 @@ -105,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 */ @@ -130,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 } }; @@ -262,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; @@ -269,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; } @@ -288,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; } @@ -301,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; } @@ -310,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; } @@ -345,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; } @@ -367,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 */ @@ -402,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) @@ -421,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 */ @@ -456,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; @@ -468,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; @@ -486,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 */ 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 89ea0f83..9b192c88 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) @@ -486,6 +487,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 @@ -522,6 +530,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 a52335da..286e2f81 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 16-Jan-11 MP Made option negotiation more reliable 20-Nov-08 RMS Added three new standardized SHOW routines 30-Sep-08 JDB Reverted tmxr_find_ldsc to original implementation @@ -154,9 +155,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, @@ -189,34 +193,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, sizeof (mantra)); - 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 */ @@ -233,7 +241,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; @@ -419,7 +428,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 */ @@ -447,24 +456,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 */ } @@ -510,10 +524,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; @@ -522,7 +536,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; @@ -536,7 +550,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 */ @@ -548,6 +562,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; @@ -564,7 +653,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; } @@ -577,8 +671,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 */ @@ -586,7 +683,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 */ @@ -696,16 +798,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; } @@ -758,7 +868,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; @@ -779,7 +889,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;