/* 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 <ctype.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 */ | |
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); | |
void ddcmp_packet_trace (uint32 reason, DEVICE *dptr, const char *txt, const uint8 *msg, int32 len, t_bool detail); | |
uint16 ddcmp_crc16(uint16 crc, const void* vbuf, size_t len); | |
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 (RXWAIT, dup_wait, 10, 24, DUP_LINES, "delay time for transmit/receive bytes") }, | |
{ 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]; | |
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; | |
if ((dup_rcvpkinoff[dup] != 0) || | |
(dup_rcvpkoffset[dup] != 0)) | |
dup_rcvpkinoff[dup] = dup_rcvpkoffset[dup] = 0; | |
} | |
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; | |
} | |
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)) | |
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; | |
} | |
/* 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])) { | |
if (dup_txdbuf[dup] & TXDBUF_M_TSOM) { | |
dup_xmtpkoffset[dup] = 0; | |
} | |
else { | |
if ((dup_xmtpkoffset[dup] != 0) || | |
((dup_txdbuf[dup] & TXDBUF_M_TXDBUF) != (dup_parcsr[dup] & PARCSR_M_ADSYNC))) { | |
if (!(dup_txdbuf[dup] & TXDBUF_M_TEOM)) { | |
if (dup_xmtpkoffset[dup] + 1 > dup_xmtpksize[dup]) { | |
dup_xmtpksize[dup] += 512; | |
dup_xmtpacket[dup] = realloc (dup_xmtpacket[dup], dup_xmtpksize[dup]); | |
} | |
dup_xmtpacket[dup][dup_xmtpkoffset[dup]] = dup_txdbuf[dup] & TXDBUF_M_TXDBUF; | |
dup_xmtpkoffset[dup] += 1; | |
} | |
} | |
} | |
dup_txcsr[dup] |= TXCSR_M_TXDONE; | |
if (dup_txcsr[dup] & TXCSR_M_TXIE) | |
dup_set_txint (dup); | |
if (dup_txdbuf[dup] & TXDBUF_M_TEOM) { /* Packet ready to send? */ | |
uint16 crc16 = ddcmp_crc16 (0, dup_xmtpacket[dup], dup_xmtpkoffset[dup]); | |
if (dup_xmtpkoffset[dup] + 2 > dup_xmtpksize[dup]) { | |
dup_xmtpksize[dup] += 512; | |
dup_xmtpacket[dup] = realloc (dup_xmtpacket[dup], dup_xmtpksize[dup]); | |
} | |
dup_xmtpacket[dup][dup_xmtpkoffset[dup]++] = crc16 & 0xFF; | |
dup_xmtpacket[dup][dup_xmtpkoffset[dup]++] = crc16 >> 8; | |
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); | |
dup_xmtpkoutoff[dup] = 0; | |
dup_xmtpkrdy[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; | |
} | |
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; | |
} | |
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]) | |
dup_txcsr[dup] &= ~TXCSR_M_TXACT; | |
} | |
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_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_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; | |
} | |
/* Debug routines */ | |
void ddcmp_packet_trace (uint32 reason, DEVICE *dptr, const char *txt, const uint8 *msg, int32 len, t_bool detail) | |
{ | |
if (sim_deb && dptr && (reason & dptr->dctrl)) { | |
sim_debug(reason, dptr, "%s len: %d\n", txt, len); | |
if (detail) { | |
int i, same, group, sidx, oidx; | |
char outbuf[80], strbuf[18]; | |
static char hex[] = "0123456789ABCDEF"; | |
switch (msg[0]) { | |
case DDCMP_SOH: /* Data Message */ | |
sim_debug (reason, dptr, "Data Message, Link: %d, Count: %d, Resp: %d, Num: %d, HDRCRC: %s, DATACRC: %s\n", msg[2]>>6, ((msg[2] & 0x3F) << 8)|msg[1], msg[3], msg[4], | |
(0 == ddcmp_crc16 (0, msg, 8)) ? "OK" : "BAD", (0 == ddcmp_crc16 (0, msg+8, 2+(((msg[2] & 0x3F) << 8)|msg[1]))) ? "OK" : "BAD"); | |
break; | |
case DDCMP_ENQ: /* Control Message */ | |
sim_debug (reason, dptr, "Control: Type: %d ", msg[1]); | |
switch (msg[1]) { | |
case 1: /* ACK */ | |
sim_debug (reason, dptr, "(ACK) ACKSUB: %d, Link: %d, Resp: %d\n", msg[2] & 0x3F, msg[2]>>6, msg[3]); | |
break; | |
case 2: /* NAK */ | |
sim_debug (reason, dptr, "(NAK) Reason: %d, Link: %d, Resp: %d\n", msg[2] & 0x3F, msg[2]>>6, msg[3]); | |
break; | |
case 3: /* REP */ | |
sim_debug (reason, dptr, "(REP) REPSUB: %d, Link: %d, Num: %d\n", msg[2] & 0x3F, msg[2]>>6, msg[4]); | |
break; | |
case 6: /* STRT */ | |
sim_debug (reason, dptr, "(STRT) STRTSUB: %d, Link: %d\n", msg[2] & 0x3F, msg[2]>>6); | |
break; | |
case 7: /* STACK */ | |
sim_debug (reason, dptr, "(STACK) STCKSUB: %d, Link: %d\n", msg[2] & 0x3F, msg[2]>>6); | |
break; | |
default: /* Unknown */ | |
sim_debug (reason, dptr, "(Unknown=0%o)\n", msg[1]); | |
break; | |
} | |
if (len != 8) | |
sim_debug (reason, dptr, "Unexpected Control Message Length: %d expected 8\n", len); | |
if (0 != ddcmp_crc16 (0, msg, len)) | |
sim_debug (reason, dptr, "Unexpected Message CRC\n"); | |
break; | |
case DDCMP_DLE: /* Maintenance Message */ | |
sim_debug (reason, dptr, "Maintenance Message, Link: %d, Count: %d, HDRCRC: %s, DATACRC: %s\n", msg[2]>>6, ((msg[2] & 0x3F) << 8)| msg[1], | |
(0 == ddcmp_crc16 (0, msg, 8)) ? "OK" : "BAD", (0 == ddcmp_crc16 (0, msg+8, 2+(((msg[2] & 0x3F) << 8)| msg[1]))) ? "OK" : "BAD"); | |
break; | |
} | |
for (i=same=0; i<len; i += 16) { | |
if ((i > 0) && (0 == memcmp(&msg[i], &msg[i-16], 16))) { | |
++same; | |
continue; | |
} | |
if (same > 0) { | |
sim_debug(reason, dptr, "%04X thru %04X same as above\n", i-(16*same), i-1); | |
same = 0; | |
} | |
group = (((len - i) > 16) ? 16 : (len - i)); | |
for (sidx=oidx=0; sidx<group; ++sidx) { | |
outbuf[oidx++] = ' '; | |
outbuf[oidx++] = hex[(msg[i+sidx]>>4)&0xf]; | |
outbuf[oidx++] = hex[msg[i+sidx]&0xf]; | |
if (isprint(msg[i+sidx])) | |
strbuf[sidx] = msg[i+sidx]; | |
else | |
strbuf[sidx] = '.'; | |
} | |
outbuf[oidx] = '\0'; | |
strbuf[sidx] = '\0'; | |
sim_debug(reason, dptr, "%04X%-48s %s\n", i, outbuf, strbuf); | |
} | |
if (same > 0) { | |
sim_debug(reason, dptr, "%04X thru %04X same as above\n", i-(16*same), len-1); | |
} | |
} | |
} | |
} | |
/* 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"; | |
} | |
/* crc16 polynomial x^16 + x^15 + x^2 + 1 (0xA001) CCITT LSB */ | |
static uint16 crc16_nibble[16] = { | |
0x0000, 0xCC01, 0xD801, 0x1400, 0xF001, 0x3C00, 0x2800, 0xE401, | |
0xA001, 0x6C00, 0x7800, 0xB401, 0x5000, 0x9C01, 0x8801, 0x4400, | |
}; | |
uint16 ddcmp_crc16(uint16 crc, const void* vbuf, size_t len) | |
{ | |
const unsigned char* buf = (const unsigned char*)vbuf; | |
while(0 != len--) { | |
crc = (crc>>4) ^ crc16_nibble[(*buf ^ crc) & 0xF]; | |
crc = (crc>>4) ^ crc16_nibble[((*buf++)>>4 ^ crc) & 0xF]; | |
}; | |
return(crc); | |
} |