| /* pdp11_dup.c: PDP-11 DUP11/DPV11 bit synchronous interface | |
| Copyright (c) 2013, Mark Pizzolato | |
| 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 | |
| */ | |
| #if defined (VM_PDP10) /* PDP10 version */ | |
| #include "pdp10_defs.h" | |
| #elif defined (VM_VAX) /* VAX version */ | |
| #include "vax_defs.h" | |
| #else /* PDP-11 version */ | |
| #include "pdp11_defs.h" | |
| #endif | |
| #include "sim_tmxr.h" | |
| #include "pdp11_ddcmp.h" | |
| #include "pdp11_dup.h" | |
| #if !defined(DUP_LINES) | |
| #define DUP_LINES 8 | |
| #endif | |
| #define INITIAL_DUP_LINES 1 | |
| #define DUP_WAIT 50 /* Minimum character time */ | |
| #define DUP_CONNECT_POLL 2 /* Seconds */ | |
| extern int32 IREQ (HLVL); | |
| extern int32 tmxr_poll; /* calibrated delay */ | |
| extern int32 clk_tps; /* clock ticks per second */ | |
| extern int32 tmr_poll; /* instructions per tick */ | |
| uint16 dup_rxcsr[DUP_LINES]; | |
| uint16 dup_rxdbuf[DUP_LINES]; | |
| uint16 dup_parcsr[DUP_LINES]; | |
| uint16 dup_txcsr[DUP_LINES]; | |
| uint16 dup_txdbuf[DUP_LINES]; | |
| uint32 dup_rxi = 0; /* rcv interrupts */ | |
| uint32 dup_txi = 0; /* xmt interrupts */ | |
| uint32 dup_wait[DUP_LINES]; /* rcv/xmt byte delay */ | |
| uint32 dup_speed[DUP_LINES]; /* line speed (bits/sec) */ | |
| uint8 *dup_rcvpacket[DUP_LINES]; /* rcv buffer */ | |
| uint16 dup_rcvpksize[DUP_LINES]; /* rcv buffer size */ | |
| uint16 dup_rcvpkoffset[DUP_LINES]; /* rcv buffer offset */ | |
| uint16 dup_rcvpkinoff[DUP_LINES]; /* rcv packet in offset */ | |
| uint8 *dup_xmtpacket[DUP_LINES]; /* xmt buffer */ | |
| uint16 dup_xmtpksize[DUP_LINES]; /* xmt buffer size */ | |
| uint16 dup_xmtpkoffset[DUP_LINES]; /* xmt buffer offset */ | |
| uint16 dup_xmtpkoutoff[DUP_LINES]; /* xmt packet out offset */ | |
| t_bool dup_xmtpkrdy[DUP_LINES]; /* xmt packet ready */ | |
| PACKET_RECEIVE_CALLBACK dup_rcv_packet_callback[DUP_LINES]; | |
| PACKET_TRANSMIT_COMPLETE_CALLBACK dup_xmt_complete_callback[DUP_LINES]; | |
| t_stat dup_rd (int32 *data, int32 PA, int32 access); | |
| t_stat dup_wr (int32 data, int32 PA, int32 access); | |
| t_stat dup_set_modem (int32 dup, int32 rxcsr_bits); | |
| t_stat dup_get_modem (int32 dup); | |
| t_stat dup_svc (UNIT *uptr); | |
| t_stat dup_poll_svc (UNIT *uptr); | |
| t_stat dup_rcv_byte (int32 dup); | |
| t_stat dup_reset (DEVICE *dptr); | |
| t_stat dup_attach (UNIT *uptr, char *ptr); | |
| t_stat dup_detach (UNIT *uptr); | |
| t_stat dup_clear (int32 dup, t_bool flag); | |
| int32 dup_rxinta (void); | |
| int32 dup_txinta (void); | |
| void dup_update_rcvi (void); | |
| void dup_update_xmti (void); | |
| void dup_clr_rxint (int32 dup); | |
| void dup_set_rxint (int32 dup); | |
| void dup_clr_txint (int32 dup); | |
| void dup_set_txint (int32 dup); | |
| t_stat dup_setnl (UNIT *uptr, int32 val, char *cptr, void *desc); | |
| t_stat dup_setspeed (UNIT* uptr, int32 val, char* cptr, void* desc); | |
| t_stat dup_showspeed (FILE* st, UNIT* uptr, int32 val, void* desc); | |
| t_stat dup_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); | |
| t_stat dup_help_attach (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); | |
| char *dup_description (DEVICE *dptr); | |
| /* RXCSR - 16XXX0 - receiver control/status register */ | |
| 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 | RXCSR_M_DCD) | |
| #define RXCSR_B_MODEM_BITS (RXCSR_M_DSR) | |
| #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 */ | |
| 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 */ | |
| 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 */ | |
| 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 */ | |
| 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 = { | |
| IOBA_AUTO, | |
| IOLN_DUP * INITIAL_DUP_LINES, | |
| &dup_rd, /* read */ | |
| &dup_wr, /* write */ | |
| 2, /* # of vectors */ | |
| IVCL (DUPRX), | |
| VEC_AUTO, | |
| { &dup_rxinta, &dup_txinta }/* int. ack. routines */ | |
| }; | |
| UNIT dup_unit_template = { | |
| UDATA (&dup_svc, UNIT_ATTABLE, 0), | |
| }; | |
| UNIT dup_poll_unit_template = { | |
| UDATA (&dup_poll_svc, UNIT_DIS, 0), | |
| }; | |
| UNIT dup_units[DUP_LINES+1]; /* One unit per line and a polling unit */ | |
| REG dup_reg[] = { | |
| { BRDATADF (RXCSR, dup_rxcsr, DEV_RDX, 16, DUP_LINES, "receive control/status register", dup_rxcsr_bits) }, | |
| { BRDATADF (RXDBUF, dup_rxdbuf, DEV_RDX, 16, DUP_LINES, "receive data buffer", dup_rxdbuf_bits) }, | |
| { BRDATADF (PARCSR, dup_parcsr, DEV_RDX, 16, DUP_LINES, "receive control/status register", dup_parcsr_bits) }, | |
| { BRDATADF (TXCSR, dup_txcsr, DEV_RDX, 16, DUP_LINES, "transmit control/status register", dup_txcsr_bits) }, | |
| { BRDATADF (TXDBUF, dup_txdbuf, DEV_RDX, 16, DUP_LINES, "transmit data buffer", dup_txdbuf_bits) }, | |
| { GRDATAD (RXINT, dup_rxi, DEV_RDX, DUP_LINES, 0, "receive interrupts") }, | |
| { GRDATAD (TXINT, dup_txi, DEV_RDX, DUP_LINES, 0, "transmit interrupts") }, | |
| { BRDATAD (WAIT, dup_wait, 10, 32, DUP_LINES, "delay time for transmit/receive bytes"), PV_RSPC }, | |
| { BRDATAD (SPEED, dup_speed, 10, 32, DUP_LINES, "line bit rate"), PV_RCOMMA }, | |
| { BRDATAD (RPOFFSET, dup_rcvpkoffset, DEV_RDX, 16, DUP_LINES, "receive assembly packet offset") }, | |
| { BRDATAD (TPOFFSET, dup_xmtpkoffset, DEV_RDX, 16, DUP_LINES, "transmit assembly packet offset") }, | |
| { BRDATAD (RPINOFF, dup_rcvpkinoff, DEV_RDX, 16, DUP_LINES, "receive digest packet offset") }, | |
| { BRDATAD (TPOUTOFF, dup_xmtpkoutoff, DEV_RDX, 16, DUP_LINES, "transmit digest packet offset") }, | |
| { BRDATAD (TPREADY, dup_xmtpkrdy, DEV_RDX, 16, DUP_LINES, "transmit packet ready") }, | |
| { NULL } | |
| }; | |
| TMLN *dup_ldsc = NULL; /* line descriptors */ | |
| TMXR dup_desc = { INITIAL_DUP_LINES, 0, 0, NULL }; /* mux descriptor */ | |
| 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_VDV|MTAB_VALR, 020, "ADDRESS", "ADDRESS", | |
| &set_addr, &show_addr, NULL, "Bus address" }, | |
| { MTAB_XTD|MTAB_VDV|MTAB_VALR, 1, "VECTOR", "VECTOR", | |
| &set_vec, &show_vec_mux, (void *) &dup_desc, "Interrupt vector" }, | |
| #if !defined (VM_PDP10) | |
| { MTAB_XTD|MTAB_VDV, 0, NULL, "AUTOCONFIGURE", | |
| &set_addr_flt, NULL, NULL, "Enable autoconfiguration of address & vector" }, | |
| #endif | |
| { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "LINES", "LINES=n", | |
| &dup_setnl, &tmxr_show_lines, (void *) &dup_desc, "Display number of lines" }, | |
| { 0 } | |
| }; | |
| /* debugging bitmaps */ | |
| #define DBG_REG 0x0001 /* trace read/write registers */ | |
| #define DBG_INT 0x0002 /* display transfer requests */ | |
| #define DBG_PKT 0x0004 /* 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 Signals */ | |
| #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 */ | |
| 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, DEV_RDX, 8, | |
| NULL, NULL, &dup_reset, | |
| NULL, &dup_attach, &dup_detach, | |
| &dup_dib, DEV_DIS | DEV_DISABLE | DEV_UBUS | DEV_DEBUG, 0, | |
| dup_debug, NULL, NULL, &dup_help, dup_help_attach, &dup_desc, | |
| &dup_description | |
| }; | |
| DEVICE dpv_dev = { | |
| "DPV", dup_units, dup_reg, dup_mod, | |
| 2, 10, 31, 1, DEV_RDX, 8, | |
| NULL, NULL, &dup_reset, | |
| NULL, &dup_attach, &dup_detach, | |
| &dup_dib, DEV_DIS | DEV_DISABLE | DEV_QBUS | DEV_DEBUG, 0, | |
| dup_debug, NULL, NULL, &dup_help, dup_help_attach, &dup_desc, | |
| &dup_description | |
| }; | |
| #define DUPDPTR ((UNIBUS) ? &dup_dev : &dpv_dev) | |
| /* Register names for Debug tracing */ | |
| static char *dup_rd_regs[] = | |
| {"RXCSR ", "RXDBUF", "TXCSR ", "TXDBUF" }; | |
| static 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 | |
| */ | |
| t_stat dup_rd (int32 *data, int32 PA, 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}; | |
| int32 dup = ((PA - dup_dib.ba) >> 3); /* get line num */ | |
| TMLN *lp = &dup_desc.ldsc[dup]; | |
| int32 orig_val; | |
| if (dup >= dup_desc.lines) /* validate line number */ | |
| return SCPE_IERR; | |
| 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]; | |
| 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(DBG_REG, DUPDPTR, "dup_rd(PA=0x%08X [%s], data=0x%X) ", PA, dup_rd_regs[(PA >> 1) & 03], *data); | |
| sim_debug_bits(DBG_REG, DUPDPTR, bitdefs[(PA >> 1) & 03], (uint32)(orig_val), (uint32)(regs[(PA >> 1) & 03][dup]), TRUE); | |
| return SCPE_OK; | |
| } | |
| t_stat dup_wr (int32 data, int32 PA, 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}; | |
| int32 dup = ((PA - dup_dib.ba) >> 3); /* get line num */ | |
| int32 orig_val; | |
| if (dup >= dup_desc.lines) /* validate line number */ | |
| return SCPE_IERR; | |
| orig_val = regs[(PA >> 1) & 03][dup]; | |
| if (PA & 1) /* unaligned byte access? */ | |
| data = ((data << 8) & (orig_val & 0xFF)) & 0xFFFF; /* Merge with original word */ | |
| else | |
| if (access == WRITEB) /* byte access? */ | |
| 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_RTS) && /* Upward transition of RTS */ | |
| (!(orig_val & RXCSR_M_RTS))) /* 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_rxcsr[dup] &= ~(RXCSR_M_RXDONE|RXCSR_M_RXACT); | |
| if ((dup_rcvpkinoff[dup] != 0) || | |
| (dup_rcvpkoffset[dup] != 0)) | |
| dup_rcvpkinoff[dup] = dup_rcvpkoffset[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_SEND)) && (orig_val & TXCSR_M_SEND)) | |
| dup_txcsr[dup] &= ~TXCSR_M_TXACT; | |
| if (dup_txcsr[dup] & TXCSR_M_DRESET) { | |
| dup_clear(dup, FALSE); | |
| break; | |
| } | |
| 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; | |
| } | |
| sim_debug(DBG_REG, DUPDPTR, "dup_wr(PA=0x%08X [%s], data=0x%X) ", PA, dup_wr_regs[(PA >> 1) & 03], data); | |
| sim_debug_bits(DBG_REG, DUPDPTR, bitdefs[(PA >> 1) & 03], (uint32)orig_val, (uint32)regs[(PA >> 1) & 03][dup], TRUE); | |
| return SCPE_OK; | |
| } | |
| 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; | |
| } | |
| t_stat dup_get_modem (int32 dup) | |
| { | |
| int32 modem_bits; | |
| 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]; | |
| old_rxcsr_a_modem_bits = dup_rxcsr[dup] & RXCSR_A_MODEM_BITS; | |
| old_rxcsr_b_modem_bits = dup_rxcsr[dup] & RXCSR_B_MODEM_BITS; | |
| tmxr_set_get_modem_bits (lp, 0, 0, &modem_bits); | |
| new_rxcsr_a_modem_bits = (((modem_bits & TMXR_MDM_RNG) ? RXCSR_M_RING : 0) | | |
| ((modem_bits & TMXR_MDM_DCD) ? RXCSR_M_DCD : 0) | | |
| ((modem_bits & TMXR_MDM_CTS) ? RXCSR_M_CTS : 0)); | |
| new_rxcsr_b_modem_bits = ((modem_bits & TMXR_MDM_DSR) ? RXCSR_M_DSR : 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; | |
| else | |
| dup_rxcsr[dup] &= ~RXCSR_M_DSCHNG; | |
| if (old_rxcsr_b_modem_bits != new_rxcsr_b_modem_bits) | |
| dup_rxcsr[dup] |= RXCSR_M_BDATSET; | |
| else | |
| dup_rxcsr[dup] &= ~RXCSR_M_BDATSET; | |
| if ((dup_rxcsr[dup] & 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; | |
| CSRPA += IOPAGEBASE; | |
| if ((dib->ba > (uint32)CSRPA) || ((uint32)CSRPA > (dib->ba + dib->lnt))) | |
| return -1; | |
| return ((uint32)CSRPA - dib->ba)/dib->lnt; | |
| } | |
| void dup_set_callback_mode (int32 dup, PACKET_RECEIVE_CALLBACK receive, PACKET_TRANSMIT_COMPLETE_CALLBACK transmit) | |
| { | |
| if ((dup < 0) || (dup >= dup_desc.lines)) | |
| return; | |
| dup_rcv_packet_callback[dup] = receive; | |
| dup_xmt_complete_callback[dup] = transmit; | |
| } | |
| int32 dup_get_line_speed (int32 dup) | |
| { | |
| if ((dup < 0) || (dup >= dup_desc.lines)) | |
| return -1; | |
| return dup_speed[dup]; | |
| } | |
| int32 dup_get_DCD (int32 dup) | |
| { | |
| if ((dup < 0) || (dup >= dup_desc.lines)) | |
| return -1; | |
| return (dup_rxcsr[dup] & RXCSR_M_DCD) ? 1 : 0; | |
| } | |
| t_stat dup_set_DTR (int32 dup, t_bool state) | |
| { | |
| if ((dup < 0) || (dup >= dup_desc.lines)) | |
| return SCPE_IERR; | |
| dup_set_modem (dup, state ? (RXCSR_M_DTR | RXCSR_M_RTS) : 0); | |
| return SCPE_OK; | |
| } | |
| t_stat dup_set_DDCMP (int32 dup, t_bool state) | |
| { | |
| if ((dup < 0) || (dup >= dup_desc.lines)) | |
| return SCPE_IERR; | |
| dup_rxcsr[dup] &= ~RXCSR_M_STRSYN; | |
| dup_rxcsr[dup] |= (state ? 0: RXCSR_M_STRSYN); | |
| dup_parcsr[dup] &= ~PARCSR_M_NOCRC; | |
| dup_parcsr[dup] |= (state ? 0: PARCSR_M_NOCRC); | |
| dup_parcsr[dup] &= ~PARCSR_M_DECMODE; | |
| dup_parcsr[dup] |= (state ? PARCSR_M_DECMODE : 0); | |
| return SCPE_OK; | |
| } | |
| 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_rcvpkoffset[dup]); | |
| if (!(dup_rxcsr[dup] & RXCSR_M_RCVEN) || (dup_rcvpkoffset[dup] == 0) || (dup_rxcsr[dup] & RXCSR_M_RXDONE)) | |
| return SCPE_OK; | |
| if (dup_rcv_packet_callback[dup]) { | |
| dup_rcv_packet_callback[dup](dup, dup_rcvpacket[dup], dup_rcvpkoffset[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_rcvpkoffset[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_rcvpkoffset[dup]) { | |
| dup_rcvpkinoff[dup] = dup_rcvpkoffset[dup] = 0; | |
| dup_rxcsr[dup] &= ~RXCSR_M_RXACT; | |
| } | |
| if (dup_rxcsr[dup] & RXCSR_M_RXIE) | |
| dup_set_rxint (dup); | |
| 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_xmtpkrdy[dup]) { /* Not Busy sending? */ | |
| if (start) | |
| dup_xmtpkoffset[dup] = 0; | |
| if (dup_xmtpkoffset[dup] + 2 + len > dup_xmtpksize[dup]) { | |
| dup_xmtpksize[dup] += 2 + len; | |
| dup_xmtpacket[dup] = realloc (dup_xmtpacket[dup], dup_xmtpksize[dup]); | |
| } | |
| /* Strip sync bytes at the beginning of a message */ | |
| while (len && (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] += 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; | |
| dup_xmtpkoutoff[dup] = 0; | |
| dup_xmtpkrdy[dup] = TRUE; | |
| } | |
| breturn = TRUE; | |
| } | |
| sim_debug (DBG_TRC, DUPDPTR, "dup_put_msg_bytes(dup=%d, len=%d, start=%s, end=%s) %s\n", | |
| dup, len, start ? "TRUE" : "FALSE", end ? "TRUE" : "FALSE", breturn ? "Good" : "Busy"); | |
| return breturn; | |
| } | |
| /* service routine to delay device activity */ | |
| 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) && (!dup_xmtpkrdy[dup])) { | |
| uint8 data = dup_txdbuf[dup] & TXDBUF_M_TXDBUF; | |
| dup_put_msg_bytes (dup, &data, (dup_txdbuf[dup] & TXDBUF_M_TEOM) && (dptr == &dup_dev) ? 0 : 1, dup_txdbuf[dup] & TXDBUF_M_TSOM, (dup_txdbuf[dup] & TXDBUF_M_TEOM)); | |
| if (dup_xmtpkrdy[dup]) { /* Packet ready to send? */ | |
| sim_debug(DBG_TRC, DUPDPTR, "dup_svc(dup=%d) - Packet Done %d bytes\n", dup, dup_xmtpkoffset[dup]); | |
| ddcmp_packet_trace (DBG_PKT, DUPDPTR, ">>> XMT Packet", dup_xmtpacket[dup], dup_xmtpkoffset[dup], TRUE); | |
| } | |
| } | |
| if (dup_xmtpkrdy[dup] && lp->xmte) { | |
| t_stat st = SCPE_OK; | |
| while ((st == SCPE_OK) && (dup_xmtpkoutoff[dup] < dup_xmtpkoffset[dup])) { | |
| st = tmxr_putc_ln (lp, dup_xmtpacket[dup][dup_xmtpkoutoff[dup]]); | |
| if (st == SCPE_OK) | |
| ++dup_xmtpkoutoff[dup]; | |
| } | |
| tmxr_send_buffered_data (lp); /* send any buffered data */ | |
| if (st == SCPE_LOST) { /* line state transition? */ | |
| dup_get_modem (dup); | |
| dup_xmtpkrdy[dup] = FALSE; | |
| dup_xmtpkoffset[dup] = 0; | |
| } | |
| else | |
| if (st == SCPE_OK) { | |
| sim_debug(DBG_PKT, DUPDPTR, "dup_svc(dup=%d) - %d byte packet transmission complete\n", dup, dup_xmtpkoutoff[dup]); | |
| dup_xmtpkrdy[dup] = FALSE; | |
| dup_xmtpkoffset[dup] = 0; | |
| } | |
| else { | |
| sim_debug(DBG_PKT, DUPDPTR, "dup_svc(dup=%d) - Packet Transmission Stalled with %d bytes remaining\n", dup, (int)(dup_xmtpkoffset[dup]-dup_xmtpkoutoff[dup])); | |
| } | |
| if (!dup_xmtpkrdy[dup]) { /* Done transmitting? */ | |
| dup_txcsr[dup] &= ~TXCSR_M_TXACT; /* Set idle */ | |
| 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; | |
| } | |
| t_stat dup_poll_svc (UNIT *uptr) | |
| { | |
| int32 dup, active, attached, c; | |
| sim_debug(DBG_TRC, DUPDPTR, "dup_poll_svc()\n"); | |
| dup = tmxr_poll_conn(&dup_desc); | |
| if (dup >= 0) { /* new connection? */ | |
| dup_rxcsr[dup] |= RXCSR_M_RING | ((dup_rxcsr[dup] & RXCSR_M_DTR) ? (RXCSR_M_DCD | RXCSR_M_CTS | RXCSR_M_DSR) : 0); | |
| dup_rxcsr[dup] |= RXCSR_M_DSCHNG; | |
| if (dup_rxcsr[dup] & RXCSR_M_DSCIE) | |
| dup_set_rxint (dup); /* Interrupt */ | |
| } | |
| 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 && dup_xmtpkrdy[dup]) { | |
| sim_debug(DBG_PKT, DUPDPTR, "dup_poll_svc(dup=%d) - Packet Transmission of remaining %d bytes restarting...\n", dup, (int)(dup_xmtpkoffset[dup]-dup_xmtpkoutoff[dup])); | |
| dup_svc (&dup_units[dup]); /* Flush pending output */ | |
| } | |
| if (!(dup_rxcsr[dup] & RXCSR_M_RXACT)) { | |
| while (TMXR_VALID & (c = tmxr_getc_ln (lp))) { | |
| if (dup_rcvpkoffset[dup] + 1 > dup_rcvpksize[dup]) { | |
| dup_rcvpksize[dup] += 512; | |
| dup_rcvpacket[dup] = realloc (dup_rcvpacket[dup], dup_rcvpksize[dup]); | |
| } | |
| dup_rcvpacket[dup][dup_rcvpkoffset[dup]] = c; | |
| dup_rcvpkoffset[dup] += 1; | |
| if (dup_rcvpkoffset[dup] == 1) { /* Validate first byte in packet */ | |
| if ((dup_rxcsr[dup] & RXCSR_M_STRSYN) && | |
| (dup_rcvpacket[dup][0] == (dup_parcsr[dup] & PARCSR_M_ADSYNC))) { | |
| dup_rcvpkoffset[dup] = 0; | |
| continue; | |
| } | |
| if (dup_parcsr[dup] & PARCSR_M_DECMODE) { | |
| switch (dup_rcvpacket[dup][0]) { | |
| default: | |
| sim_debug (DBG_PKT, DUPDPTR, "Ignoring unexpected byte 0%o in DDCMP mode\n", dup_rcvpacket[dup][0]); | |
| dup_rcvpkoffset[dup] = 0; | |
| case DDCMP_SOH: | |
| case DDCMP_ENQ: | |
| case DDCMP_DLE: | |
| continue; | |
| } | |
| } | |
| } | |
| if (dup_rcvpkoffset[dup] >= 8) { | |
| if (dup_rcvpacket[dup][0] == DDCMP_ENQ) { /* Control Message? */ | |
| ddcmp_packet_trace (DBG_PKT, DUPDPTR, "<<< RCV Packet", dup_rcvpacket[dup], dup_rcvpkoffset[dup], TRUE); | |
| dup_rcvpkinoff[dup] = 0; | |
| dup_rxcsr[dup] |= RXCSR_M_RXACT; | |
| dup_rcv_byte (dup); | |
| break; | |
| } | |
| else { | |
| int32 count = ((dup_rcvpacket[dup][2] & 0x3F) << 8)| dup_rcvpacket[dup][1]; | |
| if (dup_rcvpkoffset[dup] >= 10 + count) { | |
| ddcmp_packet_trace (DBG_PKT, DUPDPTR, "<<< RCV Packet", dup_rcvpacket[dup], dup_rcvpkoffset[dup], TRUE); | |
| dup_rcvpkinoff[dup] = 0; | |
| dup_rxcsr[dup] |= RXCSR_M_RXACT; | |
| dup_rcv_byte (dup); | |
| break; | |
| } | |
| } | |
| } | |
| } | |
| } | |
| } | |
| 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] = (tmr_poll*clk_tps)/(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 */ | |
| void dup_clr_rxint (int32 dup) | |
| { | |
| dup_rxi = dup_rxi & ~(1 << dup); /* clr mux rcv int */ | |
| if (dup_rxi == 0) /* all clr? */ | |
| CLR_INT (DUPRX); | |
| else SET_INT (DUPRX); /* no, set intr */ | |
| return; | |
| } | |
| void dup_set_rxint (int32 dup) | |
| { | |
| dup_rxi = dup_rxi | (1 << dup); /* set mux rcv int */ | |
| SET_INT (DUPRX); /* set master intr */ | |
| sim_debug(DBG_INT, DUPDPTR, "dup_set_rxint(dup=%d)\n", dup); | |
| return; | |
| } | |
| int32 dup_rxinta (void) | |
| { | |
| int32 dup; | |
| for (dup = 0; dup < dup_desc.lines; dup++) { /* find 1st mux */ | |
| if (dup_rxi & (1 << dup)) { | |
| sim_debug(DBG_INT, DUPDPTR, "dup_rxinta(dup=%d)\n", dup); | |
| dup_clr_rxint (dup); /* clear intr */ | |
| return (dup_dib.vec + (dup * 010)); /* return vector */ | |
| } | |
| } | |
| return 0; | |
| } | |
| void dup_clr_txint (int32 dup) | |
| { | |
| dup_txi = dup_txi & ~(1 << dup); /* clr mux xmt int */ | |
| if (dup_txi == 0) /* all clr? */ | |
| CLR_INT (DUPTX); | |
| else SET_INT (DUPTX); /* no, set intr */ | |
| return; | |
| } | |
| void dup_set_txint (int32 dup) | |
| { | |
| dup_txi = dup_txi | (1 << dup); /* set mux xmt int */ | |
| SET_INT (DUPTX); /* set master intr */ | |
| sim_debug(DBG_INT, DUPDPTR, "dup_set_txint(dup=%d)\n", dup); | |
| return; | |
| } | |
| int32 dup_txinta (void) | |
| { | |
| int32 dup; | |
| for (dup = 0; dup < dup_desc.lines; dup++) { /* find 1st mux */ | |
| if (dup_txi & (1 << dup)) { | |
| sim_debug(DBG_INT, DUPDPTR, "dup_txinta(dup=%d)\n", dup); | |
| dup_clr_txint (dup); /* clear intr */ | |
| return (dup_dib.vec + 4 + (dup * 010)); /* return vector */ | |
| } | |
| } | |
| return 0; | |
| } | |
| /* Device reset */ | |
| 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; | |
| else | |
| dup_rxcsr[dup] &= ~(RXCSR_M_DTR|RXCSR_M_RTS); /* else save dtr */ | |
| 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; | |
| } | |
| t_stat dup_reset (DEVICE *dptr) | |
| { | |
| int32 i, ndev; | |
| sim_debug(DBG_TRC, dptr, "dup_reset()\n"); | |
| if ((UNIBUS) && (dptr == &dpv_dev)) { | |
| if (!(dptr->flags & DEV_DIS)) { | |
| printf ("Can't enable Qbus device on Unibus system\n"); | |
| dptr->flags |= DEV_DIS; | |
| return SCPE_ARG; | |
| } | |
| return SCPE_OK; | |
| } | |
| if ((!UNIBUS) && (dptr == &dup_dev)) { | |
| if (!(dptr->flags & DEV_DIS)) { | |
| printf ("Can't enable Unibus device on Qbus system\n"); | |
| dptr->flags |= DEV_DIS; | |
| return SCPE_ARG; | |
| } | |
| return SCPE_OK; | |
| } | |
| if (dup_ldsc == NULL) { /* First time startup */ | |
| dup_desc.ldsc = dup_ldsc = calloc (dup_desc.lines, sizeof(*dup_ldsc)); | |
| for (i = 0; i < dup_desc.lines; i++) /* init each line */ | |
| dup_units[i] = dup_unit_template; | |
| dup_units[dup_desc.lines] = dup_poll_unit_template; | |
| } | |
| for (i = 0; i < dup_desc.lines; i++) /* init each line */ | |
| dup_clear (i, TRUE); | |
| dup_rxi = dup_txi = 0; /* clr master int */ | |
| CLR_INT (DUPRX); | |
| CLR_INT (DUPTX); | |
| 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 (ndev) | |
| sim_activate_after (dup_units+dup_desc.lines, DUP_CONNECT_POLL*1000000); | |
| return auto_config (dptr->name, ndev); /* auto config */ | |
| } | |
| t_stat dup_attach (UNIT *uptr, char *cptr) | |
| { | |
| t_stat r; | |
| DEVICE *dptr = DUPDPTR; | |
| int32 dup = (int32)(uptr-dptr->units); | |
| char attach_string[512]; | |
| if (!cptr || !*cptr) | |
| return SCPE_ARG; | |
| sprintf (attach_string, "Line=%d,Buffered=16384,%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, 2000000); /* start poll */ | |
| return r; | |
| } | |
| t_stat dup_detach (UNIT *uptr) | |
| { | |
| DEVICE *dptr = DUPDPTR; | |
| int32 dup = (int32)(uptr-dptr->units); | |
| TMLN *lp = &dup_ldsc[dup]; | |
| if (!(uptr->flags & UNIT_ATT)) /* attached? */ | |
| return SCPE_OK; | |
| uptr->flags &= ~UNIT_ATT; | |
| free (uptr->filename); | |
| uptr->filename = NULL; | |
| free (dup_rcvpacket[dup]); | |
| dup_rcvpacket[dup] = NULL; | |
| dup_rcvpksize[dup] = 0; | |
| dup_rcvpkoffset[dup] = 0; | |
| free (dup_xmtpacket[dup]); | |
| dup_xmtpacket[dup] = NULL; | |
| dup_xmtpksize[dup] = 0; | |
| dup_xmtpkoffset[dup] = 0; | |
| dup_xmtpkrdy[dup] = FALSE; | |
| dup_xmtpkoutoff[dup] = 0; | |
| return tmxr_detach_ln (lp); | |
| } | |
| /* SET/SHOW SPEED processor */ | |
| t_stat dup_showspeed (FILE* st, UNIT* uptr, int32 val, 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; | |
| } | |
| t_stat dup_setspeed (UNIT* uptr, int32 val, 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 LINES processor */ | |
| t_stat dup_setnl (UNIT *uptr, int32 val, 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, DUP_LINES, &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_dib.lnt = newln * IOLN_DUP; /* set length */ | |
| dup_desc.ldsc = dup_ldsc = 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; | |
| dptr->numunits = newln + 1; | |
| return dup_reset (dptr); /* setup lines and auto config */ | |
| } | |
| t_stat dup_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) | |
| { | |
| fprintf (st, "Bit Serial Synchronous interface (%s)\n\n", dptr->name); | |
| fprintf (st, "The %s connects two systems to provide a network connection.\n", dptr->name); | |
| fprintf (st, "A maximum of %d %s devices/lines can be configured in the system.\n", DUP_LINES, dptr->name); | |
| fprintf (st, "The number of configured devices can be changed with:\n\n"); | |
| fprintf (st, " sim> SET %s LINES=n\n\n", dptr->name); | |
| fprintf (st, "If you want to experience the actual data rates of the physical hardware you\n"); | |
| fprintf (st, "can set the bit rate of the simulated line can be set using the following\n"); | |
| fprintf (st, "command:\n\n"); | |
| fprintf (st, " sim> SET %sn SPEED=bps\n\n", dptr->name); | |
| fprintf (st, "Where bps is the number of data bits per second that the simulated line runs\n"); | |
| fprintf (st, "at. Use a value of zero to run at full speed with no artificial\n"); | |
| fprintf (st, "throttling.\n\n"); | |
| fprint_set_help (st, dptr); | |
| fprint_show_help (st, dptr); | |
| fprint_reg_help (st, dptr); | |
| return SCPE_OK; | |
| } | |
| t_stat dup_help_attach (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) | |
| { | |
| fprintf (st, "The communication line performs input and output through a TCP session\n"); | |
| fprintf (st, "connected to a user-specified port. The ATTACH command specifies the\n"); | |
| fprintf (st, "port to be used as well as the peer address:\n\n"); | |
| fprintf (st, " sim> ATTACH %sn {interface:}port,Connect=peerhost:port\n\n", dptr->name); | |
| fprintf (st, "where port is a decimal number between 1 and 65535 that is not being used for\n"); | |
| fprintf (st, "other TCP/IP activities.\n\n"); | |
| fprintf (st, "Specifying symmetric attach configuration (with both a listen port and\n"); | |
| fprintf (st, "a peer address) will cause the side receiving an incoming\n"); | |
| fprintf (st, "connection to validate that the connection actually comes from the\n"); | |
| fprintf (st, "connecction destination system.\n\n"); | |
| return SCPE_OK; | |
| } | |
| char *dup_description (DEVICE *dptr) | |
| { | |
| return (UNIBUS) ? "DUP11 bit synchronous interface" : | |
| "DPV11 bit synchronous interface"; | |
| } |