Asynchronous Multiplexer and Console Support

scp.c, scp.h, sim_defs.h
     - Added commands:
          SHOW MULTIPLEXER (MUX)
          SHOW TIMERS
     - Added facilities/APIs:
          sim_activate_after - time specific event scheduling (vs instruction scheduling) API visible, optional separate thread implementation in a later revision
     - Changed Commands:
          SET CONSOLE DEBUG no longer affects global debugging, but merely debugging for the console subsystem.  Use SET DEBUG and SET NODEBUG to affect global debugging.
     - Added Asynchronous polling support

sim_tmxr.h, sim_tmxr.c
     - Added Asynchronous capabilities to the multiplexer subsystem to avoid polling for input and to deliver input data instantly when it arrives instead of delaying for up to one or more full simulated clock ticks.
     - Added debug trace support
     - Added statistic tracking of total bytes transmitted on each line
     - Added more aggressive attempts to flush transmit buffers when they fill before dropping tranmitted characters
     - Fixed status return of tmxr_putc_ln to return SCPE_LOST if the transmitting line isn't connected or buffered.

sim_console.h, sim_console.c
     - Fixed issue where connections to console telnet sessions would succeed for the first connection, but hang indefinitely for additional connects without rejecting due to all lines being busy.  This is handled by using an internal device and unit to hang the required polling on.  Connection polls happen once per second.
     - Added console debugging/trace support.
     - Added Asynchronous capabilities to the console subsystem to avoid polling for input and to deliver input data instantly when it arrives instead of delaying for up to one or more full simulated clock ticks.
     - Added tmxr_set_console_input_unit() API to support asynchronous simulator console I/O

sim_timer.h, sim_timer.c
     - Added SHOW TIMERS support
     - Added mechanism to capture the timer the simulator uses for its clock tick and make this timer globally available for other uses

PDP11/pdp11_dz.c
     - Added debug trace support

PDP11/pdp11_vh.c
     - Added debug trace support
     - Changed timing mechanisms to not assume that the count unit service routine calls measures the passage of time, and created a separate unit to measure time.

VAX/vax_stddev.c
     - Added call to tmxr_set_console_input_unit to leverage Asynchronous console I/O
This commit is contained in:
Mark Pizzolato 2012-05-12 13:42:44 -07:00
parent e3bdb12aeb
commit 7c38b83d7c
13 changed files with 1242 additions and 166 deletions

View file

@ -21,22 +21,27 @@ Features.
point of view) at least 'n' instructions after it was initiated. point of view) at least 'n' instructions after it was initiated.
Benefits. Benefits.
Allows a simulator to execute simulated instructions concurrently - Allows a simulator to execute simulated instructions concurrently
with I/O operations which may take numerous milliseconds to perform. with I/O operations which may take numerous milliseconds to perform.
Allows a simulated device to potentially avoid polling for the arrival - Allows a simulated device to potentially avoid polling for the
of data. Polling consumes host processor CPU cycles which may better arrival of data. Polling consumes host processor CPU cycles which
be spent executing simulated instructions or letting other host may better be spent executing simulated instructions or letting
processes run. Measurements made of available instruction execution other host processes run. Measurements made of available
easily demonstrate the benefits of parallel instruction and I/O instruction execution easily demonstrate the benefits of parallel
activities. A VAX simulator with a process running a disk intensive instruction and I/O activities. A VAX simulator with a process
application in one process was able to process 11 X the number of running a disk intensive application in one process was able to
Dhrystone operations with Asynch I/O enabled. 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. Asynch I/O is provided through a callback model.
SimH Libraries which provide Asynch I/O support: SimH Libraries which provide Asynch I/O support:
sim_disk sim_disk
sim_tape sim_tape
sim_ether sim_ether
sim_console
sim_tmxr
Requirements to use: Requirements to use:
The Simulator's instruction loop needs to be modified to include a single 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 }, { DRDATA (INST_LATENCY, sim_asynch_inst_latency, 32), PV_LEFT },
#endif #endif
Programming Disk and Tape devices to leverage Asynch I/O
Naming conventions: Naming conventions:
All of the routines implemented in sim_disk and sim_tape have been kept 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 then it must either be recomputed prior to the top/bottom half check
or not stored in local variables of the unit service routine. 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. Sample Asynch I/O device implementations.
The pdp11_rq.c module has been refactored to leverage the asynch I/O 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 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 This approach will dovetail well with a potential future addition of
operations on physical tapes as yet another supported tape format. 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.

View file

