Added KS10 support. CPU Redid instruction decode to improve performance Triple-I display cleanup. Normalized end of line to DOS/Unix. KL10 FE, Cleanup issues with TTY devices hanging simulator.. Fixed errors in RH20 device. RP and TU drives more independent of RH controller.
1604 lines
64 KiB
C
1604 lines
64 KiB
C
/* ks10_dup.c: PDP-11 DUP11/DPV11 bit synchronous interface
|
|
|
|
Copyright (c) 2013, Mark Pizzolato
|
|
Modified for use with KA10 by Richard Cornwell 2022
|
|
|
|
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
|
|
ROBERT M SUPNIK 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 Robert M Supnik shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from Robert M Supnik.
|
|
|
|
dup DUP11 Unibus/DPV11 Qbus bit synchronous interface
|
|
|
|
This module implements a bit synchronous interface to support DDCMP. Other
|
|
synchronous protocols which may have been supported on the DUP11/DPV11 bit
|
|
synchronous interface are explicitly not supported.
|
|
|
|
Connections are modeled with a tcp session with connection management and
|
|
I/O provided by the tmxr library.
|
|
|
|
The wire protocol implemented is native DDCMP WITHOUT the DDCMP SYNC
|
|
characters both initially and between DDCMP packets.
|
|
|
|
15-May-13 MP Initial implementation
|
|
*/
|
|
|
|
#include "kx10_defs.h"
|
|
|
|
#include "sim_tmxr.h"
|
|
#include "ks10_dup.h"
|
|
#include "pdp11_ddcmp.h"
|
|
|
|
#if NUM_DEVS_DUP > 0
|
|
|
|
#define DUP_WAIT 50 /* Minimum character time */
|
|
#define DUP_CONNECT_POLL 2 /* Seconds */
|
|
|
|
extern int32 tmxr_poll; /* calibrated delay */
|
|
|
|
static uint16 dup_rxcsr[NUM_DEVS_DUP];
|
|
static uint16 dup_rxdbuf[NUM_DEVS_DUP];
|
|
static uint16 dup_parcsr[NUM_DEVS_DUP];
|
|
static uint16 dup_txcsr[NUM_DEVS_DUP];
|
|
static uint16 dup_txdbuf[NUM_DEVS_DUP];
|
|
static t_bool dup_W3[NUM_DEVS_DUP];
|
|
static t_bool dup_W5[NUM_DEVS_DUP];
|
|
static t_bool dup_W6[NUM_DEVS_DUP];
|
|
static uint32 dup_rxi = 0; /* rcv interrupts */
|
|
static uint32 dup_txi = 0; /* xmt interrupts */
|
|
static uint32 dup_wait[NUM_DEVS_DUP]; /* rcv/xmt byte delay */
|
|
static uint32 dup_speed[NUM_DEVS_DUP]; /* line speed (bits/sec) */
|
|
static uint8 *dup_rcvpacket[NUM_DEVS_DUP]; /* rcv buffer */
|
|
static uint16 dup_rcvpksize[NUM_DEVS_DUP]; /* rcv buffer size */
|
|
static uint16 dup_rcvpkbytes[NUM_DEVS_DUP]; /* rcv buffer size of packet */
|
|
static uint16 dup_rcvpkinoff[NUM_DEVS_DUP]; /* rcv packet in offset */
|
|
static uint8 *dup_xmtpacket[NUM_DEVS_DUP]; /* xmt buffer */
|
|
static uint16 dup_xmtpksize[NUM_DEVS_DUP]; /* xmt buffer size */
|
|
static uint16 dup_xmtpkoffset[NUM_DEVS_DUP]; /* xmt buffer offset */
|
|
static uint32 dup_xmtpkstart[NUM_DEVS_DUP]; /* xmt packet start time */
|
|
static uint16 dup_xmtpkbytes[NUM_DEVS_DUP]; /* xmt packet size of packet */
|
|
static uint16 dup_xmtpkdelaying[NUM_DEVS_DUP]; /* xmt packet speed delaying completion */
|
|
static int32 dup_corruption[NUM_DEVS_DUP]; /* data corrupting troll hunger value */
|
|
|
|
static PACKET_DATA_AVAILABLE_CALLBACK dup_rcv_packet_data_callback[NUM_DEVS_DUP];
|
|
static PACKET_TRANSMIT_COMPLETE_CALLBACK dup_xmt_complete_callback[NUM_DEVS_DUP];
|
|
static MODEM_CHANGE_CALLBACK dup_modem_change_callback[NUM_DEVS_DUP];
|
|
|
|
static int dup_rd (DEVICE *dptr, t_addr PA, uint16 *data, int32 access);
|
|
static int dup_wr (DEVICE *dptr, t_addr PA, uint16 data, int32 access);
|
|
static t_stat dup_set_modem (int32 dup, int32 rxcsr_bits);
|
|
static t_stat dup_get_modem (int32 dup);
|
|
static t_stat dup_svc (UNIT *uptr);
|
|
static t_stat dup_poll_svc (UNIT *uptr);
|
|
static t_stat dup_rcv_byte (int32 dup);
|
|
static t_stat dup_reset (DEVICE *dptr);
|
|
static t_stat dup_attach (UNIT *uptr, CONST char *ptr);
|
|
static t_stat dup_detach (UNIT *uptr);
|
|
static t_stat dup_clear (int32 dup, t_bool flag);
|
|
static void dup_update_rcvi (void);
|
|
static void dup_update_xmti (void);
|
|
static void dup_clr_rxint (int32 dup);
|
|
static void dup_set_rxint (int32 dup);
|
|
static void dup_clr_txint (int32 dup);
|
|
static void dup_set_txint (int32 dup);
|
|
static t_stat dup_setnl (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
|
static t_stat dup_setspeed (UNIT* uptr, int32 val, CONST char* cptr, void* desc);
|
|
static t_stat dup_showspeed (FILE* st, UNIT* uptr, int32 val, CONST void* desc);
|
|
static t_stat dup_setcorrupt (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
|
|
static t_stat dup_showcorrupt (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
|
static t_stat dup_set_W3 (UNIT* uptr, int32 val, CONST char* cptr, void* desc);
|
|
static t_stat dup_show_W3 (FILE* st, UNIT* uptr, int32 val, CONST void* desc);
|
|
static t_stat dup_set_W5 (UNIT* uptr, int32 val, CONST char* cptr, void* desc);
|
|
static t_stat dup_show_W5 (FILE* st, UNIT* uptr, int32 val, CONST void* desc);
|
|
static t_stat dup_set_W6 (UNIT* uptr, int32 val, CONST char* cptr, void* desc);
|
|
static t_stat dup_show_W6 (FILE* st, UNIT* uptr, int32 val, CONST void* desc);
|
|
static t_stat dup_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
|
|
static t_stat dup_help_attach (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
|
|
static const char *dup_description (DEVICE *dptr);
|
|
|
|
/* RXCSR - 16XXX0 - receiver control/status register */
|
|
|
|
static BITFIELD dup_rxcsr_bits[] = {
|
|
BIT(BDATSET), /* Data Set Change B */
|
|
#define RXCSR_V_BDATSET 0
|
|
#define RXCSR_M_BDATSET (1<<RXCSR_V_BDATSET)
|
|
BIT(DTR), /* Data Terminal Ready */
|
|
#define RXCSR_V_DTR 1
|
|
#define RXCSR_M_DTR (1<<RXCSR_V_DTR)
|
|
BIT(RTS), /* Request To Send */
|
|
#define RXCSR_V_RTS 2
|
|
#define RXCSR_M_RTS (1<<RXCSR_V_RTS)
|
|
BIT(SECXMT), /* Secondary Transmit Data */
|
|
#define RXCSR_V_SECXMT 3
|
|
#define RXCSR_M_SECXMT (1<<RXCSR_V_SECXMT)
|
|
BIT(RCVEN), /* Receiver Enable */
|
|
#define RXCSR_V_RCVEN 4
|
|
#define RXCSR_M_RCVEN (1<<RXCSR_V_RCVEN)
|
|
BIT(DSCIE), /* Data Set Change Interrupt Enable */
|
|
#define RXCSR_V_DSCIE 5
|
|
#define RXCSR_M_DSCIE (1<<RXCSR_V_DSCIE)
|
|
BIT(RXIE), /* Receive Interrupt Enable */
|
|
#define RXCSR_V_RXIE 6
|
|
#define RXCSR_M_RXIE (1<<RXCSR_V_RXIE)
|
|
BIT(RXDONE), /* Receive Done */
|
|
#define RXCSR_V_RXDONE 7
|
|
#define RXCSR_M_RXDONE (1<<RXCSR_V_RXDONE)
|
|
BIT(STRSYN), /* Strip Sync */
|
|
#define RXCSR_V_STRSYN 8
|
|
#define RXCSR_M_STRSYN (1<<RXCSR_V_STRSYN)
|
|
BIT(DSR), /* Data Set Ready */
|
|
#define RXCSR_V_DSR 9
|
|
#define RXCSR_M_DSR (1<<RXCSR_V_DSR)
|
|
BIT(SECRCV), /* Secondary Receive Data */
|
|
#define RXCSR_V_SECRCV 10
|
|
#define RXCSR_M_SECRCV (1<<RXCSR_V_SECRCV)
|
|
BIT(RXACT), /* Receive Active */
|
|
#define RXCSR_V_RXACT 11
|
|
#define RXCSR_M_RXACT (1<<RXCSR_V_RXACT)
|
|
BIT(DCD), /* Carrier */
|
|
#define RXCSR_V_DCD 12
|
|
#define RXCSR_M_DCD (1<<RXCSR_V_DCD)
|
|
BIT(CTS), /* Clear to Send */
|
|
#define RXCSR_V_CTS 13
|
|
#define RXCSR_M_CTS (1<<RXCSR_V_CTS)
|
|
BIT(RING), /* Ring */
|
|
#define RXCSR_V_RING 14
|
|
#define RXCSR_M_RING (1<<RXCSR_V_RING)
|
|
BIT(DSCHNG), /* Data Set Change */
|
|
#define RXCSR_V_DSCHNG 15
|
|
#define RXCSR_M_DSCHNG (1<<RXCSR_V_DSCHNG)
|
|
ENDBITS
|
|
};
|
|
#define RXCSR_A_MODEM_BITS (RXCSR_M_RING | RXCSR_M_CTS)
|
|
#define RXCSR_B_MODEM_BITS (RXCSR_M_DSR | RXCSR_M_DCD)
|
|
#define RXCSR_WRITEABLE (RXCSR_M_STRSYN|RXCSR_M_RXIE|RXCSR_M_DSCIE|RXCSR_M_RCVEN|RXCSR_M_SECXMT|RXCSR_M_RTS|RXCSR_M_DTR)
|
|
|
|
/* RXDBUF - 16XXX2 - receiver Data Buffer register */
|
|
|
|
static BITFIELD dup_rxdbuf_bits[] = {
|
|
BITF(RXDBUF,8), /* Receive Data Buffer */
|
|
#define RXDBUF_V_RXDBUF 0
|
|
#define RXDBUF_S_RXDBUF 8
|
|
#define RXDBUF_M_RXDBUF (((1<<RXDBUF_S_RXDBUF)-1)<<RXDBUF_V_RXDBUF)
|
|
BIT(RSTRMSG), /* Receiver Start of Message */
|
|
#define RXDBUF_V_RSTRMSG 8
|
|
#define RXDBUF_M_RSTRMSG (1<<RXDBUF_V_RSTRMSG)
|
|
BIT(RENDMSG), /* Receiver End Of Message */
|
|
#define RXDBUF_V_RENDMSG 9
|
|
#define RXDBUF_M_RENDMSG (1<<RXDBUF_V_RENDMSG)
|
|
BIT(RABRT), /* Receiver Abort */
|
|
#define RXDBUF_V_RABRT 10
|
|
#define RXDBUF_M_RABRT (1<<RXDBUF_V_RABRT)
|
|
BITNCF(1), /* reserved */
|
|
BIT(RCRCER), /* Receiver CRC Error */
|
|
#define RXDBUF_V_RCRCER 12
|
|
#define RXDBUF_M_RCRCER (1<<RXDBUF_V_RCRCER)
|
|
BITNCF(1), /* reserved */
|
|
BIT(RXOVR), /* Receiver Overrun */
|
|
#define RXDBUF_V_RXOVR 14
|
|
#define RXDBUF_M_RXOVR (1<<RXDBUF_V_RXOVR)
|
|
BIT(RXERR), /* Receiver Error */
|
|
#define RXDBUF_V_RXERR 15
|
|
#define RXDBUF_M_RXERR (1<<RXDBUF_V_RXERR)
|
|
ENDBITS
|
|
};
|
|
#define RXDBUF_MBZ ((1<<13)|(1<<11))
|
|
|
|
/* PARCSR - 16XXX2 - Parameter Control/Status register */
|
|
|
|
static BITFIELD dup_parcsr_bits[] = {
|
|
BITF(ADSYNC,8), /* Secondart Station Address/Receiver Sync Char */
|
|
#define PARCSR_V_ADSYNC 0
|
|
#define PARCSR_S_ADSYNC 8
|
|
#define PARCSR_M_ADSYNC (((1<<PARCSR_S_ADSYNC)-1)<<PARCSR_V_ADSYNC)
|
|
BITNCF(1), /* reserved */
|
|
BIT(NOCRC), /* No CRC */
|
|
#define PARCSR_V_NOCRC 9
|
|
#define PARCSR_M_NOCRC (1<<PARCSR_V_NOCRC)
|
|
BITNCF(2), /* reserved */
|
|
BIT(SECMODE), /* Secondary Mode Select */
|
|
#define PARCSR_V_SECMODE 12
|
|
#define PARCSR_M_SECMODE (1<<PARCSR_V_SECMODE)
|
|
BITNCF(2), /* reserved */
|
|
BIT(DECMODE), /* DEC Mode */
|
|
#define PARCSR_V_DECMODE 15
|
|
#define PARCSR_M_DECMODE (1<<PARCSR_V_DECMODE)
|
|
ENDBITS
|
|
};
|
|
#define PARCSR_MBZ ((1<<14)|(1<<13)|(1<<11)|(1<<10)|(1<<8))
|
|
#define PARCSR_WRITEABLE (PARCSR_M_DECMODE|PARCSR_M_SECMODE|PARCSR_M_NOCRC|PARCSR_M_ADSYNC)
|
|
|
|
/* TXCSR - 16XXX4 - Transmitter Control/Status register */
|
|
|
|
static BITFIELD dup_txcsr_bits[] = {
|
|
BITNCF(3), /* reserved */
|
|
BIT(HALFDUP), /* Half Duplex */
|
|
#define TXCSR_V_HALFDUP 3
|
|
#define TXCSR_M_HALFDUP (1<<TXCSR_V_HALFDUP)
|
|
BIT(SEND), /* Enable Transmit */
|
|
#define TXCSR_V_SEND 4
|
|
#define TXCSR_M_SEND (1<<TXCSR_V_SEND)
|
|
BITNCF(1), /* reserved */
|
|
BIT(TXIE), /* Transmit Interrupt Enable */
|
|
#define TXCSR_V_TXIE 6
|
|
#define TXCSR_M_TXIE (1<<TXCSR_V_TXIE)
|
|
BIT(TXDONE), /* Transmit Done */
|
|
#define TXCSR_V_TXDONE 7
|
|
#define TXCSR_M_TXDONE (1<<TXCSR_V_TXDONE)
|
|
BIT(DRESET), /* Device Reset */
|
|
#define TXCSR_V_DRESET 8
|
|
#define TXCSR_M_DRESET (1<<TXCSR_V_DRESET)
|
|
BIT(TXACT), /* Transmit Active */
|
|
#define TXCSR_V_TXACT 9
|
|
#define TXCSR_M_TXACT (1<<TXCSR_V_TXACT)
|
|
BIT(MAIDATA), /* Maintenance Mode Data Bit */
|
|
#define TXCSR_V_MAIDATA 10
|
|
#define TXCSR_M_MAIDATA (1<<TXCSR_V_MAIDATA)
|
|
BITF(MAISEL,2), /* Maintenance Select B and A */
|
|
#define TXCSR_V_MAISEL 11
|
|
#define TXCSR_S_MAISEL 2
|
|
#define TXCSR_M_MAISEL (((1<<TXCSR_S_MAISEL)-1)<<TXCSR_V_MAISEL)
|
|
#define TXCSR_GETMAISEL(x) (((x) & TXCSR_M_MAISEL) >> TXCSR_V_MAISEL)
|
|
BIT(MAISSCLK), /* Maintenance Single Step Clock */
|
|
#define TXCSR_V_MAISSCLK 13
|
|
#define TXCSR_M_MAISSCLK (1<<TXCSR_V_MAISSCLK)
|
|
BIT(TXMNTOUT), /* Transmit Maint Data Out */
|
|
#define TXCSR_V_TXMNTOUT 14
|
|
#define TXCSR_M_TXMNTOUT (1<<TXCSR_V_TXMNTOUT)
|
|
BIT(TXDLAT), /* Transmit Data Late */
|
|
#define TXCSR_V_TXDLAT 15
|
|
#define TXCSR_M_TXDLAT (1<<TXCSR_V_TXDLAT)
|
|
ENDBITS
|
|
};
|
|
#define TXCSR_MBZ ((1<<5)|(1<<2)|(1<<1)|(1<<0))
|
|
#define TXCSR_WRITEABLE (TXCSR_M_MAISSCLK|TXCSR_M_MAISEL|TXCSR_M_MAIDATA|TXCSR_M_DRESET|TXCSR_M_TXIE|TXCSR_M_SEND|TXCSR_M_HALFDUP)
|
|
|
|
/* TXDBUF - 16XXX6 - transmitter Data Buffer register */
|
|
|
|
static BITFIELD dup_txdbuf_bits[] = {
|
|
BITF(TXDBUF,8), /* Transmit Data Buffer */
|
|
#define TXDBUF_V_TXDBUF 0
|
|
#define TXDBUF_S_TXDBUF 8
|
|
#define TXDBUF_M_TXDBUF (((1<<TXDBUF_S_TXDBUF)-1)<<TXDBUF_V_TXDBUF)
|
|
BIT(TSOM), /* Transmit Start of Message */
|
|
#define TXDBUF_V_TSOM 8
|
|
#define TXDBUF_M_TSOM (1<<TXDBUF_V_TSOM)
|
|
BIT(TEOM), /* End of Transmitted Message */
|
|
#define TXDBUF_V_TEOM 9
|
|
#define TXDBUF_M_TEOM (1<<TXDBUF_V_TEOM)
|
|
BIT(TABRT), /* Transmit Abort */
|
|
#define TXDBUF_V_TABRT 10
|
|
#define TXDBUF_M_TABRT (1<<TXDBUF_V_TABRT)
|
|
BIT(MAINTT), /* Maintenance Timer */
|
|
#define TXDBUF_V_MAINTT 11
|
|
#define TXDBUF_M_MAINTT (1<<TXDBUF_V_MAINTT)
|
|
BIT(TCRCTIN), /* Transmit CSR Input */
|
|
#define TXDBUF_V_TCRCTIN 12
|
|
#define TXDBUF_M_TCRCTIN (1<<TXDBUF_V_TCRCTIN)
|
|
BITNCF(1), /* reserved */
|
|
BIT(RCRCTIN), /* Receive CSR Input */
|
|
#define TXDBUF_V_RCRCTIN 14
|
|
#define TXDBUF_M_RCRCTIN (1<<TXDBUF_V_RCRCTIN)
|
|
BITNCF(1), /* reserved */
|
|
ENDBITS
|
|
};
|
|
#define TXDBUF_MBZ ((1<<15)|(1<<13))
|
|
#define TXDBUF_WRITEABLE (TXDBUF_M_TABRT|TXDBUF_M_TEOM|TXDBUF_M_TSOM|TXDBUF_M_TXDBUF)
|
|
|
|
|
|
|
|
/* DUP data structures
|
|
|
|
dup_dev DUP device descriptor
|
|
dup_unit DUP unit descriptor
|
|
dup_reg DUP register list
|
|
*/
|
|
|
|
#define IOLN_DUP 010
|
|
|
|
DIB dup_dib = { 0760300, 017, 0570, 5, 3, &dup_rd, &dup_wr, 0, 0, 0};
|
|
|
|
static UNIT dup_unit_template = {
|
|
UDATA (&dup_svc, UNIT_ATTABLE|UNIT_IDLE, 0),
|
|
};
|
|
|
|
static UNIT dup_poll_unit_template = {
|
|
UDATA (&dup_poll_svc, UNIT_DIS|UNIT_IDLE, 0),
|
|
};
|
|
|
|
static UNIT dup_units[NUM_DEVS_DUP+1]; /* One unit per line and a polling unit */
|
|
|
|
static REG dup_reg[] = {
|
|
{ BRDATADF (RXCSR, dup_rxcsr, 8, 16, NUM_DEVS_DUP, "receive control/status register", dup_rxcsr_bits) },
|
|
{ BRDATADF (RXDBUF, dup_rxdbuf, 8, 16, NUM_DEVS_DUP, "receive data buffer", dup_rxdbuf_bits) },
|
|
{ BRDATADF (PARCSR, dup_parcsr, 8, 16, NUM_DEVS_DUP, "receive control/status register", dup_parcsr_bits) },
|
|
{ BRDATADF (TXCSR, dup_txcsr, 8, 16, NUM_DEVS_DUP, "transmit control/status register", dup_txcsr_bits) },
|
|
{ BRDATADF (TXDBUF, dup_txdbuf, 8, 16, NUM_DEVS_DUP, "transmit data buffer", dup_txdbuf_bits) },
|
|
{ BRDATAD (W3, dup_W3, 8, 1, NUM_DEVS_DUP, "Clear Option Enable") },
|
|
{ BRDATAD (W5, dup_W5, 8, 1, NUM_DEVS_DUP, "A Dataset Control Enable") },
|
|
{ BRDATAD (W6, dup_W6, 8, 1, NUM_DEVS_DUP, "A and B Dataset Control Enable") },
|
|
{ GRDATAD (RXINT, dup_rxi, 8, NUM_DEVS_DUP, 0, "receive interrupts") },
|
|
{ GRDATAD (TXINT, dup_txi, 8, NUM_DEVS_DUP, 0, "transmit interrupts") },
|
|
{ BRDATAD (WAIT, dup_wait, 10, 32, NUM_DEVS_DUP, "delay time for transmit/receive bytes"), PV_RSPC },
|
|
{ BRDATAD (SPEED, dup_speed, 10, 32, NUM_DEVS_DUP, "line bit rate"), PV_RCOMMA },
|
|
{ BRDATAD (TPOFFSET, dup_xmtpkoffset, 8, 16, NUM_DEVS_DUP, "transmit assembly packet offset") },
|
|
{ BRDATAD (TPSIZE, dup_xmtpkbytes, 8, 16, NUM_DEVS_DUP, "transmit digest packet size") },
|
|
{ BRDATAD (TPDELAY,dup_xmtpkdelaying, 8, 16, NUM_DEVS_DUP, "transmit packet completion delay") },
|
|
{ BRDATAD (TPSTART, dup_xmtpkstart, 8, 32, NUM_DEVS_DUP, "transmit digest packet start time") },
|
|
{ BRDATAD (RPINOFF, dup_rcvpkinoff, 8, 16, NUM_DEVS_DUP, "receive digest packet offset") },
|
|
{ BRDATAD (CORRUPT, dup_corruption, 8, 32, NUM_DEVS_DUP, "data corruption factor (0.1%)") },
|
|
{ NULL }
|
|
};
|
|
|
|
static TMLN *dup_ldsc = NULL; /* line descriptors */
|
|
static TMXR dup_desc = { NUM_DEVS_DUP, 0, 0, NULL }; /* mux descriptor */
|
|
|
|
static MTAB dup_mod[] = {
|
|
{ MTAB_XTD|MTAB_VUN, 0, "SPEED", "SPEED=bits/sec (0=unrestricted)" ,
|
|
&dup_setspeed, &dup_showspeed, NULL, "Display rate limit" },
|
|
{ MTAB_XTD|MTAB_VUN, 0, "CORRUPTION", "CORRUPTION=factor (0=uncorrupted)" ,
|
|
&dup_setcorrupt, &dup_showcorrupt, NULL, "Display corruption factor (0.1% of packets)" },
|
|
{ MTAB_XTD|MTAB_VUN, 1, "W3", NULL ,
|
|
NULL, &dup_show_W3, NULL, "Display Reset Option" },
|
|
{ MTAB_XTD|MTAB_VUN, 1, NULL, "W3" ,
|
|
&dup_set_W3, NULL, NULL, "Enable Reset Option" },
|
|
{ MTAB_XTD|MTAB_VUN, 0, NULL, "NOW3" ,
|
|
&dup_set_W3, NULL, NULL, "Disable Reset Option" },
|
|
{ MTAB_XTD|MTAB_VUN, 1, "W5", NULL ,
|
|
NULL, &dup_show_W5, NULL, "Display A Dataset Control Option" },
|
|
{ MTAB_XTD|MTAB_VUN, 1, NULL, "W5" ,
|
|
&dup_set_W5, NULL, NULL, "Enable A Dataset Control Option" },
|
|
{ MTAB_XTD|MTAB_VUN, 0, NULL, "NOW5" ,
|
|
&dup_set_W5, NULL, NULL, "Disable A Dataset Control Option" },
|
|
{ MTAB_XTD|MTAB_VUN, 1, "W6", NULL ,
|
|
NULL, &dup_show_W6, NULL, "Display A & B Dataset Control Option" },
|
|
{ MTAB_XTD|MTAB_VUN, 1, NULL, "W6" ,
|
|
&dup_set_W6, NULL, NULL, "Enable A & B Dataset Control Option" },
|
|
{ MTAB_XTD|MTAB_VUN, 0, NULL, "NOW6" ,
|
|
&dup_set_W6, NULL, NULL, "Disable A & B Dataset Control Option" },
|
|
{MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "addr", "addr", &uba_set_addr, uba_show_addr,
|
|
NULL, "Sets address of DUP" },
|
|
{MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "vect", "vect", &uba_set_vect, uba_show_vect,
|
|
NULL, "Sets vect of DUP" },
|
|
{MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "br", "br", &uba_set_br, uba_show_br,
|
|
NULL, "Sets br of DUP" },
|
|
{MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "ctl", "ctl", &uba_set_ctl, uba_show_ctl,
|
|
NULL, "Sets uba of DUP" },
|
|
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 1, "CONNECTIONS", NULL,
|
|
NULL, &tmxr_show_cstat, (void *) &dup_desc, "Display current connections" },
|
|
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "LINES", "LINES=n",
|
|
&dup_setnl, &tmxr_show_lines, (void *) &dup_desc, "Display number of lines" },
|
|
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "SYNC", NULL,
|
|
NULL, &tmxr_show_sync, NULL, "Display attachable DDCMP synchronous links" },
|
|
{ 0 }
|
|
};
|
|
|
|
/* debugging bitmaps */
|
|
#define DBG_REG 0x0001 /* trace read/write registers */
|
|
#define DBG_INT 0x0002 /* display transfer requests */
|
|
#define DBG_PKT (TMXR_DBG_PXMT|TMXR_DBG_PRCV) /* display packets */
|
|
#define DBG_XMT TMXR_DBG_XMT /* display Transmitted Data */
|
|
#define DBG_RCV TMXR_DBG_RCV /* display Received Data */
|
|
#define DBG_MDM TMXR_DBG_MDM /* display Modem SignalTransitions */
|
|
#define DBG_CON TMXR_DBG_CON /* display connection activities */
|
|
#define DBG_TRC TMXR_DBG_TRC /* display trace routine calls */
|
|
#define DBG_ASY TMXR_DBG_ASY /* display Asynchronous Activities */
|
|
|
|
static DEBTAB dup_debug[] = {
|
|
{"REG", DBG_REG},
|
|
{"INT", DBG_INT},
|
|
{"PKT", DBG_PKT},
|
|
{"XMT", DBG_XMT},
|
|
{"RCV", DBG_RCV},
|
|
{"MDM", DBG_MDM},
|
|
{"CON", DBG_CON},
|
|
{"TRC", DBG_TRC},
|
|
{"ASY", DBG_ASY},
|
|
{0}
|
|
};
|
|
|
|
/*
|
|
We have two devices defined here (dup_dev and dpv_dev) which have the
|
|
same units. This would normally never be allowed since two devices can't
|
|
actually share units. This problem is avoided in this case since both
|
|
devices start out as disabled and the logic in dup_reset allows only
|
|
one of these devices to be enabled at a time. The DUP device is allowed
|
|
on Unibus systems and the DPV device Qbus systems.
|
|
This monkey business is necessary due to the fact that although both
|
|
the DUP and DPV devices have almost the same functionality and almost
|
|
the same register programming interface, they are different enough that
|
|
they fall in different priorities in the autoconfigure address and vector
|
|
rules.
|
|
This 'shared' unit model therefore means we can't call the
|
|
find_dev_from_unit api to uniquely determine the device structure.
|
|
We define the DUPDPTR macro to return the active device pointer when
|
|
necessary.
|
|
*/
|
|
DEVICE dup_dev = {
|
|
"DUP", dup_units, dup_reg, dup_mod,
|
|
2, 10, 31, 1, 8, 8,
|
|
NULL, NULL, &dup_reset,
|
|
NULL, &dup_attach, &dup_detach,
|
|
&dup_dib, DEV_DIS | DEV_DISABLE | DEV_DEBUG, 0,
|
|
dup_debug, NULL, NULL, &dup_help, dup_help_attach, &dup_desc,
|
|
&dup_description
|
|
};
|
|
|
|
#define DUPDPTR (&dup_dev)
|
|
|
|
/* Register names for Debug tracing */
|
|
static const char *dup_rd_regs[] =
|
|
{"RXCSR ", "RXDBUF", "TXCSR ", "TXDBUF" };
|
|
static const char *dup_wr_regs[] =
|
|
{"RXCSR ", "PARCSR", "TXCSR ", "TXDBUF"};
|
|
|
|
|
|
/* DUP11/DPV11 bit synchronous interface routines
|
|
|
|
dup_rd I/O page read
|
|
dup_wr I/O page write
|
|
dup_svc process event
|
|
dup_poll_svc process polling events
|
|
dup_reset process reset
|
|
dup_setnl set number of lines
|
|
dup_attach process attach
|
|
dup_detach process detach
|
|
*/
|
|
|
|
static int dup_rd (DEVICE *dptr, t_addr PA, uint16 *data, int32 access)
|
|
{
|
|
static BITFIELD* bitdefs[] = {dup_rxcsr_bits, dup_rxdbuf_bits, dup_txcsr_bits, dup_txdbuf_bits};
|
|
static uint16 *regs[] = {dup_rxcsr, dup_rxdbuf, dup_txcsr, dup_txdbuf};
|
|
struct pdp_dib *dibp = (DIB *)dptr->ctxt;
|
|
int32 dup = ((PA - dup_dib.uba_addr) >> 3); /* get line num */
|
|
int32 orig_val;
|
|
|
|
if ((dptr->units[0].flags & UNIT_DIS) != 0)
|
|
return 1;
|
|
|
|
if (dup >= dup_desc.lines) /* validate line number */
|
|
return 1;
|
|
|
|
orig_val = regs[(PA >> 1) & 03][dup];
|
|
switch ((PA >> 1) & 03) { /* case on PA<2:1> */
|
|
|
|
case 00: /* RXCSR */
|
|
dup_get_modem (dup);
|
|
*data = dup_rxcsr[dup];
|
|
dup_rxcsr[dup] &= ~(RXCSR_M_DSCHNG|RXCSR_M_BDATSET);
|
|
break;
|
|
|
|
case 01: /* RXDBUF */
|
|
*data = dup_rxdbuf[dup];
|
|
dup_rxcsr[dup] &= ~RXCSR_M_RXDONE;
|
|
if (dup_rxcsr[dup] & RXCSR_M_RXACT)
|
|
sim_activate (dup_units+dup, dup_wait[dup]);
|
|
break;
|
|
|
|
case 02: /* TXCSR */
|
|
*data = dup_txcsr[dup];
|
|
break;
|
|
|
|
case 03: /* TXDBUF */
|
|
*data = dup_txdbuf[dup];
|
|
break;
|
|
}
|
|
|
|
sim_debug(DEBUG_DETAIL, DUPDPTR, "dup_rd(PA=%010o [%s], data=0x%X) ", PA, dup_rd_regs[(PA >> 1) & 03], *data);
|
|
sim_debug_bits(DEBUG_DETAIL, DUPDPTR, bitdefs[(PA >> 1) & 03], (uint32)(orig_val), (uint32)(regs[(PA >> 1) & 03][dup]), TRUE);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int dup_wr (DEVICE *dptr, t_addr PA, uint16 data, int32 access)
|
|
{
|
|
static BITFIELD* bitdefs[] = {dup_rxcsr_bits, dup_parcsr_bits, dup_txcsr_bits, dup_txdbuf_bits};
|
|
static uint16 *regs[] = {dup_rxcsr, dup_parcsr, dup_txcsr, dup_txdbuf};
|
|
struct pdp_dib *dibp = (DIB *)dptr->ctxt;
|
|
int32 dup = ((PA - dup_dib.uba_addr) >> 3); /* get line num */
|
|
int32 orig_val;
|
|
|
|
sim_debug(DEBUG_DETAIL, DUPDPTR, "dup_wr(PA=%010o [%s], data=0x%X) ", PA, dup_wr_regs[(PA >> 1) & 03], data);
|
|
if ((dptr->units[0].flags & UNIT_DIS) != 0)
|
|
return 1;
|
|
|
|
if (dup >= dup_desc.lines) /* validate line number */
|
|
return 1;
|
|
|
|
orig_val = regs[(PA >> 1) & 03][dup];
|
|
if (access == BYTE) { /* byte access? */
|
|
if (PA & 1) /* unaligned byte access? */
|
|
data = (data | (orig_val & 0xFF)) & 0xFFFF; /* Merge with original word */
|
|
else
|
|
data = (orig_val & 0xFF00) | (data & 0xFF); /* Merge with original high word */
|
|
}
|
|
|
|
switch ((PA >> 1) & 03) { /* case on PA<2:1> */
|
|
|
|
case 00: /* RXCSR */
|
|
dup_set_modem (dup, data);
|
|
dup_rxcsr[dup] &= ~RXCSR_WRITEABLE;
|
|
dup_rxcsr[dup] |= (data & RXCSR_WRITEABLE);
|
|
if ((dup_rxcsr[dup] & RXCSR_M_DTR) && /* Upward transition of DTR */
|
|
(!(orig_val & RXCSR_M_DTR))) /* Enables Receive on the line */
|
|
dup_desc.ldsc[dup].rcve = TRUE;
|
|
if ((dup_rxcsr[dup] & RXCSR_M_RTS) && /* Upward transition of RTS */
|
|
(!(orig_val & RXCSR_M_RTS)) && /* while receiver is enabled and */
|
|
(dup_rxcsr[dup] & RXCSR_M_RCVEN) && /* not stripping sync characters */
|
|
(!(dup_rxcsr[dup] & RXCSR_M_STRSYN)) ) { /* Receive a SYNC character */
|
|
dup_rxcsr[dup] |= RXCSR_M_RXDONE;
|
|
dup_rxdbuf[dup] &= ~RXDBUF_M_RXDBUF;
|
|
dup_rxdbuf[dup] |= (dup_parcsr[dup] & PARCSR_M_ADSYNC);
|
|
if (dup_rxcsr[dup] & RXCSR_M_RXIE)
|
|
dup_set_rxint (dup);
|
|
}
|
|
if ((dup_rxcsr[dup] & RXCSR_M_RCVEN) &&
|
|
(!(orig_val & RXCSR_M_RCVEN))) { /* Upward transition of receiver enable */
|
|
dup_rcv_byte (dup); /* start any pending receive */
|
|
}
|
|
if ((!(dup_rxcsr[dup] & RXCSR_M_RCVEN)) &&
|
|
(orig_val & RXCSR_M_RCVEN)) { /* Downward transition of receiver enable */
|
|
dup_rxdbuf[dup] &= ~RXDBUF_M_RXDBUF;
|
|
dup_rxcsr[dup] &= ~RXCSR_M_RXACT;
|
|
if ((dup_rcvpkinoff[dup] != 0) ||
|
|
(dup_rcvpkbytes[dup] != 0))
|
|
dup_rcvpkinoff[dup] = dup_rcvpkbytes[dup] = 0;
|
|
}
|
|
if ((!(dup_rxcsr[dup] & RXCSR_M_RXIE)) &&
|
|
(orig_val & RXCSR_M_RXIE)) /* Downward transition of receiver interrupt enable */
|
|
dup_clr_rxint (dup);
|
|
if ((dup_rxcsr[dup] & RXCSR_M_RXIE) && (dup_rxcsr[dup] & RXCSR_M_RXDONE))
|
|
dup_set_rxint (dup);
|
|
break;
|
|
|
|
case 01: /* PARCSR */
|
|
dup_parcsr[dup] &= ~PARCSR_WRITEABLE;
|
|
dup_parcsr[dup] |= (data & PARCSR_WRITEABLE);
|
|
break;
|
|
|
|
case 02: /* TXCSR */
|
|
dup_txcsr[dup] &= ~TXCSR_WRITEABLE;
|
|
dup_txcsr[dup] |= (data & TXCSR_WRITEABLE);
|
|
if (dup_txcsr[dup] & TXCSR_M_DRESET) {
|
|
dup_clear(dup, dup_W3[dup]);
|
|
break;
|
|
}
|
|
if (TXCSR_GETMAISEL(dup_txcsr[dup]) != TXCSR_GETMAISEL(orig_val)) { /* Maint Select Changed */
|
|
switch (TXCSR_GETMAISEL(dup_txcsr[dup])) {
|
|
case 0: /* User/Normal Mode */
|
|
tmxr_set_line_loopback (&dup_desc.ldsc[dup], FALSE);
|
|
break;
|
|
case 1: /* External Loopback Mode */
|
|
case 2: /* Internal Loopback Mode */
|
|
tmxr_set_line_loopback (&dup_desc.ldsc[dup], TRUE);
|
|
break;
|
|
case 3: /* System Test Mode */
|
|
break;
|
|
}
|
|
}
|
|
if ((dup_txcsr[dup] & TXCSR_M_TXACT) &&
|
|
(!(orig_val & TXCSR_M_TXACT)) &&
|
|
(orig_val & TXCSR_M_TXDONE)) {
|
|
dup_txcsr[dup] &= ~TXCSR_M_TXDONE;
|
|
}
|
|
if ((!(dup_txcsr[dup] & TXCSR_M_SEND)) &&
|
|
(orig_val & TXCSR_M_SEND)) {
|
|
dup_txcsr[dup] &= ~TXCSR_M_TXACT;
|
|
dup_put_msg_bytes (dup, NULL, 0, FALSE, TRUE);
|
|
}
|
|
if ((dup_txcsr[dup] & TXCSR_M_HALFDUP) ^ (orig_val & TXCSR_M_HALFDUP))
|
|
tmxr_set_line_halfduplex (dup_desc.ldsc+dup, dup_txcsr[dup] & TXCSR_M_HALFDUP);
|
|
if ((dup_txcsr[dup] & TXCSR_M_TXIE) &&
|
|
(!(orig_val & TXCSR_M_TXIE)) &&
|
|
(dup_txcsr[dup] & TXCSR_M_TXDONE)) {
|
|
dup_set_txint (dup);
|
|
}
|
|
break;
|
|
|
|
case 03: /* TXDBUF */
|
|
dup_txdbuf[dup] &= ~TXDBUF_WRITEABLE;
|
|
dup_txdbuf[dup] |= (data & TXDBUF_WRITEABLE);
|
|
dup_txcsr[dup] &= ~TXCSR_M_TXDONE;
|
|
if (dup_txcsr[dup] & TXCSR_M_SEND) {
|
|
dup_txcsr[dup] |= TXCSR_M_TXACT;
|
|
sim_activate (dup_units+dup, dup_wait[dup]);
|
|
}
|
|
break;
|
|
}
|
|
|
|
dup_get_modem (dup);
|
|
return 0;
|
|
}
|
|
|
|
static t_stat dup_set_modem (int32 dup, int32 rxcsr_bits)
|
|
{
|
|
int32 bits_to_set, bits_to_clear;
|
|
|
|
if ((rxcsr_bits & (RXCSR_M_DTR | RXCSR_M_RTS)) == (dup_rxcsr[dup] & (RXCSR_M_DTR | RXCSR_M_RTS)))
|
|
return SCPE_OK;
|
|
bits_to_set = ((rxcsr_bits & RXCSR_M_DTR) ? TMXR_MDM_DTR : 0) | ((rxcsr_bits & RXCSR_M_RTS) ? TMXR_MDM_RTS : 0);
|
|
bits_to_clear = (~bits_to_set) & (TMXR_MDM_DTR | TMXR_MDM_RTS);
|
|
tmxr_set_get_modem_bits (dup_desc.ldsc+dup, bits_to_set, bits_to_clear, NULL);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat dup_get_modem (int32 dup)
|
|
{
|
|
int32 modem_bits;
|
|
uint16 old_rxcsr = dup_rxcsr[dup];
|
|
int32 old_rxcsr_a_modem_bits, new_rxcsr_a_modem_bits, old_rxcsr_b_modem_bits, new_rxcsr_b_modem_bits;
|
|
TMLN *lp = &dup_desc.ldsc[dup];
|
|
t_bool new_modem_change = FALSE;
|
|
|
|
if (dup_W5[dup])
|
|
old_rxcsr_a_modem_bits = dup_rxcsr[dup] & (RXCSR_M_RING | RXCSR_M_CTS | RXCSR_M_DSR | RXCSR_M_DCD);
|
|
else
|
|
old_rxcsr_a_modem_bits = dup_rxcsr[dup] & (RXCSR_M_RING | RXCSR_M_CTS);
|
|
if (dup_W6[dup])
|
|
old_rxcsr_b_modem_bits = dup_rxcsr[dup] & RXCSR_B_MODEM_BITS;
|
|
else
|
|
old_rxcsr_b_modem_bits = 0;
|
|
tmxr_set_get_modem_bits (lp, 0, 0, &modem_bits);
|
|
if (dup_W5[dup])
|
|
new_rxcsr_a_modem_bits = (((modem_bits & TMXR_MDM_RNG) ? RXCSR_M_RING : 0) |
|
|
((modem_bits & TMXR_MDM_CTS) ? RXCSR_M_CTS : 0) |
|
|
((modem_bits & TMXR_MDM_DSR) ? RXCSR_M_DSR : 0) |
|
|
((modem_bits & TMXR_MDM_DCD) ? RXCSR_M_DCD : 0));
|
|
else
|
|
new_rxcsr_a_modem_bits = (((modem_bits & TMXR_MDM_RNG) ? RXCSR_M_RING : 0) |
|
|
((modem_bits & TMXR_MDM_CTS) ? RXCSR_M_CTS : 0));
|
|
if (dup_W6[dup])
|
|
new_rxcsr_b_modem_bits = (((modem_bits & TMXR_MDM_DSR) ? RXCSR_M_DSR : 0) |
|
|
((modem_bits & TMXR_MDM_DCD) ? RXCSR_M_DCD : 0));
|
|
else
|
|
new_rxcsr_b_modem_bits = 0;
|
|
dup_rxcsr[dup] &= ~(RXCSR_A_MODEM_BITS | RXCSR_B_MODEM_BITS);
|
|
dup_rxcsr[dup] |= new_rxcsr_a_modem_bits | new_rxcsr_b_modem_bits;
|
|
if (old_rxcsr_a_modem_bits != new_rxcsr_a_modem_bits) {
|
|
dup_rxcsr[dup] |= RXCSR_M_DSCHNG;
|
|
new_modem_change = TRUE;
|
|
}
|
|
if (old_rxcsr_b_modem_bits != new_rxcsr_b_modem_bits) {
|
|
dup_rxcsr[dup] |= RXCSR_M_BDATSET;
|
|
new_modem_change = TRUE;
|
|
}
|
|
if (new_modem_change) {
|
|
sim_debug(DBG_MDM, DUPDPTR, "dup_get_modem() - Modem Signal Change ");
|
|
sim_debug_bits(DBG_MDM, DUPDPTR, dup_rxcsr_bits, (uint32)old_rxcsr, (uint32)dup_rxcsr[dup], TRUE);
|
|
}
|
|
if (dup_modem_change_callback[dup] && new_modem_change)
|
|
dup_modem_change_callback[dup](dup);
|
|
if ((dup_rxcsr[dup] & RXCSR_M_DSCHNG) &&
|
|
((dup_rxcsr[dup] & RXCSR_M_DSCHNG) != (old_rxcsr & RXCSR_M_DSCHNG)) &&
|
|
(dup_rxcsr[dup] & RXCSR_M_DSCIE))
|
|
dup_set_rxint (dup);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/*
|
|
* Public routines for use by other devices (i.e. KDP11)
|
|
*/
|
|
int32 dup_csr_to_linenum (int32 CSRPA)
|
|
{
|
|
DEVICE *dptr = DUPDPTR;
|
|
DIB *dib = (DIB *)dptr->ctxt;
|
|
|
|
if ((dib->uba_addr < (uint32)CSRPA) ||
|
|
((uint32)CSRPA > (dib->uba_addr + (IOLN_DUP * dup_desc.lines))) ||
|
|
(DUPDPTR->flags & DEV_DIS))
|
|
return -1;
|
|
|
|
return ((uint32)CSRPA - dib->uba_addr)/IOLN_DUP;
|
|
}
|
|
|
|
|
|
|
|
void dup_set_callback_mode (int32 dup, PACKET_DATA_AVAILABLE_CALLBACK receive, PACKET_TRANSMIT_COMPLETE_CALLBACK transmit, MODEM_CHANGE_CALLBACK modem)
|
|
{
|
|
if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS))
|
|
return;
|
|
dup_rcv_packet_data_callback[dup] = receive;
|
|
dup_xmt_complete_callback[dup] = transmit;
|
|
dup_modem_change_callback[dup] = modem;
|
|
}
|
|
|
|
int32 dup_get_DCD (int32 dup)
|
|
{
|
|
if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS))
|
|
return -1;
|
|
return (dup_rxcsr[dup] & RXCSR_M_DCD) ? 1 : 0;
|
|
}
|
|
|
|
int32 dup_get_DSR (int32 dup)
|
|
{
|
|
if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS))
|
|
return -1;
|
|
return (dup_rxcsr[dup] & RXCSR_M_DSR) ? 1 : 0;
|
|
}
|
|
|
|
int32 dup_get_CTS (int32 dup)
|
|
{
|
|
if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS))
|
|
return -1;
|
|
return (dup_rxcsr[dup] & RXCSR_M_CTS) ? 1 : 0;
|
|
}
|
|
|
|
int32 dup_get_RING (int32 dup)
|
|
{
|
|
if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS))
|
|
return -1;
|
|
return (dup_rxcsr[dup] & RXCSR_M_RING) ? 1 : 0;
|
|
}
|
|
|
|
int32 dup_get_RCVEN (int32 dup)
|
|
{
|
|
if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS))
|
|
return -1;
|
|
return (dup_rxcsr[dup] & RXCSR_M_RCVEN) ? 1 : 0;
|
|
}
|
|
|
|
t_stat dup_set_DTR (int32 dup, t_bool state)
|
|
{
|
|
if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS))
|
|
return SCPE_IERR;
|
|
dup_set_modem (dup, (state ? RXCSR_M_DTR : 0) | (dup_rxcsr[dup] & RXCSR_M_RTS));
|
|
if (state)
|
|
dup_rxcsr[dup] |= RXCSR_M_DTR;
|
|
else
|
|
dup_rxcsr[dup] &= ~RXCSR_M_DTR;
|
|
dup_ldsc[dup].rcve = state;
|
|
dup_get_modem (dup);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat dup_set_RTS (int32 dup, t_bool state)
|
|
{
|
|
if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS))
|
|
return SCPE_IERR;
|
|
dup_set_modem (dup, (state ? RXCSR_M_RTS : 0) | (dup_rxcsr[dup] & RXCSR_M_DTR));
|
|
if (state)
|
|
dup_rxcsr[dup] |= RXCSR_M_RTS;
|
|
else
|
|
dup_rxcsr[dup] &= ~RXCSR_M_RTS;
|
|
dup_get_modem (dup);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat dup_set_RCVEN (int32 dup, t_bool state)
|
|
{
|
|
uint16 orig_val;
|
|
|
|
if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS))
|
|
return SCPE_IERR;
|
|
orig_val = dup_rxcsr[dup];
|
|
dup_rxcsr[dup] &= ~RXCSR_M_RCVEN;
|
|
dup_rxcsr[dup] |= (state ? RXCSR_M_RCVEN : 0);
|
|
if ((dup_rxcsr[dup] & RXCSR_M_RCVEN) &&
|
|
(!(orig_val & RXCSR_M_RCVEN))) { /* Upward transition of receiver enable */
|
|
UNIT *uptr = dup_units + dup;
|
|
|
|
dup_poll_svc (uptr); /* start any pending receive */
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat dup_setup_dup (int32 dup, t_bool enable, t_bool protocol_DDCMP, t_bool crc_inhibit, t_bool halfduplex, uint8 station)
|
|
{
|
|
if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS))
|
|
return SCPE_IERR;
|
|
if (!enable) {
|
|
dup_clear(dup, TRUE);
|
|
return SCPE_OK;
|
|
}
|
|
if (!protocol_DDCMP) {
|
|
return SCPE_NOFNC; /* only DDCMP for now */
|
|
}
|
|
if (crc_inhibit) {
|
|
return SCPE_ARG; /* Must enable CRC for DDCMP */
|
|
}
|
|
/* These settings reflect how RSX operates a bare DUP when used for
|
|
DECnet communications */
|
|
dup_clear(dup, FALSE);
|
|
dup_rxcsr[dup] |= RXCSR_M_STRSYN | RXCSR_M_RCVEN;
|
|
dup_parcsr[dup] = PARCSR_M_DECMODE | (DDCMP_SYN << PARCSR_V_ADSYNC);
|
|
dup_txcsr[dup] &= TXCSR_M_HALFDUP;
|
|
dup_txcsr[dup] |= (halfduplex ? TXCSR_M_HALFDUP : 0);
|
|
tmxr_set_line_halfduplex (dup_desc.ldsc+dup, dup_txcsr[dup] & TXCSR_M_HALFDUP);
|
|
return dup_set_DTR (dup, TRUE);
|
|
}
|
|
|
|
t_stat dup_reset_dup (int32 dup)
|
|
{
|
|
if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS))
|
|
return SCPE_IERR;
|
|
dup_clear(dup, dup_W3[dup]);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat dup_set_W3_option (int32 dup, t_bool state)
|
|
{
|
|
if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS))
|
|
return SCPE_IERR;
|
|
dup_W3[dup] = state;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat dup_set_W5_option (int32 dup, t_bool state)
|
|
{
|
|
if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS))
|
|
return SCPE_IERR;
|
|
dup_W5[dup] = state;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat dup_set_W6_option (int32 dup, t_bool state)
|
|
{
|
|
if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS))
|
|
return SCPE_IERR;
|
|
dup_W6[dup] = state;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
|
|
t_bool dup_put_msg_bytes (int32 dup, uint8 *bytes, size_t len, t_bool start, t_bool end)
|
|
{
|
|
t_bool breturn = FALSE;
|
|
|
|
if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS))
|
|
return FALSE;
|
|
|
|
if (!tmxr_tpbusyln(&dup_ldsc[dup])) { /* Not Busy sending? */
|
|
if (start) {
|
|
dup_xmtpkoffset[dup] = 0;
|
|
dup_xmtpkdelaying[dup] = 0;
|
|
dup_xmtpkstart[dup] = sim_grtime();
|
|
}
|
|
if (dup_xmtpkoffset[dup] + 2 + len > dup_xmtpksize[dup]) {
|
|
dup_xmtpksize[dup] += 2 + (uint16)len;
|
|
dup_xmtpacket[dup] = (uint8 *)realloc (dup_xmtpacket[dup], dup_xmtpksize[dup]);
|
|
}
|
|
/* Strip sync bytes at the beginning of a message */
|
|
while (len > 0 && (dup_xmtpkoffset[dup] == 0) && (bytes[0] == DDCMP_SYN)) {
|
|
--len;
|
|
++bytes;
|
|
}
|
|
/* Insert remaining bytes into transmit buffer */
|
|
if (len) {
|
|
memcpy (&dup_xmtpacket[dup][dup_xmtpkoffset[dup]], bytes, len);
|
|
dup_xmtpkoffset[dup] += (uint16)len;
|
|
}
|
|
dup_txcsr[dup] |= TXCSR_M_TXDONE;
|
|
if (dup_txcsr[dup] & TXCSR_M_TXIE)
|
|
dup_set_txint (dup);
|
|
/* On End of Message, insert CRC and flag delivery start */
|
|
if (end) {
|
|
uint16 crc16 = ddcmp_crc16 (0, dup_xmtpacket[dup], dup_xmtpkoffset[dup]);
|
|
|
|
dup_xmtpacket[dup][dup_xmtpkoffset[dup]++] = crc16 & 0xFF;
|
|
dup_xmtpacket[dup][dup_xmtpkoffset[dup]++] = crc16 >> 8;
|
|
if ((dup_xmtpkoffset[dup] > 8) || (dup_xmtpacket[dup][0] == DDCMP_ENQ)) {
|
|
dup_xmtpkbytes[dup] = dup_xmtpkoffset[dup];
|
|
ddcmp_tmxr_put_packet_ln (&dup_ldsc[dup], dup_xmtpacket[dup], dup_xmtpkbytes[dup], dup_corruption[dup]);
|
|
}
|
|
}
|
|
breturn = TRUE;
|
|
}
|
|
sim_debug (DBG_TRC, DUPDPTR, "dup_put_msg_bytes(dup=%d, len=%d, start=%s, end=%s) %s\n",
|
|
dup, (int)len, start ? "TRUE" : "FALSE", end ? "TRUE" : "FALSE", breturn ? "Good" : "Busy");
|
|
if (breturn && (tmxr_tpbusyln (&dup_ldsc[dup]) || dup_xmtpkbytes[dup])) {
|
|
if (dup_xmt_complete_callback[dup])
|
|
dup_svc(dup_units+dup);
|
|
}
|
|
return breturn;
|
|
}
|
|
|
|
t_stat dup_get_packet (int32 dup, const uint8 **pbuf, uint16 *psize)
|
|
{
|
|
if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS))
|
|
return SCPE_IERR;
|
|
|
|
if (*pbuf == &dup_rcvpacket[dup][0]) {
|
|
*pbuf = NULL;
|
|
*psize = 0;
|
|
dup_rcvpkinoff[dup] = dup_rcvpkbytes[dup] = 0;
|
|
dup_rxcsr[dup] &= ~RXCSR_M_RXACT;
|
|
}
|
|
if ((dup_rcvpkinoff[dup] == 0) && (dup_rcvpkbytes[dup] != 0)) {
|
|
*pbuf = &dup_rcvpacket[dup][0];
|
|
*psize = dup_rcvpkbytes[dup];
|
|
}
|
|
sim_debug (DBG_TRC, DUPDPTR, "dup_get_packet(dup=%d, psize=%d)\n",
|
|
dup, (int)*psize);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat dup_rcv_byte (int32 dup)
|
|
{
|
|
sim_debug (DBG_TRC, DUPDPTR, "dup_rcv_byte(dup=%d) - %s, byte %d of %d\n", dup,
|
|
(dup_rxcsr[dup] & RXCSR_M_RCVEN) ? "enabled" : "disabled",
|
|
dup_rcvpkinoff[dup], dup_rcvpkbytes[dup]);
|
|
if (!(dup_rxcsr[dup] & RXCSR_M_RCVEN) || (dup_rcvpkbytes[dup] == 0) || (dup_rxcsr[dup] & RXCSR_M_RXDONE))
|
|
return SCPE_OK;
|
|
if (dup_rcv_packet_data_callback[dup]) {
|
|
sim_debug (DBG_TRC, DUPDPTR, "dup_rcv_byte(dup=%d, psize=%d) - Invoking Receive Data callback\n",
|
|
dup, (int)dup_rcvpkbytes[dup]);
|
|
dup_rcv_packet_data_callback[dup](dup, dup_rcvpkbytes[dup]);
|
|
return SCPE_OK;
|
|
}
|
|
dup_rxcsr[dup] |= RXCSR_M_RXACT;
|
|
dup_rxdbuf[dup] &= ~RXDBUF_M_RCRCER;
|
|
dup_rxdbuf[dup] &= ~RXDBUF_M_RXDBUF;
|
|
dup_rxdbuf[dup] |= dup_rcvpacket[dup][dup_rcvpkinoff[dup]++];
|
|
dup_rxcsr[dup] |= RXCSR_M_RXDONE;
|
|
if (((dup_rcvpkinoff[dup] == 8) ||
|
|
(dup_rcvpkinoff[dup] >= dup_rcvpkbytes[dup])) &&
|
|
(0 == ddcmp_crc16 (0, dup_rcvpacket[dup], dup_rcvpkinoff[dup])))
|
|
dup_rxdbuf[dup] |= RXDBUF_M_RCRCER;
|
|
else
|
|
dup_rxdbuf[dup] &= ~RXDBUF_M_RCRCER;
|
|
if (dup_rcvpkinoff[dup] >= dup_rcvpkbytes[dup]) {
|
|
dup_rcvpkinoff[dup] = dup_rcvpkbytes[dup] = 0;
|
|
dup_rxcsr[dup] &= ~RXCSR_M_RXACT;
|
|
}
|
|
if (dup_rxcsr[dup] & RXCSR_M_RXIE)
|
|
dup_set_rxint (dup);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* service routine to delay device activity */
|
|
|
|
static t_stat dup_svc (UNIT *uptr)
|
|
{
|
|
DEVICE *dptr = DUPDPTR;
|
|
int32 dup = (int32)(uptr-dptr->units);
|
|
TMLN *lp = &dup_desc.ldsc[dup];
|
|
|
|
sim_debug(DBG_TRC, DUPDPTR, "dup_svc(dup=%d)\n", dup);
|
|
if (!(dup_txcsr[dup] & TXCSR_M_TXDONE) && (!tmxr_tpbusyln (lp))) {
|
|
uint8 data[1]; /* Make coverity happy */
|
|
data[0] = dup_txdbuf[dup] & TXDBUF_M_TXDBUF;
|
|
|
|
dup_put_msg_bytes (dup, &data[0], 0, (dup_txdbuf[dup] & TXDBUF_M_TSOM),
|
|
(dup_txdbuf[dup] & TXDBUF_M_TEOM));
|
|
if (tmxr_tpbusyln (lp)) { /* Packet ready to send? */
|
|
sim_debug(DBG_TRC, DUPDPTR, "dup_svc(dup=%d) - Packet Done %d bytes\n", dup, dup_xmtpkoffset[dup]);
|
|
}
|
|
}
|
|
if ((tmxr_tpbusyln (lp) || dup_xmtpkbytes[dup]) && (lp->xmte || (!lp->conn))) {
|
|
int32 start = tmxr_tpbusyln (lp) ? tmxr_tpqln (lp) + tmxr_tqln (lp) : dup_xmtpkbytes[dup];
|
|
int32 remain = tmxr_send_buffered_data (lp);/* send any buffered data */
|
|
if (remain) {
|
|
sim_debug(DBG_PKT, DUPDPTR, "dup_svc(dup=%d) - Packet Transmission Stalled with %d bytes remaining\n", dup, remain);
|
|
}
|
|
else {
|
|
if (!lp->conn) {
|
|
if (dup_xmtpkoffset[dup]) {
|
|
sim_debug(DBG_PKT, DUPDPTR, "dup_svc(dup=%d) - %d byte packet transmission with link down (dropped)\n", dup, dup_xmtpkoffset[dup]);
|
|
}
|
|
dup_get_modem (dup);
|
|
}
|
|
else {
|
|
sim_debug(DBG_PKT, DUPDPTR, "dup_svc(dup=%d) - %d byte packet transmission complete\n", dup, dup_xmtpkbytes[dup]);
|
|
}
|
|
dup_xmtpkoffset[dup] = 0;
|
|
}
|
|
if (!tmxr_tpbusyln (lp)) { /* Done transmitting? */
|
|
if (((start - remain) > 0) && dup_speed[dup] && dup_xmt_complete_callback[dup] && !dup_xmtpkdelaying[dup]) { /* just done, and speed limited using packet interface? */
|
|
dup_xmtpkdelaying[dup] = 1;
|
|
sim_activate_notbefore (uptr, dup_xmtpkstart[dup] + (uint32)((tmxr_poll)*((double)dup_xmtpkbytes[dup]*8)/dup_speed[dup]));
|
|
}
|
|
else {
|
|
dup_txcsr[dup] &= ~TXCSR_M_TXACT; /* Set idle */
|
|
dup_xmtpkbytes[dup] = 0;
|
|
dup_xmtpkdelaying[dup] = 0;
|
|
if (dup_xmt_complete_callback[dup])
|
|
dup_xmt_complete_callback[dup](dup, (dup_rxcsr[dup] & RXCSR_M_DCD) ? 0 : 1);
|
|
}
|
|
}
|
|
}
|
|
if (dup_rxcsr[dup] & RXCSR_M_RXACT)
|
|
dup_rcv_byte (dup);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat dup_poll_svc (UNIT *uptr)
|
|
{
|
|
int32 dup, active, attached;
|
|
|
|
sim_debug(DBG_TRC, DUPDPTR, "dup_poll_svc()\n");
|
|
|
|
(void)tmxr_poll_conn(&dup_desc);
|
|
tmxr_poll_rx (&dup_desc);
|
|
tmxr_poll_tx (&dup_desc);
|
|
for (dup=active=attached=0; dup < dup_desc.lines; dup++) {
|
|
TMLN *lp = &dup_desc.ldsc[dup];
|
|
|
|
if (dup_units[dup].flags & UNIT_ATT)
|
|
++attached;
|
|
if (dup_ldsc[dup].conn)
|
|
++active;
|
|
dup_get_modem (dup);
|
|
if (lp->xmte && tmxr_tpbusyln(lp)) {
|
|
sim_debug(DBG_PKT, DUPDPTR, "dup_poll_svc(dup=%d) - Packet Transmission of remaining %d bytes restarting...\n", dup, tmxr_tpqln (lp));
|
|
dup_svc (&dup_units[dup]); /* Flush pending output */
|
|
}
|
|
if (!(dup_rxcsr[dup] & RXCSR_M_RXACT)) {
|
|
const uint8 *buf;
|
|
uint16 size;
|
|
t_stat r;
|
|
|
|
if (dup_parcsr[dup] & PARCSR_M_DECMODE)
|
|
r = ddcmp_tmxr_get_packet_ln (lp, &buf, &size, dup_corruption[dup]);
|
|
else {
|
|
size_t size_t_size;
|
|
|
|
r = tmxr_get_packet_ln (lp, &buf, &size_t_size);
|
|
size = (uint16)size_t_size;
|
|
}
|
|
if ((r == SCPE_OK) && (buf)) {
|
|
if (dup_rcvpksize[dup] < size) {
|
|
dup_rcvpksize[dup] = size;
|
|
dup_rcvpacket[dup] = (uint8 *)realloc (dup_rcvpacket[dup], dup_rcvpksize[dup]);
|
|
}
|
|
memcpy (dup_rcvpacket[dup], buf, size);
|
|
dup_rcvpkbytes[dup] = size;
|
|
dup_rcvpkinoff[dup] = 0;
|
|
dup_rxcsr[dup] |= RXCSR_M_RXACT;
|
|
dup_rcv_byte (dup);
|
|
}
|
|
}
|
|
}
|
|
if (active)
|
|
sim_clock_coschedule (uptr, tmxr_poll); /* reactivate */
|
|
else {
|
|
for (dup=0; dup < dup_desc.lines; dup++) {
|
|
if (dup_speed[dup]/8) {
|
|
dup_wait[dup] = (tmxr_poll*2)/(dup_speed[dup]/8);
|
|
if (dup_wait[dup] < DUP_WAIT)
|
|
dup_wait[dup] = DUP_WAIT;
|
|
}
|
|
else
|
|
dup_wait[dup] = DUP_WAIT; /* set minimum byte delay */
|
|
}
|
|
if (attached)
|
|
sim_activate_after (uptr, DUP_CONNECT_POLL*1000000);/* periodic check for connections */
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Interrupt routines */
|
|
|
|
static void dup_clr_rxint (int32 dup)
|
|
{
|
|
int vect;
|
|
vect = dup_dib.uba_vect + (dup * 010); /* return vector */
|
|
uba_clr_irq(&dup_dib, vect);
|
|
return;
|
|
}
|
|
|
|
static void dup_set_rxint (int32 dup)
|
|
{
|
|
int vect;
|
|
vect = dup_dib.uba_vect + (dup * 010); /* return vector */
|
|
uba_set_irq(&dup_dib, vect);
|
|
sim_debug(DEBUG_IRQ, DUPDPTR, "dup_set_rxint(dup=%d)\n", dup);
|
|
return;
|
|
}
|
|
|
|
static void dup_clr_txint (int32 dup)
|
|
{
|
|
int vect;
|
|
vect = dup_dib.uba_vect + 4 + (dup * 010); /* return vector */
|
|
uba_clr_irq(&dup_dib, vect);
|
|
return;
|
|
}
|
|
|
|
static void dup_set_txint (int32 dup)
|
|
{
|
|
int vect;
|
|
vect = dup_dib.uba_vect + 4 + (dup * 010); /* return vector */
|
|
uba_set_irq(&dup_dib, vect);
|
|
sim_debug(DEBUG_IRQ, DUPDPTR, "dup_set_txint(dup=%d)\n", dup);
|
|
return;
|
|
}
|
|
|
|
/* Device reset */
|
|
|
|
static t_stat dup_clear (int32 dup, t_bool flag)
|
|
{
|
|
sim_debug(DBG_TRC, DUPDPTR, "dup_clear(dup=%d,flag=%d)\n", dup, flag);
|
|
|
|
dup_rxdbuf[dup] = 0; /* silo empty */
|
|
dup_txdbuf[dup] = 0;
|
|
dup_parcsr[dup] = 0; /* no params */
|
|
dup_txcsr[dup] = TXCSR_M_TXDONE; /* clear CSR */
|
|
dup_wait[dup] = DUP_WAIT; /* initial/default byte delay */
|
|
if (flag) { /* INIT? clr all */
|
|
dup_rxcsr[dup] = 0;
|
|
dup_set_modem (dup, dup_rxcsr[dup]); /* push change out to line */
|
|
}
|
|
else
|
|
dup_rxcsr[dup] &= ~(RXCSR_M_DTR|RXCSR_M_RTS); /* else save dtr & rts */
|
|
dup_clr_rxint (dup); /* clear int */
|
|
dup_clr_txint (dup);
|
|
if (!dup_ldsc[dup].conn) /* set xmt enb */
|
|
dup_ldsc[dup].xmte = 1;
|
|
dup_ldsc[dup].rcve = 0; /* clr rcv enb */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat dup_reset (DEVICE *dptr)
|
|
{
|
|
int32 i, ndev, attached = 0;
|
|
|
|
sim_debug(DBG_TRC, dptr, "dup_reset()\n");
|
|
|
|
dup_desc.packet = TRUE;
|
|
dup_desc.buffered = 16384;
|
|
|
|
if (dup_ldsc == NULL) { /* First time startup */
|
|
dup_desc.ldsc = dup_ldsc = (TMLN *)calloc (dup_desc.lines, sizeof(*dup_ldsc));
|
|
for (i = 0; i < dup_desc.lines; i++) { /* init each line */
|
|
dup_units[i] = dup_unit_template;
|
|
if (dup_units[i].flags & UNIT_ATT)
|
|
++attached;
|
|
}
|
|
dup_units[dup_desc.lines] = dup_poll_unit_template;
|
|
/* Initialize to standard factory Option Jumper Settings */
|
|
for (i = 0; i < NUM_DEVS_DUP; i++) {
|
|
dup_W3[i] = TRUE;
|
|
dup_W5[i] = FALSE;
|
|
dup_W6[i] = TRUE;
|
|
}
|
|
}
|
|
for (i = 0; i < dup_desc.lines; i++) { /* init each line */
|
|
dup_clear (i, TRUE);
|
|
if (dup_units[i].flags & UNIT_ATT)
|
|
++attached;
|
|
}
|
|
for (i = 0; i < dup_desc.lines; i++) { /* Clear irq's */
|
|
int vect = dup_dib.uba_vect + (i * 010);
|
|
uba_clr_irq(&dup_dib, vect);
|
|
vect += 4;
|
|
uba_clr_irq(&dup_dib, vect);
|
|
}
|
|
tmxr_set_modem_control_passthru (&dup_desc); /* We always want Modem Control */
|
|
dup_desc.notelnet = TRUE; /* We always want raw tcp socket */
|
|
dup_desc.dptr = DUPDPTR; /* Connect appropriate device */
|
|
dup_desc.uptr = dup_units+dup_desc.lines; /* Identify polling unit */
|
|
sim_cancel (dup_units+dup_desc.lines); /* stop poll */
|
|
ndev = ((dptr->flags & DEV_DIS)? 0: dup_desc.lines );
|
|
if (attached)
|
|
sim_activate_after (dup_units+dup_desc.lines, DUP_CONNECT_POLL*1000000);/* start poll */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat dup_attach (UNIT *uptr, CONST char *cptr)
|
|
{
|
|
t_stat r;
|
|
DEVICE *dptr = DUPDPTR;
|
|
int32 dup = (int32)(uptr-dptr->units);
|
|
char attach_string[512];
|
|
|
|
if (!cptr || !*cptr)
|
|
return SCPE_ARG;
|
|
if (!(uptr->flags & UNIT_ATTABLE))
|
|
return SCPE_NOATT;
|
|
sprintf (attach_string, "Line=%d,%s", dup, cptr);
|
|
r = tmxr_open_master (&dup_desc, attach_string); /* open master socket */
|
|
free (uptr->filename);
|
|
uptr->filename = tmxr_line_attach_string(&dup_desc.ldsc[dup]);
|
|
if (r != SCPE_OK) /* error? */
|
|
return r;
|
|
uptr->flags |= UNIT_ATT;
|
|
sim_activate_after (dup_units+dup_desc.lines, DUP_CONNECT_POLL*1000000);/* start poll */
|
|
return r;
|
|
}
|
|
|
|
static t_stat dup_detach (UNIT *uptr)
|
|
{
|
|
DEVICE *dptr = DUPDPTR;
|
|
int32 dup = (int32)(uptr-dptr->units);
|
|
TMLN *lp = &dup_ldsc[dup];
|
|
int32 i, attached;
|
|
t_stat r;
|
|
|
|
if (!(uptr->flags & UNIT_ATT)) /* attached? */
|
|
return SCPE_OK;
|
|
sim_cancel (uptr);
|
|
uptr->flags &= ~UNIT_ATT;
|
|
for (i=attached=0; i<dup_desc.lines; i++)
|
|
if (dup_dev.units[i].flags & UNIT_ATT)
|
|
++attached;
|
|
if (!attached)
|
|
sim_cancel (dup_units+dup_desc.lines); /* stop poll on last detach */
|
|
r = tmxr_detach_ln (lp);
|
|
free (uptr->filename);
|
|
uptr->filename = NULL;
|
|
free (dup_rcvpacket[dup]);
|
|
dup_rcvpacket[dup] = NULL;
|
|
dup_rcvpksize[dup] = 0;
|
|
dup_rcvpkbytes[dup] = 0;
|
|
free (dup_xmtpacket[dup]);
|
|
dup_xmtpacket[dup] = NULL;
|
|
dup_xmtpksize[dup] = 0;
|
|
dup_xmtpkoffset[dup] = 0;
|
|
return r;
|
|
}
|
|
|
|
/* SET/SHOW SPEED processor */
|
|
|
|
static t_stat dup_showspeed (FILE* st, UNIT* uptr, int32 val, CONST void* desc)
|
|
{
|
|
DEVICE *dptr = DUPDPTR;
|
|
int32 dup = (int32)(uptr-dptr->units);
|
|
|
|
if (dup_speed[dup])
|
|
fprintf(st, "speed=%d bits/sec", dup_speed[dup]);
|
|
else
|
|
fprintf(st, "speed=0 (unrestricted)");
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat dup_setspeed (UNIT* uptr, int32 val, CONST char* cptr, void* desc)
|
|
{
|
|
DEVICE *dptr = DUPDPTR;
|
|
int32 dup = (int32)(uptr-dptr->units);
|
|
t_stat r;
|
|
int32 newspeed;
|
|
|
|
if (cptr == NULL)
|
|
return SCPE_ARG;
|
|
newspeed = (int32) get_uint (cptr, 10, 100000000, &r);
|
|
if (r != SCPE_OK)
|
|
return r;
|
|
dup_speed[dup] = newspeed;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* SET/SHOW CORRUPTION processor */
|
|
|
|
static t_stat dup_showcorrupt (FILE* st, UNIT* uptr, int32 val, CONST void* desc)
|
|
{
|
|
DEVICE *dptr = DUPDPTR;
|
|
int32 dup = (int32)(uptr-dptr->units);
|
|
|
|
if (dup_corruption[dup])
|
|
fprintf(st, "Corruption=%d milligulps (%.1f%% of messages processed)", dup_corruption[dup], ((double)dup_corruption[dup])/10.0);
|
|
else
|
|
fprintf(st, "No Corruption");
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat dup_setcorrupt (UNIT* uptr, int32 val, CONST char* cptr, void* desc)
|
|
{
|
|
DEVICE *dptr = DUPDPTR;
|
|
int32 dup = (int32)(uptr-dptr->units);
|
|
t_stat r;
|
|
int32 appetite;
|
|
|
|
if (cptr == NULL)
|
|
return SCPE_ARG;
|
|
appetite = (int32) get_uint (cptr, 10, 999, &r);
|
|
if (r != SCPE_OK)
|
|
return r;
|
|
dup_corruption[dup] = appetite;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* SET/SHOW W3 processor */
|
|
|
|
static t_stat dup_show_W3 (FILE* st, UNIT* uptr, int32 val, CONST void* desc)
|
|
{
|
|
DEVICE *dptr = DUPDPTR;
|
|
int32 dup = (int32)(uptr-dptr->units);
|
|
|
|
if (dup_W3[dup])
|
|
fprintf(st, "W3 Jumper Installed");
|
|
else
|
|
fprintf(st, "W3 Jumper Removed");
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat dup_set_W3 (UNIT* uptr, int32 val, CONST char* cptr, void* desc)
|
|
{
|
|
DEVICE *dptr = DUPDPTR;
|
|
int32 dup = (int32)(uptr-dptr->units);
|
|
|
|
dup_W3[dup] = val;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* SET/SHOW W5 processor */
|
|
|
|
static t_stat dup_show_W5 (FILE* st, UNIT* uptr, int32 val, CONST void* desc)
|
|
{
|
|
DEVICE *dptr = DUPDPTR;
|
|
int32 dup = (int32)(uptr-dptr->units);
|
|
|
|
if (dup_W5[dup])
|
|
fprintf(st, "W5 Jumper Installed");
|
|
else
|
|
fprintf(st, "W5 Jumper Removed");
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat dup_set_W5 (UNIT* uptr, int32 val, CONST char* cptr, void* desc)
|
|
{
|
|
DEVICE *dptr = DUPDPTR;
|
|
int32 dup = (int32)(uptr-dptr->units);
|
|
|
|
dup_W5[dup] = val;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* SET/SHOW W6 processor */
|
|
|
|
static t_stat dup_show_W6 (FILE* st, UNIT* uptr, int32 val, CONST void* desc)
|
|
{
|
|
DEVICE *dptr = DUPDPTR;
|
|
int32 dup = (int32)(uptr-dptr->units);
|
|
|
|
if (dup_W6[dup])
|
|
fprintf(st, "W6 Jumper Installed");
|
|
else
|
|
fprintf(st, "W6 Jumper Removed");
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat dup_set_W6 (UNIT* uptr, int32 val, CONST char* cptr, void* desc)
|
|
{
|
|
DEVICE *dptr = DUPDPTR;
|
|
int32 dup = (int32)(uptr-dptr->units);
|
|
|
|
dup_W6[dup] = val;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* SET LINES processor */
|
|
|
|
static t_stat dup_setnl (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
|
|
{
|
|
int32 newln, l;
|
|
uint32 i;
|
|
t_stat r;
|
|
DEVICE *dptr = DUPDPTR;
|
|
|
|
for (i=0; i<dptr->numunits; i++)
|
|
if (dptr->units[i].flags&UNIT_ATT)
|
|
return SCPE_ALATT;
|
|
if (cptr == NULL)
|
|
return SCPE_ARG;
|
|
newln = (int32) get_uint (cptr, 10, NUM_DEVS_DUP, &r);
|
|
if ((r != SCPE_OK) || (newln == dup_desc.lines))
|
|
return r;
|
|
if (newln == 0)
|
|
return SCPE_ARG;
|
|
sim_cancel (dup_units + dup_desc.lines);
|
|
dup_desc.ldsc = dup_ldsc = (TMLN *)realloc(dup_ldsc, newln*sizeof(*dup_ldsc));
|
|
for (l=dup_desc.lines; l < newln; l++) {
|
|
memset (&dup_ldsc[l], 0, sizeof(*dup_ldsc));
|
|
dup_units[l] = dup_unit_template;
|
|
}
|
|
dup_units[newln] = dup_poll_unit_template;
|
|
dup_desc.lines = newln;
|
|
dup_desc.uptr = dptr->units + newln; /* Identify polling unit */
|
|
dptr->numunits = newln + 1;
|
|
return dup_reset (dptr); /* setup lines and auto config */
|
|
}
|
|
|
|
static t_stat dup_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
|
|
{
|
|
const char helpString[] =
|
|
/* The '*'s in the next line represent the standard text width of a help line */
|
|
/****************************************************************************/
|
|
" The %D11 is a single-line, program controlled, double buffered\n"
|
|
" communications device designed to interface the %1s system to a\n"
|
|
" serial synchronous line. The original hardware is capable of handling\n"
|
|
" a wide variety of protocols, including byte oriented protocols, such\n"
|
|
" as DDCMP and BISYNC and bit-oriented protocols such as SDLC, HDLC\n"
|
|
" and ADCCP. The emulated device currently only supports connections\n"
|
|
" using the DDCMP protocol.\n\n"
|
|
" The %D11 is ideally suited for interfacing the %1s system\n"
|
|
" to medium-speed synchronous lines for remote batch, remote data\n"
|
|
" collection, remote concentration and network applications. Multiple\n"
|
|
" %D11's on a %1s allow its use in applications requiring several\n"
|
|
" synchronous lines.\n\n"
|
|
" The %D11 is capable of transmitting data at the maximum speed of\n"
|
|
" 9600 baud. The emulated device can move data at significantly faster\n"
|
|
" data rates. The maximum emulated rate is dependent on the host CPU's\n"
|
|
" available cycles.\n"
|
|
"1 Hardware Description\n"
|
|
" The %1s %D11 consists of a microprocessor module and a synchronous line\n"
|
|
" unit module.\n"
|
|
"2 $Registers\n"
|
|
"\n"
|
|
" These registers contain the emulated state of the device. These values\n"
|
|
" don't necessarily relate to any detail of the original device being\n"
|
|
" emulated but are merely internal details of the emulation.\n"
|
|
"1 Configuration\n"
|
|
" A %D device is configured with various simh SET and ATTACH commands\n"
|
|
"2 $Set commands\n"
|
|
"3 Lines\n"
|
|
" A maximum of %2s %D11 devices can be emulated concurrently in the %S\n"
|
|
" simulator. The number of simulated %D devices or lines can be\n"
|
|
" specified with command:\n"
|
|
"\n"
|
|
"+sim> SET %D LINES=n\n"
|
|
"3 Peer\n"
|
|
" To set the host and port to which data is to be transmitted use the\n"
|
|
" following command:\n"
|
|
"\n"
|
|
"+sim> SET %U PEER=host:port\n"
|
|
"3 Connectpoll\n"
|
|
" The minimum interval between attempts to connect to the other side is set\n"
|
|
" using the following command:\n"
|
|
"\n"
|
|
"+sim> SET %U CONNECTPOLL=n\n"
|
|
"\n"
|
|
" Where n is the number of seconds. The default is %3s seconds.\n"
|
|
"3 Speed\n"
|
|
" If you want to experience the actual data rates of the physical hardware\n"
|
|
" you can set the bit rate of the simulated line can be set using the\n"
|
|
" following command:\n"
|
|
"\n"
|
|
"+sim> SET %U SPEED=n\n"
|
|
"\n"
|
|
" Where n is the number of data bits per second that the simulated line\n"
|
|
" runs at. In practice this is implemented as a delay while transmitting\n"
|
|
" bytes to the socket. Use a value of zero to run at full speed with no\n"
|
|
" artificial throttling.\n"
|
|
"3 Corruption\n"
|
|
" Corruption Troll - the DDCMP emulation includes the ability to enable a\n"
|
|
" process that will intentionally drop or corrupt some messages. This\n"
|
|
" emulates the less-than-perfect communications lines encountered in the\n"
|
|
" real world, and enables network monitoring software to see non-zero error\n"
|
|
" counters.\n"
|
|
"\n"
|
|
" The troll selects messages with a probablility selected by the SET %U\n"
|
|
" CORRUPT command. The units are 0.1%%; that is, a value of 1 means that\n"
|
|
" every message has a 1/1000 chance of being selected to be corrupted\n"
|
|
" or discarded.\n"
|
|
/****************************************************************************/
|
|
#define DUP_HLP_ATTACH "Configuration Attach"
|
|
"2 Attach\n"
|
|
" The communication line performs input and output through a TCP session\n"
|
|
" (or UDP session) connected to a user-specified port. The ATTACH command\n"
|
|
" specifies the port to be used as well as the peer address:\n"
|
|
"\n"
|
|
"+sim> ATTACH %U {interface:}port{,UDP},Connect=peerhost:port\n"
|
|
"\n"
|
|
" where port is a decimal number between 1 and 65535 that is not being\n"
|
|
" used for other TCP/IP activities.\n"
|
|
"\n"
|
|
" Specifying symmetric attach configuration (with both a listen port and\n"
|
|
" a peer address) will cause the side receiving an incoming\n"
|
|
" connection to validate that the connection actually comes from the\n"
|
|
" connecction destination system.\n"
|
|
" A symmetric attach configuration is required when using UDP packet\n"
|
|
" transport.\n"
|
|
"\n"
|
|
" The default connection uses TCP transport between the local system and\n"
|
|
" the peer. Alternatively, UDP can be used by specifying UDP on the\n"
|
|
" ATTACH command.\n"
|
|
"\n"
|
|
" Communication may alternately use the DDCMP synchronous framer device.\n"
|
|
" The DDCMP synchronous device is a USB device that can send and\n"
|
|
" receive DDCMP frames over either RS-232 or coax synchronous lines.\n"
|
|
" Refer to https://github.com/pkoning2/ddcmp for documentation.\n"
|
|
"\n"
|
|
"+sim> ATTACH %U SYNC=ifname:mode:speed\n"
|
|
"\n"
|
|
" Communicate via the synchronous DDCMP framer interface \"ifname\", \n"
|
|
" and framer mode \"mode\" -- one of INTEGRAL, RS232_DTE, or\n"
|
|
" RS232_DCE. The \"speed\" argument is the bit rate for the line.\n"
|
|
" You can use \"SHOW SYNC\" to see the list of synchronous DDCMP devices.\n"
|
|
"2 Examples\n"
|
|
" To configure two simulators to talk to each other use the following\n"
|
|
" example:\n"
|
|
" \n"
|
|
" Machine 1\n"
|
|
"+sim> SET %D ENABLE\n"
|
|
"+sim> ATTACH %U 1111,connect=LOCALHOST:2222\n"
|
|
" \n"
|
|
" Machine 2\n"
|
|
"+sim> SET %D ENABLE\n"
|
|
"+sim> ATTACH %U 2222,connect=LOCALHOST:1111\n"
|
|
"\n"
|
|
" To communicate with an \"integral modem\" DMC or similar, at 56 kbps:\n"
|
|
"+sim> ATTACH %U SYNC=sync0:INTEGRAL:56000\n"
|
|
"1 Monitoring\n"
|
|
" The %D device and %U line configuration and state can be displayed with\n"
|
|
" one of the available show commands.\n"
|
|
"2 $Show commands\n"
|
|
"1 Diagnostics\n"
|
|
" Corruption Troll - the DDCMP emulation includes a process that will\n"
|
|
" intentionally drop or corrupt some messages. This emulates the\n"
|
|
" less-than-perfect communications lines encountered in the real world,\n"
|
|
" and enables network monitoring software to see non-zero error counters.\n"
|
|
"\n"
|
|
" The troll selects messages with a probablility selected by the SET %U\n"
|
|
" CORRUPT command. The units are 0.1%%; that is, a value of 1 means that\n"
|
|
" every message has a 1/1000 chance of being selected to be corrupted\n"
|
|
" or discarded.\n"
|
|
"1 Restrictions\n"
|
|
" Real hardware synchronous connections could operate in Multi-Point mode.\n"
|
|
" Multi-Point mode was a way of sharing a single wire with multiple\n"
|
|
" destination systems or devices. Multi-Point mode is not currently\n"
|
|
" emulated by this or other simulated synchronous devices.\n"
|
|
"\n"
|
|
"1 Implementation\n"
|
|
" A real %D11 transports host generated protocol implemented data via a\n"
|
|
" synchronous connection, the emulated device makes a TCP (or UDP)\n"
|
|
" connection to another emulated device which either speaks DDCMP over the\n"
|
|
" TCP connection directly, or interfaces to a simulated computer where the\n"
|
|
" operating system speaks the DDCMP protocol on the wire.\n"
|
|
"\n"
|
|
" The %D11 can be used for point-to-point DDCMP connections carrying\n"
|
|
" DECnet and other types of networking, e.g. from ULTRIX or DSM.\n"
|
|
"1 Debugging\n"
|
|
" The simulator has a number of debug options, these are:\n"
|
|
"\n"
|
|
"++REG Shows whenever a CSR is programatically read or written\n"
|
|
"++++and the current value.\n"
|
|
"++INT Shows Interrupt activity.\n"
|
|
"++PKT Shows Packet activity.\n"
|
|
"++XMT Shows Transmitted data.\n"
|
|
"++RCV Shows Received data.\n"
|
|
"++MDM Shows Modem Signal Transitions.\n"
|
|
"++CON Shows connection activities.\n"
|
|
"++TRC Shows routine call traces.\n"
|
|
"++ASY Shows Asynchronous activities.\n"
|
|
"\n"
|
|
" To get a full trace use\n"
|
|
"\n"
|
|
"+sim> SET %D DEBUG\n"
|
|
"\n"
|
|
" However it is recommended to use the following when sending traces:\n"
|
|
"\n"
|
|
"+sim> SET %D DEBUG=REG;PKT;XMT;RCV;CON\n"
|
|
"\n"
|
|
"1 Related Devices\n"
|
|
" The %D11 can facilitate communication with other simh simulators which\n"
|
|
" have emulated synchronous network devices available. These include\n"
|
|
" the following:\n"
|
|
"\n"
|
|
"++DUP11* KS10 PDP10 simulators\n"
|
|
"\n"
|
|
"++* Indicates systems which have OS provided DDCMP implementations.\n"
|
|
;
|
|
char devcount[16];
|
|
char connectpoll[16];
|
|
|
|
sprintf (devcount, "%d", NUM_DEVS_DUP);
|
|
sprintf (connectpoll, "%d", DUP_CONNECT_POLL);
|
|
|
|
return scp_help (st, dptr, uptr, flag, helpString, cptr, "Unibus", devcount, connectpoll);
|
|
}
|
|
|
|
static t_stat dup_help_attach (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
|
|
{
|
|
return dup_help (st, dptr, uptr, flag, DUP_HLP_ATTACH);
|
|
}
|
|
|
|
static const char *dup_description (DEVICE *dptr)
|
|
{
|
|
return "DUP11 bit synchronous interface" ;
|
|
}
|
|
#endif
|