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