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:
Mark Pizzolato 2012-04-19 19:18:15 -07:00
parent 5f505ccadf
commit bc36e9dde5
16 changed files with 13799 additions and 334 deletions

View file

@ -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 */

View file

@ -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 */

View file

@ -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)
{ {

View file

@ -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 */

View file

@ -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)

View file

@ -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,

View file

@ -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 = {

View file

@ -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

File diff suppressed because it is too large Load diff

View file

@ -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
View file

@ -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
View file

@ -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
View 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
View 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

File diff suppressed because it is too large Load diff

View file

@ -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);