/* 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<> TXCSR_V_MAISEL) BIT(MAISSCLK), /* Maintenance Single Step Clock */ #define TXCSR_V_MAISSCLK 13 #define TXCSR_M_MAISSCLK (1<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; ifilename); 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; inumunits; 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