@ -163,12 +163,14 @@ TMXR dz_desc = { DZ_MUXES * DZ_LINES, 0, 0, dz_ldsc }; /* mux descriptor */
/* debugging bitmaps */ /* debugging bitmaps */
#define DBG_REG 0x0001 /* trace read/write registers */ #define DBG_REG 0x0001 /* trace read/write registers */
#define DBG_INT 0x0002 /* display transfer requests */ #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_XMT TMXR_DBG_XMT /* display Transmitted Data */
#define DBG_RCV TMXR_DBG_RCV /* display Received Data */ #define DBG_RCV TMXR_DBG_RCV /* display Received Data */
DEBTAB dz_debug[] = { DEBTAB dz_debug[] = {
{"REG", DBG_REG}, {"REG", DBG_REG},
{"INT", DBG_INT}, {"INT", DBG_INT},
{"TRC", DBG_TRC},
{"XMT", DBG_XMT}, {"XMT", DBG_XMT},
{"RCV", DBG_RCV}, {"RCV", DBG_RCV},
{0} {0}
@ -421,6 +423,8 @@ t_stat dz_svc (UNIT *uptr)
{ {
int32 dz, t, newln; 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 */ for (dz = t = 0; dz < DZ_MUXES; dz++) /* check enabled */
t = t | (dz_csr[dz] & CSR_MSE); t = t | (dz_csr[dz] & CSR_MSE);
if (t) { /* any enabled? */ if (t) { /* any enabled? */

View file

@ -82,7 +82,6 @@ extern int32 int_req[IPL_HLVL];
extern uint32 cpu_opt; extern uint32 cpu_opt;
#endif #endif
#include "sim_sock.h"
#include "sim_tmxr.h" #include "sim_tmxr.h"
/* imports from pdp11_stddev.c: */ /* imports from pdp11_stddev.c: */
@ -293,12 +292,14 @@ static TMLX vh_parm[VH_MUXES * VH_LINES] = { { 0 } };
/* debugging bitmaps */ /* debugging bitmaps */
#define DBG_REG 0x0001 /* trace read/write registers */ #define DBG_REG 0x0001 /* trace read/write registers */
#define DBG_INT 0x0002 /* display transfer requests */ #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_XMT TMXR_DBG_XMT /* display Transmitted Data */
#define DBG_RCV TMXR_DBG_RCV /* display Received Data */ #define DBG_RCV TMXR_DBG_RCV /* display Received Data */
DEBTAB vh_debug[] = { DEBTAB vh_debug[] = {
{"REG", DBG_REG}, {"REG", DBG_REG},
{"INT", DBG_INT}, {"INT", DBG_INT},
{"TRC", DBG_TRC},
{"XMT", DBG_XMT}, {"XMT", DBG_XMT},
{"RCV", DBG_RCV}, {"RCV", DBG_RCV},
{0} {0}
@ -308,6 +309,7 @@ DEBTAB vh_debug[] = {
static t_stat vh_rd (int32 *data, int32 PA, int32 access); 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_wr (int32 data, int32 PA, int32 access);
static t_stat vh_svc (UNIT *uptr); static t_stat vh_svc (UNIT *uptr);
static t_stat vh_timersvc (UNIT *uptr);
static int32 vh_rxinta (void); static int32 vh_rxinta (void);
static int32 vh_txinta (void); static int32 vh_txinta (void);
static t_stat vh_clear (int32 vh, t_bool flag); static t_stat vh_clear (int32 vh, t_bool flag);
@ -341,6 +343,7 @@ static DIB vh_dib = {
static UNIT vh_unit[VH_MUXES] = { static UNIT vh_unit[VH_MUXES] = {
{ UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0) }, { UDATA (&vh_svc, UNIT_IDLE|UNIT_ATTABLE, 0) },
{ UDATA (&vh_timersvc, 0, 0) },
}; };
static const REG vh_reg[] = { 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)) if ((vh_unit[vh].flags & UNIT_MODEDHU) && (data & CSR_SKIP))
data &= ~CSR_MASTER_RESET; data &= ~CSR_MASTER_RESET;
if (vh == 0) /* Only start unit service on the first unit. Units are polled there */ if (vh == 0) /* Only start unit service on the first unit. Units are polled there */
sim_activate (&vh_unit[vh], clk_cosched (tmxr_poll)); sim_activate (&vh_unit[0], clk_cosched (tmxr_poll));
/* vh_mcount[vh] = 72; */ /* 1.2 seconds */
vh_mcount[vh] = MS2SIMH (1200); /* 1.2 seconds */ vh_mcount[vh] = MS2SIMH (1200); /* 1.2 seconds */
sim_activate (&vh_unit[1], clk_cosched (tmxr_poll));
} }
if ((data & CSR_RXIE) == 0) if ((data & CSR_RXIE) == 0)
vh_clr_rxint (vh); vh_clr_rxint (vh);
@ -872,6 +875,7 @@ static t_stat vh_wr ( int32 data,
break; break;
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
vh_mcount[vh] = 1; vh_mcount[vh] = 1;
sim_activate (&vh_unit[1], clk_cosched (tmxr_poll));
break; break;
} }
if (vh_unit[vh].flags & UNIT_MODEDHU) { if (vh_unit[vh].flags & UNIT_MODEDHU) {
@ -916,6 +920,7 @@ static t_stat vh_wr ( int32 data,
case 2: /* LPR */ case 2: /* LPR */
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
vh_mcount[vh] = 1; vh_mcount[vh] = 1;
sim_activate (&vh_unit[1], clk_cosched (tmxr_poll));
break; break;
} }
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
@ -942,6 +947,7 @@ static t_stat vh_wr ( int32 data,
case 3: /* STAT/FIFODATA */ case 3: /* STAT/FIFODATA */
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
vh_mcount[vh] = 1; vh_mcount[vh] = 1;
sim_activate (&vh_unit[1], clk_cosched (tmxr_poll));
break; break;
} }
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
@ -965,6 +971,7 @@ static t_stat vh_wr ( int32 data,
case 4: /* LNCTRL */ case 4: /* LNCTRL */
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
vh_mcount[vh] = 1; vh_mcount[vh] = 1;
sim_activate (&vh_unit[1], clk_cosched (tmxr_poll));
break; break;
} }
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
@ -1032,6 +1039,7 @@ static t_stat vh_wr ( int32 data,
case 5: /* TBUFFAD1 */ case 5: /* TBUFFAD1 */
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
vh_mcount[vh] = 1; vh_mcount[vh] = 1;
sim_activate (&vh_unit[1], clk_cosched (tmxr_poll));
break; break;
} }
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
@ -1047,6 +1055,7 @@ static t_stat vh_wr ( int32 data,
case 6: /* TBUFFAD2 */ case 6: /* TBUFFAD2 */
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
vh_mcount[vh] = 1; vh_mcount[vh] = 1;
sim_activate (&vh_unit[1], clk_cosched (tmxr_poll));
break; break;
} }
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
@ -1067,6 +1076,7 @@ static t_stat vh_wr ( int32 data,
case 7: /* TBUFFCT */ case 7: /* TBUFFCT */
if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) { if ((data == RESET_ABORT) && (vh_csr[vh] & CSR_MASTER_RESET)) {
vh_mcount[vh] = 1; vh_mcount[vh] = 1;
sim_activate (&vh_unit[1], clk_cosched (tmxr_poll));
break; break;
} }
if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES) if (CSR_GETCHAN (vh_csr[vh]) >= VH_LINES)
@ -1125,19 +1135,35 @@ static void doDMA ( int32 vh,
/* Perform many of the functions of PROC2 */ /* 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 */ /* scan all muxes for countdown reset */
again = 0;
for (vh = 0; vh < vh_desc.lines/VH_LINES; vh++) { for (vh = 0; vh < vh_desc.lines/VH_LINES; vh++) {
if (vh_csr[vh] & CSR_MASTER_RESET) { if (vh_csr[vh] & CSR_MASTER_RESET) {
if (vh_mcount[vh] != 0) if (vh_mcount[vh] != 0) {
vh_mcount[vh] -= 1; vh_mcount[vh] -= 1;
++again;
}
else else
vh_clear (vh, FALSE); 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) */ /* sample every 10ms for modem changes (new connections) */
newln = tmxr_poll_conn (&vh_desc); newln = tmxr_poll_conn (&vh_desc);
if (newln >= 0) { if (newln >= 0) {

View file

@ -340,6 +340,7 @@ return SCPE_OK;
t_stat tti_reset (DEVICE *dptr) t_stat tti_reset (DEVICE *dptr)
{ {
tmxr_set_console_input_unit (&tti_unit);
tti_unit.buf = 0; tti_unit.buf = 0;
tti_csr = 0; tti_csr = 0;
CLR_INT (TTI); CLR_INT (TTI);

220
scp.c
View file

@ -213,6 +213,8 @@
/* Macros and data structures */ /* Macros and data structures */
#define NOT_MUX_USING_CODE /* sim_tmxr library provider or agnostic */
#include "sim_defs.h" #include "sim_defs.h"
#include "sim_rev.h" #include "sim_rev.h"
#include "sim_ether.h" #include "sim_ether.h"
@ -287,8 +289,12 @@
#if defined (SIM_ASYNCH_IO) #if defined (SIM_ASYNCH_IO)
pthread_mutex_t sim_asynch_lock = PTHREAD_MUTEX_INITIALIZER; pthread_mutex_t sim_asynch_lock = PTHREAD_MUTEX_INITIALIZER;
pthread_cond_t sim_asynch_wake = PTHREAD_COND_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; 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; t_bool sim_asynch_enabled = TRUE;
int32 sim_asynch_check; int32 sim_asynch_check;
int32 sim_asynch_latency = 4000; /* 4 usec interrupt latency */ 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" "set console LOG=log_file enable console logging to the\n"
" specified destination {STDOUT,DEBUG or filename)\n" " specified destination {STDOUT,DEBUG or filename)\n"
"set console NOLOG disable console logging\n" "set console NOLOG disable console logging\n"
"set console DEBUG=dbg_file\n" "set console DEBUG{=TRC;XMT;RCV}\n"
" enable console debugging to the\n" " enable console debugging of the\n"
" specified destination {LOG,STDOUT or filename)\n" " specified debug bit flags\n"
"set console NODEBUG disable console debugging\n" "set console NODEBUG{=TRC;XMT;RCV}\n"
"set log log_file specify the log destination\n" " disable console debugging bits indicated\n"
" (STDOUT,DEBUG or filename)\n"
"set nolog disables any currently active logging\n" "set nolog disables any currently active logging\n"
"set debug debug_file specify the debug destination\n" "set debug debug_file specify the debug destination\n"
" (STDOUT,LOG or filename)\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 */ stat = SCPE_BARE_STATUS(stat); /* remove possible flag */
sim_last_cmd_stat = stat; /* save command error status */ sim_last_cmd_stat = stat; /* save command error status */
if ((stat >= SCPE_BASE) && (!stat_nomessage)) { /* error? */ if ((stat >= SCPE_BASE) && (!stat_nomessage)) { /* error? */
if (cmdp->message) /* special message handler? */ if (cmdp && cmdp->message) /* special message handler? */
cmdp->message (NULL, stat); cmdp->message (NULL, stat);
else { else {
printf ("%s\n", sim_error_text (stat)); printf ("%s\n", sim_error_text (stat));
@ -1196,14 +1201,14 @@ do {
(stat != SCPE_STEP)) { (stat != SCPE_STEP)) {
if (!echo && !sim_quiet && /* report if not echoing */ if (!echo && !sim_quiet && /* report if not echoing */
!stat_nomessage && /* and not suppressing messages */ !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); printf("%s> %s\n", do_position(), ocptr);
if (sim_log) if (sim_log)
fprintf (sim_log, "%s> %s\n", do_position(), ocptr); fprintf (sim_log, "%s> %s\n", do_position(), ocptr);
} }
} }
if ((stat >= SCPE_BASE) && !stat_nomessage) { /* report error if not suppressed */ 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); cmdp->message ((!echo && !sim_quiet) ? ocptr : NULL, stat);
} }
else { else {
@ -1681,6 +1686,7 @@ if (cptr && (*cptr != 0)) /* now eol? */
if (flag == sim_asynch_enabled) /* already set correctly? */ if (flag == sim_asynch_enabled) /* already set correctly? */
return SCPE_OK; return SCPE_OK;
sim_asynch_enabled = flag; sim_asynch_enabled = flag;
tmxr_change_async ();
if (1) { if (1) {
uint32 i, j; uint32 i, j;
DEVICE *dptr; DEVICE *dptr;
@ -2042,6 +2048,9 @@ static SHTAB show_glob_tab[] = {
{ "THROTTLE", &sim_show_throt, 0 }, { "THROTTLE", &sim_show_throt, 0 },
{ "ASYNCH", &sim_show_asynch, 0 }, { "ASYNCH", &sim_show_asynch, 0 },
{ "ETHERNET", &eth_show_devices, 0 }, { "ETHERNET", &eth_show_devices, 0 },
{ "MULTIPLEXER", &tmxr_show_open_devices, 0 },
{ "MUX", &tmxr_show_open_devices, 0 },
{ "TIMERS", &sim_show_timers, 0 },
{ "ON", &show_on, 0 }, { "ON", &show_on, 0 },
{ NULL, NULL, 0 } { NULL, NULL, 0 }
}; };
@ -2277,28 +2286,46 @@ int32 accum;
if (cptr && (*cptr != 0)) if (cptr && (*cptr != 0))
return SCPE_2MARG; return SCPE_2MARG;
if (sim_clock_queue == NULL) { if (sim_clock_queue == NULL)
fprintf (st, "%s event queue empty, time = %.0f\n", fprintf (st, "%s event queue empty, time = %.0f\n",
sim_name, sim_time); sim_name, sim_time);
return SCPE_OK; else {
} fprintf (st, "%s event queue status, time = %.0f\n",
fprintf (st, "%s event queue status, time = %.0f\n", sim_name, sim_time);
sim_name, sim_time); accum = 0;
accum = 0; for (uptr = sim_clock_queue; uptr != NULL; uptr = uptr->next) {
for (uptr = sim_clock_queue; uptr != NULL; uptr = uptr->next) { if (uptr == &sim_step_unit)
if (uptr == &sim_step_unit) fprintf (st, " Step timer");
fprintf (st, " Step timer"); else if ((dptr = find_dev_from_unit (uptr)) != NULL) {
else if ((dptr = find_dev_from_unit (uptr)) != NULL) { fprintf (st, " %s", sim_dname (dptr));
fprintf (st, " %s", sim_dname (dptr)); if (dptr->numunits > 1)
if (dptr->numunits > 1) fprintf (st, " unit %d", (int32) (uptr - dptr->units));
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) #if defined (SIM_ASYNCH_IO)
pthread_mutex_lock (&sim_asynch_lock); 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"); fprintf (st, "asynchronous pending event queue\n");
if (sim_asynch_queue == AIO_LIST_END) if (sim_asynch_queue == AIO_LIST_END)
fprintf (st, "Empty\n"); 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++, for (l = 0; (l < SRBSIZ) && (k < high); l++,
k = k + (dptr->aincr)) { /* check for 0 block */ k = k + (dptr->aincr)) { /* check for 0 block */
r = dptr->examine (&val, k, uptr, SIM_SW_REST); r = dptr->examine (&val, k, uptr, SIM_SW_REST);
if (r != SCPE_OK) if (r != SCPE_OK) {
free (mbuf);
return r; return r;
}
if (val) zeroflg = FALSE; if (val) zeroflg = FALSE;
SZ_STORE (sz, val, mbuf, l); SZ_STORE (sz, val, mbuf, l);
} /* end for l */ } /* end for l */
@ -3358,19 +3387,26 @@ for ( ;; ) { /* device loop */
if ((mbuf = calloc (SRBSIZ, sz)) == NULL) if ((mbuf = calloc (SRBSIZ, sz)) == NULL)
return SCPE_MEM; return SCPE_MEM;
for (k = 0; k < high; ) { /* loop thru 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? */ if (blkcnt < 0) /* compressed? */
limit = -blkcnt; limit = -blkcnt;
else limit = (int32)sim_fread (mbuf, sz, blkcnt, rfile); 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; return SCPE_IOERR;
}
for (j = 0; j < limit; j++, k = k + (dptr->aincr)) { for (j = 0; j < limit; j++, k = k + (dptr->aincr)) {
if (blkcnt < 0) /* compressed? */ if (blkcnt < 0) /* compressed? */
val = 0; val = 0;
else SZ_LOAD (sz, val, mbuf, j); /* saved value */ else SZ_LOAD (sz, val, mbuf, j); /* saved value */
r = dptr->deposit (val, k, uptr, SIM_SW_REST); r = dptr->deposit (val, k, uptr, SIM_SW_REST);
if (r != SCPE_OK) if (r != SCPE_OK) {
free (mbuf);
return r; return r;
}
} /* end for j */ } /* end for j */
} /* end for k */ } /* end for k */
free (mbuf); /* dealloc buffer */ free (mbuf); /* dealloc buffer */
@ -3533,23 +3569,32 @@ for (i = 1; (dptr = sim_devices[i]) != NULL; i++) { /* reposition all */
} }
} }
stop_cpu = 0; stop_cpu = 0;
sim_is_running = 1; /* flag running */
if (sim_ttrun () != SCPE_OK) { /* set console mode */ if (sim_ttrun () != SCPE_OK) { /* set console mode */
sim_ttcmd (); sim_ttcmd ();
sim_is_running = 0; /* flag idle */
return SCPE_TTYERR; return SCPE_TTYERR;
} }
if ((r = sim_check_console (30)) != SCPE_OK) { /* check console, error? */ if ((r = sim_check_console (30)) != SCPE_OK) { /* check console, error? */
sim_ttcmd (); sim_ttcmd ();
sim_is_running = 0; /* flag idle */
return r; return r;
} }
if (signal (SIGINT, int_handler) == SIG_ERR) { /* set WRU */ if (signal (SIGINT, int_handler) == SIG_ERR) { /* set WRU */
sim_ttcmd ();
sim_is_running = 0; /* flag idle */
return SCPE_SIGERR; return SCPE_SIGERR;
} }
#ifdef SIGHUP #ifdef SIGHUP
if (signal (SIGHUP, int_handler) == SIG_ERR) { /* set WRU */ if (signal (SIGHUP, int_handler) == SIG_ERR) { /* set WRU */
sim_ttcmd ();
sim_is_running = 0; /* flag idle */
return SCPE_SIGERR; return SCPE_SIGERR;
} }
#endif #endif
if (signal (SIGTERM, int_handler) == SIG_ERR) { /* set WRU */ if (signal (SIGTERM, int_handler) == SIG_ERR) { /* set WRU */
sim_ttcmd ();
sim_is_running = 0; /* flag idle */
return SCPE_SIGERR; return SCPE_SIGERR;
} }
if (sim_step) /* set step timer */ if (sim_step) /* set step timer */
@ -3558,7 +3603,6 @@ fflush(stdout); /* flush stdout */
if (sim_log) /* flush log if enabled */ if (sim_log) /* flush log if enabled */
fflush (sim_log); fflush (sim_log);
sim_throt_sched (); /* set throttle */ sim_throt_sched (); /* set throttle */
sim_is_running = 1; /* flag running */
sim_brk_clract (); /* defang actions */ sim_brk_clract (); /* defang actions */
sim_rtcn_init_all (); /* re-init clocks */ sim_rtcn_init_all (); /* re-init clocks */
r = sim_instr(); r = sim_instr();
@ -4697,6 +4741,32 @@ for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* base + unit#? */
return NULL; 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 /* Find_dev_from_unit find device for unit
Inputs: Inputs:
@ -4712,7 +4782,14 @@ uint32 i, j;
if (uptr == NULL) if (uptr == NULL)
return 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; i<sim_internal_device_count; ++i) {
dptr = sim_internal_devices[i];
for (j = 0; j < dptr->numunits; j++) { for (j = 0; j < dptr->numunits; j++) {
if (uptr == (dptr->units + j)) if (uptr == (dptr->units + j))
return dptr; return dptr;
@ -5147,6 +5224,10 @@ return SCPE_OK;
/* Event queue package /* Event queue package
sim_activate add entry to event queue 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_cancel remove entry from event queue
sim_process_event process entries on event queue sim_process_event process entries on event queue
sim_is_active see if entry is on event queue sim_is_active see if entry is on event queue
@ -5196,9 +5277,11 @@ do {
if (sim_clock_queue != NULL) if (sim_clock_queue != NULL)
sim_interval = sim_clock_queue->time; sim_interval = sim_clock_queue->time;
else sim_interval = noqueue_time = NOQUEUE_WAIT; else sim_interval = noqueue_time = NOQUEUE_WAIT;
AIO_POLL_BEGIN(uptr);
if (uptr->action != NULL) if (uptr->action != NULL)
reason = uptr->action (uptr); reason = uptr->action (uptr);
else reason = SCPE_OK; else reason = SCPE_OK;
AIO_POLL_COMPLETE(uptr, reason);
} while ((reason == SCPE_OK) && (sim_interval == 0)); } while ((reason == SCPE_OK) && (sim_interval == 0));
/* Empty queue forces sim_interval != 0 */ /* Empty queue forces sim_interval != 0 */
@ -5215,6 +5298,11 @@ return reason;
reason = result (SCPE_OK if ok) 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) t_stat sim_activate (UNIT *uptr, int32 event_time)
{ {
UNIT *cptr, *prvptr; UNIT *cptr, *prvptr;
@ -5293,6 +5381,74 @@ else
return sim_activate (uptr, urtime-rtimenow); 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 /* sim_cancel - cancel (dequeue) event
Inputs: Inputs:

3
scp.h
View file

@ -85,8 +85,10 @@ t_stat echo_cmd (int32 flag, char *ptr);
t_stat sim_process_event (void); 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 (UNIT *uptr, int32 interval);
t_stat sim_activate_abs (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_notbefore (UNIT *uptr, int32 rtime);
t_stat sim_activate_after (UNIT *uptr, int32 usecs_walltime);
t_stat sim_cancel (UNIT *uptr); t_stat sim_cancel (UNIT *uptr);
int32 sim_is_active (UNIT *uptr); int32 sim_is_active (UNIT *uptr);
double sim_gtime (void); double sim_gtime (void);
@ -113,6 +115,7 @@ CTAB *find_cmd (char *gbuf);
DEVICE *find_dev (char *ptr); DEVICE *find_dev (char *ptr);
DEVICE *find_unit (char *ptr, UNIT **uptr); DEVICE *find_unit (char *ptr, UNIT **uptr);
DEVICE *find_dev_from_unit (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); REG *find_reg (char *ptr, char **optr, DEVICE *dptr);
CTAB *find_ctab (CTAB *tab, char *gbuf); CTAB *find_ctab (CTAB *tab, char *gbuf);
C1TAB *find_c1tab (C1TAB *tab, char *gbuf); C1TAB *find_c1tab (C1TAB *tab, char *gbuf);

View file

@ -118,10 +118,21 @@
*/ */
#include "sim_defs.h" #include "sim_defs.h"
#include "sim_sock.h"
#include "sim_tmxr.h" #include "sim_tmxr.h"
#include "sim_timer.h"
#include <ctype.h> #include <ctype.h>
/* 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_WRU 0
#define KMAP_BRK 1 #define KMAP_BRK 1
#define KMAP_DEL 2 #define KMAP_DEL 2
@ -136,14 +147,46 @@ int32 sim_del_char = '\b'; /* delete character */
#else #else
int32 sim_del_char = 0177; int32 sim_del_char = 0177;
#endif #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 volatile int32 stop_cpu;
extern int32 sim_quiet; extern int32 sim_quiet;
extern FILE *sim_log, *sim_deb; extern FILE *sim_log, *sim_deb;
extern FILEREF *sim_log_ref, *sim_deb_ref; extern FILEREF *sim_log_ref, *sim_deb_ref;
extern DEVICE *sim_devices[]; 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 */ /* Set/show data structures */
@ -156,8 +199,8 @@ static CTAB set_con_tab[] = {
{ "NOTELNET", &sim_set_notelnet, 0 }, { "NOTELNET", &sim_set_notelnet, 0 },
{ "LOG", &sim_set_logon, 0 }, { "LOG", &sim_set_logon, 0 },
{ "NOLOG", &sim_set_logoff, 0 }, { "NOLOG", &sim_set_logoff, 0 },
{ "DEBUG", &sim_set_debon, 0 }, { "DEBUG", &sim_set_cons_debug, 1 },
{ "NODEBUG", &sim_set_deboff, 0 }, { "NODEBUG", &sim_set_cons_debug, 0 },
{ NULL, NULL, 0 } { NULL, NULL, 0 }
}; };
@ -168,7 +211,7 @@ static SHTAB show_con_tab[] = {
{ "PCHAR", &sim_show_pchar, 0 }, { "PCHAR", &sim_show_pchar, 0 },
{ "LOG", &sim_show_cons_log, 0 }, { "LOG", &sim_show_cons_log, 0 },
{ "TELNET", &sim_show_telnet, 0 }, { "TELNET", &sim_show_telnet, 0 },
{ "DEBUG", &sim_show_debug, 0 }, { "DEBUG", &sim_show_cons_debug, 0 },
{ "BUFFERED", &sim_show_cons_buff, 0 }, { "BUFFERED", &sim_show_cons_buff, 0 },
{ NULL, NULL, 0 } { NULL, NULL, 0 }
}; };
@ -431,7 +474,9 @@ while (*cptr != 0) { /* do all mods */
if (isdigit (*gbuf)) { if (isdigit (*gbuf)) {
if (sim_con_tmxr.master) /* already open? */ if (sim_con_tmxr.master) /* already open? */
sim_set_notelnet (0, NULL); /* close first */ 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 else
if ((ctptr = find_ctab (set_con_telnet_tab, gbuf))) { /* match? */ if ((ctptr = find_ctab (set_con_telnet_tab, gbuf))) { /* match? */
@ -476,6 +521,28 @@ else {
return SCPE_OK; 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 */ /* Set console to Buffering */
@ -564,7 +631,7 @@ if (strcmp (gbuf, "LOG") == 0) { /* output to log? */
++(*pref)->refcount; ++(*pref)->refcount;
} }
else if (strcmp (gbuf, "DEBUG") == 0) { /* output to debug? */ else if (strcmp (gbuf, "DEBUG") == 0) { /* output to debug? */
if (sim_deb == NULL) /* any debug? */ if (sim_deb == NULL) /* any debug? */
return SCPE_ARG; return SCPE_ARG;
*pf = sim_deb; *pf = sim_deb;
*pref = sim_deb_ref; *pref = sim_deb_ref;
@ -685,8 +752,14 @@ t_stat sim_poll_kbd (void)
int32 c; int32 c;
c = sim_os_poll_kbd (); /* get character */ 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 */ return c; /* in-window */
}
if (sim_con_ldsc.conn == 0) { /* no Telnet conn? */ if (sim_con_ldsc.conn == 0) { /* no Telnet conn? */
if (!sim_con_ldsc.txbfd) /* unbuffered? */ if (!sim_con_ldsc.txbfd) /* unbuffered? */
return SCPE_LOST; /* connection lost */ 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_con_tmxr.master == 0) { /* not Telnet? */
if (sim_log) /* log file? */ if (sim_log) /* log file? */
fputc (c, sim_log); fputc (c, sim_log);
sim_debug (DBG_XMT, &sim_con_telnet, "Sending: '%c'\n", c);
return sim_os_putchar (c); /* in-window version */ return sim_os_putchar (c); /* in-window version */
} }
if (sim_log && !sim_con_ldsc.txlog) /* log file, but no line log? */ 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_con_tmxr.master == 0) { /* not Telnet? */
if (sim_log) /* log file? */ if (sim_log) /* log file? */
fputc (c, sim_log); fputc (c, sim_log);
sim_debug (DBG_XMT, &sim_con_telnet, "Sending: '%c'\n", c);
return sim_os_putchar (c); /* in-window version */ return sim_os_putchar (c); /* in-window version */
} }
if (sim_log && !sim_con_ldsc.txlog) /* log file, but no line log? */ if (sim_log && !sim_con_ldsc.txlog) /* log file, but no line log? */
@ -789,6 +864,139 @@ else c = c & 0377;
return c; 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 */ /* VMS routines, from Ben Thomas, with fixes from Robert Alan Byer */
#if defined (VMS) #if defined (VMS)
@ -796,6 +1004,7 @@ return c;
#if defined(__VAX) #if defined(__VAX)
#define sys$assign SYS$ASSIGN #define sys$assign SYS$ASSIGN
#define sys$qiow SYS$QIOW #define sys$qiow SYS$QIOW
#define sys$dassgn SYS$DASSGN
#endif #endif
#include <descrip.h> #include <descrip.h>
@ -808,6 +1017,7 @@ return c;
#define EFN 0 #define EFN 0
uint32 tty_chan = 0; uint32 tty_chan = 0;
int buffered_character = 0;
typedef struct { typedef struct {
unsigned short sense_count; unsigned short sense_count;
@ -824,7 +1034,7 @@ typedef struct {
SENSE_BUF cmd_mode = { 0 }; SENSE_BUF cmd_mode = { 0 };
SENSE_BUF run_mode = { 0 }; SENSE_BUF run_mode = { 0 };
t_stat sim_ttinit (void) t_stat sim_os_ttinit (void)
{ {
unsigned int status; unsigned int status;
IOSB iosb; IOSB iosb;
@ -867,17 +1077,19 @@ if ((status != SS$_NORMAL) || (iosb.status != SS$_NORMAL))
return SCPE_OK; 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)); 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 int status, term[2];
unsigned char buf[4]; unsigned char buf[4];
@ -902,6 +1114,40 @@ if (sim_brk_char && (buf[0] == sim_brk_char))
return (buf[0] | SCPE_KFLAG); 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) t_stat sim_os_putchar (int32 out)
{ {
unsigned int status; unsigned int status;
@ -958,7 +1204,7 @@ ControlHandler(DWORD dwCtrlType)
return FALSE; return FALSE;
} }
t_stat sim_ttinit (void) t_stat sim_os_ttinit (void)
{ {
SetConsoleCtrlHandler( ControlHandler, TRUE ); SetConsoleCtrlHandler( ControlHandler, TRUE );
std_input = GetStdHandle (STD_INPUT_HANDLE); std_input = GetStdHandle (STD_INPUT_HANDLE);
@ -969,7 +1215,7 @@ if ((std_input) && /* Not Background proces
return SCPE_OK; return SCPE_OK;
} }
t_stat sim_ttrun (void) t_stat sim_os_ttrun (void)
{ {
if ((std_input) && /* If Not Background process? */ if ((std_input) && /* If Not Background process? */
(std_input != INVALID_HANDLE_VALUE) && (std_input != INVALID_HANDLE_VALUE) &&
@ -984,7 +1230,7 @@ SetThreadPriority (GetCurrentThread(), THREAD_PRIORITY_BELOW_NORMAL);
return SCPE_OK; return SCPE_OK;
} }
t_stat sim_ttcmd (void) t_stat sim_os_ttcmd (void)
{ {
if (sim_log) { if (sim_log) {
fflush (sim_log); fflush (sim_log);
@ -998,12 +1244,12 @@ if ((std_input) && /* If Not Background pro
return SCPE_OK; return SCPE_OK;
} }
t_stat sim_ttclose (void) t_stat sim_os_ttclose (void)
{ {
return SCPE_OK; return SCPE_OK;
} }
t_bool sim_ttisatty (void) t_bool sim_os_ttisatty (void)
{ {
DWORD Mode; DWORD Mode;
@ -1016,7 +1262,7 @@ int c = -1;
DWORD nkbevents, nkbevent; DWORD nkbevents, nkbevent;
INPUT_RECORD rec; INPUT_RECORD rec;
\ sim_debug (DBG_TRC, &sim_con_telnet, "sim_os_poll_kbd()\n");
if ((std_input == NULL) || /* No keyboard for */ if ((std_input == NULL) || /* No keyboard for */
(std_input == INVALID_HANDLE_VALUE)) /* background processes */ (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; 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) t_stat sim_os_putchar (int32 c)
{ {
DWORD unused; DWORD unused;
@ -1068,27 +1325,27 @@ return SCPE_OK;
#include <conio.h> #include <conio.h>
t_stat sim_ttinit (void) t_stat sim_os_ttinit (void)
{ {
return SCPE_OK; return SCPE_OK;
} }
t_stat sim_ttrun (void) t_stat sim_os_ttrun (void)
{ {
return SCPE_OK; return SCPE_OK;
} }
t_stat sim_ttcmd (void) t_stat sim_os_ttcmd (void)
{ {
return SCPE_OK; return SCPE_OK;
} }
t_stat sim_ttclose (void) t_stat sim_os_ttclose (void)
{ {
return SCPE_OK; return SCPE_OK;
} }
t_bool sim_ttisatty (void) t_bool sim_os_ttisatty (void)
{ {
return 1; return 1;
} }
@ -1124,6 +1381,12 @@ if (sim_brk_char && ((c & 0177) == sim_brk_char))
return c | SCPE_KFLAG; 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) t_stat sim_os_putchar (int32 c)
{ {
if (c != 0177) { 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 */ /* 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; int i;
/* this blank will later be replaced by the number of characters */ /* this blank will later be replaced by the number of characters */
char title[50] = " "; char title[50] = " ";
@ -1274,22 +1537,22 @@ t_stat sim_ttinit (void) {
return SCPE_OK; return SCPE_OK;
} }
t_stat sim_ttrun (void) t_stat sim_os_ttrun (void)
{ {
return SCPE_OK; return SCPE_OK;
} }
t_stat sim_ttcmd (void) t_stat sim_os_ttcmd (void)
{ {
return SCPE_OK; return SCPE_OK;
} }
t_stat sim_ttclose (void) t_stat sim_os_ttclose (void)
{ {
return SCPE_OK; return SCPE_OK;
} }
t_bool sim_ttisatty (void) t_bool sim_os_ttisatty (void)
{ {
return 1; return 1;
} }
@ -1309,6 +1572,12 @@ if (sim_brk_char && ((c & 0177) == sim_brk_char))
return c | SCPE_KFLAG; 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) t_stat sim_os_putchar (int32 c)
{ {
if (c != 0177) { if (c != 0177) {
@ -1331,7 +1600,7 @@ struct tchars cmdtchars,runtchars; /* V7 editing */
struct ltchars cmdltchars,runltchars; /* 4.2 BSD editing */ struct ltchars cmdltchars,runltchars; /* 4.2 BSD editing */
int cmdfl,runfl; /* TTY flags */ 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 */ cmdfl = fcntl (0, F_GETFL, 0); /* get old flags and status */
runfl = cmdfl | FNDELAY; runfl = cmdfl | FNDELAY;
@ -1358,7 +1627,7 @@ runltchars.t_lnextc = 0xFF;
return SCPE_OK; /* return success */ 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 */ runtchars.t_intrc = sim_int_char; /* in case changed */
fcntl (0, F_SETFL, runfl); /* non-block mode */ fcntl (0, F_SETFL, runfl); /* non-block mode */
@ -1372,7 +1641,7 @@ nice (10); /* lower priority */
return SCPE_OK; return SCPE_OK;
} }
t_stat sim_ttcmd (void) t_stat sim_os_ttcmd (void)
{ {
nice (-10); /* restore priority */ nice (-10); /* restore priority */
fcntl (0, F_SETFL, cmdfl); /* block mode */ fcntl (0, F_SETFL, cmdfl); /* block mode */
@ -1385,12 +1654,12 @@ if (ioctl (0, TIOCSLTC, &cmdltchars) < 0)
return SCPE_OK; return SCPE_OK;
} }
t_stat sim_ttclose (void) t_stat sim_os_ttclose (void)
{ {
return sim_ttcmd (); return sim_ttcmd ();
} }
t_bool sim_ttisatty (void) t_bool sim_os_ttisatty (void)
{ {
return isatty (0); return isatty (0);
} }
@ -1407,6 +1676,22 @@ if (sim_brk_char && (buf[0] == sim_brk_char))
else return (buf[0] | SCPE_KFLAG); 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) t_stat sim_os_putchar (int32 out)
{ {
char c; char c;
@ -1426,7 +1711,7 @@ return SCPE_OK;
struct termios cmdtty, runtty; struct termios cmdtty, runtty;
static int prior_norm = 1; static int prior_norm = 1;
t_stat sim_ttinit (void) t_stat sim_os_ttinit (void)
{ {
if (!isatty (fileno (stdin))) /* skip if !tty */ if (!isatty (fileno (stdin))) /* skip if !tty */
return SCPE_OK; return SCPE_OK;
@ -1468,7 +1753,7 @@ runtty.c_cc[VSTATUS] = 0;
return SCPE_OK; return SCPE_OK;
} }
t_stat sim_ttrun (void) t_stat sim_os_ttrun (void)
{ {
if (!isatty (fileno (stdin))) /* skip if !tty */ if (!isatty (fileno (stdin))) /* skip if !tty */
return SCPE_OK; return SCPE_OK;
@ -1483,7 +1768,7 @@ if (prior_norm) { /* at normal pri? */
return SCPE_OK; return SCPE_OK;
} }
t_stat sim_ttcmd (void) t_stat sim_os_ttcmd (void)
{ {
if (!isatty (fileno (stdin))) /* skip if !tty */ if (!isatty (fileno (stdin))) /* skip if !tty */
return SCPE_OK; return SCPE_OK;
@ -1497,12 +1782,12 @@ if (tcsetattr (0, TCSAFLUSH, &cmdtty) < 0)
return SCPE_OK; return SCPE_OK;
} }
t_stat sim_ttclose (void) t_stat sim_os_ttclose (void)
{ {
return sim_ttcmd (); return sim_ttcmd ();
} }
t_bool sim_ttisatty(void) t_bool sim_os_ttisatty (void)
{ {
return isatty (fileno (stdin)); return isatty (fileno (stdin));
} }
@ -1519,6 +1804,22 @@ if (sim_brk_char && (buf[0] == sim_brk_char))
else return (buf[0] | SCPE_KFLAG); 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) t_stat sim_os_putchar (int32 out)
{ {
char c; char c;

View file

@ -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_logon (int32 flag, char *cptr);
t_stat sim_set_logoff (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_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_buff (int32 flg, char *cptr);
t_stat sim_set_cons_unbuff (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_log (int32 flg, char *cptr);
t_stat sim_set_cons_nolog (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_deboff (int32 flag, char *cptr);
t_stat sim_set_pchar (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_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_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_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_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_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_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_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_show_cons_log (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
t_stat sim_check_console (int32 sec); t_stat sim_check_console (int32 sec);
@ -83,9 +87,7 @@ t_stat sim_ttinit (void);
t_stat sim_ttrun (void); t_stat sim_ttrun (void);
t_stat sim_ttcmd (void); t_stat sim_ttcmd (void);
t_stat sim_ttclose (void); t_stat sim_ttclose (void);
t_bool sim_ttisatty(void); t_bool sim_ttisatty (void);
t_stat sim_os_poll_kbd (void);
t_stat sim_os_putchar (int32 out);
int32 sim_tt_inpcvt (int32 c, uint32 mode); int32 sim_tt_inpcvt (int32 c, uint32 mode);
int32 sim_tt_outcvt (int32 c, uint32 mode); int32 sim_tt_outcvt (int32 c, uint32 mode);

View file

@ -371,6 +371,11 @@ struct sim_unit {
struct sim_unit *a_next; /* next asynch active */ struct sim_unit *a_next; /* next asynch active */
int32 a_event_time; int32 a_event_time;
t_stat (*a_activate_call)(struct sim_unit *, int32); 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 #endif
}; };
@ -380,21 +385,24 @@ struct sim_unit {
#define UNIT_V_UF 16 /* device specific */ #define UNIT_V_UF 16 /* device specific */
#define UNIT_V_RSV 31 /* reserved!! */ #define UNIT_V_RSV 31 /* reserved!! */
#define UNIT_ATTABLE 000001 /* attachable */ #define UNIT_ATTABLE 0000001 /* attachable */
#define UNIT_RO 000002 /* read only */ #define UNIT_RO 0000002 /* read only */
#define UNIT_FIX 000004 /* fixed capacity */ #define UNIT_FIX 0000004 /* fixed capacity */
#define UNIT_SEQ 000010 /* sequential */ #define UNIT_SEQ 0000010 /* sequential */
#define UNIT_ATT 000020 /* attached */ #define UNIT_ATT 0000020 /* attached */
#define UNIT_BINK 000040 /* K = power of 2 */ #define UNIT_BINK 0000040 /* K = power of 2 */
#define UNIT_BUFABLE 000100 /* bufferable */ #define UNIT_BUFABLE 0000100 /* bufferable */
#define UNIT_MUSTBUF 000200 /* must buffer */ #define UNIT_MUSTBUF 0000200 /* must buffer */
#define UNIT_BUF 000400 /* buffered */ #define UNIT_BUF 0000400 /* buffered */
#define UNIT_ROABLE 001000 /* read only ok */ #define UNIT_ROABLE 0001000 /* read only ok */
#define UNIT_DISABLE 002000 /* disable-able */ #define UNIT_DISABLE 0002000 /* disable-able */
#define UNIT_DIS 004000 /* disabled */ #define UNIT_DIS 0004000 /* disabled */
#define UNIT_RAW 010000 /* raw mode */ #define UNIT_RAW 0010000 /* raw mode */
#define UNIT_TEXT 020000 /* text mode */ #define UNIT_TEXT 0020000 /* text mode */
#define UNIT_IDLE 040000 /* idle eligible */ #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_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)) #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) #if defined (SIM_ASYNCH_IO)
#include <pthread.h> #include <pthread.h>
#include "sim_tmxr.h"
extern pthread_mutex_t sim_asynch_lock; extern pthread_mutex_t sim_asynch_lock;
extern pthread_cond_t sim_asynch_wake; 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 pthread_t sim_asynch_main_threadid;
extern struct sim_unit *sim_asynch_queue; extern struct sim_unit *sim_asynch_queue;
extern volatile t_bool sim_idle_wait; 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 \ This allows NULL in an entry's a_next pointer to \
indicate that the entry is not currently in any list */ \ indicate that the entry is not currently in any list */ \
sim_asynch_queue = AIO_LIST_END; \ sim_asynch_queue = AIO_LIST_END; \
} } \
else \
(void)0
#define AIO_CLEANUP \ #define AIO_CLEANUP \
if (1) { \ if (1) { \
pthread_mutex_destroy(&sim_asynch_lock); \ pthread_mutex_destroy(&sim_asynch_lock); \
pthread_cond_destroy(&sim_asynch_wake); \ 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) #if defined(__DECC_VER)
#include <builtins> #include <builtins>
@ -640,7 +673,7 @@ extern int32 sim_asynch_inst_latency;
} else (void)0 } else (void)0
#define AIO_ACTIVATE(caller, uptr, event_time) \ #define AIO_ACTIVATE(caller, uptr, event_time) \
if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) { \ 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; \ uptr->a_activate_call = sim_activate_abs; \
} else { \ } else { \
UNIT *q, *qe; \ UNIT *q, *qe; \
@ -695,7 +728,7 @@ extern int32 sim_asynch_inst_latency;
#define AIO_ACTIVATE(caller, uptr, event_time) \ #define AIO_ACTIVATE(caller, uptr, event_time) \
if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) { \ if (!pthread_equal ( pthread_self(), sim_asynch_main_threadid )) { \
pthread_mutex_lock (&sim_asynch_lock); \ pthread_mutex_lock (&sim_asynch_lock); \
if (uptr->a_next) { \ if (uptr->a_next) { /* already queued? */ \
uptr->a_activate_call = sim_activate_abs; \ uptr->a_activate_call = sim_activate_abs; \
} else { \ } else { \
uptr->a_next = sim_asynch_queue; \ uptr->a_next = sim_asynch_queue; \
@ -728,6 +761,8 @@ extern int32 sim_asynch_inst_latency;
#define AIO_CHECK_EVENT #define AIO_CHECK_EVENT
#define AIO_INIT #define AIO_INIT
#define AIO_CLEANUP #define AIO_CLEANUP
#define AIO_POLL_BEGIN(uptr)
#define AIO_POLL_COMPLETE(uptr)
#define AIO_SET_INTERRUPT_LATENCY(instpersec) #define AIO_SET_INTERRUPT_LATENCY(instpersec)
#endif /* SIM_ASYNCH_IO */ #endif /* SIM_ASYNCH_IO */

View file

@ -81,6 +81,8 @@
t_bool sim_idle_enab = FALSE; /* global flag */ t_bool sim_idle_enab = FALSE; /* global flag */
volatile t_bool sim_idle_wait = 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_idle_rate_ms = 0;
static uint32 sim_os_sleep_min_ms = 0; static uint32 sim_os_sleep_min_ms = 0;
@ -509,6 +511,10 @@ rtc_based[tmr] = time;
rtc_currd[tmr] = time; rtc_currd[tmr] = time;
rtc_initd[tmr] = time; rtc_initd[tmr] = time;
rtc_elapsed[tmr] = 0; 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; 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); 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<SIM_NTIMERS; ++tmr) {
if (0 == rtc_initd[tmr])
continue;
fprintf (st, "%sTimer %d:\n", rtc_hz[tmr] ? "Calibrated " : "Uncalibrated ", tmr);
if (rtc_hz[tmr]) {
fprintf (st, " Running at: %dhz\n", rtc_hz[tmr]);
fprintf (st, " Current Ticks: %d\n", rtc_ticks[tmr]);
}
fprintf (st, " Real Time: %u\n", rtc_rtime[tmr]);
fprintf (st, " Virtual Time: %u\n", rtc_vtime[tmr]);
fprintf (st, " Next Interval: %u\n", rtc_nxintv[tmr]);
fprintf (st, " Base Delay: %d\n", rtc_based[tmr]);
fprintf (st, " Current Delay: %d\n", rtc_currd[tmr]);
fprintf (st, " Initial Delay: %d\n", rtc_initd[tmr]);
fprintf (st, " Seconds Running: %u\n", rtc_elapsed[tmr]);
}
return SCPE_OK;
}
/* sim_idle - idle simulator until next event or for specified interval /* sim_idle - idle simulator until next event or for specified interval
Inputs: Inputs:

View file

@ -85,6 +85,7 @@ void sim_rtcn_init_all (void);
int32 sim_rtcn_calb (int32 ticksper, int32 tmr); int32 sim_rtcn_calb (int32 ticksper, int32 tmr);
int32 sim_rtc_init (int32 time); int32 sim_rtc_init (int32 time);
int32 sim_rtc_calb (int32 ticksper); int32 sim_rtc_calb (int32 ticksper);
t_stat sim_show_timers (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, char* desc);
t_bool sim_idle (uint32 tmr, t_bool sin_cyc); t_bool sim_idle (uint32 tmr, t_bool sin_cyc);
t_stat sim_set_throt (int32 arg, char *cptr); t_stat sim_set_throt (int32 arg, char *cptr);
t_stat sim_show_throt (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr); t_stat sim_show_throt (FILE *st, DEVICE *dnotused, UNIT *unotused, int32 flag, char *cptr);
@ -98,4 +99,10 @@ void sim_os_sleep (unsigned int sec);
uint32 sim_os_ms_sleep (unsigned int msec); uint32 sim_os_ms_sleep (unsigned int msec);
uint32 sim_os_ms_sleep_init (void); uint32 sim_os_ms_sleep_init (void);
extern t_bool sim_idle_enab; /* idle enabled flag */
extern volatile t_bool sim_idle_wait; /* idle waiting flag */
extern int32 *sim_tmr_poll; /* pointer to instructions per clock tick */
extern int32 *sim_clk_tps; /* pointer to clock ticks per second */
#endif #endif

View file

@ -1,4 +1,4 @@
/* sim_tmxr.c: Telnet terminal multiplexor library /* sim_tmxr.c: Telnet terminal multiplexer library
Copyright (c) 2001-2011, Robert M Supnik Copyright (c) 2001-2011, Robert M Supnik
@ -57,33 +57,37 @@
This library includes: This library includes:
tmxr_poll_conn - poll for connection tmxr_poll_conn - poll for connection
tmxr_reset_ln - reset line tmxr_reset_ln - reset line
tmxr_getc_ln - get character for line tmxr_getc_ln - get character for line
tmxr_poll_rx - poll receive tmxr_poll_rx - poll receive
tmxr_putc_ln - put character for line tmxr_putc_ln - put character for line
tmxr_poll_tx - poll transmit tmxr_poll_tx - poll transmit
tmxr_open_master - open master connection tmxr_open_master - open master connection
tmxr_close_master - close master connection tmxr_close_master - close master connection
tmxr_attach - attach terminal multiplexor tmxr_attach - attach terminal multiplexer
tmxr_detach - detach terminal multiplexor tmxr_detach - detach terminal multiplexer
tmxr_ex - (null) examine tmxr_ex - (null) examine
tmxr_dep - (null) deposit tmxr_dep - (null) deposit
tmxr_msg - send message to socket tmxr_msg - send message to socket
tmxr_linemsg - send message to line tmxr_linemsg - send message to line
tmxr_fconns - output connection status tmxr_fconns - output connection status
tmxr_fstats - output connection statistics tmxr_fstats - output connection statistics
tmxr_dscln - disconnect line (SET routine) tmxr_dscln - disconnect line (SET routine)
tmxr_rqln - number of available characters for line tmxr_rqln - number of available characters for line
tmxr_tqln - number of buffered characters for line tmxr_tqln - number of buffered characters for line
tmxr_set_lnorder - set line connection order tmxr_set_lnorder - set line connection order
tmxr_show_lnorder - show line connection order tmxr_show_lnorder - show line connection order
tmxr_show_open_devices - show info about all open tmxr devices
tmxr_startup - initialize the tmxr library
tmxr_shutdown - shutdown the tmxr library
All routines are OS-independent. All routines are OS-independent.
*/ */
#define NOT_MUX_USING_CODE /* sim_tmxr library define */
#include "sim_defs.h" #include "sim_defs.h"
#include "sim_sock.h"
#include "sim_tmxr.h" #include "sim_tmxr.h"
#include "scp.h" #include "scp.h"
#include <ctype.h> #include <ctype.h>
@ -134,6 +138,7 @@ int32 tmxr_send_buffered_data (TMLN *lp);
TMLN *tmxr_find_ldsc (UNIT *uptr, int32 val, TMXR *mp); TMLN *tmxr_find_ldsc (UNIT *uptr, int32 val, TMXR *mp);
extern int32 sim_switches; extern int32 sim_switches;
extern int32 sim_is_running;
extern char sim_name[]; extern char sim_name[];
extern FILE *sim_log; extern FILE *sim_log;
extern uint32 sim_os_msec (void); 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 Called from unit service routine to test for new connection
Inputs: Inputs:
*mp = pointer to terminal multiplexor descriptor *mp = pointer to terminal multiplexer descriptor
Outputs: Outputs:
line number activated, -1 if none line number activated, -1 if none
@ -171,10 +176,12 @@ static char mantra[] = {
TN_IAC, TN_DO, TN_BIN TN_IAC, TN_DO, TN_BIN
}; };
tmxr_debug_trace (mp, "tmxr_poll_conn()");
newsock = sim_accept_conn (mp->master, &ipaddr); /* poll connect */ newsock = sim_accept_conn (mp->master, &ipaddr); /* poll connect */
if (newsock != INVALID_SOCKET) { /* got a live one? */ if (newsock != INVALID_SOCKET) { /* got a live one? */
op = mp->lnorder; /* get line connection order list pointer */ op = mp->lnorder; /* get line connection order list pointer */
i = mp->lines; /* play it safe in case lines == 0 */ 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 */ for (j = 0; j < mp->lines; j++, i++) { /* find next avail line */
if (op && (*op >= 0) && (*op < mp->lines)) /* order list present and valid? */ if (op && (*op >= 0) && (*op < mp->lines)) /* order list present and valid? */
@ -239,6 +246,7 @@ return -1;
void tmxr_reset_ln (TMLN *lp) void tmxr_reset_ln (TMLN *lp)
{ {
tmxr_debug_trace_line (lp, "tmxr_reset_ln()");
if (lp->txlog) /* dump log */ if (lp->txlog) /* dump log */
fflush (lp->txlog); fflush (lp->txlog);
tmxr_send_buffered_data (lp); /* send buffered data */ tmxr_send_buffered_data (lp); /* send buffered data */
@ -265,6 +273,7 @@ int32 tmxr_getc_ln (TMLN *lp)
int32 j, val = 0; int32 j, val = 0;
uint32 tmp; uint32 tmp;
tmxr_debug_trace_line (lp, "tmxr_getc_ln()");
if (lp->conn && lp->rcve) { /* conn & enb? */ if (lp->conn && lp->rcve) { /* conn & enb? */
j = lp->rxbpi - lp->rxbpr; /* # input chrs */ j = lp->rxbpi - lp->rxbpr; /* # input chrs */
if (j) { /* any? */ if (j) { /* any? */
@ -283,7 +292,7 @@ return val;
/* Poll for input /* Poll for input
Inputs: Inputs:
*mp = pointer to terminal multiplexor descriptor *mp = pointer to terminal multiplexer descriptor
Outputs: none Outputs: none
*/ */
@ -292,9 +301,10 @@ void tmxr_poll_rx (TMXR *mp)
int32 i, nbytes, j; int32 i, nbytes, j;
TMLN *lp; TMLN *lp;
tmxr_debug_trace (mp, "tmxr_poll_rx()");
for (i = 0; i < mp->lines; i++) { /* loop thru lines */ for (i = 0; i < mp->lines; i++) { /* loop thru lines */
lp = mp->ldsc + i; /* get line desc */ lp = mp->ldsc + i; /* get line desc */
if (!lp->conn || !lp->rcve) /* skip if !conn */ if (!lp->conn) /* skip if !conn */
continue; continue;
nbytes = 0; nbytes = 0;
@ -415,6 +425,11 @@ for (i = 0; i < mp->lines; i++) { /* loop thru lines */
} /* end for char */ } /* end for char */
if (nbytes != (lp->rxbpi-lp->rxbpr)) if (nbytes != (lp->rxbpi-lp->rxbpr))
tmxr_debug (TMXR_DBG_RCV, lp, "Remaining", &(lp->rxb[lp->rxbpi]), 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 else nbytes */
} /* end for lines */ } /* end for lines */
for (i = 0; i < mp->lines; i++) { /* loop thru lines */ for (i = 0; i < mp->lines; i++) { /* loop thru lines */
@ -448,35 +463,40 @@ return;
Inputs: Inputs:
*lp = pointer to line descriptor *lp = pointer to line descriptor
chr = characters chr = character
Outputs: Outputs:
status = ok, connection lost, or stall status = ok, connection lost, or stall
*/ */
t_stat tmxr_putc_ln (TMLN *lp, int32 chr) 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 */ if (lp->txlog) /* log if available */
fputc (chr, lp->txlog); 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_AVAIL(lp) (lp->txbsz - tmxr_tqln (lp))
#define TXBUF_CHAR(lp, c) { \ #define TXBUF_CHAR(lp, c) { \
lp->txb[lp->txbpi++] = (char)(c); \ lp->txb[lp->txbpi++] = (char)(c); \
++lp->mp->txcount; \
lp->txbpi %= lp->txbsz; \ lp->txbpi %= lp->txbsz; \
if (lp->txbpi == lp->txbpr) \ if (lp->txbpi == lp->txbpr) { \
lp->txbpr = (1+lp->txbpr)%lp->txbsz, ++lp->txdrp; \ lp->txbpr = (1+lp->txbpr)%lp->txbsz; \
++lp->txdrp; \
} \
} }
if ((lp->txbfd) || (TXBUF_AVAIL(lp) > 1)) { /* room for char (+ IAC)? */ if ((lp->txbfd) || (TXBUF_AVAIL(lp) > 1)) { /* room for char (+ IAC)? */
if (TN_IAC == (char) chr) /* char == IAC ? */ if (TN_IAC == (char) chr) /* char == IAC ? */
TXBUF_CHAR (lp, TN_IAC); /* stuff extra IAC char */ TXBUF_CHAR (lp, TN_IAC); /* stuff extra IAC char */
TXBUF_CHAR (lp, chr); /* buffer char & adv pointer */ 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 */ 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 */ return SCPE_OK; /* char sent */
} }
++lp->txdrp; lp->xmte = 0; /* no room, dsbl line */ ++lp->txdrp; lp->xmte = 0; /* no room, dsbl line */
@ -486,7 +506,7 @@ return SCPE_STALL; /* char not sent */
/* Poll for output /* Poll for output
Inputs: Inputs:
*mp = pointer to terminal multiplexor descriptor *mp = pointer to terminal multiplexer descriptor
Outputs: Outputs:
none none
*/ */
@ -496,6 +516,7 @@ void tmxr_poll_tx (TMXR *mp)
int32 i, nbytes; int32 i, nbytes;
TMLN *lp; TMLN *lp;
tmxr_debug_trace (mp, "tmxr_poll_tx()");
for (i = 0; i < mp->lines; i++) { /* loop thru lines */ for (i = 0; i < mp->lines; i++) { /* loop thru lines */
lp = mp->ldsc + i; /* get line desc */ lp = mp->ldsc + i; /* get line desc */
if (lp->conn == 0) /* skip if !conn */ 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 */ nbytes = tmxr_send_buffered_data (lp); /* buffered bytes */
if (nbytes == 0) /* buf empty? enab line */ if (nbytes == 0) /* buf empty? enab line */
lp->xmte = 1; 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 */ } /* end for */
return; return;
} }
@ -519,6 +545,7 @@ int32 tmxr_send_buffered_data (TMLN *lp)
{ {
int32 nbytes, sbytes; int32 nbytes, sbytes;
tmxr_debug_trace_line (lp, "tmxr_send_buffered_data()");
nbytes = tmxr_tqln(lp); /* avail bytes */ nbytes = tmxr_tqln(lp); /* avail bytes */
if (nbytes) { /* >0? write */ if (nbytes) { /* >0? write */
if (lp->txbpr < lp->txbpi) /* no wrap? */ if (lp->txbpr < lp->txbpi) /* no wrap? */
@ -565,6 +592,7 @@ SOCKET sock;
TMLN *lp; TMLN *lp;
t_stat r; t_stat r;
tmxr_debug_trace (mp, "tmxr_open_master()");
if (!isdigit(*cptr)) { if (!isdigit(*cptr)) {
char gbuf[CBUFSIZE]; char gbuf[CBUFSIZE];
cptr = get_glyph (cptr, gbuf, '='); cptr = get_glyph (cptr, gbuf, '=');
@ -653,6 +681,7 @@ mp->port = port; /* save port */
mp->master = sock; /* save master socket */ mp->master = sock; /* save master socket */
for (i = 0; i < mp->lines; i++) { /* initialize lines */ for (i = 0; i < mp->lines; i++) { /* initialize lines */
lp = mp->ldsc + i; lp = mp->ldsc + i;
lp->mp = mp;
lp->conn = lp->tsta = 0; lp->conn = lp->tsta = 0;
lp->rxbpi = lp->rxbpr = 0; lp->rxbpi = lp->rxbpr = 0;
lp->txbpi = lp->txbpr = 0; lp->txbpi = lp->txbpr = 0;
@ -668,9 +697,333 @@ for (i = 0; i < mp->lines; i++) { /* initialize lines */
return SCPE_OK; 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; i<tmxr_open_device_count; ++i) {
mp = tmxr_open_devices[i];
if ((mp->master) && (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; j<mp->lines; ++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; i<tmxr_open_device_count; ++i) {
mp = tmxr_open_devices[i];
if (mp->master) {
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; j<mp->lines; ++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; i<socket_count; ++i) {
if (FD_ISSET(sockets[i], &readfds) ||
FD_ISSET(sockets[i], &errorfds)) {
/* More than one socket can be associated with the
same unit. Only activate one time */
for (j=0; j<wait_count; ++j)
if (activated[j] == units[i])
break;
if (j == wait_count) {
activated[j] = units[i];
++wait_count;
if (!activated[j]->a_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; i<tmxr_open_device_count+1; ++i)
if (tmxr_open_devices[i] == mux) {
for (j=i+1; j<tmxr_open_device_count; ++j)
tmxr_open_devices[j-1] = tmxr_open_devices[j];
break;
}
#if defined(SIM_ASYNCH_IO)
pthread_mutex_unlock (&sim_tmxr_poll_lock);
#endif
}
t_stat tmxr_change_async (void)
{
#if defined(SIM_ASYNCH_IO)
if (sim_asynch_enabled)
tmxr_start_poll ();
else
tmxr_stop_poll ();
#endif
return SCPE_OK;
}
t_stat tmxr_startup (void)
{
return SCPE_OK;
}
t_stat tmxr_shutdown (void)
{
if (tmxr_open_device_count)
return SCPE_IERR;
return SCPE_OK;
}
t_stat tmxr_show_open_devices (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, char* desc)
{
int i, j;
if (0 == tmxr_open_device_count)
fprintf(st, "No Attached Multiplexer Devices\n");
else {
for (i=0; i<tmxr_open_device_count; ++i) {
TMXR *mp = tmxr_open_devices[i];
TMLN *lp;
fprintf(st, "Multiplexer device: %s", mp->dptr->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 */ /* 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; char* tptr;
t_stat r; t_stat r;
@ -681,6 +1034,7 @@ tptr = (char *) malloc (strlen (cptr) + /* get string buf */
sizeof(bmsg) + sizeof(lmsg)); sizeof(bmsg) + sizeof(lmsg));
if (tptr == NULL) /* no more mem? */ if (tptr == NULL) /* no more mem? */
return SCPE_MEM; return SCPE_MEM;
mp->uptr = uptr; /* save unit for polling */
r = tmxr_open_master (mp, cptr); /* open master socket */ r = tmxr_open_master (mp, cptr); /* open master socket */
if (r != SCPE_OK) { /* error? */ if (r != SCPE_OK) { /* error? */
free (tptr); /* release buf */ free (tptr); /* release buf */
@ -694,10 +1048,13 @@ if (mp->logfiletmpl[0])
sprintf (tptr, "%s%s%s", pmsg, bmsg, lmsg); /* assemble all */ sprintf (tptr, "%s%s%s", pmsg, bmsg, lmsg); /* assemble all */
uptr->filename = tptr; /* save */ uptr->filename = tptr; /* save */
uptr->flags = uptr->flags | UNIT_ATT; /* no more errors */ 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? */ if (mp->dptr == NULL) /* has device been set? */
mp->dptr = find_dev_from_unit (uptr); /* no, so set device now */ mp->dptr = find_dev_from_unit (uptr); /* no, so set device now */
_tmxr_add_to_open_list (mp);
return SCPE_OK; return SCPE_OK;
} }
@ -719,6 +1076,7 @@ for (i = 0; i < mp->lines; i++) { /* loop thru conn */
} /* end for */ } /* end for */
sim_close_sock (mp->master, 1); /* close master socket */ sim_close_sock (mp->master, 1); /* close master socket */
mp->master = 0; mp->master = 0;
_tmxr_remove_from_open_list (mp);
return SCPE_OK; return SCPE_OK;
} }
@ -731,10 +1089,24 @@ if (!(uptr->flags & UNIT_ATT)) /* attached? */
tmxr_close_master (mp); /* close master socket */ tmxr_close_master (mp); /* close master socket */
free (uptr->filename); /* free port string */ free (uptr->filename); /* free port string */
uptr->filename = NULL; 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; 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 */ /* Stub examine and deposit */
t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw) t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
@ -833,6 +1205,7 @@ t_stat r;
if (mp == NULL) if (mp == NULL)
return SCPE_IERR; return SCPE_IERR;
tmxr_debug_trace (mp, "tmxr_dscln()");
if (val) { /* = n form */ if (val) { /* = n form */
if (cptr == NULL) if (cptr == NULL)
return SCPE_ARG; return SCPE_ARG;
@ -1119,9 +1492,7 @@ if (mp == NULL)
return SCPE_IERR; return SCPE_IERR;
for (i = t = 0; i < mp->lines; i++) for (i = t = 0; i < mp->lines; i++)
t = t + (mp->ldsc[i].conn != 0); t = t + (mp->ldsc[i].conn != 0);
if (t == 1) fprintf (st, "%d connection%s", t, (t != 1) ? "s" : "");
fprintf (st, "1 connection");
else fprintf (st, "%d connections", t);
return SCPE_OK; return SCPE_OK;
} }
@ -1210,7 +1581,7 @@ while (*string)
tmxr_buf_debug_char (*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)) { if ((lp->mp->dptr) && (dbits & lp->mp->dptr->dctrl)) {
int i, j; 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); sim_debug (dbits, lp->mp->dptr, "%s %d bytes '%s'\n", msg, bufsize, tmxr_debug_buf);
} }
} }

View file

@ -1,4 +1,4 @@
/* sim_tmxr.h: terminal multiplexor definitions /* sim_tmxr.h: terminal multiplexer definitions
Copyright (c) 2001-2008, Robert M Supnik Copyright (c) 2001-2008, Robert M Supnik
@ -44,6 +44,8 @@
#ifndef _SIM_TMXR_H_ #ifndef _SIM_TMXR_H_
#define _SIM_TMXR_H_ 0 #define _SIM_TMXR_H_ 0
#include "sim_sock.h"
#define TMXR_V_VALID 15 #define TMXR_V_VALID 15
#define TMXR_VALID (1 << TMXR_V_VALID) #define TMXR_VALID (1 << TMXR_V_VALID)
#define TMXR_MAXBUF 256 /* buffer size */ #define TMXR_MAXBUF 256 /* buffer size */
@ -51,6 +53,12 @@
#define TMXR_DBG_XMT 0x10000 /* Debug Transmit Data */ #define TMXR_DBG_XMT 0x10000 /* Debug Transmit Data */
#define TMXR_DBG_RCV 0x20000 /* Debug Received 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 tmln TMLN;
typedef struct tmxr TMXR; typedef struct tmxr TMXR;
@ -79,6 +87,7 @@ struct tmln {
char rbr[TMXR_MAXBUF]; /* rcv break */ char rbr[TMXR_MAXBUF]; /* rcv break */
char *txb; /* xmt buffer */ char *txb; /* xmt buffer */
TMXR *mp; /* back pointer to mux */ TMXR *mp; /* back pointer to mux */
UNIT *uptr; /* pointer to receive poll unit */
}; };
struct tmxr { struct tmxr {
@ -88,8 +97,11 @@ struct tmxr {
TMLN *ldsc; /* line descriptors */ TMLN *ldsc; /* line descriptors */
int32 *lnorder; /* line connection order */ int32 *lnorder; /* line connection order */
DEVICE *dptr; /* multiplexer device */ DEVICE *dptr; /* multiplexer device */
UNIT *uptr; /* polling unit */
char logfiletmpl[FILENAME_MAX]; /* template logfile name */ char logfiletmpl[FILENAME_MAX]; /* template logfile name */
int32 txcount; /* count of transmit bytes */
int32 buffered; /* Buffered Line Behavior and Buffer Size Flag */ int32 buffered; /* Buffered Line Behavior and Buffer Size Flag */
int32 sessions; /* count of tcp connections received */
}; };
int32 tmxr_poll_conn (TMXR *mp); 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); void tmxr_poll_tx (TMXR *mp);
t_stat tmxr_open_master (TMXR *mp, char *cptr); t_stat tmxr_open_master (TMXR *mp, char *cptr);
t_stat tmxr_close_master (TMXR *mp); 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_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_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); t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
void tmxr_msg (SOCKET sock, char *msg); 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_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_cstat (FILE *st, UNIT *uptr, int32 val, void *desc);
t_stat tmxr_show_lines (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
#endif /* _SIM_TMXR_H_ */