PDP10, PDP11, VAX: Added the DDCMP corruption troll to the DDCMP layer and removed it from the KDP device.

Now all DDCMP capable devices KDP, DUP and DMC/DMR have access to packet corruption generation to simulate real world imperfect data lines for protocol testing.
This commit is contained in:
Mark Pizzolato 2014-02-10 17:22:19 -08:00
parent d7690ce060
commit b1fb809210
5 changed files with 396 additions and 212 deletions

View file

@ -173,6 +173,81 @@ if (sim_deb && dptr && (reason & dptr->dctrl)) {
uint16 ddcmp_crc16(uint16 crc, const void* vbuf, size_t len); uint16 ddcmp_crc16(uint16 crc, const void* vbuf, size_t len);
/* Data corruption troll, which simulates imperfect links.
*/
/* Evaluate the corruption troll's appetite.
*
* A message can be eaten (dropped), nibbled (corrupted) or spared.
*
* The probability of a message not being spared is trollHungerLevel,
* expressed in milli-gulps - 0.1%. The troll selects which action
* to taken on selected messages with equal probability.
*
* Nibbled messages' CRCs are changed when possible to simplify
* identifying them when debugging. When it's too much work to
* find the CRC, the first byte of data is changed. The change
* is an XOR to make it possible to reconstruct the original data.
*
* A particularly unfortunate message can be nibbled by both
* the transmitter and receiver; thus the troll applies a direction-
* dependent pattern.
*
* Return TRUE if the troll ate the message.
* Return FALSE if the message was nibbled or spared.
*/
static t_bool ddcmp_feedCorruptionTroll (TMLN *lp, uint8 *msg, t_bool rx, int32 trollHungerLevel)
{
double r, rmax;
char msgbuf[80];
if (trollHungerLevel == 0)
return FALSE;
#if defined(_POSIX_VERSION) || defined (_XOPEN_VERSION)
r = (double)random();
rmax = (double)0x7fffffff;
#else
r = rand();
rmax = (double)RAND_MAX;
#endif
if (msg[0] == DDCMP_ENQ) {
int eat = 0 + (int) (2000.0 * (r / rmax));
if (eat <= (trollHungerLevel * 2)) { /* Hungry? */
if (eat <= trollHungerLevel) { /* Eat the packet */
sprintf (msgbuf, "troll ate a %s control message", rx ? "RCV" : "XMT");
tmxr_debug_msg (rx ? DDCMP_DBG_PRCV : DDCMP_DBG_PXMT, lp, msgbuf);
return TRUE;
}
sprintf (msgbuf, "troll bit a %s control message", rx ? "RCV" : "XMT");
tmxr_debug_msg (rx ? DDCMP_DBG_PRCV : DDCMP_DBG_PXMT, lp, msgbuf);
msg[6] ^= rx? 0114: 0154; /* Eat the CRC */
}
}
else {
int eat = 0 + (int) (3000.0 * (r / rmax));
if (eat <= (trollHungerLevel * 3)) { /* Hungry? */
if (eat <= trollHungerLevel) { /* Eat the packet */
sprintf (msgbuf, "troll ate a %s %s message", rx ? "RCV" : "XMT", (msg[0] == DDCMP_SOH)? "data" : "maintenance");
tmxr_debug_msg (rx ? DDCMP_DBG_PRCV : DDCMP_DBG_PXMT, lp, msgbuf);
return TRUE;
}
if (eat <= (trollHungerLevel * 2)) { /* HCRC */
sprintf (msgbuf, "troll bit a %s %s message", rx ? "RCV" : "XMT", (msg[0] == DDCMP_SOH)? "data" : "maintenance");
tmxr_debug_msg (rx ? DDCMP_DBG_PRCV : DDCMP_DBG_PXMT, lp, msgbuf);
msg[6] ^= rx? 0124: 0164;
}
else { /* DCRC */
sprintf (msgbuf, "troll bit %s %s DCRC\n", (rx? "RCV" : "XMT"), ((msg[0] == DDCMP_SOH)? "data" : "maintenance"));
tmxr_debug_msg (rx ? DDCMP_DBG_PRCV : DDCMP_DBG_PXMT, lp, msgbuf);
msg[8] ^= rx? 0114: 0154; /* Rather than find the CRC, the first data byte will do */
}
}
}
return FALSE;
}
/* Get packet from specific line /* Get packet from specific line
Inputs: Inputs:
@ -190,7 +265,7 @@ uint16 ddcmp_crc16(uint16 crc, const void* vbuf, size_t len);
NULL, but success (SCPE_OK) is returned NULL, but success (SCPE_OK) is returned
*/ */
static t_stat ddcmp_tmxr_get_packet_ln (TMLN *lp, const uint8 **pbuf, uint16 *psize) static t_stat ddcmp_tmxr_get_packet_ln (TMLN *lp, const uint8 **pbuf, uint16 *psize, int32 corruptrate)
{ {
int32 c; int32 c;
size_t payloadsize; size_t payloadsize;
@ -230,6 +305,8 @@ while (TMXR_VALID & (c = tmxr_getc_ln (lp))) {
else else
strcpy (msg, "<<< RCV Packet"); strcpy (msg, "<<< RCV Packet");
ddcmp_packet_trace (DDCMP_DBG_PRCV, lp->mp->dptr, msg, lp->rxpb, *psize); ddcmp_packet_trace (DDCMP_DBG_PRCV, lp->mp->dptr, msg, lp->rxpb, *psize);
if (ddcmp_feedCorruptionTroll (lp, lp->rxpb, TRUE, corruptrate))
break;
return SCPE_OK; return SCPE_OK;
} }
payloadsize = ((lp->rxpb[2] & 0x3F) << 8)| lp->rxpb[1]; payloadsize = ((lp->rxpb[2] & 0x3F) << 8)| lp->rxpb[1];
@ -243,6 +320,8 @@ while (TMXR_VALID & (c = tmxr_getc_ln (lp))) {
strcpy (msg, "<<< RCV Packet"); strcpy (msg, "<<< RCV Packet");
ddcmp_packet_trace (DDCMP_DBG_PRCV, lp->mp->dptr, msg, lp->rxpb, *psize); ddcmp_packet_trace (DDCMP_DBG_PRCV, lp->mp->dptr, msg, lp->rxpb, *psize);
lp->rxpboffset = 0; lp->rxpboffset = 0;
if (ddcmp_feedCorruptionTroll (lp, lp->rxpb, TRUE, corruptrate))
break;
return SCPE_OK; return SCPE_OK;
} }
} }
@ -270,7 +349,7 @@ return SCPE_LOST;
2. If prior packet transmission still in progress, SCPE_STALL is 2. If prior packet transmission still in progress, SCPE_STALL is
returned and no packet data is stored. The caller must retry later. returned and no packet data is stored. The caller must retry later.
*/ */
static t_stat ddcmp_tmxr_put_packet_ln (TMLN *lp, const uint8 *buf, size_t size) static t_stat ddcmp_tmxr_put_packet_ln (TMLN *lp, const uint8 *buf, size_t size, int32 corruptrate)
{ {
t_stat r; t_stat r;
char msg[32]; char msg[32];
@ -293,15 +372,17 @@ if (lp->mp->lines > 1)
else else
strcpy (msg, ">>> XMT Packet"); strcpy (msg, ">>> XMT Packet");
ddcmp_packet_trace (DDCMP_DBG_PXMT, lp->mp->dptr, msg, lp->txpb, lp->txppsize); ddcmp_packet_trace (DDCMP_DBG_PXMT, lp->mp->dptr, msg, lp->txpb, lp->txppsize);
if (!ddcmp_feedCorruptionTroll (lp, lp->txpb, FALSE, corruptrate)) {
++lp->txpcnt; ++lp->txpcnt;
while ((lp->txppoffset < lp->txppsize) && while ((lp->txppoffset < lp->txppsize) &&
(SCPE_OK == (r = tmxr_putc_ln (lp, lp->txpb[lp->txppoffset])))) (SCPE_OK == (r = tmxr_putc_ln (lp, lp->txpb[lp->txppoffset]))))
++lp->txppoffset; ++lp->txppoffset;
tmxr_send_buffered_data (lp); tmxr_send_buffered_data (lp);
}
return lp->conn ? SCPE_OK : SCPE_LOST; return lp->conn ? SCPE_OK : SCPE_LOST;
} }
static t_stat ddcmp_tmxr_put_packet_crc_ln (TMLN *lp, uint8 *buf, size_t size) static t_stat ddcmp_tmxr_put_packet_crc_ln (TMLN *lp, uint8 *buf, size_t size, int32 corruptrate)
{ {
uint16 hdr_crc16 = ddcmp_crc16(0, buf, DDCMP_HEADER_SIZE-DDCMP_CRC_SIZE); uint16 hdr_crc16 = ddcmp_crc16(0, buf, DDCMP_HEADER_SIZE-DDCMP_CRC_SIZE);
@ -312,7 +393,7 @@ if (size > DDCMP_HEADER_SIZE) {
buf[size-DDCMP_CRC_SIZE] = data_crc16 & 0xFF; buf[size-DDCMP_CRC_SIZE] = data_crc16 & 0xFF;
buf[size-DDCMP_CRC_SIZE+1] = (data_crc16>>8) & 0xFF; buf[size-DDCMP_CRC_SIZE+1] = (data_crc16>>8) & 0xFF;
} }
return ddcmp_tmxr_put_packet_ln (lp, buf, size); return ddcmp_tmxr_put_packet_ln (lp, buf, size, corruptrate);
} }
static void ddcmp_build_data_packet (uint8 *buf, size_t size, uint8 flags, uint8 sequence, uint8 ack) static void ddcmp_build_data_packet (uint8 *buf, size_t size, uint8 flags, uint8 sequence, uint8 ack)
@ -338,7 +419,7 @@ buf[5] = 1;
static t_stat ddcmp_tmxr_put_data_packet_ln (TMLN *lp, uint8 *buf, size_t size, uint8 flags, uint8 sequence, uint8 ack) static t_stat ddcmp_tmxr_put_data_packet_ln (TMLN *lp, uint8 *buf, size_t size, uint8 flags, uint8 sequence, uint8 ack)
{ {
ddcmp_build_data_packet (buf, size, flags, sequence, ack); ddcmp_build_data_packet (buf, size, flags, sequence, ack);
return ddcmp_tmxr_put_packet_crc_ln (lp, buf, size); return ddcmp_tmxr_put_packet_crc_ln (lp, buf, size, 0);
} }
static void ddcmp_build_control_packet (uint8 *buf, uint8 type, uint8 subtype, uint8 flags, uint8 sndr, uint8 rcvr) static void ddcmp_build_control_packet (uint8 *buf, uint8 type, uint8 subtype, uint8 flags, uint8 sndr, uint8 rcvr)
@ -355,7 +436,7 @@ buf[5] = 1; /* ADDR */
static t_stat ddcmp_tmxr_put_control_packet_ln (TMLN *lp, uint8 *buf, uint8 type, uint8 subtype, uint8 flags, uint8 sndr, uint8 rcvr) static t_stat ddcmp_tmxr_put_control_packet_ln (TMLN *lp, uint8 *buf, uint8 type, uint8 subtype, uint8 flags, uint8 sndr, uint8 rcvr)
{ {
ddcmp_build_control_packet (buf, type, subtype, flags, sndr, rcvr); ddcmp_build_control_packet (buf, type, subtype, flags, sndr, rcvr);
return ddcmp_tmxr_put_packet_crc_ln (lp, buf, DDCMP_HEADER_SIZE); return ddcmp_tmxr_put_packet_crc_ln (lp, buf, DDCMP_HEADER_SIZE, 0);
} }
static void ddcmp_build_ack_packet (uint8 *buf, uint8 ack, uint8 flags) static void ddcmp_build_ack_packet (uint8 *buf, uint8 ack, uint8 flags)
@ -366,7 +447,7 @@ ddcmp_build_control_packet (buf, DDCMP_CTL_ACK, 0, flags, 0, ack);
static t_stat ddcmp_tmxr_put_ack_packet_ln (TMLN *lp, uint8 *buf, uint8 ack, uint8 flags) static t_stat ddcmp_tmxr_put_ack_packet_ln (TMLN *lp, uint8 *buf, uint8 ack, uint8 flags)
{ {
ddcmp_build_ack_packet (buf, ack, flags); ddcmp_build_ack_packet (buf, ack, flags);
return ddcmp_tmxr_put_packet_crc_ln (lp, buf, DDCMP_HEADER_SIZE); return ddcmp_tmxr_put_packet_crc_ln (lp, buf, DDCMP_HEADER_SIZE, 0);
} }
static void ddcmp_build_nak_packet (uint8 *buf, uint8 reason, uint8 nack, uint8 flags) static void ddcmp_build_nak_packet (uint8 *buf, uint8 reason, uint8 nack, uint8 flags)
@ -397,7 +478,7 @@ ddcmp_build_control_packet (buf, DDCMP_CTL_STRT, 0, DDCMP_FLAG_SELECT|DDCMP_FLAG
static t_stat ddcmp_tmxr_put_start_packet_ln (TMLN *lp, uint8 *buf) static t_stat ddcmp_tmxr_put_start_packet_ln (TMLN *lp, uint8 *buf)
{ {
ddcmp_build_start_packet (buf); ddcmp_build_start_packet (buf);
return ddcmp_tmxr_put_packet_crc_ln (lp, buf, DDCMP_HEADER_SIZE); return ddcmp_tmxr_put_packet_crc_ln (lp, buf, DDCMP_HEADER_SIZE, 0);
} }
static void ddcmp_build_start_ack_packet (uint8 *buf) static void ddcmp_build_start_ack_packet (uint8 *buf)
@ -408,7 +489,7 @@ ddcmp_build_control_packet (buf, DDCMP_CTL_STACK, 0, DDCMP_FLAG_SELECT|DDCMP_FLA
static t_stat ddcmp_tmxr_put_start_ack_packet_ln (TMLN *lp, uint8 *buf) static t_stat ddcmp_tmxr_put_start_ack_packet_ln (TMLN *lp, uint8 *buf)
{ {
ddcmp_build_start_ack_packet (buf); ddcmp_build_start_ack_packet (buf);
return ddcmp_tmxr_put_packet_crc_ln (lp, buf, DDCMP_HEADER_SIZE); return ddcmp_tmxr_put_packet_crc_ln (lp, buf, DDCMP_HEADER_SIZE, 0);
} }
#endif /* PDP11_DDCMP_H_ */ #endif /* PDP11_DDCMP_H_ */

View file

@ -517,6 +517,7 @@ typedef struct dmc_controller {
uint32 *baseaddr; uint32 *baseaddr;
uint16 *basesize; uint16 *basesize;
uint8 *modem; uint8 *modem;
int32 *corruption_factor;
uint32 buffers_received_from_net; uint32 buffers_received_from_net;
uint32 buffers_transmitted_to_net; uint32 buffers_transmitted_to_net;
uint32 receive_buffer_output_transfers_completed; uint32 receive_buffer_output_transfers_completed;
@ -882,6 +883,8 @@ t_stat dmc_setpeer (UNIT* uptr, int32 val, char* cptr, void* desc);
t_stat dmc_showpeer (FILE* st, UNIT* uptr, int32 val, void* desc); t_stat dmc_showpeer (FILE* st, UNIT* uptr, int32 val, void* desc);
t_stat dmc_setspeed (UNIT* uptr, int32 val, char* cptr, void* desc); t_stat dmc_setspeed (UNIT* uptr, int32 val, char* cptr, void* desc);
t_stat dmc_showspeed (FILE* st, UNIT* uptr, int32 val, void* desc); t_stat dmc_showspeed (FILE* st, UNIT* uptr, int32 val, void* desc);
t_stat dmc_setcorrupt (UNIT *uptr, int32 val, char *cptr, void *desc);
t_stat dmc_showcorrupt (FILE *st, UNIT *uptr, int32 val, void *desc);
t_stat dmc_set_microdiag (UNIT* uptr, int32 val, char* cptr, void* desc); t_stat dmc_set_microdiag (UNIT* uptr, int32 val, char* cptr, void* desc);
t_stat dmc_show_microdiag (FILE* st, UNIT* uptr, int32 val, void* desc); t_stat dmc_show_microdiag (FILE* st, UNIT* uptr, int32 val, void* desc);
t_stat dmc_settype (UNIT* uptr, int32 val, char* cptr, void* desc); t_stat dmc_settype (UNIT* uptr, int32 val, char* cptr, void* desc);
@ -968,6 +971,7 @@ uint32 dmc_baseaddr[DMC_NUMDEVICE];
uint16 dmc_basesize[DMC_NUMDEVICE]; uint16 dmc_basesize[DMC_NUMDEVICE];
uint8 dmc_modem[DMC_NUMDEVICE]; uint8 dmc_modem[DMC_NUMDEVICE];
t_bool dmc_microdiag[DMC_NUMDEVICE]; t_bool dmc_microdiag[DMC_NUMDEVICE];
int32 dmc_corruption[DMC_NUMDEVICE];
CSRS dmp_csrs[DMP_NUMDEVICE]; CSRS dmp_csrs[DMP_NUMDEVICE];
uint16 dmp_sel0[DMC_NUMDEVICE]; uint16 dmp_sel0[DMC_NUMDEVICE];
@ -982,6 +986,7 @@ char dmp_port[DMP_NUMDEVICE][CBUFSIZE];
uint32 dmp_baseaddr[DMP_NUMDEVICE]; uint32 dmp_baseaddr[DMP_NUMDEVICE];
uint16 dmp_basesize[DMP_NUMDEVICE]; uint16 dmp_basesize[DMP_NUMDEVICE];
uint8 dmp_modem[DMP_NUMDEVICE]; uint8 dmp_modem[DMP_NUMDEVICE];
int32 dmp_corruption[DMC_NUMDEVICE];
TMLN dmc_ldsc[DMC_NUMDEVICE]; /* line descriptors */ TMLN dmc_ldsc[DMC_NUMDEVICE]; /* line descriptors */
TMXR dmc_desc = { 1, NULL, 0, dmc_ldsc }; /* mux descriptor */ TMXR dmc_desc = { 1, NULL, 0, dmc_ldsc }; /* mux descriptor */
@ -1017,6 +1022,7 @@ REG dmc_reg[] = {
{ BRDATAD (SEL4, dmc_sel4, DEV_RDX, 16, DMC_NUMDEVICE, "Select 4 CSR") }, { BRDATAD (SEL4, dmc_sel4, DEV_RDX, 16, DMC_NUMDEVICE, "Select 4 CSR") },
{ BRDATAD (SEL6, dmc_sel6, DEV_RDX, 16, DMC_NUMDEVICE, "Select 6 CSR") }, { BRDATAD (SEL6, dmc_sel6, DEV_RDX, 16, DMC_NUMDEVICE, "Select 6 CSR") },
{ BRDATAD (SPEED, dmc_speed, DEV_RDX, 32, DMC_NUMDEVICE, "line speed") }, { BRDATAD (SPEED, dmc_speed, DEV_RDX, 32, DMC_NUMDEVICE, "line speed") },
{ BRDATAD (CORRUPT, dmc_corruption, DEV_RDX, 32, DMC_NUMDEVICE, "data corruption factor (0.1%)") },
{ BRDATAD (DIAG, dmc_microdiag, DEV_RDX, 1, DMC_NUMDEVICE, "Microdiagnostic Enabled") }, { BRDATAD (DIAG, dmc_microdiag, DEV_RDX, 1, DMC_NUMDEVICE, "Microdiagnostic Enabled") },
{ BRDATAD (PEER, dmc_peer, DEV_RDX, 8, DMC_NUMDEVICE*CBUFSIZE, "peer address:port") }, { BRDATAD (PEER, dmc_peer, DEV_RDX, 8, DMC_NUMDEVICE*CBUFSIZE, "peer address:port") },
{ BRDATAD (PORT, dmc_port, DEV_RDX, 8, DMC_NUMDEVICE*CBUFSIZE, "listen port") }, { BRDATAD (PORT, dmc_port, DEV_RDX, 8, DMC_NUMDEVICE*CBUFSIZE, "listen port") },
@ -1035,6 +1041,7 @@ REG dmp_reg[] = {
{ BRDATAD (SEL6, dmp_sel6, DEV_RDX, 16, DMC_NUMDEVICE, "Select 6 CSR") }, { BRDATAD (SEL6, dmp_sel6, DEV_RDX, 16, DMC_NUMDEVICE, "Select 6 CSR") },
{ BRDATAD (SEL10, dmp_sel10, DEV_RDX, 16, DMP_NUMDEVICE, "Select 10 CSR") }, { BRDATAD (SEL10, dmp_sel10, DEV_RDX, 16, DMP_NUMDEVICE, "Select 10 CSR") },
{ BRDATAD (SPEED, dmp_speed, DEV_RDX, 32, DMC_NUMDEVICE, "line speed") }, { BRDATAD (SPEED, dmp_speed, DEV_RDX, 32, DMC_NUMDEVICE, "line speed") },
{ BRDATAD (CORRUPT, dmp_corruption, DEV_RDX, 32, DMC_NUMDEVICE, "data corruption factor (0.1%)") },
{ BRDATAD (PEER, dmp_peer, DEV_RDX, 8, DMC_NUMDEVICE*CBUFSIZE, "peer address:port") }, { BRDATAD (PEER, dmp_peer, DEV_RDX, 8, DMC_NUMDEVICE*CBUFSIZE, "peer address:port") },
{ BRDATAD (PORT, dmp_port, DEV_RDX, 8, DMC_NUMDEVICE*CBUFSIZE, "listen port") }, { BRDATAD (PORT, dmp_port, DEV_RDX, 8, DMC_NUMDEVICE*CBUFSIZE, "listen port") },
{ BRDATAD (BASEADDR, dmp_baseaddr, DEV_RDX, 32, DMC_NUMDEVICE, "program set base address") }, { BRDATAD (BASEADDR, dmp_baseaddr, DEV_RDX, 32, DMC_NUMDEVICE, "program set base address") },
@ -1064,6 +1071,8 @@ MTAB dmc_mod[] = {
NULL, &dmc_showddcmp, NULL, "Display DDCMP state information" }, NULL, &dmc_showddcmp, NULL, "Display DDCMP state information" },
{ MTAB_XTD|MTAB_VDV, 0, "CONNECTPOLL", "CONNECTPOLL=seconds", { MTAB_XTD|MTAB_VDV, 0, "CONNECTPOLL", "CONNECTPOLL=seconds",
&dmc_setconnectpoll, &dmc_showconnectpoll, (void *) &dmc_connect_poll, "Display connection poll interval" }, &dmc_setconnectpoll, &dmc_showconnectpoll, (void *) &dmc_connect_poll, "Display connection poll interval" },
{ MTAB_XTD|MTAB_VUN|MTAB_VALR|MTAB_NMO, 0, "CORRUPT", "CORRUPTION=factor (0=uncorrupted)" ,
&dmc_setcorrupt, &dmc_showcorrupt, NULL, "Display corruption factor (0.1% of packets)" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 020, "ADDRESS", "ADDRESS", { MTAB_XTD|MTAB_VDV|MTAB_VALR, 020, "ADDRESS", "ADDRESS",
&set_addr, &show_addr, NULL, "Bus address" }, &set_addr, &show_addr, NULL, "Bus address" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 1, "VECTOR", "VECTOR", { MTAB_XTD|MTAB_VDV|MTAB_VALR, 1, "VECTOR", "VECTOR",
@ -1087,6 +1096,8 @@ MTAB dmp_mod[] = {
NULL, &dmc_showddcmp, NULL, "Display DDCMP state information" }, NULL, &dmc_showddcmp, NULL, "Display DDCMP state information" },
{ MTAB_XTD|MTAB_VDV, 0, "CONNECTPOLL", "CONNECTPOLL=seconds", { MTAB_XTD|MTAB_VDV, 0, "CONNECTPOLL", "CONNECTPOLL=seconds",
&dmc_setconnectpoll, &dmc_showconnectpoll, (void *) &dmp_connect_poll, "Display connection poll interval" }, &dmc_setconnectpoll, &dmc_showconnectpoll, (void *) &dmp_connect_poll, "Display connection poll interval" },
{ MTAB_XTD|MTAB_VUN|MTAB_VALR|MTAB_NMO, 0, "CORRUPTION", "CORRUPTION=factor (0=uncorrupted)" ,
&dmc_setcorrupt, &dmc_showcorrupt, NULL, "Display corruption factor (0.1% of packets)" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 020, "ADDRESS", "ADDRESS", { MTAB_XTD|MTAB_VDV|MTAB_VALR, 020, "ADDRESS", "ADDRESS",
&set_addr, &show_addr, NULL, "Bus address" }, &set_addr, &show_addr, NULL, "Bus address" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 1, "VECTOR", "VECTOR", { MTAB_XTD|MTAB_VDV|MTAB_VALR, 1, "VECTOR", "VECTOR",
@ -1270,6 +1281,47 @@ fprintf(st, "MicroDiag=%s", dmc_microdiag[dmc] ? "enabled" : "disabled");
return SCPE_OK; return SCPE_OK;
} }
/* Manage the corruption troll's appetite, in units of milli-gulps.
*
* See ddcmp_feedCorruptionTroll for usage.
*/
t_stat dmc_setcorrupt (UNIT *uptr, int32 val, char *cptr, void *desc)
{
DEVICE *dptr = (UNIBUS) ? ((&dmc_dev == find_dev_from_unit(uptr)) ? &dmc_dev : &dmp_dev) : &dmv_dev;
int32 dmc = (int32)(uptr-dptr->units);
int32 *hunger = (dptr == &dmc_dev) ? &dmc_corruption[dmc] : &dmp_corruption[dmc];
t_stat r;
int32 appetite;
if ((cptr == NULL) || (*cptr == '\0'))
return SCPE_ARG;
appetite = (int32) get_uint (cptr, 10, 999, &r);
if (r != SCPE_OK)
return r;
*hunger = appetite;
return SCPE_OK;
}
/* Display the corruption troll's appetite */
t_stat dmc_showcorrupt (FILE *st, UNIT *uptr, int32 val, void *desc)
{
DEVICE *dptr = (UNIBUS) ? ((&dmc_dev == find_dev_from_unit(uptr)) ? &dmc_dev : &dmp_dev) : &dmv_dev;
int32 dmc = (int32)(uptr-dptr->units);
int32 *hunger = (dptr == &dmc_dev) ? &dmc_corruption[dmc] : &dmp_corruption[dmc];
if (*hunger)
fprintf(st, "Corruption=%d milligulps (%.1f%% of messages processed)",
*hunger, ((double)*hunger)/10.0);
else
fprintf(st, "No Corruption");
return SCPE_OK;
}
t_stat dmc_set_microdiag(UNIT* uptr, int32 val, char* cptr, void* desc) t_stat dmc_set_microdiag(UNIT* uptr, int32 val, char* cptr, void* desc)
{ {
int32 dmc = (int32)(uptr-dmc_dev.units); int32 dmc = (int32)(uptr-dmc_dev.units);
@ -1570,7 +1622,7 @@ const char helpString[] =
" simulator. The number of simulated %1s devices or lines can be\n" " simulator. The number of simulated %1s devices or lines can be\n"
" specified with command:\n" " specified with command:\n"
"\n" "\n"
"+sim> SET %U LINES=n\n" "+sim> SET %D LINES=n\n"
"3 Peer\n" "3 Peer\n"
" To set the host and port to which data is to be transmitted use the\n" " To set the host and port to which data is to be transmitted use the\n"
" following command:\n" " following command:\n"
@ -1604,7 +1656,18 @@ const char helpString[] =
" A SET TYPE command should be entered before the device is attached to a\n" " A SET TYPE command should be entered before the device is attached to a\n"
" listening port.\n" " listening port.\n"
#endif #endif
"3 Corruption\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"
/****************************************************************************/ /****************************************************************************/
#define DMC_HLP_ATTACH "Configuration Attach"
"2 Attach\n" "2 Attach\n"
" The device must be attached to a receive port, use the ATTACH command\n" " The device must be attached to a receive port, use the ATTACH command\n"
" specifying the receive port number.\n" " specifying the receive port number.\n"
@ -1636,6 +1699,16 @@ const char helpString[] =
" The %D device and %U line configuration and state can be displayed with\n" " The %D device and %U line configuration and state can be displayed with\n"
" one of the available show commands.\n" " one of the available show commands.\n"
"2 $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\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" "1 Restrictions\n"
" Real hardware synchronous connections could operate in Multi-Point mode.\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" " Multi-Point mode was a way of sharing a single wire with multiple\n"
@ -1708,15 +1781,7 @@ return scp_help (st, dptr, uptr, flag, helpString, cptr, devname, devcount, conn
t_stat dmc_help_attach (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) t_stat dmc_help_attach (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
{ {
const char helpString[] = return dmc_help (st, dptr, uptr, flag, DMC_HLP_ATTACH);
" The communication line performs input and output through a TCP session\n"
" connected to a user-specified port. The ATTACH command specifies the\n"
" listening port to be used when the incoming connection is established:\n\n"
"+sim> ATTACH %U {interface:}port{,UDP} set up listening 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";
return scp_help (st, dptr, uptr, flag, helpString, cptr);
return SCPE_OK;
} }
void dmc_setinint(CTLR *controller) void dmc_setinint(CTLR *controller)
@ -2529,7 +2594,7 @@ if (controller->state == Running) {
BUFFER *buffer = dmc_buffer_queue_head(controller->rcv_queue); BUFFER *buffer = dmc_buffer_queue_head(controller->rcv_queue);
while (buffer) { while (buffer) {
ddcmp_tmxr_get_packet_ln (controller->line, (const uint8 **)&controller->link.rcv_pkt, &controller->link.rcv_pkt_size); ddcmp_tmxr_get_packet_ln (controller->line, (const uint8 **)&controller->link.rcv_pkt, &controller->link.rcv_pkt_size, *controller->corruption_factor);
if (!controller->link.rcv_pkt) if (!controller->link.rcv_pkt)
break; break;
ans = TRUE; ans = TRUE;
@ -3292,7 +3357,7 @@ while (buffer) {
if (buffer->transfer_buffer[0] == 0) if (buffer->transfer_buffer[0] == 0)
return; return;
/* Need to make sure we dynamically compute the packet CRCs since header details can change */ /* Need to make sure we dynamically compute the packet CRCs since header details can change */
r = ddcmp_tmxr_put_packet_crc_ln (controller->line, buffer->transfer_buffer, buffer->count); r = ddcmp_tmxr_put_packet_crc_ln (controller->line, buffer->transfer_buffer, buffer->count, *controller->corruption_factor);
if (r == SCPE_OK) { if (r == SCPE_OK) {
controller->link.xmt_buffer = buffer; controller->link.xmt_buffer = buffer;
controller->ddcmp_control_packets_sent += (buffer->transfer_buffer[0] == DDCMP_ENQ) ? 1 : 0; controller->ddcmp_control_packets_sent += (buffer->transfer_buffer[0] == DDCMP_ENQ) ? 1 : 0;
@ -3494,6 +3559,7 @@ for (i=0; i < DMC_NUMDEVICE; i++) {
controller->baseaddr = &dmc_baseaddr[i]; controller->baseaddr = &dmc_baseaddr[i];
controller->basesize = &dmc_basesize[i]; controller->basesize = &dmc_basesize[i];
controller->modem = &dmc_modem[i]; controller->modem = &dmc_modem[i];
controller->corruption_factor = &dmc_corruption[i];
controller->unit = &controller->device->units[i]; controller->unit = &controller->device->units[i];
controller->unit->ctlr = (void *)controller; controller->unit->ctlr = (void *)controller;
controller->index = i; controller->index = i;
@ -3519,6 +3585,7 @@ for (i=0; i < DMP_NUMDEVICE; i++) {
controller->baseaddr = &dmp_baseaddr[i]; controller->baseaddr = &dmp_baseaddr[i];
controller->basesize = &dmp_basesize[i]; controller->basesize = &dmp_basesize[i];
controller->modem = &dmp_modem[i]; controller->modem = &dmp_modem[i];
controller->corruption_factor = &dmp_corruption[i];
controller->unit = &controller->device->units[i]; controller->unit = &controller->device->units[i];
controller->unit->ctlr = (void *)controller; controller->unit->ctlr = (void *)controller;
controller->index = i + DMC_NUMDEVICE; controller->index = i + DMC_NUMDEVICE;
@ -3539,6 +3606,7 @@ if (0 == dmc_units[0].flags) { /* First Time Initializations */
dmc_dev.units[i] = dmc_unit_template; dmc_dev.units[i] = dmc_unit_template;
controller->unit->ctlr = (void *)controller; controller->unit->ctlr = (void *)controller;
dmc_microdiag[i] = TRUE; dmc_microdiag[i] = TRUE;
dmc_corruption[i] = 0;
} }
tmxr_set_modem_control_passthru (&dmc_desc); /* We always want Modem Control */ tmxr_set_modem_control_passthru (&dmc_desc); /* We always want Modem Control */
dmc_units[dmc_dev.numunits-2] = dmc_poll_unit_template; dmc_units[dmc_dev.numunits-2] = dmc_poll_unit_template;
@ -3556,6 +3624,7 @@ if (0 == dmc_units[0].flags) { /* First Time Initializations */
*controller->modem = 0; *controller->modem = 0;
dmp_dev.units[i] = dmc_unit_template; dmp_dev.units[i] = dmc_unit_template;
controller->unit->ctlr = (void *)controller; controller->unit->ctlr = (void *)controller;
dmp_corruption[i] = 0;
} }
tmxr_set_modem_control_passthru (&dmp_desc); /* We always want Modem Control */ tmxr_set_modem_control_passthru (&dmp_desc); /* We always want Modem Control */
dmp_units[dmp_dev.numunits-2] = dmc_poll_unit_template; dmp_units[dmp_dev.numunits-2] = dmc_poll_unit_template;

View file

@ -87,6 +87,7 @@ static uint16 dup_xmtpkoffset[DUP_LINES]; /* xmt buffer off
static uint32 dup_xmtpkstart[DUP_LINES]; /* xmt packet start time */ static uint32 dup_xmtpkstart[DUP_LINES]; /* xmt packet start time */
static uint16 dup_xmtpkbytes[DUP_LINES]; /* xmt packet size of packet */ static uint16 dup_xmtpkbytes[DUP_LINES]; /* xmt packet size of packet */
static uint16 dup_xmtpkdelaying[DUP_LINES]; /* xmt packet speed delaying completion */ static uint16 dup_xmtpkdelaying[DUP_LINES]; /* xmt packet speed delaying completion */
static int32 dup_corruption[DUP_LINES]; /* data corrupting troll hunger value */
static PACKET_DATA_AVAILABLE_CALLBACK dup_rcv_packet_data_callback[DUP_LINES]; static PACKET_DATA_AVAILABLE_CALLBACK dup_rcv_packet_data_callback[DUP_LINES];
static PACKET_TRANSMIT_COMPLETE_CALLBACK dup_xmt_complete_callback[DUP_LINES]; static PACKET_TRANSMIT_COMPLETE_CALLBACK dup_xmt_complete_callback[DUP_LINES];
@ -114,6 +115,8 @@ static void dup_set_txint (int32 dup);
static t_stat dup_setnl (UNIT *uptr, int32 val, char *cptr, void *desc); static t_stat dup_setnl (UNIT *uptr, int32 val, char *cptr, void *desc);
static t_stat dup_setspeed (UNIT* uptr, int32 val, char* cptr, void* desc); static t_stat dup_setspeed (UNIT* uptr, int32 val, char* cptr, void* desc);
static t_stat dup_showspeed (FILE* st, UNIT* uptr, int32 val, void* desc); static t_stat dup_showspeed (FILE* st, UNIT* uptr, int32 val, void* desc);
static t_stat dup_setcorrupt (UNIT *uptr, int32 val, char *cptr, void *desc);
static t_stat dup_showcorrupt (FILE *st, UNIT *uptr, int32 val, void *desc);
static t_stat dup_set_W3 (UNIT* uptr, int32 val, char* cptr, void* desc); static t_stat dup_set_W3 (UNIT* uptr, int32 val, char* cptr, void* desc);
static t_stat dup_show_W3 (FILE* st, UNIT* uptr, int32 val, void* desc); static t_stat dup_show_W3 (FILE* st, UNIT* uptr, int32 val, void* desc);
static t_stat dup_set_W5 (UNIT* uptr, int32 val, char* cptr, void* desc); static t_stat dup_set_W5 (UNIT* uptr, int32 val, char* cptr, void* desc);
@ -364,6 +367,7 @@ static REG dup_reg[] = {
{ BRDATAD (TPDELAY,dup_xmtpkdelaying, DEV_RDX, 16, DUP_LINES, "transmit packet completion delay") }, { BRDATAD (TPDELAY,dup_xmtpkdelaying, DEV_RDX, 16, DUP_LINES, "transmit packet completion delay") },
{ BRDATAD (TPSTART, dup_xmtpkstart, DEV_RDX, 32, DUP_LINES, "transmit digest packet start time") }, { BRDATAD (TPSTART, dup_xmtpkstart, DEV_RDX, 32, DUP_LINES, "transmit digest packet start time") },
{ BRDATAD (RPINOFF, dup_rcvpkinoff, DEV_RDX, 16, DUP_LINES, "receive digest packet offset") }, { BRDATAD (RPINOFF, dup_rcvpkinoff, DEV_RDX, 16, DUP_LINES, "receive digest packet offset") },
{ BRDATAD (CORRUPT, dup_corruption, DEV_RDX, 32, DUP_LINES, "data corruption factor (0.1%)") },
{ NULL } { NULL }
}; };
@ -373,6 +377,8 @@ static TMXR dup_desc = { INITIAL_DUP_LINES, 0, 0, NULL }; /* mux descriptor
static MTAB dup_mod[] = { static MTAB dup_mod[] = {
{ MTAB_XTD|MTAB_VUN, 0, "SPEED", "SPEED=bits/sec (0=unrestricted)" , { MTAB_XTD|MTAB_VUN, 0, "SPEED", "SPEED=bits/sec (0=unrestricted)" ,
&dup_setspeed, &dup_showspeed, NULL, "Display rate limit" }, &dup_setspeed, &dup_showspeed, NULL, "Display rate limit" },
{ MTAB_XTD|MTAB_VUN, 0, "CORRUPTION", "CORRUPTION=factor (0=uncorrupted)" ,
&dup_setcorrupt, &dup_showcorrupt, NULL, "Display corruption factor (0.1% of packets)" },
{ MTAB_XTD|MTAB_VUN, 1, "W3", NULL , { MTAB_XTD|MTAB_VUN, 1, "W3", NULL ,
NULL, &dup_show_W3, NULL, "Display Reset Option" }, NULL, &dup_show_W3, NULL, "Display Reset Option" },
{ MTAB_XTD|MTAB_VUN, 1, NULL, "W3" , { MTAB_XTD|MTAB_VUN, 1, NULL, "W3" ,
@ -910,7 +916,7 @@ if (!tmxr_tpbusyln(&dup_ldsc[dup])) { /* Not Busy sending? */
dup_xmtpacket[dup][dup_xmtpkoffset[dup]++] = crc16 >> 8; dup_xmtpacket[dup][dup_xmtpkoffset[dup]++] = crc16 >> 8;
if ((dup_xmtpkoffset[dup] > 8) || (dup_xmtpacket[dup][0] == DDCMP_ENQ)) { if ((dup_xmtpkoffset[dup] > 8) || (dup_xmtpacket[dup][0] == DDCMP_ENQ)) {
dup_xmtpkbytes[dup] = dup_xmtpkoffset[dup]; dup_xmtpkbytes[dup] = dup_xmtpkoffset[dup];
ddcmp_tmxr_put_packet_ln (&dup_ldsc[dup], dup_xmtpacket[dup], dup_xmtpkbytes[dup]); ddcmp_tmxr_put_packet_ln (&dup_ldsc[dup], dup_xmtpacket[dup], dup_xmtpkbytes[dup], dup_corruption[dup]);
} }
} }
breturn = TRUE; breturn = TRUE;
@ -1057,7 +1063,7 @@ for (dup=active=attached=0; dup < dup_desc.lines; dup++) {
uint16 size; uint16 size;
if (dup_parcsr[dup] & PARCSR_M_DECMODE) if (dup_parcsr[dup] & PARCSR_M_DECMODE)
ddcmp_tmxr_get_packet_ln (lp, &buf, &size); ddcmp_tmxr_get_packet_ln (lp, &buf, &size, dup_corruption[dup]);
else { else {
size_t size_t_size; size_t size_t_size;
@ -1318,6 +1324,36 @@ dup_speed[dup] = newspeed;
return SCPE_OK; return SCPE_OK;
} }
/* SET/SHOW CORRUPTION processor */
static t_stat dup_showcorrupt (FILE* st, UNIT* uptr, int32 val, 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, 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 */ /* SET/SHOW W3 processor */
static t_stat dup_show_W3 (FILE* st, UNIT* uptr, int32 val, void* desc) static t_stat dup_show_W3 (FILE* st, UNIT* uptr, int32 val, void* desc)
@ -1421,38 +1457,191 @@ return dup_reset (dptr); /* setup lines and auto conf
static t_stat dup_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) static 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); const char helpString[] =
fprintf (st, "The %s connects two systems to provide a network connection.\n", dptr->name); /* The '*'s in the next line represent the standard text width of a help line */
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"); " The %D11 is a single-line, program controlled, double buffered\n"
fprintf (st, " sim> SET %s LINES=n\n\n", dptr->name); " communications device designed to interface the %1s system to a\n"
fprintf (st, "If you want to experience the actual data rates of the physical hardware you\n"); " serial synchronous line. The original hardware is capable of handling\n"
fprintf (st, "can set the bit rate of the simulated line can be set using the following\n"); " a wide variety of protocols, including byte oriented protocols, such\n"
fprintf (st, "command:\n\n"); " as DDCMP and BISYNC and bit-oriented protocols such as SDLC, HDLC\n"
fprintf (st, " sim> SET %sn SPEED=bps\n\n", dptr->name); " and ADCCP. The emulated device currently only supports connections\n"
fprintf (st, "Where bps is the number of data bits per second that the simulated line runs\n"); " using the DDCMP protocol.\n\n"
fprintf (st, "at. Use a value of zero to run at full speed with no artificial\n"); " The %D11 is ideally suited for interfacing the %1s system\n"
fprintf (st, "throttling.\n\n"); " to medium-speed synchronous lines for remote batch, remote data\n"
fprint_set_help (st, dptr); " collection, remote concentration and network applications. Multiple\n"
fprint_show_help (st, dptr); " %D11's on a %1s allow its use in applications requiring several\n"
fprint_reg_help (st, dptr); " synchronous lines.\n\n"
return SCPE_OK; " 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 unit\n"
" 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"
"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"
"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* Unibus PDP11 simulators\n"
"++DPV11* Qbus PDP11 simulators\n"
"++KDP11* Unibus PDP11 simulators and PDP10 simulators\n"
"++DMR11 Unibus PDP11 simulators and Unibus VAX simulators\n"
"++DMC11 Unibus PDP11 simulators and Unibus VAX simulators\n"
"++DMP11 Unibus PDP11 simulators and Unibus VAX simulators\n"
"++DMV11 Qbus VAX simulators\n"
"\n"
"++* Indicates systems which have OS provided DDCMP implementations.\n"
;
char busname[16];
char devcount[16];
char connectpoll[16];
sprintf (busname, UNIBUS ? "Unibus" : "Qbus");
sprintf (devcount, "%d", DUP_LINES);
sprintf (connectpoll, "%d", DUP_CONNECT_POLL);
return scp_help (st, dptr, uptr, flag, helpString, cptr, busname, devcount, connectpoll);
} }
static t_stat dup_help_attach (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) static 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"); return dup_help (st, dptr, uptr, flag, DUP_HLP_ATTACH);
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{,UDP},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");
fprintf (st, "A symmetric attach configuration is required when using UDP packet transport.\n\n");
return SCPE_OK;
} }
static char *dup_description (DEVICE *dptr) static char *dup_description (DEVICE *dptr)

View file

@ -93,14 +93,6 @@
#define KMC_DIS 0 #define KMC_DIS 0
#endif #endif
/* Data troll facility
*
* Enables data troll, which simulates imperfect links.
*/
#ifndef KMC_TROLL
#define KMC_TROLL 1
#endif
/* Define DUP_RXRESYNC to disable/enable RCVEN at the expected times. /* Define DUP_RXRESYNC to disable/enable RCVEN at the expected times.
* This is used to resynch the receiver, but I'm not sure if it would * This is used to resynch the receiver, but I'm not sure if it would
* cause the emulated DUP to lose data...especially with QSYNC * cause the emulated DUP to lose data...especially with QSYNC
@ -479,10 +471,6 @@ static QH kmc_freecqHead[KMC_UNITS];
#define freecqHead kmc_freecqHead[k] #define freecqHead kmc_freecqHead[k]
static int32 kmc_freecqCount[KMC_UNITS]; static int32 kmc_freecqCount[KMC_UNITS];
#define freecqCount kmc_freecqCount[k] #define freecqCount kmc_freecqCount[k]
#if KMC_TROLL
static int32 kmc_trollHungerLevel[KMC_UNITS];
#define trollHungerLevel kmc_trollHungerLevel[k]
#endif
/* *** End of per-KMC state *** */ /* *** End of per-KMC state *** */
@ -502,10 +490,6 @@ static t_stat kmc_showDeviceCount (FILE *st, UNIT *txup, int32 val, void *desc);
#endif #endif
static t_stat kmc_setLineSpeed (UNIT *txup, int32 val, char *cptr, void *desc); static t_stat kmc_setLineSpeed (UNIT *txup, int32 val, char *cptr, void *desc);
static t_stat kmc_showLineSpeed (FILE *st, UNIT *txup, int32 val, void *desc); static t_stat kmc_showLineSpeed (FILE *st, UNIT *txup, int32 val, void *desc);
#if KMC_TROLL
static t_stat kmc_setTrollHunger (UNIT *txup, int32 val, char *cptr, void *desc);
static t_stat kmc_showTrollHunger (FILE *st, UNIT *txup, int32 val, void *desc);
#endif
static t_stat kmc_showStatus (FILE *st, UNIT *up, int32 v, void *dp); static t_stat kmc_showStatus (FILE *st, UNIT *up, int32 v, void *dp);
static t_stat kmc_help (FILE *st, struct sim_device *dptr, static t_stat kmc_help (FILE *st, struct sim_device *dptr,
@ -603,10 +587,6 @@ MTAB kmc_mod[] = {
#if KMC_UNITS > 1 #if KMC_UNITS > 1
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "DEVICES", "DEVICES=n", { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "DEVICES", "DEVICES=n",
&kmc_setDeviceCount, &kmc_showDeviceCount, NULL, "Display number of KMC devices enabled" }, &kmc_setDeviceCount, &kmc_showDeviceCount, NULL, "Display number of KMC devices enabled" },
#endif
#if KMC_TROLL
{ MTAB_XTD|MTAB_VUN|MTAB_VALR|MTAB_NMO, 0, "TROLL", "TROLL=appetite",
&kmc_setTrollHunger, &kmc_showTrollHunger, NULL, "Appetite in milligulps" },
#endif #endif
{ 0 }, { 0 },
}; };
@ -689,9 +669,6 @@ static int32 kmc_BintAck (void);
static t_bool kmc_printBufferIn (int32 k, DEVICE *dev, int32 line, t_bool rx, static t_bool kmc_printBufferIn (int32 k, DEVICE *dev, int32 line, t_bool rx,
int32 count, int32 ba, uint16 sel6v); int32 count, int32 ba, uint16 sel6v);
static t_bool kmc_printBDL(int32 k, uint32 dbits, DEVICE *dev, uint8 line, int32 ba, int prbuf); static t_bool kmc_printBDL(int32 k, uint32 dbits, DEVICE *dev, uint8 line, int32 ba, int prbuf);
#if KMC_TROLL
static t_bool kmc_feedTroll (int32 k, int32 line, uint8 *msg, t_bool rx);
#endif
/* Environment */ /* Environment */
static const char *kmc_verifyUcode (int32 k); static const char *kmc_verifyUcode (int32 k);
@ -721,9 +698,6 @@ static t_stat kmc_reset(DEVICE* dptr) {
d->dupidx = -1; d->dupidx = -1;
d->linespeed = DFLT_SPEED; d->linespeed = DFLT_SPEED;
} }
for (k = 0; ((uint32)k) < kmc_dev.numunits; k++) {
trollHungerLevel = 0;
}
} }
for (k = 0; ((uint32)k) < kmc_dev.numunits; k++) { for (k = 0; ((uint32)k) < kmc_dev.numunits; k++) {
@ -1189,14 +1163,6 @@ static t_stat kmc_txService (UNIT *txup) {
*/ */
case TXMRDY: /* Data with OS-embedded HCRC */ case TXMRDY: /* Data with OS-embedded HCRC */
d->txstate = TXACT; d->txstate = TXACT;
#if KMC_TROLL
if (trollHungerLevel) { /* Troll in the house? */
if (kmc_feedTroll (k, d->line, d->txmsg + d->txslen, TRUE)) {
kmc_txComplete (d->dupidx, 0);
TXDELAY (TXDONE, TXDONE_DELAY);
}
}
#endif
assert (d->txmsg[d->txslen + 0] != DDCMP_ENQ); assert (d->txmsg[d->txslen + 0] != DDCMP_ENQ);
assert (((d->txmlen - d->txslen) > 8) && /* Data, length should match count */ assert (((d->txmlen - d->txslen) > 8) && /* Data, length should match count */
(((size_t)(((d->txmsg[d->txslen + 2] & 077) << 8) | d->txmsg[d->txslen + 1])) == (((size_t)(((d->txmsg[d->txslen + 2] & 077) << 8) | d->txmsg[d->txslen + 1])) ==
@ -1210,14 +1176,6 @@ static t_stat kmc_txService (UNIT *txup) {
case TXRDY: /* Control or DATA with KDP-CRCH */ case TXRDY: /* Control or DATA with KDP-CRCH */
d->txstate = TXACT; /* Note that DUP can complete before returning */ d->txstate = TXACT; /* Note that DUP can complete before returning */
#if KMC_TROLL
if (trollHungerLevel) { /* Troll in the house? */
if (kmc_feedTroll (k, d->line, d->txmsg + d->txslen, TRUE)) {
kmc_txComplete (d->dupidx, 0);
TXDELAY (TXDONE, TXDONE_DELAY);
}
}
#endif
if (d->txmsg[d->txslen + 0] == DDCMP_ENQ) { /* Control message */ if (d->txmsg[d->txslen + 0] == DDCMP_ENQ) { /* Control message */
assert ((d->txmlen - d->txslen) == 6); assert ((d->txmlen - d->txslen) == 6);
if (!dup_put_msg_bytes (d->dupidx, d->txmsg, d->txslen + 6, TRUE, TRUE)) { if (!dup_put_msg_bytes (d->dupidx, d->txmsg, d->txslen + 6, TRUE, TRUE)) {
@ -1366,14 +1324,6 @@ static t_stat kmc_rxService (UNIT *rxup) {
break; break;
} }
#if KMC_TROLL
if (trollHungerLevel) { /* Troll in the house? */
if (kmc_feedTroll (k, d->line, d->rxmsg, TRUE)) {
break;
}
}
#endif
d->rxstate = RXBDL; d->rxstate = RXBDL;
d->rxused = 0; d->rxused = 0;
@ -2694,73 +2644,6 @@ static t_bool kmc_printBDL(int32 k, uint32 dbits, DEVICE *dev, uint8 line, int32
return TRUE; return TRUE;
} }
/* Evaluate the troll's appetite.
*
* A message can be eaten (dropped), nibbled (corrupted) or spared.
*
* The probability of a message not being spared is trollHungerLevel,
* expressed in milli-gulps - 0.1%. The troll selects which action
* to taken on selected messages with equal probability.
*
* Nibbled messages' CRCs are changed when possible to simplify
* identifying them when debugging. When it's too much work to
* find the CRC, the first byte of data is changed. The change
* is an XOR to make it possible to reconstruct the original data.
*
* A particularly unfortunate message can be nibbled by both
* the transmitter and receiver; thus the troll applies a direction-
* dependent pattern.
*
* Return TRUE if the troll ate the message.
* Return FALSE if the message was nibbled or spared.
*/
#if KMC_TROLL
static t_bool kmc_feedTroll (int32 k, int32 line, uint8 *msg, t_bool rx) {
#if defined(_POSIX_VERSION) || defined (_XOPEN_VERSION)
double r = (double)random();
double rmax = (double)0x7fffffff;
#else
double r = rand();
double rmax = (double)RAND_MAX;
#endif
if (msg[0] == DDCMP_ENQ) {
int eat = 0 + (int) (2000.0 * (r / rmax));
if (eat <= (trollHungerLevel * 2)) { /* Hungry? */
if (eat <= trollHungerLevel) { /* Eat the packet */
sim_debug (DF_PKT, &kmc_dev, "KMC%u line %u: troll ate a %s control message\n",
k, line, (rx? "rx" : "tx"));
return TRUE;
}
sim_debug (DF_PKT, &kmc_dev, "KMC%u line %u: troll bit %d control CRC\n",
k, line, (rx? "rx" : "tx"));
msg[6] ^= rx? 0114: 0154; /* Eat the CRC */
}
} else {
int eat = 0 + (int) (3000.0 * (r / rmax));
if (eat <= (trollHungerLevel * 3)) { /* Hungry? */
if (eat <= trollHungerLevel) { /* Eat the packet */
sim_debug (DF_PKT, &kmc_dev, "KMC%u line %u: troll ate a %s %s message\n",
k, line, (rx? "rx" : "tx"), ((msg[0] == DDCMP_SOH)? "data" : "maintenance"));
return TRUE;
}
if (eat <= (trollHungerLevel * 2)) { /* HCRC */
sim_debug (DF_PKT, &kmc_dev, "KMC%u line %u: troll bit %s %s HCRC\n",
k, line, (rx? "rx" : "tx"), ((msg[0] == DDCMP_SOH)? "data" : "maintenance"));
msg[6] ^= rx? 0124: 0164;
} else { /* DCRC */
sim_debug (DF_PKT, &kmc_dev, "KMC%u line %u: troll bit %s %s DCRC\n",
k, line, (rx? "rx" : "tx"), ((msg[0] == DDCMP_SOH)? "data" : "maintenance"));
msg[8] ^= rx? 0114: 0154; /* Rather than find the CRC, the first data byte will do */
}
}
}
return FALSE;
}
#endif
/* Verify that the microcode image is one that this code knows how to emulate. /* Verify that the microcode image is one that this code knows how to emulate.
* As far as I know, there was COMM IOP-DUP V1.0 and one patch, V1.0A. * As far as I know, there was COMM IOP-DUP V1.0 and one patch, V1.0A.
* This is the patched version, which I have verified by rebuilding the * This is the patched version, which I have verified by rebuilding the
@ -2991,45 +2874,6 @@ static t_stat kmc_showLineSpeed (FILE *st, UNIT *txup, int32 val, void *desc) {
return SCPE_OK; return SCPE_OK;
} }
#if KMC_TROLL
/* Manage the troll's appetite, in units of milli-gulps.
*
* See kmc_feedTroll for usage.
*/
static t_stat kmc_setTrollHunger (UNIT *txup, int32 val, char *cptr, void *desc) {
int32 k = txup->unit_kmc;
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;
}
trollHungerLevel = appetite;
return SCPE_OK;
}
/* Display the troll's appetite */
static t_stat kmc_showTrollHunger (FILE *st, UNIT *txup, int32 val, void *desc) {
int32 k = txup->unit_kmc;
if (trollHungerLevel) {
fprintf (st, "Troll's share: %u milligulps (%.1f%% of messages processed)\n",
trollHungerLevel, ((double)trollHungerLevel)/10.0);
} else {
fprintf (st, "Troll is not at the table\n");
}
return SCPE_OK;
}
#endif
/* Show KMC status */ /* Show KMC status */
t_stat kmc_showStatus (FILE *st, UNIT *up, int32 v, void *dp) { t_stat kmc_showStatus (FILE *st, UNIT *up, int32 v, void *dp) {

View file

@ -245,6 +245,7 @@ t_stat tmxr_stop_poll (void);
void _tmxr_debug (uint32 dbits, TMLN *lp, const char *msg, char *buf, int bufsize); void _tmxr_debug (uint32 dbits, TMLN *lp, const char *msg, char *buf, int bufsize);
extern FILE *sim_deb; /* debug file */ extern FILE *sim_deb; /* debug file */
#define tmxr_debug(dbits, lp, msg, buf, bufsize) if (sim_deb && (lp)->mp && (lp)->mp->dptr && ((dbits) & (lp)->mp->dptr->dctrl)) _tmxr_debug (dbits, lp, msg, buf, bufsize); else (void)0 #define tmxr_debug(dbits, lp, msg, buf, bufsize) if (sim_deb && (lp)->mp && (lp)->mp->dptr && ((dbits) & (lp)->mp->dptr->dctrl)) _tmxr_debug (dbits, lp, msg, buf, bufsize); else (void)0
#define tmxr_debug_msg(dbits, lp, msg) if (sim_deb && (lp)->mp && (lp)->mp->dptr && ((dbits) & (lp)->mp->dptr->dctrl)) sim_debug (dbits, (lp)->mp->dptr, msg); else (void)0
#define tmxr_debug_return(lp, val) if (sim_deb && (val) && (lp)->mp && (lp)->mp->dptr && (TMXR_DBG_RET & (lp)->mp->dptr->dctrl)) sim_debug (TMXR_DBG_RET, (lp)->mp->dptr, "Ln%d: 0x%x\n", (int)((lp)-(lp)->mp->ldsc), val); else (void)0 #define tmxr_debug_return(lp, val) if (sim_deb && (val) && (lp)->mp && (lp)->mp->dptr && (TMXR_DBG_RET & (lp)->mp->dptr->dctrl)) sim_debug (TMXR_DBG_RET, (lp)->mp->dptr, "Ln%d: 0x%x\n", (int)((lp)-(lp)->mp->ldsc), val); else (void)0
#define tmxr_debug_trace(mp, msg) if (sim_deb && (mp)->dptr && (TMXR_DBG_TRC & (mp)->dptr->dctrl)) sim_debug (TMXR_DBG_TRC, mp->dptr, "%s\n", (msg)); else (void)0 #define tmxr_debug_trace(mp, msg) if (sim_deb && (mp)->dptr && (TMXR_DBG_TRC & (mp)->dptr->dctrl)) sim_debug (TMXR_DBG_TRC, mp->dptr, "%s\n", (msg)); else (void)0
#define tmxr_debug_trace_line(lp, msg) if (sim_deb && (lp)->mp && (lp)->mp->dptr && (TMXR_DBG_TRC & (lp)->mp->dptr->dctrl)) sim_debug (TMXR_DBG_TRC, (lp)->mp->dptr, "Ln%d:%s\n", (int)((lp)-(lp)->mp->ldsc), (msg)); else (void)0 #define tmxr_debug_trace_line(lp, msg) if (sim_deb && (lp)->mp && (lp)->mp->dptr && (TMXR_DBG_TRC & (lp)->mp->dptr->dctrl)) sim_debug (TMXR_DBG_TRC, (lp)->mp->dptr, "Ln%d:%s\n", (int)((lp)-(lp)->mp->ldsc), (msg)); else (void)0