Change to support serial ports on multiplexer devices without any changes to existing multiplexer device emulation code.

Added support for per line tcp listen ports.
Added support for per line outgoing tcp/telnet connections.

Removed DEV_NET from pdp11_dz and pdp11_vh emulators to allow proper restore of
This commit is contained in:
Mark Pizzolato 2012-10-17 08:40:01 -07:00
parent 24696892fd
commit 02cb620c9b
21 changed files with 2360 additions and 1405 deletions

View file

@ -29,8 +29,6 @@
28-Mar-11 JDB Tidied up signal handling
26-Oct-10 JDB Changed I/O signal handler for revised signal model
25-Nov-08 JDB Revised for new multiplexer library SHOW routines
19-Nov-08 JDB [serial] Removed DEV_NET to allow restoration of listening port
17-Oct-08 JDB [serial] Added serial port support
11-Sep-08 JDB Fixed STC,C losing interrupt request on BREAK
07-Sep-08 JDB Fixed IN_LOOPBACK conflict with netinet/in.h
Changed Telnet poll to connect immediately after reset or attach
@ -80,13 +78,13 @@
an "external rate" that is equivalent to 9600 baud, as most terminals were
set to their maximum speeds.
We support the 12966A connected to an HP terminal emulator via Telnet or a
serial port. Internally, we model the BACI as a terminal multiplexer with
one line. The simulation is complicated by the half-duplex nature of the
card (there is only one FIFO, used selectively either for transmission or
reception) and the double-buffered UART (a Western Digital TR1863A), which
has holding registers as well as a shift registers for transmission and
reception. We model both sets of device registers.
We support the 12966A connected to an HP terminal emulator via Telnet.
Internally, we model the BACI as a terminal multiplexer with one line. The
simulation is complicated by the half-duplex nature of the card (there is
only one FIFO, used selectively either for transmission or reception) and the
double-buffered UART (a Western Digital TR1863A), which has holding registers
as well as a shift registers for transmission and reception. We model both
sets of device registers.
During an output operation, the first character output to the card passes
through the FIFO and into the transmitter holding register. Subsequent
@ -115,12 +113,12 @@
as an "optimized (fast) timing" option. Optimization makes three
improvements:
1. On output, characters in the FIFO are emptied into the line buffer as a
1. On output, characters in the FIFO are emptied into the Telnet buffer as a
block, rather than one character per service call, and on input, all of
the characters available in the line buffer are loaded into the FIFO as a
block.
the characters available in the Telnet buffer are loaded into the FIFO as
a block.
2. The ENQ/ACK handshake is done locally, without involving the terminal
2. The ENQ/ACK handshake is done locally, without involving the Telnet
client.
3. Input occurring during an output operation is delayed until the second or
@ -320,7 +318,7 @@
/* Unit references */
#define baci_term baci_unit[0] /* terminal I/O unit */
#define baci_poll baci_unit[1] /* line polling unit */
#define baci_poll baci_unit[1] /* Telnet polling unit */
/* BACI state variables */
@ -393,11 +391,11 @@ t_stat baci_detach (UNIT *uptr);
baci_deb BACI debug list
baci_dev BACI device descriptor
Two units are used: one to handle character I/O via the multiplexer library,
and another to poll for connections and input. The character I/O service
routine runs only when there are characters to read or write. It operates at
the approximate baud rate of the terminal (in CPU instructions per second) in
order to be compatible with the OS drivers. The line poll must run
Two units are used: one to handle character I/O via the Telnet library, and
another to poll for connections and input. The character I/O service routine
runs only when there are characters to read or write. It operates at the
approximate baud rate of the terminal (in CPU instructions per second) in
order to be compatible with the OS drivers. The Telnet poll must run
continuously, but it can operate much more slowly, as the only requirement is
that it must not present a perceptible lag to human input. To be compatible
with CPU idling, it is co-scheduled with the master poll timer, which uses a
@ -406,14 +404,14 @@ t_stat baci_detach (UNIT *uptr);
DEVICE baci_dev;
TMLN baci_ldsc = { 0 }; /* line descriptor */
TMXR baci_desc = { 1, 0, 0, &baci_ldsc, NULL, &baci_dev }; /* device descriptor */
TMLN baci_ldsc = { 0 }; /* line descriptor */
TMXR baci_desc = { 1, 0, 0, &baci_ldsc }; /* device descriptor */
DIB baci_dib = { &baci_io, BACI, 0 };
UNIT baci_unit[] = {
{ UDATA (&baci_term_svc, UNIT_ATTABLE | UNIT_FASTTIME, 0) }, /* terminal I/O unit */
{ UDATA (&baci_poll_svc, UNIT_DIS, POLL_FIRST) } /* line poll unit */
{ UDATA (&baci_poll_svc, UNIT_DIS, POLL_FIRST) } /* Telnet poll unit */
};
REG baci_reg[] = {
@ -771,7 +769,7 @@ return stat_data;
The terminal service routine is used to transmit and receive characters.
In terminal mode, it is started when a character is ready for output or when
the line poll routine determines that there are characters ready for input
the Telnet poll routine determines that there are characters ready for input
and stopped when there are no more characters to output or input. When the
terminal is quiescent, this routine does not run.
@ -811,11 +809,11 @@ return stat_data;
first character after an ENQ is not an ACK.
Finally, fast timing enables buffer combining. For output, all characters
present in the FIFO are unloaded into the line buffer before initiating a
packet send. For input, all characters present in the line buffer are loaded
into the FIFO. This reduces network traffic and decreases simulator overhead
(there is only one service routine entry per block, rather than one per
character).
present in the FIFO are unloaded into the Telnet buffer before initiating a
packet send. For input, all characters present in the Telnet buffer are
loaded into the FIFO. This reduces network traffic and decreases simulator
overhead (there is only one service routine entry per block, rather than one
per character).
In fast output mode, it is imperative that not less than 1500 instructions
elapse between the first character load to the FIFO and the initiation of
@ -978,11 +976,12 @@ return status;
}
/* BACI line poll service.
/* BACI Telnet poll service.
This service routine is used to poll for connections and incoming characters.
If characters are available, the terminal I/O service routine is scheduled.
It starts when the line is attached and stops when the line is detached.
This service routine is used to poll for Telnet connections and incoming
characters. If characters are available, the terminal I/O service routine is
scheduled. It starts when the socket is attached and stops when the socket
is detached.
As there is only one line, we only poll for a new connection when the line is
disconnected.
@ -1029,57 +1028,42 @@ baci_term.wait = service_time (baci_icw); /* set terminal I/O time
if (baci_term.flags & UNIT_ATT) { /* device attached? */
baci_poll.wait = POLL_FIRST; /* set up poll */
sim_activate (&baci_poll, baci_poll.wait); /* start line poll immediately */
sim_activate (&baci_poll, baci_poll.wait); /* start Telnet poll immediately */
}
else
sim_cancel (&baci_poll); /* else stop line poll */
sim_cancel (&baci_poll); /* else stop Telnet poll */
return SCPE_OK;
}
/* Attach line */
/* Attach controller */
t_stat baci_attach (UNIT *uptr, char *cptr)
{
t_stat status = SCPE_OK;
if (uptr->flags & UNIT_DIS) /* unit disabled? */
return SCPE_UDIS; /* report it */
status = tmxr_attach (&baci_desc, uptr, cptr); /* attach to socket */
status = tmxr_attach (&baci_desc, uptr, cptr); /* try to attach to socket */
if (status == SCPE_ARG) /* invalid numeric port supplied? */
status = tmxr_attach_line (uptr, 0, cptr, &baci_desc); /* try to attach to serial port */
if (status == SCPE_OK) { /* attach successful? */
baci_poll.wait = POLL_FIRST; /* set up poll */
sim_activate (&baci_poll, baci_poll.wait); /* start line poll immediately */
if (status == SCPE_OK) {
baci_poll.wait = POLL_FIRST; /* set up poll */
sim_activate (&baci_poll, baci_poll.wait); /* start Telnet poll immediately */
}
return status;
}
/* Detach line */
/* Detach controller */
t_stat baci_detach (UNIT *uptr)
{
t_stat status;
if (uptr->flags & UNIT_DIS) /* unit disabled? */
return SCPE_UDIS; /* report it */
status = tmxr_detach_line (uptr, 0, NULL, &baci_desc); /* attempt to detach serial line */
if (status == SCPE_UNATT) /* not attached to serial? */
status = tmxr_detach (&baci_desc, uptr); /* attempt to detach listening socket */
status = tmxr_detach (&baci_desc, uptr); /* detach socket */
baci_ldsc.rcve = 0; /* disable line reception */
sim_cancel (&baci_poll); /* stop line poll */
sim_cancel (&baci_poll); /* stop Telnet poll */
return status;
}
/* Local routines */

View file

@ -32,7 +32,6 @@
27-Oct-10 JDB Revised I/O signal enum values for concurrent signals
Revised I/O macros for new signal handling
09-Oct-10 JDB Added DA and DC device select code assignments
21-Oct-08 JDB [serial] Added "sim_unit_ref" external
07-Sep-08 JDB Added POLL_FIRST to indicate immediate connection attempt
15-Jul-08 JDB Rearranged declarations with hp2100_cpu.h
26-Jun-08 JDB Rewrote device I/O to model backplane signals
@ -457,11 +456,10 @@ extern uint32 dev_prl [2], dev_irq [2], dev_srq [2]; /* I/O signal vectors */
/* Simulator state */
extern FILE *sim_deb;
extern FILE *sim_log;
extern int32 sim_step;
extern int32 sim_switches;
extern UNITREF sim_unit_ref;
extern FILE *sim_deb;
extern FILE *sim_log;
extern int32 sim_step;
extern int32 sim_switches;
/* CPU functions */

View file

@ -29,9 +29,7 @@
28-Mar-11 JDB Tidied up signal handling
26-Oct-10 JDB Changed I/O signal handler for revised signal model
25-Nov-08 JDB Revised for new multiplexer library SHOW routines
19-Nov-08 JDB [serial] Removed DEV_NET to allow restoration of listening port
14-Nov-08 JDB Cleaned up VC++ size mismatch warnings for zero assignments
20-Oct-08 JDB [serial] Added serial port support
03-Oct-08 JDB Fixed logic for ENQ/XOFF transmit wait
07-Sep-08 JDB Changed Telnet poll to connect immediately after reset or attach
10-Aug-08 JDB Added REG_FIT to register variables < 32-bit size
@ -59,8 +57,8 @@
character editing, echoing, ENQ/ACK handshaking, and read terminator
detection, substantially reducing the load on the CPU over the earlier 12920
multiplexer. It was supported by HP under RTE-MIII, RTE-IVB, and RTE-6/VM.
Under simulation, it connects with HP terminal emulators via Telnet or serial
ports.
Under simulation, it connects with HP terminal emulators via Telnet to a
user-specified port.
The single interface card contained a Z80 CPU, DMA controller, CTC, four
two-channel SIO UARTs, 16K of RAM, 8K of ROM, and I/O backplane latches and
@ -201,7 +199,7 @@
#define MPX_CNTLS 2 /* number of control units */
#define mpx_cntl (mpx_unit [MPX_PORTS + 0]) /* controller unit */
#define mpx_poll (mpx_unit [MPX_PORTS + 1]) /* polling unit */
#define mpx_poll (mpx_unit [MPX_PORTS + 1]) /* Telnet polling unit */
/* Character constants */
@ -613,16 +611,16 @@ t_stat mpx_show_frev (FILE *st, UNIT *uptr, int32 val, void *desc);
mpx_dev MPX device descriptor
The first eight units correspond to the eight multiplexer line ports. These
handle character I/O via the multiplexer library. A ninth unit acts as the
card controller, executing commands and transferring data to and from the I/O
buffers. A tenth unit is responsible for polling for connections and line
I/O. It also holds the master socket for Telnet connections.
handle character I/O via the Telnet library. A ninth unit acts as the card
controller, executing commands and transferring data to and from the I/O
buffers. A tenth unit is responsible for polling for connections and socket
I/O. It also holds the master socket.
The character I/O service routines run only when there are characters to read
or write. They operate at the approximate baud rates of the terminals (in
CPU instructions per second) in order to be compatible with the OS drivers.
The controller service routine runs only when a command is executing or a
data transfer to or from the CPU is in progress. The poll service must run
data transfer to or from the CPU is in progress. The Telnet poll must run
continuously, but it may operate much more slowly, as the only requirement is
that it must not present a perceptible lag to human input. To be compatible
with CPU idling, it is co-scheduled with the master poll timer, which uses a
@ -634,9 +632,9 @@ t_stat mpx_show_frev (FILE *st, UNIT *uptr, int32 val, void *desc);
DEVICE mpx_dev;
int32 mpx_order [MPX_PORTS] = { -1 }; /* connection order */
TMLN mpx_ldsc [MPX_PORTS] = { { 0 } }; /* line descriptors */
TMXR mpx_desc = { MPX_PORTS, 0, 0, mpx_ldsc, mpx_order, &mpx_dev }; /* device descriptor */
int32 mpx_order [MPX_PORTS] = { -1 }; /* connection order */
TMLN mpx_ldsc [MPX_PORTS] = { { 0 } }; /* line descriptors */
TMXR mpx_desc = { MPX_PORTS, 0, 0, mpx_ldsc, mpx_order }; /* device descriptor */
DIB mpx_dib = { &mpx_io, MPX };
@ -650,7 +648,7 @@ UNIT mpx_unit [] = {
{ UDATA (&mpx_line_svc, UNIT_FASTTIME, 0) }, /* terminal I/O line 6 */
{ UDATA (&mpx_line_svc, UNIT_FASTTIME, 0) }, /* terminal I/O line 7 */
{ UDATA (&mpx_cntl_svc, UNIT_DIS, 0) }, /* controller unit */
{ UDATA (&mpx_poll_svc, UNIT_ATTABLE | UNIT_DIS, POLL_FIRST) } /* line poll unit */
{ UDATA (&mpx_poll_svc, UNIT_ATTABLE | UNIT_DIS, POLL_FIRST) } /* Telnet poll unit */
};
REG mpx_reg [] = {
@ -1604,20 +1602,21 @@ return SCPE_OK;
/* Multiplexer line service.
The line service routine is used to transmit and receive characters. It is
started when a buffer is ready for output or when the poll service routine
started when a buffer is ready for output or when the Telnet poll routine
determines that there are characters ready for input, and it is stopped when
there are no more characters to output or input. When a line is quiescent,
this routine does not run. Service times are selected to approximate the
baud rate setting of the multiplexer port.
"Fast timing" mode enables three optimizations. First, buffered characters
are transferred in blocks, rather than a character at a time; this reduces
line traffic and decreases simulator overhead (there is only one service
routine entry per block, rather than one per character). Second, ENQ/ACK
handshaking is done locally, without involving the client. Third, when
editing and echo is enabled, entering BS echoes a backspace, a space, and a
backspace, and entering DEL echoes a backslash, a carriage return, and a line
feed, providing better compatibility with prior RTE terminal drivers.
are transferred via Telnet in blocks, rather than a character at a time; this
reduces network traffic and decreases simulator overhead (there is only one
service routine entry per block, rather than one per character). Second,
ENQ/ACK handshaking is done locally, without involving the Telnet client.
Third, when editing and echo is enabled, entering BS echoes a backspace, a
space, and a backspace, and entering DEL echoes a backslash, a carriage
return, and a line feed, providing better compatibility with prior RTE
terminal drivers.
Each read and write buffer begins with a reserved header byte that stores
per-buffer information, such as whether handshaking should be suppressed
@ -1631,7 +1630,7 @@ return SCPE_OK;
write buffer is freed, and a UI check is made if the controller is idle, in
case a write buffer request is pending.
For input, the character is retrieved from the line buffer. If a BREAK was
For input, the character is retrieved from the Telnet buffer. If a BREAK was
received, break status is set, and the character is discarded (the current
multiplexer library implementation always returns a NUL with a BREAK
indication). If the character is an XOFF, and XON/XOFF pacing is enabled, a
@ -1940,11 +1939,11 @@ return SCPE_OK;
}
/* Poll service.
/* Telnet poll service.
This service routine is used to poll for connections and incoming characters.
It is started when the listening socket or a serial line is attached and is
stopped when the socket and all lines are detached.
This service routine is used to poll for Telnet connections and incoming
characters. It starts when the socket is attached and stops when the socket
is detached.
Each line is then checked for a pending ENQ/ACK handshake. If one is
pending, the ACK counter is incremented, and if it times out, another ENQ is
@ -2008,10 +2007,10 @@ return SCPE_OK;
1. Under simulation, we also clear the input buffer register, even though
the hardware doesn't.
2. We set up the first poll for connections to occur "immediately" upon
execution, so that clients will be connected before execution begins.
Otherwise, a fast program may access the multiplexer before the poll
service routine activates.
2. We set up the first poll for Telnet connections to occur "immediately"
upon execution, so that clients will be connected before execution
begins. Otherwise, a fast program may access the multiplexer before the
poll service routine activates.
3. We must set the "emptying_flags" and "filling_flags" values here, because
they cannot be initialized statically, even though the values are
@ -2031,129 +2030,83 @@ IOPRESET (&mpx_dib); /* PRESET device (does n
mpx_ibuf = 0; /* clear input buffer */
if (tmxr_mux_free (&mpx_desc)) /* any lines attached? */
sim_cancel (&mpx_poll); /* no, so stop poll */
else { /* attached or listening */
if (mpx_poll.flags & UNIT_ATT) { /* network attached? */
mpx_poll.wait = POLL_FIRST; /* set up poll */
sim_activate (&mpx_poll, mpx_poll.wait); /* start poll immediately */
sim_activate (&mpx_poll, mpx_poll.wait); /* start Telnet poll immediately */
}
else
sim_cancel (&mpx_poll); /* else stop Telnet poll */
return SCPE_OK;
}
/* Attach the multiplexer or a line.
/* Attach the multiplexer to a Telnet port.
We are called by the ATTACH MPX <port> command to attach the multiplexer to
the listening port indicated by <port> and by ATTACH MPX<n> <ser> to attach
line <n> to serial port <ser>. Logically, it is the multiplexer device that
is attached; however, SIMH only allows units to be attached. This makes
sense for devices such as tape drives, where the attached media is a property
of a specific drive. In our case, though, the listening port is a property
of the multiplexer card, not of any given serial line.
the listening port indicated by <port>. Logically, it is the multiplexer
device that is attached; however, SIMH only allows units to be attached.
This makes sense for devices such as tape drives, where the attached media is
a property of a specific drive. In our case, though, the listening port is a
property of the multiplexer card, not of any given serial line. As ATTACH
MPX is equivalent to ATTACH MPX0, the port would, by default, be attached to
the first serial line and be reported there in a SHOW MPX command.
To preserve the logical picture, we attach the listening port to the poll
unit (unit 9), which is normally disabled to inhibit its display. Serial
ports are attached to line units 0-7 normally. Attachment is reported by the
"mpx_status" routine below.
To preserve the logical picture, we attach the port to the Telnet poll unit,
which is normally disabled to inhibit its display. Attaching to a disabled
unit is not allowed, so we first enable the unit, then attach it, then
disable it again. Attachment is reported by the "mpx_status" routine below.
The connection poll service routine is synchronized with the other input
polling devices in the simulator to facilitate idling.
Implementation notes:
1. ATTACH MPX will pass a pointer unit 0. This is because the common
simulator code treats ATTACH MPX as equivalent to ATTACH MPX0. We
differentiate these cases by examining the "sim_unit_ref" global to see
if a device was referenced.
2. Directly attempting to attach to units 8 (controller) or 9 (poll) will be
rejected.
3. If we are being called as part of RESTORE processing, we may see a
request to attach the poll unit (unit 9). This will occur if unit 9 was
attached when the SAVE was done. In this case, the SIM_SW_REST flag will
be set in "sim_switches", and we will allow the call to succeed.
4. If the poll unit is attached, it will be enabled as part of RESTORE
processing. We always unilaterally disable this unit to ensure that it
remains hidden.
The Telnet poll service routine is synchronized with the other input polling
devices in the simulator to facilitate idling.
*/
t_stat mpx_attach (UNIT *uptr, char *cptr)
{
t_stat status = SCPE_OK;
if ((uptr == &mpx_cntl) || /* attaching controller? */
(uptr == &mpx_poll) && !(sim_switches & SIM_SW_REST)) /* or poll unit directly? */
return SCPE_NOATT; /* disallow */
if (uptr != mpx_unit) /* not unit 0? */
return SCPE_NOATT; /* can't attach */
if (sim_unit_ref == ref_dev || (uptr == &mpx_poll)) { /* device attach or poll restore request? */
status = tmxr_attach (&mpx_desc, &mpx_poll, cptr); /* attach to socket */
mpx_poll.flags = mpx_poll.flags | UNIT_DIS; /* disable unit */
}
else /* line attach request */
status = tmxr_attach_line (uptr, 0, cptr, &mpx_desc); /* attach line */
mpx_poll.flags = mpx_poll.flags & ~UNIT_DIS; /* enable unit */
status = tmxr_attach (&mpx_desc, &mpx_poll, cptr); /* attach to socket */
mpx_poll.flags = mpx_poll.flags | UNIT_DIS; /* disable unit */
if (status == SCPE_OK) {
mpx_poll.wait = POLL_FIRST; /* set up poll */
sim_activate (&mpx_poll, mpx_poll.wait); /* start poll immediately */
mpx_poll.wait = POLL_FIRST; /* set up poll */
sim_activate (&mpx_poll, mpx_poll.wait); /* start poll immediately */
}
return status;
}
/* Detach the multiplexer or a line.
/* Detach the multiplexer.
We are called by the DETACH MPX command to detach the listening port and all
Telnet sessions and by the DETACH MPX<n> to detach a serial port from line
<n>. We will also be called by DETACH ALL, RESTORE, and during simulator
shutdown. For DETACH ALL and RESTORE, we must not fail the call, or
processing of other units will cease.
Implementation notes:
Normally, we are called by the DETACH MPX command, which is equivalent to
DETACH MPX0. However, we may be called with other units in two cases.
1. Because DETACH MPX will pass unit 0, we check the "sim_unit_ref" global
to see if MPX or MPX0 was specified in the command.
2. Directly attempting to detach unit 8 (controller) will be rejected. We
cannot fail a direct DETACH MPX9 (poll unit), because we cannot tell that
case apart from a DETACH ALL (a RESTORE will have the SIM_SW_REST flag
set in "sim_switches").
3. During simulator shutdown, we will be called for units 0-8 (detach_all in
scp.c calls the detach routines of all units that do NOT have
UNIT_ATTABLE), as well as for unit 9 if it is attached.
A DETACH ALL command will call us for unit 9 (the poll unit) if it is
attached. Also, during simulator shutdown, we will be called for units 0-8
(detach_all in scp.c calls the detach routines of all units that do NOT have
UNIT_ATTABLE), as well as for unit 9 if it is attached. In both cases, it is
imperative that we return SCPE_OK, otherwise any remaining device detaches
will not be performed.
*/
t_stat mpx_detach (UNIT *uptr)
{
uint32 ln;
t_stat status;
t_bool mux_free = TRUE;
t_stat status = SCPE_OK;
int32 i;
if (uptr == &mpx_cntl) /* detaching controller directly? */
return SCPE_NOATT; /* disallow */
if ((uptr == mpx_unit) || (uptr == &mpx_poll)) { /* base unit or poll unit? */
status = tmxr_detach (&mpx_desc, &mpx_poll); /* detach socket */
if (sim_unit_ref == ref_dev || uptr == &mpx_poll) /* device detach or detach all request? */
status = tmxr_detach (&mpx_desc, &mpx_poll); /* detach socket */
for (i = 0; i < MPX_PORTS; i++) {
mpx_ldsc [i].rcve = 0; /* disable line reception */
sim_cancel (&mpx_unit [i]); /* cancel any scheduled I/O */
}
else /* line detach request */
status = tmxr_detach_line (uptr, 0, NULL, &mpx_desc); /* detach line */
if (status == SCPE_OK) {
for (ln = 0; ln < MPX_PORTS; ln++) /* loop through lines */
if (tmxr_line_free (&mpx_ldsc[ln])) { /* is line free? */
mpx_ldsc[ln].rcve = 0; /* disable rcv as line was reset */
sim_cancel (&mpx_unit [ln]); /* cancel any scheduled I/O */
}
else
mux_free = FALSE; /* mux isn't free if line is in use */
if (mux_free && !(mpx_poll.flags & UNIT_ATT)) /* all lines free and not listening? */
sim_cancel (&mpx_poll); /* stop poll */
sim_cancel (&mpx_poll); /* stop Telnet poll */
}
return status;
@ -2214,7 +2167,7 @@ return SCPE_OK;
/* Local routines */
/* Poll for new connections */
/* Poll for new Telnet connections */
static void poll_connection (void)
{

View file

@ -29,8 +29,6 @@
28-Mar-11 JDB Tidied up signal handling
26-Oct-10 JDB Changed I/O signal handler for revised signal model
25-Nov-08 JDB Revised for new multiplexer library SHOW routines
19-Nov-08 JDB [serial] Removed DEV_NET to allow restoration of listening port
20-Oct-08 JDB [serial] Added serial port support
09-Oct-08 JDB "muxl_unit" defined one too many units (17 instead of 16)
10-Sep-08 JDB SHOW MUX CONN/STAT with SET MUX DIAG is no longer disallowed
07-Sep-08 JDB Changed Telnet poll to connect immediately after reset or attach
@ -329,7 +327,6 @@ t_stat muxo_svc (UNIT *uptr);
t_stat muxc_reset (DEVICE *dptr);
t_stat mux_attach (UNIT *uptr, char *cptr);
t_stat mux_detach (UNIT *uptr);
t_stat muxl_detach (UNIT *uptr);
t_stat mux_setdiag (UNIT *uptr, int32 val, char *cptr, void *desc);
@ -433,8 +430,8 @@ DEVICE muxl_dev = {
NULL, /* deposit routine */
&muxc_reset, /* reset routine */
NULL, /* boot routine */
&mux_attach, /* attach routine */
&muxl_detach, /* detach routine */
NULL, /* attach routine */
NULL, /* detach routine */
&muxl_dib, /* device information block */
DEV_DISABLE, /* device flags */
0, /* debug control flags */
@ -459,9 +456,9 @@ DEVICE muxl_dev = {
DEVICE muxu_dev;
int32 mux_order [MUX_LINES] = { -1 }; /* connection order */
TMLN mux_ldsc [MUX_LINES] = { { 0 } }; /* line descriptors */
TMXR mux_desc = { MUX_LINES, 0, 0, mux_ldsc, mux_order, &muxu_dev }; /* device descriptor */
int32 mux_order [MUX_LINES] = { -1 }; /* connection order */
TMLN mux_ldsc [MUX_LINES] = { { 0 } }; /* line descriptors */
TMXR mux_desc = { MUX_LINES, 0, 0, mux_ldsc, mux_order }; /* device descriptor */
UNIT muxu_unit = { UDATA (&muxi_svc, UNIT_ATTABLE, 0), POLL_FIRST };
@ -923,7 +920,7 @@ while (working_set) {
(old & DTR) && /* DTR drop? */
!(muxc_ota[ln] & DTR)) {
tmxr_linemsg (&mux_ldsc[ln], "\r\nLine hangup\r\n");
tmxr_clear_ln (&mux_desc, &mux_ldsc[ln]); /* disconnect line */
tmxr_reset_ln (&mux_ldsc[ln]); /* reset line */
muxc_lia[ln] = 0; /* dataset off */
}
} /* end update */
@ -1308,12 +1305,12 @@ IOPRESET (dibptr); /* PRESET device (does n
muxc_chan = muxc_scan = 0; /* init modem scan */
if (tmxr_mux_free (&mux_desc)) /* any lines attached? */
sim_cancel (&muxu_unit); /* no, so stop poll */
else { /* attached or listening */
if (muxu_unit.flags & UNIT_ATT) { /* master att? */
muxu_unit.wait = POLL_FIRST; /* set up poll */
sim_activate (&muxu_unit, muxu_unit.wait); /* start poll immediately */
sim_activate (&muxu_unit, muxu_unit.wait); /* start Telnet poll immediately */
}
else
sim_cancel (&muxu_unit); /* else stop */
for (i = 0; i < MUX_LINES; i++)
mux_reset_ln (i); /* reset lines 0-15 */
@ -1325,23 +1322,20 @@ return SCPE_OK;
}
/* Attach master unit or line */
/* Attach master unit */
t_stat mux_attach (UNIT *uptr, char *cptr)
{
t_stat status = SCPE_OK;
if (muxu_unit.flags & UNIT_DIAG) /* diag mode? */
return SCPE_NOFNC; /* command not allowed */
if (muxu_unit.flags & UNIT_DIAG) /* diag mode? */
return SCPE_NOFNC; /* command not allowed */
if (uptr == &muxu_unit) /* master unit? */
status = tmxr_attach (&mux_desc, uptr, cptr); /* attach socket */
else
status = tmxr_attach_line (uptr, 0, cptr, &mux_desc); /* attach line */
status = tmxr_attach (&mux_desc, uptr, cptr); /* attach */
if (status == SCPE_OK) { /* attach successful? */
muxu_unit.wait = POLL_FIRST; /* set up poll */
sim_activate (&muxu_unit, muxu_unit.wait); /* start poll immediately */
if (status == SCPE_OK) {
muxu_unit.wait = POLL_FIRST; /* set up poll */
sim_activate (&muxu_unit, muxu_unit.wait); /* start Telnet poll immediately */
}
return status;
@ -1352,45 +1346,13 @@ return status;
t_stat mux_detach (UNIT *uptr)
{
uint32 ln;
t_stat status;
t_bool free = TRUE;
int32 i;
t_stat r;
status = tmxr_detach (&mux_desc, uptr); /* detach unit */
if (status == SCPE_OK) {
for (ln = 0; ln < MUX_LINES; ln++) /* loop through lines */
if (tmxr_line_free (&mux_ldsc[ln])) /* is line free? */
mux_ldsc[ln].rcve = 0; /* yes, so disable rcv as line was reset */
else
free = FALSE; /* mux isn't free if line is in use */
if (free) /* all lines free? */
sim_cancel (uptr); /* stop poll */
}
return status;
}
/* Detach line */
t_stat muxl_detach (UNIT *uptr)
{
uint32 ln;
t_stat status;
status = tmxr_detach_line (uptr, 0, NULL, &mux_desc); /* detach line */
if (status == SCPE_OK) {
ln = uptr - muxl_unit; /* determine line number */
mux_ldsc[ln].rcve = 0; /* disable line reception */
if (tmxr_mux_free (&mux_desc)) /* all lines free and not listening? */
sim_cancel (&muxu_unit); /* stop poll */
}
return status;
r = tmxr_detach (&mux_desc, uptr); /* detach */
for (i = 0; i < MUX_LINES; i++) mux_ldsc[i].rcve = 0; /* disable rcv */
sim_cancel (uptr); /* stop poll */
return r;
}
@ -1405,7 +1367,7 @@ return status;
for normal character transfers, which is undesirable.
Therefore, to enable diagnostic mode, we must force a disconnect of the
master socket and all Telnet and serial lines, which clears the connection
master socket and any connected Telnet lines, which clears the connection
flags on all lines. Then we set the "transmission enabled" flags on all
lines to enable output character processing for the diagnostic. (Normally,
all of the flags are set when the multiplexer is first attached. Until then,
@ -1418,12 +1380,9 @@ t_stat mux_setdiag (UNIT *uptr, int32 val, char *cptr, void *desc)
int32 ln;
if (val) { /* set diag? */
mux_detach (uptr); /* detach Telnet lines */
for (ln = 0; ln < MUX_LINES; ln++) {
muxl_detach (&muxl_unit[ln]); /* detach all serial lines */
mux_ldsc[ln].xmte = 1; /* enable transmission on all lines */
}
mux_detach (uptr); /* detach lines */
for (ln = 0; ln < MUX_LINES; ln++) /* enable transmission */
mux_ldsc[ln].xmte = 1; /* on all lines */
}
else { /* set term */
for (ln = 0; ln < MUX_LINES; ln++) /* clear connections */

View file

@ -27,7 +27,6 @@
18-Apr-2012 RMS Modified to use clock coscheduling
17-Aug-2011 RMS Added AUTOCONFIGURE modifier
26-Nov-2008 JDB [serial] Added serial port support
19-Nov-2008 RMS Revised for common TMXR show routines
Revised to autoconfigure vectors
@ -132,7 +131,6 @@ t_stat dci_svc (UNIT *uptr);
t_stat dco_svc (UNIT *uptr);
t_stat dcx_attach (UNIT *uptr, char *cptr);
t_stat dcx_detach (UNIT *uptr);
t_stat dcl_detach (UNIT *uptr);
t_stat dcx_set_lines (UNIT *uptr, int32 val, char *cptr, void *desc);
void dcx_enbdis (int32 dis);
void dci_clr_int (int32 ln);
@ -254,7 +252,7 @@ DEVICE dco_dev = {
"DCO", dco_unit, dco_reg, dco_mod,
DCX_LINES, 10, 31, 1, 8, 8,
NULL, NULL, &dcx_reset,
NULL, &dcx_attach, &dcl_detach,
NULL, NULL, NULL,
NULL, DEV_UBUS | DEV_DISABLE | DEV_DIS
};
@ -368,6 +366,8 @@ t_stat dci_svc (UNIT *uptr)
{
int32 ln, c, temp;
if ((uptr->flags & UNIT_ATT) == 0) /* attached? */
return SCPE_OK;
sim_activate (uptr, clk_cosched (tmxr_poll)); /* continue poll */
ln = tmxr_poll_conn (&dcx_desc); /* look for connect */
if (ln >= 0) { /* got one? */
@ -510,12 +510,9 @@ t_stat dcx_reset (DEVICE *dptr)
int32 ln;
dcx_enbdis (dptr->flags & DEV_DIS); /* sync enables */
//
if (tmxr_mux_free (&dcx_desc)) /* any lines attached? */
sim_cancel (&dci_unit); /* no, so stop poll */
else /* attached or listening */
sim_activate (&dci_unit, tmxr_poll); /* start poll immediately */
//
sim_cancel (&dci_unit); /* assume stop */
if (dci_unit.flags & UNIT_ATT) /* if attached, */
sim_activate (&dci_unit, tmxr_poll); /* activate */
for (ln = 0; ln < DCX_LINES; ln++) /* for all lines */
dcx_reset_ln (ln);
return auto_config (dci_dev.name, dcx_desc.lines); /* auto config */
@ -535,22 +532,16 @@ dco_clr_int (ln);
return;
}
/* Attach master unit or line */
/* Attach master unit */
t_stat dcx_attach (UNIT *uptr, char *cptr)
{
t_stat r;
//
if (uptr == &dci_unit) /* master unit? */
r = tmxr_attach (&dcx_desc, uptr, cptr); /* attach socket */
else
r = tmxr_attach_line (uptr, 0, cptr, &dcx_desc); /* attach line */
//
r = tmxr_attach (&dcx_desc, uptr, cptr); /* attach */
if (r != SCPE_OK) /* error? */
return r;
sim_activate (&dci_unit, tmxr_poll); /* start poll */
sim_activate (uptr, tmxr_poll); /* start poll */
return SCPE_OK;
}
@ -560,48 +551,14 @@ t_stat dcx_detach (UNIT *uptr)
{
int32 i;
t_stat r;
t_bool free = TRUE;
r = tmxr_detach (&dcx_desc, uptr); /* detach */
//
if (r == SCPE_OK) {
for (i = 0; i < DCX_LINES; i++) /* loop through lines */
if (tmxr_line_free (&dcx_ldsc[i])) /* is line free? */
dcx_ldsc[i].rcve = 0; /* yes, so disable rcv as line was reset */
else
free = FALSE; /* mux isn't free if line is in use */
if (free) /* all lines free? */
sim_cancel (uptr); /* stop poll */
}
//
for (i = 0; i < DCX_LINES; i++) /* all lines, */
dcx_ldsc[i].rcve = 0; /* disable rcv */
sim_cancel (uptr); /* stop poll */
return r;
}
/* Detach line */
//
t_stat dcl_detach (UNIT *uptr)
{
uint32 ln;
t_stat status;
status = tmxr_detach_line (uptr, 0, NULL, &dcx_desc); /* detach line */
if (status == SCPE_OK) {
ln = uptr - dco_unit; /* determine line number */
dcx_ldsc[ln].rcve = 0; /* disable line reception */
if (tmxr_mux_free (&dcx_desc)) /* all lines free and not listening? */
sim_cancel (&dci_unit); /* stop poll */
}
return status;
}
//
/* Enable/disable device */
void dcx_enbdis (int32 dis)

View file

@ -26,7 +26,6 @@
dz DZ11 terminal multiplexor
29-Dec-08 RMS Added MTAB_NC to SET LOG command (Walter Mueller)
24-Nov-08 JDB [serial] Added serial port support
19-Nov-08 RMS Revised for common TMXR show routines
18-Jun-07 RMS Added UNIT_IDLE flag
29-Oct-06 RMS Synced poll and clock
@ -166,12 +165,16 @@ TMXR dz_desc = { DZ_MUXES * DZ_LINES, 0, 0, dz_ldsc }; /* mux descriptor */
#define DBG_INT 0x0002 /* display transfer requests */
#define DBG_XMT TMXR_DBG_XMT /* display Transmitted Data */
#define DBG_RCV TMXR_DBG_RCV /* display Received Data */
#define DBG_TRC TMXR_DBG_TRC /* display trace routine calls */
#define DBG_ASY TMXR_DBG_ASY /* display Asynchronous Activities */
DEBTAB dz_debug[] = {
{"REG", DBG_REG},
{"INT", DBG_INT},
{"XMT", DBG_XMT},
{"RCV", DBG_RCV},
{"TRC", DBG_TRC},
{"ASY", DBG_ASY},
{0}
};
@ -232,12 +235,8 @@ MTAB dz_mod[] = {
{ TT_MODE, TT_MODE_7B, "7b", "7B", NULL },
{ TT_MODE, TT_MODE_8B, "8b", "8B", NULL },
{ TT_MODE, TT_MODE_7P, "7p", "7P", NULL },
//
{ MTAB_XTD | MTAB_VDV | MTAB_NC, ':', NULL, "CONNECT",
&tmxr_attach_line, NULL, &dz_desc },
{ MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT",
&tmxr_detach_line, NULL, &dz_desc },
//
&tmxr_dscln, NULL, &dz_desc },
{ UNIT_ATT, UNIT_ATT, "summary", NULL,
NULL, &tmxr_show_summ, (void *) &dz_desc },
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL,
@ -268,7 +267,7 @@ DEVICE dz_dev = {
1, DEV_RDX, 8, 1, DEV_RDX, 8,
&tmxr_ex, &tmxr_dep, &dz_reset,
NULL, &dz_attach, &dz_detach,
&dz_dib, DEV_FLTA | DEV_DISABLE | DEV_NET | DEV_UBUS | DEV_QBUS | DEV_DEBUG,
&dz_dib, DEV_FLTA | DEV_DISABLE | DEV_UBUS | DEV_QBUS | DEV_DEBUG,
0, dz_debug
};

View file

@ -296,12 +296,16 @@ static TMLX vh_parm[VH_MUXES * VH_LINES] = { { 0 } };
#define DBG_INT 0x0002 /* display transfer requests */
#define DBG_XMT TMXR_DBG_XMT /* display Transmitted Data */
#define DBG_RCV TMXR_DBG_RCV /* display Received Data */
#define DBG_TRC TMXR_DBG_TRC /* display trace routine calls */
#define DBG_ASY TMXR_DBG_ASY /* display Asynchronous Activities */
DEBTAB vh_debug[] = {
{"REG", DBG_REG},
{"INT", DBG_INT},
{"XMT", DBG_XMT},
{"RCV", DBG_RCV},
{"TRC", DBG_TRC},
{"ASY", DBG_ASY},
{0}
};
@ -407,7 +411,7 @@ DEVICE vh_dev = {
&vh_attach, /* attach routine */
&vh_detach, /* detach routine */
(void *)&vh_dib,/* context */
DEV_FLTA | DEV_DISABLE | DEV_DIS |DEV_NET | DEV_QBUS | DEV_UBUS | DEV_DEBUG, /* flags */
DEV_FLTA | DEV_DISABLE | DEV_DIS | DEV_QBUS | DEV_UBUS | DEV_DEBUG, /* flags */
0, vh_debug
};

View file

@ -50,7 +50,7 @@
UsePrecompiledHeader="0"
WarningLevel="3"
Detect64BitPortabilityProblems="false"
DebugInformationFormat="4"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"

184
scp.c
View file

@ -57,7 +57,6 @@
08-Feb-09 RMS Fixed warnings in help printouts
29-Dec-08 RMS Fixed implementation of MTAB_NC
24-Nov-08 RMS Revised RESTORE unit logic for consistency
21-Oct-08 JDB [serial] Added sim_unit_ref global to indicate type of reference
05-Sep-08 JDB "detach_all" ignores error status returns if shutting down
17-Aug-08 RMS Revert RUN/BOOT to standard, rather than powerup, reset
25-Jul-08 JDB DO cmd missing params now default to null string
@ -377,7 +376,7 @@ char *get_sim_sw (char *cptr);
t_stat get_aval (t_addr addr, DEVICE *dptr, UNIT *uptr);
t_value get_rval (REG *rptr, uint32 idx);
void put_rval (REG *rptr, uint32 idx, t_value val);
t_value strtotv (char *inptr, char **endptr, uint32 radix);
t_value strtotv (const char *inptr, char **endptr, uint32 radix);
void fprint_help (FILE *st);
void fprint_stopped (FILE *st, t_stat r);
void fprint_capac (FILE *st, DEVICE *dptr, UNIT *uptr);
@ -420,15 +419,16 @@ void run_cmd_message (const char *unechod_cmdline, t_stat r);
/* Global data */
UNITREF sim_unit_ref;
DEVICE *sim_dflt_dev = NULL;
UNIT *sim_clock_queue = NULL;
UNIT *sim_clock_queue = QUEUE_LIST_END;
int32 sim_interval = 0;
int32 sim_switches = 0;
FILE *sim_ofile = NULL;
SCHTAB *sim_schptr = FALSE;
DEVICE *sim_dfdev = NULL;
UNIT *sim_dfunit = NULL;
DEVICE **sim_internal_devices = NULL;
uint32 sim_internal_device_count = 0;
int32 sim_opt_out = 0;
int32 sim_is_running = 0;
uint32 sim_brk_summ = 0;
@ -635,6 +635,10 @@ static CTAB cmd_table[] = {
"set console TELNET=UNBUFFERED\n"
" disables console telnet buffering\n"
"set console NOTELNET disable console telnet\n"
"set console SERIAL=serialport[;config]\n"
" specify console serial port and optionally\n"
" the port config (i.e. ;9600-8n1)\n"
"set console NOSERIAL disable console serial session\n"
"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"
@ -801,7 +805,7 @@ stop_cpu = 0;
sim_interval = 0;
sim_time = sim_rtime = 0;
noqueue_time = 0;
sim_clock_queue = NULL;
sim_clock_queue = QUEUE_LIST_END;
sim_is_running = 0;
sim_log = NULL;
if (sim_emax <= 0)
@ -2051,6 +2055,8 @@ static SHTAB show_glob_tab[] = {
{ "ASYNCH", &sim_show_asynch, 0 },
{ "ETHERNET", &eth_show_devices, 0 },
{ "SERIAL", &sim_show_serial, 0 },
{ "MULTIPLEXER", &tmxr_show_open_devices, 0 },
{ "MUX", &tmxr_show_open_devices, 0 },
{ "ON", &show_on, 0 },
{ NULL, NULL, 0 }
};
@ -2286,7 +2292,7 @@ int32 accum;
if (cptr && (*cptr != 0))
return SCPE_2MARG;
if (sim_clock_queue == NULL) {
if (sim_clock_queue == QUEUE_LIST_END) {
fprintf (st, "%s event queue empty, time = %.0f\n",
sim_name, sim_time);
return SCPE_OK;
@ -2294,7 +2300,7 @@ if (sim_clock_queue == NULL) {
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) {
for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = uptr->next) {
if (uptr == &sim_step_unit)
fprintf (st, " Step timer");
else if ((dptr = find_dev_from_unit (uptr)) != NULL) {
@ -2309,10 +2315,10 @@ for (uptr = sim_clock_queue; uptr != NULL; uptr = uptr->next) {
#if defined (SIM_ASYNCH_IO)
pthread_mutex_lock (&sim_asynch_lock);
fprintf (st, "asynchronous pending event queue\n");
if (sim_asynch_queue == AIO_LIST_END)
if (sim_asynch_queue == QUEUE_LIST_END)
fprintf (st, "Empty\n");
else {
for (uptr = sim_asynch_queue; uptr != AIO_LIST_END; uptr = uptr->a_next) {
for (uptr = sim_asynch_queue; uptr != QUEUE_LIST_END; uptr = uptr->a_next) {
if ((dptr = find_dev_from_unit (uptr)) != NULL) {
fprintf (st, " %s", sim_dname (dptr));
if (dptr->numunits > 1) fprintf (st, " unit %d",
@ -2418,6 +2424,8 @@ if (cptr && (*cptr != 0)) /* now eol? */
return SCPE_2MARG;
for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
show_dev_modifiers (st, dptr, NULL, flag, cptr);
for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i)
show_dev_modifiers (st, dptr, NULL, flag, cptr);
return SCPE_OK;
}
@ -2508,6 +2516,8 @@ if (cptr && (*cptr != 0)) /* now eol? */
return SCPE_2MARG;
for (i = 0; (dptr = sim_devices[i]) != NULL; i++)
show_dev_show_commands (st, dptr, NULL, flag, cptr);
for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i)
show_dev_show_commands (st, dptr, NULL, flag, cptr);
return SCPE_OK;
}
@ -2685,6 +2695,13 @@ for (i = start; (dptr = sim_devices[i]) != NULL; i++) {
return reason;
}
}
for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i) {
if (dptr->reset != NULL) {
reason = dptr->reset (dptr);
if (reason != SCPE_OK)
return reason;
}
}
return SCPE_OK;
}
@ -2756,7 +2773,8 @@ if (dptr == NULL) /* found dev? */
return SCPE_NXDEV;
if (uptr == NULL) /* valid unit? */
return SCPE_NXUN;
if (uptr->flags & UNIT_ATT) { /* already attached? */
if ((uptr->flags & UNIT_ATT) && /* already attached? */
!(uptr->flags & UNIT_ATTMULT)) { /* and only single attachable */
r = scp_detach_unit (dptr, uptr); /* detach it */
if (r != SCPE_OK) /* error? */
return r;
@ -2896,7 +2914,6 @@ DEVICE *dptr;
UNIT *uptr;
t_stat r;
sim_unit_ref = ref_unit_all; /* we will reference all units */
if ((start < 0) || (start > 1))
return SCPE_IERR;
for (i = start; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */
@ -2915,6 +2932,7 @@ for (i = start; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */
return SCPE_OK;
}
/* Call device-specific or file-oriented detach unit routine */
t_stat scp_detach_unit (DEVICE *dptr, UNIT *uptr)
@ -3040,6 +3058,19 @@ char *sim_dname (DEVICE *dptr)
return (dptr->lname? dptr->lname: dptr->name);
}
/* Get unit display name */
char *sim_uname (UNIT *uptr)
{
DEVICE *d = find_dev_from_unit(uptr);
static AIO_TLS char uname[CBUFSIZE];
if (d->numunits == 1)
return sim_dname (d);
sprintf (uname, "%s%d", sim_dname (d), (int)(uptr-d->units));
return uname;
}
/* Save command
sa[ve] filename save state to specified file
@ -3216,8 +3247,6 @@ t_bool force_restore = sim_switches & SWMASK ('F');
#define READ_I(xx) if (sim_fread (&xx, sizeof (xx), 1, rfile) == 0) \
return SCPE_IOERR;
sim_unit_ref = ref_unit_all; /* we will reference all units */
fstat (fileno (rfile), &rstat);
READ_S (buf); /* [V2.5+] read version */
v35 = v32 = FALSE;
@ -3612,7 +3641,7 @@ for (i = 1; (dptr = sim_devices[i]) != NULL; i++) { /* flush attached files
sim_cancel (&sim_step_unit); /* cancel step timer */
sim_throt_cancel (); /* cancel throttle */
AIO_UPDATE_QUEUE;
if (sim_clock_queue != NULL) { /* update sim time */
if (sim_clock_queue != QUEUE_LIST_END) { /* update sim time */
UPDATE_SIM_TIME (sim_clock_queue->time);
}
else {
@ -3643,10 +3672,15 @@ if (sim_log) /* log if enabled */
t_stat run_boot_prep (void)
{
UNIT *uptr;
sim_interval = 0; /* reset queue */
sim_time = sim_rtime = 0;
noqueue_time = 0;
sim_clock_queue = NULL;
for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = sim_clock_queue) {
sim_clock_queue = uptr->next;
uptr->next = NULL;
}
return reset_all (0);
}
@ -4605,6 +4639,12 @@ for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
(strcmp (cptr, dptr->lname) == 0)))
return dptr;
}
for (i = 0; sim_internal_device_count && (dptr = sim_internal_devices[i]); ++i) {
if ((strcmp (cptr, dptr->name) == 0) ||
(dptr->lname &&
(strcmp (cptr, dptr->lname) == 0)))
return dptr;
}
return NULL;
}
@ -4617,8 +4657,6 @@ return NULL;
result = pointer to device (null if no dev)
*iptr = pointer to unit (null if nx unit)
The "sim_unit_ref" global is set to the type of reference specified by cptr
(device or unit).
*/
DEVICE *find_unit (char *cptr, UNIT **uptr)
@ -4628,13 +4666,11 @@ char *nptr, *tptr;
t_stat r;
DEVICE *dptr;
sim_unit_ref = ref_unit; /* assume unit reference */
if (uptr == NULL) /* arg error? */
return NULL;
if ((dptr = find_dev (cptr))) { /* exact match? */
if (qdisable (dptr)) /* disabled? */
return NULL;
sim_unit_ref = ref_dev; /* flag device reference */
*uptr = dptr->units; /* unit 0 */
return dptr;
}
@ -4660,6 +4696,29 @@ for (i = 0; (dptr = sim_devices[i]) != NULL; i++) { /* base + unit#? */
return NULL;
}
/* 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+1)*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:
@ -4681,6 +4740,13 @@ for (i = 0; (dptr = sim_devices[i]) != NULL; i++) {
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;
}
}
return NULL;
}
@ -5028,13 +5094,13 @@ return 0;
On an error, the endptr will equal the inptr.
*/
t_value strtotv (char *inptr, char **endptr, uint32 radix)
t_value strtotv (const char *inptr, char **endptr, uint32 radix)
{
int32 nodigit;
t_value val;
uint32 c, digit;
*endptr = inptr; /* assume fails */
*endptr = (char *)inptr; /* assume fails */
if ((radix < 2) || (radix > 36))
return 0;
while (isspace (*inptr)) /* bypass white space */
@ -5056,7 +5122,7 @@ for (c = *inptr; isalnum(c); c = *++inptr) { /* loop through char */
}
if (nodigit) /* no digits? */
return 0;
*endptr = inptr; /* result pointer */
*endptr = (char *)inptr; /* result pointer */
return val;
}
@ -5110,6 +5176,7 @@ return SCPE_OK;
/* Event queue package
sim_activate add entry to event queue
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
@ -5145,7 +5212,7 @@ t_stat reason;
if (stop_cpu) /* stop CPU? */
return SCPE_STOP;
AIO_UPDATE_QUEUE;
if (sim_clock_queue == NULL) { /* queue empty? */
if (sim_clock_queue == QUEUE_LIST_END) { /* queue empty? */
UPDATE_SIM_TIME (noqueue_time); /* update sim time */
sim_interval = noqueue_time = NOQUEUE_WAIT; /* flag queue empty */
return SCPE_OK;
@ -5156,7 +5223,7 @@ do {
sim_clock_queue = uptr->next; /* remove first */
uptr->next = NULL; /* hygiene */
uptr->time = 0;
if (sim_clock_queue != NULL)
if (sim_clock_queue != QUEUE_LIST_END)
sim_interval = sim_clock_queue->time;
else sim_interval = noqueue_time = NOQUEUE_WAIT;
if (uptr->action != NULL)
@ -5180,13 +5247,18 @@ return reason;
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;
int32 accum;
AIO_ACTIVATE (sim_activate, uptr, event_time);
AIO_ACTIVATE (_sim_activate, uptr, event_time);
if (sim_is_active (uptr)) /* already active? */
return SCPE_OK;
if (sim_clock_queue == NULL) {
if (sim_clock_queue == QUEUE_LIST_END) {
UPDATE_SIM_TIME (noqueue_time);
}
else { /* update sim time */
@ -5195,7 +5267,7 @@ else { /* update sim time */
prvptr = NULL;
accum = 0;
for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr->next) {
for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
if (event_time < (accum + cptr->time))
break;
accum = accum + cptr->time;
@ -5210,7 +5282,7 @@ else {
prvptr->next = uptr;
}
uptr->time = event_time - accum;
if (cptr != NULL)
if (cptr != QUEUE_LIST_END)
cptr->time = cptr->time - uptr->time;
sim_interval = sim_clock_queue->time;
return SCPE_OK;
@ -5256,6 +5328,28 @@ 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 event_time)
{
return _sim_activate_after (uptr, event_time);
}
t_stat _sim_activate_after (UNIT *uptr, int32 usec_delay)
{
if (sim_is_active_bool (uptr)) /* already active? */
return SCPE_OK;
AIO_ACTIVATE (_sim_activate_after, uptr, usec_delay);
return sim_timer_activate_after (uptr, usec_delay);
}
/* sim_cancel - cancel (dequeue) event
Inputs:
@ -5270,30 +5364,43 @@ t_stat sim_cancel (UNIT *uptr)
UNIT *cptr, *nptr;
AIO_VALIDATE;
if (sim_clock_queue == NULL)
if (sim_clock_queue == QUEUE_LIST_END)
return SCPE_OK;
UPDATE_SIM_TIME (sim_clock_queue->time); /* update sim time */
nptr = NULL;
nptr = QUEUE_LIST_END;
if (sim_clock_queue == uptr)
nptr = sim_clock_queue = uptr->next;
else {
for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr->next) {
for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
if (cptr->next == uptr) {
nptr = cptr->next = uptr->next;
break; /* end queue scan */
}
}
}
if (nptr != NULL)
if (nptr != QUEUE_LIST_END)
nptr->time = nptr->time + uptr->time;
uptr->next = NULL; /* hygiene */
uptr->time = 0;
if (sim_clock_queue != NULL)
if (sim_clock_queue != QUEUE_LIST_END)
sim_interval = sim_clock_queue->time;
else sim_interval = noqueue_time = NOQUEUE_WAIT;
return SCPE_OK;
}
/* sim_is_active_bool - test for entry in queue, return activation time
Inputs:
uptr = pointer to unit
Outputs:
result = TRUE if active FALSE if inactive
*/
t_bool sim_is_active_bool (UNIT *uptr)
{
return (uptr->next != NULL);
}
/* sim_is_active - test for entry in queue, return activation time
Inputs:
@ -5305,11 +5412,10 @@ return SCPE_OK;
int32 sim_is_active (UNIT *uptr)
{
UNIT *cptr;
int32 accum;
int32 accum = 0;
AIO_VALIDATE;
accum = 0;
for (cptr = sim_clock_queue; cptr != NULL; cptr = cptr->next) {
for (cptr = sim_clock_queue; cptr != QUEUE_LIST_END; cptr = cptr->next) {
if (cptr == sim_clock_queue) {
if (sim_interval > 0)
accum = accum + sim_interval;
@ -5331,7 +5437,7 @@ return 0;
double sim_gtime (void)
{
if (sim_clock_queue == NULL) {
if (sim_clock_queue == QUEUE_LIST_END) {
UPDATE_SIM_TIME (noqueue_time);
}
else {
@ -5342,7 +5448,7 @@ return sim_time;
uint32 sim_grtime (void)
{
if (sim_clock_queue == NULL) {
if (sim_clock_queue == QUEUE_LIST_END) {
UPDATE_SIM_TIME (noqueue_time);
}
else {
@ -5364,7 +5470,7 @@ int32 cnt;
UNIT *uptr;
cnt = 0;
for (uptr = sim_clock_queue; uptr != NULL; uptr = uptr->next)
for (uptr = sim_clock_queue; uptr != QUEUE_LIST_END; uptr = uptr->next)
cnt++;
return cnt;
}

13
scp.h
View file

@ -24,7 +24,6 @@
in this Software without prior written authorization from Robert M Supnik.
05-Dec-10 MP Added macro invocation of sim_debug
07-Jan-09 JDB Added UNITREF typedef
09-Aug-06 JDB Added assign_device and deassign_device
14-Jul-06 RMS Added sim_activate_abs
06-Jan-06 RMS Added fprint_stopped_gen
@ -53,10 +52,6 @@
#define CMD_OPT_SCH 004 /* search */
#define CMD_OPT_DFT 010 /* defaults */
/* unit references */
typedef enum { ref_dev, ref_unit, ref_unit_all } UNITREF;
/* Command processors */
t_stat reset_cmd (int32 flag, char *ptr);
@ -90,9 +85,13 @@ 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_activate_after (UNIT *uptr, int32 usecs_walltime);
t_stat sim_cancel (UNIT *uptr);
t_bool sim_is_active_bool (UNIT *uptr);
int32 sim_is_active (UNIT *uptr);
double sim_gtime (void);
uint32 sim_grtime (void);
@ -104,6 +103,7 @@ t_stat deassign_device (DEVICE *dptr);
t_stat reset_all (uint32 start_device);
t_stat reset_all_p (uint32 start_device);
char *sim_dname (DEVICE *dptr);
char *sim_uname (UNIT *dptr);
t_stat get_yn (char *ques, t_stat deflt);
char *get_sim_opt (int32 opt, char *cptr, t_stat *st);
char *get_glyph (char *iptr, char *optr, char mchar);
@ -111,12 +111,13 @@ char *get_glyph_nc (char *iptr, char *optr, char mchar);
t_value get_uint (char *cptr, uint32 radix, t_value max, t_stat *status);
char *get_range (DEVICE *dptr, char *cptr, t_addr *lo, t_addr *hi,
uint32 rdx, t_addr max, char term);
t_value strtotv (char *cptr, char **endptr, uint32 radix);
t_value strtotv (const char *cptr, char **endptr, uint32 radix);
t_stat fprint_val (FILE *stream, t_value val, uint32 rdx, uint32 wid, uint32 fmt);
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);

View file

@ -118,10 +118,20 @@
*/
#include "sim_defs.h"
#include "sim_sock.h"
#include "sim_tmxr.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,8 +146,51 @@ int32 sim_del_char = '\b'; /* delete character */
#else
int32 sim_del_char = 0177;
#endif
TMLN sim_con_ldsc = { 0 }; /* console line descr */
TMXR sim_con_tmxr = { 1, 0, 0, &sim_con_ldsc }; /* console line mux */
t_stat sim_con_poll_svc (UNIT *uptr); /* console connection poll routine */
t_stat sim_con_reset (DEVICE *dptr); /* 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 */
#define DBG_ASY TMXR_DBG_ASY /* asynchronous thread activity */
DEBTAB sim_con_debug[] = {
{"TRC", DBG_TRC},
{"XMT", DBG_XMT},
{"RCV", DBG_RCV},
{"ASY", DBG_ASY},
{0}
};
MTAB sim_con_mod[] = {
{ 0 },
};
DEVICE sim_con_telnet = {
"CON-TEL", &sim_con_unit, NULL, sim_con_mod,
1, 0, 0, 0, 0, 0,
NULL, NULL, sim_con_reset, NULL, NULL, NULL,
NULL, DEV_DEBUG, 0, sim_con_debug};
TMLN sim_con_ldsc = { 0 }; /* console line descr */
TMXR sim_con_tmxr = { 1, 0, 0, &sim_con_ldsc, NULL, &sim_con_telnet };/* console line mux */
/* Unit service for console connection polling */
t_stat sim_con_poll_svc (UNIT *uptr)
{
if (sim_con_tmxr.master == 0) /* not Telnet? done */
return SCPE_OK;
if (tmxr_poll_conn (&sim_con_tmxr) >= 0) /* poll connect */
sim_con_ldsc.rcve = 1; /* rcv enabled */
sim_activate_after(uptr, 1000000); /* check again in 1 second */
return SCPE_OK;
}
t_stat sim_con_reset (DEVICE *dptr)
{
return sim_con_poll_svc (&dptr->units[0]); /* establish polling as needed */
}
extern volatile int32 stop_cpu;
extern int32 sim_quiet;
@ -154,6 +207,8 @@ static CTAB set_con_tab[] = {
{ "PCHAR", &sim_set_pchar, 0 },
{ "TELNET", &sim_set_telnet, 0 },
{ "NOTELNET", &sim_set_notelnet, 0 },
{ "SERIAL", &sim_set_serial, 0 },
{ "NOSERIAL", &sim_set_noserial, 0 },
{ "LOG", &sim_set_logon, 0 },
{ "NOLOG", &sim_set_logoff, 0 },
{ "DEBUG", &sim_set_debon, 0 },
@ -182,6 +237,12 @@ static CTAB set_con_telnet_tab[] = {
{ NULL, NULL, 0 }
};
static CTAB set_con_serial_tab[] = {
{ "LOG", &sim_set_cons_log, 0 },
{ "NOLOG", &sim_set_cons_nolog, 0 },
{ NULL, NULL, 0 }
};
static int32 *cons_kmap[] = {
&sim_int_char,
&sim_brk_char,
@ -438,7 +499,10 @@ while (*cptr != 0) { /* do all mods */
if (r == SCPE_OK) {
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 */
if (r == SCPE_OK)
sim_activate_after(&sim_con_unit, 1000000); /* check for connection in 1 second */
return r;
}
return SCPE_NOPARAM;
}
@ -463,16 +527,22 @@ t_stat sim_show_telnet (FILE *st, DEVICE *dunused, UNIT *uunused, int32 flag, ch
{
if (cptr && (*cptr != 0))
return SCPE_2MARG;
if (sim_con_tmxr.master == 0)
if ((sim_con_tmxr.master == 0) &&
(sim_con_ldsc.serport == 0))
fprintf (st, "Connected to console window\n");
else {
if (sim_con_ldsc.conn == 0)
fprintf (st, "Listening on port %s\n", sim_con_tmxr.port);
else {
fprintf (st, "Listening on port %s, connected to socket %d\n",
sim_con_tmxr.port, sim_con_ldsc.conn);
if (sim_con_ldsc.serport) {
fprintf (st, "Connected to ");
tmxr_fconns (st, &sim_con_ldsc, -1);
}
else
if (sim_con_ldsc.conn == 0)
fprintf (st, "Listening on port %s\n", sim_con_tmxr.port);
else {
fprintf (st, "Listening on port %s, connected to socket %d\n",
sim_con_tmxr.port, sim_con_ldsc.conn);
tmxr_fconns (st, &sim_con_ldsc, -1);
}
tmxr_fstats (st, &sim_con_ldsc, -1);
}
return SCPE_OK;
@ -541,6 +611,59 @@ else
return SCPE_OK;
}
/* Set console to Serial port (and parameters) */
t_stat sim_set_serial (int32 flag, char *cptr)
{
char *cvptr, gbuf[CBUFSIZE], ubuf[CBUFSIZE];
CTAB *ctptr;
t_stat r;
if ((cptr == NULL) || (*cptr == 0))
return SCPE_2FARG;
while (*cptr != 0) { /* do all mods */
cptr = get_glyph_nc (cptr, gbuf, ','); /* get modifier */
if ((cvptr = strchr (gbuf, '='))) /* = value? */
*cvptr++ = 0;
get_glyph (gbuf, ubuf, 0); /* modifier to UC */
if ((ctptr = find_ctab (set_con_serial_tab, ubuf))) { /* match? */
r = ctptr->action (ctptr->arg, cvptr); /* do the rest */
if (r != SCPE_OK)
return r;
}
else {
SERHANDLE serport = sim_open_serial (gbuf, NULL, &r);
if (serport != INVALID_HANDLE) {
sim_close_serial (serport);
if (r == SCPE_OK) {
char cbuf[CBUFSIZE];
if ((sim_con_tmxr.master) || /* already open? */
(sim_con_ldsc.serport))
sim_set_noserial (0, NULL); /* close first */
sprintf(cbuf, "Connect=%s", gbuf);
r = tmxr_attach (&sim_con_tmxr, &sim_con_unit, cbuf);/* open master socket */
sim_con_ldsc.rcve = 1; /* rcv enabled */
if (r == SCPE_OK)
sim_activate_after(&sim_con_unit, 1000000); /* check for connection in 1 second */
return r;
}
}
return SCPE_NOPARAM;
}
}
return SCPE_OK;
}
/* Close console Serial port */
t_stat sim_set_noserial (int32 flag, char *cptr)
{
if (cptr && (*cptr != 0)) /* too many arguments? */
return SCPE_2MARG;
if (sim_con_ldsc.serport == 0) /* ignore if already closed */
return SCPE_OK;
return tmxr_close_master (&sim_con_tmxr); /* close master socket */
}
/* Log File Open/Close/Show Support */
@ -687,9 +810,12 @@ 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) || /* ^E or not Telnet? */
((sim_con_tmxr.master == 0) && /* and not serial? */
(sim_con_ldsc.serport == 0)))
return c; /* in-window */
if (sim_con_ldsc.conn == 0) { /* no Telnet conn? */
if ((sim_con_ldsc.conn == 0) && /* no Telnet conn */
(sim_con_ldsc.serport == 0)) { /* and no serial conn? */
if (!sim_con_ldsc.txbfd) /* unbuffered? */
return SCPE_LOST; /* connection lost */
if (tmxr_poll_conn (&sim_con_tmxr) >= 0) /* poll connect */
@ -707,14 +833,16 @@ return SCPE_OK;
t_stat sim_putchar (int32 c)
{
if (sim_con_tmxr.master == 0) { /* not Telnet? */
if ((sim_con_tmxr.master == 0) && /* not Telnet? */
(sim_con_ldsc.serport == 0)) { /* and not serial port */
if (sim_log) /* log file? */
fputc (c, sim_log);
return sim_os_putchar (c); /* in-window version */
}
if (sim_log && !sim_con_ldsc.txlog) /* log file, but no line log? */
fputc (c, sim_log);
if (sim_con_ldsc.conn == 0) { /* no Telnet conn? */
if ((sim_con_ldsc.serport == 0) && /* no serial port */
(sim_con_ldsc.conn == 0)) { /* and no Telnet conn? */
if (!sim_con_ldsc.txbfd) /* unbuffered? */
return SCPE_LOST; /* connection lost */
if (tmxr_poll_conn (&sim_con_tmxr) >= 0) /* poll connect */
@ -729,14 +857,16 @@ t_stat sim_putchar_s (int32 c)
{
t_stat r;
if (sim_con_tmxr.master == 0) { /* not Telnet? */
if ((sim_con_tmxr.master == 0) && /* not Telnet? */
(sim_con_ldsc.serport == 0)) { /* and not serial port */
if (sim_log) /* log file? */
fputc (c, sim_log);
return sim_os_putchar (c); /* in-window version */
}
if (sim_log && !sim_con_ldsc.txlog) /* log file, but no line log? */
fputc (c, sim_log);
if (sim_con_ldsc.conn == 0) { /* no Telnet conn? */
if ((sim_con_ldsc.serport == 0) && /* no serial port */
(sim_con_ldsc.conn == 0)) { /* and no Telnet conn? */
if (!sim_con_ldsc.txbfd) /* non-buffered Telnet conn? */
return SCPE_LOST; /* lost */
if (tmxr_poll_conn (&sim_con_tmxr) >= 0) /* poll connect */
@ -791,6 +921,69 @@ else c = c & 0377;
return c;
}
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) && defined(SIM_ASYNCH_MUX)
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) && defined(SIM_ASYNCH_MUX)
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 ();
}
/* Platform specific routine definitions */
/* VMS routines, from Ben Thomas, with fixes from Robert Alan Byer */
#if defined (VMS)
@ -798,6 +991,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>
@ -810,6 +1004,7 @@ return c;
#define EFN 0
uint32 tty_chan = 0;
int buffered_character = 0;
typedef struct {
unsigned short sense_count;
@ -826,7 +1021,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;
@ -869,17 +1064,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];
@ -904,6 +1101,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;
@ -960,7 +1191,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);
@ -971,7 +1202,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) &&
@ -986,7 +1217,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);
@ -1000,12 +1231,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;
@ -1018,7 +1249,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 */
@ -1055,6 +1286,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;
@ -1070,27 +1312,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;
}
@ -1126,6 +1368,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) {
@ -1253,7 +1501,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] = " ";
@ -1276,22 +1524,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;
}
@ -1311,6 +1559,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) {
@ -1333,7 +1587,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;
@ -1360,7 +1614,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 */
@ -1374,7 +1628,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 */
@ -1387,12 +1641,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);
}
@ -1409,6 +1663,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 = (ms_timeout*1000)/1000000;
timeout.tv_usec = (ms_timeout*1000)%1000000;
return (1 == select (1, &readfds, NULL, NULL, &timeout));
}
t_stat sim_os_putchar (int32 out)
{
char c;
@ -1428,7 +1698,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;
@ -1470,7 +1740,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;
@ -1485,7 +1755,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;
@ -1499,12 +1769,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));
}
@ -1521,6 +1791,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 = (ms_timeout*1000)/1000000;
timeout.tv_usec = (ms_timeout*1000)%1000000;
return (1 == select (1, &readfds, NULL, NULL, &timeout));
}
t_stat sim_os_putchar (int32 out)
{
char c;

View file

@ -55,6 +55,8 @@ t_stat sim_set_console (int32 flag, char *cptr);
t_stat sim_set_kmap (int32 flag, char *cptr);
t_stat sim_set_telnet (int32 flag, char *cptr);
t_stat sim_set_notelnet (int32 flag, char *cptr);
t_stat sim_set_serial (int32 flag, char *cptr);
t_stat sim_set_noserial (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);

View file

@ -279,6 +279,14 @@ typedef uint32 t_addr;
#define MATCH_CMD(ptr,cmd) strncmp ((ptr), (cmd), strlen (ptr))
/* End of Linked List/Queue value */
/* Chosen for 2 reasons: */
/* 1 - to not be NULL, this allowing the NULL value to */
/* indicate inclusion on a list */
/* and */
/* 2 - to not be a valid/possible pointer (alignment) */
#define QUEUE_LIST_END ((void *)1)
/* Device data structure */
struct sim_device {
@ -376,25 +384,29 @@ struct sim_unit {
/* Unit flags */
#define UNIT_V_UF_31 12 /* dev spec, V3.1 */
#define UNIT_V_UF 16 /* device specific */
#define UNIT_V_RSV 31 /* reserved!! */
#define UNIT_V_UF_31 12 /* dev spec, V3.1 */
#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_ATTMULT 0100000 /* Allow multiple attach commands */
#define UNIT_TM_POLL 0400000 /* 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))
@ -573,20 +585,38 @@ extern int32 sim_asynch_check;
extern int32 sim_asynch_latency;
extern int32 sim_asynch_inst_latency;
#define AIO_LIST_END ((void *)1) /* Chosen to deliberately not be a valid pointer (alignment) */
/* Thread local storage */
#if defined(__GNUC__) && !defined(__APPLE__)
#define AIO_TLS __thread
#elif defined(__DECC_VER) || defined(_MSC_VER)
#define AIO_TLS __declspec(thread)
#else
/* Other compiler environment, then don't worry about thread local storage. */
/* It is primarily used only used in debugging messages */
#define AIO_TLS
#endif
#define AIO_INIT \
if (1) { \
sim_asynch_main_threadid = pthread_self(); \
/* Empty list/list end uses the point value (void *)1. \
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; \
}
sim_asynch_queue = QUEUE_LIST_END; \
} \
else \
(void)0
#define AIO_CLEANUP \
if (1) { \
pthread_mutex_destroy(&sim_asynch_lock); \
pthread_cond_destroy(&sim_asynch_wake); \
}
} \
else \
(void)0
#define AIO_LOCK \
pthread_mutex_lock(&sim_asynch_lock)
#define AIO_UNLOCK \
pthread_mutex_unlock(&sim_asynch_lock)
#if defined(__DECC_VER)
#include <builtins>
@ -616,13 +646,13 @@ extern int32 sim_asynch_inst_latency;
#define AIO_QUEUE_VAL InterlockedCompareExchangePointer(&sim_asynch_queue, sim_asynch_queue, NULL)
#define AIO_QUEUE_SET(val, queue) InterlockedCompareExchangePointer(&sim_asynch_queue, val, queue)
#define AIO_UPDATE_QUEUE \
if (AIO_QUEUE_VAL != AIO_LIST_END) { /* List !Empty */ \
if (AIO_QUEUE_VAL != QUEUE_LIST_END) { /* List !Empty */ \
UNIT *q, *uptr; \
int32 a_event_time; \
do \
q = AIO_QUEUE_VAL; \
while (q != AIO_QUEUE_SET(AIO_LIST_END, q)); \
while (q != AIO_LIST_END) { /* List !Empty */ \
while (q != AIO_QUEUE_SET(QUEUE_LIST_END, q)); \
while (q != QUEUE_LIST_END) { /* List !Empty */ \
uptr = q; \
q = q->a_next; \
uptr->a_next = NULL; /* hygiene */ \
@ -646,18 +676,18 @@ extern int32 sim_asynch_inst_latency;
UNIT *q, *qe; \
uptr->a_event_time = event_time; \
uptr->a_activate_call = sim_activate; \
uptr->a_next = AIO_LIST_END; /* Mark as on list */ \
uptr->a_next = QUEUE_LIST_END; /* Mark as on list */ \
do { \
do \
q = AIO_QUEUE_VAL; \
while (q != AIO_QUEUE_SET(AIO_LIST_END, q));/* Grab current list */ \
for (qe = uptr; qe->a_next != AIO_LIST_END; qe = qe->a_next); \
while (q != AIO_QUEUE_SET(QUEUE_LIST_END, q));/* Grab current list */\
for (qe = uptr; qe->a_next != QUEUE_LIST_END; qe = qe->a_next); \
qe->a_next = q; /* append current list */\
do \
q = AIO_QUEUE_VAL; \
while (q != AIO_QUEUE_SET(uptr, q)); \
uptr = q; \
} while (uptr != AIO_LIST_END); \
} while (uptr != QUEUE_LIST_END); \
} \
if (sim_idle_wait) \
pthread_cond_signal (&sim_asynch_wake); \
@ -671,7 +701,7 @@ extern int32 sim_asynch_inst_latency;
if (1) { \
UNIT *uptr; \
pthread_mutex_lock (&sim_asynch_lock); \
while (sim_asynch_queue != AIO_LIST_END) { /* List !Empty */ \
while (sim_asynch_queue != QUEUE_LIST_END) { /* List !Empty */ \
int32 a_event_time; \
uptr = sim_asynch_queue; \
sim_asynch_queue = uptr->a_next; \
@ -728,7 +758,9 @@ extern int32 sim_asynch_inst_latency;
#define AIO_CHECK_EVENT
#define AIO_INIT
#define AIO_CLEANUP
#define AIO_RETURN_TIME(uptr)
#define AIO_SET_INTERRUPT_LATENCY(instpersec)
#define AIO_TLS
#endif /* SIM_ASYNCH_IO */
#endif

View file

@ -41,7 +41,7 @@
sim_open_serial open a serial port
sim_config_serial change baud rate and character framing configuration
sim_control_serial connect or disconnect a serial port (controls DTR)
sim_control_serial manipulate and/or return the modem bits on a serial port
sim_read_serial read from a serial port
sim_write_serial write to a serial port
sim_close_serial close a serial port
@ -60,8 +60,8 @@
returned.
t_stat sim_config_serial (SERHANDLE port, SERCONFIG config)
-----------------------------------------------------------
t_stat sim_config_serial (SERHANDLE port, const char *config)
-------------------------------------------------------------
The baud rate and framing parameters (character size, parity, and number of
stop bits) of the serial port associated with "port" are set. If any
@ -70,13 +70,17 @@
is returned. If the configuration is successful, SCPE_OK is returned.
t_bool sim_control_serial (SERHANDLE port, t_bool connect)
----------------------------------------------------------
sim_control_serial (SERHANDLE port, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits)
-------------------------------------------------------------------------------------------------
If "connect" is TRUE, the DTR (modem control) line of the serial port
associated with "port" is asserted. If "connect" is false, the line is
denied. If the DTR change is successful, the function returns TRUE. FALSE
is returned if an error occurs.
The DTR and RTS line of the serial port is set or cleared as indicated in
the respective bits_to_set or bits_to_clear parameters. If the
incoming_bits parameter is not NULL, then the modem status bits DCD, RNG,
DSR and CTS are returned.
If unreasonable or nonsense bits_to_set or bits_to_clear bits are
specified, then the return status is SCPE_ARG;
If an error occurs, SCPE_IOERR is returned.
int32 sim_read_serial (SERHANDLE port, char *buffer, int32 count, char *brk)
@ -134,9 +138,17 @@ typedef struct serial_list {
char desc[SER_DEV_DESC_MAX];
} SERIAL_LIST;
typedef struct serial_config { /* serial port configuration */
uint32 baudrate; /* baud rate */
uint32 charsize; /* character size in bits */
char parity; /* parity (N/O/E/M/S) */
uint32 stopbits; /* 0/1/2 stop bits (0 implies 1.5) */
} SERCONFIG;
static int sim_serial_os_devices (int max, SERIAL_LIST* list);
static SERHANDLE sim_open_os_serial (char *name);
static void sim_close_os_serial (SERHANDLE port);
static void sim_close_os_serial (SERHANDLE port);
static t_stat sim_config_os_serial (SERHANDLE port, SERCONFIG config);
static struct open_serial_device {
@ -147,7 +159,17 @@ static struct open_serial_device {
} *serial_open_devices = NULL;
static int serial_open_device_count = 0;
static void _serial_add_to_open_list (SERHANDLE port, TMLN *line, const char *name, const char *desc)
static struct open_serial_device *_get_open_device (SERHANDLE port)
{
int i;
for (i=0; i<serial_open_device_count; ++i)
if (serial_open_devices[i].port == port)
return &serial_open_devices[i];
return NULL;
}
static struct open_serial_device *_serial_add_to_open_list (SERHANDLE port, TMLN *line, const char *name, const char *desc)
{
serial_open_devices = realloc(serial_open_devices, (++serial_open_device_count)*sizeof(*serial_open_devices));
memset(&serial_open_devices[serial_open_device_count-1], 0, sizeof(serial_open_devices[serial_open_device_count-1]));
@ -156,6 +178,7 @@ serial_open_devices[serial_open_device_count-1].line = line;
strcpy(serial_open_devices[serial_open_device_count-1].name, name);
if (desc)
strcpy(serial_open_devices[serial_open_device_count-1].desc, desc);
return &serial_open_devices[serial_open_device_count-1];
}
static void _serial_remove_from_open_list (SERHANDLE port)
@ -194,7 +217,7 @@ SERIAL_LIST *b = (SERIAL_LIST *)pb;
return strcmp(a->name, b->name);
}
static int sim_serial_devices(int max, SERIAL_LIST *list)
static int sim_serial_devices (int max, SERIAL_LIST *list)
{
int i, j, ports = sim_serial_os_devices(max, list);
@ -219,7 +242,7 @@ if (ports) /* Order the list returned alphabetically by the port name */
return ports;
}
static char* sim_serial_getname(int number, char* name)
static char* sim_serial_getname (int number, char* name)
{
SERIAL_LIST list[SER_MAX_DEVICE];
int count = sim_serial_devices(SER_MAX_DEVICE, list);
@ -230,7 +253,7 @@ strcpy(name, list[number].name);
return name;
}
static char* sim_serial_getname_bydesc(char* desc, char* name)
static char* sim_serial_getname_bydesc (char* desc, char* name)
{
SERIAL_LIST list[SER_MAX_DEVICE];
int count = sim_serial_devices(SER_MAX_DEVICE, list);
@ -258,7 +281,7 @@ return NULL;
}
/* strncasecmp() is not available on all platforms */
static int sim_serial_strncasecmp(char* string1, char* string2, size_t len)
static int sim_serial_strncasecmp (char* string1, char* string2, size_t len)
{
size_t i;
unsigned char s1, s2;
@ -280,7 +303,7 @@ for (i=0; i<len; i++) {
return 0;
}
static char* sim_serial_getname_byname(char* name, char* temp)
static char* sim_serial_getname_byname (char* name, char* temp)
{
SERIAL_LIST list[SER_MAX_DEVICE];
int count = sim_serial_devices(SER_MAX_DEVICE, list);
@ -299,7 +322,7 @@ for (i=0; i<count && !found; i++) {
return (found ? temp : NULL);
}
char* sim_serial_getdesc_byname(char* name, char* temp)
char* sim_serial_getdesc_byname (char* name, char* temp)
{
SERIAL_LIST list[SER_MAX_DEVICE];
int count = sim_serial_devices(SER_MAX_DEVICE, list);
@ -318,7 +341,7 @@ for (i=0; i<count && !found; i++) {
return (found ? temp : NULL);
}
t_stat sim_show_serial (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, char* desc)
t_stat sim_show_serial (FILE* st, DEVICE *dptr, UNIT* uptr, int32 val, char* desc)
{
SERIAL_LIST list[SER_MAX_DEVICE];
int number = sim_serial_devices(SER_MAX_DEVICE, list);
@ -336,7 +359,7 @@ else {
if ((len = strlen(list[i].name)) > min)
min = len;
for (i=0; i<number; i++)
fprintf(st," ser%d\t%-*s (%s)\n", i, (int)min, list[i].name, list[i].desc);
fprintf(st," ser%d\t%-*s%s%s%s\n", i, (int)min, list[i].name, list[i].desc[0] ? " (" : "", list[i].desc, list[i].desc[0] ? ")" : "");
}
if (serial_open_device_count) {
int i;
@ -345,54 +368,86 @@ if (serial_open_device_count) {
fprintf(st,"Open Serial Devices:\n");
for (i=0; i<serial_open_device_count; i++) {
d = sim_serial_getdesc_byname(serial_open_devices[i].name, desc);
if (d)
fprintf(st, " %s\tLn%02d %s (%s)\n", serial_open_devices[i].line->mp->dptr->name, (int)(serial_open_devices[i].line->mp->ldsc-serial_open_devices[i].line), serial_open_devices[i].line->sername, d);
else
fprintf(st, " %s\tLn%02d %s\n", serial_open_devices[i].line->mp->dptr->name, (int)(serial_open_devices[i].line->mp->ldsc-serial_open_devices[i].line), serial_open_devices[i].line->sername);
fprintf(st, " %s\tLn%02d %s%s%s%s\tConfig: %s\n", serial_open_devices[i].line->mp->dptr->name, (int)(serial_open_devices[i].line->mp->ldsc-serial_open_devices[i].line),
serial_open_devices[i].line->destination, d ? " {" : "", d ? d : "", d ? ")" : "", serial_open_devices[i].line->serconfig);
}
}
return SCPE_OK;
}
SERHANDLE sim_open_serial (char *name, TMLN *lp)
SERHANDLE sim_open_serial (char *name, TMLN *lp, t_stat *stat)
{
char temp1[1024], temp2[1024];
char temp1[1024], temp2[1024], devname [1024];
char *savname = name;
char *savdesc = NULL;
SERHANDLE port;
SERHANDLE port = INVALID_HANDLE;
char *config;
t_stat status;
config = get_glyph_nc (name, devname, ';'); /* separate port name from optional config params */
if ((config == NULL) || (*config == '\0'))
config = "9600-8N1";
if (stat)
*stat = SCPE_OK;
/* translate name of type "serX" to real device name */
if ((strlen(name) <= 5)
&& (tolower(name[0]) == 's')
&& (tolower(name[1]) == 'e')
&& (tolower(name[2]) == 'r')
&& (isdigit(name[3]))
&& (isdigit(name[4]) || (name[4] == '\0'))
if ((strlen(devname) <= 5)
&& (tolower(devname[0]) == 's')
&& (tolower(devname[1]) == 'e')
&& (tolower(devname[2]) == 'r')
&& (isdigit(devname[3]))
&& (isdigit(devname[4]) || (devname[4] == '\0'))
) {
int num = atoi(&name[3]);
int num = atoi(&devname[3]);
savname = sim_serial_getname(num, temp1);
if (savname == NULL) /* didn't translate */
return INVALID_HANDLE;
if (savname == NULL) { /* didn't translate */
if (stat)
*stat = SCPE_OPENERR;
return port;
}
savdesc = sim_serial_getdesc_byname (savname, temp2);
}
else {
/* are they trying to use device description? */
savname = sim_serial_getname_bydesc(name, temp1);
if (savname == NULL) { /* didn't translate */
savname = sim_serial_getname_bydesc(devname, temp1);
if (savname == NULL) { /* didn't translate */
/* probably is not serX and has no description */
savname = sim_serial_getname_byname(name, temp1);
savname = sim_serial_getname_byname(devname, temp1);
if (savname == NULL) /* didn't translate */
savname = name;
savname = devname;
else
savdesc = sim_serial_getdesc_byname(savname, temp2);
}
else
savdesc = name;
savdesc = devname;
}
port = sim_open_os_serial (savname);
if (port == INVALID_HANDLE) {
if (stat)
*stat = SCPE_OPENERR;
return port;
}
status = sim_config_serial (port, config); /* set serial configuration */
if (status != SCPE_OK) { /* port configuration error? */
sim_close_serial (port); /* close the port */
if (stat)
*stat = status;
port = INVALID_HANDLE; /* report error */
}
if ((port != INVALID_HANDLE) && (*config) && (lp)) {
lp->serconfig = realloc (lp->serconfig, 1 + strlen (config));
strcpy (lp->serconfig, config);
}
if (port != INVALID_HANDLE)
_serial_add_to_open_list (port, lp, savname, savdesc);
_serial_add_to_open_list (port, lp, savname, config);
return port;
}
@ -402,11 +457,51 @@ sim_close_os_serial (port);
_serial_remove_from_open_list (port);
}
/* Windows serial implementation */
t_stat sim_config_serial (SERHANDLE port, const char *sconfig)
{
const char *pptr;
char *sptr, *tptr;
SERCONFIG config = { 0 };
t_bool arg_error = FALSE;
t_stat r;
struct open_serial_device *dev;
if ((sconfig == NULL) || (*sconfig == '\0'))
sconfig = "9600-8N1"; /* default settings */
pptr = sconfig;
config.baudrate = (uint32)strtotv (pptr, &sptr, 10); /* parse baud rate */
arg_error = (pptr == sptr); /* check for bad argument */
if (*sptr) /* separator present? */
sptr++; /* skip it */
config.charsize = (uint32)strtotv (sptr, &tptr, 10); /* parse character size */
arg_error = arg_error || (sptr == tptr); /* check for bad argument */
if (*tptr) /* parity character present? */
config.parity = toupper (*tptr++); /* save parity character */
config.stopbits = (uint32)strtotv (tptr, &sptr, 10); /* parse number of stop bits */
arg_error = arg_error || (tptr == sptr); /* check for bad argument */
if (arg_error) /* bad conversions? */
return SCPE_ARG; /* report argument error */
if (strcmp (sptr, ".5") == 0) /* 1.5 stop bits requested? */
config.stopbits = 0; /* code request */
r = sim_config_os_serial (port, config);
dev = _get_open_device (port);
if (dev) {
dev->line->serconfig = realloc (dev->line->serconfig, 1 + strlen (sconfig));
strcpy (dev->line->serconfig, sconfig);
}
return r;
}
#if defined (_WIN32)
/* Windows serial implementation */
/* Enumerate the available serial ports.
@ -566,7 +661,7 @@ return port; /* return port handle on
1.5 stop bits.
*/
t_stat sim_config_serial (SERHANDLE port, SERCONFIG config)
t_stat sim_config_os_serial (SERHANDLE port, SERCONFIG config)
{
static const struct {
char parity;
@ -628,20 +723,54 @@ return SCPE_OK; /* return success status
/* Control a serial port.
The DTR line of the serial port is set or cleared. If "connect" is true,
then the line is set to enable the serial device. If "connect" is false, the
line is disabled to disconnect the device. If the line change was
successful, the function returns TRUE.
The DTR and RTS line of the serial port is set or cleared as indicated in
the respective bits_to_set or bits_to_clear parameters. If the
incoming_bits parameter is not NULL, then the modem status bits DCD, RNG,
DSR and CTS are returned.
If unreasonable or nonsense bits_to_set or bits_to_clear bits are
specified, then the return status is SCPE_ARG;
If an error occurs, SCPE_IOERR is returned.
*/
t_bool sim_control_serial (SERHANDLE port, t_bool connect)
t_stat sim_control_serial (SERHANDLE port, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits)
{
if (!EscapeCommFunction (port, connect ? SETDTR : CLRDTR)) {
sim_error_serial ("EscapeCommFunction", (int) GetLastError ());
return FALSE;
if ((bits_to_set & ~(TMXR_MDM_OUTGOING)) || /* Assure only settable bits */
(bits_to_clear & ~(TMXR_MDM_OUTGOING)) ||
(bits_to_set & bits_to_clear)) /* and can't set and clear the same bits */
return SCPE_ARG;
if (bits_to_set&TMXR_MDM_DTR)
if (!EscapeCommFunction (port, SETDTR)) {
sim_error_serial ("EscapeCommFunction", (int) GetLastError ());
return SCPE_IOERR;
}
if (bits_to_clear&TMXR_MDM_DTR)
if (!EscapeCommFunction (port, CLRDTR)) {
sim_error_serial ("EscapeCommFunction", (int) GetLastError ());
return SCPE_IOERR;
}
if (bits_to_set&TMXR_MDM_RTS)
if (!EscapeCommFunction (port, SETRTS)) {
sim_error_serial ("EscapeCommFunction", (int) GetLastError ());
return SCPE_IOERR;
}
if (bits_to_clear&TMXR_MDM_RTS)
if (!EscapeCommFunction (port, CLRRTS)) {
sim_error_serial ("EscapeCommFunction", (int) GetLastError ());
return SCPE_IOERR;
}
if (incoming_bits) {
DWORD ModemStat;
if (GetCommModemStatus (port, &ModemStat)) {
sim_error_serial ("GetCommModemStatus", (int) GetLastError ());
return SCPE_IOERR;
}
*incoming_bits = ((ModemStat&MS_CTS_ON) ? TMXR_MDM_CTS : 0) |
((ModemStat&MS_DSR_ON) ? TMXR_MDM_DSR : 0) |
((ModemStat&MS_RING_ON) ? TMXR_MDM_RNG : 0) |
((ModemStat&MS_RLSD_ON) ? TMXR_MDM_DCD : 0);
}
return TRUE;
return SCPE_OK;
}
@ -733,11 +862,10 @@ return;
#elif defined (__unix__) || defined(__APPLE__)
/* UNIX implementation */
#elif defined (__unix__)
/* Enumerate the available serial ports.
The serial port names generated by attempting to open /dev/ttyS0 thru
@ -772,6 +900,15 @@ for (i=0; (ports < max) && (i < 64); ++i) {
close (port);
}
}
for (i=1; (ports < max) && (i < 64); ++i) {
sprintf (list[ports].name, "/dev/tty.serial%d", i);
port = open (list[ports].name, O_RDWR | O_NOCTTY | O_NONBLOCK); /* open the port */
if (port != -1) { /* open OK? */
if (isatty (port)) /* is device a TTY? */
++ports;
close (port);
}
}
return ports;
}
@ -907,7 +1044,7 @@ return port; /* return port fd for su
*/
t_stat sim_config_serial (SERHANDLE port, SERCONFIG config)
t_stat sim_config_os_serial (SERHANDLE port, SERCONFIG config)
{
struct termios tio;
int32 i;
@ -983,30 +1120,52 @@ return SCPE_OK; /* configuration set suc
/* Control a serial port.
The DTR line of the serial port is set or cleared. If "connect" is true,
then the line is set to enable the serial device. If "connect" is false, the
line is disabled to disconnect the device. If the line change was
successful, the function returns TRUE.
The DTR and RTS line of the serial port is set or cleared as indicated in
the respective bits_to_set or bits_to_clear parameters. If the
incoming_bits parameter is not NULL, then the modem status bits DCD, RNG,
DSR and CTS are returned.
If unreasonable or nonsense bits_to_set or bits_to_clear bits are
specified, then the return status is SCPE_ARG;
If an error occurs, SCPE_IOERR is returned.
*/
t_bool sim_control_serial (SERHANDLE port, t_bool connect)
t_stat sim_control_serial (SERHANDLE port, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits)
{
int request;
static const int dtr = TIOCM_DTR;
int bits;
if (connect) /* request for DTR set? */
request = TIOCMBIS; /* use "set" control request */
else /* DTR clear */
request = TIOCMBIC; /* use "clear" control request */
if (ioctl (port, request, &dtr)) { /* set or clear the DTR line */
if (errno != EINVAL) /* DTR control not supported? */
sim_error_serial ("ioctl", errno); /* no, so report unexpected error */
return FALSE; /* return failure status */
if ((bits_to_set & ~(TMXR_MDM_OUTGOING)) || /* Assure only settable bits */
(bits_to_clear & ~(TMXR_MDM_OUTGOING)) ||
(bits_to_set & bits_to_clear)) /* and can't set and clear the same bits */
return SCPE_ARG;
if (bits_to_set) {
bits = ((bits_to_set&TMXR_MDM_DTR) ? TIOCM_DTR : 0) |
((bits_to_set&TMXR_MDM_RTS) ? TIOCM_RTS : 0);
if (ioctl (port, TIOCMBIS, &bits)) { /* set the desired bits */
sim_error_serial ("ioctl", errno); /* report unexpected error */
return SCPE_IOERR; /* return failure status */
}
}
if (bits_to_clear) {
bits = ((bits_to_clear&TMXR_MDM_DTR) ? TIOCM_DTR : 0) |
((bits_to_clear&TMXR_MDM_RTS) ? TIOCM_RTS : 0);
if (ioctl (port, TIOCMBIC, &bits)) { /* clear the desired bits */
sim_error_serial ("ioctl", errno); /* report unexpected error */
return SCPE_IOERR; /* return failure status */
}
}
if (incoming_bits) {
if (ioctl (port, TIOCMGET, &bits)) { /* get the modem bits */
sim_error_serial ("ioctl", errno); /* report unexpected error */
return SCPE_IOERR; /* return failure status */
}
*incoming_bits = ((bits&TIOCM_CTS) ? TMXR_MDM_CTS : 0) |
((bits&TIOCM_DSR) ? TMXR_MDM_DSR : 0) |
((bits&TIOCM_RNG) ? TMXR_MDM_RNG : 0) |
((bits&TIOCM_CAR) ? TMXR_MDM_DCD : 0);
}
return TRUE; /* control request succeeded */
return SCPE_OK;
}
@ -1109,19 +1268,11 @@ return;
}
#else
/* Non-implemented stubs */
#else
/* Enumerate the available serial ports.
The serial port names are extracted from the appropriate place in the
windows registry (HKLM\HARDWARE\DEVICEMAP\SERIALCOMM\). The resulting
list is sorted alphabetically by device name (COMn). The device description
is set to the OS internal name for the COM device.
*/
/* Enumerate the available serial ports. */
static int sim_serial_os_devices (int max, SERIAL_LIST* list)
{
@ -1130,7 +1281,7 @@ return -1;
/* Open a serial port */
SERHANDLE sim_open_serial (char *name)
SERHANDLE sim_open_os_serial (char *name)
{
return INVALID_HANDLE;
}
@ -1138,7 +1289,7 @@ return INVALID_HANDLE;
/* Configure a serial port */
t_stat sim_config_serial (SERHANDLE port, SERCONFIG config)
t_stat sim_config_os_serial (SERHANDLE port, SERCONFIG config)
{
return SCPE_IERR;
}
@ -1146,9 +1297,9 @@ return SCPE_IERR;
/* Control a serial port */
t_bool sim_control_serial (SERHANDLE port, t_bool connect)
t_stat sim_control_serial (SERHANDLE port, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits)
{
return FALSE;
return SCPE_NOFNC;
}

View file

@ -30,10 +30,9 @@
#ifndef _SIM_SERIAL_H_
#define _SIM_SERIAL_H_ 0
/* Windows definitions */
#if defined (_WIN32)
/* Windows definitions */
/* We need the basic Win32 definitions, but including "windows.h" also includes
"winsock.h" as well. However, "sim_sock.h" explicitly includes "winsock2.h,"
@ -52,13 +51,12 @@ typedef HANDLE SERHANDLE;
#define INVALID_HANDLE INVALID_HANDLE_VALUE
#elif defined (__unix__) || defined(__APPLE__)
/* UNIX definitions */
#elif defined (__unix__)
#include <fcntl.h>
#include <termio.h>
#include <termios.h>
#include <unistd.h>
#include <sys/ioctl.h>
@ -67,29 +65,24 @@ typedef int SERHANDLE;
#define INVALID_HANDLE -1
/* Non-implemented definitions */
#else
/* Non-implemented definitions */
typedef int SERHANDLE;
#define INVALID_HANDLE -1
#endif
/* Common definitions */
typedef struct serial_config { /* serial port configuration */
uint32 baudrate; /* baud rate */
uint32 charsize; /* character size in bits */
char parity; /* parity (N/O/E/M/S) */
uint32 stopbits; /* 0/1/2 stop bits (0 implies 1.5) */
} SERCONFIG;
/* Global routines */
#include "sim_tmxr.h" /* need TMLN definition */
#include "sim_tmxr.h" /* need TMLN definition and modem definitions */
extern SERHANDLE sim_open_serial (char *name, TMLN *lp);
extern t_stat sim_config_serial (SERHANDLE port, SERCONFIG config);
extern t_bool sim_control_serial (SERHANDLE port, t_bool connect);
extern SERHANDLE sim_open_serial (char *name, TMLN *lp, t_stat *status);
extern t_stat sim_config_serial (SERHANDLE port, const char *config);
extern t_stat sim_control_serial (SERHANDLE port, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits);
extern int32 sim_read_serial (SERHANDLE port, char *buffer, int32 count, char *brk);
extern int32 sim_write_serial (SERHANDLE port, char *buffer, int32 count);
extern void sim_close_serial (SERHANDLE port);

View file

@ -23,6 +23,8 @@
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
15-Oct-12 MP Added definitions needed to detect possible tcp
connect failures
25-Sep-12 MP Reworked for RFC3493 interfaces supporting IPv6 and IPv4
22-Jun-10 RMS Fixed types in sim_accept_conn (from Mark Pizzolato)
19-Nov-05 RMS Added conditional for OpenBSD (from Federico G. Schwindt)
@ -538,7 +540,11 @@ if (err != 0)
#if defined(AF_INET6)
load_ws2 ();
#endif /* endif AF_INET6 */
#endif /* endif Win32 */
#else /* Use native addrinfo APIs */
p_getaddrinfo = (void *)getaddrinfo;
p_getnameinfo = (void *)getnameinfo;
p_freeaddrinfo = (void *)freeaddrinfo;
#endif /* endif _WIN32 */
#if defined (SIGPIPE)
signal (SIGPIPE, SIG_IGN); /* no pipe signals */
#endif
@ -771,7 +777,10 @@ if (rbytes == SOCKET_ERROR) {
err = WSAGetLastError ();
if (err == WSAEWOULDBLOCK) /* no data */
return 0;
printf ("Sockets: read error %d\n", err);
if ((err != WSAETIMEDOUT) && /* expected errors after a connect failure */
(err != WSAEHOSTUNREACH) &&
(err != WSAECONNREFUSED))
printf ("Sockets: read error %d\n", err);
return -1;
}
return rbytes;

View file

@ -23,6 +23,8 @@
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
15-Oct-12 MP Added definitions needed to detect possible tcp
connect failures
25-Sep-12 MP Reworked for RFC3493 interfaces supporting IPv6 and IPv4
04-Jun-08 RMS Addes sim_create_sock, for IBM 1130
14-Apr-05 RMS Added WSAEINPROGRESS (from Tim Riker)
@ -54,6 +56,9 @@
#define SOCKET int32
#define WSAEWOULDBLOCK EWOULDBLOCK
#define WSAEINPROGRESS EINPROGRESS
#define WSAETIMEDOUT ETIMEDOUT
#define WSAECONNREFUSED ECONNREFUSED
#define WSAEHOSTUNREACH EHOSTUNREACH
#define INVALID_SOCKET ((SOCKET)-1)
#define SOCKET_ERROR -1
#include <sys/types.h> /* for fcntl, getpid */

View file

@ -79,8 +79,10 @@
#include "sim_defs.h"
#include <ctype.h>
t_bool sim_idle_enab = FALSE; /* global flag */
volatile t_bool sim_idle_wait = FALSE; /* global flag */
t_bool sim_idle_enab = FALSE; /* global flag */
volatile t_bool sim_idle_wait = FALSE; /* global flag */
static int32 sim_calb_tmr = -1; /* the system calibrated timer */
static uint32 sim_idle_rate_ms = 0;
static uint32 sim_os_sleep_min_ms = 0;
@ -866,3 +868,97 @@ switch (sim_throt_state) {
sim_activate (uptr, sim_throt_wait); /* reschedule */
return SCPE_OK;
}
/* Instruction Execution rate. */
/* returns a double since it is mostly used in double expressions and
to avoid overflow if/when strange timing delays might produce unexpected results */
double sim_timer_inst_per_sec (void)
{
double inst_per_sec = SIM_INITIAL_IPS;
if (sim_calb_tmr == -1)
return inst_per_sec;
inst_per_sec = ((double)rtc_currd[sim_calb_tmr])*rtc_hz[sim_calb_tmr];
if (0 == inst_per_sec)
inst_per_sec = SIM_INITIAL_IPS;
return inst_per_sec;
}
t_stat sim_timer_activate_after (UNIT *uptr, int32 usec_delay)
{
int32 inst_delay;
double inst_per_sec;
AIO_VALIDATE;
if (sim_is_active_bool (uptr)) /* already active? */
return SCPE_OK;
inst_per_sec = sim_timer_inst_per_sec ();
inst_delay = (int32)((inst_per_sec*usec_delay)/1000000.0);
#if defined(SIM_ASYNCH_IO) && defined(SIM_ASYNCH_CLOCKS)
if ((sim_calb_tmr == -1) || /* if No timer initialized
(inst_delay < rtc_currd[sim_calb_tmr]) || /* or sooner than next clock tick? */
(rtc_elapsed[sim_calb_tmr] < sim_idle_stable) || /* or not idle stable yet */
(!(sim_asynch_enabled && sim_asynch_timer))) { /* or asynch disabled */
sim_debug (DBG_TIM, &sim_timer_dev, "sim_timer_activate_after() - activating %s after %d instructions\n",
sim_uname(uptr), inst_delay);
return _sim_activate (uptr, inst_delay); /* queue it now */
}
if (1) {
struct timespec now;
double d_now;
clock_gettime (CLOCK_REALTIME, &now);
d_now = _timespec_to_double (&now);
/* Determine if this is a clock tick like invocation
or an ocaisional measured device delay */
if ((uptr->a_usec_delay == usec_delay) &&
(uptr->a_due_time != 0.0) &&
(1)) {
double d_delay = ((double)usec_delay)/1000000.0;
uptr->a_due_time += d_delay;
if (uptr->a_due_time < (d_now + d_delay*0.1)) { /* Accumulate lost time */
uptr->a_skew += (d_now + d_delay*0.1) - uptr->a_due_time;
uptr->a_due_time = d_now + d_delay/10.0;
if (uptr->a_skew > 30.0) { /* Gap too big? */
uptr->a_usec_delay = usec_delay;
uptr->a_skew = uptr->a_last_fired_time = 0.0;
uptr->a_due_time = d_now + (double)(usec_delay)/1000000.0;
}
if (uptr->a_skew > rtc_clock_skew_max[sim_calb_tmr])
rtc_clock_skew_max[sim_calb_tmr] = uptr->a_skew;
}
else {
if (uptr->a_skew > 0.0) { /* Lost time to make up? */
if (uptr->a_skew > d_delay*0.9) {
uptr->a_skew -= d_delay*0.9;
uptr->a_due_time -= d_delay*0.9;
}
else {
uptr->a_due_time -= uptr->a_skew;
uptr->a_skew = 0.0;
}
}
}
}
else {
uptr->a_usec_delay = usec_delay;
uptr->a_skew = uptr->a_last_fired_time = 0.0;
uptr->a_due_time = d_now + (double)(usec_delay)/1000000.0;
}
uptr->time = usec_delay;
sim_debug (DBG_TIM, &sim_timer_dev, "sim_timer_activate_after() - queue addition %s at %.6f\n",
sim_uname(uptr), uptr->a_due_time);
}
pthread_mutex_lock (&sim_timer_lock);
sim_wallclock_entry = uptr;
pthread_mutex_unlock (&sim_timer_lock);
pthread_cond_signal (&sim_timer_wake); /* wake the timer thread to deal with it */
return SCPE_OK;
#else
return _sim_activate (uptr, inst_delay); /* queue it now */
#endif
}

View file

@ -58,25 +58,28 @@ int clock_gettime(int clock_id, struct timespec *tp);
#endif
#define SIM_NTIMERS 8 /* # timers */
#define SIM_TMAX 500 /* max timer makeup */
#define SIM_NTIMERS 8 /* # timers */
#define SIM_TMAX 500 /* max timer makeup */
#define SIM_IDLE_CAL 10 /* ms to calibrate */
#define SIM_IDLE_MAX 10 /* max granularity idle */
#define SIM_IDLE_STMIN 10 /* min sec for stability */
#define SIM_IDLE_STDFLT 20 /* dft sec for stability */
#define SIM_IDLE_STMAX 600 /* max sec for stability */
#define SIM_INITIAL_IPS 50000 /* uncalibrated assumption */
/* about instructions per second */
#define SIM_THROT_WINIT 1000 /* cycles to skip */
#define SIM_THROT_WST 10000 /* initial wait */
#define SIM_THROT_WMUL 4 /* multiplier */
#define SIM_THROT_WMIN 100 /* min wait */
#define SIM_THROT_MSMIN 10 /* min for measurement */
#define SIM_THROT_NONE 0 /* throttle parameters */
#define SIM_THROT_MCYC 1 /* MegaCycles Per Sec */
#define SIM_THROT_KCYC 2 /* KiloCycles Per Sec */
#define SIM_THROT_PCT 3 /* Max Percent of host CPU */
#define SIM_THROT_SPC 4 /* Specific periodic Delay */
#define SIM_IDLE_CAL 10 /* ms to calibrate */
#define SIM_IDLE_MAX 10 /* max granularity idle */
#define SIM_IDLE_STMIN 10 /* min sec for stability */
#define SIM_IDLE_STDFLT 20 /* dft sec for stability */
#define SIM_IDLE_STMAX 600 /* max sec for stability */
#define SIM_THROT_WINIT 1000 /* cycles to skip */
#define SIM_THROT_WST 10000 /* initial wait */
#define SIM_THROT_WMUL 4 /* multiplier */
#define SIM_THROT_WMIN 100 /* min wait */
#define SIM_THROT_MSMIN 10 /* min for measurement */
#define SIM_THROT_NONE 0 /* throttle parameters */
#define SIM_THROT_MCYC 1 /* MegaCycles Per Sec */
#define SIM_THROT_KCYC 2 /* KiloCycles Per Sec */
#define SIM_THROT_PCT 3 /* Max Percent of host CPU */
#define SIM_THROT_SPC 4 /* Specific periodic Delay */
t_bool sim_timer_init (void);
void sim_timespec_diff (struct timespec *diff, struct timespec *min, struct timespec *sub);
@ -97,5 +100,7 @@ uint32 sim_os_msec (void);
void sim_os_sleep (unsigned int sec);
uint32 sim_os_ms_sleep (unsigned int msec);
uint32 sim_os_ms_sleep_init (void);
t_stat sim_timer_activate_after (UNIT *uptr, int32 usec_delay);
double sim_timer_inst_per_sec (void);
#endif

2021
sim_tmxr.c

File diff suppressed because it is too large Load diff

View file

@ -26,11 +26,11 @@
Based on the original DZ11 simulator by Thord Nilson, as updated by
Arthur Krewat.
10-Oct-12 MP Added extended attach support for serial, per line
listener and outgoing connections
17-Jan-11 MP Added buffered line capabilities
20-Nov-08 RMS Added three new standardized SHOW routines
07-Oct-08 JDB [serial] Added serial port support to TMXR, TMLN,
added tmxr_attach_line, tmxr_detach_line,
tmxr_line_free, tmxr_mux_free
07-Oct-08 JDB Added serial port support to TMXR, TMLN
27-May-08 JDB Added lnorder to TMXR structure,
added tmxr_set_lnorder and tmxr_set_lnorder
14-May-08 JDB Added dptr to TMXR structure
@ -56,8 +56,32 @@
#define TMXR_MAXBUF 256 /* buffer size */
#define TMXR_GUARD 12 /* buffer guard */
#define TMXR_DTR_DROP_TIME 500 /* milliseconds to drop DTR for 'pseudo' modem control */
#define TMXR_CONNECT_POLL_INTERVAL 1000 /* milliseconds between connection polls */
#define TMXR_DBG_XMT 0x10000 /* Debug Transmit Data */
#define TMXR_DBG_RCV 0x20000 /* Debug Received Data */
#define TMXR_DBG_ASY 0x40000 /* Debug Asynchronous Activities */
#define TMXR_DBG_TRC 0x80000 /* Debug trace routine calls */
/* Modem Control Bits */
#define TMXR_MDM_DTR 0x01 /* Data Terminal Ready */
#define TMXR_MDM_RTS 0x02 /* Request To Send */
#define TMXR_MDM_DCD 0x04 /* Data Carrier Detect */
#define TMXR_MDM_RNG 0x08 /* Ring Indicator */
#define TMXR_MDM_CTS 0x10 /* Clear To Send */
#define TMXR_MDM_DSR 0x20 /* Data Set Ready */
#define TMXR_MDM_INCOMING (TMXR_MDM_DCD|TMXR_MDM_RNG|TMXR_MDM_CTS|TMXR_MDM_DSR) /* Settable Modem Bits */
#define TMXR_MDM_OUTGOING (TMXR_MDM_DTR|TMXR_MDM_DTR) /* Settable Modem Bits */
/* Unit flags */
#define TMUF_V_NOASYNCH (UNIT_V_UF + 12) /* Asynch Disabled unit */
#define TMUF_NOASYNCH (1u << TMUF_V_NOASYNCH) /* This flag can be defined */
/* statically in a unit's flag field */
/* This will disable the unit from */
/* supporting asynchronmous mux behaviors */
typedef struct tmln TMLN;
typedef struct tmxr TMXR;
@ -65,11 +89,15 @@ typedef struct tmxr TMXR;
struct tmln {
SOCKET conn; /* line conn */
char *ipad; /* IP address */
SOCKET master; /* line specific master socket */
char *port; /* line specific listening port */
int32 sessions; /* count of tcp connections received */
uint32 cnms; /* conn time */
int32 tsta; /* Telnet state */
int32 rcve; /* rcv enable */
int32 xmte; /* xmt enable */
int32 dstb; /* disable Tlnt bin */
t_bool notelnet; /* raw binary data (no telnet interpretation) */
int32 rxbpr; /* rcv buf remove */
int32 rxbpi; /* rcv buf insert */
int32 rxcnt; /* rcv count */
@ -86,8 +114,12 @@ struct tmln {
char rbr[TMXR_MAXBUF]; /* rcv break */
char *txb; /* xmt buffer */
TMXR *mp; /* back pointer to mux */
char *serconfig; /* line config */
SERHANDLE serport; /* serial port handle */
char *sername; /* serial port name */
SOCKET connecting; /* Outgoing socket while connecting */
char *destination; /* Outgoing destination address:port */
UNIT *uptr; /* input polling unit (default to mp->uptr) */
UNIT *o_uptr; /* output polling unit (default to lp->uptr)*/
};
struct tmxr {
@ -97,14 +129,17 @@ struct tmxr {
TMLN *ldsc; /* line descriptors */
int32 *lnorder; /* line connection order */
DEVICE *dptr; /* multiplexer device */
UNIT *uptr; /* polling unit (connection) */
char logfiletmpl[FILENAME_MAX]; /* template logfile name */
int32 buffered; /* Buffered Line Behavior and Buffer Size Flag */
uint32 pending; /* count of pending serial connections */
int32 sessions; /* count of tcp connections received */
uint32 last_poll_time; /* time of last connection poll */
t_bool notelnet; /* default telnet capability for incoming connections */
t_bool modem_control; /* multiplexer supports modem control behaviors */
};
int32 tmxr_poll_conn (TMXR *mp);
void tmxr_reset_ln (TMLN *lp);
t_stat tmxr_clear_ln (TMXR *mp, TMLN *lp);
t_stat tmxr_reset_ln (TMLN *lp);
int32 tmxr_getc_ln (TMLN *lp);
void tmxr_poll_rx (TMXR *mp);
t_stat tmxr_putc_ln (TMLN *lp, int32 chr);
@ -114,10 +149,12 @@ 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_detach (TMXR *mp, UNIT *uptr);
t_stat tmxr_attach_line (UNIT *uptr, int32 val, char *cptr, void *desc);
t_stat tmxr_detach_line (UNIT *uptr, int32 val, char *cptr, void *desc);
t_bool tmxr_line_free (TMLN *lp);
t_bool tmxr_mux_free (TMXR *mp);
t_stat tmxr_set_modem_control_passthru (TMXR *mp);
t_stat tmxr_set_get_modem_bits (TMLN *lp, int32 bits_to_set, int32 bits_to_clear, int32 *incoming_bits);
t_stat tmxr_set_config_line (TMLN *lp, char *config);
t_stat tmxr_set_line_unit (TMXR *mp, int line, UNIT *uptr_poll);
t_stat tmxr_set_line_output_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);
@ -135,7 +172,16 @@ 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_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
#endif