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:
parent
d7690ce060
commit
b1fb809210
5 changed files with 396 additions and 212 deletions
|
@ -173,6 +173,81 @@ if (sim_deb && dptr && (reason & dptr->dctrl)) {
|
|||
|
||||
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
|
||||
|
||||
Inputs:
|
||||
|
@ -190,7 +265,7 @@ uint16 ddcmp_crc16(uint16 crc, const void* vbuf, size_t len);
|
|||
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;
|
||||
size_t payloadsize;
|
||||
|
@ -230,6 +305,8 @@ while (TMXR_VALID & (c = tmxr_getc_ln (lp))) {
|
|||
else
|
||||
strcpy (msg, "<<< RCV Packet");
|
||||
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;
|
||||
}
|
||||
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");
|
||||
ddcmp_packet_trace (DDCMP_DBG_PRCV, lp->mp->dptr, msg, lp->rxpb, *psize);
|
||||
lp->rxpboffset = 0;
|
||||
if (ddcmp_feedCorruptionTroll (lp, lp->rxpb, TRUE, corruptrate))
|
||||
break;
|
||||
return SCPE_OK;
|
||||
}
|
||||
}
|
||||
|
@ -270,7 +349,7 @@ return SCPE_LOST;
|
|||
2. If prior packet transmission still in progress, SCPE_STALL is
|
||||
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;
|
||||
char msg[32];
|
||||
|
@ -293,15 +372,17 @@ if (lp->mp->lines > 1)
|
|||
else
|
||||
strcpy (msg, ">>> XMT Packet");
|
||||
ddcmp_packet_trace (DDCMP_DBG_PXMT, lp->mp->dptr, msg, lp->txpb, lp->txppsize);
|
||||
if (!ddcmp_feedCorruptionTroll (lp, lp->txpb, FALSE, corruptrate)) {
|
||||
++lp->txpcnt;
|
||||
while ((lp->txppoffset < lp->txppsize) &&
|
||||
(SCPE_OK == (r = tmxr_putc_ln (lp, lp->txpb[lp->txppoffset]))))
|
||||
++lp->txppoffset;
|
||||
tmxr_send_buffered_data (lp);
|
||||
}
|
||||
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);
|
||||
|
||||
|
@ -312,7 +393,7 @@ if (size > DDCMP_HEADER_SIZE) {
|
|||
buf[size-DDCMP_CRC_SIZE] = data_crc16 & 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)
|
||||
|
@ -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)
|
||||
{
|
||||
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)
|
||||
|
@ -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)
|
||||
{
|
||||
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)
|
||||
|
@ -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)
|
||||
{
|
||||
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)
|
||||
|
@ -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)
|
||||
{
|
||||
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)
|
||||
|
@ -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)
|
||||
{
|
||||
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_ */
|
||||
|
|
|
@ -517,6 +517,7 @@ typedef struct dmc_controller {
|
|||
uint32 *baseaddr;
|
||||
uint16 *basesize;
|
||||
uint8 *modem;
|
||||
int32 *corruption_factor;
|
||||
uint32 buffers_received_from_net;
|
||||
uint32 buffers_transmitted_to_net;
|
||||
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_setspeed (UNIT* uptr, int32 val, char* cptr, 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_show_microdiag (FILE* st, UNIT* uptr, int32 val, 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];
|
||||
uint8 dmc_modem[DMC_NUMDEVICE];
|
||||
t_bool dmc_microdiag[DMC_NUMDEVICE];
|
||||
int32 dmc_corruption[DMC_NUMDEVICE];
|
||||
|
||||
CSRS dmp_csrs[DMP_NUMDEVICE];
|
||||
uint16 dmp_sel0[DMC_NUMDEVICE];
|
||||
|
@ -982,6 +986,7 @@ char dmp_port[DMP_NUMDEVICE][CBUFSIZE];
|
|||
uint32 dmp_baseaddr[DMP_NUMDEVICE];
|
||||
uint16 dmp_basesize[DMP_NUMDEVICE];
|
||||
uint8 dmp_modem[DMP_NUMDEVICE];
|
||||
int32 dmp_corruption[DMC_NUMDEVICE];
|
||||
|
||||
TMLN dmc_ldsc[DMC_NUMDEVICE]; /* line descriptors */
|
||||
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 (SEL6, dmc_sel6, DEV_RDX, 16, DMC_NUMDEVICE, "Select 6 CSR") },
|
||||
{ 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 (PEER, dmc_peer, DEV_RDX, 8, DMC_NUMDEVICE*CBUFSIZE, "peer address: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 (SEL10, dmp_sel10, DEV_RDX, 16, DMP_NUMDEVICE, "Select 10 CSR") },
|
||||
{ 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 (PORT, dmp_port, DEV_RDX, 8, DMC_NUMDEVICE*CBUFSIZE, "listen port") },
|
||||
{ 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" },
|
||||
{ MTAB_XTD|MTAB_VDV, 0, "CONNECTPOLL", "CONNECTPOLL=seconds",
|
||||
&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",
|
||||
&set_addr, &show_addr, NULL, "Bus address" },
|
||||
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 1, "VECTOR", "VECTOR",
|
||||
|
@ -1087,6 +1096,8 @@ MTAB dmp_mod[] = {
|
|||
NULL, &dmc_showddcmp, NULL, "Display DDCMP state information" },
|
||||
{ MTAB_XTD|MTAB_VDV, 0, "CONNECTPOLL", "CONNECTPOLL=seconds",
|
||||
&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",
|
||||
&set_addr, &show_addr, NULL, "Bus address" },
|
||||
{ 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;
|
||||
}
|
||||
|
||||
/* 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)
|
||||
{
|
||||
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"
|
||||
" specified with command:\n"
|
||||
"\n"
|
||||
"+sim> SET %U LINES=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"
|
||||
|
@ -1604,7 +1656,18 @@ const char helpString[] =
|
|||
" A SET TYPE command should be entered before the device is attached to a\n"
|
||||
" listening port.\n"
|
||||
#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"
|
||||
" The device must be attached to a receive port, use the ATTACH command\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"
|
||||
" 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\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"
|
||||
|
@ -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)
|
||||
{
|
||||
const char helpString[] =
|
||||
" 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;
|
||||
return dmc_help (st, dptr, uptr, flag, DMC_HLP_ATTACH);
|
||||
}
|
||||
|
||||
void dmc_setinint(CTLR *controller)
|
||||
|
@ -2529,7 +2594,7 @@ if (controller->state == Running) {
|
|||
BUFFER *buffer = dmc_buffer_queue_head(controller->rcv_queue);
|
||||
|
||||
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)
|
||||
break;
|
||||
ans = TRUE;
|
||||
|
@ -3292,7 +3357,7 @@ while (buffer) {
|
|||
if (buffer->transfer_buffer[0] == 0)
|
||||
return;
|
||||
/* 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) {
|
||||
controller->link.xmt_buffer = buffer;
|
||||
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->basesize = &dmc_basesize[i];
|
||||
controller->modem = &dmc_modem[i];
|
||||
controller->corruption_factor = &dmc_corruption[i];
|
||||
controller->unit = &controller->device->units[i];
|
||||
controller->unit->ctlr = (void *)controller;
|
||||
controller->index = i;
|
||||
|
@ -3519,6 +3585,7 @@ for (i=0; i < DMP_NUMDEVICE; i++) {
|
|||
controller->baseaddr = &dmp_baseaddr[i];
|
||||
controller->basesize = &dmp_basesize[i];
|
||||
controller->modem = &dmp_modem[i];
|
||||
controller->corruption_factor = &dmp_corruption[i];
|
||||
controller->unit = &controller->device->units[i];
|
||||
controller->unit->ctlr = (void *)controller;
|
||||
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;
|
||||
controller->unit->ctlr = (void *)controller;
|
||||
dmc_microdiag[i] = TRUE;
|
||||
dmc_corruption[i] = 0;
|
||||
}
|
||||
tmxr_set_modem_control_passthru (&dmc_desc); /* We always want Modem Control */
|
||||
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;
|
||||
dmp_dev.units[i] = dmc_unit_template;
|
||||
controller->unit->ctlr = (void *)controller;
|
||||
dmp_corruption[i] = 0;
|
||||
}
|
||||
tmxr_set_modem_control_passthru (&dmp_desc); /* We always want Modem Control */
|
||||
dmp_units[dmp_dev.numunits-2] = dmc_poll_unit_template;
|
||||
|
|
|
@ -87,6 +87,7 @@ static uint16 dup_xmtpkoffset[DUP_LINES]; /* xmt buffer off
|
|||
static uint32 dup_xmtpkstart[DUP_LINES]; /* xmt packet start time */
|
||||
static uint16 dup_xmtpkbytes[DUP_LINES]; /* xmt packet size of packet */
|
||||
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_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_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_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_show_W3 (FILE* st, UNIT* uptr, int32 val, 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 (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 (CORRUPT, dup_corruption, DEV_RDX, 32, DUP_LINES, "data corruption factor (0.1%)") },
|
||||
{ NULL }
|
||||
};
|
||||
|
||||
|
@ -373,6 +377,8 @@ static TMXR dup_desc = { INITIAL_DUP_LINES, 0, 0, NULL }; /* mux descriptor
|
|||
static MTAB dup_mod[] = {
|
||||
{ MTAB_XTD|MTAB_VUN, 0, "SPEED", "SPEED=bits/sec (0=unrestricted)" ,
|
||||
&dup_setspeed, &dup_showspeed, NULL, "Display rate limit" },
|
||||
{ MTAB_XTD|MTAB_VUN, 0, "CORRUPTION", "CORRUPTION=factor (0=uncorrupted)" ,
|
||||
&dup_setcorrupt, &dup_showcorrupt, NULL, "Display corruption factor (0.1% of packets)" },
|
||||
{ MTAB_XTD|MTAB_VUN, 1, "W3", NULL ,
|
||||
NULL, &dup_show_W3, NULL, "Display Reset Option" },
|
||||
{ MTAB_XTD|MTAB_VUN, 1, NULL, "W3" ,
|
||||
|
@ -910,7 +916,7 @@ if (!tmxr_tpbusyln(&dup_ldsc[dup])) { /* Not Busy sending? */
|
|||
dup_xmtpacket[dup][dup_xmtpkoffset[dup]++] = crc16 >> 8;
|
||||
if ((dup_xmtpkoffset[dup] > 8) || (dup_xmtpacket[dup][0] == DDCMP_ENQ)) {
|
||||
dup_xmtpkbytes[dup] = dup_xmtpkoffset[dup];
|
||||
ddcmp_tmxr_put_packet_ln (&dup_ldsc[dup], dup_xmtpacket[dup], dup_xmtpkbytes[dup]);
|
||||
ddcmp_tmxr_put_packet_ln (&dup_ldsc[dup], dup_xmtpacket[dup], dup_xmtpkbytes[dup], dup_corruption[dup]);
|
||||
}
|
||||
}
|
||||
breturn = TRUE;
|
||||
|
@ -1057,7 +1063,7 @@ for (dup=active=attached=0; dup < dup_desc.lines; dup++) {
|
|||
uint16 size;
|
||||
|
||||
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 {
|
||||
size_t size_t_size;
|
||||
|
||||
|
@ -1318,6 +1324,36 @@ dup_speed[dup] = newspeed;
|
|||
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 */
|
||||
|
||||
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)
|
||||
{
|
||||
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;
|
||||
const char helpString[] =
|
||||
/* The '*'s in the next line represent the standard text width of a help line */
|
||||
/****************************************************************************/
|
||||
" The %D11 is a single-line, program controlled, double buffered\n"
|
||||
" communications device designed to interface the %1s system to a\n"
|
||||
" serial synchronous line. The original hardware is capable of handling\n"
|
||||
" a wide variety of protocols, including byte oriented protocols, such\n"
|
||||
" as DDCMP and BISYNC and bit-oriented protocols such as SDLC, HDLC\n"
|
||||
" and ADCCP. The emulated device currently only supports connections\n"
|
||||
" using the DDCMP protocol.\n\n"
|
||||
" The %D11 is ideally suited for interfacing the %1s system\n"
|
||||
" to medium-speed synchronous lines for remote batch, remote data\n"
|
||||
" collection, remote concentration and network applications. Multiple\n"
|
||||
" %D11's on a %1s allow its use in applications requiring several\n"
|
||||
" synchronous lines.\n\n"
|
||||
" The %D11 is capable of transmitting data at the maximum speed of\n"
|
||||
" 9600 baud. The emulated device can move data at significantly faster\n"
|
||||
" data rates. The maximum emulated rate is dependent on the host CPU's\n"
|
||||
" available cycles.\n"
|
||||
"1 Hardware Description\n"
|
||||
" The %1s %D11 consists of a microprocessor module and a synchronous line 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)
|
||||
{
|
||||
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{,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;
|
||||
return dup_help (st, dptr, uptr, flag, DUP_HLP_ATTACH);
|
||||
}
|
||||
|
||||
static char *dup_description (DEVICE *dptr)
|
||||
|
|
|
@ -93,14 +93,6 @@
|
|||
#define KMC_DIS 0
|
||||
#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.
|
||||
* 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
|
||||
|
@ -479,10 +471,6 @@ static QH kmc_freecqHead[KMC_UNITS];
|
|||
#define freecqHead kmc_freecqHead[k]
|
||||
static int32 kmc_freecqCount[KMC_UNITS];
|
||||
#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 *** */
|
||||
|
||||
|
@ -502,10 +490,6 @@ static t_stat kmc_showDeviceCount (FILE *st, UNIT *txup, int32 val, void *desc);
|
|||
#endif
|
||||
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);
|
||||
#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_help (FILE *st, struct sim_device *dptr,
|
||||
|
@ -603,10 +587,6 @@ MTAB kmc_mod[] = {
|
|||
#if KMC_UNITS > 1
|
||||
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "DEVICES", "DEVICES=n",
|
||||
&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
|
||||
{ 0 },
|
||||
};
|
||||
|
@ -689,9 +669,6 @@ static int32 kmc_BintAck (void);
|
|||
static t_bool kmc_printBufferIn (int32 k, DEVICE *dev, int32 line, t_bool rx,
|
||||
int32 count, int32 ba, uint16 sel6v);
|
||||
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 */
|
||||
static const char *kmc_verifyUcode (int32 k);
|
||||
|
@ -721,9 +698,6 @@ static t_stat kmc_reset(DEVICE* dptr) {
|
|||
d->dupidx = -1;
|
||||
d->linespeed = DFLT_SPEED;
|
||||
}
|
||||
for (k = 0; ((uint32)k) < kmc_dev.numunits; k++) {
|
||||
trollHungerLevel = 0;
|
||||
}
|
||||
}
|
||||
|
||||
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 */
|
||||
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->txmlen - d->txslen) > 8) && /* Data, length should match count */
|
||||
(((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 */
|
||||
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 */
|
||||
assert ((d->txmlen - d->txslen) == 6);
|
||||
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;
|
||||
}
|
||||
|
||||
#if KMC_TROLL
|
||||
if (trollHungerLevel) { /* Troll in the house? */
|
||||
if (kmc_feedTroll (k, d->line, d->rxmsg, TRUE)) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
d->rxstate = RXBDL;
|
||||
d->rxused = 0;
|
||||
|
||||
|
@ -2694,73 +2644,6 @@ static t_bool kmc_printBDL(int32 k, uint32 dbits, DEVICE *dev, uint8 line, int32
|
|||
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.
|
||||
* 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
|
||||
|
@ -2991,45 +2874,6 @@ static t_stat kmc_showLineSpeed (FILE *st, UNIT *txup, int32 val, void *desc) {
|
|||
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 */
|
||||
|
||||
t_stat kmc_showStatus (FILE *st, UNIT *up, int32 v, void *dp) {
|
||||
|
|
|
@ -245,6 +245,7 @@ t_stat tmxr_stop_poll (void);
|
|||
void _tmxr_debug (uint32 dbits, TMLN *lp, const char *msg, char *buf, int bufsize);
|
||||
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_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_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
|
||||
|
|
Loading…
Add table
Reference in a new issue