Adds serial port support to the multiplexer library.
It also modifies the HP 2100 and PDP11 multiplexers to add serial support as demonstrations of the capability that, one day, might be extended to all simulators. I have tested the HP support, but I relied on Holger Veit to test the DEC stuff, so I can't guarantee that it works. I also relied on Holger to test under Linux, so the same caveat applies. The changes needed in the device simulators are relatively small. For example, if you look at the patches for "hp2100_baci.c", you'll note that most of them are documentation changes. The only things of note are: - an expansion of the TMXR initializer - additional code in the "attach" routine to try attaching a serial port if attaching a socket fails - additional code in the "detach" routine for the same reasons The HP MPX device (hp2100_mpx.c) needs a tiny bit of additional support from the ATTACH and DETACH commands. Specifically, SCP was modified to set a flag ("sim_unit_ref") to indicate whether ATTACH MPX or ATTACH MPX0 was done, i.e., to differentiate between a device and a unit attach (recall that SCP treats these as both referring to unit 0). This is needed because the socket attaches (logically) to the device, whereas a serial port attaches to a line. Without this flag, the attach routine cannot differentiate between ATTACH MPX and ATTACH MPX0, as the distinction is lost by the time the VM's attach routine is called. This support isn't needed for the HP MUX device because the socket attaches to a different device than the lines do. MPX also requires a bit more work due to the capability to mix serial and Telnet lines on the same multiplexer (BACI is a single-line terminal device). The attached PDF contains revisions to the "Writing a Simulator for the SIMH System" publication that documents the additions and changes to the multiplexer library for serial port support. User documentation for serial port support currently exists only in the initial comments in "sim_tmxr.c"; I will add the appropriate text to the "SIMH User's Guide" if we decide to add this to the release version.
This commit is contained in:
parent
5f505ccadf
commit
bc36e9dde5
16 changed files with 13799 additions and 334 deletions
|
@ -29,6 +29,8 @@
|
||||||
28-Mar-11 JDB Tidied up signal handling
|
28-Mar-11 JDB Tidied up signal handling
|
||||||
26-Oct-10 JDB Changed I/O signal handler for revised signal model
|
26-Oct-10 JDB Changed I/O signal handler for revised signal model
|
||||||
25-Nov-08 JDB Revised for new multiplexer library SHOW routines
|
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
|
11-Sep-08 JDB Fixed STC,C losing interrupt request on BREAK
|
||||||
07-Sep-08 JDB Fixed IN_LOOPBACK conflict with netinet/in.h
|
07-Sep-08 JDB Fixed IN_LOOPBACK conflict with netinet/in.h
|
||||||
Changed Telnet poll to connect immediately after reset or attach
|
Changed Telnet poll to connect immediately after reset or attach
|
||||||
|
@ -78,13 +80,13 @@
|
||||||
an "external rate" that is equivalent to 9600 baud, as most terminals were
|
an "external rate" that is equivalent to 9600 baud, as most terminals were
|
||||||
set to their maximum speeds.
|
set to their maximum speeds.
|
||||||
|
|
||||||
We support the 12966A connected to an HP terminal emulator via Telnet.
|
We support the 12966A connected to an HP terminal emulator via Telnet or a
|
||||||
Internally, we model the BACI as a terminal multiplexer with one line. The
|
serial port. Internally, we model the BACI as a terminal multiplexer with
|
||||||
simulation is complicated by the half-duplex nature of the card (there is
|
one line. The simulation is complicated by the half-duplex nature of the
|
||||||
only one FIFO, used selectively either for transmission or reception) and the
|
card (there is only one FIFO, used selectively either for transmission or
|
||||||
double-buffered UART (a Western Digital TR1863A), which has holding registers
|
reception) and the double-buffered UART (a Western Digital TR1863A), which
|
||||||
as well as a shift registers for transmission and reception. We model both
|
has holding registers as well as a shift registers for transmission and
|
||||||
sets of device registers.
|
reception. We model both sets of device registers.
|
||||||
|
|
||||||
During an output operation, the first character output to the card passes
|
During an output operation, the first character output to the card passes
|
||||||
through the FIFO and into the transmitter holding register. Subsequent
|
through the FIFO and into the transmitter holding register. Subsequent
|
||||||
|
@ -113,12 +115,12 @@
|
||||||
as an "optimized (fast) timing" option. Optimization makes three
|
as an "optimized (fast) timing" option. Optimization makes three
|
||||||
improvements:
|
improvements:
|
||||||
|
|
||||||
1. On output, characters in the FIFO are emptied into the Telnet buffer as a
|
1. On output, characters in the FIFO are emptied into the line buffer as a
|
||||||
block, rather than one character per service call, and on input, all of
|
block, rather than one character per service call, and on input, all of
|
||||||
the characters available in the Telnet buffer are loaded into the FIFO as
|
the characters available in the line buffer are loaded into the FIFO as a
|
||||||
a block.
|
block.
|
||||||
|
|
||||||
2. The ENQ/ACK handshake is done locally, without involving the Telnet
|
2. The ENQ/ACK handshake is done locally, without involving the terminal
|
||||||
client.
|
client.
|
||||||
|
|
||||||
3. Input occurring during an output operation is delayed until the second or
|
3. Input occurring during an output operation is delayed until the second or
|
||||||
|
@ -318,7 +320,7 @@
|
||||||
/* Unit references */
|
/* Unit references */
|
||||||
|
|
||||||
#define baci_term baci_unit[0] /* terminal I/O unit */
|
#define baci_term baci_unit[0] /* terminal I/O unit */
|
||||||
#define baci_poll baci_unit[1] /* Telnet polling unit */
|
#define baci_poll baci_unit[1] /* line polling unit */
|
||||||
|
|
||||||
|
|
||||||
/* BACI state variables */
|
/* BACI state variables */
|
||||||
|
@ -391,11 +393,11 @@ t_stat baci_detach (UNIT *uptr);
|
||||||
baci_deb BACI debug list
|
baci_deb BACI debug list
|
||||||
baci_dev BACI device descriptor
|
baci_dev BACI device descriptor
|
||||||
|
|
||||||
Two units are used: one to handle character I/O via the Telnet library, and
|
Two units are used: one to handle character I/O via the multiplexer library,
|
||||||
another to poll for connections and input. The character I/O service routine
|
and another to poll for connections and input. The character I/O service
|
||||||
runs only when there are characters to read or write. It operates at the
|
routine runs only when there are characters to read or write. It operates at
|
||||||
approximate baud rate of the terminal (in CPU instructions per second) in
|
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
|
order to be compatible with the OS drivers. The line poll must run
|
||||||
continuously, but it can operate much more slowly, as the only requirement is
|
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
|
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
|
with CPU idling, it is co-scheduled with the master poll timer, which uses a
|
||||||
|
@ -405,13 +407,13 @@ t_stat baci_detach (UNIT *uptr);
|
||||||
DEVICE baci_dev;
|
DEVICE baci_dev;
|
||||||
|
|
||||||
TMLN baci_ldsc = { 0 }; /* line descriptor */
|
TMLN baci_ldsc = { 0 }; /* line descriptor */
|
||||||
TMXR baci_desc = { 1, 0, 0, &baci_ldsc }; /* device descriptor */
|
TMXR baci_desc = { 1, 0, 0, &baci_ldsc, NULL, &baci_dev }; /* device descriptor */
|
||||||
|
|
||||||
DIB baci_dib = { &baci_io, BACI, 0 };
|
DIB baci_dib = { &baci_io, BACI, 0 };
|
||||||
|
|
||||||
UNIT baci_unit[] = {
|
UNIT baci_unit[] = {
|
||||||
{ UDATA (&baci_term_svc, UNIT_ATTABLE | UNIT_FASTTIME, 0) }, /* terminal I/O unit */
|
{ UDATA (&baci_term_svc, UNIT_ATTABLE | UNIT_FASTTIME, 0) }, /* terminal I/O unit */
|
||||||
{ UDATA (&baci_poll_svc, UNIT_DIS, POLL_FIRST) } /* Telnet poll unit */
|
{ UDATA (&baci_poll_svc, UNIT_DIS, POLL_FIRST) } /* line poll unit */
|
||||||
};
|
};
|
||||||
|
|
||||||
REG baci_reg[] = {
|
REG baci_reg[] = {
|
||||||
|
@ -769,7 +771,7 @@ return stat_data;
|
||||||
The terminal service routine is used to transmit and receive characters.
|
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
|
In terminal mode, it is started when a character is ready for output or when
|
||||||
the Telnet poll routine determines that there are characters ready for input
|
the line poll routine determines that there are characters ready for input
|
||||||
and stopped when there are no more characters to output or input. When the
|
and stopped when there are no more characters to output or input. When the
|
||||||
terminal is quiescent, this routine does not run.
|
terminal is quiescent, this routine does not run.
|
||||||
|
|
||||||
|
@ -809,11 +811,11 @@ return stat_data;
|
||||||
first character after an ENQ is not an ACK.
|
first character after an ENQ is not an ACK.
|
||||||
|
|
||||||
Finally, fast timing enables buffer combining. For output, all characters
|
Finally, fast timing enables buffer combining. For output, all characters
|
||||||
present in the FIFO are unloaded into the Telnet buffer before initiating a
|
present in the FIFO are unloaded into the line buffer before initiating a
|
||||||
packet send. For input, all characters present in the Telnet buffer are
|
packet send. For input, all characters present in the line buffer are loaded
|
||||||
loaded into the FIFO. This reduces network traffic and decreases simulator
|
into the FIFO. This reduces network traffic and decreases simulator overhead
|
||||||
overhead (there is only one service routine entry per block, rather than one
|
(there is only one service routine entry per block, rather than one per
|
||||||
per character).
|
character).
|
||||||
|
|
||||||
In fast output mode, it is imperative that not less than 1500 instructions
|
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
|
elapse between the first character load to the FIFO and the initiation of
|
||||||
|
@ -976,12 +978,11 @@ return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* BACI Telnet poll service.
|
/* BACI line poll service.
|
||||||
|
|
||||||
This service routine is used to poll for Telnet connections and incoming
|
This service routine is used to poll for connections and incoming characters.
|
||||||
characters. If characters are available, the terminal I/O service routine is
|
If characters are available, the terminal I/O service routine is scheduled.
|
||||||
scheduled. It starts when the socket is attached and stops when the socket
|
It starts when the line is attached and stops when the line is detached.
|
||||||
is detached.
|
|
||||||
|
|
||||||
As there is only one line, we only poll for a new connection when the line is
|
As there is only one line, we only poll for a new connection when the line is
|
||||||
disconnected.
|
disconnected.
|
||||||
|
@ -1028,42 +1029,57 @@ baci_term.wait = service_time (baci_icw); /* set terminal I/O time
|
||||||
|
|
||||||
if (baci_term.flags & UNIT_ATT) { /* device attached? */
|
if (baci_term.flags & UNIT_ATT) { /* device attached? */
|
||||||
baci_poll.wait = POLL_FIRST; /* set up poll */
|
baci_poll.wait = POLL_FIRST; /* set up poll */
|
||||||
sim_activate (&baci_poll, baci_poll.wait); /* start Telnet poll immediately */
|
sim_activate (&baci_poll, baci_poll.wait); /* start line poll immediately */
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
sim_cancel (&baci_poll); /* else stop Telnet poll */
|
sim_cancel (&baci_poll); /* else stop line poll */
|
||||||
|
|
||||||
return SCPE_OK;
|
return SCPE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Attach controller */
|
/* Attach line */
|
||||||
|
|
||||||
t_stat baci_attach (UNIT *uptr, char *cptr)
|
t_stat baci_attach (UNIT *uptr, char *cptr)
|
||||||
{
|
{
|
||||||
t_stat status = SCPE_OK;
|
t_stat status = SCPE_OK;
|
||||||
|
|
||||||
status = tmxr_attach (&baci_desc, uptr, cptr); /* attach to socket */
|
if (uptr->flags & UNIT_DIS) /* unit disabled? */
|
||||||
|
return SCPE_UDIS; /* report it */
|
||||||
|
|
||||||
if (status == SCPE_OK) {
|
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 */
|
baci_poll.wait = POLL_FIRST; /* set up poll */
|
||||||
sim_activate (&baci_poll, baci_poll.wait); /* start Telnet poll immediately */
|
sim_activate (&baci_poll, baci_poll.wait); /* start line poll immediately */
|
||||||
}
|
}
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Detach controller */
|
|
||||||
|
/* Detach line */
|
||||||
|
|
||||||
t_stat baci_detach (UNIT *uptr)
|
t_stat baci_detach (UNIT *uptr)
|
||||||
{
|
{
|
||||||
t_stat status;
|
t_stat status;
|
||||||
|
|
||||||
status = tmxr_detach (&baci_desc, uptr); /* detach socket */
|
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 */
|
||||||
|
|
||||||
baci_ldsc.rcve = 0; /* disable line reception */
|
baci_ldsc.rcve = 0; /* disable line reception */
|
||||||
sim_cancel (&baci_poll); /* stop Telnet poll */
|
sim_cancel (&baci_poll); /* stop line poll */
|
||||||
return status;
|
return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Local routines */
|
/* Local routines */
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -29,6 +29,7 @@
|
||||||
27-Oct-10 JDB Revised I/O signal enum values for concurrent signals
|
27-Oct-10 JDB Revised I/O signal enum values for concurrent signals
|
||||||
Revised I/O macros for new signal handling
|
Revised I/O macros for new signal handling
|
||||||
09-Oct-10 JDB Added DA and DC device select code assignments
|
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
|
07-Sep-08 JDB Added POLL_FIRST to indicate immediate connection attempt
|
||||||
15-Jul-08 JDB Rearranged declarations with hp2100_cpu.h
|
15-Jul-08 JDB Rearranged declarations with hp2100_cpu.h
|
||||||
26-Jun-08 JDB Rewrote device I/O to model backplane signals
|
26-Jun-08 JDB Rewrote device I/O to model backplane signals
|
||||||
|
@ -447,6 +448,7 @@ extern FILE *sim_deb;
|
||||||
extern FILE *sim_log;
|
extern FILE *sim_log;
|
||||||
extern int32 sim_step;
|
extern int32 sim_step;
|
||||||
extern int32 sim_switches;
|
extern int32 sim_switches;
|
||||||
|
extern UNITREF sim_unit_ref;
|
||||||
|
|
||||||
/* CPU functions */
|
/* CPU functions */
|
||||||
|
|
||||||
|
|
|
@ -29,7 +29,9 @@
|
||||||
28-Mar-11 JDB Tidied up signal handling
|
28-Mar-11 JDB Tidied up signal handling
|
||||||
26-Oct-10 JDB Changed I/O signal handler for revised signal model
|
26-Oct-10 JDB Changed I/O signal handler for revised signal model
|
||||||
25-Nov-08 JDB Revised for new multiplexer library SHOW routines
|
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
|
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
|
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
|
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
|
10-Aug-08 JDB Added REG_FIT to register variables < 32-bit size
|
||||||
|
@ -57,8 +59,8 @@
|
||||||
character editing, echoing, ENQ/ACK handshaking, and read terminator
|
character editing, echoing, ENQ/ACK handshaking, and read terminator
|
||||||
detection, substantially reducing the load on the CPU over the earlier 12920
|
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.
|
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 to a
|
Under simulation, it connects with HP terminal emulators via Telnet or serial
|
||||||
user-specified port.
|
ports.
|
||||||
|
|
||||||
The single interface card contained a Z80 CPU, DMA controller, CTC, four
|
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
|
two-channel SIO UARTs, 16K of RAM, 8K of ROM, and I/O backplane latches and
|
||||||
|
@ -199,7 +201,7 @@
|
||||||
#define MPX_CNTLS 2 /* number of control units */
|
#define MPX_CNTLS 2 /* number of control units */
|
||||||
|
|
||||||
#define mpx_cntl (mpx_unit [MPX_PORTS + 0]) /* controller unit */
|
#define mpx_cntl (mpx_unit [MPX_PORTS + 0]) /* controller unit */
|
||||||
#define mpx_poll (mpx_unit [MPX_PORTS + 1]) /* Telnet polling unit */
|
#define mpx_poll (mpx_unit [MPX_PORTS + 1]) /* polling unit */
|
||||||
|
|
||||||
|
|
||||||
/* Character constants */
|
/* Character constants */
|
||||||
|
@ -611,16 +613,16 @@ t_stat mpx_show_frev (FILE *st, UNIT *uptr, int32 val, void *desc);
|
||||||
mpx_dev MPX device descriptor
|
mpx_dev MPX device descriptor
|
||||||
|
|
||||||
The first eight units correspond to the eight multiplexer line ports. These
|
The first eight units correspond to the eight multiplexer line ports. These
|
||||||
handle character I/O via the Telnet library. A ninth unit acts as the card
|
handle character I/O via the multiplexer library. A ninth unit acts as the
|
||||||
controller, executing commands and transferring data to and from the I/O
|
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
|
buffers. A tenth unit is responsible for polling for connections and line
|
||||||
I/O. It also holds the master socket.
|
I/O. It also holds the master socket for Telnet connections.
|
||||||
|
|
||||||
The character I/O service routines run only when there are characters to read
|
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
|
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.
|
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
|
The controller service routine runs only when a command is executing or a
|
||||||
data transfer to or from the CPU is in progress. The Telnet poll must run
|
data transfer to or from the CPU is in progress. The poll service must run
|
||||||
continuously, but it may operate much more slowly, as the only requirement is
|
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
|
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
|
with CPU idling, it is co-scheduled with the master poll timer, which uses a
|
||||||
|
@ -634,7 +636,7 @@ DEVICE mpx_dev;
|
||||||
|
|
||||||
int32 mpx_order [MPX_PORTS] = { -1 }; /* connection order */
|
int32 mpx_order [MPX_PORTS] = { -1 }; /* connection order */
|
||||||
TMLN mpx_ldsc [MPX_PORTS] = { { 0 } }; /* line descriptors */
|
TMLN mpx_ldsc [MPX_PORTS] = { { 0 } }; /* line descriptors */
|
||||||
TMXR mpx_desc = { MPX_PORTS, 0, 0, mpx_ldsc, mpx_order }; /* device descriptor */
|
TMXR mpx_desc = { MPX_PORTS, 0, 0, mpx_ldsc, mpx_order, &mpx_dev }; /* device descriptor */
|
||||||
|
|
||||||
DIB mpx_dib = { &mpx_io, MPX };
|
DIB mpx_dib = { &mpx_io, MPX };
|
||||||
|
|
||||||
|
@ -648,7 +650,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 6 */
|
||||||
{ UDATA (&mpx_line_svc, UNIT_FASTTIME, 0) }, /* terminal I/O line 7 */
|
{ UDATA (&mpx_line_svc, UNIT_FASTTIME, 0) }, /* terminal I/O line 7 */
|
||||||
{ UDATA (&mpx_cntl_svc, UNIT_DIS, 0) }, /* controller unit */
|
{ UDATA (&mpx_cntl_svc, UNIT_DIS, 0) }, /* controller unit */
|
||||||
{ UDATA (&mpx_poll_svc, UNIT_ATTABLE | UNIT_DIS, POLL_FIRST) } /* Telnet poll unit */
|
{ UDATA (&mpx_poll_svc, UNIT_ATTABLE | UNIT_DIS, POLL_FIRST) } /* line poll unit */
|
||||||
};
|
};
|
||||||
|
|
||||||
REG mpx_reg [] = {
|
REG mpx_reg [] = {
|
||||||
|
@ -1602,21 +1604,20 @@ return SCPE_OK;
|
||||||
/* Multiplexer line service.
|
/* Multiplexer line service.
|
||||||
|
|
||||||
The line service routine is used to transmit and receive characters. It is
|
The line service routine is used to transmit and receive characters. It is
|
||||||
started when a buffer is ready for output or when the Telnet poll routine
|
started when a buffer is ready for output or when the poll service routine
|
||||||
determines that there are characters ready for input, and it is stopped when
|
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,
|
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
|
this routine does not run. Service times are selected to approximate the
|
||||||
baud rate setting of the multiplexer port.
|
baud rate setting of the multiplexer port.
|
||||||
|
|
||||||
"Fast timing" mode enables three optimizations. First, buffered characters
|
"Fast timing" mode enables three optimizations. First, buffered characters
|
||||||
are transferred via Telnet in blocks, rather than a character at a time; this
|
are transferred in blocks, rather than a character at a time; this reduces
|
||||||
reduces network traffic and decreases simulator overhead (there is only one
|
line traffic and decreases simulator overhead (there is only one service
|
||||||
service routine entry per block, rather than one per character). Second,
|
routine entry per block, rather than one per character). Second, ENQ/ACK
|
||||||
ENQ/ACK handshaking is done locally, without involving the Telnet client.
|
handshaking is done locally, without involving the client. Third, when
|
||||||
Third, when editing and echo is enabled, entering BS echoes a backspace, a
|
editing and echo is enabled, entering BS echoes a backspace, a space, and a
|
||||||
space, and a backspace, and entering DEL echoes a backslash, a carriage
|
backspace, and entering DEL echoes a backslash, a carriage return, and a line
|
||||||
return, and a line feed, providing better compatibility with prior RTE
|
feed, providing better compatibility with prior RTE terminal drivers.
|
||||||
terminal drivers.
|
|
||||||
|
|
||||||
Each read and write buffer begins with a reserved header byte that stores
|
Each read and write buffer begins with a reserved header byte that stores
|
||||||
per-buffer information, such as whether handshaking should be suppressed
|
per-buffer information, such as whether handshaking should be suppressed
|
||||||
|
@ -1630,7 +1631,7 @@ return SCPE_OK;
|
||||||
write buffer is freed, and a UI check is made if the controller is idle, in
|
write buffer is freed, and a UI check is made if the controller is idle, in
|
||||||
case a write buffer request is pending.
|
case a write buffer request is pending.
|
||||||
|
|
||||||
For input, the character is retrieved from the Telnet buffer. If a BREAK was
|
For input, the character is retrieved from the line buffer. If a BREAK was
|
||||||
received, break status is set, and the character is discarded (the current
|
received, break status is set, and the character is discarded (the current
|
||||||
multiplexer library implementation always returns a NUL with a BREAK
|
multiplexer library implementation always returns a NUL with a BREAK
|
||||||
indication). If the character is an XOFF, and XON/XOFF pacing is enabled, a
|
indication). If the character is an XOFF, and XON/XOFF pacing is enabled, a
|
||||||
|
@ -1939,11 +1940,11 @@ return SCPE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Telnet poll service.
|
/* Poll service.
|
||||||
|
|
||||||
This service routine is used to poll for Telnet connections and incoming
|
This service routine is used to poll for connections and incoming characters.
|
||||||
characters. It starts when the socket is attached and stops when the socket
|
It is started when the listening socket or a serial line is attached and is
|
||||||
is detached.
|
stopped when the socket and all lines are detached.
|
||||||
|
|
||||||
Each line is then checked for a pending ENQ/ACK handshake. If one is
|
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
|
pending, the ACK counter is incremented, and if it times out, another ENQ is
|
||||||
|
@ -2007,10 +2008,10 @@ return SCPE_OK;
|
||||||
1. Under simulation, we also clear the input buffer register, even though
|
1. Under simulation, we also clear the input buffer register, even though
|
||||||
the hardware doesn't.
|
the hardware doesn't.
|
||||||
|
|
||||||
2. We set up the first poll for Telnet connections to occur "immediately"
|
2. We set up the first poll for connections to occur "immediately" upon
|
||||||
upon execution, so that clients will be connected before execution
|
execution, so that clients will be connected before execution begins.
|
||||||
begins. Otherwise, a fast program may access the multiplexer before the
|
Otherwise, a fast program may access the multiplexer before the poll
|
||||||
poll service routine activates.
|
service routine activates.
|
||||||
|
|
||||||
3. We must set the "emptying_flags" and "filling_flags" values here, because
|
3. We must set the "emptying_flags" and "filling_flags" values here, because
|
||||||
they cannot be initialized statically, even though the values are
|
they cannot be initialized statically, even though the values are
|
||||||
|
@ -2030,47 +2031,70 @@ IOPRESET (&mpx_dib); /* PRESET device (does n
|
||||||
|
|
||||||
mpx_ibuf = 0; /* clear input buffer */
|
mpx_ibuf = 0; /* clear input buffer */
|
||||||
|
|
||||||
if (mpx_poll.flags & UNIT_ATT) { /* network attached? */
|
if (tmxr_mux_free (&mpx_desc)) /* any lines attached? */
|
||||||
|
sim_cancel (&mpx_poll); /* no, so stop poll */
|
||||||
|
else { /* attached or listening */
|
||||||
mpx_poll.wait = POLL_FIRST; /* set up poll */
|
mpx_poll.wait = POLL_FIRST; /* set up poll */
|
||||||
sim_activate (&mpx_poll, mpx_poll.wait); /* start Telnet poll immediately */
|
sim_activate (&mpx_poll, mpx_poll.wait); /* start poll immediately */
|
||||||
}
|
}
|
||||||
else
|
|
||||||
sim_cancel (&mpx_poll); /* else stop Telnet poll */
|
|
||||||
|
|
||||||
return SCPE_OK;
|
return SCPE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Attach the multiplexer to a Telnet port.
|
/* Attach the multiplexer or a line.
|
||||||
|
|
||||||
We are called by the ATTACH MPX <port> command to attach the multiplexer to
|
We are called by the ATTACH MPX <port> command to attach the multiplexer to
|
||||||
the listening port indicated by <port>. Logically, it is the multiplexer
|
the listening port indicated by <port> and by ATTACH MPX<n> <ser> to attach
|
||||||
device that is attached; however, SIMH only allows units to be attached.
|
line <n> to serial port <ser>. Logically, it is the multiplexer device that
|
||||||
This makes sense for devices such as tape drives, where the attached media is
|
is attached; however, SIMH only allows units to be attached. This makes
|
||||||
a property of a specific drive. In our case, though, the listening port is a
|
sense for devices such as tape drives, where the attached media is a property
|
||||||
property of the multiplexer card, not of any given serial line. As ATTACH
|
of a specific drive. In our case, though, the listening port is a property
|
||||||
MPX is equivalent to ATTACH MPX0, the port would, by default, be attached to
|
of the multiplexer card, not of any given serial line.
|
||||||
the first serial line and be reported there in a SHOW MPX command.
|
|
||||||
|
|
||||||
To preserve the logical picture, we attach the port to the Telnet poll unit,
|
To preserve the logical picture, we attach the listening port to the poll
|
||||||
which is normally disabled to inhibit its display. Attaching to a disabled
|
unit (unit 9), which is normally disabled to inhibit its display. Serial
|
||||||
unit is not allowed, so we first enable the unit, then attach it, then
|
ports are attached to line units 0-7 normally. Attachment is reported by the
|
||||||
disable it again. Attachment is reported by the "mpx_status" routine below.
|
"mpx_status" routine below.
|
||||||
|
|
||||||
The Telnet poll service routine is synchronized with the other input polling
|
The connection poll service routine is synchronized with the other input
|
||||||
devices in the simulator to facilitate idling.
|
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.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
t_stat mpx_attach (UNIT *uptr, char *cptr)
|
t_stat mpx_attach (UNIT *uptr, char *cptr)
|
||||||
{
|
{
|
||||||
t_stat status = SCPE_OK;
|
t_stat status = SCPE_OK;
|
||||||
|
|
||||||
if (uptr != mpx_unit) /* not unit 0? */
|
if ((uptr == &mpx_cntl) || /* attaching controller? */
|
||||||
return SCPE_NOATT; /* can't attach */
|
(uptr == &mpx_poll) && !(sim_switches & SIM_SW_REST)) /* or poll unit directly? */
|
||||||
|
return SCPE_NOATT; /* disallow */
|
||||||
|
|
||||||
mpx_poll.flags = mpx_poll.flags & ~UNIT_DIS; /* enable unit */
|
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 */
|
status = tmxr_attach (&mpx_desc, &mpx_poll, cptr); /* attach to socket */
|
||||||
mpx_poll.flags = mpx_poll.flags | UNIT_DIS; /* disable unit */
|
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 */
|
||||||
|
|
||||||
if (status == SCPE_OK) {
|
if (status == SCPE_OK) {
|
||||||
mpx_poll.wait = POLL_FIRST; /* set up poll */
|
mpx_poll.wait = POLL_FIRST; /* set up poll */
|
||||||
|
@ -2080,33 +2104,56 @@ return status;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Detach the multiplexer.
|
/* Detach the multiplexer or a line.
|
||||||
|
|
||||||
Normally, we are called by the DETACH MPX command, which is equivalent to
|
We are called by the DETACH MPX command to detach the listening port and all
|
||||||
DETACH MPX0. However, we may be called with other units in two cases.
|
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.
|
||||||
|
|
||||||
A DETACH ALL command will call us for unit 9 (the poll unit) if it is
|
Implementation notes:
|
||||||
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
|
1. Because DETACH MPX will pass unit 0, we check the "sim_unit_ref" global
|
||||||
UNIT_ATTABLE), as well as for unit 9 if it is attached. In both cases, it is
|
to see if MPX or MPX0 was specified in the command.
|
||||||
imperative that we return SCPE_OK, otherwise any remaining device detaches
|
|
||||||
will not be performed.
|
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.
|
||||||
*/
|
*/
|
||||||
|
|
||||||
t_stat mpx_detach (UNIT *uptr)
|
t_stat mpx_detach (UNIT *uptr)
|
||||||
{
|
{
|
||||||
t_stat status = SCPE_OK;
|
uint32 ln;
|
||||||
int32 i;
|
t_stat status;
|
||||||
|
t_bool mux_free = TRUE;
|
||||||
|
|
||||||
if ((uptr == mpx_unit) || (uptr == &mpx_poll)) { /* base unit or poll unit? */
|
if (uptr == &mpx_cntl) /* detaching controller directly? */
|
||||||
|
return SCPE_NOATT; /* disallow */
|
||||||
|
|
||||||
|
if (sim_unit_ref == ref_dev || uptr == &mpx_poll) /* device detach or detach all request? */
|
||||||
status = tmxr_detach (&mpx_desc, &mpx_poll); /* detach socket */
|
status = tmxr_detach (&mpx_desc, &mpx_poll); /* detach socket */
|
||||||
|
|
||||||
for (i = 0; i < MPX_PORTS; i++) {
|
else /* line detach request */
|
||||||
mpx_ldsc [i].rcve = 0; /* disable line reception */
|
status = tmxr_detach_line (uptr, 0, NULL, &mpx_desc); /* detach line */
|
||||||
sim_cancel (&mpx_unit [i]); /* cancel any scheduled I/O */
|
|
||||||
|
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 */
|
||||||
}
|
}
|
||||||
|
|
||||||
sim_cancel (&mpx_poll); /* stop Telnet poll */
|
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 */
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
@ -2167,7 +2214,7 @@ return SCPE_OK;
|
||||||
/* Local routines */
|
/* Local routines */
|
||||||
|
|
||||||
|
|
||||||
/* Poll for new Telnet connections */
|
/* Poll for new connections */
|
||||||
|
|
||||||
static void poll_connection (void)
|
static void poll_connection (void)
|
||||||
{
|
{
|
||||||
|
|
|
@ -29,6 +29,8 @@
|
||||||
28-Mar-11 JDB Tidied up signal handling
|
28-Mar-11 JDB Tidied up signal handling
|
||||||
26-Oct-10 JDB Changed I/O signal handler for revised signal model
|
26-Oct-10 JDB Changed I/O signal handler for revised signal model
|
||||||
25-Nov-08 JDB Revised for new multiplexer library SHOW routines
|
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)
|
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
|
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
|
07-Sep-08 JDB Changed Telnet poll to connect immediately after reset or attach
|
||||||
|
@ -327,6 +329,7 @@ t_stat muxo_svc (UNIT *uptr);
|
||||||
t_stat muxc_reset (DEVICE *dptr);
|
t_stat muxc_reset (DEVICE *dptr);
|
||||||
t_stat mux_attach (UNIT *uptr, char *cptr);
|
t_stat mux_attach (UNIT *uptr, char *cptr);
|
||||||
t_stat mux_detach (UNIT *uptr);
|
t_stat mux_detach (UNIT *uptr);
|
||||||
|
t_stat muxl_detach (UNIT *uptr);
|
||||||
t_stat mux_setdiag (UNIT *uptr, int32 val, char *cptr, void *desc);
|
t_stat mux_setdiag (UNIT *uptr, int32 val, char *cptr, void *desc);
|
||||||
|
|
||||||
|
|
||||||
|
@ -430,8 +433,8 @@ DEVICE muxl_dev = {
|
||||||
NULL, /* deposit routine */
|
NULL, /* deposit routine */
|
||||||
&muxc_reset, /* reset routine */
|
&muxc_reset, /* reset routine */
|
||||||
NULL, /* boot routine */
|
NULL, /* boot routine */
|
||||||
NULL, /* attach routine */
|
&mux_attach, /* attach routine */
|
||||||
NULL, /* detach routine */
|
&muxl_detach, /* detach routine */
|
||||||
&muxl_dib, /* device information block */
|
&muxl_dib, /* device information block */
|
||||||
DEV_DISABLE, /* device flags */
|
DEV_DISABLE, /* device flags */
|
||||||
0, /* debug control flags */
|
0, /* debug control flags */
|
||||||
|
@ -458,7 +461,7 @@ DEVICE muxu_dev;
|
||||||
|
|
||||||
int32 mux_order [MUX_LINES] = { -1 }; /* connection order */
|
int32 mux_order [MUX_LINES] = { -1 }; /* connection order */
|
||||||
TMLN mux_ldsc [MUX_LINES] = { { 0 } }; /* line descriptors */
|
TMLN mux_ldsc [MUX_LINES] = { { 0 } }; /* line descriptors */
|
||||||
TMXR mux_desc = { MUX_LINES, 0, 0, mux_ldsc, mux_order }; /* device descriptor */
|
TMXR mux_desc = { MUX_LINES, 0, 0, mux_ldsc, mux_order, &muxu_dev }; /* device descriptor */
|
||||||
|
|
||||||
UNIT muxu_unit = { UDATA (&muxi_svc, UNIT_ATTABLE, 0), POLL_FIRST };
|
UNIT muxu_unit = { UDATA (&muxi_svc, UNIT_ATTABLE, 0), POLL_FIRST };
|
||||||
|
|
||||||
|
@ -920,7 +923,7 @@ while (working_set) {
|
||||||
(old & DTR) && /* DTR drop? */
|
(old & DTR) && /* DTR drop? */
|
||||||
!(muxc_ota[ln] & DTR)) {
|
!(muxc_ota[ln] & DTR)) {
|
||||||
tmxr_linemsg (&mux_ldsc[ln], "\r\nLine hangup\r\n");
|
tmxr_linemsg (&mux_ldsc[ln], "\r\nLine hangup\r\n");
|
||||||
tmxr_reset_ln (&mux_ldsc[ln]); /* reset line */
|
tmxr_clear_ln (&mux_desc, &mux_ldsc[ln]); /* disconnect line */
|
||||||
muxc_lia[ln] = 0; /* dataset off */
|
muxc_lia[ln] = 0; /* dataset off */
|
||||||
}
|
}
|
||||||
} /* end update */
|
} /* end update */
|
||||||
|
@ -1305,12 +1308,12 @@ IOPRESET (dibptr); /* PRESET device (does n
|
||||||
|
|
||||||
muxc_chan = muxc_scan = 0; /* init modem scan */
|
muxc_chan = muxc_scan = 0; /* init modem scan */
|
||||||
|
|
||||||
if (muxu_unit.flags & UNIT_ATT) { /* master att? */
|
if (tmxr_mux_free (&mux_desc)) /* any lines attached? */
|
||||||
|
sim_cancel (&muxu_unit); /* no, so stop poll */
|
||||||
|
else { /* attached or listening */
|
||||||
muxu_unit.wait = POLL_FIRST; /* set up poll */
|
muxu_unit.wait = POLL_FIRST; /* set up poll */
|
||||||
sim_activate (&muxu_unit, muxu_unit.wait); /* start Telnet poll immediately */
|
sim_activate (&muxu_unit, muxu_unit.wait); /* start poll immediately */
|
||||||
}
|
}
|
||||||
else
|
|
||||||
sim_cancel (&muxu_unit); /* else stop */
|
|
||||||
|
|
||||||
for (i = 0; i < MUX_LINES; i++)
|
for (i = 0; i < MUX_LINES; i++)
|
||||||
mux_reset_ln (i); /* reset lines 0-15 */
|
mux_reset_ln (i); /* reset lines 0-15 */
|
||||||
|
@ -1322,7 +1325,7 @@ return SCPE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
/* Attach master unit */
|
/* Attach master unit or line */
|
||||||
|
|
||||||
t_stat mux_attach (UNIT *uptr, char *cptr)
|
t_stat mux_attach (UNIT *uptr, char *cptr)
|
||||||
{
|
{
|
||||||
|
@ -1331,11 +1334,14 @@ t_stat status = SCPE_OK;
|
||||||
if (muxu_unit.flags & UNIT_DIAG) /* diag mode? */
|
if (muxu_unit.flags & UNIT_DIAG) /* diag mode? */
|
||||||
return SCPE_NOFNC; /* command not allowed */
|
return SCPE_NOFNC; /* command not allowed */
|
||||||
|
|
||||||
status = tmxr_attach (&mux_desc, uptr, cptr); /* attach */
|
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 */
|
||||||
|
|
||||||
if (status == SCPE_OK) {
|
if (status == SCPE_OK) { /* attach successful? */
|
||||||
muxu_unit.wait = POLL_FIRST; /* set up poll */
|
muxu_unit.wait = POLL_FIRST; /* set up poll */
|
||||||
sim_activate (&muxu_unit, muxu_unit.wait); /* start Telnet poll immediately */
|
sim_activate (&muxu_unit, muxu_unit.wait); /* start poll immediately */
|
||||||
}
|
}
|
||||||
|
|
||||||
return status;
|
return status;
|
||||||
|
@ -1346,13 +1352,45 @@ return status;
|
||||||
|
|
||||||
t_stat mux_detach (UNIT *uptr)
|
t_stat mux_detach (UNIT *uptr)
|
||||||
{
|
{
|
||||||
int32 i;
|
uint32 ln;
|
||||||
t_stat r;
|
t_stat status;
|
||||||
|
t_bool free = TRUE;
|
||||||
|
|
||||||
r = tmxr_detach (&mux_desc, uptr); /* detach */
|
status = tmxr_detach (&mux_desc, uptr); /* detach unit */
|
||||||
for (i = 0; i < MUX_LINES; i++) mux_ldsc[i].rcve = 0; /* disable rcv */
|
|
||||||
|
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 */
|
sim_cancel (uptr); /* stop poll */
|
||||||
return r;
|
}
|
||||||
|
|
||||||
|
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;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ -1367,7 +1405,7 @@ return r;
|
||||||
for normal character transfers, which is undesirable.
|
for normal character transfers, which is undesirable.
|
||||||
|
|
||||||
Therefore, to enable diagnostic mode, we must force a disconnect of the
|
Therefore, to enable diagnostic mode, we must force a disconnect of the
|
||||||
master socket and any connected Telnet lines, which clears the connection
|
master socket and all Telnet and serial lines, which clears the connection
|
||||||
flags on all lines. Then we set the "transmission enabled" flags on all
|
flags on all lines. Then we set the "transmission enabled" flags on all
|
||||||
lines to enable output character processing for the diagnostic. (Normally,
|
lines to enable output character processing for the diagnostic. (Normally,
|
||||||
all of the flags are set when the multiplexer is first attached. Until then,
|
all of the flags are set when the multiplexer is first attached. Until then,
|
||||||
|
@ -1380,9 +1418,12 @@ t_stat mux_setdiag (UNIT *uptr, int32 val, char *cptr, void *desc)
|
||||||
int32 ln;
|
int32 ln;
|
||||||
|
|
||||||
if (val) { /* set diag? */
|
if (val) { /* set diag? */
|
||||||
mux_detach (uptr); /* detach lines */
|
mux_detach (uptr); /* detach Telnet lines */
|
||||||
for (ln = 0; ln < MUX_LINES; ln++) /* enable transmission */
|
|
||||||
mux_ldsc[ln].xmte = 1; /* on all 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 */
|
||||||
|
}
|
||||||
}
|
}
|
||||||
else { /* set term */
|
else { /* set term */
|
||||||
for (ln = 0; ln < MUX_LINES; ln++) /* clear connections */
|
for (ln = 0; ln < MUX_LINES; ln++) /* clear connections */
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
dci,dco DC11 terminal input/output
|
dci,dco DC11 terminal input/output
|
||||||
|
|
||||||
17-Aug-2011 RMS Added AUTOCONFIGURE modifier
|
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
|
19-Nov-2008 RMS Revised for common TMXR show routines
|
||||||
Revised to autoconfigure vectors
|
Revised to autoconfigure vectors
|
||||||
|
|
||||||
|
@ -130,6 +131,7 @@ t_stat dci_svc (UNIT *uptr);
|
||||||
t_stat dco_svc (UNIT *uptr);
|
t_stat dco_svc (UNIT *uptr);
|
||||||
t_stat dcx_attach (UNIT *uptr, char *cptr);
|
t_stat dcx_attach (UNIT *uptr, char *cptr);
|
||||||
t_stat dcx_detach (UNIT *uptr);
|
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);
|
t_stat dcx_set_lines (UNIT *uptr, int32 val, char *cptr, void *desc);
|
||||||
void dcx_enbdis (int32 dis);
|
void dcx_enbdis (int32 dis);
|
||||||
void dci_clr_int (int32 ln);
|
void dci_clr_int (int32 ln);
|
||||||
|
@ -251,7 +253,7 @@ DEVICE dco_dev = {
|
||||||
"DCO", dco_unit, dco_reg, dco_mod,
|
"DCO", dco_unit, dco_reg, dco_mod,
|
||||||
DCX_LINES, 10, 31, 1, 8, 8,
|
DCX_LINES, 10, 31, 1, 8, 8,
|
||||||
NULL, NULL, &dcx_reset,
|
NULL, NULL, &dcx_reset,
|
||||||
NULL, NULL, NULL,
|
NULL, &dcx_attach, &dcl_detach,
|
||||||
NULL, DEV_UBUS | DEV_DISABLE | DEV_DIS
|
NULL, DEV_UBUS | DEV_DISABLE | DEV_DIS
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -365,8 +367,6 @@ t_stat dci_svc (UNIT *uptr)
|
||||||
{
|
{
|
||||||
int32 ln, c, temp;
|
int32 ln, c, temp;
|
||||||
|
|
||||||
if ((uptr->flags & UNIT_ATT) == 0) /* attached? */
|
|
||||||
return SCPE_OK;
|
|
||||||
sim_activate (uptr, tmxr_poll); /* continue poll */
|
sim_activate (uptr, tmxr_poll); /* continue poll */
|
||||||
ln = tmxr_poll_conn (&dcx_desc); /* look for connect */
|
ln = tmxr_poll_conn (&dcx_desc); /* look for connect */
|
||||||
if (ln >= 0) { /* got one? */
|
if (ln >= 0) { /* got one? */
|
||||||
|
@ -509,9 +509,12 @@ t_stat dcx_reset (DEVICE *dptr)
|
||||||
int32 ln;
|
int32 ln;
|
||||||
|
|
||||||
dcx_enbdis (dptr->flags & DEV_DIS); /* sync enables */
|
dcx_enbdis (dptr->flags & DEV_DIS); /* sync enables */
|
||||||
sim_cancel (&dci_unit); /* assume stop */
|
//
|
||||||
if (dci_unit.flags & UNIT_ATT) /* if attached, */
|
if (tmxr_mux_free (&dcx_desc)) /* any lines attached? */
|
||||||
sim_activate (&dci_unit, tmxr_poll); /* activate */
|
sim_cancel (&dci_unit); /* no, so stop poll */
|
||||||
|
else /* attached or listening */
|
||||||
|
sim_activate (&dci_unit, tmxr_poll); /* start poll immediately */
|
||||||
|
//
|
||||||
for (ln = 0; ln < DCX_LINES; ln++) /* for all lines */
|
for (ln = 0; ln < DCX_LINES; ln++) /* for all lines */
|
||||||
dcx_reset_ln (ln);
|
dcx_reset_ln (ln);
|
||||||
return auto_config (dci_dev.name, dcx_desc.lines); /* auto config */
|
return auto_config (dci_dev.name, dcx_desc.lines); /* auto config */
|
||||||
|
@ -531,16 +534,22 @@ dco_clr_int (ln);
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
/* Attach master unit */
|
/* Attach master unit or line */
|
||||||
|
|
||||||
t_stat dcx_attach (UNIT *uptr, char *cptr)
|
t_stat dcx_attach (UNIT *uptr, char *cptr)
|
||||||
{
|
{
|
||||||
t_stat r;
|
t_stat r;
|
||||||
|
|
||||||
r = tmxr_attach (&dcx_desc, uptr, cptr); /* attach */
|
//
|
||||||
|
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 */
|
||||||
|
//
|
||||||
|
|
||||||
if (r != SCPE_OK) /* error? */
|
if (r != SCPE_OK) /* error? */
|
||||||
return r;
|
return r;
|
||||||
sim_activate (uptr, tmxr_poll); /* start poll */
|
sim_activate (&dci_unit, tmxr_poll); /* start poll */
|
||||||
return SCPE_OK;
|
return SCPE_OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -550,14 +559,48 @@ t_stat dcx_detach (UNIT *uptr)
|
||||||
{
|
{
|
||||||
int32 i;
|
int32 i;
|
||||||
t_stat r;
|
t_stat r;
|
||||||
|
t_bool free = TRUE;
|
||||||
|
|
||||||
r = tmxr_detach (&dcx_desc, uptr); /* detach */
|
r = tmxr_detach (&dcx_desc, uptr); /* detach */
|
||||||
for (i = 0; i < DCX_LINES; i++) /* all lines, */
|
|
||||||
dcx_ldsc[i].rcve = 0; /* disable rcv */
|
//
|
||||||
|
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 */
|
sim_cancel (uptr); /* stop poll */
|
||||||
|
}
|
||||||
|
//
|
||||||
|
|
||||||
return r;
|
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 */
|
/* Enable/disable device */
|
||||||
|
|
||||||
void dcx_enbdis (int32 dis)
|
void dcx_enbdis (int32 dis)
|
||||||
|
|
|
@ -26,6 +26,7 @@
|
||||||
dz DZ11 terminal multiplexor
|
dz DZ11 terminal multiplexor
|
||||||
|
|
||||||
29-Dec-08 RMS Added MTAB_NC to SET LOG command (Walter Mueller)
|
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
|
19-Nov-08 RMS Revised for common TMXR show routines
|
||||||
18-Jun-07 RMS Added UNIT_IDLE flag
|
18-Jun-07 RMS Added UNIT_IDLE flag
|
||||||
29-Oct-06 RMS Synced poll and clock
|
29-Oct-06 RMS Synced poll and clock
|
||||||
|
@ -231,8 +232,12 @@ MTAB dz_mod[] = {
|
||||||
{ TT_MODE, TT_MODE_7B, "7b", "7B", NULL },
|
{ TT_MODE, TT_MODE_7B, "7b", "7B", NULL },
|
||||||
{ TT_MODE, TT_MODE_8B, "8b", "8B", NULL },
|
{ TT_MODE, TT_MODE_8B, "8b", "8B", NULL },
|
||||||
{ TT_MODE, TT_MODE_7P, "7p", "7P", 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",
|
{ MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT",
|
||||||
&tmxr_dscln, NULL, &dz_desc },
|
&tmxr_detach_line, NULL, &dz_desc },
|
||||||
|
//
|
||||||
{ UNIT_ATT, UNIT_ATT, "summary", NULL,
|
{ UNIT_ATT, UNIT_ATT, "summary", NULL,
|
||||||
NULL, &tmxr_show_summ, (void *) &dz_desc },
|
NULL, &tmxr_show_summ, (void *) &dz_desc },
|
||||||
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL,
|
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL,
|
||||||
|
|
|
@ -34,6 +34,7 @@
|
||||||
of lines available to be 8, 16, 24, or 32.
|
of lines available to be 8, 16, 24, or 32.
|
||||||
Fixed performance issue avoiding redundant polling
|
Fixed performance issue avoiding redundant polling
|
||||||
03-Jan-10 JAD Eliminate gcc warnings
|
03-Jan-10 JAD Eliminate gcc warnings
|
||||||
|
24-Nov-08 JDB Removed tmxr_send_buffered_data declaration (now in sim_tmxr.h)
|
||||||
19-Nov-08 RMS Revised for common TMXR show routines
|
19-Nov-08 RMS Revised for common TMXR show routines
|
||||||
18-Jun-07 RMS Added UNIT_IDLE flag
|
18-Jun-07 RMS Added UNIT_IDLE flag
|
||||||
29-Oct-06 RMS Synced poll and clock
|
29-Oct-06 RMS Synced poll and clock
|
||||||
|
@ -324,8 +325,6 @@ static t_stat vh_set_log (UNIT *uptr, int32 val, char *cptr, void *desc);
|
||||||
static t_stat vh_set_nolog (UNIT *uptr, int32 val, char *cptr, void *desc);
|
static t_stat vh_set_nolog (UNIT *uptr, int32 val, char *cptr, void *desc);
|
||||||
static t_stat vh_show_log (FILE *st, UNIT *uptr, int32 val, void *desc);
|
static t_stat vh_show_log (FILE *st, UNIT *uptr, int32 val, void *desc);
|
||||||
|
|
||||||
int32 tmxr_send_buffered_data (TMLN *lp);
|
|
||||||
|
|
||||||
/* SIMH I/O Structures */
|
/* SIMH I/O Structures */
|
||||||
|
|
||||||
static DIB vh_dib = {
|
static DIB vh_dib = {
|
||||||
|
|
|
@ -189,7 +189,8 @@ SIMH_LIB = $(LIB_DIR)SIMH-$(ARCH).OLB
|
||||||
SIMH_SOURCE = $(SIMH_DIR)SIM_CONSOLE.C,$(SIMH_DIR)SIM_SOCK.C,\
|
SIMH_SOURCE = $(SIMH_DIR)SIM_CONSOLE.C,$(SIMH_DIR)SIM_SOCK.C,\
|
||||||
$(SIMH_DIR)SIM_TMXR.C,$(SIMH_DIR)SIM_ETHER.C,\
|
$(SIMH_DIR)SIM_TMXR.C,$(SIMH_DIR)SIM_ETHER.C,\
|
||||||
$(SIMH_DIR)SIM_TAPE.C,$(SIMH_DIR)SIM_FIO.C,\
|
$(SIMH_DIR)SIM_TAPE.C,$(SIMH_DIR)SIM_FIO.C,\
|
||||||
$(SIMH_DIR)SIM_TIMER.C,$(SIMH_DIR)SIM_DISK.C
|
$(SIMH_DIR)SIM_TIMER.C,$(SIMH_DIR)SIM_DISK.C,\
|
||||||
|
$(SIMH_DIR)SIM_SERIAL.C
|
||||||
SIMH_MAIN = SCP.C
|
SIMH_MAIN = SCP.C
|
||||||
.IFDEF ALPHA_OR_IA64
|
.IFDEF ALPHA_OR_IA64
|
||||||
SIMH_LIB64 = $(LIB_DIR)SIMH64-$(ARCH).OLB
|
SIMH_LIB64 = $(LIB_DIR)SIMH64-$(ARCH).OLB
|
||||||
|
|
11515
doc/simh_381_serial.pdf
Normal file
11515
doc/simh_381_serial.pdf
Normal file
File diff suppressed because it is too large
Load diff
2
makefile
2
makefile
|
@ -373,7 +373,7 @@ LDFLAGS = $(OS_LDFLAGS) $(NETWORK_LDFLAGS) $(LDFLAGS_O)
|
||||||
#
|
#
|
||||||
BIN = BIN/
|
BIN = BIN/
|
||||||
SIM = scp.c sim_console.c sim_fio.c sim_timer.c sim_sock.c \
|
SIM = scp.c sim_console.c sim_fio.c sim_timer.c sim_sock.c \
|
||||||
sim_tmxr.c sim_ether.c sim_tape.c sim_disk.c
|
sim_tmxr.c sim_ether.c sim_tape.c sim_disk.c sim_serial.c
|
||||||
|
|
||||||
|
|
||||||
#
|
#
|
||||||
|
|
10
scp.c
10
scp.c
|
@ -57,6 +57,7 @@
|
||||||
08-Feb-09 RMS Fixed warnings in help printouts
|
08-Feb-09 RMS Fixed warnings in help printouts
|
||||||
29-Dec-08 RMS Fixed implementation of MTAB_NC
|
29-Dec-08 RMS Fixed implementation of MTAB_NC
|
||||||
24-Nov-08 RMS Revised RESTORE unit logic for consistency
|
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
|
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
|
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
|
25-Jul-08 JDB DO cmd missing params now default to null string
|
||||||
|
@ -414,6 +415,7 @@ t_stat do_cmd_label (int32 flag, char *cptr, char *label);
|
||||||
|
|
||||||
/* Global data */
|
/* Global data */
|
||||||
|
|
||||||
|
UNITREF sim_unit_ref;
|
||||||
DEVICE *sim_dflt_dev = NULL;
|
DEVICE *sim_dflt_dev = NULL;
|
||||||
UNIT *sim_clock_queue = NULL;
|
UNIT *sim_clock_queue = NULL;
|
||||||
int32 sim_interval = 0;
|
int32 sim_interval = 0;
|
||||||
|
@ -2817,6 +2819,7 @@ DEVICE *dptr;
|
||||||
UNIT *uptr;
|
UNIT *uptr;
|
||||||
t_stat r;
|
t_stat r;
|
||||||
|
|
||||||
|
sim_unit_ref = ref_unit_all; /* we will reference all units */
|
||||||
if ((start < 0) || (start > 1))
|
if ((start < 0) || (start > 1))
|
||||||
return SCPE_IERR;
|
return SCPE_IERR;
|
||||||
for (i = start; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */
|
for (i = start; (dptr = sim_devices[i]) != NULL; i++) { /* loop thru dev */
|
||||||
|
@ -3134,6 +3137,8 @@ t_bool force_restore = sim_switches & SWMASK ('F');
|
||||||
#define READ_I(xx) if (sim_fread (&xx, sizeof (xx), 1, rfile) == 0) \
|
#define READ_I(xx) if (sim_fread (&xx, sizeof (xx), 1, rfile) == 0) \
|
||||||
return SCPE_IOERR;
|
return SCPE_IOERR;
|
||||||
|
|
||||||
|
sim_unit_ref = ref_unit_all; /* we will reference all units */
|
||||||
|
|
||||||
fstat (fileno (rfile), &rstat);
|
fstat (fileno (rfile), &rstat);
|
||||||
READ_S (buf); /* [V2.5+] read version */
|
READ_S (buf); /* [V2.5+] read version */
|
||||||
v35 = v32 = FALSE;
|
v35 = v32 = FALSE;
|
||||||
|
@ -4579,6 +4584,9 @@ return NULL;
|
||||||
Outputs:
|
Outputs:
|
||||||
result = pointer to device (null if no dev)
|
result = pointer to device (null if no dev)
|
||||||
*iptr = pointer to unit (null if nx unit)
|
*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)
|
DEVICE *find_unit (char *cptr, UNIT **uptr)
|
||||||
|
@ -4588,11 +4596,13 @@ char *nptr, *tptr;
|
||||||
t_stat r;
|
t_stat r;
|
||||||
DEVICE *dptr;
|
DEVICE *dptr;
|
||||||
|
|
||||||
|
sim_unit_ref = ref_unit; /* assume unit reference */
|
||||||
if (uptr == NULL) /* arg error? */
|
if (uptr == NULL) /* arg error? */
|
||||||
return NULL;
|
return NULL;
|
||||||
if (dptr = find_dev (cptr)) { /* exact match? */
|
if (dptr = find_dev (cptr)) { /* exact match? */
|
||||||
if (qdisable (dptr)) /* disabled? */
|
if (qdisable (dptr)) /* disabled? */
|
||||||
return NULL;
|
return NULL;
|
||||||
|
sim_unit_ref = ref_dev; /* flag device reference */
|
||||||
*uptr = dptr->units; /* unit 0 */
|
*uptr = dptr->units; /* unit 0 */
|
||||||
return dptr;
|
return dptr;
|
||||||
}
|
}
|
||||||
|
|
7
scp.h
7
scp.h
|
@ -1,6 +1,6 @@
|
||||||
/* scp.h: simulator control program headers
|
/* scp.h: simulator control program headers
|
||||||
|
|
||||||
Copyright (c) 1993-2008, Robert M Supnik
|
Copyright (c) 1993-2009, Robert M Supnik
|
||||||
|
|
||||||
Permission is hereby granted, free of charge, to any person obtaining a
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
copy of this software and associated documentation files (the "Software"),
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
@ -24,6 +24,7 @@
|
||||||
in this Software without prior written authorization from Robert M Supnik.
|
in this Software without prior written authorization from Robert M Supnik.
|
||||||
|
|
||||||
05-Dec-10 MP Added macro invocation of sim_debug
|
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
|
09-Aug-06 JDB Added assign_device and deassign_device
|
||||||
14-Jul-06 RMS Added sim_activate_abs
|
14-Jul-06 RMS Added sim_activate_abs
|
||||||
06-Jan-06 RMS Added fprint_stopped_gen
|
06-Jan-06 RMS Added fprint_stopped_gen
|
||||||
|
@ -52,6 +53,10 @@
|
||||||
#define CMD_OPT_SCH 004 /* search */
|
#define CMD_OPT_SCH 004 /* search */
|
||||||
#define CMD_OPT_DFT 010 /* defaults */
|
#define CMD_OPT_DFT 010 /* defaults */
|
||||||
|
|
||||||
|
/* unit references */
|
||||||
|
|
||||||
|
typedef enum { ref_dev, ref_unit, ref_unit_all } UNITREF;
|
||||||
|
|
||||||
/* Command processors */
|
/* Command processors */
|
||||||
|
|
||||||
t_stat reset_cmd (int32 flag, char *ptr);
|
t_stat reset_cmd (int32 flag, char *ptr);
|
||||||
|
|
814
sim_serial.c
Normal file
814
sim_serial.c
Normal file
|
@ -0,0 +1,814 @@
|
||||||
|
/* sim_serial.c: OS-dependent serial port routines
|
||||||
|
|
||||||
|
Copyright (c) 2008, J. David Bryan
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of the author shall not be
|
||||||
|
used in advertising or otherwise to promote the sale, use or other dealings
|
||||||
|
in this Software without prior written authorization from the author.
|
||||||
|
|
||||||
|
The author gratefully acknowledges the assistance of Holger Veit with the
|
||||||
|
UNIX-specific code and testing.
|
||||||
|
|
||||||
|
07-Oct-08 JDB [serial] Created file
|
||||||
|
|
||||||
|
|
||||||
|
This module provides OS-dependent routines to access serial ports on the host
|
||||||
|
machine. The terminal multiplexer library uses these routines to provide
|
||||||
|
serial connections to simulated terminal interfaces.
|
||||||
|
|
||||||
|
Currently, the module supports Windows and UNIX. Use on other systems
|
||||||
|
returns error codes indicating that the functions failed, inhibiting serial
|
||||||
|
port support in SIMH.
|
||||||
|
|
||||||
|
The following routines are provided:
|
||||||
|
|
||||||
|
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_read_serial read from a serial port
|
||||||
|
sim_write_serial write to a serial port
|
||||||
|
sim_close_serial close a serial port
|
||||||
|
|
||||||
|
|
||||||
|
The calling sequences are as follows:
|
||||||
|
|
||||||
|
|
||||||
|
SERHANDLE sim_open_serial (char *name)
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
The serial port referenced by the OS-dependent "name" is opened. If the open
|
||||||
|
is successful, and "name" refers to a serial port on the host system, then a
|
||||||
|
handle to the port is returned. If not, then the value INVALID_HANDLE is
|
||||||
|
returned.
|
||||||
|
|
||||||
|
|
||||||
|
t_stat sim_config_serial (SERHANDLE port, SERCONFIG 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
|
||||||
|
"config" field value is unsupported by the host system, or if the combination
|
||||||
|
of values (e.g., baud rate and number of stop bits) is unsupported, SCPE_ARG
|
||||||
|
is returned. If the configuration is successful, SCPE_OK is returned.
|
||||||
|
|
||||||
|
|
||||||
|
t_bool sim_control_serial (SERHANDLE port, t_bool connect)
|
||||||
|
----------------------------------------------------------
|
||||||
|
|
||||||
|
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.
|
||||||
|
|
||||||
|
|
||||||
|
int32 sim_read_serial (SERHANDLE port, char *buffer, int32 count, char *brk)
|
||||||
|
----------------------------------------------------------------------------
|
||||||
|
|
||||||
|
A non-blocking read is issued for the serial port indicated by "port" to get
|
||||||
|
at most "count" bytes into the string "buffer". If a serial line break was
|
||||||
|
detected during the read, the variable pointed to by "brk" is set to 1. If
|
||||||
|
the read is successful, the actual number of characters read is returned. If
|
||||||
|
no characters were available, then the value 0 is returned. If an error
|
||||||
|
occurs, then the value -1 is returned.
|
||||||
|
|
||||||
|
|
||||||
|
int32 sim_write_serial (SERHANDLE port, char *buffer, int32 count)
|
||||||
|
------------------------------------------------------------------
|
||||||
|
|
||||||
|
A write is issued to the serial port indicated by "port" to put "count"
|
||||||
|
characters from "buffer". If the write is successful, the actual number of
|
||||||
|
characters written is returned. If an error occurs, then the value -1 is
|
||||||
|
returned.
|
||||||
|
|
||||||
|
|
||||||
|
void sim_close_serial (SERHANDLE port)
|
||||||
|
--------------------------------------
|
||||||
|
|
||||||
|
The serial port indicated by "port" is closed.
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#include "sim_defs.h"
|
||||||
|
#include "sim_serial.h"
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Generic error message handler.
|
||||||
|
|
||||||
|
This routine should be called for unexpected errors. Some error returns may
|
||||||
|
be expected, e.g., a "file not found" error from an "open" routine. These
|
||||||
|
should return appropriate status codes to the caller, allowing SCP to print
|
||||||
|
an error message if desired, rather than printing this generic error message.
|
||||||
|
*/
|
||||||
|
|
||||||
|
static void sim_error_serial (char *routine, int error)
|
||||||
|
{
|
||||||
|
fprintf (stderr, "Serial: %s fails with error %d\n", routine, error);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Windows serial implementation */
|
||||||
|
|
||||||
|
|
||||||
|
#if defined (_WIN32)
|
||||||
|
|
||||||
|
|
||||||
|
/* Open a serial port.
|
||||||
|
|
||||||
|
The serial port designated by "name" is opened, and the handle to the port is
|
||||||
|
returned. If an error occurs, INVALID_HANDLE is returned instead. After
|
||||||
|
opening, the port is configured with the default communication parameters
|
||||||
|
established by the system, and the timeouts are set for immediate return on a
|
||||||
|
read request to enable polling.
|
||||||
|
|
||||||
|
Implementation notes:
|
||||||
|
|
||||||
|
1. We call "GetDefaultCommConfig" to obtain the default communication
|
||||||
|
parameters for the specified port. If the name does not refer to a
|
||||||
|
communications port (serial or parallel), the function fails.
|
||||||
|
|
||||||
|
2. There is no way to limit "CreateFile" just to serial ports, so we must
|
||||||
|
check after the port is opened. The "GetCommState" routine will return
|
||||||
|
an error if the handle does not refer to a serial port.
|
||||||
|
|
||||||
|
3. Calling "GetDefaultCommConfig" for a serial port returns a structure
|
||||||
|
containing a DCB. This contains the default parameters. However, some
|
||||||
|
of the DCB fields are not set correctly, so we cannot use this directly
|
||||||
|
in a call to "SetCommState". Instead, we must copy the fields of
|
||||||
|
interest to a DCB retrieved from a call to "GetCommState".
|
||||||
|
*/
|
||||||
|
|
||||||
|
SERHANDLE sim_open_serial (char *name)
|
||||||
|
{
|
||||||
|
SERHANDLE port;
|
||||||
|
DCB dcb;
|
||||||
|
COMMCONFIG commdefault;
|
||||||
|
DWORD error;
|
||||||
|
DWORD commsize = sizeof (commdefault);
|
||||||
|
COMMTIMEOUTS cto;
|
||||||
|
|
||||||
|
if (!GetDefaultCommConfig (name, &commdefault, &commsize)) { /* get default comm parameters */
|
||||||
|
error = GetLastError (); /* function failed; get error */
|
||||||
|
|
||||||
|
if (error != ERROR_INVALID_PARAMETER) /* not a communications port name? */
|
||||||
|
sim_error_serial ("GetDefaultCommConfig", (int) error); /* no, so report unexpected error */
|
||||||
|
|
||||||
|
return INVALID_HANDLE; /* indicate bad port name */
|
||||||
|
}
|
||||||
|
|
||||||
|
port = CreateFile (name, GENERIC_READ | GENERIC_WRITE, /* open the port */
|
||||||
|
0, NULL, OPEN_EXISTING, 0, 0);
|
||||||
|
|
||||||
|
if (port == INVALID_HANDLE_VALUE) { /* open failed? */
|
||||||
|
error = GetLastError (); /* get error code */
|
||||||
|
|
||||||
|
if ((error != ERROR_FILE_NOT_FOUND) && /* bad filename? */
|
||||||
|
(error != ERROR_ACCESS_DENIED)) /* already open? */
|
||||||
|
sim_error_serial ("CreateFile", (int) error); /* no, so report unexpected error */
|
||||||
|
|
||||||
|
return INVALID_HANDLE; /* indicate bad port name */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!GetCommState (port, &dcb)) { /* get the current comm parameters */
|
||||||
|
error = GetLastError (); /* function failed; get error */
|
||||||
|
|
||||||
|
if (error != ERROR_INVALID_PARAMETER) /* not a serial port name? */
|
||||||
|
sim_error_serial ("GetCommState", (int) error); /* no, so report unexpected error */
|
||||||
|
|
||||||
|
CloseHandle (port); /* close the port */
|
||||||
|
return INVALID_HANDLE; /* and indicate bad port name */
|
||||||
|
}
|
||||||
|
|
||||||
|
dcb.BaudRate = commdefault.dcb.BaudRate; /* copy default parameters of interest */
|
||||||
|
dcb.Parity = commdefault.dcb.Parity;
|
||||||
|
dcb.ByteSize = commdefault.dcb.ByteSize;
|
||||||
|
dcb.StopBits = commdefault.dcb.StopBits;
|
||||||
|
dcb.fOutX = commdefault.dcb.fOutX;
|
||||||
|
dcb.fInX = commdefault.dcb.fInX;
|
||||||
|
|
||||||
|
dcb.fDtrControl = DTR_CONTROL_DISABLE; /* disable DTR initially until poll connects */
|
||||||
|
|
||||||
|
if (!SetCommState (port, &dcb)) { /* configure the port with default parameters */
|
||||||
|
sim_error_serial ("SetCommState", /* function failed; report unexpected error */
|
||||||
|
(int) GetLastError ());
|
||||||
|
CloseHandle (port); /* close port */
|
||||||
|
return INVALID_HANDLE; /* and indicate failure to caller */
|
||||||
|
}
|
||||||
|
|
||||||
|
cto.ReadIntervalTimeout = MAXDWORD; /* set port to return immediately on read */
|
||||||
|
cto.ReadTotalTimeoutMultiplier = 0; /* i.e., to enable polling */
|
||||||
|
cto.ReadTotalTimeoutConstant = 0;
|
||||||
|
cto.WriteTotalTimeoutMultiplier = 0;
|
||||||
|
cto.WriteTotalTimeoutConstant = 0;
|
||||||
|
|
||||||
|
if (!SetCommTimeouts (port, &cto)) { /* configure port timeouts */
|
||||||
|
sim_error_serial ("SetCommTimeouts", /* function failed; report unexpected error */
|
||||||
|
(int) GetLastError ());
|
||||||
|
CloseHandle (port); /* close port */
|
||||||
|
return INVALID_HANDLE; /* and indicate failure to caller */
|
||||||
|
}
|
||||||
|
|
||||||
|
return port; /* return port handle on success */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Configure a serial port.
|
||||||
|
|
||||||
|
Port parameters are configured as specified in the "config" structure. If
|
||||||
|
"config" contains an invalid configuration value, or if the host system
|
||||||
|
rejects the configuration (e.g., by requesting an unsupported combination of
|
||||||
|
character size and stop bits), SCPE_ARG is returned to the caller. If an
|
||||||
|
unexpected error occurs, SCPE_IOERR is returned. If the configuration
|
||||||
|
succeeds, SCPE_OK is returned.
|
||||||
|
|
||||||
|
Implementation notes:
|
||||||
|
|
||||||
|
1. We do not enable input parity checking, as the multiplexer library has no
|
||||||
|
way of communicating parity errors back to the target simulator.
|
||||||
|
|
||||||
|
2. A zero value for the "stopbits" field of the "config" structure implies
|
||||||
|
1.5 stop bits.
|
||||||
|
*/
|
||||||
|
|
||||||
|
t_stat sim_config_serial (SERHANDLE port, SERCONFIG config)
|
||||||
|
{
|
||||||
|
static const struct {
|
||||||
|
char parity;
|
||||||
|
BYTE parity_code;
|
||||||
|
} parity_map [] =
|
||||||
|
{ { 'E', EVENPARITY }, { 'M', MARKPARITY }, { 'N', NOPARITY },
|
||||||
|
{ 'O', ODDPARITY }, { 'S', SPACEPARITY } };
|
||||||
|
|
||||||
|
static const int32 parity_count = sizeof (parity_map) / sizeof (parity_map [0]);
|
||||||
|
|
||||||
|
DCB dcb;
|
||||||
|
DWORD error;
|
||||||
|
int32 i;
|
||||||
|
|
||||||
|
if (!GetCommState (port, &dcb)) { /* get the current comm parameters */
|
||||||
|
sim_error_serial ("GetCommState", /* function failed; report unexpected error */
|
||||||
|
(int) GetLastError ());
|
||||||
|
return SCPE_IOERR; /* return failure status */
|
||||||
|
}
|
||||||
|
|
||||||
|
dcb.BaudRate = config.baudrate; /* assign baud rate */
|
||||||
|
|
||||||
|
if (config.charsize >= 5 && config.charsize <= 8) /* character size OK? */
|
||||||
|
dcb.ByteSize = config.charsize; /* assign character size */
|
||||||
|
else
|
||||||
|
return SCPE_ARG; /* not a valid size */
|
||||||
|
|
||||||
|
for (i = 0; i < parity_count; i++) /* assign parity */
|
||||||
|
if (config.parity == parity_map [i].parity) { /* match mapping value? */
|
||||||
|
dcb.Parity = parity_map [i].parity_code; /* assign corresponding code */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == parity_count) /* parity assigned? */
|
||||||
|
return SCPE_ARG; /* not a valid parity specifier */
|
||||||
|
|
||||||
|
if (config.stopbits == 1) /* assign stop bits */
|
||||||
|
dcb.StopBits = ONESTOPBIT;
|
||||||
|
else if (config.stopbits == 2)
|
||||||
|
dcb.StopBits = TWOSTOPBITS;
|
||||||
|
else if (config.stopbits == 0) /* 0 implies 1.5 stop bits */
|
||||||
|
dcb.StopBits = ONE5STOPBITS;
|
||||||
|
else
|
||||||
|
return SCPE_ARG; /* not a valid number of stop bits */
|
||||||
|
|
||||||
|
if (!SetCommState (port, &dcb)) { /* set the configuration */
|
||||||
|
error = GetLastError (); /* check for error */
|
||||||
|
|
||||||
|
if (error == ERROR_INVALID_PARAMETER) /* invalid configuration? */
|
||||||
|
return SCPE_ARG; /* report as argument error */
|
||||||
|
|
||||||
|
sim_error_serial ("SetCommState", (int) error); /* function failed; report unexpected error */
|
||||||
|
return SCPE_IOERR; /* return failure status */
|
||||||
|
}
|
||||||
|
|
||||||
|
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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
t_bool sim_control_serial (SERHANDLE port, t_bool connect)
|
||||||
|
{
|
||||||
|
if (!EscapeCommFunction (port, connect ? SETDTR : CLRDTR)) {
|
||||||
|
sim_error_serial ("EscapeCommFunction", (int) GetLastError ());
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Read from a serial port.
|
||||||
|
|
||||||
|
The port is checked for available characters. If any are present, they are
|
||||||
|
copied to the passed buffer, and the count of characters is returned. If no
|
||||||
|
characters are available, 0 is returned. If an error occurs, -1 is returned.
|
||||||
|
If a BREAK is detected on the communications line, the corresponding flag in
|
||||||
|
the "brk" array is set.
|
||||||
|
|
||||||
|
Implementation notes:
|
||||||
|
|
||||||
|
1. The "ClearCommError" function will set the CE_BREAK flag in the returned
|
||||||
|
errors value if a BREAK has occurred. However, we do not know where in
|
||||||
|
the serial stream it happened, as CE_BREAK isn't associated with a
|
||||||
|
specific character. Because the "brk" array does want a flag associated
|
||||||
|
with a specific character, we guess at the proper location by setting
|
||||||
|
the "brk" entry corresponding to the first NUL in the character stream.
|
||||||
|
If no NUL is present, then the "brk" entry associated with the first
|
||||||
|
character is set.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int32 sim_read_serial (SERHANDLE port, char *buffer, int32 count, char *brk)
|
||||||
|
{
|
||||||
|
DWORD read;
|
||||||
|
DWORD commerrors;
|
||||||
|
COMSTAT cs;
|
||||||
|
char *bptr;
|
||||||
|
|
||||||
|
if (!ClearCommError (port, &commerrors, &cs)) { /* get the comm error flags */
|
||||||
|
sim_error_serial ("ClearCommError", /* function failed; report unexpected error */
|
||||||
|
(int) GetLastError ());
|
||||||
|
return -1; /* return failure to caller */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!ReadFile (port, (LPVOID) buffer, /* read any available characters */
|
||||||
|
(DWORD) count, &read, NULL)) {
|
||||||
|
sim_error_serial ("ReadFile", /* function failed; report unexpected error */
|
||||||
|
(int) GetLastError ());
|
||||||
|
return -1; /* return failure to caller */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (commerrors & CE_BREAK) { /* was a BREAK detected? */
|
||||||
|
bptr = (char *) memchr (buffer, 0, read); /* search for the first NUL in the buffer */
|
||||||
|
|
||||||
|
if (bptr) /* was one found? */
|
||||||
|
brk = brk + (bptr - buffer); /* calculate corresponding position */
|
||||||
|
|
||||||
|
*brk = 1; /* set the BREAK flag */
|
||||||
|
}
|
||||||
|
|
||||||
|
return read; /* return the number of characters read */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Write to a serial port.
|
||||||
|
|
||||||
|
"Count" characters are written from "buffer" to the serial port. The actual
|
||||||
|
number of characters written to the port is returned. If an error occurred
|
||||||
|
on writing, -1 is returned.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int32 sim_write_serial (SERHANDLE port, char *buffer, int32 count)
|
||||||
|
{
|
||||||
|
DWORD written;
|
||||||
|
|
||||||
|
if (!WriteFile (port, (LPVOID) buffer, /* write the buffer to the serial port */
|
||||||
|
(DWORD) count, &written, NULL)) {
|
||||||
|
sim_error_serial ("WriteFile", /* function failed; report unexpected error */
|
||||||
|
(int) GetLastError ());
|
||||||
|
return -1; /* return failure to caller */
|
||||||
|
}
|
||||||
|
else
|
||||||
|
return written; /* return number of characters written */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Close a serial port.
|
||||||
|
|
||||||
|
The serial port is closed. Errors are ignored.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void sim_close_serial (SERHANDLE port)
|
||||||
|
{
|
||||||
|
CloseHandle (port); /* close the port */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* UNIX implementation */
|
||||||
|
|
||||||
|
|
||||||
|
#elif defined (__unix__)
|
||||||
|
|
||||||
|
|
||||||
|
/* Open a serial port.
|
||||||
|
|
||||||
|
The serial port designated by "name" is opened, and the handle to the port is
|
||||||
|
returned. If an error occurs, INVALID_HANDLE is returned instead. After
|
||||||
|
opening, the port is configured to "raw" mode.
|
||||||
|
|
||||||
|
Implementation notes:
|
||||||
|
|
||||||
|
1. We use a non-blocking open to allow for polling during reads.
|
||||||
|
|
||||||
|
2. There is no way to limit "open" just to serial ports, so we must check
|
||||||
|
after the port is opened. We do this with a combination of "isatty" and
|
||||||
|
"tcgetattr".
|
||||||
|
|
||||||
|
3. We configure with PARMRK set and IGNBRK and BRKINT cleared. This will
|
||||||
|
mark a communication line BREAK condition in the input stream with the
|
||||||
|
three-character sequence \377 \000 \000. This is detected during
|
||||||
|
reading.
|
||||||
|
*/
|
||||||
|
|
||||||
|
SERHANDLE sim_open_serial (char *name)
|
||||||
|
{
|
||||||
|
static const tcflag_t i_clear = IGNBRK | /* ignore BREAK */
|
||||||
|
BRKINT | /* signal on BREAK */
|
||||||
|
INPCK | /* enable parity checking */
|
||||||
|
ISTRIP | /* strip character to 7 bits */
|
||||||
|
INLCR | /* map NL to CR */
|
||||||
|
IGNCR | /* ignore CR */
|
||||||
|
ICRNL | /* map CR to NL */
|
||||||
|
IXON | /* enable XON/XOFF output control */
|
||||||
|
IXOFF; /* enable XON/XOFF input control */
|
||||||
|
|
||||||
|
static const tcflag_t i_set = PARMRK | /* mark parity errors and line breaks */
|
||||||
|
IGNPAR; /* ignore parity errors */
|
||||||
|
|
||||||
|
static const tcflag_t o_clear = OPOST; /* post-process output */
|
||||||
|
|
||||||
|
static const tcflag_t o_set = 0;
|
||||||
|
|
||||||
|
static const tcflag_t c_clear = HUPCL; /* hang up line on last close */
|
||||||
|
|
||||||
|
static const tcflag_t c_set = CREAD | /* enable receiver */
|
||||||
|
CLOCAL; /* ignore modem status lines */
|
||||||
|
|
||||||
|
static const tcflag_t l_clear = ISIG | /* enable signals */
|
||||||
|
ICANON | /* canonical input */
|
||||||
|
ECHO | /* echo characters */
|
||||||
|
ECHOE | /* echo ERASE as an error correcting backspace */
|
||||||
|
ECHOK | /* echo KILL */
|
||||||
|
ECHONL | /* echo NL */
|
||||||
|
NOFLSH | /* disable flush after interrupt */
|
||||||
|
TOSTOP | /* send SIGTTOU for background output */
|
||||||
|
IEXTEN; /* enable extended functions */
|
||||||
|
|
||||||
|
static const tcflag_t l_set = 0;
|
||||||
|
|
||||||
|
|
||||||
|
SERHANDLE port;
|
||||||
|
struct termios tio;
|
||||||
|
|
||||||
|
port = open (name, O_RDWR | O_NOCTTY | O_NONBLOCK); /* open the port */
|
||||||
|
|
||||||
|
if (port == -1) { /* open failed? */
|
||||||
|
if (errno != ENOENT && errno != EACCES) /* file not found or can't open? */
|
||||||
|
sim_error_serial ("open", errno); /* no, so report unexpected error */
|
||||||
|
|
||||||
|
return INVALID_HANDLE; /* indicate failure to caller */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!isatty (port)) { /* is device a TTY? */
|
||||||
|
close (port); /* no, so close it */
|
||||||
|
return INVALID_HANDLE; /* and return failure to caller */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (tcgetattr (port, &tio)) { /* get the terminal attributes */
|
||||||
|
sim_error_serial ("tcgetattr", errno); /* function failed; report unexpected error */
|
||||||
|
close (port); /* close the port */
|
||||||
|
return INVALID_HANDLE; /* and return failure to caller */
|
||||||
|
}
|
||||||
|
|
||||||
|
// which of these methods is best?
|
||||||
|
|
||||||
|
#if 1
|
||||||
|
|
||||||
|
tio.c_iflag = tio.c_iflag & ~i_clear | i_set; /* configure the serial line for raw mode */
|
||||||
|
tio.c_oflag = tio.c_oflag & ~o_clear | o_set;
|
||||||
|
tio.c_cflag = tio.c_cflag & ~c_clear | c_set;
|
||||||
|
tio.c_lflag = tio.c_lflag & ~l_clear | l_set;
|
||||||
|
|
||||||
|
#elif 0
|
||||||
|
|
||||||
|
tio.c_iflag &= ~(IGNBRK | BRKINT | INPCK | ISTRIP | INLCR | IGNCR | ICRNL | IXON | IXOFF);
|
||||||
|
tio.c_iflag |= PARMRK | IGNPAR;
|
||||||
|
tio.c_oflag &= ~(OPOST);
|
||||||
|
tio.c_cflag &= ~(HUPCL);
|
||||||
|
tio.c_cflag |= CREAD | CLOCAL;
|
||||||
|
tio.c_lflag &= ~(ISIG | ICANON | ECHO | ECHOE | ECHOK | ECHONL | NOFLSH | TOSTOP | IEXTEN);
|
||||||
|
|
||||||
|
#elif 0
|
||||||
|
|
||||||
|
tio.c_iflag = PARMRK | IGNPAR;
|
||||||
|
tio.c_oflag = 0;
|
||||||
|
tio.c_cflag = tio.c_cflag | CLOCAL | CREAD;
|
||||||
|
tio.c_lflag = 0;
|
||||||
|
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (tcsetattr (port, TCSANOW, &tio)) { /* set the terminal attributes */
|
||||||
|
sim_error_serial ("tcsetattr", errno); /* function failed; report unexpected error */
|
||||||
|
close (port); /* close the port */
|
||||||
|
return INVALID_HANDLE; /* and return failure to caller */
|
||||||
|
}
|
||||||
|
|
||||||
|
return port; /* return port fd for success */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Configure a serial port.
|
||||||
|
|
||||||
|
Port parameters are configured as specified in the "config" structure. If
|
||||||
|
"config" contains an invalid configuration value, or if the host system
|
||||||
|
rejects the configuration (e.g., by requesting an unsupported combination of
|
||||||
|
character size and stop bits), SCPE_ARG is returned to the caller. If an
|
||||||
|
unexpected error occurs, SCPE_IOERR is returned. If the configuration
|
||||||
|
succeeds, SCPE_OK is returned.
|
||||||
|
|
||||||
|
Implementation notes:
|
||||||
|
|
||||||
|
1. 1.5 stop bits is not a supported configuration.
|
||||||
|
|
||||||
|
*/
|
||||||
|
|
||||||
|
t_stat sim_config_serial (SERHANDLE port, SERCONFIG config)
|
||||||
|
{
|
||||||
|
struct termios tio;
|
||||||
|
int32 i;
|
||||||
|
|
||||||
|
static const struct {
|
||||||
|
uint32 rate;
|
||||||
|
speed_t rate_code;
|
||||||
|
} baud_map [] =
|
||||||
|
{ { 50, B50 }, { 75, B75 }, { 110, B110 }, { 134, B134 },
|
||||||
|
{ 150, B150 }, { 200, B200 }, { 300, B300 }, { 600, B600 },
|
||||||
|
{ 1200, B1200 }, { 1800, B1800 }, { 2400, B2400 }, { 4800, B4800 },
|
||||||
|
{ 9600, B9600 }, { 19200, B19200 }, { 38400, B38400 }, { 57600, B57600 },
|
||||||
|
{ 115200, B115200 } };
|
||||||
|
|
||||||
|
static const int32 baud_count = sizeof (baud_map) / sizeof (baud_map [0]);
|
||||||
|
|
||||||
|
static const tcflag_t charsize_map [4] = { CS5, CS6, CS7, CS8 };
|
||||||
|
|
||||||
|
|
||||||
|
if (tcgetattr (port, &tio)) { /* get the current configuration */
|
||||||
|
sim_error_serial ("tcgetattr", errno); /* function failed; report unexpected error */
|
||||||
|
return SCPE_IOERR; /* return failure status */
|
||||||
|
}
|
||||||
|
|
||||||
|
for (i = 0; i < baud_count; i++) /* assign baud rate */
|
||||||
|
if (config.baudrate == baud_map [i].rate) { /* match mapping value? */
|
||||||
|
cfsetispeed(&tio, baud_map [i].rate_code); /* set input rate */
|
||||||
|
cfsetospeed(&tio, baud_map [i].rate_code); /* set output rate */
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (i == baud_count) /* baud rate assigned? */
|
||||||
|
return SCPE_ARG; /* invalid rate specified */
|
||||||
|
|
||||||
|
if (config.charsize >= 5 && config.charsize <= 8) /* character size OK? */
|
||||||
|
tio.c_cflag = tio.c_cflag & ~CSIZE | /* replace character size code */
|
||||||
|
charsize_map [config.charsize - 5];
|
||||||
|
else
|
||||||
|
return SCPE_ARG; /* not a valid size */
|
||||||
|
|
||||||
|
switch (config.parity) { /* assign parity */
|
||||||
|
case 'E':
|
||||||
|
tio.c_cflag = tio.c_cflag & ~PARODD | PARENB; /* set for even parity */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'N':
|
||||||
|
tio.c_cflag = tio.c_cflag & ~PARENB; /* set for no parity */
|
||||||
|
break;
|
||||||
|
|
||||||
|
case 'O':
|
||||||
|
tio.c_cflag = tio.c_cflag | PARODD | PARENB; /* set for odd parity */
|
||||||
|
break;
|
||||||
|
|
||||||
|
default:
|
||||||
|
return SCPE_ARG; /* not a valid parity specifier */
|
||||||
|
}
|
||||||
|
|
||||||
|
if (config.stopbits == 1) /* one stop bit? */
|
||||||
|
tio.c_cflag = tio.c_cflag & ~CSTOPB; /* clear two-bits flag */
|
||||||
|
else if (config.stopbits == 2) /* two stop bits? */
|
||||||
|
tio.c_cflag = tio.c_cflag | CSTOPB; /* set two-bits flag */
|
||||||
|
else /* some other number? */
|
||||||
|
return SCPE_ARG; /* not a valid number of stop bits */
|
||||||
|
|
||||||
|
if (tcsetattr (port, TCSAFLUSH, &tio)) { /* set the new configuration */
|
||||||
|
sim_error_serial ("tcsetattr", errno); /* function failed; report unexpected error */
|
||||||
|
return SCPE_IERR; /* return failure status */
|
||||||
|
}
|
||||||
|
|
||||||
|
return SCPE_OK; /* configuration set successfully */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* 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.
|
||||||
|
*/
|
||||||
|
|
||||||
|
t_bool sim_control_serial (SERHANDLE port, t_bool connect)
|
||||||
|
{
|
||||||
|
int request;
|
||||||
|
static const int dtr = TIOCM_DTR;
|
||||||
|
|
||||||
|
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 */
|
||||||
|
}
|
||||||
|
|
||||||
|
return TRUE; /* control request succeeded */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Read from a serial port.
|
||||||
|
|
||||||
|
The port is checked for available characters. If any are present, they are
|
||||||
|
copied to the passed buffer, and the count of characters is returned. If no
|
||||||
|
characters are available, 0 is returned. If an error occurs, -1 is returned.
|
||||||
|
If a BREAK is detected on the communications line, the corresponding flag in
|
||||||
|
the "brk" array is set.
|
||||||
|
|
||||||
|
Implementation notes:
|
||||||
|
|
||||||
|
1. A character with a framing or parity error is indicated in the input
|
||||||
|
stream by the three-character sequence \377 \000 \ccc, where "ccc" is the
|
||||||
|
bad character. A communications line BREAK is indicated by the sequence
|
||||||
|
\377 \000 \000. A received \377 character is indicated by the
|
||||||
|
two-character sequence \377 \377. If we find any of these sequences,
|
||||||
|
they are replaced by the single intended character by sliding the
|
||||||
|
succeeding characters backward by one or two positions. If a BREAK
|
||||||
|
sequence was encountered, the corresponding location in the "brk" array
|
||||||
|
is determined, and the flag is set. Note that there may be multiple
|
||||||
|
sequences in the buffer.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int32 sim_read_serial (SERHANDLE port, char *buffer, int32 count, char *brk)
|
||||||
|
{
|
||||||
|
int read_count;
|
||||||
|
char *bptr, *cptr;
|
||||||
|
int32 remaining;
|
||||||
|
|
||||||
|
read_count = read (port, (void *) buffer, (size_t) count); /* read from the serial port */
|
||||||
|
|
||||||
|
if (read_count == -1) /* read error? */
|
||||||
|
if (errno == EAGAIN) /* no characters available? */
|
||||||
|
return 0; /* return 0 to indicate */
|
||||||
|
else /* some other problem */
|
||||||
|
sim_error_serial ("read", errno); /* report unexpected error */
|
||||||
|
|
||||||
|
else { /* read succeeded */
|
||||||
|
cptr = buffer; /* point at start of buffer */
|
||||||
|
remaining = read_count - 1; /* stop search one char from end of string */
|
||||||
|
|
||||||
|
while (remaining > 0 && /* still characters to search? */
|
||||||
|
(bptr = memchr (cptr, '\377', remaining))) { /* search for start of PARMRK sequence */
|
||||||
|
remaining = remaining - (bptr - cptr) - 1; /* calc characters remaining */
|
||||||
|
|
||||||
|
if (*(bptr + 1) == '\377') { /* is it a \377 \377 sequence? */
|
||||||
|
memmove (bptr + 1, bptr + 2, remaining); /* slide string backward to leave first \377 */
|
||||||
|
remaining = remaining - 1; /* drop remaining count */
|
||||||
|
read_count = read_count - 1; /* and read count by char eliminated */
|
||||||
|
}
|
||||||
|
|
||||||
|
else if (remaining > 0 && *(bptr + 1) == '\0') { /* is it a \377 \000 \ccc sequence? */
|
||||||
|
memmove (bptr, bptr + 2, remaining); /* slide string backward to leave \ccc */
|
||||||
|
remaining = remaining - 2; /* drop remaining count */
|
||||||
|
read_count = read_count - 2; /* and read count by chars eliminated */
|
||||||
|
|
||||||
|
if (*bptr == '\0') /* is it a BREAK sequence? */
|
||||||
|
*(brk + (bptr - buffer)) = 1; /* set corresponding BREAK flag */
|
||||||
|
}
|
||||||
|
|
||||||
|
cptr = bptr + 1; /* point at remainder of string */
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return (int32) read_count; /* return the number of characters read */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Write to a serial port.
|
||||||
|
|
||||||
|
"Count" characters are written from "buffer" to the serial port. The actual
|
||||||
|
number of characters written to the port is returned. If an error occurred
|
||||||
|
on writing, -1 is returned.
|
||||||
|
*/
|
||||||
|
|
||||||
|
int32 sim_write_serial (SERHANDLE port, char *buffer, int32 count)
|
||||||
|
{
|
||||||
|
int written;
|
||||||
|
|
||||||
|
written = write (port, (void *) buffer, (size_t) count); /* write the buffer to the serial port */
|
||||||
|
|
||||||
|
if (written == -1) /* write error? */
|
||||||
|
sim_error_serial ("write", errno); /* report unexpected error */
|
||||||
|
|
||||||
|
return (int32) written; /* return number of characters written */
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Close a serial port.
|
||||||
|
|
||||||
|
The serial port is closed. Errors are ignored.
|
||||||
|
*/
|
||||||
|
|
||||||
|
void sim_close_serial (SERHANDLE port)
|
||||||
|
{
|
||||||
|
close (port); /* close the port */
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
/* Non-implemented stubs */
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
|
||||||
|
/* Open a serial port */
|
||||||
|
|
||||||
|
SERHANDLE sim_open_serial (char *name)
|
||||||
|
{
|
||||||
|
return INVALID_HANDLE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Configure a serial port */
|
||||||
|
|
||||||
|
t_stat sim_config_serial (SERHANDLE port, SERCONFIG config)
|
||||||
|
{
|
||||||
|
return SCPE_IERR;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Control a serial port */
|
||||||
|
|
||||||
|
t_bool sim_control_serial (SERHANDLE port, t_bool connect)
|
||||||
|
{
|
||||||
|
return FALSE;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Read from a serial port */
|
||||||
|
|
||||||
|
int32 sim_read_serial (SERHANDLE port, char *buffer, int32 count, char *brk)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Write to a serial port */
|
||||||
|
|
||||||
|
int32 sim_write_serial (SERHANDLE port, char *buffer, int32 count)
|
||||||
|
{
|
||||||
|
return -1;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/* Close a serial port */
|
||||||
|
|
||||||
|
void sim_close_serial (SERHANDLE port)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
#endif /* end else !implemented */
|
99
sim_serial.h
Normal file
99
sim_serial.h
Normal file
|
@ -0,0 +1,99 @@
|
||||||
|
/* sim_serial.h: OS-dependent serial port routines header file
|
||||||
|
|
||||||
|
Copyright (c) 2008, J. David Bryan
|
||||||
|
|
||||||
|
Permission is hereby granted, free of charge, to any person obtaining a
|
||||||
|
copy of this software and associated documentation files (the "Software"),
|
||||||
|
to deal in the Software without restriction, including without limitation
|
||||||
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
||||||
|
and/or sell copies of the Software, and to permit persons to whom the
|
||||||
|
Software is furnished to do so, subject to the following conditions:
|
||||||
|
|
||||||
|
The above copyright notice and this permission notice shall be included in
|
||||||
|
all copies or substantial portions of the Software.
|
||||||
|
|
||||||
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
||||||
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
||||||
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
||||||
|
THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
||||||
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
||||||
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||||
|
|
||||||
|
Except as contained in this notice, the name of the author shall not be
|
||||||
|
used in advertising or otherwise to promote the sale, use or other dealings
|
||||||
|
in this Software without prior written authorization from the author.
|
||||||
|
|
||||||
|
07-Oct-08 JDB [serial] Created file
|
||||||
|
*/
|
||||||
|
|
||||||
|
|
||||||
|
#ifndef _SIM_SERIAL_H_
|
||||||
|
#define _SIM_SERIAL_H_ 0
|
||||||
|
|
||||||
|
|
||||||
|
/* Windows definitions */
|
||||||
|
|
||||||
|
#if defined (_WIN32)
|
||||||
|
|
||||||
|
|
||||||
|
/* We need the basic Win32 definitions, but including "windows.h" also includes
|
||||||
|
"winsock.h" as well. However, "sim_sock.h" explicitly includes "winsock2.h,"
|
||||||
|
and this file cannot coexist with "winsock.h". So we set a guard definition
|
||||||
|
that prevents "winsock.h" from being included.
|
||||||
|
*/
|
||||||
|
|
||||||
|
#ifndef WIN32_LEAN_AND_MEAN
|
||||||
|
#define WIN32_LEAN_AND_MEAN
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#include <windows.h>
|
||||||
|
|
||||||
|
typedef HANDLE SERHANDLE;
|
||||||
|
|
||||||
|
#define INVALID_HANDLE INVALID_HANDLE_VALUE
|
||||||
|
|
||||||
|
|
||||||
|
/* UNIX definitions */
|
||||||
|
|
||||||
|
#elif defined (__unix__)
|
||||||
|
|
||||||
|
|
||||||
|
#include <fcntl.h>
|
||||||
|
#include <termio.h>
|
||||||
|
#include <unistd.h>
|
||||||
|
#include <sys/ioctl.h>
|
||||||
|
|
||||||
|
typedef int SERHANDLE;
|
||||||
|
|
||||||
|
#define INVALID_HANDLE -1
|
||||||
|
|
||||||
|
|
||||||
|
/* Non-implemented definitions */
|
||||||
|
|
||||||
|
#else
|
||||||
|
|
||||||
|
typedef int SERHANDLE;
|
||||||
|
|
||||||
|
#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 */
|
||||||
|
|
||||||
|
extern SERHANDLE sim_open_serial (char *name);
|
||||||
|
extern t_stat sim_config_serial (SERHANDLE port, SERCONFIG config);
|
||||||
|
extern t_bool sim_control_serial (SERHANDLE port, t_bool connect);
|
||||||
|
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);
|
||||||
|
|
||||||
|
#endif
|
1150
sim_tmxr.c
1150
sim_tmxr.c
File diff suppressed because it is too large
Load diff
14
sim_tmxr.h
14
sim_tmxr.h
|
@ -28,6 +28,9 @@
|
||||||
|
|
||||||
17-Jan-11 MP Added buffered line capabilities
|
17-Jan-11 MP Added buffered line capabilities
|
||||||
20-Nov-08 RMS Added three new standardized SHOW routines
|
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
|
||||||
27-May-08 JDB Added lnorder to TMXR structure,
|
27-May-08 JDB Added lnorder to TMXR structure,
|
||||||
added tmxr_set_lnorder and tmxr_set_lnorder
|
added tmxr_set_lnorder and tmxr_set_lnorder
|
||||||
14-May-08 JDB Added dptr to TMXR structure
|
14-May-08 JDB Added dptr to TMXR structure
|
||||||
|
@ -44,6 +47,8 @@
|
||||||
#ifndef _SIM_TMXR_H_
|
#ifndef _SIM_TMXR_H_
|
||||||
#define _SIM_TMXR_H_ 0
|
#define _SIM_TMXR_H_ 0
|
||||||
|
|
||||||
|
#include "sim_serial.h"
|
||||||
|
|
||||||
#define TMXR_V_VALID 15
|
#define TMXR_V_VALID 15
|
||||||
#define TMXR_VALID (1 << TMXR_V_VALID)
|
#define TMXR_VALID (1 << TMXR_V_VALID)
|
||||||
#define TMXR_MAXBUF 256 /* buffer size */
|
#define TMXR_MAXBUF 256 /* buffer size */
|
||||||
|
@ -79,6 +84,8 @@ struct tmln {
|
||||||
char rbr[TMXR_MAXBUF]; /* rcv break */
|
char rbr[TMXR_MAXBUF]; /* rcv break */
|
||||||
char *txb; /* xmt buffer */
|
char *txb; /* xmt buffer */
|
||||||
TMXR *mp; /* back pointer to mux */
|
TMXR *mp; /* back pointer to mux */
|
||||||
|
SERHANDLE serport; /* serial port handle */
|
||||||
|
char *sername; /* serial port name */
|
||||||
};
|
};
|
||||||
|
|
||||||
struct tmxr {
|
struct tmxr {
|
||||||
|
@ -90,18 +97,25 @@ struct tmxr {
|
||||||
DEVICE *dptr; /* multiplexer device */
|
DEVICE *dptr; /* multiplexer device */
|
||||||
char logfiletmpl[FILENAME_MAX]; /* template logfile name */
|
char logfiletmpl[FILENAME_MAX]; /* template logfile name */
|
||||||
int32 buffered; /* Buffered Line Behavior and Buffer Size Flag */
|
int32 buffered; /* Buffered Line Behavior and Buffer Size Flag */
|
||||||
|
uint32 pending; /* count of pending serial connections */
|
||||||
};
|
};
|
||||||
|
|
||||||
int32 tmxr_poll_conn (TMXR *mp);
|
int32 tmxr_poll_conn (TMXR *mp);
|
||||||
void tmxr_reset_ln (TMLN *lp);
|
void tmxr_reset_ln (TMLN *lp);
|
||||||
|
t_stat tmxr_clear_ln (TMXR *mp, TMLN *lp);
|
||||||
int32 tmxr_getc_ln (TMLN *lp);
|
int32 tmxr_getc_ln (TMLN *lp);
|
||||||
void tmxr_poll_rx (TMXR *mp);
|
void tmxr_poll_rx (TMXR *mp);
|
||||||
t_stat tmxr_putc_ln (TMLN *lp, int32 chr);
|
t_stat tmxr_putc_ln (TMLN *lp, int32 chr);
|
||||||
void tmxr_poll_tx (TMXR *mp);
|
void tmxr_poll_tx (TMXR *mp);
|
||||||
|
int32 tmxr_send_buffered_data (TMLN *lp);
|
||||||
t_stat tmxr_open_master (TMXR *mp, char *cptr);
|
t_stat tmxr_open_master (TMXR *mp, char *cptr);
|
||||||
t_stat tmxr_close_master (TMXR *mp);
|
t_stat tmxr_close_master (TMXR *mp);
|
||||||
t_stat tmxr_attach (TMXR *mp, UNIT *uptr, char *cptr);
|
t_stat tmxr_attach (TMXR *mp, UNIT *uptr, char *cptr);
|
||||||
t_stat tmxr_detach (TMXR *mp, UNIT *uptr);
|
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_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
|
t_stat tmxr_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
|
||||||
t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
|
t_stat tmxr_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
|
||||||
void tmxr_msg (SOCKET sock, char *msg);
|
void tmxr_msg (SOCKET sock, char *msg);
|
||||||
|
|
Loading…
Add table
Reference in a new issue