/* hp3000_atc.c: HP 3000 30032B Asynchronous Terminal Controller simulator | |
Copyright (c) 2014-2017, J. David Bryan | |
Copyright (c) 2002-2012, Robert M Supnik | |
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 | |
AUTHORS 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 names of the authors 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 authors. | |
ATCD,ATCC HP 30032B Asynchronous Terminal Controller | |
18-Dec-17 JDB Return event time instead of status from "activate_unit" | |
11-Dec-17 JDB Reschedule "line_service" if receive buffer has data | |
26-Oct-17 JDB Call "tmxr_poll_tx" if transmit buffer is full | |
05-Sep-17 JDB Changed REG_A (permit any symbolic override) to REG_X | |
16-Sep-16 JDB Fixed atcd_detach to skip channel cancel if SIM_SW_REST | |
12-Sep-16 JDB Changed DIB register macro usage from SRDATA to DIB_REG | |
20-Jul-16 JDB Corrected poll_unit "wait" field initializer. | |
26-Jun-16 JDB Removed tmxr_set_modem_control_passthru call in atcc_reset | |
09-Jun-16 JDB Added casts for ptrdiff_t to int32 values | |
16-May-16 JDB Fixed interrupt mask setting | |
13-May-16 JDB Modified for revised SCP API function parameter types | |
21-Mar-16 JDB Changed uint16 types to HP_WORD | |
26-Aug-15 JDB First release version | |
31-Jul-15 JDB Passes the terminal control diagnostic (D438A) | |
11-Aug-14 JDB Passes the terminal data diagnostic (D427A) | |
28-Jul-14 JDB Created from the HP 2100 12920A MUX device simulator | |
References: | |
- 30032B Asynchronous Terminal Controller Installation and Service Manual | |
(30032-90004, February 1977) | |
- Stand-Alone HP 30032B Terminal Data Interface Diagnostic | |
(30032-90011, October 1980) | |
- Stand-Alone HP 30061A Terminal Controller Interface Diagnostic | |
(30060-90004, February 1976) | |
- HP 3000 Series III Engineering Diagrams Set | |
(30000-90141, April 1980) | |
The HP 30032B Asynchronous Terminal Controller is a 16-channel terminal | |
multiplexer used with the HP 3000 CX through Series III systems. The ATC | |
connects from 1 to 16 serial terminals or modems to the HP 3000 at | |
programmable baud rates from 75 to 2400 bits per second. Character sizes are | |
also programmable from 5 to 12 bits in length, including the start and stop | |
bits. Each channel can be independently configured, including for separate | |
send and receive rates. The ATC is not buffered, so the CPU has to retrieve | |
each character from a given channel before the next character arrives. To | |
avoid saturating the CPU with interrupt requests, the ATC maintains an | |
internal "mini-interrupt" system that queues requests and holds additional | |
interrupts off until the CPU acknowledges the current request. | |
The HP 3000CX and Series I use a dedicated serial interface for the system | |
console, while user terminals are connected to the ATC. For the Series II | |
and III, the separate card is eliminated, and channel 0 of the ATC is | |
reserved for the console. | |
This module is an adaptation of the code originally written by Bob Supnik for | |
the HP2100 MUX simulator. The MUX device simulates an HP 12920A interface | |
for an HP 2100/1000 computer. The 12920A is an ATC with the HP 3000 I/O bus | |
connection replaced by an HP 2100 I/O bus connection. Programming and | |
operation of the two multiplexers are virtually identical. | |
The ATC consists of a Terminal Data Interface, which provides direct | |
connection for 16 serial terminals, and one or two optional Terminal Control | |
Interfaces, which provides control and status lines for Bell 103 and 202 data | |
sets, respectively. The ATC base product, order number 30032, consisted of | |
one TDI card. Option -001 added one TCI, and option -002 added two. A | |
second ATC subsystem could be added to support an additional 16 terminals or | |
modems. | |
This simulation provides one TDI and one optional TCI. Each of the channels | |
may be connected either to a Telnet session or a serial port on the host | |
machine. Channel 0 is connected to the simulation console, which initially | |
performs I/O to the controlling window but may be rerouted instead to a | |
Telnet session or serial port, if desired. Additional channel configuration | |
options select the input mode (upshifted or normal), output mode (8-bit, | |
7-bit, printable, or upshifted), and whether the HP-standard ENQ/ACK | |
handshaking is done by the external device or internally by the simulator. | |
A device mode specifies whether terminals or diagnostic loopback cables are | |
connected to the TDI and TCI. Enabling the diagnostic mode simulates the | |
installation of eight HP 30062-60003 diagnostic test (loopback) cables | |
between channels 0-1, 2-3, etc., as required by the multiplexer diagnostics. | |
In this mode, sending data on one channel automatically receives the same | |
data on the alternate channel. In addition, all Telnet and serial sessions | |
are disconnected, and the TDI is detached from the listening port. While in | |
diagnostic mode, the ATTACH command is not allowed. Enabling terminal mode | |
allows the TDI to be attached to accept incoming connections again. | |
Another device mode specifies whether the TDI operates in real-time or | |
optimized ("fast") timing mode. In the former, character send and receive | |
events occur at approximately the same rate (in machine instructions) as in | |
hardware. The latter mode increases the rate to the maximum value consistent | |
with correct operation in MPE. | |
Both the TDI and TCI are normally enabled, although the TCI will not be used | |
unless MPE is configured to use data sets on one or more channels. When so | |
configured, logging off will cause the channel to disconnect the Telnet | |
session or drop the Data Terminal Ready signal on the serial port. A channel | |
controlled by the TCI will be marked as "data set" in a unit listing; | |
channels not controlled will be marked as "direct". | |
The TDI and TCI may be disabled, if desired, although the TDI must be | |
detached from the listening port first. Disabling the TDI does not affect | |
the simulation console, as the CPU process clock will take over console | |
polling automatically. | |
The Terminal Data Interface provides 16 send channels, 16 receive channels, | |
and 5 auxiliary channels. The auxiliary channels are receive-only and do | |
not connect to external devices. Rather, they may be connected as a group to | |
one or more of the other channels. Their primary purpose is to diagnose | |
conditions (e.g., baud rate) on the connected channel(s). | |
In hardware, a recirculating memory stores seven 8-bit words of data, | |
parameters, and status for each of the 37 channels. A set of registers form | |
a "window" into the recirculating memory, and the memory makes one complete | |
pass every 69.44 microseconds. Serial transfer rates are determined by each | |
channel's parameter word, which specifies the number of recirculations that | |
occur for each bit sent or received. | |
In simulation, the memory is represented by separate buffer, parameter, and | |
status arrays. Recirculation is simulated by indexing through each of the | |
arrays in sequence. | |
The TDI responds only to direct I/O instructions, as follows: | |
TDI Control Word Format (CIO): | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| M | R | channel number | - - - - - - - | E | A | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
Where: | |
M = master reset | |
R = reset interrupts | |
E = enable store of preceding data or parameter word to memory | |
A = acknowledge interrupt | |
TDI Status Word Format (TIO): | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| S | D | I | - | C | R | L | B | - - - - - - - - | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
Where: | |
S = SIO OK (always 0) | |
D = direct read/write I/O OK | |
I = interrupt request | |
C = read/write completion flag | |
R = receive/send (0/1) character interrupt | |
L = character lost | |
B = break status | |
TDI Parameter Word Format (WIO): | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 1 | R | I | E | D | char size | baud rate | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
Where: | |
R = receive/send (0/1) configuration | |
I = enable channel completion interrupt | |
E = enable echo (receive) or generate parity (send) | |
D = diagnose using the auxiliary channels | |
Character size: | |
The three least-significant bits of the sum of the data, parity, and stop | |
bits. For example, 7E1 is 1001, so 001 is coded. | |
Baud rate: | |
The value (14400 / device bit rate) - 1. For example, 2400 baud is 005. | |
TDI Output Data Word Format (WIO): | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 | 1 | - - | S | send data | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
Where: | |
S = sync bit | |
data = right-justified with leading ones | |
TDI Input Data Word Format (RIO): | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| channel | P | receive data | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
Where: | |
P = computed parity | |
data = right-justified with leading ones | |
The Terminal Control Interface provides two serial control outputs and two | |
serial status inputs for each of 16 channels. The first TCI connects to the | |
Request to Send (CA) and Data Terminal Ready (CD) control lines and the Data | |
Carrier Detect (CF) and Data Set Ready (CC) status lines. Addressable | |
latches hold the control line values and assert them continuously to the 16 | |
channels. In addition, a 16-word by 4-bit RAM holds the expected state for | |
each channel's status lines and the corresponding interrupt enable bits to | |
provide notification if those lines change. | |
The TCI responds only to direct I/O instructions, as follows: | |
TCI Control Word Format (CIO): | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| M | R | S | U | channel | W | X | Q | T | Y | Z | C | D | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
Where: | |
M = master reset | |
R = reset interrupts | |
S = scan status | |
U = enable DCD/DSR state update | |
W = enable RTS change | |
X = enable DTR change | |
Q = new RTS state | |
T = new DTR state | |
Y = DCD interrupt enabled | |
Z = DSR interrupt enabled | |
C = expected DCD state | |
D = expected DSR state | |
TCI Status Word Format (TIO or RIO): | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 | 1 | I | 1 | channel | 0 | 0 | J | K | Y | Z | C | D | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
Where: | |
I = interrupt request | |
J = DCD interrupt present | |
K = DSR interrupt present | |
Y = DCD interrupt enabled | |
Z = DSR interrupt enabled | |
C = current DCD state | |
D = current DSR state | |
Implementation notes: | |
1. The UNIT_MODEM flag indicates that a channel is controlled by the TCI. | |
However, no modifier entry is provided, nor is one needed, as the flag is | |
set automatically when the TCI first initializes the channel. MPE | |
defines separate terminal subtype numbers for directly connected | |
terminals and modem-connected terminals, which are set at system | |
generation time. | |
2. Both TMXR_VALID and SCPE_KFLAG are set on internally generated ACKs only | |
so that a debug trace will record the generation correctly. | |
*/ | |
#include <ctype.h> | |
#include "hp3000_defs.h" | |
#include "hp3000_io.h" | |
#include "sim_tmxr.h" | |
/* Program limits */ | |
#define TERM_COUNT 16 /* number of terminal channels */ | |
#define AUX_COUNT 5 /* number of auxiliary channels */ | |
#define POLL_COUNT 1 /* number of poll units */ | |
#define RECV_CHAN_COUNT (TERM_COUNT + AUX_COUNT) /* number of receive channels */ | |
#define SEND_CHAN_COUNT TERM_COUNT /* number of send channels */ | |
#define UNIT_COUNT (TERM_COUNT + POLL_COUNT) /* number of units */ | |
#define FIRST_TERM 0 /* first terminal index */ | |
#define LAST_TERM (FIRST_TERM + TERM_COUNT - 1) /* last terminal index */ | |
#define FIRST_AUX TERM_COUNT /* first auxiliary index */ | |
#define LAST_AUX (FIRST_AUX + AUX_COUNT - 1) /* last auxiliary index */ | |
/* Program constants */ | |
#define FAST_IO_TIME 500 /* initial fast receive/send time in event ticks */ | |
#define POLL_RATE 100 /* poll 100 times per second (unless synchronized) */ | |
#define POLL_TIME mS (10) /* poll time is 10 milliseconds */ | |
#define NUL '\000' /* null */ | |
#define ENQ '\005' /* enquire */ | |
#define ACK '\006' /* acknowledge */ | |
#define ASCII_MASK 000177u /* 7-bit ASCII character set mask */ | |
#define GEN_ACK (TMXR_VALID | SCPE_KFLAG | ACK) /* a generated ACK character */ | |
#define SCAN_ALL (-1) /* scan all channels for completion */ | |
/* Parity functions derived from the global lookup table */ | |
#define RECV_PARITY(c) (odd_parity [(c) & D8_MASK] ? 0 : DDR_PARITY) | |
#define SEND_PARITY(c) (odd_parity [(c) & D8_MASK] ? 0 : DDS_PARITY) | |
/* Debug flags */ | |
#define DEB_CSRW (1u << 0) /* trace command initiations and completions */ | |
#define DEB_XFER (1u << 1) /* trace data receptions and transmissions */ | |
#define DEB_IOB (1u << 2) /* trace I/O bus signals and data words */ | |
#define DEB_SERV (1u << 3) /* trace channel service scheduling calls */ | |
#define DEB_PSERV (1u << 4) /* trace poll service scheduling calls */ | |
/* Common per-unit multiplexer channel state variables */ | |
#define recv_time u3 /* realistic receive time in event ticks */ | |
#define send_time u4 /* realistic send time in event ticks */ | |
#define stop_bits u5 /* stop bits to be added to each character received */ | |
/* Device flags */ | |
#define DEV_DIAG_SHIFT (DEV_V_UF + 0) /* diagnostic loopback */ | |
#define DEV_REALTIME_SHIFT (DEV_V_UF + 1) /* timing mode is realistic */ | |
#define DEV_DIAG (1u << DEV_DIAG_SHIFT) /* diagnostic mode flag */ | |
#define DEV_REALTIME (1u << DEV_REALTIME_SHIFT) /* realistic timing flag */ | |
/* Unit flags */ | |
#define UNIT_CAPSLOCK_SHIFT (TTUF_V_UF + 0) /* caps lock mode */ | |
#define UNIT_LOCALACK_SHIFT (TTUF_V_UF + 1) /* local ACK mode */ | |
#define UNIT_MODEM_SHIFT (TTUF_V_UF + 2) /* modem control */ | |
#define UNIT_CAPSLOCK (1u << UNIT_CAPSLOCK_SHIFT) /* caps lock is down flag */ | |
#define UNIT_LOCALACK (1u << UNIT_LOCALACK_SHIFT) /* ENQ/ACK mode is local flag */ | |
#define UNIT_MODEM (1u << UNIT_MODEM_SHIFT) /* channel connects to a data set flag */ | |
/* Unit references */ | |
#define line_unit atcd_unit /* receive/send channel units */ | |
#define poll_unit atcd_unit [LAST_TERM + 1] /* input polling unit */ | |
/* Activation reasons */ | |
typedef enum { | |
Receive, | |
Send, | |
Loop, | |
Stall | |
} ACTIVATOR; | |
/* TDI control word. | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| M | R | channel number | - - - - - - - | E | A | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
*/ | |
#define DCN_MR 0100000u /* (M) master reset */ | |
#define DCN_IRQ_RESET 0040000u /* (R) interrupt request reset */ | |
#define DCN_CHAN_MASK 0037000u /* channel number mask */ | |
#define DCN_ENABLE 0000002u /* (E) enable store of preceding data or parameter word */ | |
#define DCN_ACKN 0000001u /* (A) acknowledge interrupt */ | |
#define DCN_CHAN_SHIFT 9 /* channel number alignment shift */ | |
#define DCN_CHAN(c) (((c) & DCN_CHAN_MASK) >> DCN_CHAN_SHIFT) | |
static const BITSET_NAME tdi_control_names [] = { /* TDI control word names */ | |
"master reset", /* bit 0 */ | |
"reset interrupt", /* bit 1 */ | |
NULL, /* bit 2 */ | |
NULL, /* bit 3 */ | |
NULL, /* bit 4 */ | |
NULL, /* bit 5 */ | |
NULL, /* bit 6 */ | |
NULL, /* bit 7 */ | |
NULL, /* bit 8 */ | |
NULL, /* bit 9 */ | |
NULL, /* bit 10 */ | |
NULL, /* bit 11 */ | |
NULL, /* bit 12 */ | |
NULL, /* bit 13 */ | |
"store word", /* bit 14 */ | |
"acknowledge interrupt" /* bit 15 */ | |
}; | |
static const BITSET_FORMAT tdi_control_format = /* names, offset, direction, alternates, bar */ | |
{ FMT_INIT (tdi_control_names, 0, msb_first, no_alt, no_bar) }; | |
/* TDI status word. | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| - | D | I | - | C | R | L | B | - - - - - - - - | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
*/ | |
#define DST_DIO_OK 0040000u /* (D) direct I/O OK to use */ | |
#define DST_IRQ 0020000u /* (I) interrupt requested */ | |
#define DST_COMPLETE 0004000u /* (C) operation is complete and channel is ready to interrupt */ | |
#define DST_SEND_IRQ 0002000u /* (R) interrupt request is for character sent */ | |
#define DST_CHAR_LOST 0001000u /* (L) character was lost */ | |
#define DST_BREAK 0000400u /* (B) break occurred */ | |
#define DST_DIAGNOSE 0000000u /* status is from an auxiliary channel (not used on ATC) */ | |
#define DST_CHAN(n) 0 /* position channel number for status (not used on ATC) */ | |
static const BITSET_NAME tdi_status_names [] = { /* TDI status word names */ | |
"DIO OK", /* bit 1 */ | |
"interrupt", /* bit 2 */ | |
NULL, /* bit 3 */ | |
"complete", /* bit 4 */ | |
"\1send\0receive", /* bit 5 */ | |
"lost", /* bit 6 */ | |
"break" /* bit 7 */ | |
}; | |
static const BITSET_FORMAT tdi_status_format = /* names, offset, direction, alternates, bar */ | |
{ FMT_INIT (tdi_status_names, 8, msb_first, has_alt, no_bar) }; | |
/* TDI parameter word. | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 1 | R | I | E | D | char size | baud rate | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
The baud rate is encoded as 14400 / device_bit_rate - 1, but the manual says | |
to round the result, so that, e.g., the 110 baud rate encoding of 129.91 is | |
rounded to 130. To reconstruct the rate without floating-point calculations, | |
the parameter print routine uses: | |
baud_rate = (2 * 14400 / (encoded_rate + 1) + 1) / 2 | |
...which is equivalent to: | |
baud_rate = (int) (14400 / (encoded_rate + 1) + 0.5) | |
The multiplexer pads the received character data to the left with one-bits. | |
The PAD_BITS function generates the pad bits, assuming that the received | |
character transmission has one stop bit. This isn't always correct, e.g., a | |
Teleprinter uses two stop bits at 110 baud, but there's no way to reconstruct | |
the number of stop bits from the receive parameter word. | |
*/ | |
#define DPI_IS_PARAM 0100000u /* value is a parameter (always set) */ | |
#define DPI_IS_SEND 0040000u /* (R) value is a send parameter */ | |
#define DPI_ENABLE_IRQ 0020000u /* (I) enable interrupt requests */ | |
#define DPI_ENABLE_PARITY 0010000u /* (E) enable parity for send */ | |
#define DPI_ENABLE_ECHO 0010000u /* (E) enable echo for receive */ | |
#define DPI_DIAGNOSE 0004000u /* (D) connect to the auxiliary channels */ | |
#define DPI_SIZE_MASK 0003400u /* character size mask */ | |
#define DPI_RATE_MASK 0000377u /* baud rate mask */ | |
#define DPI_CHAR_CONFIG (DPI_SIZE_MASK | DPI_RATE_MASK) /* character configuration data */ | |
#define DPI_SIZE_SHIFT 8 /* character size alignment shift */ | |
#define DPI_RATE_SHIFT 0 /* baud rate alignment shift */ | |
#define DPI_CHAR_SIZE(p) (((p) & DPI_SIZE_MASK) >> DPI_SIZE_SHIFT) | |
#define DPI_BAUD_RATE(p) (((p) & DPI_RATE_MASK) >> DPI_RATE_SHIFT) | |
#define BAUD_RATE(p) ((28800 / (DPI_BAUD_RATE (p) + 1) + 1) / 2) | |
#define PAD_BITS(c) (~((1u << bits_per_char [DPI_CHAR_SIZE (c)] - 2) - 1)) | |
static const uint32 bits_per_char [8] = { /* bits per character, indexed by DPI_CHAR_SIZE encoding */ | |
9, 10, 11, 12, 5, 6, 7, 8 | |
}; | |
static const BITSET_NAME tdi_parameter_names [] = { /* TDI parameter word names */ | |
"\1send\0receive", /* bit 1 */ | |
"enable interrupt", /* bit 2 */ | |
"enable parity/echo", /* bit 3 */ | |
"diagnose" /* bit 4 */ | |
}; | |
static const BITSET_FORMAT tdi_parameter_format = /* names, offset, direction, alternates, bar */ | |
{ FMT_INIT (tdi_parameter_names, 11, msb_first, has_alt, append_bar) }; | |
/* TDI output (send) data word. | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| 0 | 1 | - - | S | send data | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
*/ | |
#define DDS_IS_SEND 0040000u /* value is a send data word (always set) */ | |
#define DDS_SYNC 0004000u /* (S) sync */ | |
#define DDS_DATA_MASK 0003777u /* data value mask */ | |
#define DDS_PARITY 0000200u /* data parity bit */ | |
#define DDS_MARK (DDS_SYNC | DDS_DATA_MASK) /* all-mark character */ | |
#define DDS_DATA(d) ((d) & DDS_DATA_MASK) | |
static const BITSET_NAME tdi_output_data_names [] = { /* TDI output data word names */ | |
"send", /* bit 1 */ | |
NULL, /* bit 2 */ | |
NULL, /* bit 3 */ | |
"sync" /* bit 4 */ | |
}; | |
static const BITSET_FORMAT tdi_output_data_format = /* names, offset, direction, alternates, bar */ | |
{ FMT_INIT (tdi_output_data_names, 11, msb_first, no_alt, append_bar) }; | |
/* TDI input (receive) data word. | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| channel | P | receive data | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
*/ | |
#define DDR_CHAN_MASK 0174000u /* channel number mask */ | |
#define DDR_PARITY 0002000u /* (P) computed parity bit */ | |
#define DDR_DATA_MASK 0001777u /* data value mask */ | |
#define DDR_CHAN_SHIFT 11 /* channel number alignment shift */ | |
#define DDR_DATA_SHIFT 0 /* data alignment shift */ | |
#define DDR_CHAN(n) ((n) << DDR_CHAN_SHIFT & DDR_CHAN_MASK) | |
#define DDR_DATA(d) ((d) << DDR_DATA_SHIFT & DDR_DATA_MASK) | |
#define DDR_TO_CHAN(w) (((w) & DDR_CHAN_MASK) >> DDR_CHAN_SHIFT) | |
#define DDR_TO_DATA(w) (((w) & DDR_DATA_MASK) >> DDR_DATA_SHIFT) | |
static const BITSET_NAME tdi_input_data_names [] = { /* TDI input data word names */ | |
"\1odd parity\0even parity", /* bit 5 */ | |
}; | |
static const BITSET_FORMAT tdi_input_data_format = /* names, offset, direction, alternates, bar */ | |
{ FMT_INIT (tdi_input_data_names, 10, msb_first, has_alt, append_bar) }; | |
/* TCI control word. | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| M | R | S | U | channel | W | X | Q | T | Y | Z | C | D | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
*/ | |
#define CCN_MR 0100000u /* (M) master reset */ | |
#define CCN_IRQ_RESET 0040000u /* (R) interrupt request reset */ | |
#define CCN_SCAN 0020000u /* (S) scan enable */ | |
#define CCN_UPDATE 0010000u /* (U) update enable */ | |
#define CCN_CHAN_MASK 0007400u /* channel number mask */ | |
#define CCN_ECX_MASK 0000300u /* control output enable mask */ | |
#define CCN_EC2 0000200u /* (W) C2 output enable */ | |
#define CCN_EC1 0000100u /* (X) C1 output enable */ | |
#define CCN_CX_MASK 0000060u /* output mask */ | |
#define CCN_C2 0000040u /* (Q) C2 output [RTS] */ | |
#define CCN_C1 0000020u /* (T) C1 output [DTR] */ | |
#define CCN_STAT_MASK 0000017u /* status RAM mask */ | |
#define CCN_ESX_MASK 0000014u /* status interrupt enable mask */ | |
#define CCN_ES2 0000010u /* (Y) S2 interrupt enable */ | |
#define CCN_ES1 0000004u /* (Z) S1 interrupt enable */ | |
#define CCN_SX_MASK 0000003u /* status mask */ | |
#define CCN_S2 0000002u /* (C) S2 status [DCD]*/ | |
#define CCN_S1 0000001u /* (D) S1 status [DSR] */ | |
#define CCN_CHAN_SHIFT 8 /* channel number alignment shift */ | |
#define CCN_CX_SHIFT 4 /* control alignment shift */ | |
#define CCN_ECX_SHIFT 2 /* control output enable alignment shift (to Cx) */ | |
#define CCN_ESX_SHIFT 2 /* status interrupt enable alignment shift */ | |
#define CCN_CHAN(c) (((c) & CCN_CHAN_MASK) >> CCN_CHAN_SHIFT) | |
#define CCN_ECX(c) (((c) & CCN_ECX_MASK) >> CCN_ECX_SHIFT) | |
#define CCN_CX(c) (((c) & CCN_CX_MASK) >> CCN_CX_SHIFT) | |
#define CCN_ESX(c) (((c) & CCN_ESX_MASK) >> CCN_ESX_SHIFT) | |
static const BITSET_NAME tci_control_names [] = { /* TCI control word names */ | |
"master reset", /* bit 0 */ | |
"reset interrupt", /* bit 1 */ | |
"scan", /* bit 2 */ | |
"update", /* bit 3 */ | |
NULL, /* bit 4 */ | |
NULL, /* bit 5 */ | |
NULL, /* bit 6 */ | |
NULL, /* bit 7 */ | |
"EC2", /* bit 8 */ | |
"EC1", /* bit 9 */ | |
"\1C2\0~C2", /* bit 10 */ | |
"\1C1\0~C1", /* bit 11 */ | |
"ES2", /* bit 12 */ | |
"ES1", /* bit 13 */ | |
"\1S2\0~S2", /* bit 14 */ | |
"\1S1\0~S1" /* bit 15 */ | |
}; | |
static const BITSET_FORMAT tci_control_format = /* names, offset, direction, alternates, bar */ | |
{ FMT_INIT (tci_control_names, 0, msb_first, has_alt, no_bar) }; | |
/* TCI status word. | |
0 | 1 2 3 | 4 5 6 | 7 8 9 |10 11 12 |13 14 15 | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
| - | 1 | I | 1 | channel | - | - | J | K | Y | Z | C | D | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | |
*/ | |
#define CST_DIO_OK 0040000u /* direct I/O OK to use (always set) */ | |
#define CST_IRQ 0020000u /* (I) interrupt request */ | |
#define CST_ON 0010000u /* (always set) */ | |
#define CST_CHAN_MASK 0007400u /* channel number mask */ | |
#define CST_IX_MASK 0000060u /* status interrupt mask */ | |
#define CST_I2 0000040u /* (J) S2 interrupt */ | |
#define CST_I1 0000020u /* (K) S1 interrupt */ | |
#define CST_ESX_MASK 0000014u /* status interrupt enable mask */ | |
#define CST_ES2 0000010u /* (Y) S2 interrupt enable */ | |
#define CST_ES1 0000004u /* (Z) S1 interrupt enable */ | |
#define CST_SX_MASK 0000003u /* status mask */ | |
#define CST_S2 0000002u /* (C) S2 status [DCD] */ | |
#define CST_S1 0000001u /* (D) S1 status [DSR] */ | |
#define CST_CHAN_SHIFT 8 /* channel number alignment shift */ | |
#define CST_IX_SHIFT 4 /* status interrupt alignment shift */ | |
#define CST_CHAN(n) ((n) << CST_CHAN_SHIFT & CST_CHAN_MASK) | |
#define CST_IX(i) ((i) << CST_IX_SHIFT & CST_IX_MASK) | |
static const BITSET_NAME tci_status_names [] = { /* TCI status word names */ | |
"interrupt", /* bit 2 */ | |
NULL, /* bit 3 */ | |
NULL, /* bit 4 */ | |
NULL, /* bit 5 */ | |
NULL, /* bit 6 */ | |
NULL, /* bit 7 */ | |
NULL, /* bit 8 */ | |
NULL, /* bit 9 */ | |
"I2", /* bit 10 */ | |
"I1", /* bit 11 */ | |
"ES2", /* bit 12 */ | |
"ES1", /* bit 13 */ | |
"\1S2\0~S2", /* bit 14 */ | |
"\1S1\0~S1" /* bit 15 */ | |
}; | |
static const BITSET_FORMAT tci_status_format = /* names, offset, direction, alternates, bar */ | |
{ FMT_INIT (tci_status_names, 0, msb_first, has_alt, no_bar) }; | |
/* TCI #1 serial line bits */ | |
#define RTS CCN_C2 /* TCI #1 C2 = Request to Send */ | |
#define DTR CCN_C1 /* TCI #1 C1 = Data Terminal Ready */ | |
#define DCD CCN_S2 /* TCI #1 S2 = Data Carrier Detect */ | |
#define DSR CCN_S1 /* TCI #1 S1 = Data Set Ready */ | |
static const BITSET_NAME tci_line_names [] = { /* TCI serial line status names */ | |
"RTS", /* bit 10 */ | |
"DTR", /* bit 11 */ | |
NULL, /* bit 12 */ | |
NULL, /* bit 13 */ | |
"DCD", /* bit 14 */ | |
"DSR" /* bit 15 */ | |
}; | |
static const BITSET_FORMAT tci_line_format = /* names, offset, direction, alternates, bar */ | |
{ FMT_INIT (tci_line_names, 0, msb_first, no_alt, no_bar) }; | |
/* ATC global state */ | |
t_bool atc_is_polling = TRUE; /* TRUE if the ATC is polling for the simulation console */ | |
/* TDI interface state */ | |
static HP_WORD tdi_control_word = 0; /* control word */ | |
static HP_WORD tdi_status_word = 0; /* status word */ | |
static HP_WORD tdi_read_word = 0; /* read word */ | |
static HP_WORD tdi_write_word = 0; /* write word */ | |
static FLIP_FLOP tdi_interrupt_mask = SET; /* interrupt mask flip-flop */ | |
static FLIP_FLOP tdi_data_flag = CLEAR; /* data flag */ | |
static int32 fast_data_time = FAST_IO_TIME; /* fast receive/send time */ | |
/* TDI per-channel state */ | |
static HP_WORD recv_status [RECV_CHAN_COUNT]; /* receive status words */ | |
static HP_WORD recv_param [RECV_CHAN_COUNT]; /* receive parameter words */ | |
static HP_WORD recv_buffer [RECV_CHAN_COUNT]; /* receive character buffers */ | |
static HP_WORD send_status [SEND_CHAN_COUNT]; /* send status words */ | |
static HP_WORD send_param [SEND_CHAN_COUNT]; /* send parameter words */ | |
static HP_WORD send_buffer [SEND_CHAN_COUNT]; /* send character buffers */ | |
/* TCI interface state */ | |
static HP_WORD tci_control_word = 0; /* control word */ | |
static HP_WORD tci_status_word = 0; /* status word */ | |
static uint32 tci_cntr = 0; /* channel counter */ | |
static FLIP_FLOP tci_interrupt_mask = SET; /* interrupt mask flip-flop */ | |
static FLIP_FLOP tci_scan = CLEAR; /* scanning enabled flip-flop */ | |
/* TCI per-channel state */ | |
static uint8 cntl_status [TERM_COUNT]; /* C2/C1/S2/S1 line status */ | |
static uint8 cntl_param [TERM_COUNT]; /* ES2/ES1/S2/S1 parameter RAM */ | |
/* ATC local SCP support routines */ | |
static CNTLR_INTRF atcd_interface; | |
static CNTLR_INTRF atcc_interface; | |
static t_stat atc_set_endis (UNIT *uptr, int32 value, CONST char *cptr, void *desc); | |
static t_stat atc_set_mode (UNIT *uptr, int32 value, CONST char *cptr, void *desc); | |
static t_stat atc_show_mode (FILE *st, UNIT *uptr, int32 value, CONST void *desc); | |
static t_stat atc_show_status (FILE *st, UNIT *uptr, int32 value, CONST void *desc); | |
static t_stat atcd_reset (DEVICE *dptr); | |
static t_stat atcc_reset (DEVICE *dptr); | |
static t_stat atcd_attach (UNIT *uptr, CONST char *cptr); | |
static t_stat atcd_detach (UNIT *uptr); | |
/* ATC local utility routines */ | |
static void tdi_set_interrupt (void); | |
static void tdi_master_reset (void); | |
static void tci_master_reset (void); | |
static t_stat line_service (UNIT *uptr); | |
static t_stat poll_service (UNIT *uptr); | |
static int32 activate_unit (UNIT *uptr, ACTIVATOR reason); | |
static uint32 service_time (HP_WORD control, ACTIVATOR reason); | |
static void store (HP_WORD control, HP_WORD data); | |
static void receive (int32 channel, int32 data, t_bool loopback); | |
static void diagnose (HP_WORD control, int32 data); | |
static void scan_channels (int32 channel); | |
static HP_WORD scan_status (void); | |
/* ATC SCP data structures */ | |
DEVICE atcd_dev; /* incomplete device structure */ | |
DEVICE atcc_dev; /* incomplete device structure */ | |
/* Terminal multiplexer library structures. | |
The ATC uses the connection line order feature to bypass channel 0, which is | |
dedicated to the system console. For convenience, the system console is | |
connected to the simulation console. As such, it calls the console I/O | |
routines instead of the terminal multiplexer routines. | |
User-defined line order is not supported. | |
*/ | |
static int32 atcd_order [TERM_COUNT] = { /* line connection order */ | |
1, 1, 2, 3, 4, 5, 6, 7, | |
8, 9, 10, 11, 12, 13, 14, 15 }; | |
static TMLN atcd_ldsc [TERM_COUNT] = { /* line descriptors */ | |
{ 0 } | |
}; | |
static TMXR atcd_mdsc = { /* multiplexer descriptor */ | |
TERM_COUNT, /* number of terminal lines */ | |
0, /* listening port (reserved) */ | |
0, /* master socket (reserved) */ | |
atcd_ldsc, /* line descriptors */ | |
atcd_order, /* line connection order */ | |
NULL /* multiplexer device (derived internally) */ | |
}; | |
/* Device information blocks */ | |
static DIB atcd_dib = { | |
&atcd_interface, /* device interface */ | |
7, /* device number */ | |
SRNO_UNUSED, /* service request number */ | |
0, /* interrupt priority */ | |
INTMASK_E /* interrupt mask */ | |
}; | |
static DIB atcc_dib = { | |
&atcc_interface, /* device interface */ | |
8, /* device number */ | |
SRNO_UNUSED, /* service request number */ | |
8, /* interrupt priority */ | |
INTMASK_E /* interrupt mask */ | |
}; | |
/* Unit lists. | |
The first sixteen TDI units correspond to the sixteen multiplexer main | |
send/receive channels. These handle character I/O via the Telnet library. A | |
seventeenth unit is responsible for polling for connections and socket I/O. | |
It also holds the master socket. | |
Channel 0 is reserved for the system console and is connected to the | |
simulation console. As such, it's not likely to be using an HP terminal | |
emulator, so the default is CAPSLOCK input mode and 7P output mode. The | |
remainder of the channels default to NOCAPSLOCK and 7B, as they're likely to | |
be connected to HP terminals or terminal emulators. All channels initially | |
omit the UNIT_MODEM flag to allow the MPE terminal subtype configuration to | |
determine which channels support data sets and which do not. | |
The TDI line service routine runs only when there are characters to read or | |
write. It is scheduled either at a realistic rate corresponding to the | |
programmed baud rate of the channel to be serviced, or at a somewhat faster | |
optimized rate. The multiplexer connection and input poll must run | |
continuously, but it may operate much more slowly, as the only requirement is | |
that it must not present a perceptible lag to human input. It is coscheduled | |
with the process clock to permit idling. The poll unit is hidden by | |
disabling it, so as to present a logical picture of the multiplexer to the | |
user. | |
The TCI does not use any units, but a dummy one is defined to satisfy SCP | |
requirements. | |
Implementation notes: | |
1. There are no units corresponding to the auxiliary receive channels. This | |
is because reception isn't scheduled on these channels but instead occurs | |
concurrently with the main channel that is connected to the auxiliary | |
channels. | |
*/ | |
static UNIT atcd_unit [UNIT_COUNT] = { | |
{ UDATA (&line_service, TT_MODE_7P | UNIT_LOCALACK | UNIT_CAPSLOCK, 0) }, | |
{ UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, | |
{ UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, | |
{ UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, | |
{ UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, | |
{ UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, | |
{ UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, | |
{ UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, | |
{ UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, | |
{ UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, | |
{ UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, | |
{ UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, | |
{ UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, | |
{ UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, | |
{ UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, | |
{ UDATA (&line_service, TT_MODE_7B | UNIT_LOCALACK, 0) }, | |
{ UDATA (&poll_service, UNIT_ATTABLE | UNIT_DIS | UNIT_IDLE, 0), POLL_TIME } /* multiplexer poll unit */ | |
}; | |
static UNIT atcc_unit [] = { /* a dummy unit to satisfy SCP requirements */ | |
{ UDATA (NULL, 0, 0) } | |
}; | |
/* Register lists. | |
The internal state of the TDI and TCI are exposed to the user and to ensure | |
that SAVE and RESTORE pick up the values. The user may set FTIME explicitly | |
as needed to accommodate software that does not work with the default | |
setting. | |
Implementation notes: | |
1. The TCI control and status line register definitions use the VM-defined | |
FBDATA macro. This macro defines a flag that is replicated in the same | |
bit position in each element of an array. | |
*/ | |
static REG atcd_reg [] = { | |
/* Macro Name Location Radix Width Offset Depth Flags */ | |
/* ------ ------ ------------------- ----- ----- ------ ---------------- --------------- */ | |
{ ORDATA (CNTL, tdi_control_word, 16), REG_FIT }, | |
{ ORDATA (STAT, tdi_status_word, 16), REG_FIT }, | |
{ ORDATA (READ, tdi_read_word, 16), REG_X | REG_FIT }, | |
{ ORDATA (WRITE, tdi_write_word, 16), REG_X | REG_FIT }, | |
{ FLDATA (FLAG, tdi_data_flag, 0) }, | |
{ FLDATA (MASK, tdi_interrupt_mask, 0) }, | |
{ DRDATA (FTIME, fast_data_time, 24), PV_LEFT }, | |
{ BRDATA (RSTAT, recv_status, 8, 16, RECV_CHAN_COUNT) }, | |
{ BRDATA (RPARM, recv_param, 8, 16, RECV_CHAN_COUNT) }, | |
{ BRDATA (RBUFR, recv_buffer, 8, 16, RECV_CHAN_COUNT), REG_X }, | |
{ BRDATA (SSTAT, send_status, 8, 16, SEND_CHAN_COUNT) }, | |
{ BRDATA (SPARM, send_param, 8, 16, SEND_CHAN_COUNT) }, | |
{ BRDATA (SBUFR, send_buffer, 8, 16, SEND_CHAN_COUNT), REG_X }, | |
{ FLDATA (POLL, atc_is_polling, 0), REG_HRO }, | |
DIB_REGS (atcd_dib), | |
{ NULL } | |
}; | |
static REG atcc_reg [] = { | |
/* Macro Name Location Width Offset Depth Flags */ | |
/* ------ ------ ------------------- ----- ------ ---------- ------- */ | |
{ ORDATA (CNTL, tci_control_word, 16), REG_FIT }, | |
{ ORDATA (STAT, tci_status_word, 16), REG_FIT }, | |
{ DRDATA (CNTR, tci_cntr, 4) }, | |
{ FLDATA (SCAN, tci_scan, 0) }, | |
{ FLDATA (MASK, tci_interrupt_mask, 0) }, | |
{ FBDATA (C2, cntl_status, 5, TERM_COUNT, PV_RZRO) }, | |
{ FBDATA (C1, cntl_status, 4, TERM_COUNT, PV_RZRO) }, | |
{ FBDATA (S2, cntl_status, 1, TERM_COUNT, PV_RZRO) }, | |
{ FBDATA (S1, cntl_status, 0, TERM_COUNT, PV_RZRO) }, | |
{ FBDATA (ES2, cntl_param, 3, TERM_COUNT, PV_RZRO) }, | |
{ FBDATA (ES1, cntl_param, 2, TERM_COUNT, PV_RZRO) }, | |
{ FBDATA (MS2, cntl_param, 1, TERM_COUNT, PV_RZRO) }, | |
{ FBDATA (MS1, cntl_param, 0, TERM_COUNT, PV_RZRO) }, | |
DIB_REGS (atcc_dib), | |
{ NULL } | |
}; | |
/* Modifier lists */ | |
typedef enum { | |
Fast_Time, | |
Real_Time, | |
Terminal, | |
Diagnostic | |
} DEVICE_MODES; | |
static MTAB atcd_mod [] = { | |
/* Mask Value Match Value Print String Match String Validation Display Descriptor */ | |
/* ------------- ------------- ---------------- ------------ ---------- ------- ---------- */ | |
{ UNIT_MODEM, UNIT_MODEM, "data set", NULL, NULL, NULL, NULL }, | |
{ UNIT_MODEM, 0, "direct", NULL, NULL, NULL, NULL }, | |
{ UNIT_LOCALACK, UNIT_LOCALACK, "local ENQ/ACK", "LOCALACK", NULL, NULL, NULL }, | |
{ UNIT_LOCALACK, 0, "remote ENQ/ACK", "REMOTEACK", NULL, NULL, NULL }, | |
{ UNIT_CAPSLOCK, UNIT_CAPSLOCK, "CAPS LOCK down", "CAPSLOCK", NULL, NULL, NULL }, | |
{ UNIT_CAPSLOCK, 0, "CAPS LOCK up", "NOCAPSLOCK", NULL, NULL, NULL }, | |
{ TT_MODE, TT_MODE_UC, "UC output", "UC", NULL, NULL, NULL }, | |
{ TT_MODE, TT_MODE_7B, "7b output", "7B", NULL, NULL, NULL }, | |
{ TT_MODE, TT_MODE_7P, "7p output", "7P", NULL, NULL, NULL }, | |
{ TT_MODE, TT_MODE_8B, "8b output", "8B", NULL, NULL, NULL }, | |
/* Entry Flags Value Print String Match String Validation Display Descriptor */ | |
/* -------------------- ----------- ------------- ------------ --------------- ---------------- ------------------- */ | |
{ MTAB_XUN | MTAB_NC, 0, "LOG", "LOG", &tmxr_set_log, &tmxr_show_log, (void *) &atcd_mdsc }, | |
{ MTAB_XUN | MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, (void *) &atcd_mdsc }, | |
{ MTAB_XUN, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, (void *) &atcd_mdsc }, | |
{ MTAB_XDV, Fast_Time, NULL, "FASTTIME", &atc_set_mode, NULL, (void *) &atcd_dev }, | |
{ MTAB_XDV, Real_Time, NULL, "REALTIME", &atc_set_mode, NULL, (void *) &atcd_dev }, | |
{ MTAB_XDV, Terminal, NULL, "TERMINAL", &atc_set_mode, NULL, (void *) &atcd_dev }, | |
{ MTAB_XDV, Diagnostic, NULL, "DIAGNOSTIC", &atc_set_mode, NULL, (void *) &atcd_dev }, | |
{ MTAB_XDV, 0, "MODES", NULL, NULL, &atc_show_mode, (void *) &atcd_dev }, | |
{ MTAB_XDV, 0, "", NULL, NULL, &atc_show_status, (void *) &atcd_mdsc }, | |
{ MTAB_XDV | MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat, (void *) &atcd_mdsc }, | |
{ MTAB_XDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, (void *) &atcd_mdsc }, | |
{ MTAB_XDV, VAL_DEVNO, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &atcd_dib }, | |
{ MTAB_XDV, VAL_INTMASK, "INTMASK", "INTMASK", &hp_set_dib, &hp_show_dib, (void *) &atcd_dib }, | |
{ MTAB_XDV, VAL_INTPRI, "INTPRI", "INTPRI", &hp_set_dib, &hp_show_dib, (void *) &atcd_dib }, | |
{ MTAB_XDV | MTAB_NMO, 1, NULL, "ENABLED", &atc_set_endis, NULL, NULL }, | |
{ MTAB_XDV | MTAB_NMO, 0, NULL, "DISABLED", &atc_set_endis, NULL, NULL }, | |
{ 0 } | |
}; | |
static MTAB atcc_mod [] = { | |
/* Entry Flags Value Print String Match String Validation Display Descriptor */ | |
/* ----------- ----------- ------------ ------------ ------------- -------------- ------------------ */ | |
{ MTAB_XDV, Terminal, NULL, "TERMINAL", &atc_set_mode, NULL, (void *) &atcc_dev }, | |
{ MTAB_XDV, Diagnostic, NULL, "DIAGNOSTIC", &atc_set_mode, NULL, (void *) &atcc_dev }, | |
{ MTAB_XDV, 1, "MODES", NULL, NULL, &atc_show_mode, (void *) &atcc_dev }, | |
{ MTAB_XDV, VAL_DEVNO, "DEVNO", "DEVNO", &hp_set_dib, &hp_show_dib, (void *) &atcc_dib }, | |
{ MTAB_XDV, VAL_INTMASK, "INTMASK", "INTMASK", &hp_set_dib, &hp_show_dib, (void *) &atcc_dib }, | |
{ MTAB_XDV, VAL_INTPRI, "INTPRI", "INTPRI", &hp_set_dib, &hp_show_dib, (void *) &atcc_dib }, | |
{ 0 } | |
}; | |
/* Debugging trace lists */ | |
static DEBTAB atcd_deb [] = { | |
{ "CSRW", DEB_CSRW }, /* Interface control, status, read, and write actions */ | |
{ "SERV", DEB_SERV }, /* Channel unit service scheduling calls */ | |
{ "PSERV", DEB_PSERV }, /* Poll unit service scheduling calls */ | |
{ "XFER", DEB_XFER }, /* Data receptions and transmissions */ | |
{ "IOBUS", DEB_IOB }, /* Interface I/O bus signals and data words */ | |
{ NULL, 0 } | |
}; | |
static DEBTAB atcc_deb [] = { | |
{ "CSRW", DEB_CSRW }, /* Interface control, status, read, and write actions */ | |
{ "PSERV", DEB_PSERV }, /* Poll unit service scheduling calls */ | |
{ "XFER", DEB_XFER }, /* Control and status line changes */ | |
{ "IOBUS", DEB_IOB }, /* Interface I/O bus signals and data words */ | |
{ NULL, 0 } | |
}; | |
/* Device descriptors. | |
Both devices may be disabled. However, we want to be able to disable the TDI | |
while it is polling for the simulation console, which the standard SCP | |
routine will not do (it refuses if any unit is active). So we define our own | |
DISABLED and ENABLED modifiers and a validation routine that sets or clears | |
the DEV_DIS flag and then calls atcd_reset. The reset routine cancels or | |
reenables the poll as indicated. | |
Implementation notes: | |
1. The ATCD device does not specify the DEV_DISABLE flag to avoid the | |
DISABLED and ENABLED modifiers from being listed twice for a SHOW ATCD | |
MODIFIERS command. SIMH 3.9 tested for user-defined ENABLED/DISABLED | |
modifiers and skipped the printing that results from specifying | |
DEV_DISABLE. SIMH 4.0 no longer does this, so we omit the flag to | |
suppress the duplicate printing (the flag is otherwise used only to | |
validate the SET DISABLED command). | |
*/ | |
DEVICE atcd_dev = { | |
"ATCD", /* device name */ | |
atcd_unit, /* unit array */ | |
atcd_reg, /* register array */ | |
atcd_mod, /* modifier array */ | |
UNIT_COUNT, /* number of units */ | |
10, /* address radix */ | |
PA_WIDTH, /* address width */ | |
1, /* address increment */ | |
8, /* data radix */ | |
DV_WIDTH, /* data width */ | |
&tmxr_ex, /* examine routine */ | |
&tmxr_dep, /* deposit routine */ | |
&atcd_reset, /* reset routine */ | |
NULL, /* boot routine */ | |
&atcd_attach, /* attach routine */ | |
&atcd_detach, /* detach routine */ | |
&atcd_dib, /* device information block pointer */ | |
DEV_DEBUG, /* device flags */ | |
0, /* debug control flags */ | |
atcd_deb, /* debug flag name array */ | |
NULL, /* memory size change routine */ | |
NULL }; /* logical device name */ | |
DEVICE atcc_dev = { | |
"ATCC", /* device name */ | |
atcc_unit, /* unit array */ | |
atcc_reg, /* register array */ | |
atcc_mod, /* modifier array */ | |
1, /* number of units */ | |
10, /* address radix */ | |
PA_WIDTH, /* address width */ | |
1, /* address increment */ | |
8, /* data radix */ | |
DV_WIDTH, /* data width */ | |
NULL, /* examine routine */ | |
NULL, /* deposit routine */ | |
&atcc_reset, /* reset routine */ | |
NULL, /* boot routine */ | |
NULL, /* attach routine */ | |
NULL, /* detach routine */ | |
&atcc_dib, /* device information block pointer */ | |
DEV_DEBUG | DEV_DISABLE, /* device flags */ | |
0, /* debug control flags */ | |
atcc_deb, /* debug flag name array */ | |
NULL, /* memory size change routine */ | |
NULL }; /* logical device name */ | |
/* ATC local SCP support routines */ | |
/* TDI interface. | |
The interface is installed on the IOP bus and receives direct I/O commands | |
from the IOP. In simulation, the asserted signals on the bus are represented | |
as bits in the inbound_signals set. Each signal is processed sequentially in | |
numerical order, and a set of similar outbound_signals is assembled and | |
returned to the caller, simulating assertion of the corresponding backplane | |
signals. | |
Before a channel can receive or send, it must be configured. The number of | |
the channel to configure is set via a CIO instruction, followed by parameters | |
for baud rate and character size via WIO instructions. Data to be sent is | |
passed to the interface via WIO, while received data is picked up with RIO | |
instructions. | |
When a channel has completed sending or receiving a character, it will set | |
its completion flag. If the TDI data flag is clear, indicating that all | |
prior interrupts have been serviced, a scan of the serviced channel is made | |
to see if the channel is enabled to interrupt. If it is, the TDI data flag | |
will be set, the channel flag will be cleared, and an interrupt will be | |
requested. When the interrupt is serviced and acknowledged, the flag will be | |
cleared, and the scan will continue to look for other channel flags. | |
The status word is set during the scan to reflect the interrupting channel | |
status. If status bit 3 (DST_COMPLETE) is clear, then status bits 5, 6, and | |
7 (DST_SEND_IRQ, DST_CHAR_LOST, and DST_BREAK) retain their values from the | |
prior send or receive interrupt. | |
Implementation notes: | |
1. In hardware, the DIO OK status bit (bit 1) is denied when a store to the | |
recirculating memory is pending and is reasserted once the designated | |
channel rotates into the window and the parameter or data is stored. The | |
duration of the denial varies from 0 to 69.44 microseconds, depending on | |
the location of the window in memory when DWRITESTB is asserted. In | |
simulation, DIO OK is always asserted. | |
2. Receipt of a DRESETINT signal clears the interrupt request and active | |
flip-flops but does not cancel a request pending but not yet serviced by | |
the IOP. However, when the IOP does service the request by asserting | |
INTPOLLIN, the interface routine returns INTPOLLOUT, which will cancel | |
the request. | |
*/ | |
static SIGNALS_DATA atcd_interface (DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value) | |
{ | |
INBOUND_SIGNAL signal; | |
INBOUND_SET working_set = inbound_signals; | |
HP_WORD outbound_value = 0; | |
OUTBOUND_SET outbound_signals = NO_SIGNALS; | |
dprintf (atcd_dev, DEB_IOB, "Received data %06o with signals %s\n", | |
inbound_value, fmt_bitset (inbound_signals, inbound_format)); | |
while (working_set) { /* while signals remain */ | |
signal = IONEXTSIG (working_set); /* isolate the next signal */ | |
switch (signal) { /* dispatch the I/O signal */ | |
case DCONTSTB: | |
dprintf (atcd_dev, DEB_CSRW, (inbound_value & DCN_ENABLE | |
? "Control is %s | channel %u\n" | |
: "Control is %s\n"), | |
fmt_bitset (inbound_value, tdi_control_format), | |
DCN_CHAN (inbound_value)); | |
tdi_control_word = inbound_value; /* save the control word */ | |
if (tdi_control_word & DCN_MR) /* if master reset is requested */ | |
tdi_master_reset (); /* then perform an I/O reset */ | |
if (tdi_control_word & DCN_IRQ_RESET) /* if reset interrupt is requested */ | |
dibptr->interrupt_request = CLEAR; /* then clear the interrupt request */ | |
if (tdi_control_word & DCN_ENABLE) /* if output is enabled */ | |
store (tdi_control_word, tdi_write_word); /* then store the parameter or data word */ | |
if (tdi_control_word & DCN_ACKN) { /* if acknowledge interrupt is requested */ | |
tdi_data_flag = CLEAR; /* then clear the data flag */ | |
scan_channels (SCAN_ALL); /* scan all channels for a new interrupt request */ | |
} | |
break; | |
case DSTATSTB: | |
tdi_status_word |= DST_DIO_OK; /* the interface is always ready for commands */ | |
if (dibptr->interrupt_request == SET) /* reflect the interrupt request value */ | |
tdi_status_word |= DST_IRQ; /* in the status word */ | |
else /* to indicate */ | |
tdi_status_word &= ~DST_IRQ; /* whether or not a request is pending */ | |
if (tdi_data_flag == SET) /* reflect the data flag value */ | |
tdi_status_word |= DST_COMPLETE; /* in the status word */ | |
else /* to indicate */ | |
tdi_status_word &= ~DST_COMPLETE; /* whether or not a channel has completed */ | |
outbound_value = tdi_status_word; /* return the status word */ | |
dprintf (atcd_dev, DEB_CSRW, "Status is %s\n", | |
fmt_bitset (outbound_value, tdi_status_format)); | |
break; | |
case DWRITESTB: | |
tdi_write_word = inbound_value; /* save the data or parameter word */ | |
if (DPRINTING (atcd_dev, DEB_CSRW)) | |
if (inbound_value & DPI_IS_PARAM) | |
hp_debug (&atcd_dev, DEB_CSRW, "Parameter is %s%u bits | %u baud\n", | |
fmt_bitset (inbound_value, tdi_parameter_format), | |
bits_per_char [DPI_CHAR_SIZE (inbound_value)], | |
BAUD_RATE (inbound_value)); | |
else | |
hp_debug (&atcd_dev, DEB_CSRW, "Output data is %s%04o\n", | |
fmt_bitset (inbound_value, tdi_output_data_format), | |
DDS_DATA (inbound_value)); | |
break; | |
case DREADSTB: | |
outbound_value = tdi_read_word; /* return the data word */ | |
dprintf (atcd_dev, DEB_CSRW, "Input data is channel %u | %s%04o\n", | |
DDR_TO_CHAN (outbound_value), | |
fmt_bitset (outbound_value, tdi_input_data_format), | |
DDR_TO_DATA (outbound_value)); | |
break; | |
case DSETINT: | |
dibptr->interrupt_request = SET; /* request an interrupt */ | |
if (tdi_interrupt_mask) /* if the interrupt mask is satisfied */ | |
outbound_signals |= INTREQ; /* then assert the INTREQ signal */ | |
break; | |
case DRESETINT: | |
dibptr->interrupt_active = CLEAR; /* reset the interrupt active flip-flop */ | |
break; | |
case INTPOLLIN: | |
if (dibptr->interrupt_request) { /* if a request is pending */ | |
dibptr->interrupt_request = CLEAR; /* then clear it */ | |
dibptr->interrupt_active = SET; /* and mark it now active */ | |
outbound_signals |= INTACK; /* acknowledge the interrupt */ | |
outbound_value = dibptr->device_number; /* and return our device number */ | |
} | |
else /* otherwise the request has been reset */ | |
outbound_signals |= INTPOLLOUT; /* so let the IOP know to cancel it */ | |
break; | |
case DSETMASK: | |
if (dibptr->interrupt_mask == INTMASK_E) /* if the mask is always enabled */ | |
tdi_interrupt_mask = SET; /* then set the mask flip-flop */ | |
else /* otherwise */ | |
tdi_interrupt_mask = D_FF (dibptr->interrupt_mask /* set the mask flip-flop if the mask bit */ | |
& inbound_value); /* is present in the mask value */ | |
if (tdi_interrupt_mask && dibptr->interrupt_request) /* if the mask is enabled and a request is pending */ | |
outbound_signals |= INTREQ; /* then assert INTREQ */ | |
break; | |
case DSTARTIO: /* not used by this interface */ | |
case XFERERROR: /* not used by this interface */ | |
case ACKSR: /* not used by this interface */ | |
case TOGGLESR: /* not used by this interface */ | |
case TOGGLESIOOK: /* not used by this interface */ | |
case TOGGLEINXFER: /* not used by this interface */ | |
case TOGGLEOUTXFER: /* not used by this interface */ | |
case READNEXTWD: /* not used by this interface */ | |
case PREADSTB: /* not used by this interface */ | |
case PWRITESTB: /* not used by this interface */ | |
case PCMD1: /* not used by this interface */ | |
case PCONTSTB: /* not used by this interface */ | |
case PSTATSTB: /* not used by this interface */ | |
case DEVNODB: /* not used by this interface */ | |
case SETINT: /* not used by this interface */ | |
case EOT: /* not used by this interface */ | |
case SETJMP: /* not used by this interface */ | |
case CHANSO: /* not used by this interface */ | |
case PFWARN: /* not used by this interface */ | |
break; | |
} | |
IOCLEARSIG (working_set, signal); /* remove the current signal from the set */ | |
} | |
dprintf (atcd_dev, DEB_IOB, "Returned data %06o with signals %s\n", | |
outbound_value, fmt_bitset (outbound_signals, outbound_format)); | |
return IORETURN (outbound_signals, outbound_value); /* return the outbound signals and value */ | |
} | |
/* TCI interface. | |
The interface is installed on the IOP bus and receives direct I/O commands | |
from the IOP. In simulation, the asserted signals on the bus are represented | |
as bits in the inbound_signals set. Each signal is processed sequentially in | |
numerical order, and a set of similar outbound_signals is assembled and | |
returned to the caller, simulating assertion of the corresponding backplane | |
signals. For this interface, a read order executes identically to a test | |
order, and a write order is ignored. | |
The control word contains three independent enables that affect the | |
interpretation of the rest of the word. Bit 3 (CCN_UPDATE) must be set to | |
enable storing bits 12-15 (CCN_ES2/1 and CCN_S2/1) into the state RAM. Bits | |
8 (CCN_EC2) and 9 (CCN_EC1) must be set to enable storing bits 10 (CCN_C2) | |
and 11 (CCN_C1), respectively, into the addressable latch. If none of these | |
enables are set, then only bits 0-2 are interpreted. | |
Implementation notes: | |
1. The cntl_status array contains the values for the serial device control | |
and status lines. The line bit positions in the array correspond to the | |
C2/C1 and S2/S1 positions in the control word. | |
2. A control word write directed to a given channel sets that channel's | |
UNIT_MODEM flag to indicate that the serial line status should be | |
updated at each input poll service. | |
3. The terminal multiplexer library will disconnect an associated Telnet | |
session if DTR is dropped. | |
4. Receipt of a DRESETINT signal clears the interrupt request and active | |
flip-flops but does not cancel a request pending but not yet serviced by | |
the IOP. However, when the IOP does service the request by asserting | |
INTPOLLIN, the interface routine returns INTPOLLOUT, which will cancel | |
the request. | |
*/ | |
static SIGNALS_DATA atcc_interface (DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value) | |
{ | |
INBOUND_SIGNAL signal; | |
INBOUND_SET working_set = inbound_signals; | |
HP_WORD outbound_value = 0; | |
OUTBOUND_SET outbound_signals = NO_SIGNALS; | |
int32 set_lines, clear_lines; | |
dprintf (atcc_dev, DEB_IOB, "Received data %06o with signals %s\n", | |
inbound_value, fmt_bitset (inbound_signals, inbound_format)); | |
while (working_set) { /* while signals remain */ | |
signal = IONEXTSIG (working_set); /* isolate the next signal */ | |
switch (signal) { /* dispatch the I/O signal */ | |
case DCONTSTB: | |
tci_cntr = CCN_CHAN (inbound_value); /* set the counter to the target channel */ | |
dprintf (atcc_dev, DEB_CSRW, "Control is channel %u | %s\n", | |
tci_cntr, fmt_bitset (inbound_value, tci_control_format)); | |
tci_control_word = inbound_value; /* save the control word */ | |
line_unit [tci_cntr].flags |= UNIT_MODEM; /* set the modem control flag on this unit */ | |
if (tci_control_word & CCN_MR) /* if master reset is requested */ | |
tci_master_reset (); /* then perform an I/O reset */ | |
if (tci_control_word & CCN_IRQ_RESET) /* if reset interrupt is requested */ | |
dibptr->interrupt_request = CLEAR; /* then clear the interrupt request */ | |
cntl_status [tci_cntr] = cntl_status [tci_cntr] /* set the control lines */ | |
& ~CCN_ECX (tci_control_word) /* that are enabled for output */ | |
| CCN_CX_MASK /* to the control bits */ | |
& CCN_ECX (tci_control_word) /* that are enabled */ | |
& tci_control_word; /* in the control word */ | |
dprintf (atcc_dev, DEB_XFER, "Channel %u line status is %s\n", | |
tci_cntr, fmt_bitset (cntl_status [tci_cntr], tci_line_format)); | |
if (atcc_dev.flags & DEV_DIAG) { /* if the interface is in diagnostic mode */ | |
cntl_status [tci_cntr ^ 1] = /* then loop the control lines */ | |
cntl_status [tci_cntr ^ 1] & ~CCN_SX_MASK /* back to the alternate channel */ | |
| CCN_CX (cntl_status [tci_cntr]); /* from the selected channel */ | |
dprintf (atcc_dev, DEB_XFER, "Channel %u line status is %s\n", | |
tci_cntr ^ 1, fmt_bitset (cntl_status [tci_cntr ^ 1], tci_line_format)); | |
} | |
else if (tci_control_word & CCN_ECX_MASK) { /* otherwise if either control line is enabled */ | |
set_lines = 0; /* then prepare the multiplexer library to set */ | |
clear_lines = 0; /* the modem status (either real or simulated) */ | |
if (tci_control_word & CCN_EC2) /* if control line 2 is enabled for output */ | |
if (RTS & cntl_status [tci_cntr]) /* then if the line is asserted */ | |
set_lines |= TMXR_MDM_RTS; /* then set the RTS line up */ | |
else /* otherwise */ | |
clear_lines |= TMXR_MDM_RTS; /* set it down */ | |
if (tci_control_word & CCN_EC1) /* if control line 1 is enabled for output */ | |
if (DTR & cntl_status [tci_cntr]) /* then if the line is asserted */ | |
set_lines |= TMXR_MDM_DTR; /* then set the DTR line up */ | |
else { /* otherwise */ | |
clear_lines |= TMXR_MDM_DTR; /* set it down */ | |
if (cntl_status [tci_cntr] & DCD) /* setting DTR down will disconnect the channel */ | |
dprintf (atcc_dev, DEB_CSRW, "Channel %u disconnected by DTR drop\n", | |
tci_cntr); | |
} | |
tmxr_set_get_modem_bits (&atcd_ldsc [tci_cntr], /* tell the multiplexer library */ | |
set_lines, clear_lines, /* to set or clear the indicated lines */ | |
NULL); /* and omit returning the current status */ | |
} | |
if (tci_control_word & CCN_UPDATE) /* if the status output is enabled */ | |
cntl_param [tci_cntr] = tci_control_word /* then store the status line enables and states */ | |
& CCN_STAT_MASK; /* in the parameter RAM */ | |
tci_scan = D_FF (tci_control_word & CCN_SCAN); /* set or clear the scan flip-flop as directed */ | |
if (tci_scan) /* if scanning is enabled */ | |
scan_status (); /* then look for channel status changes */ | |
break; | |
case DREADSTB: /* RIO and TIO return the same value */ | |
case DSTATSTB: | |
tci_status_word = CST_DIO_OK | CST_ON /* form the status word */ | |
| CST_CHAN (tci_cntr) | |
| cntl_param [tci_cntr] & CST_ESX_MASK | |
| cntl_status [tci_cntr] & CST_SX_MASK | |
| scan_status (); | |
if (dibptr->interrupt_request == SET) /* reflect the interrupt request value */ | |
tci_status_word |= CST_IRQ; /* in the status word */ | |
outbound_value = tci_status_word; /* return the status word */ | |
dprintf (atcc_dev, DEB_CSRW, "Status is channel %u | %s\n", | |
tci_cntr, fmt_bitset (outbound_value, tci_status_format)); | |
break; | |
case DSETINT: | |
dibptr->interrupt_request = SET; /* request an interrupt */ | |
if (tci_interrupt_mask) /* if the interrupt mask is satisfied */ | |
outbound_signals |= INTREQ; /* then assert the INTREQ signal */ | |
break; | |
case DRESETINT: | |
dibptr->interrupt_active = CLEAR; /* reset the interrupt active flip-flop */ | |
break; | |
case INTPOLLIN: | |
if (dibptr->interrupt_request) { /* if a request is pending */ | |
dibptr->interrupt_request = CLEAR; /* then clear it */ | |
dibptr->interrupt_active = SET; /* and mark it now active */ | |
outbound_signals |= INTACK; /* acknowledge the interrupt */ | |
outbound_value = dibptr->device_number; /* and return our device number */ | |
} | |
else /* otherwise the request has been reset */ | |
outbound_signals |= INTPOLLOUT; /* so let the IOP know to cancel it */ | |
break; | |
case DSETMASK: | |
if (dibptr->interrupt_mask == INTMASK_E) /* if the mask is always enabled */ | |
tci_interrupt_mask = SET; /* then set the mask flip-flop */ | |
else /* otherwise */ | |
tci_interrupt_mask = D_FF (dibptr->interrupt_mask /* set the mask flip-flop if the mask bit */ | |
& inbound_value); /* is present in the mask value */ | |
if (tci_interrupt_mask && dibptr->interrupt_request) /* if the mask is enabled and a request is pending */ | |
outbound_signals |= INTREQ; /* then assert INTREQ */ | |
break; | |
case DWRITESTB: /* not used by this interface */ | |
case DSTARTIO: /* not used by this interface */ | |
case XFERERROR: /* not used by this interface */ | |
case ACKSR: /* not used by this interface */ | |
case TOGGLESR: /* not used by this interface */ | |
case TOGGLESIOOK: /* not used by this interface */ | |
case TOGGLEINXFER: /* not used by this interface */ | |
case TOGGLEOUTXFER: /* not used by this interface */ | |
case READNEXTWD: /* not used by this interface */ | |
case PREADSTB: /* not used by this interface */ | |
case PWRITESTB: /* not used by this interface */ | |
case PCMD1: /* not used by this interface */ | |
case PCONTSTB: /* not used by this interface */ | |
case PSTATSTB: /* not used by this interface */ | |
case DEVNODB: /* not used by this interface */ | |
case SETINT: /* not used by this interface */ | |
case EOT: /* not used by this interface */ | |
case SETJMP: /* not used by this interface */ | |
case CHANSO: /* not used by this interface */ | |
case PFWARN: /* not used by this interface */ | |
break; | |
} | |
IOCLEARSIG (working_set, signal); /* remove the current signal from the set */ | |
} | |
dprintf (atcc_dev, DEB_IOB, "Returned data %06o with signals %s\n", | |
outbound_value, fmt_bitset (outbound_signals, outbound_format)); | |
return IORETURN (outbound_signals, outbound_value); /* return the outbound signals and value */ | |
} | |
/* Enable or disable the TDI. | |
This validation routine is entered with "value" set to 1 for an ENABLE and 0 | |
for a DISABLE, and "cptr" pointing to the next character after the keyword. | |
If the TDI is already enabled or disabled, respectively, the routine returns | |
with no further action. Otherwise, if "value" is 1, the device is enabled by | |
clearing the DEV_DIS flag, and the polling flag is set TRUE to indicate that | |
the TDI is polling for the simulation console. If "value" is 0, a check is | |
made to see if the TDI is listening for connections. If it is, the disable | |
request is rejected; the device must be detached first. Otherwise, the | |
device is disabled by setting the DEV_DIS flag, and the polling flag is set | |
FALSE to indicate that the TDI is no longer polling for the simulation | |
console (the PCLK device will take over when the polling flag is FALSE). | |
In either case, the device is reset, which will restart or cancel the poll, | |
as appropriate. | |
*/ | |
static t_stat atc_set_endis (UNIT *uptr, int32 value, CONST char *cptr, void *desc) | |
{ | |
if (value) /* if this is an ENABLE request */ | |
if (atcd_dev.flags & DEV_DIS) { /* then if the device is disabled */ | |
atcd_dev.flags &= ~DEV_DIS; /* then reenable it */ | |
atc_is_polling = TRUE; /* and set the polling flag */ | |
} | |
else /* otherwise the device is already enabled */ | |
return SCPE_OK; /* so there's nothing to do */ | |
else /* otherwise this is a DISABLE request */ | |
if (atcd_dev.flags & DEV_DIS) /* so if the device is already disabled */ | |
return SCPE_OK; /* so there's nothing to do */ | |
else if (poll_unit.flags & UNIT_ATT) /* otherwise if the poll unit is still attached */ | |
return SCPE_NOFNC; /* then report that the command failed */ | |
else { /* otherwise */ | |
atcd_dev.flags |= DEV_DIS; /* disable the device */ | |
atc_is_polling = FALSE; /* and clear the polling flag */ | |
} | |
return atcd_reset (&atcd_dev); /* reset the TDI and restart or cancel polling */ | |
} | |
/* Set the device modes. | |
The device flag implied by the DEVICE_MODES "value" passed to the routine is | |
set or cleared in the device specified by the "desc" parameter. The unit and | |
character pointers are not used. | |
Implementation notes: | |
1. In hardware, terminals and modems must be disconnected from the ATC and | |
loopback cables installed between each pair or channels when the | |
diagnostic is run. In simulation, setting DIAG mode detaches any | |
existing listening port, so that Telnet sessions will not interfere with | |
the internal loopback connections from the send to the receive channels. | |
*/ | |
static t_stat atc_set_mode (UNIT *uptr, int32 value, CONST char *cptr, void *desc) | |
{ | |
DEVICE * const dptr = (DEVICE *) desc; /* a pointer to the device */ | |
switch ((DEVICE_MODES) value) { /* dispatch based on the mode to set */ | |
case Fast_Time: /* entering optimized timing mode */ | |
dptr->flags &= ~DEV_REALTIME; /* so clear the real-time flag */ | |
break; | |
case Real_Time: /* entering realistic timing mode */ | |
dptr->flags |= DEV_REALTIME; /* so set the flag */ | |
break; | |
case Terminal: /* entering terminal mode */ | |
dptr->flags &= ~DEV_DIAG; /* so clear the diagnostic flag */ | |
break; | |
case Diagnostic: /* entering the diagnostic mode */ | |
dptr->flags |= DEV_DIAG; /* so set the flag */ | |
if (dptr == &atcd_dev) /* if we're setting the TDI mode */ | |
atcd_detach (&poll_unit); /* then detach any existing connections */ | |
break; | |
} | |
return SCPE_OK; | |
} | |
/* Show the device modes. | |
The output stream and device pointer are passed in the "st" and "desc" | |
parameters, respectively. If "value" is 0, then all of the flags are checked | |
for the TDI. If "value" is 1, then only the diagnostic flag is checked for | |
the TCI. The unit pointer is not used. | |
*/ | |
static t_stat atc_show_mode (FILE *st, UNIT *uptr, int32 value, CONST void *desc) | |
{ | |
const DEVICE * const dptr = (const DEVICE *) desc; /* a pointer to the device */ | |
if (value == 0) /* if this is the TDI */ | |
if (dptr->flags & DEV_REALTIME) /* then if the real-time flag is set */ | |
fputs ("realistic timing, ", st); /* then report that we are using realistic timing */ | |
else /* otherwise */ | |
fputs ("fast timing, ", st); /* report that we are using optimized timing */ | |
if (dptr->flags & DEV_DIAG) /* if the diagnostic flag is set */ | |
fputs ("diagnostic mode", st); /* then report that we're in loopback mode */ | |
else /* otherwise */ | |
fputs ("terminal mode", st); /* we're in normal (terminal) mode */ | |
return SCPE_OK; | |
} | |
/* Show the TDI device status. | |
The attachment condition and connection count are printed to the stream | |
specified by "st" as part of the ATCD device display. The "desc" parameter | |
is a pointer to the terminal multiplexer library descriptor; the unit pointer | |
and value parameters are not used. | |
*/ | |
static t_stat atc_show_status (FILE *st, UNIT *uptr, int32 value, CONST void *desc) | |
{ | |
if (poll_unit.flags & UNIT_ATT) /* if the poll unit is attached */ | |
fprintf (st, "attached to port %s, ", /* then report it */ | |
poll_unit.filename); /* with the listening port number */ | |
else /* otherwise */ | |
fprintf (st, "not attached, "); /* report the condition */ | |
tmxr_show_summ (st, uptr, value, desc); /* also report the count of connections */ | |
return SCPE_OK; | |
} | |
/* TDI device reset. | |
This routine is called for a RESET or RESET ATCD command. It is the | |
simulation equivalent of the IORESET signal, which is asserted by the front | |
panel LOAD and DUMP switches. | |
If a power-on reset (RESET -P) is being done, the poll timer is initialized. | |
In addition, the original FASTTIME setting is restored, in case it's been | |
changed by the user. | |
If the polling flag is set, then start or resynchronize the poll unit with | |
the process clock to enable idling. If the CPU process clock is calibrated, | |
then the poll event service is synchronized with the process clock service. | |
Otherwise, the service time is set up but is otherwise asynchronous with the | |
process clock. | |
If the polling flag is clear, then the poll is stopped, as it's not needed. | |
Implementation notes: | |
1. To synchronize events, the poll must be activated absolutely, as a | |
service event may already be scheduled, and normal activation will not | |
disturb an existing event. | |
*/ | |
static t_stat atcd_reset (DEVICE *dptr) | |
{ | |
tdi_master_reset (); /* perform a master reset */ | |
if (sim_switches & SWMASK ('P')) { /* if this is a power-on reset */ | |
sim_rtcn_init (poll_unit.wait, TMR_ATC); /* then initialize the poll timer */ | |
fast_data_time = FAST_IO_TIME; /* restore the initial fast data time */ | |
atcd_ldsc [0].xmte = 1; /* enable transmission on the system console port */ | |
} | |
if (atc_is_polling) { /* if we're polling for the simulation console */ | |
if (cpu_is_calibrated) /* then if the process clock is calibrated */ | |
poll_unit.wait = sim_activate_time (cpu_pclk_uptr); /* then synchronize with it */ | |
else /* otherwise */ | |
poll_unit.wait = POLL_TIME; /* set up an independent poll time */ | |
sim_activate_abs (&poll_unit, poll_unit.wait); /* restart the poll timer */ | |
} | |
else /* otherwise */ | |
sim_cancel (&poll_unit); /* cancel the poll */ | |
return SCPE_OK; | |
} | |
/* TCI device reset. | |
This routine is called for a RESET or RESET ATCC command. It is the | |
simulation equivalent of the IORESET signal, which is asserted by the front | |
panel LOAD and DUMP switches. | |
If a power-on reset (RESET -P) is being done, then local modem control is | |
established by setting DTR on all channels. This is necessary so that | |
channels not controlled by the TCI will be able to connect (TCI-controlled | |
channels will have their DTR and RTS state set by the MPE TCI initialization | |
routine). | |
*/ | |
static t_stat atcc_reset (DEVICE *dptr) | |
{ | |
uint32 channel; | |
tci_master_reset (); /* perform a master reset */ | |
if (sim_switches & SWMASK ('P')) { /* if this is a power-on reset */ | |
for (channel = 0; channel < TERM_COUNT; channel++) /* then for each terminal channel */ | |
tmxr_set_get_modem_bits (&atcd_ldsc [channel], /* set the DTR line on */ | |
TMXR_MDM_DTR, /* to allow non-TCI channels to connect */ | |
0, NULL); | |
} | |
return SCPE_OK; | |
} | |
/* Attach the TDI to a Telnet listening port. | |
This routine is called by the ATTACH ATCD <port> command to attach the TDI to | |
the listening port indicated by <port>. Logically, it is the ATCD device | |
that is attached; however, SIMH only allows units to be attached. This makes | |
sense for devices such as tape drives, where the attached media is a property | |
of a specific drive. In our case, though, the listening port is a property | |
of the TDI card, not of any given serial line. As ATTACH ATCD is equivalent | |
to ATTACH ATCD0, the port would, by default, be attached to the first channel | |
and be reported there in a SHOW ATCD command. | |
To preserve the logical picture, we attach the port to the Telnet poll unit, | |
which is normally disabled to inhibit its display. Attaching to a disabled | |
unit is not allowed, so we first enable the unit, then attach it, then | |
disable it again. Attachment is reported by the "atc_show_status" routine. | |
A direct attach to the poll unit is allowed only when restoring a previously | |
saved session via the RESTORE command. | |
*/ | |
static t_stat atcd_attach (UNIT *uptr, CONST char *cptr) | |
{ | |
t_stat status; | |
if (atcd_dev.flags & DEV_DIAG) /* if the TDI is in diagnostic mode */ | |
return SCPE_NOFNC; /* then the command is not allowed */ | |
if (uptr != line_unit /* if we're not attaching unit 0 */ | |
&& (uptr != &poll_unit || !(sim_switches & SIM_SW_REST))) /* and not we're not restoring the poll unit */ | |
return SCPE_NOATT; /* then the unit specified is not attachable */ | |
poll_unit.flags &= ~UNIT_DIS; /* enable the poll unit */ | |
status = tmxr_attach (&atcd_mdsc, &poll_unit, cptr); /* and attach it to the specified listening port */ | |
poll_unit.flags |= UNIT_DIS; /* and then disable it again */ | |
return status; | |
} | |
/* Detach the TDI. | |
Normally, this routine is called by the DETACH ATCD command, which is | |
equivalent to DETACH ATCD0. However, it may be called with other units in | |
three cases. | |
A DETACH ALL command will call us for unit 16 (the poll unit) if it is | |
attached. A RESTORE command also will call us for unit 16 if it is attached. | |
In the latter case, the terminal channels will have already been rescheduled | |
as appropriate, so canceling them is skipped. Also, during simulator | |
shutdown, we will be called for units 0-15 (detach_all in scp.c calls the | |
detach routines of all units that do NOT have UNIT_ATTABLE), as well as for | |
unit 16 if it is attached. In all cases, it is imperative that we not reject | |
the request for unit 16; otherwise any remaining device detaches will not be | |
performed. | |
Implementation notes: | |
1. Detaching the multiplexer resets each line first, which flushes the | |
output buffer. This ensures that buffered data that has not been output | |
via a poll_service entry before the DETACH command was issued is written | |
before the line is disconnected. | |
*/ | |
static t_stat atcd_detach (UNIT *uptr) | |
{ | |
uint32 channel; | |
t_stat status = SCPE_OK; | |
if (uptr == line_unit || uptr == &poll_unit) { /* if we're detaching the base unit or poll unit */ | |
status = tmxr_detach (&atcd_mdsc, &poll_unit); /* then detach the listening port */ | |
if ((sim_switches & SIM_SW_REST) == 0) /* if this is not a RESTORE call */ | |
for (channel = 0; channel < TERM_COUNT; channel++) { /* then for each terminal channel */ | |
atcd_ldsc [channel].rcve = FALSE; /* disable reception */ | |
sim_cancel (&line_unit [channel]); /* and cancel any transfer in progress */ | |
} | |
} | |
return status; | |
} | |
/* ATC local utility routines */ | |
/* Request a TDI interrupt. | |
The data flag and interrupt request flip-flops are set. If the interrupt | |
mask permits, the interrupt request is passed to the IOP. | |
*/ | |
static void tdi_set_interrupt (void) | |
{ | |
tdi_data_flag = SET; /* set the data flag */ | |
atcd_dib.interrupt_request = SET; /* request an interrupt */ | |
if (tdi_interrupt_mask) /* if the interrupt mask is satisfied */ | |
iop_assert_INTREQ (&atcd_dib); /* then assert the INTREQ signal to the IOP */ | |
return; | |
} | |
/* TDI master reset. | |
A master reset is generated either by an IORESET signal or a programmed | |
master reset (CIO bit 0 set). It clears any pending or active interrupt, | |
sets the interrupt mask, clears the status word and data flag, and resets all | |
channels to their initial, unconfigured state. | |
Implementation notes: | |
1. In hardware, a master reset sets the Initialize flip-flop. This causes a | |
direct clear of the recirculating memory window registers, thereby | |
clearing each channel's buffer, parameter, and status values as they | |
pass through the window. The flip-flop is cleared when a control word is | |
sent with the master clear bit (CIO bit 0) cleared. A full recirculation | |
takes 69.44 microseconds, so the CPU must allow at least this time for | |
each channel to pass through the window to ensure that all memory | |
locations are reset. In simulation, the clear occurs "instantaneously." | |
*/ | |
static void tdi_master_reset (void) | |
{ | |
uint32 chan; | |
atcd_dib.interrupt_request = CLEAR; /* clear any current */ | |
atcd_dib.interrupt_active = CLEAR; /* interrupt request */ | |
tdi_interrupt_mask = SET; /* set the interrupt mask */ | |
tdi_status_word = 0; /* clear the status word */ | |
tdi_data_flag = CLEAR; /* and the data flag */ | |
for (chan = FIRST_TERM; chan <= LAST_TERM; chan++) { /* for each terminal channel */ | |
recv_buffer [chan] = 0; /* clear the receive data buffer */ | |
recv_param [chan] = 0; /* and parameter */ | |
recv_status [chan] = 0; /* and status */ | |
send_buffer [chan] = 0; /* also clear the send data buffer */ | |
send_param [chan] = 0; /* and parameter */ | |
send_status [chan] = 0; /* and status */ | |
sim_cancel (&line_unit [chan]); /* cancel any transfer in progress */ | |
} | |
for (chan = FIRST_AUX; chan <= LAST_AUX; chan++) { /* for each auxiliary channel */ | |
recv_buffer [chan] = 0; /* clear the receive data buffer */ | |
recv_param [chan] = 0; /* and parameter */ | |
recv_status [chan] = 0; /* and status */ | |
} | |
return; | |
} | |
/* TCI master reset. | |
A master reset is generated either by an IORESET signal or a programmed | |
master reset (CIO bit 0 set). It clears any pending or active interrupt, | |
sets the interrupt mask, clears the control word and channel counter, and | |
resets all channels to their initial, unconfigured state. | |
Implementation notes: | |
1. In hardware, a master reset sets the Status Clear flip-flop. This causes | |
a direct clear of the Control Word Holding Register and enables writing | |
into each location of the addressable latches and state RAM. The | |
flip-flop is reset automatically when the channel counter rolls over. | |
This takes approximately 12 microseconds, so the CPU must allow at least | |
this time before sending new control information. In simulation, the | |
master reset occurs "instantaneously." | |
2. In hardware, the C2 and C1 control line outputs are cleared by a master | |
clear. In simulation, we also clear the S2 and S1 status line input | |
values. This is OK, because they will be reestablished at the next poll | |
service entry. | |
*/ | |
static void tci_master_reset (void) | |
{ | |
uint32 chan; | |
atcc_dib.interrupt_request = CLEAR; /* clear any current */ | |
atcc_dib.interrupt_active = CLEAR; /* interrupt request */ | |
tci_interrupt_mask = SET; /* set the interrupt mask */ | |
tci_control_word = 0; /* clear the control word */ | |
tci_cntr = 0; /* and the channel counter */ | |
for (chan = FIRST_TERM; chan <= LAST_TERM; chan++) { /* for each terminal channel */ | |
cntl_status [chan] = 0; /* clear all serial line values */ | |
cntl_param [chan] = 0; /* and the parameter RAM */ | |
} | |
return; | |
} | |
/* Multiplexer channel service. | |
The channel service routine runs only when there are characters to read or | |
write. It is scheduled either at a realistic rate corresponding to the | |
programmed baud rate of the channel to be serviced, or at a somewhat faster | |
optimized rate. It is entered when a channel buffer is ready for output, | |
when the poll routine determines that there are characters ready for input, | |
or while waiting for an ACK to complete an ENQ/ACK handshake. | |
On entry, the receive channel buffer is checked for a character. If one is | |
not already present, then the terminal multiplexer library is called to | |
retrieve the waiting character. If a valid character is now available, it is | |
processed. If the receive channel has its "diagnose" bit set, the character | |
is also passed to the auxiliary channels. | |
If a received character is not available, then the unit's "wait" field is | |
checked to see if an ACK is expected in reply to an earlier ENQ. If a wait | |
time is present, it is doubled, and the service is rescheduled. However, if | |
the new wait time is longer than the current poll time, service rescheduling | |
is abandoned in favor of the normal poll for received characters. | |
The send channel buffer is then checked for a character to output. If one is | |
present, then if it is an all-mark (sync) character, it is discarded, as the | |
receiver would never see it. Otherwise, if the TDI is in diagnostic mode, | |
then the character is looped back to the associated receive channel by | |
storing it in that channel's receive buffer and then recursively calling the | |
routine for that channel. | |
If the TDI is in terminal mode, then if the channel flag is set for local | |
ENQ/ACK handshaking, and the character is an ENQ, it is discarded, an ACK is | |
stored in the channel's receive buffer, and its reception is scheduled. | |
Otherwise, the character is processed and then transmitted either to the | |
simulation console (if output is to channel 0) or to the terminal multiplexer | |
library for output via Telnet or a serial port on the host machine. If the | |
channel has its "diagnose" bit set, the character is also passed to the | |
auxiliary channels. If an ENQ was transmitted, service is rescheduled to | |
wait for reception of the ACK. | |
If the data flag is clear, the indicated receive and send channels are | |
checked for completion flags. If either is set, an interrupt is requested. | |
If characters remain in the Telnet receive buffer, the service routine is | |
rescheduled to receive the next one. Otherwise, the routine goes idle until | |
the next character is output or the next poll determines that there are | |
characters to receive. | |
Implementation notes: | |
1. The "wait" fields of the channel units are not used (i.e., are set to | |
zero) except when channels are waiting for ACKs. Because we want to | |
retrieve the ACK as quickly as possible but also minimize the load on the | |
host system, we initially wait the normal reception time (fast or | |
realistic) and then double the wait each time it expires without | |
reception. Therefore, a non-zero "wait" value indicates that an ACK is | |
expected. | |
2. Receipt of any character cancels a pending ACK wait, even though it is | |
possible that the character is not an ACK (for example, if the character | |
was received but not processed before the ENQ was sent). | |
3. Calling "tmxr_getc_ln" for channel 0 is OK, as reception is disabled by | |
default and therefore will return 0. | |
4. The send channel buffer will always be non-zero if a character is present | |
(even a NUL) because the data word will have DDS_IS_SEND set. | |
The receive buffer will always be non-zero if a character is present | |
(even a NUL) because characters from the console will have SCPE_KFLAG | |
set, characters from the terminal multiplexer library will have | |
TMXR_VALID set, and characters looped back from sending will have | |
DDS_IS_SEND set. | |
5. Reception of a loopback character is performed immediately because the | |
reception occurs concurrently with transmission. Reception of a locally | |
generated ACK is scheduled with a one-character delay to reflect the | |
remote device transmission delay. | |
6. If storing an ACK locally overwrites a character already present but not | |
yet processed, then the receive routine will set the character lost flag. | |
7. Both TMXR_VALID and SCPE_KFLAG are set on internally generated ACKs only | |
so that a debug trace will record the generation correctly. | |
8. The console library "sim_putchar_s" routine and the terminal multiplexer | |
library "tmxr_putc_ln" routine return SCPE_STALL if they are called when | |
the transmit buffer is full. When called to add the last character to | |
the buffer, the routines return SCPE_OK but also change the "xmte" field | |
of the terminal multiplexer line (TMLN) structure from 1 to 0 to indicate | |
that further calls will be rejected; the value is set back to 1 when the | |
transmit buffer empties. | |
Entry with the transmit buffer full causes the service to be rescheduled | |
to retry the write after a short delay. The "tmxr_poll_tx" routine must | |
be called in this case, as it is responsible for transmitting the buffer | |
contents and therefore freeing space in the buffer. | |
Both library "put" routines also return SCPE_LOST if the line has been | |
dropped on the remote end. We ignore that error here to allow the | |
simulation to continue while ignoring the output to a disconnected | |
terminal. | |
9. Characters written using "tmxr_putc_ln" are buffered and not transmitted | |
until the buffer is full, the character is an ENQ (and so we will be | |
waiting to receive an ACK), or the next input poll is performed. The | |
last case ensures that the buffer is flushed when output is complete, as | |
there is no indication from the CPU that the last character has been | |
sent. This does incur a delay of up to 10 milliseconds, but this is | |
imperceptible by the user. Buffering offers significantly better | |
throughput compared to transmitting each character as it is written. | |
10. The receive/send completion flag (buffer flag) will not set unless the | |
interrupt enable flag for that channel is also set. If enable is not | |
set, the completion indication will be lost. | |
*/ | |
static t_stat line_service (UNIT *uptr) | |
{ | |
const int32 channel = (int32) (uptr - line_unit); /* the channel number */ | |
const int32 alt_channel = channel ^ 1; /* alternate channel number for diagnostic mode */ | |
const t_bool loopback = (atcd_dev.flags & DEV_DIAG) != 0; /* TRUE if device is set for diagnostic mode */ | |
int32 recv_data, send_data, char_data, cvtd_data; | |
t_stat result = SCPE_OK; | |
dprintf (atcd_dev, DEB_SERV, "Channel %d service entered\n", | |
channel); | |
/* Reception service */ | |
recv_data = recv_buffer [channel]; /* get the current buffer character */ | |
if (recv_data == 0) { /* if no character is present */ | |
if (uptr->wait) /* then if the channel is waiting for an ACK */ | |
tmxr_poll_rx (&atcd_mdsc); /* then poll the line to see if it has arrived */ | |
recv_data = tmxr_getc_ln (&atcd_ldsc [channel]); /* see if there's now a character ready */ | |
} | |
if (recv_data & ~DDR_DATA_MASK) { /* if we now have a valid character */ | |
receive (channel, recv_data, loopback); /* then process the reception */ | |
if (recv_param [channel] & DPI_DIAGNOSE) /* if a diagnosis is requested */ | |
diagnose (recv_param [channel], recv_data); /* then route the data to the auxiliary channels */ | |
uptr->wait = 0; /* clear any pending ACK wait */ | |
} | |
else if (uptr->wait) { /* otherwise if an ACK is expected but has not arrived */ | |
uptr->wait = uptr-> wait * 2; /* then double the wait time for the next check */ | |
if (uptr->wait < poll_unit.wait) { /* if the wait is shorter than the standard poll wait */ | |
sim_activate (uptr, uptr->wait); /* then reschedule the line service */ | |
dprintf (atcd_dev, DEB_SERV, "Channel %d delay %d service rescheduled for ACK\n", | |
channel, uptr->wait); | |
} | |
} | |
/* Transmission service */ | |
if (send_buffer [channel]) { /* if data is available to send */ | |
send_data = DDS_DATA (send_buffer [channel]); /* then pick up the data and stop bits */ | |
char_data = send_data & ASCII_MASK; /* and also the ASCII character value */ | |
if (send_status [channel] & DST_COMPLETE) { /* if the last completion hasn't been acknowledged */ | |
send_status [channel] |= DST_CHAR_LOST; /* then indicate an overrun condition */ | |
dprintf (atcd_dev, DEB_CSRW, "Channel %d send data overrun\n", | |
channel); | |
} | |
if ((send_buffer [channel] & DDS_MARK) == DDS_MARK) { /* if it's an all-mark character */ | |
send_buffer [channel] = 0; /* then the receiver won't see it */ | |
if (send_param [channel] & DPI_ENABLE_IRQ) /* if this channel is enabled to interrupt */ | |
send_status [channel] |= DST_COMPLETE; /* then set the completion flag */ | |
dprintf (atcd_dev, DEB_XFER, (loopback | |
? "Channel %d sync character sent to channel %d\n" | |
: "Channel %d sync character sent\n"), | |
channel, alt_channel); | |
} | |
else if (loopback) { /* otherwise if the device is in loopback mode */ | |
if (send_param [channel] & DPI_DIAGNOSE) /* then if a diagnosis is requested */ | |
diagnose (send_param [channel], send_data); /* then route the data to the auxiliary channels */ | |
if ((send_buffer [channel] & DDR_DATA_MASK) == 0) /* if all bits are clear */ | |
recv_buffer [alt_channel] = SCPE_BREAK; /* then it will be seen as a BREAK */ | |
else /* otherwise a character will be received */ | |
recv_buffer [alt_channel] = send_buffer [channel]; /* so store it in the buffer */ | |
send_buffer [channel] = 0; /* clear the send buffer */ | |
if (send_param [channel] & DPI_ENABLE_IRQ) /* if this channel is enabled to interrupt */ | |
send_status [channel] |= DST_COMPLETE; /* then set the completion flag */ | |
dprintf (atcd_dev, DEB_XFER, "Channel %d character %s sent to channel %d\n", | |
channel, fmt_char (char_data), alt_channel); | |
line_service (&line_unit [alt_channel]); /* receive the character on the alternate channel */ | |
} | |
else if (char_data == ENQ && uptr->flags & UNIT_LOCALACK) { /* otherwise if it's an ENQ and local reply is enabled */ | |
recv_buffer [channel] = GEN_ACK; /* then "receive" an ACK on the channel */ | |
send_buffer [channel] = 0; /* discard the ENQ */ | |
if (send_param [channel] & DPI_ENABLE_IRQ) /* if this channel is enabled to interrupt */ | |
send_status [channel] |= DST_COMPLETE; /* then set the completion flag */ | |
dprintf (atcd_dev, DEB_XFER, "Channel %d character ENQ absorbed internally\n", | |
channel); | |
activate_unit (uptr, Receive); /* schedule the reception */ | |
} | |
else { /* otherwise it's a normal character */ | |
cvtd_data = sim_tt_outcvt (LOWER_BYTE (send_data), /* so convert it as directed */ | |
TT_GET_MODE (uptr->flags)); /* by the output mode flag */ | |
if (cvtd_data >= 0 && atcd_ldsc [channel].xmte == 0) { /* if it's printable but the transmit buffer is full */ | |
activate_unit (uptr, Stall); /* then retry the output a while later */ | |
tmxr_poll_tx (&atcd_mdsc); /* transmit the line buffer */ | |
dprintf (atcd_dev, DEB_XFER, "Channel %d character %s transmission stalled for full buffer\n", | |
channel, fmt_char (cvtd_data)); | |
} | |
else { /* otherwise the character will be consumed */ | |
if (cvtd_data >= 0) /* if the converted character is printable */ | |
if (channel == 0) /* then if we are writing to channel 0 */ | |
result = sim_putchar_s (cvtd_data); /* then output it to the simulation console */ | |
else { /* otherwise */ | |
result = tmxr_putc_ln (&atcd_ldsc [channel], /* output it to the multiplexer line */ | |
cvtd_data); | |
if (char_data == ENQ /* if sending an ENQ */ | |
|| atcd_ldsc [channel].xmte == 0) /* or the output buffer is full */ | |
tmxr_poll_tx (&atcd_mdsc); /* then transmit the line buffer */ | |
} | |
if (result == SCPE_OK || result == SCPE_LOST) { /* if the character is queued to transmit */ | |
if (DPRINTING (atcd_dev, DEB_XFER)) | |
if (result == SCPE_LOST) | |
hp_debug (&atcd_dev, DEB_XFER, "Channel %d character %s discarded by connection loss\n", | |
channel, fmt_char (char_data)); | |
else if (cvtd_data >= 0) | |
hp_debug (&atcd_dev, DEB_XFER, "Channel %d character %s sent\n", | |
channel, fmt_char (cvtd_data)); | |
else | |
hp_debug (&atcd_dev, DEB_XFER, "Channel %d character %s discarded by output filter\n", | |
channel, fmt_char (char_data)); | |
if (send_param [channel] & DPI_DIAGNOSE) /* if a diagnosis is requested */ | |
diagnose (send_param [channel], send_data); /* then route the data to the auxiliary channels */ | |
send_buffer [channel] = 0; /* clear the buffer */ | |
if (send_param [channel] & DPI_ENABLE_IRQ) /* if this channel is enabled to interrupt */ | |
send_status [channel] |= DST_COMPLETE; /* then set the completion flag */ | |
if (cvtd_data == ENQ && result == SCPE_OK) /* if an ENQ was successfully sent */ | |
uptr->wait = activate_unit (uptr, Receive); /* then schedule the ACK reception */ | |
result = SCPE_OK; /* return OK in case the connection was lost */ | |
} | |
} | |
} | |
} | |
if (tdi_data_flag == CLEAR) /* if an interrupt is not currently pending */ | |
scan_channels (channel); /* then scan the channels for completion flags */ | |
if (tmxr_rqln (&atcd_ldsc [channel])) /* if characters are still available on this channel */ | |
activate_unit (uptr, Receive); /* then reschedule the line service */ | |
return result; /* return the result of the service */ | |
} | |
/* Multiplexer poll service. | |
The poll service routine is used to poll for Telnet connections and incoming | |
characters. It also polls the simulation console for channel 0 and flushes | |
the output buffers for all channels. Polling starts at simulator startup or | |
when the TDI is enabled and stops when it is disabled. | |
Implementation notes: | |
1. The poll service routine may be entered with the TCI either enabled or | |
disabled. It will not be entered if the TDI is disabled, as it may be | |
disabled only when it is detached from a listening port. | |
2. If a character is received on the simulation console, we must call the | |
channel 0 line service directly. This is necessary because the poll time | |
may be shorter than the channel service time, and as the console provides | |
no buffering, a second character received before the channel service had | |
been entered would be lost. | |
3. A channel that is waiting for an ACK to complete an ENQ/ACK handshake has | |
its unit "wait" field set non-zero. If the field value is greater than | |
the realistic reception time, then the line service is scheduled | |
immediately, as the channel has already waited the minimum time necessary | |
(the "wait" field value is doubled each time the line service is entered | |
before the ACK has been received). Otherwise, the service is scheduled | |
using the normal reception time. | |
*/ | |
static t_stat poll_service (UNIT *uptr) | |
{ | |
int32 chan, line_state; | |
t_stat status = SCPE_OK; | |
dprintf (atcd_dev, DEB_PSERV, "Poll delay %d service entered\n", | |
uptr->wait); | |
if ((atcc_dev.flags & DEV_DIS) == 0) | |
dprintf (atcc_dev, DEB_PSERV, "Poll delay %d service entered\n", | |
uptr->wait); | |
if ((atcd_dev.flags & DEV_DIAG) == 0) { /* if we're not in diagnostic mode */ | |
chan = tmxr_poll_conn (&atcd_mdsc); /* then check for a new multiplexer connection */ | |
if (chan != -1) { /* if a new connection was established */ | |
atcd_ldsc [chan].rcve = TRUE; /* then enable the channel to receive */ | |
dprintf (atcc_dev, DEB_XFER, "Channel %d connected\n", | |
chan); | |
} | |
} | |
tmxr_poll_tx (&atcd_mdsc); /* flush the multiplexer output buffers */ | |
tmxr_poll_rx (&atcd_mdsc); /* and poll the multiplexer connections for input */ | |
if ((atcc_dev.flags & (DEV_DIAG | DEV_DIS)) == 0) /* if we're not in diagnostic mode or are disabled */ | |
for (chan = FIRST_TERM; chan <= LAST_TERM; chan++) /* then scan the channels for line state changes */ | |
if (line_unit [chan].flags & UNIT_MODEM) { /* if the channel is controlled by the TCI */ | |
tmxr_set_get_modem_bits (&atcd_ldsc [chan], /* then get the current line state */ | |
0, 0, &line_state); | |
if (line_state & TMXR_MDM_DCD) /* if DCD is set */ | |
cntl_status [chan] |= DCD; /* then set the corresponding line flag */ | |
else { /* otherwise DCD is clear */ | |
if (cntl_status [chan] & DCD) /* and a disconnect occurred if DCD was previously set */ | |
dprintf (atcc_dev, DEB_XFER, "Channel %d disconnect dropped DCD and DSR\n", | |
chan); | |
cntl_status [chan] &= ~DCD; /* clear the corresponding flag */ | |
} | |
if (line_state & TMXR_MDM_DSR) /* if DSR is set */ | |
cntl_status [chan] |= DSR; /* then set the corresponding line flag */ | |
else /* otherwise */ | |
cntl_status [chan] &= ~DSR; /* clear the flag */ | |
} | |
status = sim_poll_kbd (); /* poll the simulation console keyboard for input */ | |
if (status >= SCPE_KFLAG) { /* if a character was present */ | |
recv_buffer [0] = (HP_WORD) status; /* then save it for processing */ | |
status = SCPE_OK; /* and then clear the status */ | |
line_service (&line_unit [0]); /* run the system console's I/O service */ | |
} | |
for (chan = FIRST_TERM; chan <= LAST_TERM; chan++) /* check each of the channels for available input */ | |
if (tmxr_rqln (&atcd_ldsc [chan])) /* if characters are available on this channel */ | |
if (line_unit [chan].wait > line_unit [chan].recv_time) { /* then if the channel is waiting for an ACK */ | |
sim_cancel (&line_unit [chan]); /* then cancel any current wait */ | |
activate_unit (&line_unit [chan], Loop); /* and activate the line service immediately */ | |
} | |
else /* otherwise this is a normal input */ | |
activate_unit (&line_unit [chan], Receive); /* so schedule with the normal receive timing */ | |
if (cpu_is_calibrated) /* if the process clock is calibrated */ | |
uptr->wait = sim_activate_time (cpu_pclk_uptr); /* then synchronize with it */ | |
else /* otherwise */ | |
uptr->wait = sim_rtcn_calb (POLL_RATE, TMR_ATC); /* calibrate the poll timer independently */ | |
sim_activate (uptr, uptr->wait); /* continue polling */ | |
if (tci_scan) /* if scanning is active */ | |
scan_status (); /* then check for line status changes */ | |
return status; /* return the service status */ | |
} | |
/* Activate a channel unit. | |
The specified unit is activated to receive or send a character. The reason | |
for the activation is specified by the "reason" parameter. If the TDI is in | |
real-time mode, the previously calculated service time is used to schedule | |
the event. Otherwise, the current value of the optimized timing delay is | |
used. If tracing is enabled, the activation is logged to the debug file. | |
Implementation notes: | |
1. The loopback time is the difference between the reception and | |
transmission times, as the latter event has already occurred when we are | |
called. | |
*/ | |
static int32 activate_unit (UNIT *uptr, ACTIVATOR reason) | |
{ | |
const int32 channel = (int32) (uptr - line_unit); /* the channel number */ | |
int32 delay = 0; | |
if (atcd_dev.flags & (DEV_DIAG | DEV_REALTIME)) /* if either diagnostic or real-time mode is set */ | |
switch (reason) { /* then dispatch the REALTIME activation */ | |
case Receive: /* reception event */ | |
delay = uptr->recv_time; /* schedule for the realistic reception time */ | |
break; | |
case Send: /* transmission event */ | |
delay = uptr->send_time; /* schedule for the realistic transmission time */ | |
break; | |
case Loop: /* diagnostic loopback reception event */ | |
delay = uptr->recv_time - uptr->send_time; /* schedule the additional reception overhead */ | |
if (delay < 0) /* if the receive time is less than the send time */ | |
delay = 0; /* then schedule the reception immediately */ | |
break; | |
case Stall: /* transmission stall event */ | |
delay = uptr->send_time / 10; /* reschedule the transmission after a delay */ | |
break; | |
} | |
else /* otherwise, we are in optimized timing mode */ | |
switch (reason) { /* so dispatch the FASTTIME activation */ | |
case Receive: /* reception event */ | |
case Send: /* transmission event */ | |
delay = fast_data_time; /* use the optimized timing value */ | |
break; | |
case Loop: /* diagnostic loopback reception event */ | |
delay = 1; /* use a nominal delay */ | |
break; | |
case Stall: /* transmission stall event */ | |
delay = fast_data_time / 10; /* reschedule the transmission after a delay */ | |
break; | |
} | |
dprintf (atcd_dev, DEB_SERV, "Channel %d delay %d service scheduled\n", | |
channel, delay); | |
sim_activate (uptr, delay); /* activate the unit */ | |
return delay; /* and return the activation delay */ | |
} | |
/* Calculate the service time. | |
The realistic channel service time in event ticks per character is calculated | |
from the encoded character size and baud rate in the supplied control word. | |
The time consists of the transfer time plus a small overhead, which is | |
different for receiving and sending. | |
The character size field in the control word is generated by this equation: | |
encoded_size = (bits_per_character - 1) AND 7 | |
That is, the encoded character size is the value expressed by the three | |
least-significant bits of the count of the data and stop bits. Therefore, | |
the actual number of bits per character (including the start bit) is encoded | |
as: | |
Actual Encoded | |
------ ------- | |
5 4 | |
6 5 | |
7 6 | |
8 7 | |
9 0 | |
10 1 | |
11 2 | |
12 3 | |
The baud rate field in the control word is generated by this equation: | |
14400 | |
encoded_rate = --------- - 1 | |
baud_rate | |
The transmission and overhead times are related to the recirculation of the | |
multiplexer's internal memory, which contains the data, parameters, and | |
status for each of the 16 send channels, 16 receive channels, and 5 auxiliary | |
channels. Data for a given channel can be accessed only once per | |
recirculation, which takes 69.44 microseconds (1/14400 of a second). The | |
encoded rate plus one gives the number of recirculations corresponding to a | |
single bit time; multiplying by the number of bits per character gives the | |
number of recirculations to send or receive an entire character. | |
All operations encounter two overhead delays. First, an average of one-half | |
of a recirculation must occur to align the memory with the channel of | |
interest. Second, a full recirculation is required after receiving or | |
sending is complete before an interrupt may be generated. | |
For receiving, there is an additional delay to right-justify the received | |
character in the data accumulator. The accumulator is a 12-bit shift | |
register, with received data bits are shifted from left to right. When the | |
final bit is entered, the register must be shifted additionally until the | |
first data bit is in the LSB (i.e., until the start bit is shifted out of the | |
register). One shift per recirculation is performed, and the number of | |
additional shifts required is 12 + 1 - the number of bits per character. | |
Justification begins immediately after the stop bit has been received, so the | |
full set of recirculations for that bit are skipped in lieu of justification. | |
Also, reception of the start bit is delayed by one-half of the bit time to | |
improve noise immunity. | |
Therefore, given R = encoded_rate + 1 and B = bits_per_character, the number | |
of memory recirculations required for sending is: | |
0.5 to align memory with the target channel (on average) | |
+ R * B to send the start, data, and stop bits | |
+ 1 to set the data flag to request an interrupt | |
For example, at 2400 baud (encoded rate 5), a 10-bit character size, and | |
69.44 microseconds per circulation, the service time would be: | |
34.7 usec to align | |
4166.7 usec to send the start, data, and stop bits | |
69.4 usec to set the data flag | |
=========== | |
4270.8 usec from initiation to data flag | |
The number of memory recirculations required for receiving is: | |
0.5 to align memory with the target channel (on average) | |
+ R / 2 to receive the start bit | |
+ R * (B - 1) to receive the data and stop bits | |
+ (12 - B + 1) to right-justify the data | |
+ 1 to set the data flag to request an interrupt | |
Using the same example as above, the service time would be: | |
34.7 usec to align | |
208.3 usec to receive the start bit | |
3750.0 usec to receive the data and stop bits | |
208.3 usec to right-justify the data | |
69.4 usec to set the data flag | |
=========== | |
4270.7 usec from initiation to data flag | |
Implementation notes: | |
1. The multiplexer uses an 8-bit field to set the baud rate. In practice, | |
only the common rates (110, 150, 300, 600, 1200, 2400) will be used. | |
Still, the real-time calculation must accommodate any valid setting, so a | |
lookup table is infeasible. | |
2. The receive calculation is simplified by noting that R / 2 + R * (B - 1) | |
is equivalent to R * B - R / 2, so the send calculation may be reused. | |
Note that the receive time may be less than the send time, typically when | |
the baud rate is low, so that the time to send the stop bits is longer | |
than the time to right-justify the reception. This means that the | |
"addition" of the receive overhead may actually be a subtraction. | |
*/ | |
static uint32 service_time (HP_WORD control, ACTIVATOR reason) | |
{ | |
const double recirc_time = 69.44; /* microseconds per memory recirculation */ | |
const uint32 recirc_per_bit = DPI_BAUD_RATE (control) + 1; /* number of memory recirculations per bit */ | |
const uint32 char_size = bits_per_char [DPI_CHAR_SIZE (control)]; /* number of bits per character */ | |
double usec_per_char; | |
usec_per_char = recirc_time * /* calculate the overhead for sending */ | |
(char_size * recirc_per_bit + 1.5); | |
if (reason == Receive) /* if we're receiving */ | |
usec_per_char += recirc_time * /* then add the additional receiving overhead */ | |
(12 - char_size + 1 | |
- recirc_per_bit / 2.0); | |
return (uint32) (usec_per_char / USEC_PER_EVENT); /* return the service time for indicated rate */ | |
} | |
/* Store a word in the recirculating memory. | |
A parameter or data word is stored in the recirculating memory for the | |
channel indicated by the associated field of the "control" parameter. If the | |
channel number is out of range, the store is ignored. | |
For receive and send parameters, the realistic service time is calculated and | |
stored in the unit for use when a receive or send event is scheduled. For | |
send data, parity is calculated and added if specified by the channel's | |
parameter, and the transmission event is scheduled. For a receive parameter, | |
the pad bits that would normally be added during right-justification after | |
reception are calculated and stored in the unit. | |
Implementation notes: | |
1. Service times are not calculated or set for auxiliary channels because | |
events are not scheduled on them (and so no units are allocated for | |
them). | |
2. Pad bits begin with the stop bit and continue until the character is | |
right-justified in the receive buffer. The calculation assumes one stop | |
bit, but there is no way of ascertaining the actual number of stop bits | |
from the parameter word. | |
*/ | |
static void store (HP_WORD control, HP_WORD data) | |
{ | |
const uint32 channel = DCN_CHAN (control); /* current channel number */ | |
if (data & DDS_IS_SEND) /* if this is a send parameter or data */ | |
if (channel > LAST_TERM) /* then report if the channel number is out of range */ | |
dprintf (atcd_dev, DEB_CSRW, "Send channel %u invalid\n", | |
channel); | |
else if (data & DPI_IS_PARAM) { /* otherwise if this is a parameter store */ | |
send_param [channel] = data; /* then save it */ | |
line_unit [channel].send_time = /* and set the service time */ | |
service_time (data, Send); | |
dprintf (atcd_dev, DEB_CSRW, "Channel %u send parameter %06o stored\n", | |
channel, data); | |
} | |
else { /* otherwise this is a data store */ | |
if (send_param [channel] & DPI_ENABLE_PARITY) /* if parity is enabled */ | |
data = data & ~DDS_PARITY /* then replace the parity bit */ | |
| SEND_PARITY (data); /* with the calculated value */ | |
send_buffer [channel] = data; /* store it in the buffer */ | |
dprintf (atcd_dev, DEB_CSRW, "Channel %u send data %06o stored\n", | |
channel, data); | |
activate_unit (&line_unit [channel], Send); /* schedule the transmission event */ | |
} | |
else /* otherwise this is a receive parameter */ | |
if (channel >= RECV_CHAN_COUNT) /* report if the channel number is out of range */ | |
dprintf (atcd_dev, DEB_CSRW, "Receive channel %u invalid\n", | |
channel); | |
else if (data & DPI_IS_PARAM) { /* otherwise if this is a parameter store */ | |
recv_param [channel] = data; /* then save it */ | |
if (channel <= LAST_TERM) { /* if this is a terminal channel */ | |
line_unit [channel].recv_time = /* and not an auxiliary channel */ | |
service_time (data, Receive); /* then set the service time */ | |
line_unit [channel].stop_bits = /* set the stop bits mask for reception */ | |
PAD_BITS (data); | |
} | |
dprintf (atcd_dev, DEB_CSRW, "Channel %u receive parameter %06o stored\n", | |
channel, data); | |
} | |
else /* otherwise a data store to a receive channel is invalid */ | |
dprintf (atcd_dev, DEB_CSRW, "Channel %u receive output data word %06o invalid\n", | |
channel, data); | |
} | |
/* Process a character received from a channel. | |
This routine is called to process received data on a channel, typically when | |
a character exists in the channel's receive buffer, but also when a character | |
is received on an auxiliary channel. The "channel" parameter indicates the | |
channel on which reception occurred, "data" is the (full) character data | |
as received from the console or terminal multiplexer libraries, and | |
"loopback" is TRUE if the data should be looped back to the alternate channel | |
for diagnostic execution. | |
On entry, the bits required to pad the character are obtained. If a BREAK | |
was detected, then break status is set, and the character is set to NUL, | |
reflecting the all-space reception. Otherwise, if a character is already | |
present in the receive buffer, "character lost" status is set to indicate | |
that it will be overwritten. | |
If this is a loopback reception, and echo is enabled on the channel, the | |
character is sent back to the alternate channel. Otherwise, if this is a | |
main and not auxiliary channel reception, the character is upshifted if the | |
UNIT_CAPSLOCK flag is set. If echo is enabled, the character is written back | |
to the console or terminal multiplexer library line. Finally, the completion | |
flag is set if enabled. | |
Implementation notes: | |
1. The echo to a terminal multiplexer library line will return SCPE_LOST if | |
the line has been dropped on the remote end. We can ignore the error | |
here, as the line drop will be picked up when the next input poll is | |
performed. | |
In addition, the SCPE_STALL returned for a full output buffer is also | |
ignored, as there's no way of queuing echoed characters while waiting for | |
the buffer to empty. | |
*/ | |
static void receive (int32 channel, int32 data, t_bool loopback) | |
{ | |
int32 recv_data, char_data, char_echo, pad; | |
recv_data = data & DDR_DATA_MASK; /* mask to just the character data */ | |
char_data = recv_data & ASCII_MASK; /* and to the equivalent ASCII character */ | |
if (channel <= LAST_TERM) /* if this is a receive channel */ | |
pad = line_unit [channel].stop_bits; /* then set the stop-bit padding from the unit */ | |
else /* otherwise it's an auxiliary channel */ | |
pad = PAD_BITS (recv_param [channel]); /* so calculate the padding */ | |
if (data & SCPE_BREAK) { /* if a break was detected */ | |
recv_buffer [channel] = NUL; /* then return a NUL character */ | |
recv_status [channel] |= DST_BREAK; /* and set break reception status */ | |
dprintf (atcd_dev, DEB_XFER, "Channel %d break detected\n", | |
channel); | |
} | |
else { /* otherwise a normal character was received */ | |
if (recv_status [channel] & DST_COMPLETE) { /* if a character is already pending */ | |
recv_status [channel] |= DST_CHAR_LOST; /* then the previous character will be lost */ | |
dprintf (atcd_dev, DEB_CSRW, "Channel %d receive data overrun\n", | |
channel); | |
} | |
recv_buffer [channel] = recv_data | pad; /* save the character and padding in the buffer */ | |
if (loopback) { /* if this channel has a loopback cable installed */ | |
if (recv_param [channel] & DPI_ENABLE_ECHO) { /* and the channel has echo enabled */ | |
recv_buffer [channel ^ 1] = data; /* then send the data back to the other channel */ | |
activate_unit (&line_unit [channel ^ 1], Loop); /* schedule the reception */ | |
dprintf (atcd_dev, DEB_XFER, "Channel %d character %s echoed to channel %d\n", | |
channel, fmt_char (char_data), channel ^ 1); | |
} | |
} | |
else if (channel <= LAST_TERM) { /* otherwise if it's a receive channel */ | |
if (line_unit [channel].flags & UNIT_CAPSLOCK) { /* then if caps lock is down */ | |
recv_data = toupper (recv_data); /* then convert to upper case if lower */ | |
recv_buffer [channel] = recv_data | pad; /* and replace the character in the buffer */ | |
} | |
if (recv_param [channel] & DPI_ENABLE_ECHO) { /* if the channel has echo enabled */ | |
char_echo = sim_tt_outcvt (recv_data, /* then convert the character per the output mode */ | |
TT_GET_MODE (line_unit [channel].flags)); | |
if (char_echo >= 0) { /* if the converted character is valid for the mode */ | |
if (channel == 0) /* then if this is for channel 0 */ | |
sim_putchar (char_echo); /* then write it back to the simulation console */ | |
else { /* otherwise */ | |
tmxr_putc_ln (&atcd_ldsc [channel], /* write it to the multiplexer output line */ | |
char_echo); | |
if (atcd_ldsc [channel].xmte == 0) /* if the output buffer is full */ | |
tmxr_poll_tx (&atcd_mdsc); /* then transmit the line buffer */ | |
} | |
dprintf (atcd_dev, DEB_XFER, ("Channel %d character %s echoed\n"), | |
channel, fmt_char (char_echo)); | |
} | |
else /* otherwise the echo character was discarded */ | |
dprintf (atcd_dev, DEB_XFER, "Channel %d character %s echo discarded by output filter\n", | |
channel, fmt_char (char_data)); | |
} | |
} | |
} | |
if (recv_param [channel] & DPI_ENABLE_IRQ) /* if the channel is enabled to interrupt */ | |
recv_status [channel] |= DST_COMPLETE; /* then set the completion flag */ | |
dprintf (atcd_dev, DEB_XFER, "Channel %d character %s %s\n", | |
channel, fmt_char (char_data), | |
(data == GEN_ACK ? "generated internally" : "received")); | |
return; | |
} | |
/* Check for a character received on an auxiliary channel. | |
If a send or receive channel has its "diagnose" bit set, then this routine is | |
called to check if any of the auxiliary channels would receive the character | |
too. If one or more would, then the "receive" routine is called to store the | |
character in the appropriate buffer. | |
The diagnosis mode is typically used to speed-sense a receive channel. | |
In hardware, reception on a given channel is simultaneously received on the | |
five auxiliary channels, with each channel set for a different baud rate. | |
When a specific character (e.g., CR) is sent, only the channel with the | |
correct baud rate setting will receive the intended character. By | |
determining which channel received the correct data, the baud rate of the | |
sending terminal may be obtained. | |
In simulation, a main channel will receive a character regardless of the baud | |
rate configuration. Therefore, an auxiliary channel will receive the same | |
character only if it is configured for the same baud rate and character size. | |
*/ | |
static void diagnose (HP_WORD control, int32 data) | |
{ | |
const HP_WORD config = control & DPI_CHAR_CONFIG; /* main channel character size and baud rate */ | |
int32 channel; | |
for (channel = FIRST_AUX; channel <= LAST_AUX; channel++) /* scan the auxiliary channels */ | |
if ((recv_param [channel] & DPI_CHAR_CONFIG) == config) /* if the character configurations match */ | |
receive (channel, data, FALSE); /* then receive the data on this channel */ | |
return; | |
} | |
/* Scan the channels for a transfer completion interrupt. | |
If the multiplexer data flag is not set, this routine is called to scan the | |
channels for completion flags. If the "channel" parameter value is SCAN_ALL, | |
then all of the channels are checked. Otherwise, only the specified channel | |
is checked. | |
If a channel has its completion flag set, the multiplexer data and status | |
words are set for return to the CPU, the data flag is set, and an interrupt | |
is requested. The channel requesting the interrupt is contained in the | |
status word. | |
In hardware, the recirculating buffer consists of the sixteen receive | |
channels, then the sixteen send channels, and then the five auxiliary | |
channels. The completion flags are checked in this order during the | |
recirculation after a completion flag is set. If the scan has been inhibited | |
by the data flag, it will commence with the channel currently in the | |
recirculation window at the time the flag was cleared and then continue in | |
the order indicated. | |
In simulation, the scan is always initiated as though at the beginning of a | |
recirculation. | |
Implementation notes: | |
1. After a send completion, the data word contains all ones (stop bits). | |
*/ | |
static void scan_channels (int32 channel) | |
{ | |
int32 chan, first_chan, last_chan; | |
if (channel == SCAN_ALL) { /* if all channels are to be scanned */ | |
first_chan = FIRST_TERM; /* then set the loop limits */ | |
last_chan = LAST_TERM; /* to the full range of channels */ | |
} | |
else /* otherwise scan just the channel indicated */ | |
first_chan = last_chan = channel; /* plus the auxiliary channels if requested */ | |
for (chan = first_chan; chan <= last_chan; chan++) { /* scan the receive channels */ | |
if (recv_status [chan] & DST_COMPLETE) { /* if this channel's completion flag is set */ | |
tdi_read_word = DDR_DATA (recv_buffer [chan]) /* then form the input data word */ | |
| DDR_CHAN (chan) /* from the character, channel, and parity */ | |
| RECV_PARITY (recv_buffer [chan]); | |
tdi_status_word = recv_status [chan] /* form the partial status word */ | |
| DST_CHAN (chan); | |
recv_buffer [chan] = 0; /* clear the receive buffer */ | |
recv_status [chan] = 0; /* and the channel status */ | |
dprintf (atcd_dev, DEB_CSRW, "Channel %d receive interrupt requested\n", | |
chan); | |
tdi_set_interrupt (); /* set the data flag and request an interrupt */ | |
return; /* and terminate scanning */ | |
} | |
} | |
for (chan = first_chan; chan <= last_chan; chan++) { /* scan the send channels */ | |
if (send_status [chan] & DST_COMPLETE) { /* if this channel's completion flag is set */ | |
tdi_read_word = DDR_DATA_MASK /* then form the input data word from the */ | |
| DDR_CHAN (chan); /* data input buffer and the channel number */ | |
tdi_status_word = send_status [chan] /* form the partial status word */ | |
| DST_CHAN (chan) | |
| DST_SEND_IRQ; | |
send_status [chan] = 0; /* clear the channel status */ | |
dprintf (atcd_dev, DEB_CSRW, "Channel %d send interrupt requested\n", | |
chan); | |
tdi_set_interrupt (); /* set the data flag and request an interrupt */ | |
return; /* and terminate scanning */ | |
} | |
} | |
if (channel == SCAN_ALL /* if we're scanning all channels */ | |
|| send_param [channel] & DPI_DIAGNOSE /* or the indicated channel is diagnosing */ | |
|| recv_param [channel] & DPI_DIAGNOSE) /* its transmission or reception */ | |
for (chan = FIRST_AUX; chan <= LAST_AUX; chan++) { /* then scan the auxiliary channels */ | |
if (recv_status [chan] & DST_COMPLETE) { /* if this channel's completion flag is set */ | |
tdi_read_word = DDR_DATA (recv_buffer [chan]) /* then form the input data word */ | |
| DDR_CHAN (chan) /* from the character, channel, and parity */ | |
| RECV_PARITY (recv_buffer [chan]); | |
tdi_status_word = recv_status [chan] /* form the partial status word */ | |
| DST_CHAN (chan) | |
| DST_DIAGNOSE; | |
recv_buffer [chan] = 0; /* clear the receive buffer */ | |
recv_status [chan] = 0; /* and the channel status */ | |
dprintf (atcd_dev, DEB_CSRW, "Channel %d receive interrupt requested\n", | |
chan); | |
tdi_set_interrupt (); /* set the data flag and request an interrupt */ | |
return; /* and terminate scanning */ | |
} | |
} | |
return; /* no channel has completed */ | |
} | |
/* Check for a control interrupt. | |
If the scan flag is clear, then return the interrupt status bits for the | |
channel indicated by the current control counter value. Otherwise, scan all | |
of the control channels, starting with the current counter, to check for a | |
status mismatch. This occurs when either of the incoming status bits does | |
not match the stored status, and the corresponding mismatch detection is | |
enabled. If an enabled mismatch is found, request an interrupt from the CPU, | |
clear the scan flag, and return the interrupt status bits with the counter | |
pointing at the interrupting channel. | |
*/ | |
static HP_WORD scan_status (void) | |
{ | |
uint32 chan_count; | |
HP_WORD interrupts; | |
if (tci_scan) /* if the control interface is scanning */ | |
chan_count = TERM_COUNT; /* then look at all of the channels */ | |
else /* otherwise */ | |
chan_count = 1; /* look at only the current channel */ | |
while (chan_count > 0) { /* scan the control channels */ | |
interrupts = CST_IX (CCN_ESX (cntl_param [tci_cntr]) /* check for an enabled status mismatch */ | |
& (cntl_param [tci_cntr] ^ cntl_status [tci_cntr])); | |
if (tci_scan) { /* if the interface is scanning */ | |
if (interrupts) { /* and a mismatch was found */ | |
atcc_dib.interrupt_request = SET; /* then request an interrupt */ | |
if (tci_interrupt_mask) /* if the interrupt mask is satisfied */ | |
iop_assert_INTREQ (&atcc_dib); /* then assert the INTREQ signal */ | |
tci_scan = CLEAR; /* stop the scan at the current channel */ | |
dprintf (atcc_dev, DEB_CSRW, "Channel %u interrupt requested\n", | |
tci_cntr); | |
break; | |
} | |
tci_cntr = (tci_cntr + 1) % TERM_COUNT; /* set the counter to the next channel in sequence */ | |
} | |
chan_count = chan_count - 1; /* drop the count of channels to check */ | |
} | |
return interrupts; /* return the interrupt status bits */ | |
} |