diff --git a/0readmeAsynchIO.txt b/0readmeAsynchIO.txt index 76af706d..425e3262 100644 --- a/0readmeAsynchIO.txt +++ b/0readmeAsynchIO.txt @@ -21,22 +21,27 @@ Features. point of view) at least 'n' instructions after it was initiated. Benefits. - Allows a simulator to execute simulated instructions concurrently - with I/O operations which may take numerous milliseconds to perform. - Allows a simulated device to potentially avoid polling for the arrival - of data. Polling consumes host processor CPU cycles which may better - be spent executing simulated instructions or letting other host - processes run. Measurements made of available instruction execution - easily demonstrate the benefits of parallel instruction and I/O - activities. A VAX simulator with a process running a disk intensive - application in one process was able to process 11 X the number of - Dhrystone operations with Asynch I/O enabled. + - Allows a simulator to execute simulated instructions concurrently + with I/O operations which may take numerous milliseconds to perform. + - Allows a simulated device to potentially avoid polling for the + arrival of data. Polling consumes host processor CPU cycles which + may better be spent executing simulated instructions or letting + other host processes run. Measurements made of available + instruction execution easily demonstrate the benefits of parallel + instruction and I/O activities. A VAX simulator with a process + running a disk intensive application in one process was able to + run (in another process) 11 times the number of Dhrystone operations + with Asynch I/O enabled vs not enabled. + - Allows simulator clock ticks to track wall clock was precisely as + possible under varying I/O load and activities. Asynch I/O is provided through a callback model. SimH Libraries which provide Asynch I/O support: sim_disk sim_tape sim_ether + sim_console + sim_tmxr Requirements to use: The Simulator's instruction loop needs to be modified to include a single @@ -78,6 +83,7 @@ to enable viewing and setting of these variables via scp: { DRDATA (INST_LATENCY, sim_asynch_inst_latency, 32), PV_LEFT }, #endif +Programming Disk and Tape devices to leverage Asynch I/O Naming conventions: All of the routines implemented in sim_disk and sim_tape have been kept @@ -142,14 +148,6 @@ information must be referenced in both the top and bottom half code paths then it must either be recomputed prior to the top/bottom half check or not stored in local variables of the unit service routine. -Run time requirements to use SIM_ASYNCH_IO. -The Posix threads API (pthreads) is required for asynchronous execution. -Most *nix platforms have these APIs available and on these platforms -simh is typically built with these available since on these platforms, -pthreads is required for simh networking support. Windows can also -utilize the pthreads APIs if the compile and run time support for the -win32Pthreads package has been installed on the build system. - Sample Asynch I/O device implementations. The pdp11_rq.c module has been refactored to leverage the asynch I/O features of the sim_disk library. The impact to this code to adopt the @@ -167,3 +165,110 @@ device layer (in sim_tape.c) which combined these multiple operations. This approach will dovetail well with a potential future addition of operations on physical tapes as yet another supported tape format. +Programming Console and Multiplexer devices to leverage Asynch I/O to +minimize 'unproductive' polling. + +There are two goals for asynchronous Multiplexer I/O: 1) Minimize polling +to only happen when data is available, not arbitrarily on every clock tick, +and 2) to have polling actually happen as soon as data may be available. +In most cases no effort is required to add Asynch I/O support to a +multiplexer device emulation. If a device emulation takes the normal +model of polling for arriving data on every simulated clock tick, then if +Asynch I/O is enabled, then device will operate asynchronously and behave +well. There is one restriction in this model. Specifically, the device +emulation logic can't expect that there will be a particular number (clock +tick rate maybe) of invocations of a unit service routine to perform polls +in any interval of time (this is what we're trying to change, right?). +Therefore presumptions about measuring time by counting polls is not +valid. If a device needs to manage time related activities, then the +device should create a separate unit which is dedicated to the timing +activities and which explicitly schedules a different unit service routine +for those activities as needed. Such scheduled polling should only be +enabled when actual timing is required. + +A device which is unprepared to operate asynchronously can specifically +disable multiplexer Asynch I/O for that device by explicitly defining +NO_ASYNCH_MUX at compile time. This can be defined at the top of a +particular device emulation which isn't capable of asynch operation, or +it can be defined globally on the compile command line for the simulator. +Alternatively, if a specific Multiplexer device doesn't function correctly +under the multiplexer asynchronous environment and it will never be +revised to operate correctly, it may set the TMUF_NOASYNCH bit in its +unit flags field. + +Console I/O can operate asynchronously if the simulator notifies the +tmxr/console subsystem which device unit is used by the simulator to poll +for console input. This is done by including sim_tmxr.h in the source +module which contains the console input device definition and calling +tmxr_set_console_input_unit(). tmxr_set_console_input_unit would usually +be called in a device reset routine. + +Some devices will need a small amount of extra coding to leverage the +Multiplexer Asynch I/O capabilties. Devices which require extra coding +have one or more of the following characteristics: +- they poll for input data on a different unit (or units) than the unit + which was provided when tmxr_attach was called with. +- they poll for connections on a different unit than the unit which was + provided when tmxr_attach was called with. + +The extra coding required for proper operation is to call +tmxr_set_line_unit() to associate the appropriate input polling unit to +the respective multiplexer line. + +sim_tmxr consumers: + - Altair Z80 SIO devices = 1, units = 1, lines = 4, flagbits = 8, Untested Asynch + - HP2100 BACI devices = 1, units = 1, lines = 1, flagbits = 3, Untested Asynch + - HP2100 MPX devices = 1, units = 10, lines = 8, flagbits = 2, Untested Asynch + - HP2100 MUX devices = 3, units = 1/16/1, lines = 16, flagbits = 4, Untested Asynch + - I7094 COM devices = 2, units = 4/33, lines = 33, flagbits = 4, Untested Asynch + - Interdata PAS devices = 2, units = 1/32, lines = 32, flagbits = 3, Untested Asynch + - Nova QTY devices = 1, units = 1, lines = 64, flagbits = 1, Untested Asynch + - Nova TT1 devices = 2, units = 1/1, lines = 1, flagbits = 1, Untested Asynch + - PDP-1 DCS devices = 2, units = 1/32, lines = 32, flagbits = 0, Untested Asynch + - PDP-8 TTX devices = 2, units = 1/4, lines = 4, flagbits = 0, Untested Asynch + - PDP-11 DC devices = 2, units = 1/16, lines = 16, flagbits = 5, Untested Asynch + - PDP-11 DL devices = 2, units = 1/16, lines = 16, flagbits = 3, Untested Asynch + - PDP-11 DZ devices = 1, units = 1/1, lines = 32, flagbits = 0, Good Asynch + - PDP-11 VH devices = 1, units = 4, lines = 32, flagbits = 4, Good Asynch + - PDP-18b TT1 devices = 2, units = 1/16, lines = 16, flagbits = 0, Untested Asynch + - SDS MUX devices = 2, units = 1/32, lines = 32, flagbits = 0, Untested Asynch + - sim_console Good Asynch + +Program Clock Devices to leverage Asynsh I/O + +simh's concept of time is calibrated by counting the number of +instructions which the simulator can execute in a given amount of wall +clock time. Once this is determined, the appropriate value is continually +recalibrated and used throughout a simulator to schedule device time +related delays as needed. Historically, this was fine until modern +processors started having dynamically variable processor clock rates. +On such host systems, the simulator's concept of time passing can vary +drastically, which may cause dramatic drifting of the simulated operating +system's concept of time. Once all devices are disconnected from the +calibrated clock's instruction count, the only concern for time in the +simulated system is that it's clock tick be as accurate as possible. +This has worked well in the past, however each simulator was burdened +with providing code which facilitated managing the concept of the +relationship between the number of instructions executed and the passage +of wall clock time. To accomodate the needs of activities or events which +should be measured against wall clock time (vs specific number of +instructions executed), the simulator framework has been extended to +specifically provide event scheduling based on elapsed wall time. A new +API can be used by devices to schedule unit event delivery after the +passage of a specific amount of wall clock time. The +api sim_activate_after() provides this capability. This capability is +not limited to being available ONLY when compiling with SIM_SYNCH_IO +defined. When SIM_ASYNCH_IO is defined, this facility is implemented by +a thread which drives the delivery of these events from the host system's +clock ticks (interpolated as needed to accomodate hosts with relatively +large clock ticks). When SIM_ASYNCH_IO is not defined, this facility is +implemented using the traditional simh calibrated clock approach. + +Run time requirements to use SIM_ASYNCH_IO. +The Posix threads API (pthreads) is required for asynchronous execution. +Most *nix platforms have these APIs available and on these platforms +simh is typically built with these available since on these platforms, +pthreads is required for simh networking support. Windows can also +utilize the pthreads APIs if the compile and run time support for the +win32Pthreads package has been installed on the build system. + diff --git a/PDP11/pdp11_dz.c b/PDP11/pdp11_dz.c index 5e96a6de..67740107 100644 --- a/PDP11/pdp11_dz.c +++ b/PDP11/pdp11_dz.c @@ -163,12 +163,14 @@ TMXR dz_desc = { DZ_MUXES * DZ_LINES, 0, 0, dz_ldsc }; /* mux descriptor */ /* debugging bitmaps */ #define DBG_REG 0x0001 /* trace read/write registers */ #define DBG_INT 0x0002 /* display transfer requests */ +#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 */ DEBTAB dz_debug[] = { {"REG", DBG_REG}, {"INT", DBG_INT}, + {"TRC", DBG_TRC}, {"XMT", DBG_XMT}, {"RCV", DBG_RCV}, {0} @@ -421,6 +423,8 @@ t_stat dz_svc (UNIT *uptr) { int32 dz, t, newln; +sim_debug(DBG_TRC, find_dev_from_unit(uptr), "dz_svc()\n"); + for (dz = t = 0; dz < DZ_MUXES; dz++) /* check enabled */ t = t | (dz_csr[dz] & CSR_MSE); if (t) { /* any enabled? */ diff --git a/PDP11/pdp11_vh.c b/PDP11/pdp11_vh.c index fde07975..5b1bb0d9 100644 --- a/PDP11/pdp11_vh.c +++ b/PDP11/pdp11_vh.c @@ -82,7 +82,6 @@ extern int32 int_req[IPL_HLVL]; extern uint32 cpu_opt; #endif -#include "sim_sock.h" #include "sim_tmxr.h" /* imports from pdp11_stddev.c: */ @@ -293,12 +292,14 @@ static TMLX vh_parm[VH_MUXES * VH_LINES] = { { 0 } }; /* debugging bitmaps */ #define DBG_REG 0x0001 /* trace read/write registers */ #define DBG_INT 0x0002 /* display transfer requests */ +#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 */ DEBTAB vh_debug[] = { {"REG", DBG_REG}, {"INT", DBG_INT}, + {"TRC", DBG_TRC}, {"XMT", DBG_XMT}, {"RCV", DBG_RCV}, {0} @@ -308,6 +309,7 @@ DEBTAB vh_debug[] = { static t_stat vh_rd (int32 *data, int32 PA, int32 access); static t_stat vh_wr (int32 data, int32 PA, int32 access); static t_stat vh_svc (UNIT *uptr); +static t_stat vh_timersvc (UNIT *uptr); static int32 vh_rxinta (void); static int32 vh_txinta (void); static t_stat vh_clear (int32 vh, t_bool flag); @@ -341,6 +343,7 @@ static DIB vh_dib = { static UNIT vh_unit[VH_MUXES] = { { UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0) }, + { UDATA (&vh_timersvc, 0, 0) }, }; static const REG vh_reg[] = { @@ -838,9 +841,9 @@ static t_stat vh_wr ( int32 data, if ((vh_unit[vh].flags & UNIT_MODEDHU) && (data & CSR_SKIP)) data &= ~CSR_MASTER_RESET; if (vh == 0) /* Only start unit service on the first unit. Units are polled there */ - sim_activate (&vh_unit[vh], clk_cosched (tmxr_poll)); - /* vh_mcount[vh] = 72; */ /* 1.2 seconds */ + sim_activate (&vh_unit[0], clk_cosched (tmxr_poll)); vh_mcount[vh] = MS2SIMH (1200); /* 1.2 seconds */ + sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); } if ((data & CSR_RXIE) == 0) vh_clr_rxint (vh); @@ -872,6 +875,7 @@ static t_stat vh_wr ( int32 data, break; if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { vh_mcount[vh] = 1; + sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); break; } if (vh_unit[vh].flags & UNIT_MODEDHU) { @@ -916,6 +920,7 @@ static t_stat vh_wr ( int32 data, case 2: /* LPR */ if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { vh_mcount[vh] = 1; + sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); break; } if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) @@ -942,6 +947,7 @@ static t_stat vh_wr ( int32 data, case 3: /* STAT/FIFODATA */ if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { vh_mcount[vh] = 1; + sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); break; } if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) @@ -965,6 +971,7 @@ static t_stat vh_wr ( int32 data, case 4: /* LNCTRL */ if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { vh_mcount[vh] = 1; + sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); break; } if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) @@ -1032,6 +1039,7 @@ static t_stat vh_wr ( int32 data, case 5: /* TBUFFAD1 */ if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { vh_mcount[vh] = 1; + sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); break; } if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) @@ -1047,6 +1055,7 @@ static t_stat vh_wr ( int32 data, case 6: /* TBUFFAD2 */ if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { vh_mcount[vh] = 1; + sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); break; } if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) @@ -1067,6 +1076,7 @@ static t_stat vh_wr ( int32 data, case 7: /* TBUFFCT */ if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { vh_mcount[vh] = 1; + sim_activate (&vh_unit[1], clk_cosched (tmxr_poll)); break; } if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) @@ -1125,19 +1135,35 @@ static void doDMA ( int32 vh, /* Perform many of the functions of PROC2 */ -static t_stat vh_svc ( UNIT *uptr ) +static t_stat vh_timersvc ( UNIT *uptr ) { - int32 vh, newln, i; + int32 vh, again; + + sim_debug(DBG_TRC, find_dev_from_unit(uptr), "vh_timersvc()\n"); /* scan all muxes for countdown reset */ + again = 0; for (vh = 0; vh < vh_desc.lines/VH_LINES; vh++) { if (vh_csr[vh] & CSR_MASTER_RESET) { - if (vh_mcount[vh] != 0) + if (vh_mcount[vh] != 0) { vh_mcount[vh] -= 1; + ++again; + } else vh_clear (vh, FALSE); } } + if (again) + sim_activate (uptr, clk_cosched (tmxr_poll)); /* requeue ourselves */ + return (SCPE_OK); +} + +static t_stat vh_svc ( UNIT *uptr ) +{ + int32 vh, newln, i; + + sim_debug(DBG_TRC, find_dev_from_unit(uptr), "vh_svc()\n"); + /* sample every 10ms for modem changes (new connections) */ newln = tmxr_poll_conn (&vh_desc); if (newln >= 0) { diff --git a/VAX/vax_stddev.c b/VAX/vax_stddev.c index 2d98e3f7..ece69417 100644 --- a/VAX/vax_stddev.c +++ b/VAX/vax_stddev.c @@ -340,6 +340,7 @@ return SCPE_OK; t_stat tti_reset (DEVICE *dptr) { +tmxr_set_console_input_unit (&tti_unit); tti_unit.buf = 0; tti_csr = 0; CLR_INT (TTI); diff --git a/scp.c b/scp.c index fd064a55..1df06219 100644 --- a/scp.c +++ b/scp.c @@ -213,6 +213,8 @@ /* Macros and data structures */ +#define NOT_MUX_USING_CODE /* sim_tmxr library provider or agnostic */ + #include "sim_defs.h" #include "sim_rev.h" #include "sim_ether.h" @@ -287,8 +289,12 @@ #if defined (SIM_ASYNCH_IO) pthread_mutex_t sim_asynch_lock = PTHREAD_MUTEX_INITIALIZER; pthread_cond_t sim_asynch_wake = PTHREAD_COND_INITIALIZER; +pthread_mutex_t sim_tmxr_poll_lock = PTHREAD_MUTEX_INITIALIZER; +pthread_cond_t sim_tmxr_poll_cond = PTHREAD_COND_INITIALIZER; +int32 sim_tmxr_poll_count; pthread_t sim_asynch_main_threadid; -struct sim_unit *sim_asynch_queue = NULL; +UNIT *sim_asynch_queue = NULL; +UNIT *sim_wallclock_queue = NULL; t_bool sim_asynch_enabled = TRUE; int32 sim_asynch_check; int32 sim_asynch_latency = 4000; /* 4 usec interrupt latency */ @@ -634,12 +640,11 @@ static CTAB cmd_table[] = { "set console LOG=log_file enable console logging to the\n" " specified destination {STDOUT,DEBUG or filename)\n" "set console NOLOG disable console logging\n" - "set console DEBUG=dbg_file\n" - " enable console debugging to the\n" - " specified destination {LOG,STDOUT or filename)\n" - "set console NODEBUG disable console debugging\n" - "set log log_file specify the log destination\n" - " (STDOUT,DEBUG or filename)\n" + "set console DEBUG{=TRC;XMT;RCV}\n" + " enable console debugging of the\n" + " specified debug bit flags\n" + "set console NODEBUG{=TRC;XMT;RCV}\n" + " disable console debugging bits indicated\n" "set nolog disables any currently active logging\n" "set debug debug_file specify the debug destination\n" " (STDOUT,LOG or filename)\n" @@ -881,7 +886,7 @@ while (stat != SCPE_EXIT) { /* in case exit */ stat = SCPE_BARE_STATUS(stat); /* remove possible flag */ sim_last_cmd_stat = stat; /* save command error status */ if ((stat >= SCPE_BASE) && (!stat_nomessage)) { /* error? */ - if (cmdp->message) /* special message handler? */ + if (cmdp && cmdp->message) /* special message handler? */ cmdp->message (NULL, stat); else { printf ("%s\n", sim_error_text (stat)); @@ -1196,14 +1201,14 @@ do { (stat != SCPE_STEP)) { if (!echo && !sim_quiet && /* report if not echoing */ !stat_nomessage && /* and not suppressing messages */ - !cmdp->message) { /* and not handling them specially */ + !(cmdp && cmdp->message)) { /* and not handling them specially */ printf("%s> %s\n", do_position(), ocptr); if (sim_log) fprintf (sim_log, "%s> %s\n", do_position(), ocptr); } } if ((stat >= SCPE_BASE) && !stat_nomessage) { /* report error if not suppressed */ - if (cmdp->message) { /* special message handler */ + if (cmdp && cmdp->message) { /* special message handler */ cmdp->message ((!echo && !sim_quiet) ? ocptr : NULL, stat); } else { @@ -1681,6 +1686,7 @@ if (cptr && (*cptr != 0)) /* now eol? */ if (flag == sim_asynch_enabled) /* already set correctly? */ return SCPE_OK; sim_asynch_enabled = flag; +tmxr_change_async (); if (1) { uint32 i, j; DEVICE *dptr; @@ -2042,6 +2048,9 @@ static SHTAB show_glob_tab[] = { { "THROTTLE", &sim_show_throt, 0 }, { "ASYNCH", &sim_show_asynch, 0 }, { "ETHERNET", ð_show_devices, 0 }, + { "MULTIPLEXER", &tmxr_show_open_devices, 0 }, + { "MUX", &tmxr_show_open_devices, 0 }, + { "TIMERS", &sim_show_timers, 0 }, { "ON", &show_on, 0 }, { NULL, NULL, 0 } }; @@ -2277,28 +2286,46 @@ int32 accum; if (cptr && (*cptr != 0)) return SCPE_2MARG; -if (sim_clock_queue == NULL) { +if (sim_clock_queue == NULL) fprintf (st, "%s event queue empty, time = %.0f\n", sim_name, sim_time); - return SCPE_OK; - } -fprintf (st, "%s event queue status, time = %.0f\n", - sim_name, sim_time); -accum = 0; -for (uptr = sim_clock_queue; uptr != NULL; uptr = uptr->next) { - if (uptr == &sim_step_unit) - fprintf (st, " Step timer"); - else if ((dptr = find_dev_from_unit (uptr)) != NULL) { - fprintf (st, " %s", sim_dname (dptr)); - if (dptr->numunits > 1) - fprintf (st, " unit %d", (int32) (uptr - dptr->units)); +else { + fprintf (st, "%s event queue status, time = %.0f\n", + sim_name, sim_time); + accum = 0; + for (uptr = sim_clock_queue; uptr != NULL; uptr = uptr->next) { + if (uptr == &sim_step_unit) + fprintf (st, " Step timer"); + else if ((dptr = find_dev_from_unit (uptr)) != NULL) { + fprintf (st, " %s", sim_dname (dptr)); + if (dptr->numunits > 1) + fprintf (st, " unit %d", (int32) (uptr - dptr->units)); + } + else fprintf (st, " Unknown"); + fprintf (st, " at %d\n", accum + uptr->time); + accum = accum + uptr->time; } - else fprintf (st, " Unknown"); - fprintf (st, " at %d\n", accum + uptr->time); - accum = accum + uptr->time; } #if defined (SIM_ASYNCH_IO) pthread_mutex_lock (&sim_asynch_lock); +if (sim_wallclock_queue == NULL) + fprintf (st, "%s wall clock event queue empty, time = %.0f\n", + sim_name, sim_time); +else { + fprintf (st, "%s wall clock event queue status, time = %.0f\n", + sim_name, sim_time); + accum = 0; + for (uptr = sim_wallclock_queue; uptr != NULL; uptr = uptr->next) { + if ((dptr = find_dev_from_unit (uptr)) != NULL) { + fprintf (st, " %s", sim_dname (dptr)); + if (dptr->numunits > 1) + fprintf (st, " unit %d", (int32) (uptr - dptr->units)); + } + else fprintf (st, " Unknown"); + fprintf (st, " after %d usec\n", accum + uptr->time); + accum = accum + uptr->time; + } + } fprintf (st, "asynchronous pending event queue\n"); if (sim_asynch_queue == AIO_LIST_END) fprintf (st, "Empty\n"); @@ -3118,8 +3145,10 @@ for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru devices */ for (l = 0; (l < SRBSIZ) && (k < high); l++, k = k + (dptr->aincr)) { /* check for 0 block */ r = dptr->examine (&val, k, uptr, SIM_SW_REST); - if (r != SCPE_OK) + if (r != SCPE_OK) { + free (mbuf); return r; + } if (val) zeroflg = FALSE; SZ_STORE (sz, val, mbuf, l); } /* end for l */ @@ -3358,19 +3387,26 @@ for ( ;; ) { /* device loop */ if ((mbuf = calloc (SRBSIZ, sz)) == NULL) return SCPE_MEM; for (k = 0; k < high; ) { /* loop thru mem */ - READ_I (blkcnt); /* block count */ + if (sim_fread (&blkcnt, sizeof (blkcnt), 1, rfile) == 0) { + free (mbuf); + return SCPE_IOERR; + } if (blkcnt < 0) /* compressed? */ limit = -blkcnt; else limit = (int32)sim_fread (mbuf, sz, blkcnt, rfile); - if (limit <= 0) /* invalid or err? */ + if (limit <= 0) { /* invalid or err? */ + free (mbuf); return SCPE_IOERR; + } for (j = 0; j < limit; j++, k = k + (dptr->aincr)) { if (blkcnt < 0) /* compressed? */ val = 0; else SZ_LOAD (sz, val, mbuf, j); /* saved value */ r = dptr->deposit (val, k, uptr, SIM_SW_REST); - if (r != SCPE_OK) + if (r != SCPE_OK) { + free (mbuf); return r; + } } /* end for j */ } /* end for k */ free (mbuf); /* dealloc buffer */ @@ -3533,23 +3569,32 @@ for (i = 1; (dptr = sim_devices[i]) != NULL; i++) { /* reposition all */ } } stop_cpu = 0; +sim_is_running = 1; /* flag running */ if (sim_ttrun () != SCPE_OK) { /* set console mode */ sim_ttcmd (); + sim_is_running = 0; /* flag idle */ return SCPE_TTYERR; } if ((r = sim_check_console (30)) != SCPE_OK) { /* check console, error? */ sim_ttcmd (); + sim_is_running = 0; /* flag idle */ return r; } if (signal (SIGINT, int_handler) == SIG_ERR) { /* set WRU */ + sim_ttcmd (); + sim_is_running = 0; /* flag idle */ return SCPE_SIGERR; } #ifdef SIGHUP if (signal (SIGHUP, int_handler) == SIG_ERR) { /* set WRU */ + sim_ttcmd (); + sim_is_running = 0; /* flag idle */ return SCPE_SIGERR; } #endif if (signal (SIGTERM, int_handler) == SIG_ERR) { /* set WRU */ + sim_ttcmd (); + sim_is_running = 0; /* flag idle */ return SCPE_SIGERR; } if (sim_step) /* set step timer */ @@ -3558,7 +3603,6 @@ fflush(stdout); /* flush stdout */ if (sim_log) /* flush log if enabled */ fflush (sim_log); sim_throt_sched (); /* set throttle */ -sim_is_running = 1; /* flag running */ sim_brk_clract (); /* defang actions */ sim_rtcn_init_all (); /* re-init clocks */ r = sim_instr(); @@ -4697,6 +4741,32 @@ for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* base + unit#? */ return NULL; } +DEVICE **sim_internal_devices = NULL; +uint32 sim_internal_device_count = 0; + +/* sim_register_internal_device Add device to internal device list + + Inputs: + dptr = pointer to device +*/ + +t_stat sim_register_internal_device (DEVICE *dptr) +{ +uint32 i; + +for (i = 0; (sim_devices[i] != NULL); ++i) + if (sim_devices[i] == dptr) + return SCPE_OK; +for (i = 0; i < sim_internal_device_count; ++i) + if (sim_internal_devices[i] == dptr) + return SCPE_OK; +++sim_internal_device_count; +sim_internal_devices = realloc(sim_internal_devices, sim_internal_device_count*sizeof(*sim_internal_devices)); +sim_internal_devices[sim_internal_device_count-1] = dptr; +sim_internal_devices[sim_internal_device_count] = NULL; +return SCPE_OK; +} + /* Find_dev_from_unit find device for unit Inputs: @@ -4712,7 +4782,14 @@ uint32 i, j; if (uptr == NULL) return NULL; -for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { +for (i = 0; (dptr = sim_devices[i]) != NULL; ++i) { + for (j = 0; j < dptr->numunits; j++) { + if (uptr == (dptr->units + j)) + return dptr; + } + } +for (i = 0; inumunits; j++) { if (uptr == (dptr->units + j)) return dptr; @@ -5147,6 +5224,10 @@ return SCPE_OK; /* Event queue package sim_activate add entry to event queue + sim_activate_abs add entry to event queue even if event already scheduled + sim_activate_notbefure add entry to event queue even if event already scheduled + but not before the specified time + sim_activate_after add entry to event queue after a specified amount of wall time sim_cancel remove entry from event queue sim_process_event process entries on event queue sim_is_active see if entry is on event queue @@ -5196,9 +5277,11 @@ do { if (sim_clock_queue != NULL) sim_interval = sim_clock_queue->time; else sim_interval = noqueue_time = NOQUEUE_WAIT; + AIO_POLL_BEGIN(uptr); if (uptr->action != NULL) reason = uptr->action (uptr); else reason = SCPE_OK; + AIO_POLL_COMPLETE(uptr, reason); } while ((reason == SCPE_OK) && (sim_interval == 0)); /* Empty queue forces sim_interval != 0 */ @@ -5215,6 +5298,11 @@ return reason; reason = result (SCPE_OK if ok) */ +t_stat _sim_activate (UNIT *uptr, int32 event_time) +{ +return sim_activate (uptr, event_time); +} + t_stat sim_activate (UNIT *uptr, int32 event_time) { UNIT *cptr, *prvptr; @@ -5293,6 +5381,74 @@ else return sim_activate (uptr, urtime-rtimenow); } + +/* sim_activate_after - activate (queue) event + + Inputs: + uptr = pointer to unit + usec_delay = relative timeout (in microseconds) + Outputs: + reason = result (SCPE_OK if ok) +*/ + +t_stat sim_activate_after (UNIT *uptr, int32 usec_delay) +{ +#if defined(SIM_ASYNCH_IO) +int32 inst_delay; +int32 inst_per_sec = (*sim_tmr_poll)*(*sim_clk_tps); + +if (0 == inst_per_sec) + inst_per_sec = 1000; +/* compute instruction count avoiding overflow */ +if ((0 == (usec_delay%1000000)) || /* whole seconds? */ + (usec_delay > 100000000)) /* more than 100 seconds */ + inst_delay = inst_per_sec*(usec_delay/1000000); +else + if ((0 == (usec_delay%1000)) || /* whole milliseconds seconds? */ + (usec_delay > 1000000)) /* more than a second */ + inst_delay = (inst_per_sec*(usec_delay/1000))/1000; + else /* microseconds */ + inst_delay = (inst_per_sec*usec_delay)/1000000; +return sim_activate (uptr, inst_delay); +#else +UNIT *cptr, *prvptr; +int32 accum; + +if (sim_is_active (uptr)) /* already active? */ + return SCPE_OK; +pthread_mutex_lock (&sim_asynch_lock); +if (sim_wallclock_queue == NULL) { + UPDATE_SIM_TIME (noqueue_time); + } +else { /* update sim time */ + UPDATE_SIM_TIME (sim_clock_queue->time); + } + +prvptr = NULL; +accum = 0; +for (cptr = sim_wallclock_queue; cptr != NULL; cptr = cptr->next) { + if (usec_delay < (accum + cptr->time)) + break; + accum = accum + cptr->time; + prvptr = cptr; + } +if (prvptr == NULL) { /* insert at head */ + cptr = uptr->next = sim_wallclock_queue; + sim_wallclock_queue = uptr; + } +else { + cptr = uptr->next = prvptr->next; /* insert at prvptr */ + prvptr->next = uptr; + } +uptr->time = usec_delay - accum; +if (cptr != NULL) + cptr->time = cptr->time - uptr->time; +if (prvptr == NULL) /* Need to wake timer thread to time first element on list */ +pthread_mutex_unlock (&sim_asynch_lock); +#endif +return SCPE_OK; +} + /* sim_cancel - cancel (dequeue) event Inputs: diff --git a/scp.h b/scp.h index 3ee43b97..5308a34a 100644 --- a/scp.h +++ b/scp.h @@ -85,8 +85,10 @@ t_stat echo_cmd (int32 flag, char *ptr); t_stat sim_process_event (void); t_stat sim_activate (UNIT *uptr, int32 interval); +t_stat _sim_activate (UNIT *uptr, int32 interval); t_stat sim_activate_abs (UNIT *uptr, int32 interval); t_stat sim_activate_notbefore (UNIT *uptr, int32 rtime); +t_stat sim_activate_after (UNIT *uptr, int32 usecs_walltime); t_stat sim_cancel (UNIT *uptr); int32 sim_is_active (UNIT *uptr); double sim_gtime (void); @@ -113,6 +115,7 @@ CTAB *find_cmd (char *gbuf); DEVICE *find_dev (char *ptr); DEVICE *find_unit (char *ptr, UNIT **uptr); DEVICE *find_dev_from_unit (UNIT *uptr); +t_stat sim_register_internal_device (DEVICE *dptr); REG *find_reg (char *ptr, char **optr, DEVICE *dptr); CTAB *find_ctab (CTAB *tab, char *gbuf); C1TAB *find_c1tab (C1TAB *tab, char *gbuf); diff --git a/sim_console.c b/sim_console.c index 956c8cbd..e57b5d45 100644 --- a/sim_console.c +++ b/sim_console.c @@ -118,10 +118,21 @@ */ #include "sim_defs.h" -#include "sim_sock.h" #include "sim_tmxr.h" +#include "sim_timer.h" #include +/* Forward Declaraations of Platform specific routines */ + +t_stat sim_os_poll_kbd (void); +t_bool sim_os_poll_kbd_ready (int ms_timeout); +t_stat sim_os_putchar (int32 out); +t_stat sim_os_ttinit (void); +t_stat sim_os_ttrun (void); +t_stat sim_os_ttcmd (void); +t_stat sim_os_ttclose (void); +t_bool sim_os_ttisatty (void); + #define KMAP_WRU 0 #define KMAP_BRK 1 #define KMAP_DEL 2 @@ -136,14 +147,46 @@ int32 sim_del_char = '\b'; /* delete character */ #else int32 sim_del_char = 0177; #endif -TMLN sim_con_ldsc = { 0 }; /* console line descr */ -TMXR sim_con_tmxr = { 1, 0, 0, &sim_con_ldsc }; /* console line mux */ + +t_stat sim_con_poll_svc (UNIT *uptr); /* console connection poll routine */ +UNIT sim_con_unit = { UDATA (&sim_con_poll_svc, 0, 0) }; /* console connection unit */ +/* debugging bitmaps */ +#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 */ + +DEBTAB sim_con_debug[] = { + {"TRC", DBG_TRC}, + {"XMT", DBG_XMT}, + {"RCV", DBG_RCV}, + {0} +}; + +DEVICE sim_con_telnet = {"Console Telnet", &sim_con_unit, NULL, NULL, 1, 0, 0, 0, 0, 0, NULL, NULL, NULL, NULL, NULL, NULL, NULL, DEV_DEBUG, 0, sim_con_debug}; +TMLN sim_con_ldsc = { 0 }; /* console line descr */ +TMXR sim_con_tmxr = { 1, 0, 0, &sim_con_ldsc, NULL, &sim_con_telnet };/* console line mux */ + +/* Unit service for console connection polling */ + +t_stat sim_con_poll_svc (UNIT *uptr) +{ +if (sim_con_tmxr.master == 0) /* not Telnet? done */ + return SCPE_OK; +if (tmxr_poll_conn (&sim_con_tmxr) >= 0) /* poll connect */ + sim_con_ldsc.rcve = 1; /* rcv enabled */ +sim_activate_after(uptr, 1000000); /* check again in 1 second */ +return SCPE_OK; +} + extern volatile int32 stop_cpu; extern int32 sim_quiet; extern FILE *sim_log, *sim_deb; extern FILEREF *sim_log_ref, *sim_deb_ref; extern DEVICE *sim_devices[]; +extern t_stat show_dev_debug (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); +extern t_stat set_dev_debug (DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); + /* Set/show data structures */ @@ -156,8 +199,8 @@ static CTAB set_con_tab[] = { { "NOTELNET", &sim_set_notelnet, 0 }, { "LOG", &sim_set_logon, 0 }, { "NOLOG", &sim_set_logoff, 0 }, - { "DEBUG", &sim_set_debon, 0 }, - { "NODEBUG", &sim_set_deboff, 0 }, + { "DEBUG", &sim_set_cons_debug, 1 }, + { "NODEBUG", &sim_set_cons_debug, 0 }, { NULL, NULL, 0 } }; @@ -168,7 +211,7 @@ static SHTAB show_con_tab[] = { { "PCHAR", &sim_show_pchar, 0 }, { "LOG", &sim_show_cons_log, 0 }, { "TELNET", &sim_show_telnet, 0 }, - { "DEBUG", &sim_show_debug, 0 }, + { "DEBUG", &sim_show_cons_debug, 0 }, { "BUFFERED", &sim_show_cons_buff, 0 }, { NULL, NULL, 0 } }; @@ -431,7 +474,9 @@ while (*cptr != 0) { /* do all mods */ if (isdigit (*gbuf)) { if (sim_con_tmxr.master) /* already open? */ sim_set_notelnet (0, NULL); /* close first */ - return tmxr_open_master (&sim_con_tmxr, gbuf); /* open master socket */ + r = tmxr_attach (&sim_con_tmxr, &sim_con_unit, gbuf);/* open master socket */ + sim_activate_after(&sim_con_unit, 1000000); /* check for connection in 1 second */ + return r; } else if ((ctptr = find_ctab (set_con_telnet_tab, gbuf))) { /* match? */ @@ -476,6 +521,28 @@ else { return SCPE_OK; } +/* Show console Debug status */ + +t_stat sim_show_cons_debug (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, char *cptr) +{ +return show_dev_debug (st, &sim_con_telnet, sim_con_telnet.units, flag, cptr); +} + +/* Set console to Debug */ + +t_stat sim_set_cons_debug (int32 flg, char *cptr) +{ +t_stat r = set_dev_debug (&sim_con_telnet, sim_con_telnet.units, flg, cptr); + +if ((r == SCPE_OK) && (sim_con_ldsc.uptr != &sim_con_unit)) { + DEVICE *dptr = find_dev_from_unit(sim_con_ldsc.uptr); + + dptr->dctrl = sim_con_telnet.dctrl; + if (dptr->debflags == NULL) + dptr->debflags = sim_con_telnet.debflags; + } +return r; +} /* Set console to Buffering */ @@ -564,7 +631,7 @@ if (strcmp (gbuf, "LOG") == 0) { /* output to log? */ ++(*pref)->refcount; } else if (strcmp (gbuf, "DEBUG") == 0) { /* output to debug? */ - if (sim_deb == NULL) /* any debug? */ + if (sim_deb == NULL) /* any debug? */ return SCPE_ARG; *pf = sim_deb; *pref = sim_deb_ref; @@ -685,8 +752,14 @@ t_stat sim_poll_kbd (void) int32 c; c = sim_os_poll_kbd (); /* get character */ -if ((c == SCPE_STOP) || (sim_con_tmxr.master == 0)) /* ^E or not Telnet? */ +if ((c == SCPE_STOP) || (sim_con_tmxr.master == 0)) { /* ^E or not Telnet? */ + if (c & SCPE_KFLAG) + sim_debug (DBG_RCV, &sim_con_telnet, "Received: '%c'\n", c); + else + if (c == SCPE_STOP) + sim_debug (DBG_RCV, &sim_con_telnet, "Received: - STOP\n"); return c; /* in-window */ + } if (sim_con_ldsc.conn == 0) { /* no Telnet conn? */ if (!sim_con_ldsc.txbfd) /* unbuffered? */ return SCPE_LOST; /* connection lost */ @@ -708,6 +781,7 @@ t_stat sim_putchar (int32 c) if (sim_con_tmxr.master == 0) { /* not Telnet? */ if (sim_log) /* log file? */ fputc (c, sim_log); + sim_debug (DBG_XMT, &sim_con_telnet, "Sending: '%c'\n", c); return sim_os_putchar (c); /* in-window version */ } if (sim_log && !sim_con_ldsc.txlog) /* log file, but no line log? */ @@ -730,6 +804,7 @@ t_stat r; if (sim_con_tmxr.master == 0) { /* not Telnet? */ if (sim_log) /* log file? */ fputc (c, sim_log); + sim_debug (DBG_XMT, &sim_con_telnet, "Sending: '%c'\n", c); return sim_os_putchar (c); /* in-window version */ } if (sim_log && !sim_con_ldsc.txlog) /* log file, but no line log? */ @@ -789,6 +864,139 @@ else c = c & 0377; return c; } + +#if defined(SIM_ASYNCH_IO) +extern pthread_mutex_t sim_tmxr_poll_lock; +extern pthread_cond_t sim_tmxr_poll_cond; +extern int32 sim_tmxr_poll_count; +extern t_bool sim_tmxr_poll_running; +extern pthread_t sim_console_poll_thread; /* Keyboard Polling Thread Id */ +extern pthread_cond_t sim_console_startup_cond; +extern t_bool sim_console_poll_running; +extern int32 sim_is_running; + +static void * +_console_poll(void *arg) +{ +int sched_policy; +struct sched_param sched_priority; +int poll_timeout_count = 0; +int wait_count = 0; + +/* Boost Priority for this I/O thread vs the CPU instruction execution + thread which, in general, won't be readily yielding the processor when + this thread needs to run */ +pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority); +++sched_priority.sched_priority; +pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); + +sim_debug (DBG_TRC, &sim_con_telnet, "_console_poll() - starting\n"); + +pthread_mutex_lock (&sim_tmxr_poll_lock); +pthread_cond_signal (&sim_console_startup_cond); /* Signal we're ready to go */ +while (sim_asynch_enabled) { + DEVICE *d; + + /* If we started something, let it finish before polling again */ + if (wait_count) { + pthread_cond_wait (&sim_tmxr_poll_cond, &sim_tmxr_poll_lock); + sim_debug (DBG_TRC, &sim_con_telnet, "_console_poll() - continuing with ti\n"); + } + + if (!sim_is_running) + break; + + pthread_mutex_unlock (&sim_tmxr_poll_lock); + wait_count = 0; + if (sim_os_poll_kbd_ready (1000)) { + sim_debug (DBG_TRC, &sim_con_telnet, "_console_poll() - Keyboard Data available\n"); + pthread_mutex_lock (&sim_tmxr_poll_lock); + ++wait_count; + if (!sim_con_ldsc.uptr->a_polling_now) { + sim_con_ldsc.uptr->a_polling_now = TRUE; + sim_con_ldsc.uptr->a_poll_waiter_count = 1; + d = find_dev_from_unit(sim_con_ldsc.uptr); + sim_debug (DBG_TRC, &sim_con_telnet, "_console_poll() - Activating %s\n", d->name); + _sim_activate (sim_con_ldsc.uptr, 0); + } + else + ++sim_con_ldsc.uptr->a_poll_waiter_count; + } + else + pthread_mutex_lock (&sim_tmxr_poll_lock); + + sim_tmxr_poll_count += wait_count; + } +pthread_mutex_unlock (&sim_tmxr_poll_lock); + +sim_debug (DBG_TRC, &sim_con_telnet, "_console_poll() - exiting\n"); + +return NULL; +} + + +#endif + + +t_stat sim_ttinit (void) +{ +sim_register_internal_device (&sim_con_telnet); +tmxr_startup (); +return sim_os_ttinit (); +} + +t_stat sim_ttrun (void) +{ +if (!sim_con_tmxr.ldsc->uptr) /* If simulator didn't declare its input polling unit */ + sim_con_unit.flags &= ~UNIT_TM_POLL; /* we can't poll asynchronously */ +#if defined(SIM_ASYNCH_IO) +pthread_mutex_lock (&sim_tmxr_poll_lock); +if (sim_asynch_enabled) { + pthread_attr_t attr; + + pthread_cond_init (&sim_console_startup_cond, NULL); + pthread_attr_init (&attr); + pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); + pthread_create (&sim_console_poll_thread, &attr, _console_poll, NULL); + pthread_attr_destroy( &attr); + pthread_cond_wait (&sim_console_startup_cond, &sim_tmxr_poll_lock); /* Wait for thread to stabilize */ + pthread_cond_destroy (&sim_console_startup_cond); + sim_console_poll_running = TRUE; + } +pthread_mutex_unlock (&sim_tmxr_poll_lock); +#endif +tmxr_start_poll (); +return sim_os_ttrun (); +} + +t_stat sim_ttcmd (void) +{ +#if defined(SIM_ASYNCH_IO) +pthread_mutex_lock (&sim_tmxr_poll_lock); +if (sim_console_poll_running) { + pthread_cond_signal (&sim_tmxr_poll_cond); + pthread_mutex_unlock (&sim_tmxr_poll_lock); + pthread_join (sim_console_poll_thread, NULL); + sim_console_poll_running = FALSE; + } +else + pthread_mutex_unlock (&sim_tmxr_poll_lock); +#endif +tmxr_stop_poll (); +return sim_os_ttcmd (); +} + +t_stat sim_ttclose (void) +{ +tmxr_shutdown (); +return sim_os_ttclose (); +} + +t_bool sim_ttisatty (void) +{ +return sim_os_ttisatty (); +} + /* VMS routines, from Ben Thomas, with fixes from Robert Alan Byer */ #if defined (VMS) @@ -796,6 +1004,7 @@ return c; #if defined(__VAX) #define sys$assign SYS$ASSIGN #define sys$qiow SYS$QIOW +#define sys$dassgn SYS$DASSGN #endif #include @@ -808,6 +1017,7 @@ return c; #define EFN 0 uint32 tty_chan = 0; +int buffered_character = 0; typedef struct { unsigned short sense_count; @@ -824,7 +1034,7 @@ typedef struct { SENSE_BUF cmd_mode = { 0 }; SENSE_BUF run_mode = { 0 }; -t_stat sim_ttinit (void) +t_stat sim_os_ttinit (void) { unsigned int status; IOSB iosb; @@ -867,17 +1077,19 @@ if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) return SCPE_OK; } -t_stat sim_ttclose (void) +t_stat sim_os_ttclose (void) { -return sim_ttcmd (); +sim_ttcmd (); +sys$dassgn (tty_chan); +return SCPE_OK; } -t_bool sim_ttisatty (void) +t_bool sim_os_ttisatty (void) { return isatty (fileno (stdin)); } -t_stat sim_os_poll_kbd (void) +t_stat sim_os_poll_kbd_data (void) { unsigned int status, term[2]; unsigned char buf[4]; @@ -902,6 +1114,40 @@ if (sim_brk_char && (buf[0] == sim_brk_char)) return (buf[0] | SCPE_KFLAG); } +t_stat sim_os_poll_kbd (void) +{ +t_stat response; + +if (response = buffered_character) { + buffered_character = 0; + return response; + } +return sim_os_poll_kbd_data (); +} + +t_bool sim_os_poll_kbd_ready (int ms_timeout) +{ +unsigned int status, term[2]; +unsigned char buf[4]; +IOSB iosb; + +term[0] = 0; term[1] = 0; +status = sys$qiow (EFN, tty_chan, + IO$_READLBLK | IO$M_NOECHO | IO$M_NOFILTR | IO$M_TIMED | IO$M_TRMNOECHO, + &iosb, 0, 0, buf, 1, (ms_timeout+999)/1000, term, 0, 0); +if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL)) + return FALSE; +if (buf[0] == sim_int_char) + buffered_character = SCPE_STOP; +else + if (sim_brk_char && (buf[0] == sim_brk_char)) + buffered_character = SCPE_BREAK; + else + buffered_character = (buf[0] | SCPE_KFLAG) +return TRUE; +} + + t_stat sim_os_putchar (int32 out) { unsigned int status; @@ -958,7 +1204,7 @@ ControlHandler(DWORD dwCtrlType) return FALSE; } -t_stat sim_ttinit (void) +t_stat sim_os_ttinit (void) { SetConsoleCtrlHandler( ControlHandler, TRUE ); std_input = GetStdHandle (STD_INPUT_HANDLE); @@ -969,7 +1215,7 @@ if ((std_input) && /* Not Background proces return SCPE_OK; } -t_stat sim_ttrun (void) +t_stat sim_os_ttrun (void) { if ((std_input) && /* If Not Background process? */ (std_input != INVALID_HANDLE_VALUE) && @@ -984,7 +1230,7 @@ SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL); return SCPE_OK; } -t_stat sim_ttcmd (void) +t_stat sim_os_ttcmd (void) { if (sim_log) { fflush (sim_log); @@ -998,12 +1244,12 @@ if ((std_input) && /* If Not Background pro return SCPE_OK; } -t_stat sim_ttclose (void) +t_stat sim_os_ttclose (void) { return SCPE_OK; } -t_bool sim_ttisatty (void) +t_bool sim_os_ttisatty (void) { DWORD Mode; @@ -1016,7 +1262,7 @@ int c = -1; DWORD nkbevents, nkbevent; INPUT_RECORD rec; -\ +sim_debug (DBG_TRC, &sim_con_telnet, "sim_os_poll_kbd()\n"); if ((std_input == NULL) || /* No keyboard for */ (std_input == INVALID_HANDLE_VALUE)) /* background processes */ @@ -1053,6 +1299,17 @@ if ((sim_brk_char && ((c & 0177) == sim_brk_char)) || (c & SCPE_BREAK)) return c | SCPE_KFLAG; } +t_bool sim_os_poll_kbd_ready (int ms_timeout) +{ +sim_debug (DBG_TRC, &sim_con_telnet, "sim_os_poll_kbd_ready()\n"); +if ((std_input == NULL) || /* No keyboard for */ + (std_input == INVALID_HANDLE_VALUE)) { /* background processes */ + Sleep (ms_timeout); + return FALSE; + } +return (WAIT_OBJECT_0 == WaitForSingleObject (std_input, ms_timeout)); +} + t_stat sim_os_putchar (int32 c) { DWORD unused; @@ -1068,27 +1325,27 @@ return SCPE_OK; #include -t_stat sim_ttinit (void) +t_stat sim_os_ttinit (void) { return SCPE_OK; } -t_stat sim_ttrun (void) +t_stat sim_os_ttrun (void) { return SCPE_OK; } -t_stat sim_ttcmd (void) +t_stat sim_os_ttcmd (void) { return SCPE_OK; } -t_stat sim_ttclose (void) +t_stat sim_os_ttclose (void) { return SCPE_OK; } -t_bool sim_ttisatty (void) +t_bool sim_os_ttisatty (void) { return 1; } @@ -1124,6 +1381,12 @@ if (sim_brk_char && ((c & 0177) == sim_brk_char)) return c | SCPE_KFLAG; } +t_bool sim_os_poll_kbd_ready (int ms_timeout) /* Don't know how to do this on this platform */ +{ +sim_os_ms_sleep (MIN(20,ms_timeout)); /* Wait a little */ +return TRUE; /* force a poll */ +} + t_stat sim_os_putchar (int32 c) { if (c != 0177) { @@ -1251,7 +1514,7 @@ int ps_getch(void) { /* Note that this only works if the call to sim_ttinit comes before any output to the console */ -t_stat sim_ttinit (void) { +t_stat sim_os_ttinit (void) { int i; /* this blank will later be replaced by the number of characters */ char title[50] = " "; @@ -1274,22 +1537,22 @@ t_stat sim_ttinit (void) { return SCPE_OK; } -t_stat sim_ttrun (void) +t_stat sim_os_ttrun (void) { return SCPE_OK; } -t_stat sim_ttcmd (void) +t_stat sim_os_ttcmd (void) { return SCPE_OK; } -t_stat sim_ttclose (void) +t_stat sim_os_ttclose (void) { return SCPE_OK; } -t_bool sim_ttisatty (void) +t_bool sim_os_ttisatty (void) { return 1; } @@ -1309,6 +1572,12 @@ if (sim_brk_char && ((c & 0177) == sim_brk_char)) return c | SCPE_KFLAG; } +t_bool sim_os_poll_kbd_ready (int ms_timeout) /* Don't know how to do this on this platform */ +{ +sim_os_ms_sleep (MIN(20,ms_timeout)); /* Wait a little */ +return TRUE; /* force a poll */ +} + t_stat sim_os_putchar (int32 c) { if (c != 0177) { @@ -1331,7 +1600,7 @@ struct tchars cmdtchars,runtchars; /* V7 editing */ struct ltchars cmdltchars,runltchars; /* 4.2 BSD editing */ int cmdfl,runfl; /* TTY flags */ -t_stat sim_ttinit (void) +t_stat sim_os_ttinit (void) { cmdfl = fcntl (0, F_GETFL, 0); /* get old flags and status */ runfl = cmdfl | FNDELAY; @@ -1358,7 +1627,7 @@ runltchars.t_lnextc = 0xFF; return SCPE_OK; /* return success */ } -t_stat sim_ttrun (void) +t_stat sim_os_ttrun (void) { runtchars.t_intrc = sim_int_char; /* in case changed */ fcntl (0, F_SETFL, runfl); /* non-block mode */ @@ -1372,7 +1641,7 @@ nice (10); /* lower priority */ return SCPE_OK; } -t_stat sim_ttcmd (void) +t_stat sim_os_ttcmd (void) { nice (-10); /* restore priority */ fcntl (0, F_SETFL, cmdfl); /* block mode */ @@ -1385,12 +1654,12 @@ if (ioctl (0, TIOCSLTC, &cmdltchars) < 0) return SCPE_OK; } -t_stat sim_ttclose (void) +t_stat sim_os_ttclose (void) { return sim_ttcmd (); } -t_bool sim_ttisatty (void) +t_bool sim_os_ttisatty (void) { return isatty (0); } @@ -1407,6 +1676,22 @@ if (sim_brk_char && (buf[0] == sim_brk_char)) else return (buf[0] | SCPE_KFLAG); } +t_bool sim_os_poll_kbd_ready (int ms_timeout) +{ +fd_set readfds; +struct timeval timeout; + +if (!isatty (0)) { /* skip if !tty */ + sim_os_ms_sleep (ms_timeout); + return FALSE; + } +FD_ZERO (&readfds); +FD_SET (0, &readfds); +timeout.tv_sec = 0; +timeout.tv_usec = ms_timeout*1000; +return (1 == select (1, &readfds, NULL, NULL, &timeout)); +} + t_stat sim_os_putchar (int32 out) { char c; @@ -1426,7 +1711,7 @@ return SCPE_OK; struct termios cmdtty, runtty; static int prior_norm = 1; -t_stat sim_ttinit (void) +t_stat sim_os_ttinit (void) { if (!isatty (fileno (stdin))) /* skip if !tty */ return SCPE_OK; @@ -1468,7 +1753,7 @@ runtty.c_cc[VSTATUS] = 0; return SCPE_OK; } -t_stat sim_ttrun (void) +t_stat sim_os_ttrun (void) { if (!isatty (fileno (stdin))) /* skip if !tty */ return SCPE_OK; @@ -1483,7 +1768,7 @@ if (prior_norm) { /* at normal pri? */ return SCPE_OK; } -t_stat sim_ttcmd (void) +t_stat sim_os_ttcmd (void) { if (!isatty (fileno (stdin))) /* skip if !tty */ return SCPE_OK; @@ -1497,12 +1782,12 @@ if (tcsetattr (0, TCSAFLUSH, &cmdtty) < 0) return SCPE_OK; } -t_stat sim_ttclose (void) +t_stat sim_os_ttclose (void) { return sim_ttcmd (); } -t_bool sim_ttisatty(void) +t_bool sim_os_ttisatty (void) { return isatty (fileno (stdin)); } @@ -1519,6 +1804,22 @@ if (sim_brk_char && (buf[0] == sim_brk_char)) else return (buf[0] | SCPE_KFLAG); } +t_bool sim_os_poll_kbd_ready (int ms_timeout) +{ +fd_set readfds; +struct timeval timeout; + +if (!sim_os_ttisatty()) { /* skip if !tty */ + sim_os_ms_sleep (ms_timeout); + return FALSE; + } +FD_ZERO (&readfds); +FD_SET (0, &readfds); +timeout.tv_sec = 0; +timeout.tv_usec = ms_timeout*1000; +return (1 == select (1, &readfds, NULL, NULL, &timeout)); +} + t_stat sim_os_putchar (int32 out) { char c; diff --git a/sim_console.h b/sim_console.h index 78d51dd7..4d255800 100644 --- a/sim_console.h +++ b/sim_console.h @@ -58,18 +58,22 @@ 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_debug (int32 flg, char *cptr); +t_stat sim_set_cons_nodebug (int32 flg, 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_set_console_units (UNIT *rxuptr, UNIT *txuptr); t_stat sim_show_console (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat sim_show_kmap (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); t_stat sim_show_telnet (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); 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_debug (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); @@ -83,9 +87,7 @@ t_stat sim_ttinit (void); t_stat sim_ttrun (void); t_stat sim_ttcmd (void); t_stat sim_ttclose (void); -t_bool sim_ttisatty(void); -t_stat sim_os_poll_kbd (void); -t_stat sim_os_putchar (int32 out); +t_bool sim_ttisatty (void); int32 sim_tt_inpcvt (int32 c, uint32 mode); int32 sim_tt_outcvt (int32 c, uint32 mode); diff --git a/sim_defs.h b/sim_defs.h index 4c4d1f5c..e8ed9bb1 100644 --- a/sim_defs.h +++ b/sim_defs.h @@ -371,6 +371,11 @@ struct sim_unit { struct sim_unit *a_next; /* next asynch active */ int32 a_event_time; t_stat (*a_activate_call)(struct sim_unit *, int32); + /* Asynchronous Polling control */ + /* These fields should only be referenced when holding the sim_tmxr_poll_lock */ + t_bool a_polling_now; /* polling active flag */ + int32 a_poll_waiter_count; /* count of polling threads */ + /* waiting for this unit */ #endif }; @@ -380,21 +385,24 @@ struct sim_unit { #define UNIT_V_UF 16 /* device specific */ #define UNIT_V_RSV 31 /* reserved!! */ -#define UNIT_ATTABLE 000001 /* attachable */ -#define UNIT_RO 000002 /* read only */ -#define UNIT_FIX 000004 /* fixed capacity */ -#define UNIT_SEQ 000010 /* sequential */ -#define UNIT_ATT 000020 /* attached */ -#define UNIT_BINK 000040 /* K = power of 2 */ -#define UNIT_BUFABLE 000100 /* bufferable */ -#define UNIT_MUSTBUF 000200 /* must buffer */ -#define UNIT_BUF 000400 /* buffered */ -#define UNIT_ROABLE 001000 /* read only ok */ -#define UNIT_DISABLE 002000 /* disable-able */ -#define UNIT_DIS 004000 /* disabled */ -#define UNIT_RAW 010000 /* raw mode */ -#define UNIT_TEXT 020000 /* text mode */ -#define UNIT_IDLE 040000 /* idle eligible */ +#define UNIT_ATTABLE 0000001 /* attachable */ +#define UNIT_RO 0000002 /* read only */ +#define UNIT_FIX 0000004 /* fixed capacity */ +#define UNIT_SEQ 0000010 /* sequential */ +#define UNIT_ATT 0000020 /* attached */ +#define UNIT_BINK 0000040 /* K = power of 2 */ +#define UNIT_BUFABLE 0000100 /* bufferable */ +#define UNIT_MUSTBUF 0000200 /* must buffer */ +#define UNIT_BUF 0000400 /* buffered */ +#define UNIT_ROABLE 0001000 /* read only ok */ +#define UNIT_DISABLE 0002000 /* disable-able */ +#define UNIT_DIS 0004000 /* disabled */ +#define UNIT_RAW 0010000 /* raw mode */ +#define UNIT_TEXT 0020000 /* text mode */ +#define UNIT_IDLE 0040000 /* idle eligible */ +#define UNIT_TM_POLL 0100000 /* TMXR Polling unit */ + /* This flag is ONLY set dynamically */ + /* it should NOT be set via initialization */ #define UNIT_UFMASK_31 (((1u << UNIT_V_RSV) - 1) & ~((1u << UNIT_V_UF_31) - 1)) #define UNIT_UFMASK (((1u << UNIT_V_RSV) - 1) & ~((1u << UNIT_V_UF) - 1)) @@ -562,9 +570,13 @@ typedef struct sim_fileref FILEREF; #if defined (SIM_ASYNCH_IO) #include +#include "sim_tmxr.h" extern pthread_mutex_t sim_asynch_lock; extern pthread_cond_t sim_asynch_wake; +extern int32 sim_tmxr_poll_count; +extern pthread_cond_t sim_tmxr_poll_cond; +extern pthread_mutex_t sim_tmxr_poll_lock; extern pthread_t sim_asynch_main_threadid; extern struct sim_unit *sim_asynch_queue; extern volatile t_bool sim_idle_wait; @@ -581,12 +593,33 @@ extern int32 sim_asynch_inst_latency; This allows NULL in an entry's a_next pointer to \ indicate that the entry is not currently in any list */ \ sim_asynch_queue = AIO_LIST_END; \ - } + } \ + else \ + (void)0 #define AIO_CLEANUP \ if (1) { \ pthread_mutex_destroy(&sim_asynch_lock); \ pthread_cond_destroy(&sim_asynch_wake); \ - } + pthread_mutex_destroy(&sim_tmxr_poll_lock); \ + pthread_cond_destroy(&sim_tmxr_poll_cond); \ + } \ + else \ + (void)0 +#define AIO_POLL_BEGIN(uptr) \ + do { +#define AIO_POLL_COMPLETE(uptr, reason) \ + if (uptr->flags & UNIT_TM_POLL) { \ + pthread_mutex_lock (&sim_tmxr_poll_lock); \ + uptr->a_polling_now = FALSE; \ + if (uptr->a_poll_waiter_count) { \ + sim_tmxr_poll_count -= uptr->a_poll_waiter_count; \ + uptr->a_poll_waiter_count = 0; \ + if (0 == sim_tmxr_poll_count) \ + pthread_cond_broadcast (&sim_tmxr_poll_cond); \ + } \ + pthread_mutex_unlock (&sim_tmxr_poll_lock); \ + } \ + } while (0) #if defined(__DECC_VER) #include @@ -640,7 +673,7 @@ 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 )) { \ - if (uptr->a_next) { \ + if (uptr->a_next) { /* already queued? */ \ uptr->a_activate_call = sim_activate_abs; \ } else { \ UNIT *q, *qe; \ @@ -695,7 +728,7 @@ extern int32 sim_asynch_inst_latency; #define AIO_ACTIVATE(caller, uptr, event_time) \ if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) { \ pthread_mutex_lock (&sim_asynch_lock); \ - if (uptr->a_next) { \ + if (uptr->a_next) { /* already queued? */ \ uptr->a_activate_call = sim_activate_abs; \ } else { \ uptr->a_next = sim_asynch_queue; \ @@ -728,6 +761,8 @@ extern int32 sim_asynch_inst_latency; #define AIO_CHECK_EVENT #define AIO_INIT #define AIO_CLEANUP +#define AIO_POLL_BEGIN(uptr) +#define AIO_POLL_COMPLETE(uptr) #define AIO_SET_INTERRUPT_LATENCY(instpersec) #endif /* SIM_ASYNCH_IO */ diff --git a/sim_timer.c b/sim_timer.c index 963c090c..42694a2f 100644 --- a/sim_timer.c +++ b/sim_timer.c @@ -81,6 +81,8 @@ t_bool sim_idle_enab = FALSE; /* global flag */ volatile t_bool sim_idle_wait = FALSE; /* global flag */ +int32 *sim_tmr_poll = NULL; /* global */ +int32 *sim_clk_tps = NULL; /* global */ static uint32 sim_idle_rate_ms = 0; static uint32 sim_os_sleep_min_ms = 0; @@ -509,6 +511,10 @@ rtc_based[tmr] = time; rtc_currd[tmr] = time; rtc_initd[tmr] = time; rtc_elapsed[tmr] = 0; +if (!sim_tmr_poll) + sim_tmr_poll = &rtc_currd[tmr]; +if (!sim_clk_tps) + sim_clk_tps = &rtc_hz[tmr]; return time; } @@ -578,6 +584,32 @@ sim_idle_rate_ms = sim_os_ms_sleep_init (); /* get OS timer rate */ return (sim_idle_rate_ms != 0); } +/* sim_show_timers - show running timer information */ + +t_stat sim_show_timers (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, char* desc) +{ +int tmr; + +for (tmr=0; tmr @@ -134,6 +138,7 @@ int32 tmxr_send_buffered_data (TMLN *lp); TMLN *tmxr_find_ldsc (UNIT *uptr, int32 val, TMXR *mp); extern int32 sim_switches; +extern int32 sim_is_running; extern char sim_name[]; extern FILE *sim_log; extern uint32 sim_os_msec (void); @@ -143,7 +148,7 @@ extern uint32 sim_os_msec (void); Called from unit service routine to test for new connection Inputs: - *mp = pointer to terminal multiplexor descriptor + *mp = pointer to terminal multiplexer descriptor Outputs: line number activated, -1 if none @@ -171,10 +176,12 @@ static char mantra[] = { TN_IAC, TN_DO, TN_BIN }; +tmxr_debug_trace (mp, "tmxr_poll_conn()"); newsock = sim_accept_conn (mp->master, &ipaddr); /* poll connect */ if (newsock != INVALID_SOCKET) { /* got a live one? */ 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 */ for (j = 0; j < mp->lines; j++, i++) { /* find next avail line */ if (op && (*op >= 0) && (*op < mp->lines)) /* order list present and valid? */ @@ -239,6 +246,7 @@ return -1; void tmxr_reset_ln (TMLN *lp) { +tmxr_debug_trace_line (lp, "tmxr_reset_ln()"); if (lp->txlog) /* dump log */ fflush (lp->txlog); tmxr_send_buffered_data (lp); /* send buffered data */ @@ -265,6 +273,7 @@ int32 tmxr_getc_ln (TMLN *lp) int32 j, val = 0; uint32 tmp; +tmxr_debug_trace_line (lp, "tmxr_getc_ln()"); if (lp->conn && lp->rcve) { /* conn & enb? */ j = lp->rxbpi - lp->rxbpr; /* # input chrs */ if (j) { /* any? */ @@ -283,7 +292,7 @@ return val; /* Poll for input Inputs: - *mp = pointer to terminal multiplexor descriptor + *mp = pointer to terminal multiplexer descriptor Outputs: none */ @@ -292,9 +301,10 @@ void tmxr_poll_rx (TMXR *mp) int32 i, nbytes, j; TMLN *lp; +tmxr_debug_trace (mp, "tmxr_poll_rx()"); for (i = 0; i < mp->lines; i++) { /* loop thru lines */ lp = mp->ldsc + i; /* get line desc */ - if (!lp->conn || !lp->rcve) /* skip if !conn */ + if (!lp->conn) /* skip if !conn */ continue; nbytes = 0; @@ -415,6 +425,11 @@ for (i = 0; i < mp->lines; i++) { /* loop thru lines */ } /* end for char */ if (nbytes != (lp->rxbpi-lp->rxbpr)) tmxr_debug (TMXR_DBG_RCV, lp, "Remaining", &(lp->rxb[lp->rxbpi]), lp->rxbpi-lp->rxbpr); + if (!lp->rcve) { + nbytes = lp->rxbpi-lp->rxbpr; + lp->rxbpi = lp->rxbpi - nbytes; /* reverse pointers */ + tmxr_debug (TMXR_DBG_RCV, lp, "Receive Disabled...Discarding", &(lp->rxb[lp->rxbpi]), nbytes); + } } /* end else nbytes */ } /* end for lines */ for (i = 0; i < mp->lines; i++) { /* loop thru lines */ @@ -448,35 +463,40 @@ return; Inputs: *lp = pointer to line descriptor - chr = characters + chr = character Outputs: status = ok, connection lost, or stall */ t_stat tmxr_putc_ln (TMLN *lp, int32 chr) { +if ((lp->conn == 0) && /* no conn & */ + (!(lp->mp && lp->mp->buffered))) { /* not buffered? */ + ++lp->txdrp; /* lost */ + return SCPE_LOST; + } +tmxr_debug_trace_line (lp, "tmxr_putc_ln()"); if (lp->txlog) /* log if available */ fputc (chr, lp->txlog); -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; - } #define TXBUF_AVAIL(lp) (lp->txbsz - tmxr_tqln (lp)) #define TXBUF_CHAR(lp, c) { \ lp->txb[lp->txbpi++] = (char)(c); \ + ++lp->mp->txcount; \ lp->txbpi %= lp->txbsz; \ - if (lp->txbpi == lp->txbpr) \ - lp->txbpr = (1+lp->txbpr)%lp->txbsz, ++lp->txdrp; \ + 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? */ + if ((!lp->txbfd) && (TXBUF_AVAIL (lp) <= TMXR_GUARD)) {/* near full? */ lp->xmte = 0; /* disable line */ + if (0 == tmxr_send_buffered_data (lp)) /* try to flush now */ + lp->xmte = 1; /* re-enable since empty now */ + } return SCPE_OK; /* char sent */ } ++lp->txdrp; lp->xmte = 0; /* no room, dsbl line */ @@ -486,7 +506,7 @@ return SCPE_STALL; /* char not sent */ /* Poll for output Inputs: - *mp = pointer to terminal multiplexor descriptor + *mp = pointer to terminal multiplexer descriptor Outputs: none */ @@ -496,6 +516,7 @@ void tmxr_poll_tx (TMXR *mp) int32 i, nbytes; TMLN *lp; +tmxr_debug_trace (mp, "tmxr_poll_tx()"); for (i = 0; i < mp->lines; i++) { /* loop thru lines */ lp = mp->ldsc + i; /* get line desc */ if (lp->conn == 0) /* skip if !conn */ @@ -503,6 +524,11 @@ for (i = 0; i < mp->lines; i++) { /* loop thru lines */ nbytes = tmxr_send_buffered_data (lp); /* buffered bytes */ if (nbytes == 0) /* buf empty? enab line */ lp->xmte = 1; + if (lp->uptr && + (lp->uptr->flags & UNIT_TM_POLL) && + sim_asynch_enabled && + tmxr_rqln (lp)) + _sim_activate (lp->uptr, 0); } /* end for */ return; } @@ -519,6 +545,7 @@ int32 tmxr_send_buffered_data (TMLN *lp) { int32 nbytes, sbytes; +tmxr_debug_trace_line (lp, "tmxr_send_buffered_data()"); nbytes = tmxr_tqln(lp); /* avail bytes */ if (nbytes) { /* >0? write */ if (lp->txbpr < lp->txbpi) /* no wrap? */ @@ -565,6 +592,7 @@ SOCKET sock; TMLN *lp; t_stat r; +tmxr_debug_trace (mp, "tmxr_open_master()"); if (!isdigit(*cptr)) { char gbuf[CBUFSIZE]; cptr = get_glyph (cptr, gbuf, '='); @@ -653,6 +681,7 @@ mp->port = port; /* save port */ mp->master = sock; /* save master socket */ for (i = 0; i < mp->lines; i++) { /* initialize lines */ lp = mp->ldsc + i; + lp->mp = mp; lp->conn = lp->tsta = 0; lp->rxbpi = lp->rxbpr = 0; lp->txbpi = lp->txbpr = 0; @@ -668,9 +697,333 @@ for (i = 0; i < mp->lines; i++) { /* initialize lines */ return SCPE_OK; } +t_stat tmxr_set_line_unit (TMXR *mp, int line, UNIT *uptr_poll) +{ +if ((line < 0) || (line >= mp->lines)) + return SCPE_ARG; +mp->ldsc[line].uptr = uptr_poll; +return SCPE_OK; +} + +t_stat tmxr_set_console_input_unit (UNIT *uptr) +{ +extern TMLN sim_con_ldsc; + +sim_con_ldsc.uptr = uptr; +if (!(uptr->flags & UNIT_TM_POLL)) { + uptr->flags |= UNIT_TM_POLL; /* tag as polling unit */ + } +return SCPE_OK; +} + + +static TMXR **tmxr_open_devices = NULL; +static int tmxr_open_device_count = 0; + +#if defined(SIM_ASYNCH_IO) +pthread_t sim_tmxr_poll_thread; /* Polling Thread Id */ +pthread_mutex_t sim_tmxr_poll_lock; +pthread_cond_t sim_tmxr_poll_cond; +pthread_cond_t sim_tmxr_startup_cond; +int32 sim_tmxr_poll_count = 0; +t_bool sim_tmxr_poll_running = FALSE; +pthread_t sim_console_poll_thread; /* Keyboard Polling Thread Id */ +pthread_cond_t sim_console_startup_cond; +t_bool sim_console_poll_running = FALSE; + +static void * +_tmxr_poll(void *arg) +{ +int sched_policy; +struct sched_param sched_priority; +struct timeval timeout; +int timeout_usec; +DEVICE *dptr = tmxr_open_devices[0]->dptr; +UNIT **units = NULL; +UNIT **activated = NULL; +SOCKET *sockets = NULL; +int wait_count = 0; + +/* Boost Priority for this I/O thread vs the CPU instruction execution + thread which, in general, won't be readily yielding the processor when + this thread needs to run */ +pthread_getschedparam (pthread_self(), &sched_policy, &sched_priority); +++sched_priority.sched_priority; +pthread_setschedparam (pthread_self(), sched_policy, &sched_priority); + +sim_debug (TMXR_DBG_TRC, dptr, "_tmxr_poll() - starting\n"); + +units = calloc(FD_SETSIZE, sizeof(*units)); +activated = calloc(FD_SETSIZE, sizeof(*activated)); +sockets = calloc(FD_SETSIZE, sizeof(*sockets)); +timeout_usec = 1000000; +pthread_mutex_lock (&sim_tmxr_poll_lock); +pthread_cond_signal (&sim_tmxr_startup_cond); /* Signal we're ready to go */ +while (sim_asynch_enabled) { + int i, j, status; + fd_set readfds, errorfds; + int socket_count; + SOCKET max_socket_fd; + TMXR *mp; + DEVICE *d; + + /* If we started something we should wait for, let it finish before polling again */ + if (wait_count) { + pthread_cond_wait (&sim_tmxr_poll_cond, &sim_tmxr_poll_lock); + sim_debug (TMXR_DBG_TRC, dptr, "_tmxr_poll() - continuing with timeout of %dms\n", timeout_usec/1000); + } + if ((tmxr_open_device_count == 0) || (!sim_is_running)) + break; + FD_ZERO (&readfds); + FD_ZERO (&errorfds); + for (i=max_socket_fd=socket_count=0; imaster) && (mp->uptr->flags&UNIT_TM_POLL)) { + units[socket_count] = mp->uptr; + sockets[socket_count] = mp->master; + FD_SET (mp->master, &readfds); + FD_SET (mp->master, &errorfds); + if (mp->master > max_socket_fd) + max_socket_fd = mp->master; + ++socket_count; + } + for (j=0; jlines; ++j) { + if (mp->ldsc[j].conn) { + units[socket_count] = mp->ldsc[j].uptr; + if (units[socket_count] == NULL) + units[socket_count] = mp->uptr; + sockets[socket_count] = mp->ldsc[j].conn; + FD_SET (mp->ldsc[j].conn, &readfds); + FD_SET (mp->ldsc[j].conn, &errorfds); + if (mp->ldsc[j].conn > max_socket_fd) + max_socket_fd = mp->ldsc[j].conn; + ++socket_count; + } + } + } + pthread_mutex_unlock (&sim_tmxr_poll_lock); + if (timeout_usec > 1000000) + timeout_usec = 1000000; + timeout.tv_sec = 0; + timeout.tv_usec = timeout_usec; + status = select(1+(int)max_socket_fd, &readfds, NULL, &errorfds, &timeout); + wait_count=0; + pthread_mutex_lock (&sim_tmxr_poll_lock); + switch (status) { + case 0: /* timeout */ + for (i=max_socket_fd=socket_count=0; imaster) { + if (!mp->uptr->a_polling_now) { + mp->uptr->a_polling_now = TRUE; + mp->uptr->a_poll_waiter_count = 0; + d = find_dev_from_unit(mp->uptr); + sim_debug (TMXR_DBG_TRC, d, "_tmxr_poll() - Activating %s%d to poll connect\n", d->name, (int)(mp->uptr-d->units)); + _sim_activate (mp->uptr, 0); + } + if (mp->txcount) { + timeout_usec = 10000; /* Wait 10ms next time (this gets doubled below) */ + mp->txcount = 0; + } + } + for (j=0; jlines; ++j) { + if ((mp->ldsc[j].conn) && (mp->ldsc[j].uptr)) { + if (tmxr_tqln(&mp->ldsc[j]) || tmxr_rqln (&mp->ldsc[j])) { + timeout_usec = 10000; /* Wait 10ms next time (this gets doubled below) */ + /* More than one socket can be associated with the + same unit. Make sure to only activate it one time */ + if (!mp->ldsc[j].uptr->a_polling_now) { + mp->ldsc[j].uptr->a_polling_now = TRUE; + mp->ldsc[j].uptr->a_poll_waiter_count = 0; + d = find_dev_from_unit(mp->ldsc[j].uptr); + sim_debug (TMXR_DBG_TRC, d, "_tmxr_poll() - Line %d Activating %s%d to poll data: %d/%d\n", + j, d->name, (int)(mp->ldsc[j].uptr-d->units), tmxr_tqln(&mp->ldsc[j]), tmxr_rqln (&mp->ldsc[j])); + _sim_activate (mp->ldsc[j].uptr, 0); + } + } + } + } + } + sim_debug (TMXR_DBG_TRC, dptr, "_tmxr_poll() - Poll Timeout - %dms\n", timeout_usec/1000); + timeout_usec *= 2; /* Double timeout time */ + break; + case SOCKET_ERROR: + abort(); + break; + default: + wait_count = 0; + for (i=0; ia_polling_now) { + activated[j]->a_polling_now = TRUE; + activated[j]->a_poll_waiter_count = 1; + d = find_dev_from_unit(activated[j]); + sim_debug (TMXR_DBG_TRC, d, "_tmxr_poll() - Activating for data %s%d\n", d->name, (int)(activated[j]-d->units)); + _sim_activate (activated[j], 0); + } + else + ++activated[j]->a_poll_waiter_count; + } + } + } + if (wait_count) + timeout_usec = 10000; /* Wait 10ms next time */ + break; + } + sim_tmxr_poll_count += wait_count; + } +pthread_mutex_unlock (&sim_tmxr_poll_lock); +free(units); +free(activated); +free(sockets); + +sim_debug (TMXR_DBG_TRC, dptr, "_tmxr_poll() - exiting\n"); + +return NULL; +} +#endif +t_stat tmxr_start_poll (void) +{ +#if defined(SIM_ASYNCH_IO) +pthread_mutex_lock (&sim_tmxr_poll_lock); +if ((tmxr_open_device_count > 0) && + sim_asynch_enabled && + sim_is_running && + !sim_tmxr_poll_running) { + pthread_attr_t attr; + + pthread_cond_init (&sim_tmxr_startup_cond, NULL); + pthread_attr_init (&attr); + pthread_attr_setscope (&attr, PTHREAD_SCOPE_SYSTEM); + pthread_create (&sim_tmxr_poll_thread, &attr, _tmxr_poll, NULL); + pthread_attr_destroy( &attr); + pthread_cond_wait (&sim_tmxr_startup_cond, &sim_tmxr_poll_lock); /* Wait for thread to stabilize */ + pthread_cond_destroy (&sim_tmxr_startup_cond); + sim_tmxr_poll_running = TRUE; + } +pthread_mutex_unlock (&sim_tmxr_poll_lock); +#endif +return SCPE_OK; +} + +t_stat tmxr_stop_poll (void) +{ +#if defined(SIM_ASYNCH_IO) +pthread_mutex_lock (&sim_tmxr_poll_lock); +if (sim_tmxr_poll_running) { + pthread_cond_signal (&sim_tmxr_poll_cond); + pthread_mutex_unlock (&sim_tmxr_poll_lock); + pthread_join (sim_tmxr_poll_thread, NULL); + sim_tmxr_poll_running = FALSE; + } +else + pthread_mutex_unlock (&sim_tmxr_poll_lock); +#endif +return SCPE_OK; +} + +static void _tmxr_add_to_open_list (TMXR* mux) +{ +tmxr_open_devices = realloc(tmxr_open_devices, (tmxr_open_device_count+1)*sizeof(*tmxr_open_devices)); +tmxr_open_devices[tmxr_open_device_count++] = mux; +if ((tmxr_open_device_count == 1) && (sim_asynch_enabled)) + tmxr_start_poll (); +} + +static void _tmxr_remove_from_open_list (TMXR* mux) +{ +int i, j; + +#if defined(SIM_ASYNCH_IO) +tmxr_stop_poll (); +pthread_mutex_lock (&sim_tmxr_poll_lock); +#endif +--tmxr_open_device_count; +for (i=0; idptr->name); + fprintf(st, ", attached to %s, ", mp->uptr->filename); + tmxr_show_lines(st, NULL, 0, mp); + fprintf(st, ", "); + tmxr_show_summ(st, NULL, 0, mp); + fprintf(st, ", sessions=%d\n", mp->sessions); + for (j = 0; j < mp->lines; j++) { + lp = mp->ldsc + j; + fprintf (st, "Line: %d", j); + if (lp->uptr && (lp->uptr != lp->mp->uptr)) { + DEVICE *dptr = find_dev_from_unit (lp->uptr); + fprintf (st, " - Unit: %s%d\n", dptr->name, (int)(lp->uptr-dptr->units)); + } + else + fprintf (st, "\n"); + if (!lp->conn) + continue; + tmxr_fconns (st, lp, j); + tmxr_fstats (st, lp, -1); + } + } + } +return SCPE_OK; +} + + + + /* Attach unit to master socket */ -t_stat tmxr_attach (TMXR *mp, UNIT *uptr, char *cptr) +t_stat tmxr_attach_ex (TMXR *mp, UNIT *uptr, char *cptr, t_bool async) { char* tptr; t_stat r; @@ -681,6 +1034,7 @@ tptr = (char *) malloc (strlen (cptr) + /* get string buf */ sizeof(bmsg) + sizeof(lmsg)); if (tptr == NULL) /* no more mem? */ return SCPE_MEM; +mp->uptr = uptr; /* save unit for polling */ r = tmxr_open_master (mp, cptr); /* open master socket */ if (r != SCPE_OK) { /* error? */ free (tptr); /* release buf */ @@ -694,10 +1048,13 @@ if (mp->logfiletmpl[0]) sprintf (tptr, "%s%s%s", pmsg, bmsg, lmsg); /* assemble all */ uptr->filename = tptr; /* save */ uptr->flags = uptr->flags | UNIT_ATT; /* no more errors */ +if (!(uptr->flags & TMUF_NOASYNCH) && async) /* if asynch not disabled */ + uptr->flags |= UNIT_TM_POLL; /* tag as polling unit */ if (mp->dptr == NULL) /* has device been set? */ mp->dptr = find_dev_from_unit (uptr); /* no, so set device now */ +_tmxr_add_to_open_list (mp); return SCPE_OK; } @@ -719,6 +1076,7 @@ for (i = 0; i < mp->lines; i++) { /* loop thru conn */ } /* end for */ sim_close_sock (mp->master, 1); /* close master socket */ mp->master = 0; +_tmxr_remove_from_open_list (mp); return SCPE_OK; } @@ -731,10 +1089,24 @@ if (!(uptr->flags & UNIT_ATT)) /* attached? */ tmxr_close_master (mp); /* close master socket */ free (uptr->filename); /* free port string */ uptr->filename = NULL; -uptr->flags = uptr->flags & ~UNIT_ATT; /* not attached */ +uptr->flags = uptr->flags & ~(UNIT_ATT|UNIT_TM_POLL); /* not attached, no polling */ return SCPE_OK; } +t_stat tmxr_activate (UNIT *uptr, int32 interval) +{ +#if defined(SIM_ASYNCH_IO) +if ((!(uptr->flags & UNIT_TM_POLL)) || + (!sim_asynch_enabled)) { + return _sim_activate (uptr, interval); + } +return SCPE_OK; +#else +return _sim_activate (uptr, interval); +#endif +} + + /* Stub examine and deposit */ t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) @@ -833,6 +1205,7 @@ t_stat r; if (mp == NULL) return SCPE_IERR; +tmxr_debug_trace (mp, "tmxr_dscln()"); if (val) { /* = n form */ if (cptr == NULL) return SCPE_ARG; @@ -1119,9 +1492,7 @@ if (mp == NULL) return SCPE_IERR; for (i = t = 0; i < mp->lines; i++) t = t + (mp->ldsc[i].conn != 0); -if (t == 1) - fprintf (st, "1 connection"); -else fprintf (st, "%d connections", t); +fprintf (st, "%d connection%s", t, (t != 1) ? "s" : ""); return SCPE_OK; } @@ -1210,7 +1581,7 @@ while (*string) tmxr_buf_debug_char (*string++); } -void tmxr_debug (uint32 dbits, TMLN *lp, const char *msg, char *buf, int bufsize) +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; @@ -1235,4 +1606,3 @@ if ((lp->mp->dptr) && (dbits & lp->mp->dptr->dctrl)) { sim_debug (dbits, lp->mp->dptr, "%s %d bytes '%s'\n", msg, bufsize, tmxr_debug_buf); } } - diff --git a/sim_tmxr.h b/sim_tmxr.h index 72826a04..fa607f6a 100644 --- a/sim_tmxr.h +++ b/sim_tmxr.h @@ -1,4 +1,4 @@ -/* sim_tmxr.h: terminal multiplexor definitions +/* sim_tmxr.h: terminal multiplexer definitions Copyright (c) 2001-2008, Robert M Supnik @@ -44,6 +44,8 @@ #ifndef _SIM_TMXR_H_ #define _SIM_TMXR_H_ 0 +#include "sim_sock.h" + #define TMXR_V_VALID 15 #define TMXR_VALID (1 << TMXR_V_VALID) #define TMXR_MAXBUF 256 /* buffer size */ @@ -51,6 +53,12 @@ #define TMXR_DBG_XMT 0x10000 /* Debug Transmit Data */ #define TMXR_DBG_RCV 0x20000 /* Debug Received Data */ +#define TMXR_DBG_TRC 0x40000 /* Debug trace routine calls */ + +/* Unit flags */ + +#define TMUF_V_NOASYNCH (UNIT_V_UF + 12) /* Asynch Disabled unit */ +#define TMUF_NOASYNCH (1u << TMUF_V_NOASYNCH) typedef struct tmln TMLN; typedef struct tmxr TMXR; @@ -79,6 +87,7 @@ struct tmln { char rbr[TMXR_MAXBUF]; /* rcv break */ char *txb; /* xmt buffer */ TMXR *mp; /* back pointer to mux */ + UNIT *uptr; /* pointer to receive poll unit */ }; struct tmxr { @@ -88,8 +97,11 @@ struct tmxr { TMLN *ldsc; /* line descriptors */ int32 *lnorder; /* line connection order */ DEVICE *dptr; /* multiplexer device */ + UNIT *uptr; /* polling unit */ char logfiletmpl[FILENAME_MAX]; /* template logfile name */ + int32 txcount; /* count of transmit bytes */ int32 buffered; /* Buffered Line Behavior and Buffer Size Flag */ + int32 sessions; /* count of tcp connections received */ }; int32 tmxr_poll_conn (TMXR *mp); @@ -100,8 +112,10 @@ t_stat tmxr_putc_ln (TMLN *lp, int32 chr); void tmxr_poll_tx (TMXR *mp); t_stat tmxr_open_master (TMXR *mp, char *cptr); t_stat tmxr_close_master (TMXR *mp); -t_stat tmxr_attach (TMXR *mp, UNIT *uptr, char *cptr); +t_stat tmxr_attach_ex (TMXR *mp, UNIT *uptr, char *cptr, t_bool async); t_stat tmxr_detach (TMXR *mp, UNIT *uptr); +t_stat tmxr_set_line_unit (TMXR *mp, int line, UNIT *uptr_poll); +t_stat tmxr_set_console_input_unit (UNIT *uptr); t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw); t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw); void tmxr_msg (SOCKET sock, char *msg); @@ -119,7 +133,27 @@ t_stat tmxr_show_lnorder (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat tmxr_show_summ (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat tmxr_show_cstat (FILE *st, UNIT *uptr, int32 val, void *desc); t_stat tmxr_show_lines (FILE *st, UNIT *uptr, int32 val, void *desc); -void tmxr_debug (uint32 dbits, TMLN *lp, const char *msg, char *buf, int bufsize); +t_stat tmxr_show_open_devices (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, char* desc); +t_stat tmxr_activate (UNIT *uptr, int32 interval); +t_stat tmxr_change_async (void); +t_stat tmxr_startup (void); +t_stat tmxr_shutdown (void); +t_stat tmxr_start_poll (void); +t_stat tmxr_stop_poll (void); +void _tmxr_debug (uint32 dbits, TMLN *lp, const char *msg, char *buf, int bufsize); +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 +#if defined(SIM_ASYNCH_IO) && !defined(NO_ASYNCH_MUX) +#define tmxr_attach(mp, uptr, cptr) tmxr_attach_ex(mp, uptr, cptr, TRUE) +#if (!defined(NOT_MUX_USING_CODE)) +#define sim_activate tmxr_activate +#endif +#else +#define tmxr_attach(mp, uptr, cptr) tmxr_attach_ex(mp, uptr, cptr, FALSE) #endif + +#endif /* _SIM_TMXR_H_ */