1571 lines
75 KiB
C
1571 lines
75 KiB
C
/* hp2100_baci.c: HP 12966A buffered asynchronous communications interface simulator
|
|
|
|
Copyright (c) 2007-2011, J. David Bryan
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a
|
|
copy of this software and associated documentation files (the "Software"),
|
|
to deal in the Software without restriction, including without limitation
|
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
and/or sell copies of the Software, and to permit persons to whom the
|
|
Software is furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of the author shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from the author.
|
|
|
|
BACI 12966A BACI card
|
|
|
|
28-Mar-11 JDB Tidied up signal handling
|
|
26-Oct-10 JDB Changed I/O signal handler for revised signal model
|
|
25-Nov-08 JDB Revised for new multiplexer library SHOW routines
|
|
11-Sep-08 JDB Fixed STC,C losing interrupt request on BREAK
|
|
07-Sep-08 JDB Fixed IN_LOOPBACK conflict with netinet/in.h
|
|
Changed Telnet poll to connect immediately after reset or attach
|
|
10-Aug-08 JDB Added REG_FIT to register variables < 32-bit size
|
|
26-Jun-08 JDB Rewrote device I/O to model backplane signals
|
|
17-Jun-08 JDB Moved fmt_char() function to hp2100_sys.c
|
|
13-Jun-08 JDB Cleaned up debug reporting for sim_activate calls
|
|
16-Apr-08 JDB Separated terminal I/O and Telnet poll for idle compatibility
|
|
07-Dec-07 JDB Created BACI device
|
|
|
|
References:
|
|
- HP 12966A Buffered Asynchronous Data Communications Interface Installation
|
|
and Reference Manual (12966-90001, Jul-1982)
|
|
- Western Digital Communications Products Handbook (Jun-1984)
|
|
|
|
|
|
The 12966A BACI card supplanted the 12531C Teletype and 12880A CRT interfaces
|
|
as the primary terminal connection for HP 1000 systems. The main advantage
|
|
of this card over the others was its 128-character FIFO memory. While this
|
|
allowed more efficient I/O than its interrupt-per-character predecessors, the
|
|
most significant advantage was that block input from the 264x-series of CRT
|
|
terminals was supported. The 264x were the first HP-supported terminals to
|
|
provide local editing and character storage, as well as mass storage via dual
|
|
DC-100 minicartridge drives. This support meant that input from the terminal
|
|
could come in bursts at the full baud rate, which would overrun the older
|
|
cards that needed a small intercharacter handling time. Also, the older
|
|
cards placed a substantial load on the CPU in high-baud-rate output
|
|
applications. Indeed, block output under RTE on a 1000 M-Series with a
|
|
12880A CRT card would saturate the CPU at about 5700 baud.
|
|
|
|
For a while, the BACI and the earlier cards were both supported as the system
|
|
console interface, and RTE primary systems were generated with drivers for
|
|
both cards. The boot-time I/O reconfigurator would detect the presence of
|
|
the BACI card and would dynamically select the correct driver (DVR05 vs.
|
|
DVR00). However, the 12880A card faded quickly as the 264x and later 262x
|
|
terminals gained in popularity, and support for the 12880A was dropped in
|
|
favor of the BACI. This meant that later RTE primary systems could only be
|
|
run on CPUs containing a BACI card.
|
|
|
|
The simulation supports terminal and diagnostic modes. The latter simulates
|
|
the installation of the 12966-60003 diagnostic loopback connector on the
|
|
card.
|
|
|
|
Fifteen programmable baud rates were supported by the BACI. We simulate
|
|
these "realistic" rates by scheduling I/O service based on the appropriate
|
|
number of 1000 E-Series instructions for the rate selected. We also provide
|
|
an "external rate" that is equivalent to 9600 baud, as most terminals were
|
|
set to their maximum speeds.
|
|
|
|
We support the 12966A connected to an HP terminal emulator via Telnet.
|
|
Internally, we model the BACI as a terminal multiplexer with one line. The
|
|
simulation is complicated by the half-duplex nature of the card (there is
|
|
only one FIFO, used selectively either for transmission or reception) and the
|
|
double-buffered UART (a Western Digital TR1863A), which has holding registers
|
|
as well as a shift registers for transmission and reception. We model both
|
|
sets of device registers.
|
|
|
|
During an output operation, the first character output to the card passes
|
|
through the FIFO and into the transmitter holding register. Subsequent
|
|
characters remain in the FIFO. If the FIFO is then turned around by a mode
|
|
switch from transmission to reception, the second character output becomes
|
|
the first character input to the CPU, as the first character output remains
|
|
in the THR. Also, the FIFO counter reflects the combined state of the FIFO
|
|
and the THR: it is incremented by a "shift in" to the FIFO and decremented by
|
|
the "transmit complete" signal from the UART. This has two implications:
|
|
|
|
1. If the FIFO is turned around before the character in the THR is
|
|
transmitted, the counter will not decrement when transmission is
|
|
complete, so the FIFO will show as "empty" when the counter reads "1".
|
|
|
|
2. The FIFO counter will indicate "half full" and "full" one character
|
|
before the FIFO itself reaches those stages.
|
|
|
|
The diagnostic hood connects the UART clock to a spare output register. This
|
|
allows the diagnostic to supply programmed clock pulses to the UART. The
|
|
serial transmit and receive lines from the UART are also available to the
|
|
diagnostic. Functional operation is checked by supplying or testing serial
|
|
data while clocking the UART sixteen times for each bit. This meant that we
|
|
had to model the UART shift registers for faithful hardware simulation.
|
|
|
|
The simulation provides both the "realistic timing" described above, as well
|
|
as an "optimized (fast) timing" option. Optimization makes three
|
|
improvements:
|
|
|
|
1. On output, characters in the FIFO are emptied into the Telnet buffer as a
|
|
block, rather than one character per service call, and on input, all of
|
|
the characters available in the Telnet buffer are loaded into the FIFO as
|
|
a block.
|
|
|
|
2. The ENQ/ACK handshake is done locally, without involving the Telnet
|
|
client.
|
|
|
|
3. Input occurring during an output operation is delayed until the second or
|
|
third consecutive ENQ/ACK handshake.
|
|
|
|
During development, it was noted that a comparatively long time elapsed
|
|
(approximately 30 milliseconds on a 3 GHz system) between the transmission of
|
|
an ENQ and the reception of the ACK. As the RTE BACI driver, DVR05, does
|
|
three ENQ/ACKs at the end of each line, plus an additional ENQ/ACK every 33
|
|
characters within a line, maximum throughput was about ten lines per second.
|
|
The source of this delay is not understood but apparently lies within the
|
|
terminal emulator, as it was observed with two emulators from two different
|
|
companies. Absorbing the ENQ and generating the ACK locally provided a
|
|
dramatic improvement in output speed.
|
|
|
|
However, as a result, RTE break-mode became effectively impossible, i.e.,
|
|
striking a key during output no longer produced the break-mode prompt. This
|
|
was traced to the RTE driver. DVR05 only checks for an input character
|
|
during ENQ/ACK processing, and then only during the second and third
|
|
end-of-line handshakes. When the ENQ/ACKs were eliminated, break-mode also
|
|
disappeared.
|
|
|
|
The workaround is to save a character received during output and supply it
|
|
during the second or third consecutive handshake. This ensures that
|
|
break-mode is recognized. Because the driver tries to "cheat" the card by
|
|
selecting receive mode before the ENQ has actually been transmitted (in order
|
|
to save an interrupt), the FIFO counter becomes "off by one" and is reset
|
|
with a master clear at the end of each handshake. This would normally clear
|
|
the UART receiving register, thereby losing the deferred character. We work
|
|
around this by skipping the register clear in "fast timing" mode.
|
|
*/
|
|
|
|
#include <ctype.h>
|
|
|
|
#include "hp2100_defs.h"
|
|
#include "sim_sock.h"
|
|
#include "sim_tmxr.h"
|
|
|
|
|
|
/* Program limits */
|
|
|
|
#define FIFO_SIZE 128 /* read/write buffer size */
|
|
|
|
|
|
/* Character constants */
|
|
|
|
#define ENQ '\005'
|
|
#define ACK '\006'
|
|
|
|
|
|
/* Unit flags */
|
|
|
|
#define UNIT_V_DIAG (UNIT_V_UF + 0) /* diagnostic mode */
|
|
#define UNIT_V_FASTTIME (UNIT_V_UF + 1) /* fast timing mode */
|
|
#define UNIT_V_CAPSLOCK (UNIT_V_UF + 2) /* caps lock mode */
|
|
|
|
#define UNIT_DIAG (1 << UNIT_V_DIAG)
|
|
#define UNIT_FASTTIME (1 << UNIT_V_FASTTIME)
|
|
#define UNIT_CAPSLOCK (1 << UNIT_V_CAPSLOCK)
|
|
|
|
|
|
/* Debug flags */
|
|
|
|
#define DEB_CMDS (1 << 0) /* commands and status */
|
|
#define DEB_CPU (1 << 1) /* CPU I/O */
|
|
#define DEB_BUF (1 << 2) /* buffer gets and puts */
|
|
#define DEB_XFER (1 << 3) /* character reads and writes */
|
|
|
|
|
|
/* Bit flags */
|
|
|
|
#define OUT_MR 0100000 /* common master reset */
|
|
|
|
#define OUT_ENCM 0000040 /* ID1: enable character mode */
|
|
#define OUT_ENCB 0000020 /* ID1: enable CB */
|
|
#define OUT_ENCC 0000010 /* ID1: enable CC */
|
|
#define OUT_ENCE 0000004 /* ID1: enable CE */
|
|
#define OUT_ENCF 0000002 /* ID1: enable CF */
|
|
#define OUT_ENSXX 0000001 /* ID1: enable SBB/SCF */
|
|
|
|
#define OUT_DIAG 0000040 /* ID2: diagnostic output */
|
|
#define OUT_REFCB 0000020 /* ID2: reference CB */
|
|
#define OUT_REFCC 0000010 /* ID2: reference CC */
|
|
#define OUT_REFCE 0000004 /* ID2: reference CE */
|
|
#define OUT_REFCF 0000002 /* ID2: reference CF */
|
|
#define OUT_REFSXX 0000001 /* ID2: reference SBB/SCF */
|
|
|
|
#define OUT_STBITS 0000040 /* ID3: number of stop bits */
|
|
#define OUT_ECHO 0000020 /* ID3: enable echo */
|
|
#define OUT_PARITY 0000010 /* ID3: enable parity */
|
|
#define OUT_PAREVEN 0000004 /* ID3: even parity or odd */
|
|
|
|
#define OUT_XMIT 0000400 /* ID4: transmit or receive */
|
|
#define OUT_CA 0000200 /* ID4: CA on */
|
|
#define OUT_CD 0000100 /* ID4: CD on */
|
|
#define OUT_SXX 0000040 /* ID4: SBA/SCA on */
|
|
#define OUT_DCPC 0000020 /* ID4: DCPC on */
|
|
|
|
#define OUT_CSC 0000040 /* ID5: clear special char interrupt */
|
|
#define OUT_CBH 0000020 /* ID5: clear buffer half-full interrupt */
|
|
#define OUT_CBF 0000010 /* ID5: clear buffer full interrupt */
|
|
#define OUT_CBE 0000004 /* ID5: clear buffer empty interrupt */
|
|
#define OUT_CBRK 0000002 /* ID5: clear break interrupt */
|
|
#define OUT_COVR 0000001 /* ID5: clear overrun/parity interrupt */
|
|
|
|
#define OUT_SPFLAG 0000400 /* ID6: special character */
|
|
|
|
#define OUT_IRQCLR (OUT_CBH | OUT_CBF | OUT_CBE | OUT_CBRK | OUT_COVR)
|
|
|
|
|
|
#define IN_VALID 0100000 /* received data: character valid */
|
|
#define IN_SPFLAG 0040000 /* received data: is special character */
|
|
|
|
#define IN_DEVINT 0100000 /* status: device interrupt */
|
|
#define IN_SPCHAR 0040000 /* status: special char has been recd */
|
|
#define IN_SPARE 0010000 /* status: spare receiver state */
|
|
#define IN_TEST 0004000 /* status: unprocessed serial data line */
|
|
#define IN_BUFHALF 0001000 /* status: buffer is half full */
|
|
#define IN_BUFFULL 0000400 /* status: buffer is full */
|
|
#define IN_BUFEMPTY 0000200 /* status: buffer is empty */
|
|
#define IN_BREAK 0000100 /* status: break detected */
|
|
#define IN_OVRUNPE 0000040 /* status: overrun or parity error */
|
|
#define IN_CB 0000020 /* status: CB is on */
|
|
#define IN_CC 0000010 /* status: CC is on */
|
|
#define IN_CE 0000004 /* status: CE is on */
|
|
#define IN_CF 0000002 /* status: CF is on */
|
|
#define IN_SXX 0000001 /* status: SBB/SCF is on */
|
|
|
|
#define IN_MODEM (IN_CB | IN_CC | IN_CE | IN_CF | IN_SXX)
|
|
#define IN_DIAG (IN_DEVINT | IN_SPARE | IN_TEST | IN_MODEM)
|
|
#define IN_STDIRQ (IN_DEVINT | IN_SPCHAR | IN_BREAK | IN_OVRUNPE)
|
|
#define IN_FIFOIRQ (IN_BUFEMPTY | IN_BUFHALF | IN_BUFFULL)
|
|
|
|
|
|
/* Packed starting bit numbers */
|
|
|
|
#define OUT_V_ID 12 /* common output word ID */
|
|
#define OUT_V_DATA 0 /* ID 0: output data character */
|
|
#define OUT_V_CHARSIZE 0 /* ID 3: character size */
|
|
#define OUT_V_BAUDRATE 0 /* ID 4: baud rate */
|
|
#define OUT_V_SPCHAR 0 /* ID 6: special character */
|
|
|
|
#define IN_V_CHARCNT 8 /* data: char count in buffer */
|
|
#define IN_V_DATA 0 /* data: input character */
|
|
#define IN_V_IRQCLR 5 /* status: interrupt status clear */
|
|
|
|
|
|
/* Packed bit widths */
|
|
|
|
#define OUT_W_ID 3
|
|
#define OUT_W_DATA 8
|
|
#define OUT_W_CHARSIZE 2
|
|
#define OUT_W_BAUDRATE 4
|
|
#define OUT_W_SPCHAR 8
|
|
|
|
#define IN_W_CHARCNT 6
|
|
#define IN_W_DATA 8
|
|
|
|
/* Packed bit masks */
|
|
|
|
#define OUT_M_ID ((1 << OUT_W_ID) - 1)
|
|
#define OUT_M_DATA ((1 << OUT_W_DATA) - 1)
|
|
#define OUT_M_CHARSIZE ((1 << OUT_W_CHARSIZE) - 1)
|
|
#define OUT_M_BAUDRATE ((1 << OUT_W_BAUDRATE) - 1)
|
|
#define OUT_M_SPCHAR ((1 << OUT_W_SPCHAR) - 1)
|
|
|
|
#define IN_M_CHARCNT ((1 << IN_W_CHARCNT) - 1)
|
|
#define IN_M_DATA ((1 << IN_W_DATA) - 1)
|
|
|
|
/* Packed field masks */
|
|
|
|
#define OUT_ID (OUT_M_ID << OUT_V_ID)
|
|
#define OUT_DATA (OUT_M_DATA << OUT_V_DATA)
|
|
#define OUT_CHARSIZE (OUT_M_CHARSIZE << OUT_V_CHARSIZE)
|
|
#define OUT_BAUDRATE (OUT_M_BAUDRATE << OUT_V_BAUDRATE)
|
|
#define OUT_SPCHAR (OUT_M_SPCHAR << OUT_V_SPCHAR)
|
|
|
|
#define IN_CHARCNT (IN_M_CHARCNT << IN_V_CHARCNT)
|
|
#define IN_DATA (IN_M_DATA << IN_V_DATA)
|
|
|
|
|
|
/* Command helpers */
|
|
|
|
#define TO_CHARCNT(c) (((c) << IN_V_CHARCNT) & IN_CHARCNT)
|
|
|
|
#define GET_ID(i) (((i) & OUT_ID) >> OUT_V_ID)
|
|
#define GET_BAUDRATE(b) (((b) & OUT_BAUDRATE) >> OUT_V_BAUDRATE)
|
|
|
|
#define IO_MODE (baci_icw & OUT_XMIT)
|
|
#define XMIT OUT_XMIT
|
|
#define RECV 0
|
|
|
|
#define CLEAR_HR 0 /* UART holding register clear value */
|
|
#define CLEAR_R -1 /* UART register clear value */
|
|
|
|
|
|
/* Unit references */
|
|
|
|
#define baci_term baci_unit[0] /* terminal I/O unit */
|
|
#define baci_poll baci_unit[1] /* Telnet polling unit */
|
|
|
|
|
|
/* BACI state variables */
|
|
|
|
struct {
|
|
FLIP_FLOP control; /* control flip-flop */
|
|
FLIP_FLOP flag; /* flag flip-flop */
|
|
FLIP_FLOP flagbuf; /* flag buffer flip-flop */
|
|
FLIP_FLOP srq; /* SRQ flip-flop */
|
|
FLIP_FLOP lockout; /* interrupt lockout flip-flop */
|
|
} baci = { CLEAR, CLEAR, CLEAR, CLEAR, CLEAR };
|
|
|
|
uint16 baci_ibuf = 0; /* status/data in */
|
|
uint16 baci_obuf = 0; /* command/data out */
|
|
uint16 baci_status = 0; /* current status */
|
|
|
|
uint16 baci_edsiw = 0; /* enable device status word */
|
|
uint16 baci_dsrw = 0; /* device status reference word */
|
|
uint16 baci_cfcw = 0; /* character frame control word */
|
|
uint16 baci_icw = 0; /* interface control word */
|
|
uint16 baci_isrw = 0; /* interrupt status reset word */
|
|
|
|
uint32 baci_fput = 0; /* FIFO buffer add index */
|
|
uint32 baci_fget = 0; /* FIFO buffer remove index */
|
|
uint32 baci_fcount = 0; /* FIFO buffer counter */
|
|
uint32 baci_bcount = 0; /* break counter */
|
|
|
|
uint8 baci_fifo [FIFO_SIZE]; /* read/write buffer FIFO */
|
|
uint8 baci_spchar [256]; /* special character RAM */
|
|
|
|
uint16 baci_uart_thr = CLEAR_HR; /* UART transmitter holding register */
|
|
uint16 baci_uart_rhr = CLEAR_HR; /* UART receiver holding register */
|
|
int32 baci_uart_tr = CLEAR_R; /* UART transmitter register */
|
|
int32 baci_uart_rr = CLEAR_R; /* UART receiver register */
|
|
uint32 baci_uart_clk = 0; /* UART transmit/receive clock */
|
|
|
|
t_bool baci_enq_seen = FALSE; /* ENQ seen flag */
|
|
uint32 baci_enq_cntr = 0; /* ENQ seen counter */
|
|
|
|
|
|
/* Terminal multiplexer library interface */
|
|
|
|
TMLN baci_ldsc = { 0 }; /* line descriptor */
|
|
TMXR baci_desc = { 1, 0, 0, &baci_ldsc }; /* device descriptor */
|
|
|
|
|
|
/* BACI local routines */
|
|
|
|
static int32 service_time (uint32 control_word);
|
|
static void update_status (void);
|
|
static void master_reset (void);
|
|
|
|
static uint32 fifo_get (void);
|
|
static void fifo_put (uint8 ch);
|
|
static void clock_uart (void);
|
|
|
|
/* BACI global routines */
|
|
|
|
IOHANDLER baci_io;
|
|
|
|
t_stat baci_term_svc (UNIT *uptr);
|
|
t_stat baci_poll_svc (UNIT *uptr);
|
|
t_stat baci_reset (DEVICE *dptr);
|
|
t_stat baci_attach (UNIT *uptr, char *cptr);
|
|
t_stat baci_detach (UNIT *uptr);
|
|
|
|
|
|
/* BACI data structures
|
|
|
|
baci_dib BACI device information block
|
|
baci_dev BACI device descriptor
|
|
baci_unit BACI unit list
|
|
baci_reg BACI register list
|
|
baci_mod BACI modifier list
|
|
baci_deb BACI debug list
|
|
|
|
Two units are used: one to handle character I/O via the Telnet library, and
|
|
another to poll for connections and input. The character I/O service routine
|
|
runs only when there are characters to read or write. It operates at the
|
|
approximate baud rate of the terminal (in CPU instructions per second) in
|
|
order to be compatible with the OS drivers. The Telnet poll must run
|
|
continuously, but it can operate much more slowly, as the only requirement is
|
|
that it must not present a perceptible lag to human input. To be compatible
|
|
with CPU idling, it is co-scheduled with the master poll timer, which uses a
|
|
ten millisecond period.
|
|
*/
|
|
|
|
DIB baci_dib = { &baci_io, BACI, 0 };
|
|
|
|
DEVICE baci_dev;
|
|
|
|
UNIT baci_unit[] = {
|
|
{ UDATA (&baci_term_svc, UNIT_ATTABLE | UNIT_FASTTIME, 0) }, /* terminal I/O unit */
|
|
{ UDATA (&baci_poll_svc, UNIT_DIS, POLL_FIRST) } /* Telnet poll unit */
|
|
};
|
|
|
|
REG baci_reg[] = {
|
|
{ ORDATA (IBUF, baci_ibuf, 16), REG_FIT },
|
|
{ ORDATA (OBUF, baci_obuf, 16), REG_FIT },
|
|
{ ORDATA (STATUS, baci_status, 16), REG_FIT },
|
|
|
|
{ ORDATA (EDSIW, baci_edsiw, 16), REG_FIT },
|
|
{ ORDATA (DSRW, baci_dsrw, 16), REG_FIT },
|
|
{ ORDATA (CFCW, baci_cfcw, 16), REG_FIT },
|
|
{ ORDATA (ICW, baci_icw, 16), REG_FIT },
|
|
{ ORDATA (ISRW, baci_isrw, 16), REG_FIT },
|
|
|
|
{ DRDATA (FIFOPUT, baci_fput, 8) },
|
|
{ DRDATA (FIFOGET, baci_fget, 8) },
|
|
{ DRDATA (FIFOCNTR, baci_fcount, 8) },
|
|
{ DRDATA (BRKCNTR, baci_bcount, 16) },
|
|
|
|
{ BRDATA (FIFO, baci_fifo, 8, 8, FIFO_SIZE) },
|
|
{ BRDATA (SPCHAR, baci_spchar, 8, 1, 256) },
|
|
|
|
{ ORDATA (UARTTHR, baci_uart_thr, 16), REG_FIT },
|
|
{ ORDATA (UARTTR, baci_uart_tr, 16), REG_NZ },
|
|
{ ORDATA (UARTRHR, baci_uart_rhr, 16), REG_FIT },
|
|
{ ORDATA (UARTRR, baci_uart_rr, 16), REG_NZ },
|
|
{ DRDATA (UARTCLK, baci_uart_clk, 16) },
|
|
|
|
{ DRDATA (CTIME, baci_term.wait, 19), REG_RO },
|
|
|
|
{ FLDATA (ENQFLAG, baci_enq_seen, 0), REG_HRO },
|
|
{ DRDATA (ENQCNTR, baci_enq_cntr, 16), REG_HRO },
|
|
|
|
{ FLDATA (LKO, baci.lockout, 0) },
|
|
{ FLDATA (CTL, baci.control, 0) },
|
|
{ FLDATA (FLG, baci.flag, 0) },
|
|
{ FLDATA (FBF, baci.flagbuf, 0) },
|
|
{ FLDATA (SRQ, baci.srq, 0) },
|
|
{ ORDATA (DEVNO, baci_dib.select_code, 6), REG_HRO },
|
|
{ NULL }
|
|
};
|
|
|
|
MTAB baci_mod[] = {
|
|
{ UNIT_DIAG, UNIT_DIAG, "diagnostic mode", "DIAG", NULL, NULL, NULL },
|
|
{ UNIT_DIAG, 0, "terminal mode", "TERMINAL", NULL, NULL, NULL },
|
|
|
|
{ UNIT_FASTTIME, UNIT_FASTTIME, "fast timing", "FASTTIME", NULL, NULL, NULL },
|
|
{ UNIT_FASTTIME, 0, "realistic timing", "REALTIME", NULL, NULL, NULL },
|
|
|
|
{ UNIT_CAPSLOCK, UNIT_CAPSLOCK, "CAPS LOCK down", "CAPSLOCK", NULL, NULL, NULL },
|
|
{ UNIT_CAPSLOCK, 0, "CAPS LOCK up", "NOCAPSLOCK", NULL, NULL, NULL },
|
|
|
|
{ MTAB_XTD | MTAB_VDV | MTAB_NC, 0, "LOG", "LOG", &tmxr_set_log, &tmxr_show_log, &baci_desc },
|
|
{ MTAB_XTD | MTAB_VDV | MTAB_NC, 0, NULL, "NOLOG", &tmxr_set_nolog, NULL, &baci_desc },
|
|
|
|
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTION", NULL, NULL, &tmxr_show_cstat, &baci_desc },
|
|
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, &baci_desc },
|
|
{ MTAB_XTD | MTAB_VDV, 0, NULL, "DISCONNECT", &tmxr_dscln, NULL, &baci_desc },
|
|
|
|
{ MTAB_XTD | MTAB_VDV, 0, "DEVNO", "DEVNO", &hp_setdev, &hp_showdev, &baci_dev },
|
|
{ 0 }
|
|
};
|
|
|
|
DEBTAB baci_deb[] = {
|
|
{ "CMDS", DEB_CMDS },
|
|
{ "CPU", DEB_CPU },
|
|
{ "BUF", DEB_BUF },
|
|
{ "XFER", DEB_XFER },
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
DEVICE baci_dev = {
|
|
"BACI", /* device name */
|
|
baci_unit, /* unit array */
|
|
baci_reg, /* register array */
|
|
baci_mod, /* modifier array */
|
|
2, /* number of units */
|
|
10, /* address radix */
|
|
31, /* address width */
|
|
1, /* address increment */
|
|
8, /* data radix */
|
|
8, /* data width */
|
|
&tmxr_ex, /* examine routine */
|
|
&tmxr_dep, /* deposit routine */
|
|
&baci_reset, /* reset routine */
|
|
NULL, /* boot routine */
|
|
&baci_attach, /* attach routine */
|
|
&baci_detach, /* detach routine */
|
|
&baci_dib, /* device information block */
|
|
DEV_NET | DEV_DEBUG | DEV_DISABLE, /* device flags */
|
|
0, /* debug control flags */
|
|
baci_deb, /* debug flag name table */
|
|
NULL, /* memory size change routine */
|
|
NULL }; /* logical device name */
|
|
|
|
|
|
/* I/O signal handler.
|
|
|
|
The BACI processes seven types of output words and supplies two types of
|
|
input words. Output word type is identified by an ID code in bits 14-12.
|
|
Input word type is determined by the state of the control flip-flop.
|
|
|
|
The card has the usual control, flag buffer, flag, and SRQ flip-flops.
|
|
However, they have the following unusual characteristics:
|
|
|
|
- STC is not required to transfer a character.
|
|
- Flag is not set after character transfer completes.
|
|
- FLAG and SRQ are decoupled and are set independently.
|
|
|
|
An interrupt lockout flip-flop is used to prevent the generation of multiple
|
|
interrupts until the cause of the first interrupt is identified and cleared
|
|
by the CPU.
|
|
|
|
Implementation notes:
|
|
|
|
1. The STC handler checks to see if it was invoked for STC SC or STC SC,C.
|
|
In the latter case, the check for new interrupt requests is deferred
|
|
until after the CLF. Otherwise, the flag set by the interrupt check
|
|
would be cleared, and the interrupt would be lost.
|
|
*/
|
|
|
|
uint32 baci_io (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data)
|
|
{
|
|
const char *hold_or_clear = (signal_set & ioCLF ? ",C" : "");
|
|
uint8 ch;
|
|
uint16 data;
|
|
uint32 mask;
|
|
IOSIGNAL signal;
|
|
IOCYCLE working_set = IOADDSIR (signal_set); /* add ioSIR if needed */
|
|
|
|
while (working_set) {
|
|
signal = IONEXT (working_set); /* isolate next signal */
|
|
|
|
switch (signal) { /* dispatch I/O signal */
|
|
|
|
case ioCLF: /* clear flag flip-flop */
|
|
baci.flag = baci.flagbuf = CLEAR; /* clear flag and flag buffer */
|
|
baci.srq = CLEAR; /* clear SRQ */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_CMDS))
|
|
fputs (">>BACI cmds: [CLF] Flag and SRQ cleared\n", sim_deb);
|
|
|
|
update_status (); /* FLG might set when SRQ clears */
|
|
break;
|
|
|
|
|
|
case ioSTF: /* set flag flip-flop */
|
|
baci.flag = baci.flagbuf = SET; /* set flag and flag buffer */
|
|
baci.lockout = SET; /* set lockout */
|
|
baci.srq = SET; /* set SRQ */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_CMDS))
|
|
fputs (">>BACI cmds: [STF] Flag, SRQ, and lockout set\n", sim_deb);
|
|
break;
|
|
|
|
|
|
case ioENF: /* enable flag */
|
|
baci.flag = baci.flagbuf = SET; /* set device flag and flag buffer */
|
|
baci.lockout = SET; /* set lockout */
|
|
break;
|
|
|
|
|
|
case ioSFC: /* skip if flag is clear */
|
|
setstdSKF (baci);
|
|
break;
|
|
|
|
|
|
case ioSFS: /* skip if flag is set */
|
|
setstdSKF (baci);
|
|
break;
|
|
|
|
|
|
case ioIOI: /* I/O data input */
|
|
if (baci.control) { /* control set? */
|
|
baci_ibuf = TO_CHARCNT (baci_fcount); /* get FIFO count */
|
|
|
|
if (IO_MODE == RECV) /* receiving? */
|
|
baci_ibuf = baci_ibuf | fifo_get (); /* add char and validity flag */
|
|
|
|
data = baci_ibuf; /* return received data */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_CPU))
|
|
fprintf (sim_deb, ">>BACI cpu: [LIx%s] Received data = %06o\n", hold_or_clear, baci_ibuf);
|
|
}
|
|
|
|
else { /* control clear? */
|
|
data = baci_status; /* return status */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_CPU))
|
|
fprintf (sim_deb, ">>BACI cpu: [LIx%s] Status = %06o\n", hold_or_clear, baci_status);
|
|
}
|
|
|
|
stat_data = IORETURN (SCPE_OK, data); /* merge in return status */
|
|
break;
|
|
|
|
|
|
case ioIOO: /* I/O data output */
|
|
baci_obuf = IODATA (stat_data); /* get data value */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_CPU))
|
|
fprintf (sim_deb, ">>BACI cpu: [OTx%s] Command = %06o\n", hold_or_clear, baci_obuf);
|
|
|
|
if (baci_obuf & OUT_MR) { /* master reset? */
|
|
master_reset (); /* do before processing */
|
|
baci_io (&baci_dib, ioSIR, 0); /* set interrupt request */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_CMDS))
|
|
fprintf (sim_deb, ">>BACI cmds: [OTx%s] Master reset\n", hold_or_clear);
|
|
}
|
|
|
|
switch (GET_ID (baci_obuf)) { /* isolate ID code */
|
|
|
|
case 0: /* transmit data */
|
|
if (IO_MODE == XMIT) { /* transmitting? */
|
|
ch = baci_obuf & OUT_DATA; /* mask to character */
|
|
fifo_put (ch); /* queue character */
|
|
|
|
if (baci_term.flags & UNIT_ATT) { /* attached to network? */
|
|
if (DEBUG_PRI (baci_dev, DEB_CMDS) && /* debugging? */
|
|
(sim_is_active (&baci_term) == 0)) /* service stopped? */
|
|
fprintf (sim_deb, ">>BACI cmds: [OTx%s] Terminal service scheduled, "
|
|
"time = %d\n", hold_or_clear, baci_term.wait);
|
|
|
|
if (baci_fcount == 1) /* first char to xmit? */
|
|
sim_activate_abs (&baci_term, /* start service with full char time */
|
|
baci_term.wait);
|
|
else
|
|
sim_activate (&baci_term, /* start service if not running */
|
|
baci_term.wait);
|
|
}
|
|
}
|
|
break;
|
|
|
|
case 1: /* enable device status interrupt */
|
|
baci_edsiw = baci_obuf; /* load new enable word */
|
|
update_status (); /* may have enabled an interrupt */
|
|
break;
|
|
|
|
case 2: /* device status reference */
|
|
if ((baci_term.flags & UNIT_DIAG) && /* diagnostic mode? */
|
|
(baci_dsrw & OUT_DIAG) && /* and last DIAG was high? */
|
|
!(baci_obuf & OUT_DIAG) && /* and new DIAG is low? */
|
|
!(baci_icw & OUT_BAUDRATE)) /* and clock is external? */
|
|
clock_uart (); /* pulse UART clock */
|
|
|
|
baci_dsrw = baci_obuf; /* load new reference word */
|
|
update_status (); /* clocking UART may interrupt */
|
|
break;
|
|
|
|
case 3: /* character frame control */
|
|
baci_cfcw = baci_obuf; /* load new frame word */
|
|
break;
|
|
|
|
case 4: /* interface control */
|
|
if ((baci_icw ^ baci_obuf) & OUT_BAUDRATE) { /* baud rate change? */
|
|
baci_term.wait = service_time (baci_obuf); /* set service time to match rate */
|
|
|
|
if (baci_term.flags & UNIT_DIAG) /* diagnostic mode? */
|
|
if (baci_obuf & OUT_BAUDRATE) { /* internal baud rate requested? */
|
|
sim_activate (&baci_term, /* activate I/O service */
|
|
baci_term.wait);
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_CMDS))
|
|
fprintf (sim_deb, ">>BACI cmds: [OTx%s] Terminal service scheduled, "
|
|
"time = %d\n", hold_or_clear, baci_term.wait);
|
|
}
|
|
|
|
else { /* external rate */
|
|
sim_cancel (&baci_term); /* stop I/O service */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_CMDS))
|
|
fprintf (sim_deb, ">>BACI cmds: [OTx%s] Terminal service stopped\n",
|
|
hold_or_clear);
|
|
}
|
|
}
|
|
|
|
baci_icw = baci_obuf; /* load new reference word */
|
|
update_status (); /* loopback may change status */
|
|
break;
|
|
|
|
case 5: /* interrupt status reset */
|
|
baci_isrw = baci_obuf; /* load new reset word */
|
|
|
|
mask = (baci_isrw & OUT_IRQCLR) << /* form reset mask */
|
|
IN_V_IRQCLR; /* for common irqs */
|
|
|
|
if (baci_isrw & OUT_CSC) /* add special char mask bit */
|
|
mask = mask | IN_SPCHAR; /* if requested */
|
|
|
|
baci_status = baci_status & ~mask; /* clear specified status bits */
|
|
break;
|
|
|
|
case 6: /* special character */
|
|
baci_spchar [baci_obuf & OUT_SPCHAR] = /* set special character entry */
|
|
((baci_obuf & OUT_SPFLAG) != 0);
|
|
break;
|
|
}
|
|
break;
|
|
|
|
|
|
case ioCRS: /* control reset */
|
|
master_reset (); /* issue master reset */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_CMDS))
|
|
fputs (">>BACI cmds: [CRS] Master reset\n", sim_deb);
|
|
break;
|
|
|
|
|
|
case ioCLC: /* clear control flip-flop */
|
|
baci.control = CLEAR; /* clear control */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_CMDS))
|
|
fprintf (sim_deb, ">>BACI cmds: [CLC%s] Control cleared\n", hold_or_clear);
|
|
break;
|
|
|
|
|
|
case ioSTC: /* set control flip-flop */
|
|
baci.control = SET; /* set control */
|
|
baci.lockout = CLEAR; /* clear lockout */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_CMDS))
|
|
fprintf (sim_deb, ">>BACI cmds: [STC%s] Control set and lockout cleared\n", hold_or_clear);
|
|
|
|
if (!(signal_set & ioCLF)) /* STC without ,C ? */
|
|
update_status (); /* clearing lockout might interrupt */
|
|
break;
|
|
|
|
|
|
case ioSIR: /* set interrupt request */
|
|
setstdPRL (baci); /* set standard PRL signal */
|
|
setstdIRQ (baci); /* set standard IRQ signal */
|
|
setSRQ (dibptr->select_code, baci.srq); /* set SRQ signal */
|
|
break;
|
|
|
|
|
|
case ioIAK: /* interrupt acknowledge */
|
|
baci.flagbuf = CLEAR;
|
|
break;
|
|
|
|
|
|
default: /* all other signals */
|
|
break; /* are ignored */
|
|
}
|
|
|
|
working_set = working_set & ~signal; /* remove current signal from set */
|
|
}
|
|
|
|
return stat_data;
|
|
}
|
|
|
|
|
|
/* BACI terminal service.
|
|
|
|
The terminal service routine is used to transmit and receive characters.
|
|
|
|
In terminal mode, it is started when a character is ready for output or when
|
|
the Telnet poll routine determines that there are characters ready for input
|
|
and stopped when there are no more characters to output or input. When the
|
|
terminal is quiescent, this routine does not run.
|
|
|
|
In diagnostic mode, it is started whenever an internal baud rate is set and
|
|
stopped when the external clock is requested. In this mode, the routine will
|
|
be called without an attached socket, so character I/O will be skipped.
|
|
|
|
Because there is only one FIFO, the card is half-duplex and must be
|
|
configured for transmit or receive mode. The UART, though, is double-
|
|
buffered, so it may transmit and receive simultaneously. We implement both
|
|
the UART shift and holding registers for each mode.
|
|
|
|
If a character is received by the UART while the card is in transmit mode, it
|
|
will remain in the receiver holding register (RHR). When the mode is
|
|
reversed, the RHR contents will be unloaded into the FIFO. Conversely,
|
|
transmit mode enables the output of the FIFO to be unloaded into the
|
|
transmitter holding register (THR). Characters received or transmitted pass
|
|
through the receiver register (RR) or transmitter register (TR),
|
|
respectively. They are not strictly necessary in terminal (Telnet)
|
|
transactions but are critical to diagnostic operations.
|
|
|
|
The UART signals an overrun if a complete character is received while the RHR
|
|
still contains the previous character. The BACI does not use this signal,
|
|
though; an overrun is only indicated if the FIFO is full, and another
|
|
character is received.
|
|
|
|
In "fast timing" mode, we defer the recognition of a received character until
|
|
the card is put into receive mode for the second or third consecutive ENQ/ACK
|
|
handshake. This improves RTE break-mode recognition. "Realistic timing"
|
|
mode behaves as the hardware does: a character present in the RHR is unloaded
|
|
into the FIFO as soon as receive mode is set.
|
|
|
|
Fast timing mode also enables internal ENQ/ACK handshaking. We allow one
|
|
character time for the RTE driver to turn the card around, as otherwise the
|
|
ACK may not be seen by the driver. Also, the local ACK is supplied after any
|
|
received characters, as the driver detects operator attention only when the
|
|
first character after an ENQ is not an ACK.
|
|
|
|
Finally, fast timing enables buffer combining. For output, all characters
|
|
present in the FIFO are unloaded into the Telnet buffer before initiating a
|
|
packet send. For input, all characters present in the Telnet buffer are
|
|
loaded into the FIFO. This reduces network traffic and decreases simulator
|
|
overhead (there is only one service routine entry per block, rather than one
|
|
per character).
|
|
|
|
In fast output mode, it is imperative that not less than 1500 instructions
|
|
elapse between the first character load to the FIFO and the initiation of
|
|
transmission. The RTE driver must have enough time to output the maximum
|
|
number of contiguous characters (33) and reset the interrupt status flags
|
|
before the service routine is entered. Because all of the characters are
|
|
transmitted as a block, the FIFO empty flag will be set by the service
|
|
routine. If the driver has not yet exited at that time, the buffer-empty
|
|
interrupt will be cleared when the interrupt status reset is done. The
|
|
symptom will be a 3.8-second pause in output until the driver times out.
|
|
|
|
To avoid this, the OTx output character handler does an absolute schedule for
|
|
the first character to ensure that a full character time is used.
|
|
*/
|
|
|
|
t_stat baci_term_svc (UNIT *uptr)
|
|
{
|
|
uint32 data_bits, data_mask;
|
|
const t_bool fast_timing = (baci_term.flags & UNIT_FASTTIME) != 0;
|
|
const t_bool is_attached = (baci_term.flags & UNIT_ATT) != 0;
|
|
t_stat status = SCPE_OK;
|
|
t_bool xmit_loop = TRUE;
|
|
t_bool recv_loop = TRUE;
|
|
|
|
|
|
/* Transmission */
|
|
|
|
while (xmit_loop && (baci_uart_thr & IN_VALID)) { /* valid character in UART? */
|
|
data_bits = 5 + (baci_cfcw & OUT_CHARSIZE); /* calculate number of data bits */
|
|
data_mask = (1 << data_bits) - 1; /* generate mask for data bits */
|
|
baci_uart_tr = baci_uart_thr & data_mask; /* mask data into transmitter register */
|
|
|
|
if ((baci_uart_tr == ENQ) && fast_timing) { /* char is ENQ and fast timing? */
|
|
baci_enq_seen = TRUE; /* set flag instead of transmitting */
|
|
baci_enq_cntr = baci_enq_cntr + 1; /* bump ENQ counter */
|
|
recv_loop = FALSE; /* skip recv to allow time before ACK */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_XFER))
|
|
fprintf (sim_deb, ">>BACI xfer: Character ENQ absorbed internally, "
|
|
"ENQ count = %d\n", baci_enq_cntr);
|
|
}
|
|
|
|
else { /* character is not an ENQ */
|
|
baci_enq_cntr = 0; /* reset ENQ counter */
|
|
|
|
if (is_attached) { /* attached to network? */
|
|
status = tmxr_putc_ln (&baci_ldsc, /* transmit the character */
|
|
baci_uart_tr);
|
|
|
|
if ((status == SCPE_OK) && /* transmitted OK? */
|
|
DEBUG_PRI (baci_dev, DEB_XFER))
|
|
fprintf (sim_deb, ">>BACI xfer: Character %s "
|
|
"transmitted from UART\n", fmt_char (baci_uart_tr));
|
|
}
|
|
}
|
|
|
|
if (status == SCPE_OK) { /* transmitted OK? */
|
|
baci_uart_tr = CLEAR_R; /* clear transmitter register */
|
|
|
|
if (IO_MODE == XMIT) { /* transmit mode? */
|
|
baci_fcount = baci_fcount - 1; /* decrement occupancy counter */
|
|
baci_uart_thr = fifo_get (); /* get next char into UART */
|
|
update_status (); /* update FIFO status */
|
|
}
|
|
|
|
else /* receive mode */
|
|
baci_uart_thr = CLEAR_HR; /* clear holding register */
|
|
|
|
xmit_loop = fast_timing && !baci_enq_seen; /* loop if fast mode and char not ENQ */
|
|
}
|
|
|
|
else
|
|
xmit_loop = FALSE;
|
|
}
|
|
|
|
|
|
/* Deferred reception */
|
|
|
|
if (recv_loop && /* ok to process? */
|
|
baci_uart_rhr && (IO_MODE == RECV) && /* and deferred char in RHR in recv mode? */
|
|
(!baci_enq_seen || (baci_enq_cntr >= 2))) { /* and either no ENQ or at least 2nd ENQ? */
|
|
|
|
baci_uart_rhr = baci_uart_rhr & ~IN_VALID; /* clear valid bit */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_XFER))
|
|
fprintf (sim_deb, ">>BACI xfer: Deferred character %s processed\n",
|
|
fmt_char ((uint8) baci_uart_rhr));
|
|
|
|
fifo_put ((uint8) baci_uart_rhr); /* move deferred character to FIFO */
|
|
baci_uart_rhr = CLEAR_HR; /* clear RHR */
|
|
update_status (); /* update FIFO status */
|
|
}
|
|
|
|
|
|
/* Reception */
|
|
|
|
while (recv_loop && /* OK to process? */
|
|
(baci_uart_rr = tmxr_getc_ln (&baci_ldsc))) { /* and new character available? */
|
|
|
|
if (baci_uart_rr & SCPE_BREAK) { /* break detected? */
|
|
baci_status = baci_status | IN_BREAK; /* set break status */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_XFER))
|
|
fputs (">>BACI xfer: Break detected\n", sim_deb);
|
|
}
|
|
|
|
data_bits = 5 + (baci_cfcw & OUT_CHARSIZE); /* calculate number of data bits */
|
|
data_mask = (1 << data_bits) - 1; /* generate mask for data bits */
|
|
baci_uart_rhr = baci_uart_rr & data_mask; /* mask data into holding register */
|
|
baci_uart_rr = CLEAR_R; /* clear receiver register */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_XFER))
|
|
fprintf (sim_deb, ">>BACI xfer: Character %s received by UART\n",
|
|
fmt_char ((uint8) baci_uart_rhr));
|
|
|
|
if (baci_term.flags & UNIT_CAPSLOCK) /* caps lock mode? */
|
|
baci_uart_rhr = toupper (baci_uart_rhr); /* convert to upper case if lower */
|
|
|
|
if (baci_cfcw & OUT_ECHO) /* echo wanted? */
|
|
tmxr_putc_ln (&baci_ldsc, baci_uart_rhr); /* send it back */
|
|
|
|
if ((IO_MODE == RECV) && !baci_enq_seen) { /* receive mode and not ENQ/ACK? */
|
|
fifo_put ((uint8) baci_uart_rhr); /* put data in FIFO */
|
|
baci_uart_rhr = CLEAR_HR; /* clear RHR */
|
|
update_status (); /* update FIFO status (may set flag) */
|
|
|
|
recv_loop = fast_timing && !IRQ (baci_dib.select_code); /* loop if fast mode and no IRQ */
|
|
}
|
|
|
|
else { /* xmit or ENQ/ACK, leave char in RHR */
|
|
baci_uart_rhr = baci_uart_rhr | IN_VALID; /* set character valid bit */
|
|
recv_loop = FALSE; /* terminate loop */
|
|
}
|
|
}
|
|
|
|
|
|
/* Housekeeping */
|
|
|
|
if (recv_loop && baci_enq_seen) { /* OK to process and ENQ seen? */
|
|
baci_enq_seen = FALSE; /* reset flag */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_XFER))
|
|
fputs (">>BACI xfer: Character ACK generated internally\n", sim_deb);
|
|
|
|
fifo_put (ACK); /* fake ACK from terminal */
|
|
update_status (); /* update FIFO status */
|
|
}
|
|
|
|
if (is_attached) /* attached to network? */
|
|
tmxr_poll_tx (&baci_desc); /* output any accumulated chars */
|
|
|
|
if ((baci_uart_thr & IN_VALID) || baci_enq_seen || /* more to transmit? */
|
|
tmxr_rqln (&baci_ldsc)) /* or more to receive? */
|
|
sim_activate (uptr, uptr->wait); /* reschedule service */
|
|
else
|
|
if (DEBUG_PRI (baci_dev, DEB_CMDS))
|
|
fputs (">>BACI cmds: Terminal service stopped\n", sim_deb);
|
|
|
|
return status;
|
|
}
|
|
|
|
|
|
/* BACI Telnet poll service.
|
|
|
|
This service routine is used to poll for Telnet connections and incoming
|
|
characters. If characters are available, the terminal I/O service routine is
|
|
scheduled. It starts when the socket is attached and stops when the socket
|
|
is detached.
|
|
*/
|
|
|
|
t_stat baci_poll_svc (UNIT *uptr)
|
|
{
|
|
if (baci_term.flags & UNIT_ATT) { /* attached to network? */
|
|
if (tmxr_poll_conn (&baci_desc) >= 0) /* new connection established? */
|
|
baci_ldsc.rcve = 1; /* enable line to receive */
|
|
|
|
tmxr_poll_rx (&baci_desc); /* poll for input */
|
|
|
|
if (tmxr_rqln (&baci_ldsc)) /* chars available? */
|
|
sim_activate (&baci_term, baci_term.wait); /* activate I/O service */
|
|
}
|
|
|
|
if (uptr->wait == POLL_FIRST) /* first poll? */
|
|
uptr->wait = sync_poll (INITIAL); /* initial synchronization */
|
|
else /* not first */
|
|
uptr->wait = sync_poll (SERVICE); /* continue synchronization */
|
|
|
|
sim_activate (uptr, uptr->wait); /* continue polling */
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
|
|
/* Simulator reset routine */
|
|
|
|
t_stat baci_reset (DEVICE *dptr)
|
|
{
|
|
IOPRESET (&baci_dib); /* PRESET device (does not use PON) */
|
|
|
|
baci_ibuf = 0; /* clear input buffer */
|
|
baci_obuf = 0; /* clear output buffer */
|
|
baci_uart_rhr = CLEAR_HR; /* clear receiver holding register */
|
|
|
|
baci_enq_seen = FALSE; /* reset ENQ seen flag */
|
|
baci_enq_cntr = 0; /* clear ENQ counter */
|
|
|
|
baci_term.wait = service_time (baci_icw); /* set terminal I/O time */
|
|
|
|
if (baci_term.flags & UNIT_ATT) { /* device attached? */
|
|
baci_poll.wait = POLL_FIRST; /* set up poll */
|
|
sim_activate (&baci_poll, baci_poll.wait); /* start Telnet poll immediately */
|
|
}
|
|
else
|
|
sim_cancel (&baci_poll); /* else stop Telnet poll */
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
|
|
/* Attach controller */
|
|
|
|
t_stat baci_attach (UNIT *uptr, char *cptr)
|
|
{
|
|
t_stat status = SCPE_OK;
|
|
|
|
status = tmxr_attach (&baci_desc, uptr, cptr); /* attach to socket */
|
|
|
|
if (status == SCPE_OK) {
|
|
baci_poll.wait = POLL_FIRST; /* set up poll */
|
|
sim_activate (&baci_poll, baci_poll.wait); /* start Telnet poll immediately */
|
|
}
|
|
return status;
|
|
}
|
|
|
|
/* Detach controller */
|
|
|
|
t_stat baci_detach (UNIT *uptr)
|
|
{
|
|
t_stat status;
|
|
|
|
status = tmxr_detach (&baci_desc, uptr); /* detach socket */
|
|
baci_ldsc.rcve = 0; /* disable line reception */
|
|
sim_cancel (&baci_poll); /* stop Telnet poll */
|
|
return status;
|
|
}
|
|
|
|
/* Local routines */
|
|
|
|
|
|
/* Master reset.
|
|
|
|
This is the programmed card master reset, not the simulator reset routine.
|
|
Master reset normally clears the UART registers. However, if we are in "fast
|
|
timing" mode, the receiver holding register may hold a deferred character.
|
|
In this case, we do not clear the RHR, unless we are called from the
|
|
simulator reset routine.
|
|
|
|
The HP BACI manual states that master reset "Clears Service Request (SRQ)."
|
|
An examination of the schematic, though, shows that it sets SRQ instead.
|
|
*/
|
|
|
|
static void master_reset (void)
|
|
{
|
|
baci_fput = baci_fget = 0; /* clear FIFO indexes */
|
|
baci_fcount = 0; /* clear FIFO counter */
|
|
memset (baci_fifo, 0, sizeof (baci_fifo)); /* clear FIFO data */
|
|
|
|
baci_uart_thr = CLEAR_HR; /* clear transmitter holding register */
|
|
|
|
if (!(baci_term.flags & UNIT_FASTTIME)) /* real time mode? */
|
|
baci_uart_rhr = CLEAR_HR; /* clear receiver holding register */
|
|
|
|
baci_uart_tr = CLEAR_R; /* clear transmitter register */
|
|
baci_uart_rr = CLEAR_R; /* clear receiver register */
|
|
|
|
baci_uart_clk = 0; /* clear UART clock */
|
|
baci_bcount = 0; /* clear break counter */
|
|
|
|
baci.control = CLEAR; /* clear control */
|
|
baci.flag = baci.flagbuf = SET; /* set flag and flag buffer */
|
|
baci.srq = SET; /* set SRQ */
|
|
baci.lockout = SET; /* set lockout flip-flop */
|
|
|
|
baci_edsiw = 0; /* clear interrupt enables */
|
|
baci_dsrw = 0; /* clear status reference */
|
|
baci_cfcw = baci_cfcw & ~OUT_ECHO; /* clear echo flag */
|
|
baci_icw = baci_icw & OUT_BAUDRATE; /* clear interface control */
|
|
|
|
if (baci_term.flags & UNIT_DIAG) /* diagnostic mode? */
|
|
baci_status = baci_status & ~IN_MODEM | IN_SPARE; /* clear loopback status, set BA */
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* Update status.
|
|
|
|
In diagnostic mode, several of the modem output lines are looped back to the
|
|
input lines. Also, CD is tied to BB (received data), which is presented on
|
|
the TEST status bit via an inversion. Echo mode couples BB to BA
|
|
(transmitted data), which is presented on the SPARE status bit.
|
|
|
|
If a modem line interrupt condition is present and enabled, the DEVINT status
|
|
bit is set. Other potential "standard" interrupt sources are the special
|
|
character, break detected, and overrun/parity error bits. If DCPC transfers
|
|
are not selected, then the FIFO interrupts (buffer empty, half-full, and
|
|
full) and the "data ready" condition (i.e., receive and character modes
|
|
enabled and FIFO not empty) also produces an interrupt request.
|
|
|
|
An interrupt request will set the card flag unless either the lockout or SRQ
|
|
flip-flops are set. SRQ will set if DCPC mode is enabled and there is room
|
|
(transmit mode) or data (receive mode) in the FIFO.
|
|
*/
|
|
|
|
static void update_status (void)
|
|
{
|
|
if (baci_term.flags & UNIT_DIAG) { /* diagnostic mode? */
|
|
baci_status = baci_status & ~IN_DIAG; /* clear loopback flags */
|
|
|
|
if (baci_icw & OUT_SXX) /* SCA to SCF and CF */
|
|
baci_status = baci_status | IN_SXX | IN_CF;
|
|
if ((baci_icw & OUT_CA) && (baci_fcount < 128)) /* CA to CC and CE */
|
|
baci_status = baci_status | IN_CC | IN_CE;
|
|
if (baci_icw & OUT_CD) /* CD to CB */
|
|
baci_status = baci_status | IN_CB;
|
|
else {
|
|
baci_status = baci_status | IN_TEST; /* BB is inversion of CD */
|
|
if (baci_cfcw & OUT_ECHO)
|
|
baci_status = baci_status | IN_SPARE; /* BB couples to BA with echo */
|
|
}
|
|
|
|
if (!(baci_cfcw & OUT_ECHO) && (baci_uart_tr & 1)) /* no echo and UART TR set? */
|
|
baci_status = baci_status | IN_SPARE; /* BA to SPARE */
|
|
}
|
|
|
|
if (baci_edsiw & (baci_status ^ baci_dsrw) & IN_MODEM) /* device interrupt? */
|
|
baci_status = baci_status | IN_DEVINT; /* set flag */
|
|
|
|
if ((baci_status & IN_STDIRQ) || /* standard interrupt? */
|
|
!(baci_icw & OUT_DCPC) && /* or under program control */
|
|
(baci_status & IN_FIFOIRQ) || /* and FIFO interrupt? */
|
|
(IO_MODE == RECV) && /* or receiving */
|
|
(baci_edsiw & OUT_ENCM) && /* and char mode */
|
|
(baci_fget != baci_fput)) { /* and FIFO not empty? */
|
|
|
|
if (baci.lockout) { /* interrupt lockout? */
|
|
if (DEBUG_PRI (baci_dev, DEB_CMDS))
|
|
fputs (">>BACI cmds: Lockout prevents flag set", sim_deb);
|
|
}
|
|
|
|
else if (baci.srq) { /* SRQ set? */
|
|
if (DEBUG_PRI (baci_dev, DEB_CMDS))
|
|
fputs (">>BACI cmds: SRQ prevents flag set", sim_deb);
|
|
}
|
|
|
|
else {
|
|
baci_io (&baci_dib, ioENF, 0); /* set flag */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_CMDS))
|
|
fputs (">>BACI cmds: Flag and lockout set", sim_deb);
|
|
}
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_CMDS))
|
|
fprintf (sim_deb, ", status = %06o\n", baci_status);
|
|
}
|
|
|
|
if ((baci_icw & OUT_DCPC) && /* DCPC enabled? */
|
|
((IO_MODE == XMIT) && (baci_fcount < 128) || /* and xmit and room in FIFO */
|
|
(IO_MODE == RECV) && (baci_fcount > 0))) { /* or recv and data in FIFO? */
|
|
|
|
if (baci.lockout) { /* interrupt lockout? */
|
|
if (DEBUG_PRI (baci_dev, DEB_CMDS))
|
|
fputs (">>BACI cmds: Lockout prevents SRQ set", sim_deb);
|
|
}
|
|
|
|
else {
|
|
baci.srq = SET; /* set SRQ */
|
|
baci_io (&baci_dib, ioSIR, 0); /* set interrupt request */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_CMDS))
|
|
fputs (">>BACI cmds: SRQ set", sim_deb);
|
|
}
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_CMDS))
|
|
fprintf (sim_deb, ", status = %06o\n", baci_status);
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* Calculate service time from baud rate.
|
|
|
|
Service times are based on 1580 instructions per second, which is the 1000
|
|
E-Series execution speed. The "external clock" rate uses the 9600 baud rate,
|
|
as most real terminals were set to their maximum rate.
|
|
|
|
Note that the RTE driver has a race condition that will trip if the service
|
|
time is less than 1500 instructions. Therefore, these times cannot be
|
|
shortened arbitrarily.
|
|
*/
|
|
|
|
static int32 service_time (uint32 control_word)
|
|
{
|
|
static const int32 ticks [] = { 1646, 316000, 210667, 143636, 117472, 105333, 52667, 26333,
|
|
17556, 13667, 8778, 6583, 4389, 3292, 2194, 1646 };
|
|
|
|
return ticks [GET_BAUDRATE (control_word)]; /* return service time for indicated rate */
|
|
}
|
|
|
|
|
|
/* FIFO manipulation routines.
|
|
|
|
The BACI is a half-duplex device that has a single 128-byte FIFO that is used
|
|
for both transmitting and receiving. Whether the FIFO is connected to the
|
|
input or output of the UART is determined by the XMIT bit in word 4. A
|
|
separate 8-bit FIFO up/down counter is used to track the number of bytes
|
|
available. FIFO operations are complicated slightly by the UART, which is
|
|
double-buffered.
|
|
|
|
The FIFO is modeled as a circular 128-byte array. Separate get and put
|
|
indexes track the current data extent. A FIFO character counter is used to
|
|
derive empty, half-full, and full status indications, and counts greater than
|
|
128 are possible.
|
|
|
|
In the transmit mode, an OTA/B with word type 0 generates SI (shift in) to
|
|
load the FIFO and increment the FIFO counter. When the UART is ready for a
|
|
character, THRE (UART transmitter holding register empty) and OR (FIFO output
|
|
ready) generate THRL (transmitter holding register load) and SO (FIFO shift
|
|
out) to unload the FIFO into the UART. When transmission of the character
|
|
over the serial line is complete, TRE (UART transmitter register empty)
|
|
decrements the FIFO counter.
|
|
|
|
In the receive mode, the UART sets DR (data received) when has obtained a
|
|
character, which generates SI (FIFO shift in) to load the FIFO and increment
|
|
the FIFO counter. This also clocks PE (UART parity error) and IR (FIFO input
|
|
ready) into the overrun/parity error flip-flop. An LIA/B with control set
|
|
and with OR (FIFO output ready) set, indicating valid data is available,
|
|
generates SO (FIFO shift out) to unload the FIFO and decrement the FIFO
|
|
counter.
|
|
|
|
Presuming an empty FIFO and UART, double-buffering in the transmit mode means
|
|
that the first byte deposited into the FIFO is removed and loaded into the
|
|
UART transmitter holding register. Even though the FIFO is actually empty,
|
|
the FIFO counter remains at 1, because FIFO decrement does not occur until
|
|
the UART actually transmits the data byte. The intended mode of operation is
|
|
to wait until the buffer-empty interrupt occurs, which will happen when the
|
|
final character is transmitted from the UART, before switching the BACI into
|
|
receive mode. The counter will match the FIFO contents properly, i.e., will
|
|
be zero, when the UART transmission completes.
|
|
|
|
However, during diagnostic operation, FIFO testing will take this "extra"
|
|
count into consideration. For example, after a master reset, if ten bytes
|
|
are written to the FIFO in transmit mode, the first byte will pass through to
|
|
the UART transmitter holding register, and the next nine bytes will fill the
|
|
FIFO. The first byte read in receive mode will be byte 2, not byte 1; the
|
|
latter remains in the UART. After the ninth byte is read, OR (FIFO output
|
|
ready) will drop, resetting the valid data flip-flop and inhibiting any
|
|
further FIFO counter decrement pulses. The counter will remain at 1 until
|
|
another master reset is done.
|
|
|
|
The same situation occurs in the RTE driver during ENQ/ACK handshakes. The
|
|
driver sets the card to transmit mode, sends an ENQ, waits for a short time
|
|
for the character to "bubble through" the FIFO and into the UART transmitter
|
|
holding register, and then switches the card to receive mode to await the
|
|
interrupt from the reception of the ACK. This is done to avoid the overhead
|
|
of the interrupt after the ENQ is transmitted. However, switching the card
|
|
into receive mode before the ENQ is actually transmitted means that the FIFO
|
|
counter will not decrement when that occurs, leaving the counter in an "off
|
|
by one" configuration. To remedy this, the driver does a master reset after
|
|
the ACK is received.
|
|
|
|
Therefore, for proper operation, we must simulate both the UART
|
|
double-buffering and the decoupling of the FIFO and FIFO character counter.
|
|
*/
|
|
|
|
|
|
/* Get a character from the FIFO.
|
|
|
|
In receive mode, getting a character from the FIFO decrements the character
|
|
counter concurrently. In transmit mode, the counter must not be decremented
|
|
until the character is actually sent; in this latter case, the caller is
|
|
responsible for decrementing. Attempting to get a character when the FIFO is
|
|
empty returns the last valid data and does not alter the FIFO indexes.
|
|
|
|
Because the FIFO counter may indicate more characters than are actually in
|
|
the FIFO, the count is not an accurate indicator of FIFO fill status. We
|
|
account for this by examining the get and put indexes. If these are equal,
|
|
then the FIFO is either empty or exactly full. We differentiate by examining
|
|
the FIFO counter and seeing if it is >= 128, indicating an (over)full
|
|
condition. If it is < 128, then the FIFO is empty, even if the counter is
|
|
not 0.
|
|
*/
|
|
|
|
static uint32 fifo_get (void)
|
|
{
|
|
uint32 data;
|
|
|
|
data = baci_fifo [baci_fget]; /* get character */
|
|
|
|
if ((baci_fget != baci_fput) || (baci_fcount >= 128)) { /* FIFO occupied? */
|
|
if (IO_MODE == RECV) /* receive mode? */
|
|
baci_fcount = baci_fcount - 1; /* decrement occupancy counter */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_BUF))
|
|
fprintf (sim_deb, ">>BACI buf: Character %s get from FIFO [%d], "
|
|
"character counter = %d\n", fmt_char (data), baci_fget, baci_fcount);
|
|
|
|
baci_fget = (baci_fget + 1) % FIFO_SIZE; /* bump index modulo array size */
|
|
|
|
if (baci_spchar [data]) /* is it a special character? */
|
|
data = data | IN_SPFLAG; /* set flag */
|
|
|
|
data = data | IN_VALID; /* set valid flag in return */
|
|
}
|
|
|
|
else /* FIFO empty */
|
|
if (DEBUG_PRI (baci_dev, DEB_BUF))
|
|
fprintf (sim_deb, ">>BACI buf: Attempted get on empty FIFO, "
|
|
"character count = %d\n", baci_fcount);
|
|
|
|
if (baci_fcount == 0) /* count now zero? */
|
|
baci_status = baci_status | IN_BUFEMPTY; /* set buffer empty flag */
|
|
|
|
update_status (); /* update FIFO status */
|
|
|
|
return data; /* return character */
|
|
}
|
|
|
|
|
|
/* Put a character into the FIFO.
|
|
|
|
In transmit mode, available characters are unloaded from the FIFO into the
|
|
UART transmitter holding register as soon as the THR is empty. That is,
|
|
given an empty FIFO and THR, a stored character will pass through the FIFO
|
|
and into the THR immediately. Otherwise, the character will remain in the
|
|
FIFO. In either case, the FIFO character counter is incremented.
|
|
|
|
In receive mode, characters are only unloaded from the FIFO explicitly, so
|
|
stores always load the FIFO and increment the counter.
|
|
*/
|
|
|
|
static void fifo_put (uint8 ch)
|
|
{
|
|
uint32 index = 0;
|
|
t_bool pass_thru;
|
|
|
|
pass_thru = (IO_MODE == XMIT) && /* pass thru if XMIT and THR empty */
|
|
!(baci_uart_thr & IN_VALID);
|
|
|
|
if (pass_thru) /* pass char thru to UART */
|
|
baci_uart_thr = ch | IN_VALID; /* and set valid character flag */
|
|
|
|
else { /* RECV or THR occupied */
|
|
index = baci_fput; /* save current index */
|
|
baci_fifo [baci_fput] = ch; /* put char in FIFO */
|
|
baci_fput = (baci_fput + 1) % FIFO_SIZE; /* bump index modulo array size */
|
|
}
|
|
|
|
baci_fcount = baci_fcount + 1; /* increment occupancy counter */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_BUF))
|
|
if (pass_thru)
|
|
fprintf (sim_deb, ">>BACI buf: Character %s put to UART transmitter holding register, "
|
|
"character counter = 1\n", fmt_char (ch));
|
|
else
|
|
fprintf (sim_deb, ">>BACI buf: Character %s put to FIFO [%d], "
|
|
"character counter = %d\n", fmt_char (ch), index, baci_fcount);
|
|
|
|
if ((IO_MODE == RECV) && (baci_spchar [ch])) /* receive mode and special character? */
|
|
baci_status = baci_status | IN_SPCHAR; /* set special char seen flag */
|
|
|
|
if (baci_fcount == 64) /* FIFO half full? */
|
|
baci_status = baci_status | IN_BUFHALF;
|
|
|
|
else if (baci_fcount == 128) /* FIFO completely full? */
|
|
baci_status = baci_status | IN_BUFFULL;
|
|
|
|
else if (baci_fcount > 128) /* FIFO overrun? */
|
|
baci_status = baci_status | IN_OVRUNPE;
|
|
|
|
update_status (); /* update FIFO status */
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* Clock the UART.
|
|
|
|
In the diagnostic mode, the DIAG output is connected to the EXT CLK input.
|
|
If the baud rate of the Interface Control Word is set to "external clock,"
|
|
then raising and lowering the DIAG output will pulse the UART transmitter and
|
|
receiver clock lines, initiating transmission or reception of serial data.
|
|
Sixteen pulses are needed to shift one bit through the UART.
|
|
|
|
The diagnostic hood ties CD to BB (received data), so bits presented to CD
|
|
via the Interface Control Word can be clocked into the UART receiver register
|
|
(RR). Similarly, the UART transmitter register (TR) shifts data onto BA
|
|
(transmitted data), and the hood ties BA to SPARE, so transmitted bits are
|
|
presented to the SPARE bit in the status word.
|
|
|
|
"baci_uart_clk" contains the number of clock pulses remaining for the current
|
|
character transfer. Calling this routine with "baci_uart_clk" = 0 initiates
|
|
a transfer. The value will be a multiple of 16 and will account for the
|
|
start bit, the data bits, the optional parity bit, and the stop bits. The
|
|
transfer terminates when the count reaches zero (or eight, if 1.5 stop bits
|
|
is selected during transmission).
|
|
|
|
Every sixteen pulses when the lower four bits of the clock count are zero,
|
|
the transmitter or receiver register will be shifted to present or receive a
|
|
new serial bit. The registers are initialized to all ones for proper
|
|
handling of the stop bits.
|
|
|
|
A break counter is maintained and incremented whenever a space (0) condition
|
|
is seen on the serial line. After 160 clock times (10 bits) of continuous
|
|
zero data, the "break seen" status is set.
|
|
|
|
This routine is not used in terminal mode.
|
|
*/
|
|
|
|
static void clock_uart (void)
|
|
{
|
|
uint32 uart_bits, data_bits, data_mask, parity, bit_low, i;
|
|
|
|
if (baci_uart_clk > 0) { /* transfer in progress? */
|
|
bit_low = (baci_icw & OUT_CD); /* get current receive bit */
|
|
|
|
if ((baci_uart_clk & 017) == 0) /* end of a bit? */
|
|
if (IO_MODE == XMIT) /* transmit? */
|
|
baci_uart_tr = baci_uart_tr >> 1; /* shift new bit onto line */
|
|
else /* receive? */
|
|
baci_uart_rr = (baci_uart_rr >> 1) & /* shift new bit in */
|
|
(bit_low ? ~SIGN : -1); /* (inverted sense) */
|
|
|
|
if (bit_low) { /* another low bit? */
|
|
baci_bcount = baci_bcount + 1; /* update break counter */
|
|
|
|
if (baci_bcount == 160) { /* break held long enough? */
|
|
baci_status = baci_status | IN_BREAK; /* set break flag */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_XFER))
|
|
fputs (">>BACI xfer: Break detected\n", sim_deb);
|
|
}
|
|
}
|
|
|
|
else /* high bit? */
|
|
baci_bcount = 0; /* reset break counter */
|
|
|
|
baci_uart_clk = baci_uart_clk - 1; /* decrement clocks remaining */
|
|
|
|
if ((IO_MODE == XMIT) && /* transmit mode? */
|
|
((baci_uart_clk == 0) || /* and end of character? */
|
|
(baci_uart_clk == 8) && /* or last stop bit */
|
|
(baci_cfcw & OUT_STBITS) && /* and extra stop bit requested */
|
|
((baci_cfcw & OUT_CHARSIZE) == 0))) { /* and 1.5 stop bits used? */
|
|
|
|
baci_uart_clk = 0; /* clear clock count */
|
|
|
|
baci_fcount = baci_fcount - 1; /* decrement occupancy counter */
|
|
baci_uart_thr = fifo_get (); /* get next char into THR */
|
|
update_status (); /* update FIFO status */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_XFER))
|
|
fprintf (sim_deb, ">>BACI xfer: UART transmitter empty, "
|
|
"holding register = %06o\n", baci_uart_thr);
|
|
}
|
|
|
|
else if ((IO_MODE == RECV) && /* receive mode? */
|
|
(baci_uart_clk == 0)) { /* and end of character? */
|
|
|
|
data_bits = 5 + (baci_cfcw & OUT_CHARSIZE); /* calculate number of data bits */
|
|
data_mask = (1 << data_bits) - 1; /* generate mask for data bits */
|
|
|
|
uart_bits = data_bits + /* calculate UART bits as data bits */
|
|
((baci_cfcw & OUT_PARITY) != 0) + /* plus parity bit if used */
|
|
((baci_cfcw & OUT_STBITS) != 0); /* plus extra stop bit if used */
|
|
|
|
baci_uart_rhr = baci_uart_rr >> (16 - uart_bits); /* position data to right align */
|
|
baci_uart_rr = CLEAR_R; /* clear receiver register */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_XFER))
|
|
fprintf (sim_deb, ">>BACI xfer: UART receiver = %06o (%s)\n",
|
|
baci_uart_rhr, fmt_char (baci_uart_rhr & data_mask));
|
|
|
|
fifo_put (baci_uart_rhr & data_mask); /* put data in FIFO */
|
|
update_status (); /* update FIFO status */
|
|
|
|
if (baci_cfcw & OUT_PARITY) { /* parity present? */
|
|
data_mask = data_mask << 1 | 1; /* widen mask to encompass parity */
|
|
uart_bits = baci_uart_rhr & data_mask; /* get data plus parity */
|
|
|
|
parity = (baci_cfcw & OUT_PAREVEN) == 0; /* preset for even/odd parity */
|
|
|
|
for (i = 0; i < data_bits + 1; i++) { /* calc parity of data + parity bit */
|
|
parity = parity ^ uart_bits; /* parity calculated in LSB */
|
|
uart_bits = uart_bits >> 1;
|
|
}
|
|
|
|
if (parity & 1) { /* parity error? */
|
|
baci_status = baci_status | IN_OVRUNPE; /* report it */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_XFER))
|
|
fputs (">>BACI xfer: Parity error detected\n", sim_deb);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if ((baci_uart_clk == 0) && /* start of transfer? */
|
|
((IO_MODE == RECV) || /* and receive mode */
|
|
(baci_uart_thr & IN_VALID))) { /* or character ready to transmit? */
|
|
|
|
data_bits = 5 + (baci_cfcw & OUT_CHARSIZE); /* calculate number of data bits */
|
|
|
|
uart_bits = data_bits + /* calculate UART bits as data bits */
|
|
((baci_cfcw & OUT_PARITY) != 0) + /* plus parity bit if used */
|
|
2 + ((baci_cfcw & OUT_STBITS) != 0); /* plus start/stop and extra stop if used */
|
|
|
|
baci_uart_clk = 16 * uart_bits; /* calculate clocks pulses expected */
|
|
|
|
if (IO_MODE == XMIT) { /* transmit mode? */
|
|
data_mask = (1 << data_bits) - 1; /* generate mask for data bits */
|
|
baci_uart_tr = baci_uart_thr & data_mask; /* mask data into holding register */
|
|
|
|
if (baci_cfcw & OUT_PARITY) { /* add parity to this transmission? */
|
|
uart_bits = baci_uart_tr; /* copy data bits */
|
|
parity = (baci_cfcw & OUT_PAREVEN) == 0; /* preset for even/odd parity */
|
|
|
|
for (i = 0; i < data_bits; i++) { /* calculate parity of data */
|
|
parity = parity ^ uart_bits; /* parity calculated in LSB */
|
|
uart_bits = uart_bits >> 1;
|
|
}
|
|
|
|
data_mask = data_mask << 1 | 1; /* extend mask for the parity bit */
|
|
baci_uart_tr = baci_uart_tr | /* include parity in transmission register */
|
|
(parity & 1) << data_bits; /* (mask to parity bit and position it) */
|
|
}
|
|
|
|
baci_uart_tr = (~data_mask | baci_uart_tr) << 2 | 1; /* form serial data stream */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_XFER))
|
|
fprintf (sim_deb, ">>BACI xfer: UART transmitter = %06o (%s), "
|
|
"clock count = %d\n", baci_uart_tr & DMASK,
|
|
fmt_char (baci_uart_thr & data_mask), baci_uart_clk);
|
|
}
|
|
|
|
else {
|
|
baci_uart_rr = CLEAR_R; /* clear receiver register */
|
|
|
|
if (DEBUG_PRI (baci_dev, DEB_XFER))
|
|
fprintf (sim_deb, ">>BACI xfer: UART receiver empty, "
|
|
"clock count = %d\n", baci_uart_clk);
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|