diff --git a/PDP10/pdp10_defs.h b/PDP10/pdp10_defs.h index 2fd8e414..4450232e 100644 --- a/PDP10/pdp10_defs.h +++ b/PDP10/pdp10_defs.h @@ -115,7 +115,8 @@ typedef t_int64 d10; /* PDP-10 data (36b) */ #define STOP_ILLIOC 10 /* invalid UBA num */ #define STOP_ASTOP 11 /* address stop */ #define STOP_CONSOLE 12 /* FE halt */ -#define STOP_UNKNOWN 13 /* unknown stop */ +#define STOP_IOALIGN 13 /* DMA word access to odd address */ +#define STOP_UNKNOWN 14 /* unknown stop */ #define PAGE_FAIL -1 /* page fail */ #define INTERRUPT -2 /* interrupt */ #define ABORT(x) longjmp (save_env, (x)) /* abort */ @@ -614,6 +615,10 @@ struct pdp_dib { int32 vec; /* value */ int32 (*ack[VEC_DEVMAX])(void); /* ack routines */ uint32 ulnt; /* IO length per unit */ + uint32 flags; /* Special flags */ +#define DIB_M_REGSIZE 03 /* Device register size */ +#define DIB_REG16BIT 00 +#define DIB_REG18BIT 01 }; typedef struct pdp_dib DIB; @@ -622,6 +627,8 @@ typedef struct pdp_dib DIB; #define DZ_MUXES 4 /* max # of muxes */ #define DZ_LINES 8 /* lines per mux */ +#define KMC_UNITS 1 /* max # of KMCs */ +#define INITIAL_KMCS 0 /* Number initially enabled */ #define DUP_LINES 4 /* max # of DUP11's */ #define DIB_MAX 100 /* max DIBs */ @@ -700,9 +707,10 @@ typedef struct pdp_dib DIB; #define INT_V_RP 6 /* RH11/RP,RM drives */ #define INT_V_TU 7 /* RH11/TM03/TU45 */ -#define INT_V_DMCRX 13 -#define INT_V_DMCTX 14 -#define INT_V_XU 15 /* DEUNA/DELUA */ +#define INT_V_KMCA 8 /* KMC11 */ +#define INT_V_KMCB 9 +#define INT_V_DMCRX 8 /* DMC11/DMR11 */ +#define INT_V_DMCTX 9 #define INT_V_DZRX 16 /* DZ11 */ #define INT_V_DZTX 17 #define INT_V_RY 18 /* RX211 */ @@ -715,6 +723,8 @@ typedef struct pdp_dib DIB; #define INT_RP (1u << INT_V_RP) #define INT_TU (1u << INT_V_TU) +#define INT_KMCA (1u << INT_V_KMCA) +#define INT_KMCB (1u << INT_V_KMCB) #define INT_DMCRX (1u << INT_V_DMCRX) #define INT_DMCTX (1u << INT_V_DMCTX) #define INT_XU (1u << INT_V_XU) @@ -730,8 +740,8 @@ typedef struct pdp_dib DIB; #define IPL_RP 6 /* int levels */ #define IPL_TU 6 -#define IPL_DMCRX 5 -#define IPL_DMCTX 5 +#define IPL_KMCA 5 +#define IPL_KMCB 5 #define IPL_XU 5 #define IPL_DZRX 5 #define IPL_DZTX 5 @@ -759,8 +769,6 @@ typedef struct pdp_dib DIB; #define VEC_CR 0230 #define VEC_RP 0254 #define VEC_RY 0264 -#define VEC_DZRX 0340 -#define VEC_DZTX 0344 #define VEC_LP20 0754 #define VEC_AUTO 0 /* Set by Auto Configure */ diff --git a/PDP10/pdp10_fe.c b/PDP10/pdp10_fe.c index 0097af27..fab2aa4b 100644 --- a/PDP10/pdp10_fe.c +++ b/PDP10/pdp10_fe.c @@ -265,8 +265,8 @@ if (M[FE_KEEPA] & INT64_C(0020000000000)) { /* KSRLD - "Forced" (ac } else if (M[FE_KEEPA] & INT64_C(0010000000000)) { /* KPACT */ d10 kav = M[FE_KEEPA] & INT64_C(0000000177400); /* KPALIV */ - if (kaf_unit.u3 != (uint32)kav) { - kaf_unit.u3 = (uint32)kav; + if (kaf_unit.u3 != (int32)kav) { + kaf_unit.u3 = (int32)kav; kaf_unit.u4 = 0; } else if (++kaf_unit.u4 >= 15) { diff --git a/PDP10/pdp10_ksio.c b/PDP10/pdp10_ksio.c index 554d1aab..6afcfee4 100644 --- a/PDP10/pdp10_ksio.c +++ b/PDP10/pdp10_ksio.c @@ -43,7 +43,8 @@ The KS10 uses the PDP-11 Unibus for its I/O, via adapters. While nominally four adapters are supported, in practice only 1 and 3 are implemented. The disks are placed on adapter 1, the rest of - the I/O devices on adapter 3. + the I/O devices on adapter 3. (adapter 4 IS used in some supported + configurations, but those devices haven't been emulated yet.) In theory, we should maintain completely separate Unibuses, with distinct PI systems. In practice, this simulator has so few devices @@ -71,6 +72,7 @@ #include "pdp10_defs.h" #include +#include #include "sim_sock.h" #include "sim_tmxr.h" @@ -91,8 +93,8 @@ ((op == WRITEB)? PF_BYTE: 0) | \ (TSTF (F_USR)? PF_USER: 0) | (pa); \ ABORT (PAGE_FAIL) -/* Is Unibus address mapped to host memory */ -#define HOST_MAPPED(ub,ba) ((ubmap[ub][PAG_GETVPN(((ba) & 0777777) >> 2)] & UMAP_VLD) != 0) +/* Is Unibus address mapped to -10 memory */ +#define TEN_MAPPED(ub,ba) ((ubmap[ub][PAG_GETVPN(((ba) & 0777777) >> 2)] & UMAP_VLD) != 0) /* Translate UBA number in a PA to UBA index. 1,,* -> ubmap[0], all others -> ubmap[1] */ #define ADDR2UBA(x) (iocmap[GET_IOUBA (x)]) @@ -100,7 +102,7 @@ /* Unibus adapter data */ int32 ubcs[UBANUM] = { 0 }; /* status registers */ -int32 ubmap[UBANUM][UMAP_MEMSIZE] = { 0 }; /* Unibus maps */ +int32 ubmap[UBANUM][UMAP_MEMSIZE] = {{ 0 }}; /* Unibus maps */ int32 int_req = 0; /* interrupt requests */ int32 autcon_enb = 1; /* auto configure enabled */ @@ -118,7 +120,41 @@ static const int32 ubabr76[UBANUM] = { static const int32 ubabr54[UBANUM] = { INT_UB1 & (INT_IPL5 | INT_IPL4), INT_UB3 & (INT_IPL5 | INT_IPL4) }; -static const int32 ubashf[4] = { 18, 26, 0, 8 }; + +/* Masks for Unibus quantities */ +#define M_BYTE (0xFF) +#define M_WORD (0xFFFF) +#define M_WORD18 (0777777) +#define M_LH (0777777000000) +#define M_RH (0000000777777) + +/* Bits to shift for each Unibus byte */ +#define V_BYTE0 (18) +#define V_BYTE1 (26) +#define V_BYTE2 (0) +#define V_BYTE3 (8) + +#define V_WORD0 V_BYTE0 +#define V_WORD1 V_BYTE2 + +#if 0 +static const int32 ubashf[4] = { V_BYTE0, V_BYTE1, V_BYTE2, V_BYTE3 }; +#endif + +/* Bits to preserve when writing each Unibus byte. + * This excludes the XX bits so they are cleared. + */ +#define M_BYTE0 (~INT64_C (0000377000000)) /* Clear byte 0 */ +#define M_BYTE1 (~INT64_C (0777400000000)) /* Clear byte 1 + XX */ +#define M_BYTE2 (~INT64_C (0000000000377)) /* Clear byte 2 */ +#define M_BYTE3 (~INT64_C (0000000777400)) /* Clear byte 3 + XX */ + +#define M_WORD0 (~INT64_C (0777777000000)) /* Clear word 0 + XX */ +#define M_WORD1 (~INT64_C (0000000777777)) /* Clear word 1 + XX */ + +#if 0 +static const d10 ubamask[4] = { M_BYTE0, M_BYTE1, M_BYTE2, M_BYTE3 }; +#endif extern d10 *M; /* main memory */ extern d10 *ac_cur; @@ -254,7 +290,7 @@ return ReadIO (ea); /* RDIO, IORD */ void io713 (d10 val, a10 ea) { -WriteIO (ea, val & 0177777, WRITE); /* WRIO, IOWR */ +WriteIO (ea, val, WRITE); /* WRIO, IOWR */ return; } @@ -266,7 +302,6 @@ void io714 (d10 val, a10 ea) { d10 temp; -val = val & 0177777; if (Q_ITS) /* IOWRI */ WriteIO (IO_UBA3 | ea, val, WRITE); else { @@ -285,7 +320,6 @@ void io715 (d10 val, a10 ea) { d10 temp; -val = val & 0177777; if (Q_ITS) /* IOWRQ */ WriteIO (IO_UBA1 | ea, val, WRITE); else { @@ -358,7 +392,7 @@ return GETBYTE (ea, val); void io723 (d10 val, a10 ea) { -WriteIO (ea, val & 0377, WRITEB); /* WRIOB, IOWRB */ +WriteIO (ea, val & M_BYTE, WRITEB); /* WRIOB, IOWRB */ return; } @@ -370,7 +404,7 @@ void io724 (d10 val, a10 ea) { d10 temp; -val = val & 0377; +val = val & M_BYTE; if (Q_ITS) /* IOWRBI */ WriteIO (IO_UBA3 | ea, val, WRITEB); else { @@ -390,7 +424,7 @@ void io725 (d10 val, a10 ea) { d10 temp; -val = val & 0377; +val = val & M_BYTE; if (Q_ITS) /* IOWRBQ */ WriteIO (IO_UBA1 | ea, val, WRITEB); else { @@ -407,6 +441,13 @@ return; simulator and the 32b world of the device simulators. */ +/* UBReadIO and UBWriteIO handle the device lookup and access + * These are used for all IO space accesses. They return status. + * + * ReadIO and WriteIO are used by the CPU instructions, and generate + * UBA NXM page fails for unassigned IO addresses. + */ + static t_stat UBReadIO (int32 *data, int32 ba, int32 access) { uint32 pa = (uint32) ba; @@ -445,6 +486,9 @@ DIB *dibp; for (i = 0; (dibp = dib_tab[i]); i++ ) { if ((pa >= dibp->ba) && (pa < (dibp->ba + dibp->lnt))) { + if ((dibp->flags & DIB_M_REGSIZE) == DIB_REG16BIT) { + data &= M_WORD; + } dibp->wr (data, ba, access); pi_eval (); return SCPE_OK; @@ -465,10 +509,10 @@ UBNXM_FAIL (pa, mode); /* Mapped read and write routines - used by standard Unibus devices on Unibus 1 * I/O space accesses will work. Note that Unibus addresses with bit 17 set can - * not be mapped by the UBA, so I/O space (and more) can not be mapped to host memory. + * not be mapped by the UBA, so I/O space (and more) can not be mapped to -10 memory. */ -a10 Map_Addr10 (a10 ba, int32 ub, int32 *ubmp) +static a10 Map_Addr10 (a10 ba, int32 ub, int32 *ubmp) { a10 pa10; int32 vpn = PAG_GETVPN (ba >> 2); /* get PDP-10 page number */ @@ -489,52 +533,215 @@ pa10 = (ubm + PAG_GETOFF (ba >> 2)) & PAMASK; return pa10; } +/* Routines for Bytes, Words (16-bit) and Words (18-bit). + * + * Note that the byte count argument is always BYTES, even if + * the unit transfered is a word. This is for compatibility with + * the 11/VAX system Unibus; these routines abstract DMA for all + * U/Q device simulations. + * + * All return the number of bytes NOT transferred; 0 means success. + * A non-zero return implies a NXM was encountered. + * + * Unaligned accesses to 16/18-bit words in IOSPACE are a STOP condition. + * (Should be in memory too, but some devices are lazy.) + * + * Unibus memory is mapped into 36-bit words so that 16-bit + * values appear in 18-bit half-words, and PDP10 byte pointers will + * increment through 16-bit (but not 8-bit) data. Viewed as bytes or + * words from the PDP10, memory looks like this: + * + * +-----+-----------+------------+-------+------------+------------+ + * | 0 1 | 2 9 | 10 17 | 18 19 | 20 27| 28 35 | PDP10 bits + * +-----+-----------+------------+-------+------------+------------+ + * | X X | BYTE 1<01>| BYTE 0<00> | X X | BYTE 3<11> | BYTE 2<10> | PDP11 bytes + * +-----+-----------+------------+-------+------------+------------+ + * | X X | WORD 0 <00> | X X | WORD 1 <10> | PDP11 words + * +-----+-----------+------------+-------+------------+------------+ + * + * are the values of the two low-order address bits as viewed on + * the Unibus. + * + * The bits marked XX are written as zero for 8 and 16 bit transfers + * and with data from the Unibus parity lines for 18 bit transfers. + * In a -10 read-modify-write cycle, they are cleared if the high byte + * of the adjacent word is written, and preserved otherwise. + * + * Unibus addressing does not change with 18-bit transfers; they are + * accounted for as 2 bytes. <0:1> are bits <17:16> of word 0; + * <18:19> are bits <17:16> of word 1. + * + * Normal writes assume that DMA will access sequential Unibus addresses. + * The UBA optimizes this by writing NPR data to <00> addresses + * without preserving the rest of the -10 word. This allows a memory + * write cycle, rather than the read-modify-write cycle required to + * preserve the rest of the word. The 'read reverse' bit in the UBA + * map forces a read-modify-write on all addresses. + * + * 16-bit transfers (the d18 bit in the map selects) write 0s into + * the correspnding X bits when <00> or <10> are written. + * + * Address mapping uses bits <1:0> of the Unibus address to select + * the byte as indicated above. Bits <10:2> are the offset within + * the PDP10 page; thus Unibus addressing assumes 4 bytes/PDP10 word. + * + * 9 bits = 512 words/PDP10 page = 2048 bytes / Unibus page + * + * Bits 16:11 select a UBA mapping register, which indicates whether + * PDP10 memory at that address is accessible, and if so, provides + * PDP10 bus address bits that replace and extend the Unibus bits. + * + * Unibus addresses with bit 17 set do not map PDP10 memory. The + * high end is reserved for Unibus IO space. The rest is used for + * UBA maintenance modes (not simulated). + * + * IO space accesses may have side effects in the device; an aligned + * read of two bytes is NOT equivalent to two one byte reads of the + * same addresses. + * + * The memory access in these routines is optimized to minimize UBA + * page table lookups and shift/merge operations with PDP10 memory. + * + * Memory transfers happen in up to 3 pieces: + * head : 0-3 bytes to an aligned PDP10 word (UB address 000b) + * body : As many PDP10 whole words as possible (4 bytes 32/36 bits) + * tail : 0-3 bytes remaining after the body. + */ + int32 Map_ReadB (uint32 ba, int32 bc, uint8 *buf) { -uint32 lim, cp, np; -a10 pa10; +uint32 ea, ofs, cp, np; +int32 seg; +a10 pa10 = ~0u; +d10 m; -if ((ba & ~((IO_M_UBA<> 8) & 0xff): csr & 0xff; + *buf++ = (ba & 1)? ((csr >> 8) & 0xff): csr & 0xff; ba++; bc--; } return bc; -} -lim = ba + bc; -for ( cp = ~ba ; ba < lim; ba++) { /* by bytes */ - np = UBMPAGE (ba); - if (np != cp) { /* New (or first) page? */ - pa10 = Map_Addr10 (ba, 1, NULL); /* map addr */ - if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ - ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA times out */ - return (lim - ba); /* return bc */ - } - cp = np; - } - *buf++ = (uint8) ((M[pa10] >> ubashf[ba & 3]) & 0377); - if ((ba & 3) == 3) - pa10++; } + +/* Memory */ + +if (bc == 0) + return 0; + +cp = ~ba; +ofs = ba & 3; +seg = (4 - ofs) & 3; + +if (seg) { /* Unaligned head */ + if (seg > bc) + seg = bc; + cp = UBMPAGE (ba); /* Only one word, can't cross page */ + pa10 = Map_Addr10 (ba, 1, NULL); /* map addr */ + if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA timeout */ + return bc; /* return bc */ + } + m = M[pa10++]; + ba += seg; + bc -= seg; + switch (ofs) { + case 1: + *buf++ = (uint8) ((m >> V_BYTE1) & M_BYTE); + if (!--seg) + break; + case 2: + *buf++ = (uint8) (m & M_BYTE); /* V_BYTE2 */ + if (!--seg) + break; + case 3: + *buf++ = (uint8) ((m >> V_BYTE3) & M_BYTE); + --seg; + break; + default: + assert (FALSE); + } + if (bc == 0) + return 0; + } /* Head */ + +/* At this point, ba is aligned. Therefore, ea<1:0> are the tail's length */ +ea = ba + bc; +seg = bc - (ea & 3); + +if (seg > 0) { /* Body: Whole PDP-10 words, 4 bytes */ + assert (((seg & 3) == 0) && (bc >= seg)); + bc -= seg; + for ( ; seg; seg -= 4, ba += 4) { /* aligned longwords */ + np = UBMPAGE (ba); + if (np != cp) { /* New (or first) page? */ + pa10 = Map_Addr10 (ba, 1, NULL); /* map addr */ + if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA timeout */ + return (bc + seg); /* return bc */ + } + cp = np; + } + m = M[pa10++]; /* Next word from -10 */ + buf[2] = (uint8) (m & M_BYTE); /* Byte 2 */ + m >>= 8; + buf[3] = (uint8) (m & M_BYTE); /* Byte 3 */ + m >>= 10; + buf[0] = (uint8) (m & M_BYTE); /* Byte 0 */ + m >>= 8; + buf[1] = (uint8) (m & M_BYTE); /* Byte 1 */ + buf += 4; + } + } /* Body */ + + /* Tail: partial -10 word, must be aligned. 1-3 bytes */ +assert ((bc >= 0) && ((ba & 3) == 0)); +if (bc) { + assert (bc <= 3); + np = UBMPAGE (ba); /* Only one word, last possible page crossing */ + if (np != cp) { /* New (or first) page? */ + pa10 = Map_Addr10 (ba, 1, NULL); /* map addr */ + if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) {/* inv map or NXM? */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA timeout */ + return (bc); /* return bc */ + } + } + m = M[pa10]; + switch (bc) { + case 3: + buf[2] = (uint8) (m & M_BYTE); /* V_BYTE2 */ + case 2: + buf[1] = (uint8) ((m >> V_BYTE1) & M_BYTE); + case 1: + buf[0] = (uint8) ((m >> V_BYTE0) & M_BYTE); + break; + default: + assert (FALSE); + } + } + return 0; } int32 Map_ReadW (uint32 ba, int32 bc, uint16 *buf) { -uint32 lim, cp, np; -a10 pa10; +uint32 ea, cp, np; +int32 seg; +a10 pa10 = ~0u; +d10 m; -if ((ba & ~((IO_M_UBA<> ((ba & 2)? 0: 18)) & 0177777); - if (ba & 2 ) - pa10++; } + +/* Memory */ + +if (bc == 0) + return 0; + +ba &= ~1; +if (bc & 1) + ABORT (STOP_IOALIGN); + +cp = ~ba; +seg = (4 - (ba & 3)) & 3; + +if (seg) { /* Unaligned head, can only be WORD1 */ + assert ((ba & 2) && (seg == 2)); + if (seg > bc) + seg = bc; + cp = UBMPAGE (ba); /* Only one word, can't cross page */ + pa10 = Map_Addr10 (ba, 1, NULL); /* map addr */ + if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA timeout */ + return bc; /* return bc */ + } + ba += seg; + *buf++ = (uint16) (M[pa10++] & M_WORD); + if ((bc -= seg) == 0) + return 0; + } /* Head */ + +ea = ba + bc; +seg = bc - (ea & 3); + +if (seg > 0) { + assert (((seg & 3) == 0) && (bc >= seg)); + bc -= seg; + for ( ; seg; seg -= 4, ba += 4) { /* aligned longwords */ + np = UBMPAGE (ba); + if (np != cp) { /* New (or first) page? */ + pa10 = Map_Addr10 (ba, 1, NULL); /* map addr */ + if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA timeout */ + return (bc + seg); /* return bc */ + } + cp = np; + } + m = M[pa10++]; /* Next word from -10 */ + buf[1] = (uint16) (m & M_WORD); /* Bytes 3,,2 */ + m >>= 18; + buf[0] = (uint16) (m & M_WORD); /* Bytes 1,,0 */ + buf += 2; + } + } /* Body */ + +/* Tail: partial word, must be aligned, can only be WORD0 */ +assert ((bc >= 0) && ((ba & 3) == 0)); +if (bc) { + assert (bc == 2); + np = UBMPAGE (ba); /* Only one word, last possible page crossing */ + if (np != cp) { /* New (or first) page? */ + pa10 = Map_Addr10 (ba, 1, NULL); /* map addr */ + if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) {/* inv map or NXM? */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA timeout */ + return (bc); /* return bc */ + } + } + *buf = (uint16) ((M[pa10] >> V_WORD0) & M_WORD); + } + return 0; } -/* Word reads returning 18-bit data */ +/* Word reads returning 18-bit data + * + * Identical to 16-bit reads except that buffer is uint32 + * and masked to 18 bits. + */ int32 Map_ReadW18 (uint32 ba, int32 bc, uint32 *buf) { -uint32 lim, cp, np; -a10 pa10; +uint32 ea, cp, np; +int32 seg; +a10 pa10 = ~0u; +d10 m; -if ((ba & ~((IO_M_UBA<> ((ba & 2)? 0: 18)) & 0777777); - if (ba & 2 ) - pa10++; } + +/* Memory */ + +if (bc == 0) + return 0; + +ba &= ~1; +if (bc & 1) + ABORT (STOP_IOALIGN); + +cp = ~ba; +seg = (4 - (ba & 3)) & 3; + +if (seg) { /* Unaligned head */ + assert ((ba & 2) && (seg == 2)); + if (seg > bc) + seg = bc; + cp = UBMPAGE (ba); /* Only one word, can't cross page */ + pa10 = Map_Addr10 (ba, 1, NULL); /* map addr */ + if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA timeout */ + return bc; /* return bc */ + } + ba += seg; + *buf++ = (uint32) (M[pa10++] & M_RH); + if ((bc -= seg) == 0) + return 0; + } /* Head */ + +ea = ba + bc; +seg = bc - (ea & 3); + +if (seg > 0) { + assert (((seg & 3) == 0) && (bc >= seg)); + bc -= seg; + for ( ; seg; seg -= 4, ba += 4) { /* aligned longwords */ + np = UBMPAGE (ba); + if (np != cp) { /* New (or first) page? */ + pa10 = Map_Addr10 (ba, 1, NULL); /* map addr */ + if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA timeout */ + return (bc + seg); /* return bc */ + } + cp = np; + } + m = M[pa10++]; /* Next word from -10 */ + buf[1] = (uint32) (m & M_RH); /* Bytes 3,,2 */ + m >>= 18; + buf[0] = (uint32) (m & M_RH); /* Bytes 1,,0 */ + buf += 2; + } + } /* Body */ + +/* Tail: partial word, must be aligned */ +assert ((bc >= 0) && ((ba & 3) == 0)); +if (bc) { + assert (bc == 2); + np = UBMPAGE (ba); /* Only one word, last possible page crossing */ + if (np != cp) { /* New (or first) page? */ + pa10 = Map_Addr10 (ba, 1, NULL); /* map addr */ + if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA timeout */ + return (bc); /* return bc */ + } + } + *buf++ = (uint32) ((M[pa10] >> V_WORD0) & M_RH); + } + return 0; } @@ -607,12 +926,14 @@ return 0; int32 Map_WriteB (uint32 ba, int32 bc, uint8 *buf) { -uint32 lim, cp, np; -a10 pa10; -d10 mask; +uint32 ea, ofs, cp, np; +int32 seg, ubm = 0; +a10 pa10 = ~0u; +d10 m; + +if ((ba & ~((IO_M_UBA< of M[] are undefined. */ - M[pa10] = ((d10) *buf++) << 18; /* Clears undefined bits */ - } else { /* RPW - clear byte position, and UB<17:16> of correct 1/2 word when writing high byte */ - mask = 0377<< ubashf[ba & 3]; - if (ba & 1) - mask |= INT64_C(0000000600000) << ((ba & 2)? 0 : 18); - M[pa10] = (M[pa10] & ~mask) | (((d10) *buf++) << ubashf[ba & 3]); - if ((ba & 3) == 3) - pa10++; - } } + +/* Memory */ + +if (bc == 0) + return 0; + +cp = ~ba; +ofs = ba & 3; +seg = (4 - ofs) & 3; + +if (seg) { /* Unaligned head */ + if (seg > bc) + seg = bc; + cp = UBMPAGE (ba); /* Only one word, can't cross page */ + pa10 = Map_Addr10 (ba, 1, &ubm); /* map addr */ + if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA timeout */ + return bc; /* return bc */ + } + m = M[pa10]; + ba += seg; + bc -= seg; + switch (ofs) { + case 1: + m = (m & M_BYTE1) | (((d10) (*buf++)) << V_BYTE1); + if (!--seg) + break; + case 2: + m = (m & M_BYTE2) | ((d10) (*buf++)); /* V_BYTE2 */ + if (!--seg) + break; + case 3: + m = (m & M_BYTE3) | (((d10) (*buf++)) << V_BYTE3); + --seg; + break; + default: + assert (FALSE); + } + M[pa10++] = m; + if (bc == 0) + return 0; + } /* Head */ + +ea = ba + bc; +seg = bc - (ea & 3); + +if (seg > 0) { + assert (((seg & 3) == 0) && (bc >= seg)); + bc -= seg; + for ( ; seg; seg -= 4, ba += 4) { /* aligned longwords */ + np = UBMPAGE (ba); + if (np != cp) { /* New (or first) page? */ + pa10 = Map_Addr10 (ba, 1, &ubm); /* map addr */ + if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA timeout */ + return (bc + seg); /* return bc */ + } + cp = np; + } + M[pa10++] = (((d10)((buf[1] << 8) | buf[0])) << 18) | /* <0:1,18:19> = 0 */ + ((buf[3] << 8) | buf[2]); + buf += 4; + } + } /* Body */ + +/* Tail: partial word, must be aligned */ + +assert ((bc >= 0) && ((ba & 3) == 0)); +if (bc) { + assert (bc <= 3); + np = UBMPAGE (ba); /* Only one word, last possible page crossing */ + if (np != cp) { /* New (or first) page? */ + pa10 = Map_Addr10 (ba, 1, &ubm); /* map addr */ + if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA timeout */ + return (bc); /* return bc */ + } + } + m = M[pa10]; + if ((ubm & UMAP_RRV )) { /* RMW */ + switch (bc) { + case 3: + m = (m & M_BYTE2) | ((d10) (buf[2])); /* V_BYTE2 */ + case 2: + m = (m & M_BYTE1) | (((d10) (buf[1])) << V_BYTE1); + case 1: + m = (m & M_BYTE0) | (((d10) (buf[0])) << V_BYTE0); + break; + default: + assert (FALSE); + } + } + else { + switch (bc) { /* Write byte 0 + RMW bytes 1 & 2 */ + case 3: + m = (((d10) (buf[1])) << V_BYTE1) | (((d10) (buf[0])) << V_BYTE0) | + ((d10) (buf[2])); /* V_BYTE2 */ + break; + case 2: + m = (((d10) (buf[1])) << V_BYTE1) | (((d10) (buf[0])) << V_BYTE0); + break; + case 1: + m = ((d10) (buf[0])) << V_BYTE0; + break; + default: + assert (FALSE); + } + } + M[pa10] = m; + } + return 0; } @@ -650,15 +1060,16 @@ return 0; int32 Map_WriteW (uint32 ba, int32 bc, uint16 *buf) { -uint32 lim, cp, np; -int32 ubm; -a10 pa10; -d10 val; +uint32 ea, cp, np; +int32 seg, ubm = 0; +a10 pa10 = ~0u; + +if ((ba & ~((IO_M_UBA< */ - if (ubm & UMAP_RRV ) { /* Read reverse preserves even word */ - if (ba & 2) { - M[pa10] = (M[pa10] & INT64_C(0777777000000)) | val; - pa10++; - } else - M[pa10] = (M[pa10] & INT64_C(0000000777777)) | (val << 18); /* preserve */ - } else { /* Not RRV */ - if (ba & 2) { /* Write odd preserves even word */ - M[pa10] = (M[pa10] & INT64_C(0777777000000)) | val; - pa10++; - } else - M[pa10] = val << 18; /* Write even clears odd */ - } } + +/* Memory */ + +if (bc == 0) + return 0; + +ba &= ~1; +if (bc & 1) + ABORT (STOP_IOALIGN); + +cp = ~ba; +seg = (4 - (ba & 3)) & 3; + +if (seg) { /* Unaligned head */ + assert ((ba & 2) && (seg == 2)); + if (seg > bc) + seg = bc; + cp = UBMPAGE (ba); /* Only one word, can't cross page */ + pa10 = Map_Addr10 (ba, 1, &ubm); /* map addr */ + if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA timeout */ + return bc; /* return bc */ + } + M[pa10] = (M[pa10] & M_WORD1) | ((d10) (*buf++)); + pa10++; + + if ((bc -= seg) == 0) + return 0; + ba += seg; + } /* Head */ + +ea = ba + bc; +seg = bc - (ea & 3); + +if (seg > 0) { + assert (((seg & 3) == 0) && (bc >= seg)); + bc -= seg; + for ( ; seg; seg -= 4, ba += 4) { /* aligned longwords */ + np = UBMPAGE (ba); + if (np != cp) { /* New (or first) page? */ + pa10 = Map_Addr10 (ba, 1, &ubm); /* map addr */ + if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA timeout */ + return (bc + seg); /* return bc */ + } + cp = np; + } + M[pa10++] = (((d10)(buf[0])) << V_WORD0) | buf[1];/* <0:1,18:19> = 0 + * V_WORD1 + */ + buf += 2; + } + } /* Body */ + +/* Tail: partial word, must be aligned, can only be WORD0 */ +assert ((bc >= 0) && ((ba & 3) == 0)); +if (bc) { + assert (bc == 2); + np = UBMPAGE (ba); /* Only one word, last possible page crossing */ + if (np != cp) { /* New (or first) page? */ + pa10 = Map_Addr10 (ba, 1, &ubm); /* map addr */ + if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA timeout */ + return (bc); /* return bc */ + } + } + if (ubm & UMAP_RRV ) /* Read reverse preserves RH */ + M[pa10] = (((d10)(buf[0])) << V_WORD0) | (M[pa10] & M_WORD0); + else + M[pa10] = ((d10)(buf[0])) << V_WORD0; +} + return 0; } @@ -702,50 +1158,94 @@ return 0; int32 Map_WriteW18 (uint32 ba, int32 bc, uint32 *buf) { -uint32 lim, cp, np; -int32 ubm; -a10 pa10; -d10 val; +uint32 ea, cp, np; +int32 seg, ubm = 0; +a10 pa10 = ~0u; if ((ba & ~((IO_M_UBA< bc) + seg = bc; + cp = UBMPAGE (ba); /* Only one word, can't cross page */ + pa10 = Map_Addr10 (ba, 1, &ubm); /* map addr */ + if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA timeout */ + return bc; /* return bc */ + } + M[pa10] = (M[pa10] & M_WORD1) | ((d10) (M_WORD18 & *buf++)); /* V_WORD1 */ + pa10++; + + if ((bc -= seg) == 0) + return 0; + ba += seg; + } /* Head */ + +ea = ba + bc; +seg = bc - (ea & 3); + +if (seg > 0) { + assert (((seg & 3) == 0) && (bc >= seg)); + bc -= seg; + for ( ; seg; seg -= 4, ba += 4) { /* aligned longwords */ + np = UBMPAGE (ba); + if (np != cp) { /* New (or first) page? */ + pa10 = Map_Addr10 (ba, 1, &ubm); /* map addr */ + if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA timeout */ + return (bc + seg); /* return bc */ + } + cp = np; } - cp = np; + M[pa10++] = (((d10)(M_WORD18 & buf[0])) << V_WORD0) | (M_WORD18 & buf[1]);/* V_WORD1 */ + buf += 2; } - val = *buf++; /* get 18-bit data */ - if (ubm & UMAP_RRV ) { /* Read reverse preserves even word */ - if (ba & 2) { - M[pa10] = (M[pa10] & INT64_C(0777777000000)) | val; - pa10++; - } else - M[pa10] = (M[pa10] & INT64_C(0000000777777)) | (val << 18); /* preserve */ - } else { /* Not RRV */ - if (ba & 2) { /* Write odd preserves even word */ - M[pa10] = (M[pa10] & INT64_C(0777777000000)) | val; - pa10++; - } else - M[pa10] = val << 18; /* Write even clears odd */ + } /* Body */ + +/* Tail: partial word, must be aligned */ +assert ((bc >= 0) && ((ba & 3) == 0)); +if (bc) { + assert (bc == 2); + np = UBMPAGE (ba); /* Only one word, last possible page crossing */ + if (np != cp) { /* New (or first) page? */ + pa10 = Map_Addr10 (ba, 1, &ubm); /* map addr */ + if ((pa10 < 0) || MEM_ADDR_NXM (pa10)) { /* inv map or NXM? */ + ubcs[1] = ubcs[1] | UBCS_TMO; /* UBA timeout */ + return (bc); /* return bc */ + } } - } + if (ubm & UMAP_RRV ) /* Read reverse preserves RH */ + M[pa10] = (M[pa10] & M_WORD0) | (((d10)(M_WORD18 & buf[0])) << V_WORD0); + else + M[pa10] = ((d10)(M_WORD18 & buf[0])) << V_WORD0; +} + return 0; } @@ -1208,6 +1708,8 @@ AUTO_CON auto_tab[] = {/*c #v am vm fxa fxv */ {0000300}, {0570} }, /* DUP11 bit sync - fx CSR, fx VEC */ { { "KDP" }, 1, 2, 0, 0, {0000540}, {0540} }, /* KMC11-A comm IOP-DUP ucode - fx CSR, fx VEC */ + { { "DMR" }, 1, 2, 0, 0, + {0000700}, {0440} }, /* DMR11 comm - fx CSR, fx VEC */ #else { { "QBA" }, 1, 0, 0, 0, {017500} }, /* doorbell - fx CSR, no VEC */ diff --git a/PDP10/pdp10_pag.c b/PDP10/pdp10_pag.c index 235a5357..8bf3b0a5 100644 --- a/PDP10/pdp10_pag.c +++ b/PDP10/pdp10_pag.c @@ -429,7 +429,7 @@ else { /* TOPS-20 paging */ int32 pmi, vpn, xpte; int32 flg, t; t_bool stop; - a10 pa, csta; + a10 pa, csta = 0; d10 ptr, cste; d10 acc = PTE_T20_W | PTE_T20_C; /* init access bits */ diff --git a/PDP10/pdp10_rp.c b/PDP10/pdp10_rp.c index c307a6c2..a4947314 100644 --- a/PDP10/pdp10_rp.c +++ b/PDP10/pdp10_rp.c @@ -1229,7 +1229,7 @@ return SCPE_OK; static const d10 boot_rom_dec[] = { INT64_C(0510040000000)+FE_RHBASE, /* boot:hllz 1,FE_RHBASE ; uba # */ INT64_C(0201000140001), /* movei 0,140001 ; vld,fst,pg 1 */ - INT64_C(0713001000000)+(IOBA_UBMAP+1 & RMASK), /* wrio 0,763001(1); set ubmap */ + INT64_C(0713001000000)+((IOBA_UBMAP+1) & RMASK), /* wrio 0,763001(1); set ubmap */ INT64_C(0200040000000)+FE_RHBASE, /* move 1,FE_RHBASE */ INT64_C(0201000000040), /* movei 0,40 ; ctrl reset */ INT64_C(0713001000010), /* wrio 0,10(1) ; ->RPCS2 */ @@ -1293,7 +1293,7 @@ static const d10 boot_rom_dec[] = { static const d10 boot_rom_its[] = { INT64_C(0510040000001)+FE_RHBASE, /* boot:hllzi 1,FE_RHBASE ; uba # */ INT64_C(0201000140001), /* movei 0,140001 ; vld,fst,pg 1 */ - INT64_C(0715000000000)+(IOBA_UBMAP+1 & RMASK), /* iowrq 0,763001 ; set ubmap */ + INT64_C(0715000000000)+((IOBA_UBMAP+1) & RMASK), /* iowrq 0,763001 ; set ubmap */ INT64_C(0200040000000)+FE_RHBASE, /* move 1,FE_RHBASE */ INT64_C(0201000000040), /* movei 0,40 ; ctrl reset */ INT64_C(0715001000010), /* iowrq 0,10(1) ; ->RPCS2 */ diff --git a/PDP10/pdp10_sys.c b/PDP10/pdp10_sys.c index c36f85fa..8b6094d9 100644 --- a/PDP10/pdp10_sys.c +++ b/PDP10/pdp10_sys.c @@ -56,6 +56,8 @@ extern DEVICE ry_dev; extern DEVICE cr_dev; extern DEVICE lp20_dev; extern DEVICE dup_dev; +extern DEVICE kmc_dev; +extern DEVICE dmc_dev; extern UNIT cpu_unit; extern REG cpu_reg[]; extern d10 *M; @@ -92,6 +94,8 @@ DEVICE *sim_devices[] = { &tu_dev, &dz_dev, &dup_dev, + &kmc_dev, + &dmc_dev, NULL }; @@ -109,6 +113,7 @@ const char *sim_stop_messages[] = { "Invalid I/O controller", "Address stop", "Console FE halt", + "Unaligned DMA", "Panic stop" }; diff --git a/PDP10/pdp10_tu.c b/PDP10/pdp10_tu.c index f37c3ca8..8a5cbd67 100644 --- a/PDP10/pdp10_tu.c +++ b/PDP10/pdp10_tu.c @@ -810,7 +810,7 @@ return; t_stat tu_svc (UNIT *uptr) { int32 fnc, fmt, i, j, k, wc10, ba10; -int32 ba, fc, wc, drv, mpa10, vpn; +int32 ba, fc, wc, drv, mpa10 = 0, vpn; d10 val, v[4]; t_mtrlnt tbc; t_stat st, r = SCPE_OK; @@ -1258,7 +1258,7 @@ return sim_tape_detach (uptr); static const d10 boot_rom_dec[] = { INT64_C(0510040000000)+FE_RHBASE, /* boot:hllz 1,FE_RHBASE ; uba # */ INT64_C(0201000040001), /* movei 0,40001 ; vld,pg 1 */ - INT64_C(0713001000000)+(IOBA_UBMAP+1 & RMASK), /* wrio 0,763001(1); set ubmap */ + INT64_C(0713001000000)+((IOBA_UBMAP+1) & RMASK), /* wrio 0,763001(1); set ubmap */ INT64_C(0200040000000)+FE_RHBASE, /* move 1,FE_RHBASE */ INT64_C(0201000000040), /* movei 0,40 ; ctrl reset */ INT64_C(0713001000010), /* wrio 0,10(1) ; ->MTFS */ @@ -1312,7 +1312,7 @@ static const d10 boot_rom_dec[] = { static const d10 boot_rom_its[] = { INT64_C(0510040000000)+FE_RHBASE, /* boot:hllz 1,FE_RHBASE ; uba # - not used */ INT64_C(0201000040001), /* movei 0,40001 ; vld,pg 1 */ - INT64_C(0714000000000)+(IOBA_UBMAP+1 & RMASK), /* iowri 0,763001 ; set ubmap */ + INT64_C(0714000000000)+((IOBA_UBMAP+1) & RMASK), /* iowri 0,763001 ; set ubmap */ INT64_C(0200040000000)+FE_RHBASE, /* move 1,FE_RHBASE */ INT64_C(0201000000040), /* movei 0,40 ; ctrl reset */ INT64_C(0714001000010), /* iowri 0,10(1) ; ->MTFS */ diff --git a/PDP10/pdp10_xtnd.c b/PDP10/pdp10_xtnd.c index d64e7fde..f60a4512 100644 --- a/PDP10/pdp10_xtnd.c +++ b/PDP10/pdp10_xtnd.c @@ -155,41 +155,41 @@ d10 xlate (d10 by, a10 tblad, d10 *xflgs, int32 pflgs); void filldst (d10 fill, int32 ac, d10 cnt, int32 pflgs); static const d10 pwrs10[23][2] = { - INT64_C(0), INT64_C(0), - INT64_C(0), INT64_C(1), - INT64_C(0), INT64_C(10), - INT64_C(0), INT64_C(100), - INT64_C(0), INT64_C(1000), - INT64_C(0), INT64_C(10000), - INT64_C(0), INT64_C(100000), - INT64_C(0), INT64_C(1000000), - INT64_C(0), INT64_C(10000000), - INT64_C(0), INT64_C(100000000), - INT64_C(0), INT64_C(1000000000), - INT64_C(0), INT64_C(10000000000), - INT64_C(2), INT64_C(31280523264), - INT64_C(29), INT64_C(3567587328), - INT64_C(291), INT64_C(1316134912), - INT64_C(2910), INT64_C(13161349120), - INT64_C(29103), INT64_C(28534276096), - INT64_C(291038), INT64_C(10464854016), - INT64_C(2910383), INT64_C(1569325056), - INT64_C(29103830), INT64_C(15693250560), - INT64_C(291038304), INT64_C(19493552128), - INT64_C(2910383045), INT64_C(23136829440), - INT64_C(29103830456), INT64_C(25209864192) +{ INT64_C(0), INT64_C(0),}, +{ INT64_C(0), INT64_C(1),}, +{ INT64_C(0), INT64_C(10),}, +{ INT64_C(0), INT64_C(100),}, +{ INT64_C(0), INT64_C(1000),}, +{ INT64_C(0), INT64_C(10000),}, +{ INT64_C(0), INT64_C(100000),}, +{ INT64_C(0), INT64_C(1000000),}, +{ INT64_C(0), INT64_C(10000000),}, +{ INT64_C(0), INT64_C(100000000),}, +{ INT64_C(0), INT64_C(1000000000),}, +{ INT64_C(0), INT64_C(10000000000),}, +{ INT64_C(2), INT64_C(31280523264),}, +{ INT64_C(29), INT64_C(3567587328),}, +{ INT64_C(291), INT64_C(1316134912),}, +{ INT64_C(2910), INT64_C(13161349120),}, +{ INT64_C(29103), INT64_C(28534276096),}, +{ INT64_C(291038), INT64_C(10464854016),}, +{ INT64_C(2910383), INT64_C(1569325056),}, +{ INT64_C(29103830), INT64_C(15693250560),}, +{ INT64_C(291038304), INT64_C(19493552128),}, +{ INT64_C(2910383045), INT64_C(23136829440),}, +{ INT64_C(29103830456), INT64_C(25209864192),}, }; int xtend (int32 ac, int32 ea, int32 pflgs) { d10 b1, b2, ppi; -d10 xinst, xoff, digit, f1, f2, rs[2]; +d10 xinst, xoff = 0, digit, f1, f2, rs[2]; d10 xflgs = 0; -a10 e1, entad; +a10 e1 = 0, entad; int32 p1 = ADDAC (ac, 1); int32 p3 = ADDAC (ac, 3); int32 p4 = ADDAC (ac, 4); -int32 flg, i, s2, t, pp, pat, xop, xac, ret; +int32 flg, i, s2 = 0, t, pp, pat, xop, xac, ret; xinst = Read (ea, MM_OPND); /* get extended instr */ xop = GET_OP (xinst); /* get opcode */ diff --git a/PDP11/pdp11_ddcmp.h b/PDP11/pdp11_ddcmp.h index 6ac3f1a9..19094e1c 100644 --- a/PDP11/pdp11_ddcmp.h +++ b/PDP11/pdp11_ddcmp.h @@ -31,6 +31,8 @@ #ifndef PDP11_DDCMP_H_ #define PDP11_DDCMP_H_ 0 +#include "sim_tmxr.h" + /* DDCMP packet types */ #define DDCMP_SYN 0226u /* Sync character on synchronous links */ @@ -39,6 +41,27 @@ #define DDCMP_ENQ 0005u /* Control Message Identifier */ #define DDCMP_DLE 0220u /* Maintenance Message Identifier */ +#define DDCMP_CTL_ACK 1 /* Control Message ACK Type */ +#define DDCMP_CTL_NAK 2 /* Control Message NAK Type */ +#define DDCMP_CTL_REP 3 /* Control Message REP Type */ +#define DDCMP_CTL_STRT 6 /* Control Message STRT Type */ +#define DDCMP_CTL_STACK 7 /* Control Message STACK Type */ + +#define DDCMP_FLAG_SELECT 0x2 /* Link Select */ +#define DDCMP_FLAG_QSYNC 0x1 /* Quick Sync (next message won't abut this message) */ + +#define DDCMP_CRC_SIZE 2 /* Bytes in DDCMP CRC fields */ +#define DDCMP_HEADER_SIZE 8 /* Bytes in DDCMP Control and Data Message headers (including header CRC) */ + +#define DDCMP_RESP_OFFSET 3 /* Byte offset of response (ack) number field */ +#define DDCMP_NUM_OFFSET 4 /* Byte offset of packet number field */ + +#define DDCMP_PACKET_TIMEOUT 4 /* Seconds before sending REP command for unacknowledged packets */ + +#define DDCMP_DBG_PXMT TMXR_DBG_PXMT /* Debug Transmitted Packet Header Contents */ +#define DDCMP_DBG_PRCV TMXR_DBG_PRCV /* Debug Received Packet Header Contents */ +#define DDCMP_DBG_PDAT 0x1000000 /* Debug Packet Data */ + /* Support routines */ /* crc16 polynomial x^16 + x^15 + x^2 + 1 (0xA001) CCITT LSB */ @@ -62,54 +85,62 @@ return(crc); #include -static void ddcmp_packet_trace (uint32 reason, DEVICE *dptr, const char *txt, const uint8 *msg, int32 len, t_bool detail) +static void ddcmp_packet_trace (uint32 reason, DEVICE *dptr, const char *txt, const uint8 *msg, int32 len) { if (sim_deb && dptr && (reason & dptr->dctrl)) { - sim_debug(reason, dptr, "%s len: %d\n", txt, len); - if (detail) { - int i, same, group, sidx, oidx; - char outbuf[80], strbuf[18]; - static char hex[] = "0123456789ABCDEF"; + int i, same, group, sidx, oidx; + char outbuf[80], strbuf[18]; + static const char hex[] = "0123456789ABCDEF"; + static const char *const flags [4] = { "..", ".Q", "S.", "SQ" }; + static const char *const nak[18] = { "", " (HCRC)", " (DCRC)", " (REPREPLY)", /* 0-3 */ + "", "", "", "", /* 4-7 */ + " (NOBUF)", " (RXOVR)", "", "", /* 8-11 */ + "", "", "", "", /* 12-15 */ + " (TOOLONG)", " (HDRFMT)" }; /* 16-17 */ + const char *flag = flags[msg[2]>>6]; + int msg2 = msg[2] & 0x3F; - switch (msg[0]) { - case DDCMP_SOH: /* Data Message */ - sim_debug (reason, dptr, "Data Message, Link: %d, Count: %d, Resp: %d, Num: %d, HDRCRC: %s, DATACRC: %s\n", msg[2]>>6, ((msg[2] & 0x3F) << 8)|msg[1], msg[3], msg[4], - (0 == ddcmp_crc16 (0, msg, 8)) ? "OK" : "BAD", (0 == ddcmp_crc16 (0, msg+8, 2+(((msg[2] & 0x3F) << 8)|msg[1]))) ? "OK" : "BAD"); - break; - case DDCMP_ENQ: /* Control Message */ - sim_debug (reason, dptr, "Control: Type: %d ", msg[1]); - switch (msg[1]) { - case 1: /* ACK */ - sim_debug (reason, dptr, "(ACK) ACKSUB: %d, Link: %d, Resp: %d\n", msg[2] & 0x3F, msg[2]>>6, msg[3]); - break; - case 2: /* NAK */ - sim_debug (reason, dptr, "(NAK) Reason: %d, Link: %d, Resp: %d\n", msg[2] & 0x3F, msg[2]>>6, msg[3]); - break; - case 3: /* REP */ - sim_debug (reason, dptr, "(REP) REPSUB: %d, Link: %d, Num: %d\n", msg[2] & 0x3F, msg[2]>>6, msg[4]); - break; - case 6: /* STRT */ - sim_debug (reason, dptr, "(STRT) STRTSUB: %d, Link: %d\n", msg[2] & 0x3F, msg[2]>>6); - break; - case 7: /* STACK */ - sim_debug (reason, dptr, "(STACK) STCKSUB: %d, Link: %d\n", msg[2] & 0x3F, msg[2]>>6); - break; - default: /* Unknown */ - sim_debug (reason, dptr, "(Unknown=0%o)\n", msg[1]); - break; - } - if (len != 8) { - sim_debug (reason, dptr, "Unexpected Control Message Length: %d expected 8\n", len); - } - if (0 != ddcmp_crc16 (0, msg, len)) { - sim_debug (reason, dptr, "Unexpected Message CRC\n"); - } - break; - case DDCMP_DLE: /* Maintenance Message */ - sim_debug (reason, dptr, "Maintenance Message, Link: %d, Count: %d, HDRCRC: %s, DATACRC: %s\n", msg[2]>>6, ((msg[2] & 0x3F) << 8)| msg[1], - (0 == ddcmp_crc16 (0, msg, 8)) ? "OK" : "BAD", (0 == ddcmp_crc16 (0, msg+8, 2+(((msg[2] & 0x3F) << 8)| msg[1]))) ? "OK" : "BAD"); - break; - } + sim_debug(reason, dptr, "%s len: %d\n", txt, len); + switch (msg[0]) { + case DDCMP_SOH: /* Data Message */ + sim_debug (reason, dptr, "Data Message, Count: %d, Num: %d, Flags: %s, Resp: %d, HDRCRC: %s, DATACRC: %s\n", (msg2 << 8)|msg[1], msg[4], flag, msg[3], + (0 == ddcmp_crc16 (0, msg, 8)) ? "OK" : "BAD", (0 == ddcmp_crc16 (0, msg+8, 2+((msg2 << 8)|msg[1]))) ? "OK" : "BAD"); + break; + case DDCMP_ENQ: /* Control Message */ + sim_debug (reason, dptr, "Control: Type: %d ", msg[1]); + switch (msg[1]) { + case DDCMP_CTL_ACK: /* ACK */ + sim_debug (reason, dptr, "(ACK) ACKSUB: %d, Flags: %s, Resp: %d\n", msg2, flag, msg[3]); + break; + case DDCMP_CTL_NAK: /* NAK */ + sim_debug (reason, dptr, "(NAK) Reason: %d%s, Flags: %s, Resp: %d\n", msg2, ((msg2 > 17)? "": nak[msg2]), flag, msg[3]); + break; + case DDCMP_CTL_REP: /* REP */ + sim_debug (reason, dptr, "(REP) REPSUB: %d, Num: %d, Flags: %s\n", msg2, msg[4], flag); + break; + case DDCMP_CTL_STRT: /* STRT */ + sim_debug (reason, dptr, "(STRT) STRTSUB: %d, Flags: %s\n", msg2, flag); + break; + case DDCMP_CTL_STACK: /* STACK */ + sim_debug (reason, dptr, "(STACK) STCKSUB: %d, Flags: %s\n", msg2, flag); + break; + default: /* Unknown */ + sim_debug (reason, dptr, "(Unknown=0%o)\n", msg[1]); + break; + } + if (len != DDCMP_HEADER_SIZE) { + sim_debug (reason, dptr, "Unexpected Control Message Length: %d expected %d\n", len, DDCMP_HEADER_SIZE); + } + if (0 != ddcmp_crc16 (0, msg, len)) { + sim_debug (reason, dptr, "Unexpected Message CRC\n"); + } + break; + case DDCMP_DLE: /* Maintenance Message */ + sim_debug (reason, dptr, "Maintenance Message, Count: %d, Flags: %s, HDRCRC: %s, DATACRC: %s\n", (msg2 << 8)| msg[1], flag, + (0 == ddcmp_crc16 (0, msg, DDCMP_HEADER_SIZE)) ? "OK" : "BAD", (0 == ddcmp_crc16 (0, msg+DDCMP_HEADER_SIZE, 2+((msg2 << 8)| msg[1]))) ? "OK" : "BAD"); + break; + } + if (DDCMP_DBG_PDAT & dptr->dctrl) { for (i=same=0; i 0) && (0 == memcmp(&msg[i], &msg[i-16], 16))) { ++same; @@ -142,4 +173,228 @@ if (sim_deb && dptr && (reason & dptr->dctrl)) { uint16 ddcmp_crc16(uint16 crc, const void* vbuf, size_t len); +/* Get packet from specific line + + Inputs: + *lp = pointer to terminal line descriptor + **pbuf = pointer to pointer of packet contents + *psize = pointer to packet size + + Output: + SCPE_LOST link state lost + SCPE_OK Packet returned OR no packet available + + Implementation notes: + + 1. If a packet is not yet available, then the pbuf address returned is + NULL, but success (SCPE_OK) is returned +*/ + +static t_stat ddcmp_tmxr_get_packet_ln (TMLN *lp, const uint8 **pbuf, uint16 *psize) +{ +int32 c; +size_t payloadsize; + +while (TMXR_VALID & (c = tmxr_getc_ln (lp))) { + c &= ~TMXR_VALID; + if (lp->rxpboffset + 1 > lp->rxpbsize) { + lp->rxpbsize += 512; + lp->rxpb = (uint8 *)realloc (lp->rxpb, lp->rxpbsize); + } + lp->rxpb[lp->rxpboffset] = c; + if ((lp->rxpboffset == 0) && ((c == DDCMP_SYN) || (c == DDCMP_DEL))) { + tmxr_debug (DDCMP_DBG_PRCV, lp, "Ignoring Interframe Sync Character", (char *)&lp->rxpb[0], 1); + continue; + } + lp->rxpboffset += 1; + if (lp->rxpboffset == 1) { + switch (c) { + default: + tmxr_debug (DDCMP_DBG_PRCV, lp, "Ignoring unexpected byte in DDCMP mode", (char *)&lp->rxpb[0], 1); + lp->rxpboffset = 0; + case DDCMP_SOH: + case DDCMP_ENQ: + case DDCMP_DLE: + continue; + } + } + if (lp->rxpboffset >= DDCMP_HEADER_SIZE) { + if (lp->rxpb[0] == DDCMP_ENQ) { /* Control Message? */ + ++lp->rxpcnt; + *pbuf = lp->rxpb; + *psize = DDCMP_HEADER_SIZE; + lp->rxpboffset = 0; + ddcmp_packet_trace (DDCMP_DBG_PRCV, lp->mp->dptr, "<<< RCV Packet", lp->rxpb, *psize); + return SCPE_OK; + } + payloadsize = ((lp->rxpb[2] & 0x3F) << 8)| lp->rxpb[1]; + if (lp->rxpboffset >= 10 + payloadsize) { + ++lp->rxpcnt; + *pbuf = lp->rxpb; + *psize = 10 + payloadsize; + ddcmp_packet_trace (DDCMP_DBG_PRCV, lp->mp->dptr, "<<< RCV Packet", lp->rxpb, *psize); + lp->rxpboffset = 0; + return SCPE_OK; + } + } + } +*pbuf = NULL; +*psize = 0; +if (lp->conn) + return SCPE_OK; +return SCPE_LOST; +} + +/* Store packet in line buffer (or store packet in line buffer and add needed CRCs) + + Inputs: + *lp = pointer to line descriptor + *buf = pointer to packet data + size = size of packet + + Outputs: + status = ok, connection lost, or stall + + Implementation notea: + + 1. If the line is not connected, SCPE_LOST is returned. + 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) +{ +t_stat r; + +if (!lp->conn) + return SCPE_LOST; +if (lp->txppoffset < lp->txppsize) { + tmxr_debug (DDCMP_DBG_PXMT, lp, "Skipped Sending Packet - Transmit Busy", (char *)&lp->txpb[3], size); + return SCPE_STALL; + } +if (lp->txpbsize < size) { + lp->txpbsize = size; + lp->txpb = (uint8 *)realloc (lp->txpb, lp->txpbsize); + } +memcpy (lp->txpb, buf, size); +lp->txppsize = size; +lp->txppoffset = 0; +ddcmp_packet_trace (DDCMP_DBG_PXMT, lp->mp->dptr, ">>> XMT Packet", lp->txpb, lp->txppsize); +++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) +{ +uint16 hdr_crc16 = ddcmp_crc16(0, buf, DDCMP_HEADER_SIZE-DDCMP_CRC_SIZE); + +buf[DDCMP_HEADER_SIZE-DDCMP_CRC_SIZE] = hdr_crc16 & 0xFF; +buf[DDCMP_HEADER_SIZE-DDCMP_CRC_SIZE+1] = (hdr_crc16>>8) & 0xFF; +if (size > DDCMP_HEADER_SIZE) { + uint16 data_crc16 = ddcmp_crc16(0, buf+DDCMP_HEADER_SIZE, size-(DDCMP_HEADER_SIZE+DDCMP_CRC_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); +} + +static void ddcmp_build_data_packet (uint8 *buf, size_t size, uint8 flags, uint8 sequence, uint8 ack) +{ +buf[0] = DDCMP_SOH; +buf[1] = size & 0xFF; +buf[2] = ((size >> 8) & 0x3F) | (flags << 6); +buf[3] = ack; +buf[4] = sequence; +buf[5] = 1; +} + +static void ddcmp_build_maintenance_packet (uint8 *buf, size_t size) +{ +buf[0] = DDCMP_DLE; +buf[1] = size & 0xFF; +buf[2] = ((size >> 8) & 0x3F) | (DDCMP_FLAG_SELECT|DDCMP_FLAG_QSYNC << 6); +buf[3] = 0; +buf[4] = 0; +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); +} + +static void ddcmp_build_control_packet (uint8 *buf, uint8 type, uint8 subtype, uint8 flags, uint8 sndr, uint8 rcvr) +{ +buf[0] = DDCMP_ENQ; /* Control Message */ +buf[1] = type; /* STACK type */ +buf[2] = (subtype & 0x3f) | (flags << 6); + /* STACKSUB type and flags */ +buf[3] = rcvr; /* RCVR */ +buf[4] = sndr; /* SNDR */ +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); +} + +static void ddcmp_build_ack_packet (uint8 *buf, uint8 ack, uint8 flags) +{ +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); +} + +static void ddcmp_build_nak_packet (uint8 *buf, uint8 reason, uint8 nack, uint8 flags) +{ +ddcmp_build_control_packet (buf, DDCMP_CTL_NAK, reason, flags, 0, nack); +} + +static t_stat ddcmp_tmxr_put_nak_packet_ln (TMLN *lp, uint8 *buf, uint8 reason, uint8 nack, uint8 flags) +{ +return ddcmp_tmxr_put_control_packet_ln (lp, buf, DDCMP_CTL_NAK, reason, flags, 0, nack); +} + +static void ddcmp_build_rep_packet (uint8 *buf, uint8 ack, uint8 flags) +{ +ddcmp_build_control_packet (buf, DDCMP_CTL_REP, 0, flags, ack, 0); +} + +static t_stat ddcmp_tmxr_put_rep_packet_ln (TMLN *lp, uint8 *buf, uint8 ack, uint8 flags) +{ +return ddcmp_tmxr_put_control_packet_ln (lp, buf, DDCMP_CTL_REP, 0, flags, ack, 0); +} + +static void ddcmp_build_start_packet (uint8 *buf) +{ +ddcmp_build_control_packet (buf, DDCMP_CTL_STRT, 0, DDCMP_FLAG_SELECT|DDCMP_FLAG_QSYNC, 0, 0); +} + +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); +} + +static void ddcmp_build_start_ack_packet (uint8 *buf) +{ +ddcmp_build_control_packet (buf, DDCMP_CTL_STACK, 0, DDCMP_FLAG_SELECT|DDCMP_FLAG_QSYNC, 0, 0); +} + +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); +} + #endif /* PDP11_DDCMP_H_ */ diff --git a/PDP11/pdp11_defs.h b/PDP11/pdp11_defs.h index c3a36348..796eebfc 100644 --- a/PDP11/pdp11_defs.h +++ b/PDP11/pdp11_defs.h @@ -485,6 +485,8 @@ typedef struct { #define DLX_LINES 16 /* max # of KL11/DL11's */ #define DCX_LINES 16 /* max # of DC11's */ #define DUP_LINES 8 /* max # of DUP11/DPV11's */ +#define KMC_UNITS 2 /* max # of KMC11s */ +#define INITIAL_KMCS 0 /* Number to initially enable */ #define MT_MAXFR (1 << 16) /* magtape max rec */ #define DIB_MAX 100 /* max DIBs */ @@ -607,6 +609,8 @@ typedef struct pdp_dib DIB; #define INT_V_DMCTX 20 #define INT_V_DUPRX 21 #define INT_V_DUPTX 22 +#define INT_V_KMCA 23 +#define INT_V_KMCB 24 #define INT_V_PIR4 0 /* BR4 */ #define INT_V_TTI 1 @@ -661,6 +665,8 @@ typedef struct pdp_dib DIB; #define INT_RS (1u << INT_V_RS) #define INT_DMCRX (1u << INT_V_DMCRX) #define INT_DMCTX (1u << INT_V_DMCTX) +#define INT_KMCA (1u << INT_V_KMCA) +#define INT_KMCB (1u << INT_V_KMCB) #define INT_DUPRX (1u << INT_V_DUPRX) #define INT_DUPTX (1u << INT_V_DUPTX) #define INT_PIR4 (1u << INT_V_PIR4) @@ -717,6 +723,8 @@ typedef struct pdp_dib DIB; #define IPL_RS 5 #define IPL_DMCRX 5 #define IPL_DMCTX 5 +#define IPL_KMCA 5 +#define IPL_KMCB 5 #define IPL_DUPRX 5 #define IPL_DUPTX 5 #define IPL_PTR 4 diff --git a/PDP11/pdp11_dmc.c b/PDP11/pdp11_dmc.c index d01cdf56..47a1f97c 100644 --- a/PDP11/pdp11_dmc.c +++ b/PDP11/pdp11_dmc.c @@ -1,4 +1,4 @@ -/* pdp11_dmc.c: DMC11 Emulation +/* pdp11_dmc.c: DMC11/DMR11/DMP11/DMV11 Emulation ------------------------------------------------------------------------------ Copyright (c) 2011, Robert M. A. Jarratt @@ -34,35 +34,31 @@ 23-Jan-13 RJ Clock co-scheduling move to generic framework (from Mark Pizzolato) 21-Jan-13 RJ Added help. 15-Jan-13 RJ Contribution from Paul Koning: - Support for RSTS using the ROM INPUT (ROM I) command to get the DMC11 to report DSR status. - Don't accept any data from the peer until a buffer has been made available. + 04-Aug-13 MP Massive Rewrite/Restructure to implement the DDCMP wire + protocol for interoperation with other sync devices (DUP11, + KDP, etc.) - Also added shadow CSRs. The code was using the CSRs to check - the command being executed, but the driver could end up - changing the bits, so a shadow set is used to do this. ------------------------------------------------------------------------------ -I/O is done through sockets so that the remote system can be on the same host machine. The device starts polling for -incoming connections when it receives its first read buffer. The device opens the connection for writing when it receives -the first write buffer. +I/O is done through sockets so that the remote system can be on the same +host machine. The device starts polling for incoming connections when it +receives its first read buffer. The device opens the connection for writing +when it receives the first write buffer. -Transmit and receive buffers are added to their respective queues and the polling method in dmc_svc() checks for input -and sends any output. +Transmit and receive buffers are added to their respective queues and the +polling method in dmc_poll_svc() checks for input and sends any output. -On the wire the format is a 2-byte block length followed by that number of bytes. Some of the diagnostics expect to receive -the same number of bytes in a buffer as were sent by the other end. Using sockets without a block length can cause the -buffers to coalesce and then the buffer lengths in the diagnostics fail. The block length is always sent in network byte -order. +Tested with two diagnostics. To run the diagnostics set the default +directory to SYS$MAINTENANCE, run ESSAA and then configure it for the +DMC-11 with the following commands: -Tested with two diagnostics. To run the diagnostics set the default directory to SYS$MAINTENANCE, run ESSAA and then -configure it for the DMC-11 with the following commands: - -The above commands can be put into a COM file in SYS$MAINTENANCE (works on VMS 3.0 but not 4.6, not sure why). +The above commands can be put into a COM file in SYS$MAINTENANCE (works +on VMS 3.0 but not 4.6, not sure why). ATT DW780 SBI DW0 3 4 ATT DMC11 DW0 XMA0 760070 300 5 @@ -70,162 +66,688 @@ SELECT XMA0 (if putting these into a COM file to be executed by ESSAA add a "DS> " prefix) -The first is EVDCA which takes no parameters. Invoke it with the command R EVDCA. This diagnostic uses the DMC-11 loopback -functionality and the transmit port is not used when LU LOOP is enabled. Seems to work only under later versions of VMS -such as 4.6, does not work on 3.0. +The first is EVDCA which takes no parameters. Invoke it with the command +R EVDCA. This diagnostic uses the DMC-11 loopback functionality and the +transmit port is not used when LU LOOP is enabled. Seems to work only under +later versions of VMS such as 4.6, does not work on 3.0. It does not work +under VMS 3.x since in that environment, no receive buffers are ever made +available to the device while testing. -The second is EVDMC, invoke this with the command R EVDMC. For this I used the following commands inside the diagnostic: +The second is EVDMC, invoke this with the command R EVDMC. For this I used +the following commands inside the diagnostic: RUN MODE=TRAN on one machine -RUN MODE=REC on the other (unless the one instance is configured with the ports looping back). +RUN MODE=REC on the other -You can add /PASS=n to the above commands to get the diagnostic to send and receive more buffers. +or using loopback mode: + +SET TRANSMIT=CCITT/SIZE=25/COPY=5 +SET EXPECT=CCITT/SIZE=25/COPY=5 +RUN MODE=ACTIVE/LOOP=INT/STATUS/PASS=3 + +You can add /PASS=n to the above commands to get the diagnostic to send and +receive more buffers. The other test was to configure DECnet on VMS 4.6 and do SET HOST. */ -// TODO: Avoid need for manifests and newest runtime, compile with 2003 -// TODO: Investigate line number and set parameters at the unit level (?) -// TODO: Multipoint. In this case perhaps don't need transmit port, allow all lines to connect to port on control node. -// TODO: Show active connections like DZ does, for multipoint. // TODO: Test MOP. -// TODO: Implement actual DDCMP protocol and run over UDP. -// TODO: Allow NCP SHOW COUNTERS to work (think this is the base address thing). Since fixing how I get the addresses this should work now. -#include -#include +#if defined (VM_PDP10) /* PDP10 version */ +#include "pdp10_defs.h" -#include "pdp11_dmc.h" +#if !defined(DMP_NUMDEVICE) +#define DMP_NUMDEVICE 1 /* Minimum number for array size DMP-11/DMV-11 devices */ +#endif -#define POLL 1000 -#define TRACE_BYTES_PER_LINE 16 +#elif defined (VM_VAX) /* VAX version */ +#include "vax_defs.h" + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +#endif + +#include +#include "sim_tmxr.h" +#include "pdp11_ddcmp.h" + +#define DMC_CONNECT_POLL 2 /* Seconds */ + +extern int32 IREQ (HLVL); +extern int32 tmxr_poll; /* calibrated delay */ +extern int32 clk_tps; /* clock ticks per second */ +extern int32 tmr_poll; /* instructions per tick */ + +#if !defined(DMC_NUMDEVICE) +#define DMC_NUMDEVICE 8 /* default MAX # DMC-11 devices */ +#endif + +#if !defined(DMP_NUMDEVICE) +#define DMP_NUMDEVICE 8 /* default MAX # DMP-11/DMV-11 devices */ +#endif + +#define DMC_RDX 8 + + +/* DMC/DMR register SEL0 */ +#define DMC_SEL0_V_ITYPE 0 +#define DMC_SEL0_S_ITYPE 4 +#define DMC_SEL0_M_ITYPE (((1<queue->size > 0) && (pred->queue->count >= pred->queue->size)) + return FALSE; +assert (entry->queue == NULL); +entry->next = pred->next; +entry->prev = pred; +entry->queue = pred->queue; +pred->next->prev = entry; +pred->next = entry; +++pred->queue->count; +return TRUE; +} + +/* Remove entry from queue. + * Decrement count. + * To remove from head of queue, specify head.next. + * To remove form tail of queue, specify head.prev. + * + * returns NULL if queue is empty. + */ + +static void *remqueue (QH *entry) +{ +if (entry->queue->count <= 0) + return NULL; +entry->prev->next = entry->next; +entry->next->prev = entry->prev; +--entry->queue->count; +entry->next = entry->prev = NULL; +entry->queue = NULL; +return (void *)entry; +} + +/* Initialize a queue to empty. + * + * Optionally, adds a list of elements to the queue. + * max, list and size are only used if list is non-NULL. + * + * Convenience macros: + * MAX_LIST_SIZE(q) specifies max, list and size for an array of elements q + * INIT_HDR_ONLY provides placeholders for these arguments when only the + * header and count are to be initialized. + */ + +#define DIM(x) (sizeof(x)/sizeof((x)[0])) +/* Convenience for initqueue() calls */ +# define MAX_LIST_SIZE(q) DIM(q), (q), sizeof(q[0]) +# define INIT_HDR_ONLY 0, NULL, 0 + +static void initqueue (QH *head, struct buffer_queue *queue, size_t max, void *list, size_t size) +{ +head->next = head->prev = head; +head->queue = queue; +head->queue->count = 0; +head->queue->size = max; +if (list == NULL) + return; +while (insqueue ((QH *)list, head->prev)) + list = (QH *)(((char *)list)+size); +return; +} + +typedef enum { + Receive, /* Receive Buffer */ + TransmitData, /* Transmit Buffer */ + TransmitControl /* Transmit Control Packet */ + } BufferType; + +typedef struct buffer { + QH hdr; /* queue linkage */ + BufferType type; /* 0 = Receive Buffer, 1 = Transmit Buffer */ + uint32 address; /* unibus address of the buffer (or 0 for DDCMP control messages) */ uint16 count; /* size of the buffer passed to the device by the driver */ - uint16 actual_block_len; /* actual length of the received block */ uint8 *transfer_buffer; /* the buffer into which data is received or from which it is transmitted*/ - int block_len_bytes_read; /* the number of bytes read so far for the block length */ - int actual_bytes_transferred; /* the number of bytes from the actual block that have been read or written so far*/ - struct buffer *next; /* next buffer in the queue */ - BufferState state; /* state of this buffer */ - int is_loopback; /* loopback was requested when this buffer was queued */ -} BUFFER; + int actual_bytes_transferred;/* the number of bytes from the actual block that have been read or written */ + uint32 buffer_return_time;/* time to return buffer to host */ + } BUFFER; -typedef struct -{ - char * name; - BUFFER queue[BUFFER_QUEUE_SIZE]; - int head; - int tail; - int count; - struct dmc_controller *controller; /* back pointer to the containing controller */ -} BUFFER_QUEUE; +typedef enum { + Halt, /* initially */ + IStart, /* Initiating Start */ + AStart, /* Acknowledging Start */ + Run, /* Start ACK received */ + Maintenance,/* off-line maintenance messages */ + All /* Not settable, but match for any state in state table */ + } DDCMP_LinkState; -typedef struct -{ - int started; - clock_t start_time; - clock_t cumulative_time; -} TIMER; +typedef struct { + uint8 R; /* number of the highest sequential data message received . + Sent in the RESP field of data messages, ACK messages, + and NAK messages as acknowledgment to the other station. */ + uint8 N; /* Number of the highest sequential data message + transmitted. Sent in the NUM field of REP + messages. N is the number assigned to the last + user transmit request which has been transmitted + (sent in the NUM field of that data message).*/ + uint8 A; /* Number of the highest sequential data message acknowledged. + Received in the RESP field of data messages, ACK messages, + and NAK messages.*/ + uint8 T; /* Number of the next data message to be transmitted. + When sending new data messages T will have the + value N+l. When retransmitting T will be set back + to A+l and will advance to N+l.*/ + uint8 X; /* Number of the last data message that has been + transmitted. When a new data message has been + completely transmitted X will have the value N. + When retransmitting and receiving acknowledgments + asynchronously with respect to transmission, X will + have some value less than or equal to N. */ + t_bool SACK; /* Send ACK flag. This flag is set when either R + is incremented, meaning a new sequential data + message has been received which requires an ACK + reply, or a REP message is received which requires + an ACK reply. The SACK flag is cleared when + sending either a DATA message with the latest RESP + field information, an ACK with the latest RESP + field information, or when the SNAK flag is set. */ + /* + The SACK and SNAK flags are mutually exclusive. At most one + will be set at a given time. The events that set the SACK + flag, R is increment~d or a REP is received requiring an ACK, + also clear the SNAK flag. Similarly, the events that set the + SNAK flag, reasons for send ing a NAK (see 5.3.7), also clear + the SACK flag. Setting or clearing a flag that is already set + or cleared respectively has no effect. For the SNAK flag, a + reason variable (or field) is also maintained which is + overwritten with the latest NAK error reason. + Whenever the SNAK flag is set the NAK reason variable is set + to the reason for the NAK. */ + t_bool SNAK; /* Send NAK flag. This flag is set when a receive + error occurs that requires a NAK reply. It is + cleared when a NAK message is sent with the + latest RESP information, or when the SACK flag + is set.*/ + t_bool SREP; /* Send REP flag. This flag is set when a reply + timer expires in the running state and a REP + should be sent. It is independent of the SACK + and SNAK flags.*/ + uint8 *rcv_pkt; /* Current Rcvd Packet buffer */ + uint16 rcv_pkt_size; /* Current Rcvd Packet size */ + BUFFER *xmt_buffer; /* Current Transmit Buffer */ + BUFFER *xmt_done_buffer;/* Just Completed Transmit Buffer */ + uint8 nak_reason; /* */ + DDCMP_LinkState state; /* Current State */ + t_bool TimerRunning; /* Timer Running Flag */ + t_bool TimeRemaining; /* Seconds remaining before timeout (when timer running) */ + t_bool Scanning; /* Event Scanning in progress */ + uint32 ScanningEvents; /* Event Mask while scanning */ + t_bool RecurseScan; /* Scan was attempted while scanning */ + uint32 RecurseEventMask;/* Mask for recursive Scan */ +#define DDCMP_EVENT_XMIT_DONE 0x001 +#define DDCMP_EVENT_PKT_RCVD 0x002 +#define DDCMP_EVENT_TIMER 0x004 +#define DDCMP_EVENT_MAINTMODE 0x008 + } DDCMP; -typedef struct -{ - TIMER between_polls_timer; - TIMER poll_timer; - uint32 poll_count; +typedef struct control { + struct control *next; /* Link */ + uint16 sel6; /* Control Out Status Flags */ + } CONTROL_OUT; -} UNIT_STATS; - -typedef enum -{ +typedef enum { DMC, DMR, DMP -} DEVTYPE; + } DEVTYPE; -struct dmc_controller { +typedef struct dmc_controller { CSRS *csrs; - CSRS *shadow_csrs; DEVICE *device; + UNIT *unit; + int index; /* Index in controller array */ ControllerState state; - TransferState transfer_state; /* current transfer state (type of transfer) */ - int transfer_type; - int transfer_in_io; // remembers IN I/O setting at start of input transfer as host changes it during transfer! - LINE *line; - BUFFER_QUEUE *receive_queue; - BUFFER_QUEUE *transmit_queue; - UNIT_STATS *stats; - SOCKET master_socket; - int32 connect_poll_interval; + TransferState transfer_state; /* current transfer state (type of transfer) */ + DDCMP link; + int transfer_type; /* Input Command setting at start of input transfer as host changes it during transfer! */ + TMLN *line; + BUFFER_QUEUE *rcv_queue; /* Receive Buffer Queue */ + BUFFER_QUEUE *completion_queue; /* Transmit and Recieve Buffers waiting to pass to driver */ + BUFFER_QUEUE *xmt_queue; /* Control and/or Data packets pending transmission */ + BUFFER_QUEUE *ack_wait_queue; /* Data packets awaiting acknowledgement */ + BUFFER_QUEUE *free_queue; /* Unused Buffer Queue */ + BUFFER **buffers; /* Buffers */ + CONTROL_OUT *control_out; DEVTYPE dev_type; - uint32 rxi; - uint32 txi; + uint32 in_int; + uint32 out_int; + uint32 dmc_wr_delay; + uint32 *baseaddr; + uint16 *basesize; + uint8 *modem; uint32 buffers_received_from_net; uint32 buffers_transmitted_to_net; uint32 receive_buffer_output_transfers_completed; uint32 transmit_buffer_output_transfers_completed; uint32 receive_buffer_input_transfers_completed; uint32 transmit_buffer_input_transfers_completed; -}; + uint32 control_out_operations_completed; + uint32 ddcmp_control_packets_received; + uint32 ddcmp_control_packets_sent; + uint32 byte_wait; /* rcv/xmt byte delay */ + } CTLR; -typedef struct dmc_controller CTLR; +/* + DDCMP implementation follows the DDCMP protocol specification documented in the + "DECNET DIGITAL NETWORK ARCHITECTURE Digital Data Communications Message Protocol" + Version 4.0, March 1, 1978. + + available on http://bitsavers.org/pdf/dec/decnet/AA-D599A-TC_DDCMP4.0_Mar78.pdf -t_stat dmc_rd(int32* data, int32 PA, int32 access); -t_stat dmc_wr(int32 data, int32 PA, int32 access); -t_stat dmc_svc(UNIT * uptr); + */ + +typedef void (*DDCMP_LinkAction_Routine)(CTLR *controller); + +typedef t_bool (*DDCMP_Condition_Routine)(CTLR *controller); + +t_bool ddcmp_UserHalt (CTLR *controller); +t_bool ddcmp_UserStartup (CTLR *controller); +t_bool ddcmp_UserMaintenanceMode (CTLR *controller); +t_bool ddcmp_ReceiveStack (CTLR *controller); +t_bool ddcmp_ReceiveStrt (CTLR *controller); +t_bool ddcmp_TimerRunning (CTLR *controller); +t_bool ddcmp_TimerNotRunning (CTLR *controller); +t_bool ddcmp_TimerExpired (CTLR *controller); +t_bool ddcmp_ReceiveMaintMessage (CTLR *controller); +t_bool ddcmp_ReceiveAck (CTLR *controller); +t_bool ddcmp_ReceiveNak (CTLR *controller); +t_bool ddcmp_ReceiveRep (CTLR *controller); +t_bool ddcmp_NUMEqRplus1 (CTLR *controller); /* (NUM == R+1) */ +t_bool ddcmp_NUMGtRplus1 (CTLR *controller); /* (NUM > R+1) */ +t_bool ddcmp_ReceiveDataMsg (CTLR *controller); /* Receive Data Message */ +t_bool ddcmp_ReceiveMaintMsg (CTLR *controller); /* Receive Maintenance Message */ +t_bool ddcmp_ALtRESPleN (CTLR *controller); /* (A < RESP <= N) */ +t_bool ddcmp_ALeRESPleN (CTLR *controller); /* (A <= RESP <= N) */ +t_bool ddcmp_RESPleAOrRESPgtN (CTLR *controller); /* (RESP <= A) OR (RESP > N) */ +t_bool ddcmp_TltNplus1 (CTLR *controller); /* T < N + 1 */ +t_bool ddcmp_TeqNplus1 (CTLR *controller); /* T == N + 1 */ +t_bool ddcmp_ReceiveMessageError (CTLR *controller); +t_bool ddcmp_NumEqR (CTLR *controller); /* (NUM == R) */ +t_bool ddcmp_NumNeR (CTLR *controller); /* (NUM != R) */ +t_bool ddcmp_TransmitterIdle (CTLR *controller); +t_bool ddcmp_TramsmitterBusy (CTLR *controller); +t_bool ddcmp_SACKisSet (CTLR *controller); +t_bool ddcmp_SACKisClear (CTLR *controller); +t_bool ddcmp_SNAKisSet (CTLR *controller); +t_bool ddcmp_SNAKisClear (CTLR *controller); +t_bool ddcmp_SREPisSet (CTLR *controller); +t_bool ddcmp_SREPisClear (CTLR *controller); +t_bool ddcmp_UserSendMessage (CTLR *controller); +t_bool ddcmp_LineConnected (CTLR *controller); +t_bool ddcmp_LineDisconnected (CTLR *controller); +t_bool ddcmp_DataMessageSent (CTLR *controller); +t_bool ddcmp_REPMessageSent (CTLR *controller); + +void ddcmp_StartTimer (CTLR *controller); +void ddcmp_StopTimer (CTLR *controller); +void ddcmp_ResetVariables (CTLR *controller); +void ddcmp_SendStrt (CTLR *controller); +void ddcmp_SendStack (CTLR *controller); +void ddcmp_SendAck (CTLR *controller); +void ddcmp_SendNak (CTLR *controller); +void ddcmp_SendRep (CTLR *controller); +void ddcmp_SetSACK (CTLR *controller); +void ddcmp_ClearSACK (CTLR *controller); +void ddcmp_SetSNAK (CTLR *controller); +void ddcmp_ClearSNAK (CTLR *controller); +void ddcmp_SetSREP (CTLR *controller); +void ddcmp_ClearSREP (CTLR *controller); +void ddcmp_IncrementR (CTLR *controller); +void ddcmp_SetAeqRESP (CTLR *controller); +void ddcmp_SetTequalAplus1 (CTLR *controller); +void ddcmp_IncrementT (CTLR *controller); +void ddcmp_SetNAKreason3 (CTLR *controller); +void ddcmp_SetNAKreason2 (CTLR *controller); +void ddcmp_NAKMissingPackets (CTLR *controller); +void ddcmp_IfTleAthenSetTeqAplus1 (CTLR *controller); +void ddcmp_IfAltXthenStartTimer (CTLR *controller); +void ddcmp_IfAgeXthenStopTimer (CTLR *controller); +void ddcmp_Ignore (CTLR *controller); +void ddcmp_GiveBufferToUser (CTLR *controller); +void ddcmp_CompleteAckedTransmits (CTLR *controller); +void ddcmp_ReTransmitMessageT (CTLR *controller); +void ddcmp_NotifyDisconnect (CTLR *controller); +void ddcmp_NotifyStartRcvd (CTLR *controller); +void ddcmp_NotifyMaintRcvd (CTLR *controller); +void ddcmp_SendDataMessage (CTLR *controller); +void ddcmp_SendMaintMessage (CTLR *controller); +void ddcmp_SetXSetNUM (CTLR *controller); + +typedef struct _ddcmp_state_table +{ +int RuleNumber; +DDCMP_LinkState State; +DDCMP_Condition_Routine Conditions[10]; +DDCMP_LinkState NewState; +DDCMP_LinkAction_Routine Actions[10]; +} DDCMP_STATETABLE; + +DDCMP_STATETABLE DDCMP_TABLE[] = { + { 0, All, {ddcmp_UserHalt}, Halt, {ddcmp_StopTimer}}, + { 1, Halt, {ddcmp_UserStartup, + ddcmp_LineConnected}, IStart, {ddcmp_ResetVariables, + ddcmp_SendStrt, + ddcmp_StopTimer}}, + { 2, Halt, {ddcmp_UserMaintenanceMode}, Maintenance, {ddcmp_ResetVariables}}, + { 3, Halt, {ddcmp_ReceiveMaintMsg}, Maintenance, {ddcmp_ResetVariables, + ddcmp_NotifyMaintRcvd}}, + { 4, IStart, {ddcmp_TimerNotRunning}, IStart, {ddcmp_StartTimer}}, + { 5, IStart, {ddcmp_ReceiveStack}, Run, {ddcmp_SetSACK, + ddcmp_StopTimer}}, + { 6, IStart, {ddcmp_ReceiveStrt}, AStart, {ddcmp_SendStack, + ddcmp_StartTimer}}, + { 7, IStart, {ddcmp_TimerExpired}, IStart, {ddcmp_SendStrt, + ddcmp_StartTimer}}, + { 8, IStart, {ddcmp_ReceiveMaintMsg}, Maintenance, {ddcmp_ResetVariables, + ddcmp_NotifyMaintRcvd}}, + { 9, IStart, {ddcmp_ReceiveMessageError}, IStart, {ddcmp_Ignore}}, + {10, IStart, {ddcmp_UserSendMessage, + ddcmp_TimerNotRunning}, IStart, {ddcmp_StartTimer}}, + {11, IStart, {ddcmp_LineDisconnected}, Halt, {ddcmp_StopTimer}}, + {12, AStart, {ddcmp_LineDisconnected}, Halt, {ddcmp_StopTimer}}, + {13, AStart, {ddcmp_ReceiveAck}, Run, {ddcmp_StopTimer}}, + {14, AStart, {ddcmp_ReceiveDataMsg}, Run, {ddcmp_StopTimer}}, + {15, AStart, {ddcmp_ReceiveStack}, Run, {ddcmp_SetSACK, + ddcmp_StopTimer}}, + {16, AStart, {ddcmp_TimerExpired}, AStart, {ddcmp_SendStack, + ddcmp_StartTimer}}, + {17, AStart, {ddcmp_ReceiveMaintMsg}, Maintenance, {ddcmp_ResetVariables, + ddcmp_NotifyMaintRcvd}}, + {18, AStart, {ddcmp_ReceiveMessageError}, AStart, {ddcmp_Ignore}}, + {19, Run, {ddcmp_LineDisconnected}, Halt, {ddcmp_StopTimer, + ddcmp_NotifyDisconnect, + ddcmp_NotifyStartRcvd}}, + {20, Run, {ddcmp_ReceiveStrt}, Halt, {ddcmp_NotifyStartRcvd}}, + {21, Run, {ddcmp_ReceiveMaintMsg}, Maintenance, {ddcmp_ResetVariables, + ddcmp_NotifyMaintRcvd}}, + {22, Run, {ddcmp_ReceiveStack}, Run, {ddcmp_SetSACK}}, + {23, Run, {ddcmp_ReceiveDataMsg, + ddcmp_NUMGtRplus1}, Run, {ddcmp_NAKMissingPackets}}, + {24, Run, {ddcmp_ReceiveDataMsg, + ddcmp_NUMEqRplus1}, Run, {ddcmp_GiveBufferToUser}}, + {25, Run, {ddcmp_ReceiveMessageError}, Run, {ddcmp_SetSNAK}}, + {26, Run, {ddcmp_ReceiveRep, + ddcmp_NumEqR}, Run, {ddcmp_SetSACK}}, + {27, Run, {ddcmp_ReceiveRep, + ddcmp_NumNeR}, Run, {ddcmp_SetSNAK, + ddcmp_SetNAKreason3}}, + {28, Run, {ddcmp_ReceiveDataMsg, + ddcmp_ALtRESPleN}, Run, {ddcmp_CompleteAckedTransmits, + ddcmp_SetAeqRESP, + ddcmp_IfTleAthenSetTeqAplus1, + ddcmp_IfAgeXthenStopTimer}}, + {29, Run, {ddcmp_ReceiveAck, + ddcmp_ALtRESPleN}, Run, {ddcmp_CompleteAckedTransmits, + ddcmp_SetAeqRESP, + ddcmp_IfTleAthenSetTeqAplus1, + ddcmp_IfAgeXthenStopTimer}}, + {30, Run, {ddcmp_ReceiveAck, + ddcmp_RESPleAOrRESPgtN}, Run, {ddcmp_Ignore}}, + {31, Run, {ddcmp_ReceiveDataMsg, + ddcmp_RESPleAOrRESPgtN}, Run, {ddcmp_Ignore}}, + {32, Run, {ddcmp_ReceiveNak, + ddcmp_ALeRESPleN}, Run, {ddcmp_CompleteAckedTransmits, + ddcmp_SetAeqRESP, + ddcmp_SetTequalAplus1, + ddcmp_StopTimer}}, + {33, Run, {ddcmp_ReceiveNak, + ddcmp_RESPleAOrRESPgtN}, Run, {ddcmp_Ignore}}, + {34, Run, {ddcmp_TimerExpired}, Run, {ddcmp_SetSREP}}, + {35, Run, {ddcmp_TransmitterIdle, + ddcmp_SNAKisSet}, Run, {ddcmp_SendNak, + ddcmp_ClearSNAK}}, + {36, Run, {ddcmp_TransmitterIdle, + ddcmp_SNAKisClear, + ddcmp_SREPisSet}, Run, {ddcmp_SendRep, + ddcmp_ClearSREP}}, + {37, Run, {ddcmp_TransmitterIdle, + ddcmp_SNAKisClear, + ddcmp_SREPisClear, + ddcmp_TltNplus1}, Run, {ddcmp_ReTransmitMessageT, + ddcmp_IncrementT, + ddcmp_ClearSACK}}, + {38, Run, {ddcmp_UserSendMessage, + ddcmp_TeqNplus1, + ddcmp_TransmitterIdle, + ddcmp_SNAKisClear, + ddcmp_SREPisClear}, Run, {ddcmp_SendDataMessage, + ddcmp_ClearSACK}}, + {39, Run, {ddcmp_TransmitterIdle, + ddcmp_SNAKisClear, + ddcmp_SREPisClear, + ddcmp_SACKisSet, + ddcmp_TeqNplus1}, Run, {ddcmp_SendAck, + ddcmp_ClearSACK}}, + {40, Run, {ddcmp_DataMessageSent}, Run, {ddcmp_SetXSetNUM, + ddcmp_IfAltXthenStartTimer, + ddcmp_IfAgeXthenStopTimer}}, + {41, Run, {ddcmp_REPMessageSent}, Run, {ddcmp_StartTimer}}, + {42, Maintenance, {ddcmp_ReceiveMaintMsg}, Maintenance, {ddcmp_GiveBufferToUser}}, + {43, Maintenance, {ddcmp_UserSendMessage, + ddcmp_TransmitterIdle}, Maintenance, {ddcmp_SendMaintMessage}}, + {44, All} /* End of Table */ + }; + + +#define ctlr up7 /* Unit back pointer to controller */ + +void ddcmp_dispatch (CTLR *controller, uint32 EventMask); + + +t_stat dmc_rd (int32* data, int32 PA, int32 access); +t_stat dmc_wr (int32 data, int32 PA, int32 access); +t_stat dmc_svc (UNIT * uptr); +t_stat dmc_poll_svc (UNIT * uptr); +t_stat dmc_timer_svc (UNIT * uptr); t_stat dmc_reset (DEVICE * dptr); t_stat dmc_attach (UNIT * uptr, char * cptr); t_stat dmc_detach (UNIT * uptr); -int32 dmc_rxint (void); -int32 dmc_txint (void); +int32 dmc_ininta (void); +int32 dmc_outinta (void); +t_stat dmc_setnumdevices (UNIT *uptr, int32 val, char *cptr, void *desc); +t_stat dmc_shownumdevices (FILE *st, UNIT *uptr, int32 val, void *desc); 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); @@ -234,2214 +756,2748 @@ t_stat dmc_settype (UNIT* uptr, int32 val, char* cptr, void* desc); t_stat dmc_showtype (FILE* st, UNIT* uptr, int32 val, void* desc); t_stat dmc_setstats (UNIT* uptr, int32 val, char* cptr, void* desc); t_stat dmc_showstats (FILE* st, UNIT* uptr, int32 val, void* desc); +t_stat dmc_showqueues (FILE* st, UNIT* uptr, int32 val, void* desc); t_stat dmc_setconnectpoll (UNIT* uptr, int32 val, char* cptr, void* desc); t_stat dmc_showconnectpoll (FILE* st, UNIT* uptr, int32 val, void* desc); -t_stat dmc_setlinemode (UNIT* uptr, int32 val, char* cptr, void* desc); -t_stat dmc_showlinemode (FILE* st, UNIT* uptr, int32 val, void* desc); +t_stat dmc_showddcmp (FILE* st, UNIT* uptr, int32 val, void* desc); t_stat dmc_help (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); char *dmc_description (DEVICE *dptr); char *dmp_description (DEVICE *dptr); -int dmc_is_attached(UNIT* uptr); -int dmc_is_dmc(CTLR *controller); -int dmc_is_rqi_set(CTLR *controller); -int dmc_is_rdyi_set(CTLR *controller); -int dmc_is_iei_set(CTLR *controller); -int dmc_is_ieo_set(CTLR *controller); -void dmc_process_command(CTLR *controller); -int dmc_buffer_fill_receive_buffers(CTLR *controller); -void dmc_start_transfer_receive_buffer(CTLR *controller); -int dmc_buffer_send_transmit_buffers(CTLR *controller); -void dmc_buffer_queue_init(CTLR *controller, BUFFER_QUEUE *q, char *name); -void dmc_buffer_queue_init_all(CTLR *controller); -BUFFER *dmc_buffer_queue_head(BUFFER_QUEUE *q); -int dmc_buffer_queue_full(BUFFER_QUEUE *q); -void dmc_buffer_queue_get_stats(BUFFER_QUEUE *q, int *available, int *contains_data, int *transfer_in_progress); -void dmc_start_transfer_transmit_buffer(CTLR *controller); -void dmc_error_and_close_socket(CTLR *controller, char *format); -void dmc_close_socket(CTLR *controller, char *reason); -void dmc_close_receive(CTLR *controller, char *reason, char *from); -void dmc_close_transmit(CTLR *controller, char *reason); -int dmc_get_socket(CTLR *controller, int forRead); -int dmc_get_receive_socket(CTLR *controller, int forRead); -int dmc_get_transmit_socket(CTLR *controller, int is_loopback, int forRead); -void dmc_line_update_speed_stats(LINE *line); +int dmc_is_attached (UNIT* uptr); +int dmc_is_dmc (CTLR *controller); +int dmc_is_rqi_set (CTLR *controller); +int dmc_is_rdyi_set (CTLR *controller); +int dmc_is_iei_set (CTLR *controller); +int dmc_is_ieo_set (CTLR *controller); +uint32 dmc_get_addr (CTLR *controller); +void dmc_set_addr (CTLR *controller, uint32 addr); +uint16 dmc_get_count (CTLR *controller); +void dmc_set_count (CTLR *controller, uint16 count); +uint8 dmc_get_modem (CTLR *controller); +void dmc_set_modem_dtr (CTLR *controller); +void dmc_clr_modem_dtr (CTLR *controller); +void dmc_process_command (CTLR *controller); +t_bool dmc_buffer_fill_receive_buffers (CTLR *controller); +void dmc_start_transfer_buffer (CTLR *controller); +void dmc_buffer_queue_init (CTLR *controller, BUFFER_QUEUE *q, char *name, size_t size, BUFFER *buffers); +void dmc_buffer_queue_init_all (CTLR *controller); +BUFFER *dmc_buffer_queue_head (BUFFER_QUEUE *q); +BUFFER *dmc_buffer_allocate (CTLR *controller); +t_bool dmc_transmit_queue_empty (CTLR *controller); +void dmc_ddcmp_start_transmitter (CTLR *controller); +void dmc_queue_control_out (CTLR *controller, uint16 sel6); + +/* debugging bitmaps */ +#define DBG_TRC 0x0001 /* trace routine calls */ +#define DBG_REG 0x0002 /* trace programatic read/write registers */ +#define DBG_RGC 0x0004 /* internal read/write registers changes */ +#define DBG_WRN 0x0008 /* display warnings */ +#define DBG_INF 0x0010 /* display informational messages (high level trace) */ +#define DBG_DTS (DDCMP_DBG_PXMT|DDCMP_DBG_PRCV) /* display data summary */ +#define DBG_DAT (DBG_DTS|DDCMP_DBG_PDAT) /* display data buffer contents */ +#define DBG_MDM 0x0040 /* modem related transitions */ +#define DBG_CON TMXR_DBG_CON /* display socket open/close, connection establishment */ +#define DBG_INT 0x0080 /* display interrupt activites */ + DEBTAB dmc_debug[] = { - {"TRACE", DBG_TRC}, - {"WARN", DBG_WRN}, - {"REG", DBG_REG}, - {"INFO", DBG_INF}, - {"DATA", DBG_DAT}, - {"DATASUM",DBG_DTS}, - {"SOCKET", DBG_SOK}, + {"TRACE", DBG_TRC}, + {"WARN", DBG_WRN}, + {"REG", DBG_REG}, + {"INTREG", DBG_RGC}, + {"INFO", DBG_INF}, + {"DATA", DBG_DAT}, + {"DATASUM", DBG_DTS}, + {"MODEM", DBG_MDM}, {"CONNECT", DBG_CON}, + {"INT", DBG_INT}, {0} -}; + }; -UNIT dmc0_unit = { UDATA (&dmc_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 0) }; -UNIT dmc1_unit = { UDATA (&dmc_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 0) }; -UNIT dmc2_unit = { UDATA (&dmc_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 0) }; -UNIT dmc3_unit = { UDATA (&dmc_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 0) }; +UNIT dmc_units[DMC_NUMDEVICE+2]; /* One per device plus an I/O polling unit and a timing unit */ -UNIT dmpa_unit = { UDATA (&dmc_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 0) }; +UNIT dmc_unit_template = { UDATA (&dmc_svc, UNIT_ATTABLE, 0) }; +UNIT dmc_poll_unit_template = { UDATA (&dmc_poll_svc, UNIT_DIS, 0) }; +UNIT dmc_timer_unit_template = { UDATA (&dmc_timer_svc, UNIT_DIS, 0) }; + +UNIT dmp_units[DMP_NUMDEVICE+2]; /* One per device plus an I/O polling unit and a timing unit */ CSRS dmc_csrs[DMC_NUMDEVICE]; -CSRS dmc_shadow_csrs[DMC_NUMDEVICE]; +uint16 dmc_sel0[DMC_NUMDEVICE]; +uint16 dmc_sel2[DMC_NUMDEVICE]; +uint16 dmc_sel4[DMC_NUMDEVICE]; +uint16 dmc_sel6[DMC_NUMDEVICE]; + +uint32 dmc_speed[DMC_NUMDEVICE]; +char dmc_peer[DMC_NUMDEVICE][CBUFSIZE]; +char dmc_port[DMC_NUMDEVICE][CBUFSIZE]; +uint32 dmc_baseaddr[DMC_NUMDEVICE]; +uint16 dmc_basesize[DMC_NUMDEVICE]; +uint8 dmc_modem[DMC_NUMDEVICE]; CSRS dmp_csrs[DMP_NUMDEVICE]; -CSRS dmp_shadow_csrs[DMP_NUMDEVICE]; +uint16 dmp_sel0[DMC_NUMDEVICE]; +uint16 dmp_sel2[DMC_NUMDEVICE]; +uint16 dmp_sel4[DMC_NUMDEVICE]; +uint16 dmp_sel6[DMC_NUMDEVICE]; +uint16 dmp_sel10[DMC_NUMDEVICE]; -LINE dmc_line[DMC_NUMDEVICE] = -{ - { 0, INVALID_SOCKET }, - { 0, INVALID_SOCKET }, - { 0, INVALID_SOCKET }, - { 0, INVALID_SOCKET } -}; +uint32 dmp_speed[DMP_NUMDEVICE]; +char dmp_peer[DMP_NUMDEVICE][CBUFSIZE]; +char dmp_port[DMP_NUMDEVICE][CBUFSIZE]; +uint32 dmp_baseaddr[DMP_NUMDEVICE]; +uint16 dmp_basesize[DMP_NUMDEVICE]; +uint8 dmp_modem[DMP_NUMDEVICE]; -BUFFER_QUEUE dmc_receive_queues[DMC_NUMDEVICE]; -BUFFER_QUEUE dmc_transmit_queues[DMC_NUMDEVICE]; +TMLN dmc_ldsc[DMC_NUMDEVICE]; /* line descriptors */ +TMXR dmc_desc = { 1, NULL, 0, dmc_ldsc }; /* mux descriptor */ +uint32 dmc_connect_poll = DMC_CONNECT_POLL; /* seconds between polls when no connection */ -LINE dmp_line[DMP_NUMDEVICE] = -{ - { 0, INVALID_SOCKET } -}; +uint32 dmc_ini_summary = 0; /* In Command Interrupt Summary for all controllers */ +uint32 dmc_outi_summary = 0; /* Out Command Interrupt Summary for all controllers */ -BUFFER_QUEUE dmp_receive_queues[DMP_NUMDEVICE]; -BUFFER_QUEUE dmp_transmit_queues[DMP_NUMDEVICE]; +BUFFER_QUEUE dmc_rcv_queues[DMC_NUMDEVICE]; +BUFFER_QUEUE dmc_completion_queues[DMC_NUMDEVICE]; +BUFFER_QUEUE dmc_xmt_queues[DMC_NUMDEVICE]; +BUFFER_QUEUE dmc_ack_wait_queues[DMC_NUMDEVICE]; +BUFFER_QUEUE dmc_free_queues[DMC_NUMDEVICE]; +BUFFER *dmc_buffers[DMC_NUMDEVICE]; -UNIT_STATS dmc_stats[DMC_NUMDEVICE]; -UNIT_STATS dmp_stats[DMP_NUMDEVICE]; +TMLN dmp_ldsc[DMC_NUMDEVICE]; /* line descriptors */ +TMXR dmp_desc = { 1, NULL, 0, dmp_ldsc }; /* mux descriptor */ +uint32 dmp_connect_poll; /* seconds between polls when no connection */ -REG dmca_reg[] = { - { HRDATA (SEL0, dmc_csrs[0].sel0, 16) }, - { HRDATA (SEL2, dmc_csrs[0].sel2, 16) }, - { HRDATA (SEL4, dmc_csrs[0].sel4, 16) }, - { HRDATA (SEL6, dmc_csrs[0].sel6, 16) }, - { GRDATA (BSEL0, dmc_csrs[0].sel0, DEV_RDX, 8, 0) }, - { GRDATA (BSEL1, dmc_csrs[0].sel0, DEV_RDX, 8, 8) }, - { GRDATA (BSEL2, dmc_csrs[0].sel2, DEV_RDX, 8, 0) }, - { GRDATA (BSEL3, dmc_csrs[0].sel2, DEV_RDX, 8, 8) }, - { GRDATA (BSEL4, dmc_csrs[0].sel4, DEV_RDX, 8, 0) }, - { GRDATA (BSEL5, dmc_csrs[0].sel4, DEV_RDX, 8, 8) }, - { GRDATA (BSEL6, dmc_csrs[0].sel6, DEV_RDX, 8, 0) }, - { GRDATA (BSEL7, dmc_csrs[0].sel6, DEV_RDX, 8, 8) }, - { HRDATA (SHADOWSEL0, dmc_shadow_csrs[0].sel0, 16), REG_HRO }, - { HRDATA (SHADOWSEL2, dmc_shadow_csrs[0].sel2, 16), REG_HRO }, - { HRDATA (SHADOWSEL4, dmc_shadow_csrs[0].sel4, 16), REG_HRO }, - { HRDATA (SHADOWSEL6, dmc_shadow_csrs[0].sel6, 16), REG_HRO }, - { BRDATA (PEER, dmc_line[0].peer, 16, 8, sizeof(dmc_line[0].peer)), REG_HRO}, - { HRDATA (MODE, dmc_line[0].isPrimary, 32), REG_HRO }, - { HRDATA (SPEED, dmc_line[0].speed, 32), REG_HRO }, - { NULL } }; +BUFFER_QUEUE dmp_rcv_queues[DMP_NUMDEVICE]; +BUFFER_QUEUE dmp_completion_queues[DMP_NUMDEVICE]; +BUFFER_QUEUE dmp_xmt_queues[DMP_NUMDEVICE]; +BUFFER_QUEUE dmp_ack_wait_queues[DMP_NUMDEVICE]; +BUFFER_QUEUE dmp_free_queues[DMP_NUMDEVICE]; +BUFFER *dmp_buffers[DMC_NUMDEVICE]; -REG dmcb_reg[] = { - { HRDATA (SEL0, dmc_csrs[1].sel0, 16) }, - { HRDATA (SEL2, dmc_csrs[1].sel2, 16) }, - { HRDATA (SEL4, dmc_csrs[1].sel4, 16) }, - { HRDATA (SEL6, dmc_csrs[1].sel6, 16) }, - { GRDATA (BSEL0, dmc_csrs[1].sel0, DEV_RDX, 8, 0) }, - { GRDATA (BSEL1, dmc_csrs[1].sel0, DEV_RDX, 8, 8) }, - { GRDATA (BSEL2, dmc_csrs[1].sel2, DEV_RDX, 8, 0) }, - { GRDATA (BSEL3, dmc_csrs[1].sel2, DEV_RDX, 8, 8) }, - { GRDATA (BSEL4, dmc_csrs[1].sel4, DEV_RDX, 8, 0) }, - { GRDATA (BSEL5, dmc_csrs[1].sel4, DEV_RDX, 8, 8) }, - { GRDATA (BSEL6, dmc_csrs[1].sel6, DEV_RDX, 8, 0) }, - { GRDATA (BSEL7, dmc_csrs[1].sel6, DEV_RDX, 8, 8) }, - { HRDATA (SHADOWSEL0, dmc_shadow_csrs[1].sel0, 16), REG_HRO }, - { HRDATA (SHADOWSEL2, dmc_shadow_csrs[1].sel2, 16), REG_HRO }, - { HRDATA (SHADOWSEL4, dmc_shadow_csrs[1].sel4, 16), REG_HRO }, - { HRDATA (SHADOWSEL6, dmc_shadow_csrs[1].sel6, 16), REG_HRO }, - { BRDATA (PEER, dmc_line[1].peer, 16, 8, sizeof(dmc_line[1].peer)), REG_HRO}, - { HRDATA (MODE, dmc_line[1].isPrimary, 32), REG_HRO }, - { HRDATA (SPEED, dmc_line[1].speed, 32), REG_HRO }, - { NULL } }; - -REG dmcc_reg[] = { - { HRDATA (SEL0, dmc_csrs[2].sel0, 16) }, - { HRDATA (SEL2, dmc_csrs[2].sel2, 16) }, - { HRDATA (SEL4, dmc_csrs[2].sel4, 16) }, - { HRDATA (SEL6, dmc_csrs[2].sel6, 16) }, - { GRDATA (BSEL0, dmc_csrs[2].sel0, DEV_RDX, 8, 0) }, - { GRDATA (BSEL1, dmc_csrs[2].sel0, DEV_RDX, 8, 8) }, - { GRDATA (BSEL2, dmc_csrs[2].sel2, DEV_RDX, 8, 0) }, - { GRDATA (BSEL3, dmc_csrs[2].sel2, DEV_RDX, 8, 8) }, - { GRDATA (BSEL4, dmc_csrs[2].sel4, DEV_RDX, 8, 0) }, - { GRDATA (BSEL5, dmc_csrs[2].sel4, DEV_RDX, 8, 8) }, - { GRDATA (BSEL6, dmc_csrs[2].sel6, DEV_RDX, 8, 0) }, - { GRDATA (BSEL7, dmc_csrs[2].sel6, DEV_RDX, 8, 8) }, - { HRDATA (SHADOWSEL0, dmc_shadow_csrs[2].sel0, 16), REG_HRO }, - { HRDATA (SHADOWSEL2, dmc_shadow_csrs[2].sel2, 16), REG_HRO }, - { HRDATA (SHADOWSEL4, dmc_shadow_csrs[2].sel4, 16), REG_HRO }, - { HRDATA (SHADOWSEL6, dmc_shadow_csrs[2].sel6, 16), REG_HRO }, - { BRDATA (PEER, dmc_line[2].peer, 16, 8, sizeof(dmc_line[2].peer)), REG_HRO}, - { HRDATA (MODE, dmc_line[2].isPrimary, 32), REG_HRO }, - { HRDATA (SPEED, dmc_line[2].speed, 32), REG_HRO }, - { NULL } }; - -REG dmcd_reg[] = { - { HRDATA (SEL0, dmc_csrs[3].sel0, 16) }, - { HRDATA (SEL2, dmc_csrs[3].sel2, 16) }, - { HRDATA (SEL4, dmc_csrs[3].sel4, 16) }, - { HRDATA (SEL6, dmc_csrs[3].sel6, 16) }, - { GRDATA (BSEL0, dmc_csrs[3].sel0, DEV_RDX, 8, 0) }, - { GRDATA (BSEL1, dmc_csrs[3].sel0, DEV_RDX, 8, 8) }, - { GRDATA (BSEL2, dmc_csrs[3].sel2, DEV_RDX, 8, 0) }, - { GRDATA (BSEL3, dmc_csrs[3].sel2, DEV_RDX, 8, 8) }, - { GRDATA (BSEL4, dmc_csrs[3].sel4, DEV_RDX, 8, 0) }, - { GRDATA (BSEL5, dmc_csrs[3].sel4, DEV_RDX, 8, 8) }, - { GRDATA (BSEL6, dmc_csrs[3].sel6, DEV_RDX, 8, 0) }, - { GRDATA (BSEL7, dmc_csrs[3].sel6, DEV_RDX, 8, 8) }, - { HRDATA (SHADOWSEL0, dmc_shadow_csrs[3].sel0, 16), REG_HRO }, - { HRDATA (SHADOWSEL2, dmc_shadow_csrs[3].sel2, 16), REG_HRO }, - { HRDATA (SHADOWSEL4, dmc_shadow_csrs[3].sel4, 16), REG_HRO }, - { HRDATA (SHADOWSEL6, dmc_shadow_csrs[3].sel6, 16), REG_HRO }, - { BRDATA (PEER, dmc_line[3].peer, 16, 8, sizeof(dmc_line[3].peer)), REG_HRO}, - { HRDATA (MODE, dmc_line[3].isPrimary, 32), REG_HRO }, - { HRDATA (SPEED, dmc_line[3].speed, 32), REG_HRO }, +REG dmc_reg[] = { + { GRDATAD (RXINT, dmc_ini_summary, DEV_RDX, 32, 0, "input interrupt summary") }, + { GRDATAD (TXINT, dmc_outi_summary, DEV_RDX, 32, 0, "output interrupt summary") }, + { GRDATAD (POLL, dmc_connect_poll, DEV_RDX, 32, 0, "connect poll interval") }, + { BRDATAD (SEL0, dmc_sel0, DEV_RDX, 16, DMC_NUMDEVICE, "Select 0 CSR") }, + { BRDATAD (SEL2, dmc_sel2, DEV_RDX, 16, DMC_NUMDEVICE, "Select 2 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 (SPEED, dmc_speed, DEV_RDX, 32, DMC_NUMDEVICE, "line speed") }, + { 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 (BASEADDR, dmc_baseaddr, DEV_RDX, 32, DMC_NUMDEVICE, "program set base address") }, + { BRDATAD (BASESIZE, dmc_basesize, DEV_RDX, 16, DMC_NUMDEVICE, "program set base size") }, + { BRDATAD (MODEM, dmc_modem, DEV_RDX, 8, DMC_NUMDEVICE, "modem control bits") }, { NULL } }; REG dmp_reg[] = { - { HRDATA (SEL0, dmc_csrs[3].sel0, 16) }, - { HRDATA (SEL2, dmc_csrs[3].sel2, 16) }, - { HRDATA (SEL4, dmc_csrs[3].sel4, 16) }, - { HRDATA (SEL6, dmc_csrs[3].sel6, 16) }, - { GRDATA (BSEL0, dmc_csrs[3].sel0, DEV_RDX, 8, 0) }, - { GRDATA (BSEL1, dmc_csrs[3].sel0, DEV_RDX, 8, 8) }, - { GRDATA (BSEL2, dmc_csrs[3].sel2, DEV_RDX, 8, 0) }, - { GRDATA (BSEL3, dmc_csrs[3].sel2, DEV_RDX, 8, 8) }, - { GRDATA (BSEL4, dmc_csrs[3].sel4, DEV_RDX, 8, 0) }, - { GRDATA (BSEL5, dmc_csrs[3].sel4, DEV_RDX, 8, 8) }, - { GRDATA (BSEL6, dmc_csrs[3].sel6, DEV_RDX, 8, 0) }, - { GRDATA (BSEL7, dmc_csrs[3].sel6, DEV_RDX, 8, 8) }, - { HRDATA (SHADOWSEL0, dmp_shadow_csrs[0].sel0, 16), REG_HRO }, - { HRDATA (SHADOWSEL2, dmp_shadow_csrs[0].sel2, 16), REG_HRO }, - { HRDATA (SHADOWSEL4, dmp_shadow_csrs[0].sel4, 16), REG_HRO }, - { HRDATA (SHADOWSEL6, dmp_shadow_csrs[0].sel6, 16), REG_HRO }, - { BRDATA (PEER, dmp_line[0].peer, 16, 8, sizeof(dmp_line[0].peer)), REG_HRO}, - { HRDATA (MODE, dmp_line[0].isPrimary, 32), REG_HRO }, - { HRDATA (SPEED, dmp_line[0].speed, 32), REG_HRO }, + { GRDATAD (RXINT, dmc_ini_summary, DEV_RDX, 32, 0, "input interrupt summary") }, + { GRDATAD (TXINT, dmc_outi_summary, DEV_RDX, 32, 0, "output interrupt summary") }, + { GRDATAD (POLL, dmp_connect_poll, DEV_RDX, 32, 0, "connect poll interval") }, + { BRDATAD (SEL0, dmp_sel0, DEV_RDX, 16, DMC_NUMDEVICE, "Select 0 CSR") }, + { BRDATAD (SEL2, dmp_sel2, DEV_RDX, 16, DMC_NUMDEVICE, "Select 2 CSR") }, + { BRDATAD (SEL4, dmp_sel4, DEV_RDX, 16, DMC_NUMDEVICE, "Select 4 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 (SPEED, dmp_speed, DEV_RDX, 32, DMC_NUMDEVICE, "line speed") }, + { 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") }, + { BRDATAD (BASESIZE, dmp_basesize, DEV_RDX, 16, DMC_NUMDEVICE, "program set base size") }, + { BRDATAD (MODEM, dmp_modem, DEV_RDX, 8, DMP_NUMDEVICE, "modem control bits") }, { NULL } }; +extern DEVICE dmc_dev; + MTAB dmc_mod[] = { - { MTAB_XTD|MTAB_VDV, 0, "PEER", "PEER=address:port", - &dmc_setpeer, &dmc_showpeer, NULL, "Display destination/source depends on LINEMODE" }, - { MTAB_XTD|MTAB_VDV, 0, "SPEED", "SPEED=bits/sec (0=unrestricted)" , + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "LINES", "LINES=n", + &dmc_setnumdevices, &dmc_shownumdevices, (void *) &dmc_dev, "Display number of devices" }, + { MTAB_XTD|MTAB_VUN, 0, "PEER", "PEER=address:port", + &dmc_setpeer, &dmc_showpeer, NULL, "Display destination/source" }, + { MTAB_XTD|MTAB_VUN, 0, "SPEED", "SPEED=bits/sec (0=unrestricted)" , &dmc_setspeed, &dmc_showspeed, NULL, "Display rate limit" }, -#ifdef DMP - { MTAB_XTD|MTAB_VDV|MTAB_VALR,0, "TYPE", "TYPE" ,&dmc_settype, &dmc_showtype, NULL, "Set/Display device type" }, +#if !defined (VM_PDP10) + { MTAB_XTD|MTAB_VUN|MTAB_VALR,0, "TYPE", "TYPE={DMR,DMC}" ,&dmc_settype, &dmc_showtype, NULL, "Set/Display device type" }, #endif - { MTAB_XTD|MTAB_VDV, 0, "LINEMODE", "LINEMODE={PRIMARY|SECONDARY}", - &dmc_setlinemode, &dmc_showlinemode, NULL, "Display the connection orientation" }, - { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "STATS", "STATS", - &dmc_setstats, &dmc_showstats, NULL, "Display statistics" }, + { MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "STATS", "STATS", + &dmc_setstats, &dmc_showstats, NULL, "Display/Clear statistics" }, + { MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "QUEUES", "QUEUES", + NULL, &dmc_showqueues, NULL, "Display Queue state" }, + { MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "DDCMP", "DDCMP", + NULL, &dmc_showddcmp, NULL, "Display DDCMP state information" }, { MTAB_XTD|MTAB_VDV, 0, "CONNECTPOLL", "CONNECTPOLL=seconds", - &dmc_setconnectpoll, &dmc_showconnectpoll, NULL, "Display connection poll interval" }, - { MTAB_XTD|MTAB_VDV|MTAB_VALR, 006, "ADDRESS", "ADDRESS", + &dmc_setconnectpoll, &dmc_showconnectpoll, (void *) &dmc_connect_poll, "Display connection poll interval" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 020, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL, "Bus address" }, - { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "VECTOR", "VECTOR", + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 1, "VECTOR", "VECTOR", &set_vec, &show_vec, NULL, "Interrupt vector" }, - { 0 }, -}; + { 0 } }; + +extern DEVICE dmp_dev; + +MTAB dmp_mod[] = { + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "LINES", "LINES=n", + &dmc_setnumdevices, &dmc_shownumdevices, (void *) &dmp_dev, "Display number of devices" }, + { MTAB_XTD|MTAB_VUN, 0, "PEER", "PEER=address:port", + &dmc_setpeer, &dmc_showpeer, NULL, "Display destination/source" }, + { MTAB_XTD|MTAB_VUN, 0, "SPEED", "SPEED=bits/sec (0=unrestricted)" , + &dmc_setspeed, &dmc_showspeed, NULL, "Display rate limit" }, + { MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "STATS", "STATS", + &dmc_setstats, &dmc_showstats, NULL, "Display/Clear statistics" }, + { MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "QUEUES", "QUEUES", + NULL, &dmc_showqueues, NULL, "Display Queue state" }, + { MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "DDCMP", "DDCMP", + 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_VDV|MTAB_VALR, 020, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL, "Bus address" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 1, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL, "Interrupt vector" }, + { 0 } }; + +extern DEVICE dmv_dev; + +MTAB dmv_mod[] = { + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "LINES", "LINES=n", + &dmc_setnumdevices, &dmc_shownumdevices, (void *) &dmv_dev, "Display number of devices" }, + { MTAB_XTD|MTAB_VUN, 0, "PEER", "PEER=address:port", + &dmc_setpeer, &dmc_showpeer, NULL, "Display destination/source" }, + { MTAB_XTD|MTAB_VUN, 0, "SPEED", "SPEED=bits/sec (0=unrestricted)" , + &dmc_setspeed, &dmc_showspeed, NULL, "Display rate limit" }, + { MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "STATS", "STATS", + &dmc_setstats, &dmc_showstats, NULL, "Display/Clear statistics" }, + { MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "QUEUES", "QUEUES", + NULL, &dmc_showqueues, NULL, "Display Queue state" }, + { MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "DDCMP", "DDCMP", + 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_VDV|MTAB_VALR, 020, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL, "Bus address" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 1, "VECTOR", "VECTOR", + &set_vec, &show_vec, NULL, "Interrupt vector" }, + { 0 } }; #define IOLN_DMC 010 -DIB dmc0_dib = { IOBA_AUTO, IOLN_DMC, &dmc_rd, &dmc_wr, 2, IVCL (DMCRX), VEC_AUTO, {&dmc_rxint, &dmc_txint} }; -DIB dmc1_dib = { IOBA_AUTO, IOLN_DMC, &dmc_rd, &dmc_wr, 2, IVCL (DMCRX), VEC_AUTO, {&dmc_rxint, &dmc_txint} }; -DIB dmc2_dib = { IOBA_AUTO, IOLN_DMC, &dmc_rd, &dmc_wr, 2, IVCL (DMCRX), VEC_AUTO, {&dmc_rxint, &dmc_txint} }; -DIB dmc3_dib = { IOBA_AUTO, IOLN_DMC, &dmc_rd, &dmc_wr, 2, IVCL (DMCRX), VEC_AUTO, {&dmc_rxint, &dmc_txint} }; + +DIB dmc_dib = { IOBA_AUTO, IOLN_DMC, &dmc_rd, &dmc_wr, 2, IVCL (DMCRX), VEC_AUTO, {&dmc_ininta, &dmc_outinta}, IOLN_DMC }; #define IOLN_DMP 010 -DIB dmp_dib = { IOBA_AUTO, IOLN_DMP, &dmc_rd, &dmc_wr, 2, IVCL (DMCRX), VEC_AUTO, {&dmc_rxint, &dmc_txint }}; +DIB dmp_dib = { IOBA_AUTO, IOLN_DMP, &dmc_rd, &dmc_wr, 2, IVCL (DMCRX), VEC_AUTO, {&dmc_ininta, &dmc_outinta }, IOLN_DMP}; -DEVICE dmc_dev[] = -{ - { "DMC0", &dmc0_unit, dmca_reg, dmc_mod, DMC_UNITSPERDEVICE, DMC_RDX, 8, 1, DMC_RDX, 8, - NULL,NULL,&dmc_reset,NULL,&dmc_attach,&dmc_detach, - &dmc0_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG, 0, dmc_debug, - NULL, NULL, &dmc_help, &dmc_help_attach, NULL, &dmc_description }, - { "DMC1", &dmc1_unit, dmcb_reg, dmc_mod, DMC_UNITSPERDEVICE, DMC_RDX, 8, 1, DMC_RDX, 8, - NULL,NULL,&dmc_reset,NULL,&dmc_attach,&dmc_detach, - &dmc1_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG, 0, dmc_debug, - NULL, NULL, &dmc_help, &dmc_help_attach, NULL, &dmc_description }, - { "DMC2", &dmc2_unit, dmcc_reg, dmc_mod, DMC_UNITSPERDEVICE, DMC_RDX, 8, 1, DMC_RDX, 8, - NULL,NULL,&dmc_reset,NULL,&dmc_attach,&dmc_detach, - &dmc2_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG, 0, dmc_debug, - NULL, NULL, &dmc_help, &dmc_help_attach, NULL, &dmc_description }, - { "DMC3", &dmc3_unit, dmcd_reg, dmc_mod, DMC_UNITSPERDEVICE, DMC_RDX, 8, 1, DMC_RDX, 8, - NULL,NULL,&dmc_reset,NULL,&dmc_attach,&dmc_detach, - &dmc3_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG, 0, dmc_debug, - NULL, NULL, &dmc_help, &dmc_help_attach, NULL, &dmc_description } -}; +#define IOLN_DMV 020 -#ifdef DMP -DEVICE dmp_dev[] = -{ - { "DMP", &dmp_unit, dmp_reg, dmc_mod, DMP_UNITSPERDEVICE, DMC_RDX, 8, 1, DMC_RDX, 8, - NULL,NULL,&dmc_reset,NULL,&dmc_attach,&dmc_detach, +DIB dmv_dib = { IOBA_AUTO, IOLN_DMV, &dmc_rd, &dmc_wr, 2, IVCL (DMCRX), VEC_AUTO, {&dmc_ininta, &dmc_outinta }, IOLN_DMV}; + +DEVICE dmc_dev = + { +#if defined (VM_PDP10) + "DMR", +#else + "DMC", +#endif + dmc_units, dmc_reg, dmc_mod, 3, DMC_RDX, 8, 1, DMC_RDX, 8, + NULL, NULL, &dmc_reset, NULL, &dmc_attach, &dmc_detach, + &dmc_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG, 0, dmc_debug, + NULL, NULL, &dmc_help, &dmc_help_attach, NULL, &dmc_description }; + +/* + We have two devices defined here (dmp_dev and dmv_dev) which have the + same units. This would normally never be allowed since two devices can't + actually share units. This problem is avoided in this case since both + devices start out as disabled and the logic in dmc_reset allows only + one of these devices to be enabled at a time. The DMP device is allowed + on Unibus systems and the DMV device Qbus systems. + This monkey business is necessary due to the fact that although both + the DMP and DMV devices have almost the same functionality and almost + the same register programming interface, they are different enough that + they fall in different priorities in the autoconfigure address and vector + rules. + */ +DEVICE dmp_dev = + { "DMP", dmp_units, dmp_reg, dmp_mod, 3, DMC_RDX, 8, 1, DMC_RDX, 8, + NULL, NULL, &dmc_reset, NULL, &dmc_attach, &dmc_detach, &dmp_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG, 0, dmc_debug, - NULL, NULL, &dmc_help, &dmc_help_attach, NULL, &dmp_description } -}; -#endif + NULL, NULL, &dmc_help, &dmc_help_attach, NULL, &dmp_description }; -CTLR dmc_ctrls[] = -{ - { &dmc_csrs[0], &dmc_shadow_csrs[0], &dmc_dev[0], Initialised, Idle, 0, 0, &dmc_line[0], &dmc_receive_queues[0], &dmc_transmit_queues[0], &dmc_stats[0], INVALID_SOCKET, 30, DMC }, - { &dmc_csrs[1], &dmc_shadow_csrs[1], &dmc_dev[1], Initialised, Idle, 0, 0, &dmc_line[1], &dmc_receive_queues[1], &dmc_transmit_queues[1], &dmc_stats[1], INVALID_SOCKET, 30, DMC }, - { &dmc_csrs[2], &dmc_shadow_csrs[2], &dmc_dev[2], Initialised, Idle, 0, 0, &dmc_line[2], &dmc_receive_queues[2], &dmc_transmit_queues[2], &dmc_stats[2], INVALID_SOCKET, 30, DMC }, - { &dmc_csrs[3], &dmc_shadow_csrs[3], &dmc_dev[3], Initialised, Idle, 0, 0, &dmc_line[3], &dmc_receive_queues[3], &dmc_transmit_queues[3], &dmc_stats[3], INVALID_SOCKET, 30, DMC }, -#ifdef DMP - { &dmp_csrs[0], &dmp_shadow_csrs[0], &dmp_dev[0], Initialised, Idle, 0, 0, &dmp_line[0], &dmp_receive_queues[0], &dmp_transmit_queues[0], &dmp_stats[0], INVALID_SOCKET, -1, 30, DMP } -#endif -}; +DEVICE dmv_dev = + { "DMV", dmp_units, dmp_reg, dmv_mod, 3, DMC_RDX, 8, 1, DMC_RDX, 8, + NULL, NULL, &dmc_reset, NULL, &dmc_attach, &dmc_detach, + &dmp_dib, DEV_DISABLE | DEV_DIS | DEV_QBUS | DEV_DEBUG, 0, dmc_debug, + NULL, NULL, &dmc_help, &dmc_help_attach, NULL, &dmp_description }; -extern int32 tmxr_poll; /* calibrated delay */ - -void dmc_reset_unit_stats(UNIT_STATS *s) -{ - s->between_polls_timer.started = FALSE; - s->poll_timer.started = FALSE; - s->poll_count = 0; -} - -int dmc_timer_started(TIMER *t) -{ - return t->started; -} - -void dmc_timer_start(TIMER *t) -{ - t->start_time = clock(); - t->cumulative_time = 0; - t->started = TRUE; -} - -void dmc_timer_stop(TIMER *t) -{ - clock_t end_time = clock(); - t->cumulative_time += end_time - t->start_time; -} - -void dmc_timer_resume(TIMER *t) -{ - t->start_time = clock(); -} - -double dmc_timer_cumulative_seconds(TIMER *t) -{ - return (double)t->cumulative_time/CLOCKS_PER_SEC; -} +CTLR dmc_ctrls[DMC_NUMDEVICE + DMP_NUMDEVICE]; int dmc_is_attached(UNIT* uptr) { - return uptr->flags & UNIT_ATT; +return uptr->flags & UNIT_ATT; } int dmc_is_dmc(CTLR *controller) { - return controller->dev_type != DMP; +return controller->dev_type != DMP; } CTLR *dmc_get_controller_from_unit(UNIT *unit) { - int i; - CTLR *ans = NULL; - for (i = 0; i < DMC_NUMDEVICE + DMP_NUMDEVICE; i++) - { - if (dmc_ctrls[i].device->units == unit) - { - ans = &dmc_ctrls[i]; - break; - } - } - - return ans; +return (CTLR *)unit->ctlr; } CTLR* dmc_get_controller_from_address(uint32 address) { - int i; - for (i=0; ictxt; - if ((address >= dib->ba) && (address < (dib->ba + dib->lnt))) - return &dmc_ctrls[i]; - } - /* not found */ - return 0; -} +int i; -CTLR *dmc_get_controller_from_device(DEVICE *device) -{ - int i; - CTLR *ans = NULL; - for (i = 0; i < DMC_NUMDEVICE + DMP_NUMDEVICE; i++) - { - if (dmc_ctrls[i].device == device) - { - ans = &dmc_ctrls[i]; - break; - } +for (i=0; ictxt; + if ((address >= dib->ba) && (address < (dib->ba + dib->lnt))) + return &dmc_ctrls[(address - dib->ba) >> ((UNIBUS) ? 3 : 4)]; } - - return ans; +/* not found */ +return 0; } t_stat dmc_showpeer (FILE* st, UNIT* uptr, int32 val, void* desc) { - CTLR *controller = dmc_get_controller_from_unit(uptr); - if (controller->line->peer[0]) - { - fprintf(st, "peer=%s", controller->line->peer); - } - else - { - fprintf(st, "peer=unspecified"); - } +DEVICE *dptr = (UNIBUS) ? ((&dmc_dev == find_dev_from_unit(uptr)) ? &dmc_dev : &dmp_dev) : &dmv_dev; +int32 dmc = (int32)(uptr-dptr->units); +char *peer = ((dptr == &dmc_dev)? &dmc_peer[dmc][0] : &dmp_peer[dmc][0]); - return SCPE_OK; +if (peer[0]) + fprintf(st, "peer=%s", peer); +else + fprintf(st, "peer=unspecified"); +return SCPE_OK; } t_stat dmc_setpeer (UNIT* uptr, int32 val, char* cptr, void* desc) { - t_stat status = SCPE_OK; - char host[CBUFSIZE], port[CBUFSIZE]; - CTLR *controller = dmc_get_controller_from_unit(uptr); - - if (!cptr) return SCPE_IERR; - if (dmc_is_attached(uptr)) return SCPE_ALATT; - status = sim_parse_addr (cptr, host, sizeof(host), NULL, port, sizeof(port), NULL, NULL); - if (status != SCPE_OK) - return status; - if (host[0] == '\0') - return SCPE_ARG; - strncpy(controller->line->peer, cptr, sizeof(controller->line->peer)-1); +DEVICE *dptr = (UNIBUS) ? ((&dmc_dev == find_dev_from_unit(uptr)) ? &dmc_dev : &dmp_dev) : &dmv_dev; +int32 dmc = (int32)(uptr-dptr->units); +char *peer = ((dptr == &dmc_dev)? &dmc_peer[dmc][0] : &dmp_peer[dmc][0]); +t_stat status = SCPE_OK; +char host[CBUFSIZE], port[CBUFSIZE]; +CTLR *controller = dmc_get_controller_from_unit(uptr); +if ((!cptr) || (!*cptr)) + return SCPE_ARG; +if (dmc_is_attached(uptr)) + return SCPE_ALATT; +status = sim_parse_addr (cptr, host, sizeof(host), NULL, port, sizeof(port), NULL, NULL); +if (status != SCPE_OK) return status; +if (host[0] == '\0') + return SCPE_ARG; +strncpy(peer, cptr, CBUFSIZE-1); +return status; } t_stat dmc_showspeed (FILE* st, UNIT* uptr, int32 val, void* desc) { - CTLR *controller = dmc_get_controller_from_unit(uptr); - if (controller->line->speed > 0) - { - fprintf(st, "speed=%d bits/sec", controller->line->speed); - } - else - { - fprintf(st, "speed=0 (unrestricted)"); - } +DEVICE *dptr = (UNIBUS) ? ((&dmc_dev == find_dev_from_unit(uptr)) ? &dmc_dev : &dmp_dev) : &dmv_dev; +int32 dmc = (int32)(uptr-dptr->units); +uint32 *speeds = ((dptr == &dmc_dev)? dmc_speed : dmp_speed); - return SCPE_OK; +if (speeds[dmc] > 0) + fprintf(st, "speed=%d bits/sec", speeds[dmc]); +else + fprintf(st, "speed=0 (unrestricted)"); +return SCPE_OK; } + t_stat dmc_setspeed (UNIT* uptr, int32 val, char* cptr, void* desc) { - t_stat status = SCPE_OK; - CTLR *controller = dmc_get_controller_from_unit(uptr); +t_stat status = SCPE_OK; +DEVICE *dptr = (UNIBUS) ? ((&dmc_dev == find_dev_from_unit(uptr)) ? &dmc_dev : &dmp_dev) : &dmv_dev; +int32 dmc = (int32)(uptr-dptr->units); +uint32 *speeds = ((dptr == &dmc_dev)? dmc_speed : dmp_speed); +t_stat r; +int32 newspeed; - if (!cptr) return SCPE_IERR; - if (dmc_is_attached(uptr)) return SCPE_ALATT; - if (sscanf(cptr, "%d", &controller->line->speed) != 1) - { - status = SCPE_ARG; - } - - return status; +if (cptr == NULL) + return SCPE_ARG; +newspeed = (int32) get_uint (cptr, 10, 100000000, &r); +if (r != SCPE_OK) + return r; +speeds[dmc] = newspeed; +return SCPE_OK; } t_stat dmc_showtype (FILE* st, UNIT* uptr, int32 val, void* desc) { - CTLR *controller = dmc_get_controller_from_unit(uptr); - switch (controller->dev_type) - { +DEVICE *dptr = (UNIBUS) ? ((&dmc_dev == find_dev_from_unit(uptr)) ? &dmc_dev : &dmp_dev) : &dmv_dev; +int32 dmc = (int32)(uptr-dptr->units); +CTLR *controller = dmc_get_controller_from_unit(uptr); + +switch (controller->dev_type) { case DMC: - { - fprintf(st, "type=DMC"); - break; - } + fprintf(st, "type=DMC"); + break; case DMR: - { - fprintf(st, "type=DMR"); - break; - } + fprintf(st, "type=DMR"); + break; case DMP: - { - fprintf(st, "type=DMP"); - break; - } + fprintf(st, "type=%s", (UNIBUS) ? "DMP" : "DMV"); + break; default: - { - fprintf(st, "type=???"); - break; - } + fprintf(st, "type=???"); + break; } - return SCPE_OK; +return SCPE_OK; } t_stat dmc_settype (UNIT* uptr, int32 val, char* cptr, void* desc) { - char buf[80]; - t_stat status = SCPE_OK; - CTLR *controller = dmc_get_controller_from_unit(uptr); +char gbuf[80]; +t_stat status = SCPE_OK; +CTLR *controller = dmc_get_controller_from_unit(uptr); - if (!cptr) return SCPE_IERR; - if (dmc_is_attached(uptr)) return SCPE_ALATT; - if (sscanf(cptr, "%s", buf) != 1) - { - status = SCPE_ARG; - } +if ((!cptr) || (!*cptr)) + return SCPE_2FARG; +if (dmc_is_attached(uptr)) + return SCPE_ALATT; +cptr = get_glyph (cptr, gbuf, 0); +if (strcmp (gbuf,"DMC") == 0) + controller->dev_type = DMC; +else + if (strcmp (gbuf,"DMR") == 0) + controller->dev_type = DMR; else - { - if (strcmp(buf,"DMC")==0) - { - controller->dev_type = DMC; - } - else if (strcmp(buf,"DMR")==0) - { - controller->dev_type = DMR; - } - else if (strcmp(buf,"DMP")==0) - { - controller->dev_type = DMP; - } - else - { - status = SCPE_ARG; - } - } + status = SCPE_ARG; - return status; +return status; } t_stat dmc_showstats (FILE* st, UNIT* uptr, int32 val, void* desc) { - CTLR *controller = dmc_get_controller_from_unit(uptr); - TIMER *poll_timer = &controller->stats->poll_timer; - TIMER *between_polls_timer = &controller->stats->between_polls_timer; - uint32 poll_count = controller->stats->poll_count; +CTLR *controller = dmc_get_controller_from_unit(uptr); + +fprintf (st, "%s%d\n", controller->device->name, (int)(uptr-controller->device->units)); - if (dmc_timer_started(between_polls_timer) && poll_count > 0) - { - fprintf(st, "Average time between polls=%f (sec)\n", dmc_timer_cumulative_seconds(between_polls_timer)/poll_count); - } - else - { - fprintf(st, "Average time between polls=n/a\n"); - } +tmxr_fstats (st, controller->line, -1); - if (dmc_timer_started(poll_timer) && poll_count > 0) - { - fprintf(st, "Average time within poll=%f (sec)\n", dmc_timer_cumulative_seconds(poll_timer)/poll_count); - } - else - { - fprintf(st, "Average time within poll=n/a\n"); - } +fprintf(st, "Buffers received from the network=%d\n", controller->buffers_received_from_net); +fprintf(st, "Buffers sent to the network=%d\n", controller->buffers_transmitted_to_net); +fprintf(st, "Output transfers completed for receive buffers=%d\n", controller->receive_buffer_output_transfers_completed); +fprintf(st, "Output transfers completed for transmit buffers=%d\n", controller->transmit_buffer_output_transfers_completed); +fprintf(st, "Input transfers completed for receive buffers=%d\n", controller->receive_buffer_input_transfers_completed); +fprintf(st, "Input transfers completed for transmit buffers=%d\n", controller->transmit_buffer_input_transfers_completed); +fprintf(st, "Control Out operations processed=%d\n", controller->control_out_operations_completed); +fprintf(st, "DDCMP control packets received=%d\n", controller->ddcmp_control_packets_received); +fprintf(st, "DDCMP control packets sent=%d\n", controller->ddcmp_control_packets_sent); - fprintf(st, "Buffers received from the network=%d\n", controller->buffers_received_from_net); - fprintf(st, "Buffers sent to the network=%d\n", controller->buffers_transmitted_to_net); - fprintf(st, "Output transfers completed for receive buffers=%d\n", controller->receive_buffer_output_transfers_completed); - fprintf(st, "Output transfers completed for transmit buffers=%d\n", controller->transmit_buffer_output_transfers_completed); - fprintf(st, "Input transfers completed for receive buffers=%d\n", controller->receive_buffer_input_transfers_completed); - fprintf(st, "Input transfers completed for transmit buffers=%d\n", controller->transmit_buffer_input_transfers_completed); +return SCPE_OK; +} - return SCPE_OK; +void dmc_showqueue (FILE* st, BUFFER_QUEUE *queue, t_bool detail) +{ +size_t i; + +fprintf (st, "%s Queue:\n", queue->name); +fprintf (st, " Size: %d\n", (int)queue->size); +fprintf (st, " Count: %d\n", (int)queue->count); +if (detail) { + BUFFER *buffer = (BUFFER *)queue->hdr.next; + size_t same; + + for (i=same=0; icount; ++i) { + if ((i == 0) || + (0 != memcmp(((char *)buffer)+sizeof(QH), ((char *)(buffer->hdr.prev))+sizeof(QH), sizeof(*buffer)-sizeof(QH)))) { + if (same > 0) + fprintf (st, "%s.queue[%d] thru %s.queue[%d] same as above\n", queue->name, (int)(i-same), queue->name, (int)(i-1)); + fprintf (st, "%s.queue[%d] at %p\n", queue->name, (int)i, buffer); + fprintf (st, " address: 0x%08X\n", buffer->address); + fprintf (st, " count: 0x%04X\n", buffer->count); + fprintf (st, " actual_bytes_transferred: 0x%04X\n", buffer->actual_bytes_transferred); + if (buffer->buffer_return_time) + fprintf (st, " buffer_return_time: 0x%08X\n", buffer->buffer_return_time); + same = 0; + } + else + ++same; + buffer = (BUFFER *)buffer->hdr.next; + } + if (same > 0) + fprintf (st, "%s.queue[%d] thru %s.queue[%d] same as above\n", queue->name, (int)(i-same), queue->name, (int)(i-1)); + } +} + +t_stat dmc_showqueues (FILE* st, UNIT* uptr, int32 val, void* desc) +{ +CTLR *controller = dmc_get_controller_from_unit(uptr); +static const char *states[] = {"Uninitialised", "Initialised", "Running", "Halted"}; +static const char *tstates[] = {"Idle", "InputTransfer", "OutputTransfer", "OutputControl"}; + +dmc_showstats (st, uptr, val, desc); +fprintf (st, "State: %s\n", states[controller->state]); +fprintf (st, "TransferState: %s\n", tstates[controller->transfer_state]); +dmc_showqueue (st, controller->completion_queue, TRUE); +dmc_showqueue (st, controller->xmt_queue, TRUE); +dmc_showqueue (st, controller->ack_wait_queue, TRUE); +dmc_showqueue (st, controller->rcv_queue, TRUE); +dmc_showqueue (st, controller->free_queue, TRUE); +if (controller->control_out) { + CONTROL_OUT *control; + + fprintf (st, "Control Out Queue:\n"); + for (control=controller->control_out; control; control=control->next) + fprintf (st, " SEL6: 0x%04X\n", control->sel6); + } +return SCPE_OK; } t_stat dmc_setstats (UNIT* uptr, int32 val, char* cptr, void* desc) { - t_stat status = SCPE_OK; - CTLR *controller = dmc_get_controller_from_unit(uptr); +CTLR *controller = dmc_get_controller_from_unit(uptr); - dmc_reset_unit_stats(controller->stats); +controller->receive_buffer_output_transfers_completed = 0; +controller->transmit_buffer_output_transfers_completed = 0; +controller->receive_buffer_input_transfers_completed = 0; +controller->transmit_buffer_input_transfers_completed = 0; +controller->control_out_operations_completed = 0; +controller->ddcmp_control_packets_received = 0; +controller->ddcmp_control_packets_sent = 0; - controller->receive_buffer_output_transfers_completed = 0; - controller->transmit_buffer_output_transfers_completed = 0; - controller->receive_buffer_input_transfers_completed = 0; - controller->transmit_buffer_input_transfers_completed = 0; +printf("Statistics reset\n" ); - printf("Statistics reset\n" ); - - return status; +return SCPE_OK; } t_stat dmc_showconnectpoll (FILE* st, UNIT* uptr, int32 val, void* desc) { - CTLR *controller = dmc_get_controller_from_unit(uptr); - fprintf(st, "connectpoll=%d", controller->connect_poll_interval); - return SCPE_OK; +uint32 poll_interval = *((uint32 *)desc); + +fprintf(st, "connectpoll=%u", poll_interval); +return SCPE_OK; } t_stat dmc_setconnectpoll (UNIT* uptr, int32 val, char* cptr, void* desc) { - t_stat status = SCPE_OK; - CTLR *controller = dmc_get_controller_from_unit(uptr); - - if (!cptr) return SCPE_IERR; - if (sscanf(cptr, "%d", &controller->connect_poll_interval) != 1) - { - status = SCPE_ARG; - } +t_stat status = SCPE_OK; +uint32 *poll_interval = ((uint32 *)desc); +uint32 newpoll; +if (!cptr) + return SCPE_IERR; +newpoll = (int32) get_uint (cptr, 10, 1800, &status); +if ((status != SCPE_OK) || (newpoll == (*poll_interval))) return status; +*poll_interval = newpoll; +return tmxr_connection_poll_interval ((poll_interval == &dmc_connect_poll) ? &dmc_desc : &dmp_desc, newpoll); } -t_stat dmc_showlinemode (FILE* st, UNIT* uptr, int32 val, void* desc) +/* SET LINES processor */ + +t_stat dmc_setnumdevices (UNIT *uptr, int32 val, char *cptr, void *desc) { - CTLR *controller = dmc_get_controller_from_unit(uptr); - fprintf(st, "linemode=%s", controller->line->isPrimary? "PRIMARY" : "SECONDARY"); - return SCPE_OK; +int32 newln; +uint32 i, j; +t_stat r; +DEVICE *dptr = (DEVICE *)desc; +TMXR *mp = (dptr == &dmc_dev) ? &dmc_desc : &dmp_desc; +int maxunits = (&dmc_dev == dptr) ? DMC_NUMDEVICE : DMP_NUMDEVICE; +DIB *dibptr = (DIB *)dptr->ctxt; +int addrlnt = (UNIBUS) ? IOLN_DMC : IOLN_DMV; + +for (j=0; j<2; j++) { + dptr = (j == 0) ? &dmc_dev : &dmp_dev; + for (i=0; inumunits; i++) + if (dptr->units[i].flags&UNIT_ATT) + return SCPE_ALATT; + } +dptr = (DEVICE *)desc; +for (i=0; inumunits; i++) + if (dptr->units[i].flags&UNIT_ATT) + return SCPE_ALATT; +if (cptr == NULL) + return SCPE_ARG; +newln = (int32) get_uint (cptr, 10, maxunits, &r); +if ((r != SCPE_OK) || (newln == (dptr->numunits - 2))) + return r; +if (newln == 0) + return SCPE_ARG; +sim_cancel (dptr->units + dptr->numunits - 2); +sim_cancel (dptr->units + dptr->numunits - 1); +for (i=dptr->numunits-2; i<(uint32)newln; ++i) { + dptr->units[i] = dmc_unit_template; + dptr->units[i].ctlr = &dmc_ctrls[(dptr == &dmc_dev) ? i : i+DMC_NUMDEVICE]; + } +dibptr->lnt = newln * addrlnt; /* set length */ +dptr->numunits = newln + 2; +dptr->units[newln] = dmc_poll_unit_template; +dptr->units[newln].ctlr = &dmc_ctrls[(dptr == &dmc_dev) ? 0 : DMC_NUMDEVICE]; +dptr->units[newln+1] = dmc_timer_unit_template; +dptr->units[newln+1].ctlr = &dmc_ctrls[(dptr == &dmc_dev) ? 0 : DMC_NUMDEVICE]; +mp->lines = newln; +return dmc_reset ((DEVICE *)desc); /* setup devices and auto config */ } -t_stat dmc_setlinemode (UNIT* uptr, int32 val, char* cptr, void* desc) +t_stat dmc_shownumdevices (FILE *st, UNIT *uptr, int32 val, void *desc) { - t_stat status = SCPE_OK; - CTLR *controller = dmc_get_controller_from_unit(uptr); +DEVICE *dptr = (UNIBUS) ? find_dev_from_unit (uptr) : &dmv_dev; - if (!cptr) return SCPE_IERR; - if (dmc_is_attached(uptr)) return SCPE_ALATT; +fprintf (st, "lines=%d", dptr->numunits-2); +return SCPE_OK; +} - if (MATCH_CMD(cptr, "PRIMARY") == 0) - { - controller->line->isPrimary = 1; - } - else if (MATCH_CMD(cptr, "SECONDARY") == 0) - { - controller->line->isPrimary = 0; - } - else - { - status = SCPE_ARG; - } +t_stat dmc_showddcmp (FILE* st, UNIT* uptr, int32 val, void* desc) +{ +CTLR *controller = dmc_get_controller_from_unit(uptr); +static const char *states[] = {"Halt", "IStart", "AStart", "Run", "Maintenance"}; - return status; +fprintf (st, "%s%d\n", controller->device->name, (int)(uptr-controller->device->units)); + +fprintf(st, "DDCMP Link State: %s\n", states[controller->link.state]); +fprintf(st, " R: %d\n", controller->link.R); +fprintf(st, " N: %d\n", controller->link.N); +fprintf(st, " A: %d\n", controller->link.A); +fprintf(st, " T: %d\n", controller->link.T); +fprintf(st, " X: %d\n", controller->link.X); +fprintf(st, " SACK: %s\n", controller->link.SACK ? "true" : "false"); +fprintf(st, " SNAK: %s\n", controller->link.SNAK ? "true" : "false"); +fprintf(st, " SREP: %s\n", controller->link.SREP ? "true" : "false"); +fprintf(st, " rcv_pkt: %p\n", controller->link.rcv_pkt); +fprintf(st, " rcv_pkt_size: %d\n", controller->link.rcv_pkt_size); +fprintf(st, " xmt_buffer: %p\n", controller->link.xmt_buffer); +fprintf(st, " nak_reason: %d\n", controller->link.nak_reason); +fprintf(st, " TimerRunning: %s\n", controller->link.TimerRunning ? "true" : "false"); +fprintf(st, " TimeRemaining: %d\n", controller->link.TimeRemaining); +return SCPE_OK; } t_stat dmc_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) { - fprintf(st, "The DMC11 is a synchronous serial point-to-point communications device.\n"); - fprintf(st, "A real DMC11 transports data using DDCMP, the emulated device makes a\n"); - fprintf(st, "TCP/IP connection to another emulated device and sends length-prefixed\n"); - fprintf(st, "messages across the connection, each message representing a single buffer\n"); - fprintf(st, "passed to the DMC11. The DMC11 can be used for point-to-point DDCMP\n"); - fprintf(st, "connections carrying DECnet and other types of networking, e.g. from ULTRIX\n"); - fprintf(st, "or DSM.\n\n"); - fprintf(st, "The line mode of the two ends of the link must be set. One end must always\n"); - fprintf(st, "be primary and one end always secondary, setting both to primary or both\n"); - fprintf(st, "to secondary will not work. If there are firewall problems at one side,\n"); - fprintf(st, "set that side to be primary as the primary always initiates the TCP/IP\n"); - fprintf(st, "connection.\n"); - fprintf(st, "\n"); - fprintf(st, " sim> SET DMC0 LINEMODE= {PRIMARY|SECONDARY}\n"); - fprintf(st, "\n"); - fprintf(st, "To set the host and port to which data is to be transmitted use the\n"); - fprintf(st, "following command (required for PRIMARY and SECONDARY, secondary will check\n"); - fprintf(st, "it is receiving from the configured primary):\n"); - fprintf(st, "\n"); - fprintf(st, " sim> SET DMC0 PEER=host:port\n"); - fprintf(st, "\n"); - fprintf(st, "The device must be attached to a receive port, use the ATTACH command\n"); - fprintf(st, "specifying the receive port number, even if the line mode is primary.\n"); - fprintf(st, "\n"); - fprintf(st, "The minimum interval between attempts to connect to the other side is set\n"); - fprintf(st, "using the following command:\n"); - fprintf(st, "\n"); - fprintf(st, " sim> SET DMC0 CONNECTPOLL=n\n"); - fprintf(st, "\n"); - fprintf(st, "Where n is the number of seconds. The default is 30 seconds.\n"); - fprintf(st, "\n"); - 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 DMC0 SPEED=n\n"); - fprintf(st, "\n"); - fprintf(st, "Where n is the number of data bits per second that the simulated line runs\n"); - fprintf(st, "at. In practice this is implemented as a delay in reading the bytes from\n"); - fprintf(st, "the socket. Use a value of zero to run at full speed with no artificial\n"); - fprintf(st, "throttling.\n"); - fprintf(st, "\n"); - fprintf(st, "To configure two simulators to talk to each other use the following example:\n"); - fprintf(st, "\n"); - fprintf(st, "Machine 1\n"); - fprintf(st, " sim> SET DMC0 ENABLE\n"); - fprintf(st, " sim> SET DMC0 PRIMARY\n"); - fprintf(st, " sim> SET DMC0 PEER=LOCALHOST:2222\n"); - fprintf(st, " sim> ATTACH DMC0 1111\n"); - fprintf(st, "\n"); - fprintf(st, "Machine 2\n"); - fprintf(st, " sim> SET DMC0 ENABLE\n"); - fprintf(st, " sim> SET DMC0 SECONDARY\n"); - fprintf(st, " sim> SET DMC0 PEER= LOCALHOST:1111\n"); - fprintf(st, " sim> ATTACH DMC0 2222\n"); - fprintf(st, "\n"); - fprintf(st, "Debugging\n"); - fprintf(st, "=========\n"); - fprintf(st, "The simulator has a number of debug options, these are:\n"); - fprintf(st, " REG Shows whenever a CSR is read or written and the current value.\n"); - fprintf(st, " INFO Shows higher-level tracing only.\n"); - fprintf(st, " WARN Shows any warnings.\n"); - fprintf(st, " TRACE Shows more detailed trace information.\n"); - fprintf(st, " DATA Shows the actual data sent and received.\n"); - fprintf(st, " DATASUM Brief summary of each received and transmitted buffer.\n"); - fprintf(st, " Ignored if DATA is set.\n"); - fprintf(st, " SOCKET Shows socket opens and closes.\n"); - fprintf(st, " CONNECT Shows sockets actually connecting.\n"); - fprintf(st, "\n"); - fprintf(st, "To get a full trace use\n"); - fprintf(st, "\n"); - fprintf(st, " sim> SET DMC0 DEBUG\n"); - fprintf(st, "\n"); - fprintf(st, "However it is recommended to use the following when sending traces:\n"); - fprintf(st, "\n"); - fprintf(st, " sim> SET DMC0 DEBUG=REG;INFO;WARN\n"); - fprintf(st, "\n"); - return SCPE_OK; +const char helpString[] = + /* The '*'s in the next line represent the standard text width of a help line */ + /****************************************************************************/ + " The %1s is a communication subsystem which consists of a microprocessor\n" + " based, intelligent synchronous communications controller which employs\n" + " the DIGITAL Data Communications Message Protocol (DDCMP).\n" + "1 Hardware Description\n" + " The %1s consists of a microprocessor module and a synchronous line unit\n" + " module.\n" +#if !defined (VM_PDP10) + "2 Models\n" + " There were a number of microprocessor DDCMP devices produced.\n" + "3 DMC11\n" + " The original kmc11 microprocessor board with DMC microcode and a sync\n" + " line unit.\n" + "3 DMR11\n" + " The more advanced kmc11 microprocessor board with DMR microcode and a sync\n" + " line unit.\n" + "3 DMP11\n" + " A newly designed Unibus board with a more complete programming interface\n" + " and a sync line unit.\n" + "3 DMV11\n" + " A Qbus version of the DMP11 with some more advanced refinements and a sync\n" + " line unit.\n" +#endif + "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 %1s devices can be emulated concurrently in the %S\n" + " simulator. The number of simulated %1s devices or lines can be\n" + " specified with command:\n" + "\n" + "+sim> SET %U 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" +#if !defined (VM_PDP10) + "3 Type\n" + " The type of device being emulated can be changed with the following\n" + " command:\n" + "\n" + "+sim> SET %U TYPE={DMR,DMC}\n" + "\n" + " A SET TYPE command should be entered before the device is attached to a\n" + " listening port.\n" +#endif + /****************************************************************************/ + "2 Attach\n" + " The device must be attached to a receive port, use the ATTACH command\n" + " specifying the receive port number.\n" + "\n" + "+sim> ATTACH %U port\n" + "\n" + " The Peer host:port value must be specified before the attach command.\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" + "+sim> ATTACH %U port,UDP\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> SET %U PEER=LOCALHOST:2222\n" + "+sim> ATTACH %U 1111\n" + " \n" + " Machine 2\n" + "+sim> SET %D ENABLE\n" + "+sim> SET %U PEER=LOCALHOST:1111\n" + "+sim> ATTACH %U 2222\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 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" + " In real hardware, the DMC11 spoke a version of DDCMP which peer devices\n" + " needed to be aware of. The DMR11, DMP11, and DMV11 boards have\n" + " configuration switches or programatic methods to indicate that the peer\n" + " device was a DMC11. The emulated devices all speak the same level of\n" + " DDCMP so no special remote device awareness need be considered.\n" + "1 Implementation\n" + " A real %1s transports data using DDCMP via a synchronous connection, the\n" + " emulated device makes a TCP/IP connection to another emulated device which\n" + " either speaks DDCMP over the TCP connection directly, or interfaces to a\n" + " simulated computer where the operating system speaks the DDCMP protocol on\n" + " the wire.\n" + "\n" + " The %1s 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" + "++INTREG Shows internal register value changes.\n" + "++INFO Shows higher-level tracing only.\n" + "++WARN Shows any warnings.\n" + "++TRACE Shows more detailed trace information.\n" + "++DATASUM Brief summary of each received and transmitted buffer.\n" + "++++Ignored if DATA is set.\n" + "++DATA Shows the actual data sent and received.\n" + "++MODEM Shows modem signal transitions details.\n" + "++CONNECT Shows sockets connection activity.\n" + "++INT Shows Interrupt activity.\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;INFO;WARN\n" + "\n" + "1 Related Devices\n" + " The %D 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 devname[16]; +char devcount[16]; +char connectpoll[16]; + +sprintf (devname, "%s11" , dptr->name); +sprintf (devcount, "%d", (dptr == &dmc_dev) ? DMC_NUMDEVICE : DMP_NUMDEVICE); +sprintf (connectpoll, "%d", DMC_CONNECT_POLL); + +return scp_help (st, dptr, uptr, flag, helpString, cptr, devname, devcount, connectpoll); } t_stat dmc_help_attach (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) { - //tmxr_attach_help (st, dptr, uptr, flag, 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"); - fprintf (st, "port to be used:\n\n"); - fprintf (st, " sim> ATTACH %s {interface:}port set up listening 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. An ATTACH is required even if in PRIMARY mode. \n\n"); - return SCPE_OK; +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; } -void dmc_setrxint(CTLR *controller) +void dmc_setinint(CTLR *controller) { - controller->rxi = 1; +if (!dmc_is_iei_set(controller)) + return; +if (!controller->in_int) { + sim_debug(DBG_INT, controller->device, "SET_INT(RX:%d)\n", controller->index); + } +controller->in_int = 1; +dmc_ini_summary |= (1u << controller->index); +SET_INT(DMCRX); +} + +void dmc_clrinint(CTLR *controller) +{ +controller->in_int = 0; +if (dmc_ini_summary & (1u << controller->index)) { + sim_debug(DBG_INT, controller->device, "CLR_INT(RX:%d)\n", controller->index); + } +dmc_ini_summary &= ~(1u << controller->index); +if (!dmc_ini_summary) + CLR_INT(DMCRX); +else SET_INT(DMCRX); } -void dmc_clrrxint(CTLR *controller) +void dmc_setoutint(CTLR *controller) { - controller->rxi = 0; - CLR_INT(DMCRX); +if (!dmc_is_ieo_set(controller)) + return; +if (!controller->out_int) { + sim_debug(DBG_INT, controller->device, "SET_INT(TX:%d)\n", controller->index); + } +controller->out_int = 1; +dmc_outi_summary |= (1u << controller->index); +SET_INT(DMCTX); } -void dmc_settxint(CTLR *controller) +void dmc_clroutint(CTLR *controller) { - controller->txi = 1; - SET_INT(DMCTX); -} - -void dmc_clrtxint(CTLR *controller) -{ - controller->txi = 0; +controller->out_int = 0; +if (dmc_outi_summary & (1u << controller->index)) { + sim_debug(DBG_INT, controller->device, "CLR_INT(TX:%d)\n", controller->index); + } +dmc_outi_summary &= ~(1u << controller->index); +if (!dmc_outi_summary) CLR_INT(DMCTX); +else + SET_INT(DMCTX); +CLR_INT(DMCTX); } int dmc_getsel(int addr) { - return (addr >> 1) & 03; +return (addr >> 1) & ((UNIBUS) ? 03 : 07); } uint16 dmc_bitfld(int data, int start_bit, int length) { - uint16 ans = data >> start_bit; - uint32 mask = (1 << (length))-1; - ans &= mask; - return ans; +uint16 ans = data >> start_bit; +uint32 mask = (1 << (length))-1; +ans &= mask; +return ans; } void dmc_dumpregsel0(CTLR *controller, int trace_level, char * prefix, uint16 data) { - char *type_str = ""; - uint16 type = dmc_bitfld(data, SEL0_TYPEI_BIT, 2); +char *type_str = ""; +uint16 type = dmc_bitfld(data, DMC_SEL0_V_ITYPE, DMC_SEL0_S_ITYPE); +static char *dmc_types[] = {"XMT BA/CC", "CNTL IN", "HALT", "BASE IN", "RCV BA/CC", "?????", "?????", "?????"}; - if (dmc_is_dmc(controller)) - { - if (dmc_is_rqi_set(controller)) - { - if (type==TYPE_BACCI) - type_str = "BA/CC I"; - else if (type==TYPE_CNTLI) - type_str = "CNTL I"; - else if (type==TYPE_BASEI) - type_str = "BASE I"; - else - type_str = "?????"; +if (dmc_is_dmc(controller)) { + if (dmc_is_rqi_set(controller)) { + type_str = dmc_types[type]; } - sim_debug( - trace_level, - controller->device, - "%s SEL0 (0x%04x) %s%s%s%s%s%s%s%s%s\n", - prefix, - data, - dmc_bitfld(data, SEL0_RUN_BIT, 1) ? "RUN " : "", - dmc_bitfld(data, SEL0_MCLR_BIT, 1) ? "MCLR " : "", - dmc_bitfld(data, SEL0_LU_LOOP_BIT, 1) ? "LU LOOP " : "", - dmc_bitfld(data, SEL0_ROMI_BIT, 1) ? "ROMI " : "", - dmc_bitfld(data, SEL0_RDI_BIT, 1) ? "RDI " : "", - dmc_bitfld(data, SEL0_DMC_IEI_BIT, 1) ? "IEI " : "", - dmc_bitfld(data, SEL0_DMC_RQI_BIT, 1) ? "RQI " : "", - dmc_bitfld(data, SEL0_IN_IO_BIT, 1) ? "IN I/O " : "", - type_str - ); + sim_debug( + trace_level, + controller->device, + "%s SEL0 (0x%04x) %s%s%s%s%s%s%s%s%s\n", + prefix, + data, + dmc_bitfld(data, DMC_SEL0_V_RUN, 1) ? "RUN " : "", + dmc_bitfld(data, DMC_SEL0_V_MCLEAR, 1) ? "MCLR " : "", + dmc_bitfld(data, DMC_SEL0_V_LU_LOOP, 1) ? "LU LOOP " : "", + dmc_bitfld(data, DMC_SEL0_V_ROMI, 1) ? "ROMI " : "", + dmc_bitfld(data, DMC_SEL0_V_STEPUP, 1) ? "STEPUP " : "", + dmc_bitfld(data, DMC_SEL0_V_RDI, 1) ? "RDI " : "", + dmc_bitfld(data, DMC_SEL0_V_IEI, 1) ? "IEI " : "", + dmc_bitfld(data, DMC_SEL0_V_RQI, 1) ? "RQI " : "", + type_str + ); } - else - { - sim_debug( - trace_level, - controller->device, - "%s SEL0 (0x%04x) %s%s%s%s%s%s\n", - prefix, - data, - dmc_bitfld(data, SEL0_RUN_BIT, 1) ? "RUN " : "", - dmc_bitfld(data, SEL0_MCLR_BIT, 1) ? "MCLR " : "", - dmc_bitfld(data, SEL0_LU_LOOP_BIT, 1) ? "LU LOOP " : "", - dmc_bitfld(data, SEL0_DMP_RQI_BIT, 1) ? "RQI " : "", - dmc_bitfld(data, SEL0_DMP_IEO_BIT, 1) ? "IEO " : "", - dmc_bitfld(data, SEL0_DMP_IEI_BIT, 1) ? "IEI " : "" - ); +else { + sim_debug( + trace_level, + controller->device, + "%s SEL0 (0x%04x) %s%s%s%s%s%s\n", + prefix, + data, + dmc_bitfld(data, DMC_SEL0_V_RUN, 1) ? "RUN " : "", + dmc_bitfld(data, DMC_SEL0_V_MCLEAR, 1) ? "MCLR " : "", + dmc_bitfld(data, DMC_SEL0_V_LU_LOOP, 1) ? "LU LOOP " : "", + dmc_bitfld(data, DMP_SEL0_V_RQI, 1) ? "RQI " : "", + dmc_bitfld(data, DMP_SEL0_V_IEO, 1) ? "IEO " : "", + dmc_bitfld(data, DMP_SEL0_V_IEI, 1) ? "IEI " : "" + ); } } void dmc_dumpregsel2(CTLR *controller, int trace_level, char *prefix, uint16 data) { - char *type_str = ""; - uint16 type = dmc_bitfld(data, SEL2_TYPEO_BIT, 2); +char *type_str = ""; +uint16 type = dmc_bitfld(data, DMC_SEL2_V_CODE, DMC_SEL2_S_CODE); +static char *dmc_types[] = {"XMT BA/CC OUT", "CNTL OUT", "?????", "?????", "RCV BA/CC OUT", "?????", "?????", "?????"}; - if (type==TYPE_BACCO) - type_str = "BA/CC O"; - else if (type==TYPE_CNTLO) - type_str = "CNTL O"; - else - type_str = "?????"; +type_str = dmc_types[type]; - sim_debug( - trace_level, - controller->device, - "%s SEL2 (0x%04x) PRIO=%d LINE=%d %s%s%s%s\n", - prefix, - data, - dmc_bitfld(data, SEL2_PRIO_BIT, SEL2_PRIO_BIT_LENGTH), - dmc_bitfld(data, SEL2_LINE_BIT, SEL2_LINE_BIT_LENGTH), - dmc_bitfld(data, SEL2_RDO_BIT, 1) ? "RDO " : "", - dmc_bitfld(data, SEL2_IEO_BIT, 1) ? "IEO " : "", - dmc_bitfld(data, SEL2_OUT_IO_BIT, 1) ? "OUT I/O " : "", - type_str - ); +sim_debug( + trace_level, + controller->device, + "%s SEL2 (0x%04x) PRIO=%d LINE=%d %s%s%s%s\n", + prefix, + data, + dmc_bitfld(data, SEL2_PRIO_BIT, SEL2_PRIO_BIT_LENGTH), + dmc_bitfld(data, SEL2_LINE_BIT, SEL2_LINE_BIT_LENGTH), + dmc_bitfld(data, SEL2_RDO_BIT, 1) ? "RDO " : "", + dmc_bitfld(data, SEL2_IEO_BIT, 1) ? "IEO " : "", + dmc_bitfld(data, SEL2_OUT_IO_BIT, 1) ? "OUT I/O " : "", + type_str + ); } void dmc_dumpregsel4(CTLR *controller, int trace_level, char *prefix, uint16 data) { - sim_debug(trace_level, controller->device, "%s SEL4 (0x%04x)\n", prefix, data); +if (dmc_is_rdyi_set(controller)) { + sim_debug( + trace_level, + controller->device, + "%s SEL4 (0x%04x) %s%s%s%s%s%s%s%s\n", + prefix, + data, + dmc_bitfld(data, DMC_SEL4_V_RI, 1) ? "RI " : "", + dmc_bitfld(data, DMC_SEL4_V_DTR, 1) ? "DTR " : "", + dmc_bitfld(data, DMC_SEL4_V_RTS, 1) ? "RTS " : "", + dmc_bitfld(data, DMC_SEL4_V_HDX, 1) ? "HDX " : "", + dmc_bitfld(data, DMC_SEL4_V_DSR, 1) ? "DSR " : "", + dmc_bitfld(data, DMC_SEL4_V_CTS, 1) ? "CTS " : "", + dmc_bitfld(data, DMC_SEL4_V_STN, 1) ? "STN " : "", + dmc_bitfld(data, DMC_SEL4_V_CAR, 1) ? "CAR " : ""); + } +else { + sim_debug( + trace_level, + controller->device, + "%s SEL4 (0x%04x)\n", + prefix, + data); + } } void dmc_dumpregsel6(CTLR *controller, int trace_level, char *prefix, uint16 data) { +if (dmc_is_rdyi_set(controller)) { sim_debug( trace_level, controller->device, - "%s SEL6 (0x%04x) %s\n", + "%s SEL6 (0x%04x) %s%s\n", prefix, data, - dmc_bitfld(data, SEL6_LOST_DATA_BIT, 1) ? "LOST_DATA " : ""); + dmc_bitfld(data, DMC_SEL6_M_LOSTDATA, 1) ? "LOST_DATA " : "", + dmc_bitfld(data, DMC_SEL6_M_DISCONN, 1) ? "DISCONNECT " : ""); + } +else { + sim_debug( + trace_level, + controller->device, + "%s SEL6 (0x%04x)\n", + prefix, + data); + } } -uint16 dmc_getreg(CTLR *controller, int reg, int ext) +void dmc_dumpregsel10(CTLR *controller, int trace_level, char *prefix, uint16 data) { - uint16 ans = 0; - switch (dmc_getsel(reg)) - { +sim_debug( + trace_level, + controller->device, + "%s SEL10 (0x%04x) %s\n", + prefix, + data, + dmc_bitfld(data, DMC_SEL6_M_LOSTDATA, 1) ? "LOST_DATA " : ""); +} + +uint16 dmc_getreg(CTLR *controller, int reg, int ctx) +{ +uint16 ans = 0; + +switch (dmc_getsel(reg)) { case 00: - ans = controller->csrs->sel0; - if (ext) dmc_dumpregsel0(controller, DBG_REG, "Getting", ans); + ans = *controller->csrs->sel0; + dmc_dumpregsel0(controller, ctx, "Getting", ans); break; case 01: - ans = controller->csrs->sel2; - if (ext) dmc_dumpregsel2(controller, DBG_REG, "Getting", ans); + ans = *controller->csrs->sel2; + dmc_dumpregsel2(controller, ctx, "Getting", ans); break; case 02: - ans = controller->csrs->sel4; - if (ext) dmc_dumpregsel4(controller, DBG_REG, "Getting", ans); + ans = *controller->csrs->sel4; + dmc_dumpregsel4(controller, ctx, "Getting", ans); break; case 03: - ans = controller->csrs->sel6; - if (ext) dmc_dumpregsel6(controller, DBG_REG, "Getting", ans); + ans = *controller->csrs->sel6; + dmc_dumpregsel6(controller, ctx, "Getting", ans); + break; + case 04: + ans = *controller->csrs->sel10; + dmc_dumpregsel10(controller, ctx, "Getting", ans); break; default: - { - sim_debug(DBG_WRN, controller->device, "dmc_getreg(). Invalid register %d", reg); - } + sim_debug(DBG_WRN, controller->device, "dmc_getreg(). Invalid register %d", reg); + break; } - return ans; +return ans; } -void dmc_setreg(CTLR *controller, int reg, uint16 data, int ext) +void dmc_setreg(CTLR *controller, int reg, uint16 data, int ctx) { - char *trace = (ext) ? "Writing" : "Setting"; - switch (dmc_getsel(reg)) - { +char *trace = "Setting"; + +switch (dmc_getsel(reg)) { case 00: - dmc_dumpregsel0(controller, DBG_REG, trace, data); - controller->csrs->sel0 = data; - if (!ext) - { - controller->shadow_csrs->sel0 = data; - } + dmc_dumpregsel0(controller, ctx, trace, data); + *controller->csrs->sel0 = data; break; case 01: - dmc_dumpregsel2(controller, DBG_REG, trace, data); - controller->csrs->sel2 = data; - if (!ext) - { - controller->shadow_csrs->sel2 = data; - } + dmc_dumpregsel2(controller, ctx, trace, data); + *controller->csrs->sel2 = data; break; case 02: - dmc_dumpregsel4(controller, DBG_REG, trace, data); - controller->csrs->sel4 = data; - if (!ext) - { - controller->shadow_csrs->sel4 = data; - } + dmc_dumpregsel4(controller, ctx, trace, data); + *controller->csrs->sel4 = data; break; case 03: - dmc_dumpregsel6(controller, DBG_REG, trace, data); - controller->csrs->sel6 = data; - if (!ext) - { - controller->shadow_csrs->sel6 = data; - } + dmc_dumpregsel6(controller, ctx, trace, data); + *controller->csrs->sel6 = data; + break; + case 04: + dmc_dumpregsel10(controller, ctx, trace, data); + *controller->csrs->sel10 = data; break; default: - { - sim_debug(DBG_WRN, controller->device, "dmc_setreg(). Invalid register %d", reg); - } + sim_debug(DBG_WRN, controller->device, "dmc_setreg(). Invalid register %d", reg); } } int dmc_is_master_clear_set(CTLR *controller) { - return controller->csrs->sel0 & MASTER_CLEAR_MASK; +return *controller->csrs->sel0 & DMC_SEL0_M_MCLEAR; } int dmc_is_lu_loop_set(CTLR *controller) { - return controller->csrs->sel0 & LU_LOOP_MASK; +if (dmc_is_dmc(controller)) + return ((*controller->csrs->sel0 & DMC_SEL0_M_LU_LOOP) != 0); +else + return FALSE; +} + +int dmc_is_run_set(CTLR *controller) +{ +return *controller->csrs->sel0 & DMC_SEL0_M_RUN; } int dmc_is_rqi_set(CTLR *controller) { - int ans = 0; - if (dmc_is_dmc(controller)) - { - ans = controller->csrs->sel0 & DMC_RQI_MASK; - } - else - { - ans = controller->csrs->sel0 & DMP_RQI_MASK; - } - return ans; +int ans = 0; + +if (dmc_is_dmc(controller)) + ans = *controller->csrs->sel0 & DMC_SEL0_M_RQI; +else + ans = *controller->csrs->sel0 & DMP_SEL0_M_RQI; +return ans; } int dmc_is_rdyi_set(CTLR *controller) { - int ans = 0; - if (dmc_is_dmc(controller)) - { - ans = controller->csrs->sel0 & DMC_RDYI_MASK; - } - else - { - ans = controller->csrs->sel2 & DMP_RDYI_MASK; - } - return ans; +int ans = 0; + +if (dmc_is_dmc(controller)) + ans = *controller->csrs->sel0 & DMC_SEL0_M_RDI; +else + ans = *controller->csrs->sel2 & DMP_SEL2_M_RDI; +return ans; } int dmc_is_iei_set(CTLR *controller) { - int ans = 0; - if (dmc_is_dmc(controller)) - { - ans = controller->csrs->sel0 & DMC_IEI_MASK; - } - else - { - ans = controller->csrs->sel0 & DMP_IEI_MASK; - } +int ans = 0; - return ans; +if (dmc_is_dmc(controller)) + ans = *controller->csrs->sel0 & DMC_SEL0_M_IEI; +else + ans = *controller->csrs->sel0 & DMP_SEL0_M_IEI; +return ans; } + int dmc_is_ieo_set(CTLR *controller) { - int ans = 0; - if (dmc_is_dmc(controller)) - { - ans = controller->csrs->sel2 & DMC_IEO_MASK; - } - else - { - ans = controller->csrs->sel0 & DMP_IEO_MASK; - } +int ans = 0; - return ans; +if (dmc_is_dmc(controller)) + ans = *controller->csrs->sel2 & DMC_SEL2_M_IEO; +else + ans = *controller->csrs->sel0 & DMP_SEL0_M_IEO; +return ans; } -int dmc_is_in_io_set(CTLR *controller) -{ - int ans = 0; - if (dmc_is_dmc(controller)) - { - ans = controller->csrs->sel0 & DMC_IN_IO_MASK; - } - else - { - ans = !controller->csrs->sel2 & DMP_IN_IO_MASK; - } - return ans; -} -int dmc_is_out_io_set(CTLR *controller) +int dmc_is_in_io(CTLR *controller) { - int ans = controller->shadow_csrs->sel2 & OUT_IO_MASK; - return ans; +int ans = 0; + +if (dmc_is_dmc(controller)) + ans = ((*controller->csrs->sel0 & DMC_SEL0_M_ITYPE) == DMC_C_TYPE_RBACC); +else + ans = ((*controller->csrs->sel2 & DMP_SEL2_M_CODE) == DMP_C_TYPE_RBACC); +return ans; } int dmc_is_rdyo_set(CTLR *controller) { - return controller->csrs->sel2 & DMC_RDYO_MASK; +return *controller->csrs->sel2 & DMC_SEL2_M_RDO; } void dmc_set_rdyi(CTLR *controller) { - if (dmc_is_dmc(controller)) - { - dmc_setreg(controller, 0, controller->csrs->sel0 | DMC_RDYI_MASK, 0); - } - else - { - dmc_setreg(controller, 2, controller->csrs->sel2 | DMP_RDYI_MASK, 0); - } - - if (dmc_is_iei_set(controller)) - { - dmc_setrxint(controller); +if (dmc_is_dmc(controller)) { + dmc_setreg(controller, 0, *controller->csrs->sel0 | DMC_SEL0_M_RDI, DBG_RGC); + dmc_setreg(controller, 4, *controller->modem | 0x0800, DBG_RGC); + dmc_setreg(controller, 6, *controller->modem & DMC_SEL4_M_DTR, DBG_RGC); } +else + dmc_setreg(controller, 2, *controller->csrs->sel2 | DMP_SEL2_M_RDI, DBG_RGC); +dmc_setinint(controller); } void dmc_clear_rdyi(CTLR *controller) { - if (dmc_is_dmc(controller)) - { - dmc_setreg(controller, 0, controller->csrs->sel0 & ~DMC_RDYI_MASK, 0); - } - else - { - dmc_setreg(controller, 2, controller->csrs->sel2 & ~DMP_RDYI_MASK, 0); - } +if (dmc_is_dmc(controller)) + dmc_setreg(controller, 0, *controller->csrs->sel0 & ~(DMC_SEL0_M_RDI|DMC_SEL0_M_ITYPE), DBG_RGC); +else + dmc_setreg(controller, 2, *controller->csrs->sel2 & ~DMP_SEL2_M_RDI, DBG_RGC); } void dmc_set_rdyo(CTLR *controller) { - dmc_setreg(controller, 2, controller->csrs->sel2 | DMC_RDYO_MASK, 0); +dmc_setreg(controller, 2, *controller->csrs->sel2 | DMC_SEL2_M_RDO, DBG_RGC); - if (dmc_is_ieo_set(controller)) - { - dmc_settxint(controller); +dmc_setoutint(controller); +} + +uint32 dmc_get_addr(CTLR *controller) +{ +if (dmc_is_dmc(controller) || (!(*controller->csrs->sel2 & DMP_SEL2_M_22BIT))) + return ((uint32)dmc_getreg(controller, 4, DBG_RGC)) | ((((uint32)dmc_getreg(controller, 6, DBG_RGC) & 0xc000)) << 2); +else + return ((uint32)dmc_getreg(controller, 4, DBG_RGC)) | (((uint32)dmc_getreg(controller, 6, DBG_RGC) & 0x3FF) << 16); +} + +void dmc_set_addr(CTLR *controller, uint32 addr) +{ +if (dmc_is_dmc(controller) || (!(*controller->csrs->sel2 & DMP_SEL2_M_22BIT))) { + dmc_setreg(controller, 4, addr & 0xFFFF, DBG_RGC); + dmc_setreg(controller, 6, ((addr >> 2) << 14) | (*controller->csrs->sel6 & 0x3FFF) , DBG_RGC); } +else { + dmc_setreg(controller, 4, addr & 0xFFFF, DBG_RGC); + dmc_setreg(controller, 6, ((addr >> 16) & 0x3F), DBG_RGC); + } +} + +uint16 dmc_get_count(CTLR *controller) +{ +if (dmc_is_dmc(controller) || (!(*controller->csrs->sel2 & DMP_SEL2_M_22BIT))) + return dmc_getreg(controller, 6, DBG_RGC) & 0x3FFF; +else + return dmc_getreg(controller, 010, DBG_RGC) & 0x3FFF; +} + +void dmc_set_count(CTLR *controller, uint16 count) +{ +if (dmc_is_dmc(controller) || (!(*controller->csrs->sel2 & DMP_SEL2_M_22BIT))) + dmc_setreg(controller, 6, (*controller->csrs->sel6 & 0xc000) | (0x3FFF & count), DBG_RGC); +else + dmc_setreg(controller, 010, (*controller->csrs->sel10 & 0xc000) | (0x3FFF & count), DBG_RGC); +} + +uint8 dmc_get_modem(CTLR *controller) +{ +int32 modem_bits; + +tmxr_set_get_modem_bits (controller->line, 0, 0, &modem_bits); +*controller->modem &= ~(DMC_SEL4_M_CTS|DMC_SEL4_M_CAR|DMC_SEL4_M_RI|DMC_SEL4_M_DSR|DMC_SEL4_M_DTR|DMC_SEL4_M_RTS); +*controller->modem |= (modem_bits&TMXR_MDM_DCD) ? DMC_SEL4_M_CAR : 0; +*controller->modem |= (modem_bits&TMXR_MDM_CTS) ? DMC_SEL4_M_CTS : 0; +*controller->modem |= (modem_bits&TMXR_MDM_DSR) ? DMC_SEL4_M_DSR : 0; +*controller->modem |= (modem_bits&TMXR_MDM_RNG) ? DMC_SEL4_M_RI : 0; +*controller->modem |= (modem_bits&TMXR_MDM_DTR) ? DMC_SEL4_M_DTR : 0; +*controller->modem |= (modem_bits&TMXR_MDM_RTS) ? DMC_SEL4_M_RTS : 0; +return (*controller->modem); +} + +void dmc_set_modem_dtr(CTLR *controller) +{ +if (dmc_is_attached(controller->unit) && (!(DMC_SEL4_M_DTR & *controller->modem))) { + sim_debug(DBG_MDM, controller->device, "DTR State Change to UP(ON)\n"); + tmxr_set_get_modem_bits (controller->line, TMXR_MDM_DTR|TMXR_MDM_RTS, 0, NULL); + dmc_get_modem(controller); + controller->line->rcve = 1; + } +} + +void dmc_clr_modem_dtr(CTLR *controller) +{ +if (*controller->modem & DMC_SEL4_M_DTR) { + sim_debug(DBG_MDM, controller->device, "DTR State Change to DOWN(OFF)\n"); + } +tmxr_set_get_modem_bits (controller->line, 0, TMXR_MDM_DTR|TMXR_MDM_RTS, NULL); +dmc_get_modem(controller); } void dmc_set_lost_data(CTLR *controller) { - dmc_setreg(controller, 6, controller->csrs->sel6 | LOST_DATA_MASK, 0); +dmc_setreg(controller, 6, *controller->csrs->sel6 | DMC_SEL6_M_LOSTDATA, DBG_RGC); } void dmc_clear_master_clear(CTLR *controller) { - dmc_setreg(controller, 0, controller->csrs->sel0 & ~MASTER_CLEAR_MASK, 0); +dmc_setreg(controller, 0, *controller->csrs->sel0 & ~DMC_SEL0_M_MCLEAR, DBG_RGC); } void dmc_set_run(CTLR *controller) { - dmc_setreg(controller, 0, controller->csrs->sel0 | RUN_MASK, 0); +dmc_setreg(controller, 0, *controller->csrs->sel0 | DMC_SEL0_M_RUN, DBG_RGC); } int dmc_get_input_transfer_type(CTLR *controller) { - int ans = 0; +int ans = 0; - if (dmc_is_dmc(controller)) - { - ans = controller->csrs->sel0 & DMC_TYPE_INPUT_MASK; - } - else - { - ans = controller->csrs->sel2 & DMP_TYPE_INPUT_MASK; - } +if (dmc_is_dmc(controller)) + ans = *controller->csrs->sel0 & DMC_SEL0_M_ITYPE; +else + ans = *controller->csrs->sel2 & DMP_SEL2_M_CODE; +return ans; +} - return ans; -} -int dmc_get_output_transfer_type(CTLR *controller) -{ - return controller->shadow_csrs->sel2 & TYPE_OUTPUT_MASK; -} void dmc_set_type_output(CTLR *controller, int type) { - dmc_setreg(controller, 2, controller->csrs->sel2 | (type & TYPE_OUTPUT_MASK), 0); -} - -void dmc_set_out_io(CTLR *controller) -{ - dmc_setreg(controller, 2, controller->csrs->sel2 | OUT_IO_MASK, 0); -} - -void dmc_clear_out_io(CTLR *controller) -{ - dmc_setreg(controller, 2, controller->csrs->sel2 & ~OUT_IO_MASK, 0); +dmc_setreg(controller, 2, (*controller->csrs->sel2 & ~DMC_SEL2_M_CODE) | (type & DMC_SEL2_M_CODE), DBG_RGC); } void dmc_process_master_clear(CTLR *controller) { - sim_debug(DBG_INF, controller->device, "Master clear\n"); - dmc_clear_master_clear(controller); - dmc_close_socket(controller, "Master clear"); /* to resynch both ends */ - controller->state = Initialised; - dmc_setreg(controller, 0, 0, 0); - if (controller->dev_type == DMR) - { - /* DMR-11 indicates microdiagnostics complete when this is set */ - dmc_setreg(controller, 2, 0x8000, 0); - } - else - { - /* preserve contents of BSEL3 if DMC-11 */ - dmc_setreg(controller, 2, controller->csrs->sel2 & 0xFF00, 0); - } - if (controller->dev_type == DMP) - { - dmc_setreg(controller, 4, 077, 0); - } - else - { - dmc_setreg(controller, 4, 0, 0); - } +CONTROL_OUT *control; - if (controller->dev_type == DMP) - { - dmc_setreg(controller, 6, 0305, 0); +sim_debug(DBG_INF, controller->device, "Master clear\n"); +dmc_clear_master_clear(controller); +dmc_clr_modem_dtr(controller); +controller->link.state = Halt; +controller->state = Initialised; +while ((control = controller->control_out)) { + controller->control_out = control->next; + free (control); } - else - { - dmc_setreg(controller, 6, 0, 0); +controller->control_out = NULL; +dmc_setreg(controller, 0, 0, DBG_RGC); +if (controller->dev_type == DMR) { + /* DMR-11 indicates microdiagnostics complete when this is set */ + dmc_setreg(controller, 2, 0x8000, DBG_RGC); } - dmc_buffer_queue_init_all(controller); +else { + /* preserve contents of BSEL3 if DMC-11 */ + dmc_setreg(controller, 2, *controller->csrs->sel2 & 0xFF00, DBG_RGC); + } +if (controller->dev_type == DMP) + dmc_setreg(controller, 4, 077, DBG_RGC); +else + dmc_setreg(controller, 4, 0, DBG_RGC); - controller->transfer_state = Idle; - dmc_set_run(controller); +if (controller->dev_type == DMP) + dmc_setreg(controller, 6, 0305, DBG_RGC); +else + dmc_setreg(controller, 6, 0, DBG_RGC); +dmc_buffer_queue_init_all(controller); - sim_cancel (controller->device->units); /* stop poll */ - sim_clock_coschedule (controller->device->units, tmxr_poll); /* reactivate */ +controller->transfer_state = Idle; +dmc_set_run(controller); } void dmc_start_input_transfer(CTLR *controller) { - int ok = 1; - int type = dmc_get_input_transfer_type(controller); - - /* if this is a BA/CC I then check that the relevant queue has room first */ - if (type == TYPE_BACCI) - { - ok = (dmc_is_in_io_set(controller) && !dmc_buffer_queue_full(controller->receive_queue)) - || - (!dmc_is_in_io_set(controller) && !dmc_buffer_queue_full(controller->transmit_queue)); - } - - if (ok) - { - sim_debug(DBG_INF, controller->device, "Starting input transfer\n"); - controller->transfer_state = InputTransfer; - controller->transfer_type = type; - controller->transfer_in_io = dmc_is_in_io_set(controller); - dmc_set_rdyi(controller); - } - else - { - sim_debug(DBG_WRN, controller->device, "Input transfer request not granted as queue is full\n"); - } -} - -void dmc_start_data_output_transfer(CTLR *controller, uint32 addr, int16 count, int is_receive) -{ - if (is_receive) - { - sim_debug(DBG_INF, controller->device, "Starting data output transfer for receive, address=0x%08x, count=%d\n", addr, count); - dmc_set_out_io(controller); - } - else - { - sim_debug(DBG_INF, controller->device, "Starting data output transfer for transmit, address=0x%08x, count=%d\n", addr, count); - dmc_clear_out_io(controller); - } - - dmc_setreg(controller, 4, addr & 0xFFFF, 0); - dmc_setreg(controller, 6, (((addr & 0x30000)) >> 2) | count, 0); - controller->transfer_state = OutputTransfer; - dmc_set_type_output(controller, TYPE_BACCO); - dmc_set_rdyo(controller); +sim_debug(DBG_INF, controller->device, "Starting input transfer\n"); +controller->transfer_state = InputTransfer; +dmc_set_rdyi(controller); } void dmc_start_control_output_transfer(CTLR *controller) { - sim_debug(DBG_INF, controller->device, "Starting control output transfer\n"); - controller->transfer_state = OutputTransfer; - dmc_set_type_output(controller, TYPE_CNTLO); - dmc_set_rdyo(controller); +if ((!controller->control_out) || + (controller->transfer_state != Idle) || + (dmc_is_rdyo_set(controller))) + return; +sim_debug(DBG_INF, controller->device, "Starting control output transfer: SEL6 = 0x%04X\n", controller->control_out->sel6); +controller->transfer_state = OutputControl; +dmc_setreg (controller, 6, controller->control_out->sel6, DBG_RGC); +dmc_set_type_output(controller, DMC_C_TYPE_CNTL); +dmc_set_rdyo(controller); +} + +void dmc_complete_transmit(CTLR *controller) +{ +BUFFER *buffer = controller->link.xmt_buffer; +if (!buffer) + return; +controller->link.xmt_done_buffer = buffer; +ddcmp_dispatch (controller, DDCMP_EVENT_XMIT_DONE); +controller->link.xmt_done_buffer = NULL; +buffer = (BUFFER *)remqueue (&buffer->hdr); +buffer->actual_bytes_transferred = buffer->count; +controller->buffers_transmitted_to_net++; +if (buffer->type == TransmitData) { + buffer->actual_bytes_transferred = buffer->count - (DDCMP_HEADER_SIZE + DDCMP_CRC_SIZE); + assert (insqueue (&buffer->hdr, controller->ack_wait_queue->hdr.prev)); + } +else { + assert (insqueue (&buffer->hdr, &controller->free_queue->hdr)); + } +controller->link.xmt_buffer = NULL; } t_stat dmc_svc(UNIT* uptr) { - CTLR *controller; - TIMER *poll_timer; - TIMER *between_polls_timer; +CTLR *controller = dmc_get_controller_from_unit(uptr); +DEVICE *dptr = controller->device; - controller = dmc_get_controller_from_unit(uptr); +sim_debug(DBG_TRC, dptr, "dmc_svc()\n"); - poll_timer = &controller->stats->poll_timer; - between_polls_timer = &controller->stats->between_polls_timer; - - if (dmc_timer_started(between_polls_timer)) - { - dmc_timer_stop(between_polls_timer); - } - - if (dmc_timer_started(poll_timer)) - { - dmc_timer_resume(poll_timer); - } - else - { - dmc_timer_start(poll_timer); - } - - if (dmc_is_attached(controller->device->units)) - { - dmc_line_update_speed_stats(controller->line); - - dmc_buffer_fill_receive_buffers(controller); - if (controller->transfer_state == Idle) dmc_start_transfer_receive_buffer(controller); - - dmc_buffer_send_transmit_buffers(controller); - if (controller->transfer_state == Idle) dmc_start_transfer_transmit_buffer(controller); - } - - /* resubmit service timer */ - sim_clock_coschedule (controller->device->units, tmxr_poll); - - dmc_timer_stop(poll_timer); - if (dmc_timer_started(between_polls_timer)) - { - dmc_timer_resume(between_polls_timer); - } - else - { - dmc_timer_start(between_polls_timer); - } - controller->stats->poll_count++; - - return SCPE_OK; -} - -void dmc_line_update_speed_stats(LINE *line) -{ - clock_t current = clock(); - int current_second = current / CLOCKS_PER_SEC; - if (current_second != line->last_second) - { - line->bytes_received_in_last_second = 0; - line->bytes_sent_in_last_second = 0; - line->last_second = current_second; - } -} - -/* given the number of bytes sent/received in the last second, the number of bytes to send or receive and the line speed, calculate how many bytes can be sent/received now */ -int dmc_line_speed_calculate_byte_length(int bytes_in_last_second, int num_bytes, int speed) -{ - int ans; - - if (speed == 0) - { - ans = num_bytes; - } - else - { - int clocks_this_second = clock() % CLOCKS_PER_SEC; - int allowable_bytes_to_date = ((speed/8) * clocks_this_second)/CLOCKS_PER_SEC; - int allowed_bytes = allowable_bytes_to_date - bytes_in_last_second; - if (allowed_bytes < 0) - { - allowed_bytes = 0; - } - - if (num_bytes > allowed_bytes) - { - ans = allowed_bytes; - } - else - { - ans = num_bytes; - } -//sim_debug(DBG_WRN, dmc_ctrls[0].device, "Bytes in last second %4d, clocks this sec %3d allowable bytes %4d, requested %4d allowed %4d\n", bytes_in_last_second, clocks_this_second, allowable_bytes_to_date, num_bytes, ans); - } - - return ans; -} - -void dmc_buffer_trace_line(int tracelevel, CTLR *controller, uint8 *buf, int length, char *prefix) -{ - char hex[TRACE_BYTES_PER_LINE*3+1]; - char ascii[TRACE_BYTES_PER_LINE+1]; - int i; - hex[0] = 0; - ascii[TRACE_BYTES_PER_LINE] = 0; - - for (i = 0; i=length) - { - strcat(hex, " "); - ascii[i] = ' '; - } - else - { - char hexByte[4]; - sprintf(hexByte, "%02X ", buf[i]); - strcat(hex, hexByte); - if (isprint(buf[i])) - { - ascii[i] = (char)buf[i]; +if (dmc_is_attached(controller->unit)) { + /* Perform delayed register actions */ + if (controller->dmc_wr_delay) { + controller->dmc_wr_delay = 0; + dmc_process_command(controller); + if (controller->link.xmt_buffer) { + sim_activate_notbefore (uptr, controller->link.xmt_buffer->buffer_return_time); + return SCPE_OK; } + } + /* Speed limited transmissions are completed here after the appropriate service delay */ + if (controller->link.xmt_buffer) { + dmc_complete_transmit(controller); + dmc_ddcmp_start_transmitter (controller); + } + + dmc_start_control_output_transfer(controller); + + if (ddcmp_UserSendMessage (controller)) + ddcmp_dispatch (controller, 0); + if (controller->transfer_state == Idle) + dmc_start_transfer_buffer(controller); + + dmc_buffer_fill_receive_buffers(controller); + if (controller->transfer_state == Idle) + dmc_start_transfer_buffer(controller); + /* Execution of this routine normally is triggered by programatic access by the + simulated system's device driver to the device registers or the arrival of + traffic from the network. Network traffic is handled by the dmc_poll_svc + which may call this routine as needed. Direct polling here is extra overhead + and is only scheduled when needed. + */ + if ((controller->completion_queue->count) || /* if completion queue not empty? */ + (controller->control_out) || /* or pending control outs? */ + (controller->transfer_state != Idle)) /* or registers are busy */ + sim_activate (uptr, tmxr_poll); /* wake up periodically until these don't exist */ + } + +return SCPE_OK; +} + +t_stat dmc_poll_svc (UNIT *uptr) +{ +DEVICE *dptr = ((CTLR *)uptr->ctlr)->device; +CTLR *controller; +int maxunits = (&dmc_dev == dptr) ? DMC_NUMDEVICE : DMP_NUMDEVICE; +DIB *dibptr = (DIB *)dptr->ctxt; +int addrlnt = (UNIBUS) ? IOLN_DMC : IOLN_DMV; +TMXR *mp = (dptr == &dmc_dev) ? &dmc_desc : &dmp_desc; +int32 dmc, active, attached; + +sim_debug(DBG_TRC, dptr, "dmc_poll_svc()\n"); + +dmc = tmxr_poll_conn(mp); +if (dmc >= 0) { /* new connection? */ + controller = (CTLR *)dptr->units[dmc].ctlr; + dmc_get_modem (controller); + sim_debug(DBG_MDM, dptr, "dmc_poll_svc(dmc=%d) - Connection State Change to UP(ON)\n", dmc); + ddcmp_dispatch (controller, 0); + sim_activate (controller->unit, tmxr_poll); /* be sure to wake up soon to continue processing */ + } +tmxr_poll_rx (mp); +tmxr_poll_tx (mp); +for (dmc=active=attached=0; dmc < mp->lines; dmc++) { + CTLR *controller = (CTLR *)dptr->units[dmc].ctlr; + TMLN *lp = controller->line; + uint8 old_modem, new_modem; + + controller = (CTLR *)dptr->units[dmc].ctlr; + if (controller->unit->flags & UNIT_ATT) + ++attached; + if (lp->conn) + ++active; + old_modem = *controller->modem; + new_modem = dmc_get_modem (controller); + if ((old_modem & DMC_SEL4_M_CAR) && + (!(new_modem & DMC_SEL4_M_CAR))) { + sim_debug(DBG_MDM, controller->device, "dmc_poll_svc(dmc=%d) - Connection State Change to %s\n", dmc, (new_modem & DMC_SEL4_M_CAR) ? "UP(ON)" : "DOWN(OFF)"); + ddcmp_dispatch (controller, 0); + sim_activate (controller->unit, tmxr_poll); /* wake up soon to finish processing */ + } + if ((lp->xmte && tmxr_tpbusyln(lp)) || + (lp->xmte && controller->link.xmt_buffer) || + ddcmp_UserSendMessage(controller)) { + if (tmxr_tpbusyln(lp)) { + sim_debug(DBG_DAT, controller->device, "dmc_poll_svc(dmc=%d) - Packet Transmission of remaining %d bytes restarting...\n", dmc, tmxr_tpqln (lp)); + } + dmc_svc (&controller->device->units[dmc]); /* Flush pending output */ + } + if (dmc_buffer_fill_receive_buffers(controller)) + dmc_svc (&controller->device->units[dmc]); /* return ACKed transmit packets */ + } +if (active) + sim_clock_coschedule (uptr, tmxr_poll); /* reactivate */ +else { + for (dmc=0; dmc < mp->lines; dmc++) { + uint32 *speeds = (dptr == &dmc_dev) ? dmc_speed : dmp_speed; + CTLR *controller = (CTLR *)dptr->units[dmc].ctlr; + + if (speeds[dmc]/8) + controller->byte_wait = (tmr_poll*clk_tps)/(speeds[dmc]/8); + else + controller->byte_wait = 0; + } + if (attached) + sim_activate_after (uptr, DMC_CONNECT_POLL*1000000);/* periodic check for connections */ + } +return SCPE_OK; +} + +t_stat dmc_timer_svc (UNIT *uptr) +{ +int32 dmc, active; +DEVICE *dptr = (UNIBUS) ? ((&dmc_dev == find_dev_from_unit(uptr)) ? &dmc_dev : &dmp_dev) : &dmv_dev; +TMXR *mp = (dptr == &dmc_dev) ? &dmc_desc : &dmp_desc; +CTLR *controller; + +sim_debug(DBG_TRC, dptr, "dmc_timer_svc()\n"); + +for (dmc=active=0; dmc < mp->lines; dmc++) { + controller = (CTLR *)dptr->units[dmc].ctlr; + if (controller->link.TimerRunning) { + if (controller->link.TimeRemaining > 0) { + --controller->link.TimeRemaining; + if (controller->link.TimeRemaining > 0) + ++active; else - { - ascii[i] = '.'; + ddcmp_dispatch (controller, DDCMP_EVENT_TIMER); /* Timer Expiring Event */ } } } - - sim_debug(tracelevel, controller->device, "%s %s %s\n", prefix, hex, ascii); +if (active) + sim_activate_after (uptr, 1000000); /* Per Second Event trigger */ +return SCPE_OK; } -void dmc_buffer_trace(CTLR *controller, uint8 *buf, int length, char *prefix, uint32 address) +void dmc_buffer_queue_init(CTLR *controller, BUFFER_QUEUE *q, char *name, size_t size, BUFFER *buffers) { - int i; - if (length >= 0 && controller->device->dctrl & DBG_DAT) - { - sim_debug(DBG_DAT, controller->device, "%s Buffer address 0x%08x (%d bytes)\n", prefix, address, length); - for(i = 0; i < length / TRACE_BYTES_PER_LINE; i++) - { - dmc_buffer_trace_line(DBG_DAT, controller, buf + i*TRACE_BYTES_PER_LINE, TRACE_BYTES_PER_LINE, prefix); - } - - if (length %TRACE_BYTES_PER_LINE > 0) - { - dmc_buffer_trace_line(DBG_DAT, controller, buf + length/TRACE_BYTES_PER_LINE, length % TRACE_BYTES_PER_LINE, prefix); - } - } - else if (length >= 0 && controller->device->dctrl & DBG_DTS) - { - char prefix2[80]; - sprintf(prefix2, "%s (len=%d)", prefix, length); - dmc_buffer_trace_line(DBG_DTS, controller, buf, (length > TRACE_BYTES_PER_LINE)? TRACE_BYTES_PER_LINE : length, prefix2); - } +q->name = name; +initqueue (&q->hdr, q, size, buffers, sizeof(*buffers)); +q->controller = controller; } -void dmc_buffer_queue_init(CTLR *controller, BUFFER_QUEUE *q, char *name) +t_bool dmc_transmit_queue_empty(CTLR *controller) { - q->name = name; - q->head = 0; - q->tail = 0; - q->count = 0; - q->controller = controller; +return (controller->xmt_queue->count == 0); } void dmc_buffer_queue_init_all(CTLR *controller) { - dmc_buffer_queue_init(controller, controller->receive_queue, "receive"); - dmc_buffer_queue_init(controller, controller->transmit_queue, "transmit"); +size_t queue_size = (controller->dev_type == DMC) ? DMC_QUEUE_SIZE : + ((controller->dev_type == DMR) ? DMR_QUEUE_SIZE : + DMP_QUEUE_SIZE); +queue_size *= 2; +*controller->buffers = (BUFFER *)realloc(*controller->buffers, queue_size*sizeof(**controller->buffers)); +memset (*controller->buffers, 0, queue_size*sizeof(**controller->buffers)); +dmc_buffer_queue_init(controller, controller->rcv_queue, "receive", 0, NULL); +dmc_buffer_queue_init(controller, controller->completion_queue, "completion", 0, NULL); +dmc_buffer_queue_init(controller, controller->xmt_queue, "transmit", 0, NULL); +dmc_buffer_queue_init(controller, controller->ack_wait_queue, "ack wait", 0, NULL); +dmc_buffer_queue_init(controller, controller->free_queue, "free", queue_size, *controller->buffers); } -int dmc_buffer_queue_full(BUFFER_QUEUE *q) +BUFFER *dmc_buffer_allocate(CTLR *controller) { - return q->count > BUFFER_QUEUE_SIZE; -} +BUFFER *buffer = (BUFFER *)remqueue (controller->free_queue->hdr.next); -void dmc_buffer_queue_add(BUFFER_QUEUE *q, uint32 address, uint16 count) -{ - if (!dmc_buffer_queue_full(q)) - { - int new_buffer = 0; - if (q->count > 0) - { - int last_buffer = q->tail; - new_buffer = (q->tail +1) % BUFFER_QUEUE_SIZE; - - /* Link last buffer to the new buffer */ - q->queue[last_buffer].next = &q->queue[new_buffer]; +if (!buffer) { + fprintf (stdout, "DDCMP Buffer allocation failure.\n"); + fprintf (stdout, "This is a fatal error which should never happen.\n"); + fprintf (stdout, "Please submit this output when you report this bug.\n"); + dmc_showqueues (stdout, controller->unit, 0, NULL); + dmc_showstats (stdout, controller->unit, 0, NULL); + dmc_showddcmp (stdout, controller->unit, 0, NULL); + if (sim_log) { + fprintf (sim_log, "DDCMP Buffer allocation failure.\n"); + fprintf (sim_log, "This is a fatal error which should never happen.\n"); + fprintf (sim_log, "Please submit this output when you report this bug.\n"); + dmc_showqueues (sim_log, controller->unit, 0, NULL); + dmc_showstats (sim_log, controller->unit, 0, NULL); + dmc_showddcmp (sim_log, controller->unit, 0, NULL); + fflush (sim_log); } - else - { - q->head = 0; - new_buffer = 0; - } - - q->tail = new_buffer; - q->queue[new_buffer].address = address; - q->queue[new_buffer].count = count; - q->queue[new_buffer].actual_block_len = 0; - q->queue[new_buffer].transfer_buffer = NULL; - q->queue[new_buffer].block_len_bytes_read = 0; - q->queue[new_buffer].actual_bytes_transferred = 0; - q->queue[new_buffer].next = NULL; - q->queue[new_buffer].state = Available; - q->queue[new_buffer].is_loopback = dmc_is_lu_loop_set(q->controller); - q->count++; - sim_debug(DBG_INF, q->controller->device, "Queued %s buffer address=0x%08x count=%d\n", q->name, address, count); - } - else - { - sim_debug(DBG_WRN, q->controller->device, "Failed to queue %s buffer address=0x%08x, queue full\n", q->name, address); - // TODO: Report error here. + return buffer; } +buffer->address = 0; +buffer->count = 0; +free (buffer->transfer_buffer); +buffer->transfer_buffer = NULL; +buffer->actual_bytes_transferred = 0; +buffer->type = TransmitControl; /* Default */ +buffer->buffer_return_time = 0; +return buffer; } -void dmc_buffer_queue_release_head(BUFFER_QUEUE *q) +BUFFER *dmc_buffer_queue_add(BUFFER_QUEUE *q, uint32 address, uint16 count, BufferType type) { - if (q->count > 0) - { - q->head = (q->head + 1) % BUFFER_QUEUE_SIZE; - q->count--; +BUFFER *buffer = dmc_buffer_allocate(q->controller); + +if (buffer) { + buffer->type = type; + buffer->address = address; + buffer->count = count; + assert (insqueue (&buffer->hdr, q->hdr.prev)); /* Insert at tail */ + sim_debug(DBG_INF, q->controller->device, "Queued %s buffer address=0x%08x count=%d\n", q->name, address, count); } - else - { - sim_debug(DBG_INF, q->controller->device, "Failed to release %s buffer, queue already empty\n", q->name); +else { + sim_debug(DBG_WRN, q->controller->device, "Failed to queue %s buffer address=0x%08x, queue full\n", q->name, address); + // TODO: Report error here. } +return buffer; } + BUFFER *dmc_buffer_queue_head(BUFFER_QUEUE *q) { - BUFFER *ans = NULL; - if (q->count >0) - { - ans = &q->queue[q->head]; - } - - return ans; +return ((q->hdr.next == &q->hdr) ? NULL : (BUFFER *)q->hdr.next); } -BUFFER *dmc_buffer_queue_find_first_available(BUFFER_QUEUE *q) +void dmc_queue_control_out(CTLR *controller, uint16 sel6) { - BUFFER *ans = dmc_buffer_queue_head(q); - while (ans != NULL) - { - if (ans->state == Available) - { - break; - } +CONTROL_OUT *control = calloc(1, sizeof(*control)); +CONTROL_OUT *last = NULL; - ans = ans->next; - } - - return ans; -} - -BUFFER *dmc_buffer_queue_find_first_contains_data(BUFFER_QUEUE *q) -{ - BUFFER *ans = dmc_buffer_queue_head(q); - while (ans != NULL) - { - if (ans->state == ContainsData) - { - break; - } - - ans = ans->next; - } - - return ans; -} - -void dmc_buffer_queue_get_stats(BUFFER_QUEUE *q, int *available, int *contains_data, int *transfer_in_progress) -{ - BUFFER *buf = dmc_buffer_queue_head(q); - *available = 0; - *contains_data = 0; - *transfer_in_progress = 0; - - while (buf != NULL) - { - switch (buf->state) - { - case Available: - { - (*available)++; - break; - } - - case ContainsData: - { - (*contains_data)++; - break; - } - - case TransferInProgress: - { - (*transfer_in_progress)++; - break; - } - } - - buf = buf->next; - } -} - -t_stat dmc_open_master_socket(CTLR *controller, char *port) -{ - t_stat ans; - ans = SCPE_OK; - if (controller->master_socket == INVALID_SOCKET) - { - controller->master_socket = sim_master_sock(port, &ans); - if (controller->master_socket == INVALID_SOCKET) - { - sim_debug(DBG_WRN, controller->device, "Failed to open master socket on port %s\n", port); - ans = SCPE_OPENERR; - } - else - { - printf ("DMC-11 %s listening on port %s\n", controller->device->name, port); - } - } - - return ans; -} - -t_stat dmc_close_master_socket(CTLR *controller) -{ - sim_close_sock (controller->master_socket, TRUE); - controller->master_socket = INVALID_SOCKET; - return SCPE_OK; -} - -// Gets the bidirectional socket and handles arbitration of determining which socket to use. -int dmc_get_socket(CTLR *controller, int forRead) -{ - int ans = 0; - if (controller->line->isPrimary) - { - ans = dmc_get_transmit_socket(controller, 0, forRead); // TODO: After change to single socket, loopback may not work. - } - else - { - ans = dmc_get_receive_socket(controller, forRead); // TODO: After change to single socket, loopback may not work. - } - return ans; -} - -int dmc_get_receive_socket(CTLR *controller, int forRead) -{ - int ans = 0; - if (controller->line->socket == INVALID_SOCKET) - { - char *ipaddr; - //sim_debug(DBG_SOK, controller->device, "Trying to open receive socket\n"); - controller->line->socket = sim_accept_conn (controller->master_socket, &ipaddr); /* poll connect */ - if (controller->line->socket != INVALID_SOCKET) - { - char host[sizeof(controller->line->peer)]; - - if (sim_parse_addr (controller->line->peer, host, sizeof(host), NULL, NULL, 0, NULL, ipaddr)) - { - sim_debug(DBG_WRN, controller->device, "Received connection from unexpected source IP %s. Closing the connection.\n", ipaddr); - dmc_close_receive(controller, "Unathorized connection", ipaddr); - } - else - { - sim_debug(DBG_SOK, controller->device, "Opened receive socket %d\n", controller->line->socket); - controller->line->receive_readable = FALSE; - } - free(ipaddr); - } - } - - if (controller->line->socket != INVALID_SOCKET) - { - int readable = sim_check_conn(controller->line->socket, forRead); - if (readable == 0) /* Still opening */ - { - // Socket is still being opened, or is open but there is no data ready to be read. - ans = 0; - } - else if (readable == -1) /* Failed to open */ - { - dmc_close_receive(controller, "failed to connect", NULL); - ans = 0; - } - else /* connected */ - { - if (!controller->line->receive_readable) - { - sim_debug(DBG_CON, controller->device, "Receive socket is now readable\n"); - } - controller->line->receive_readable = TRUE; - ans = 1; - } - } - - return ans; -} - -int dmc_get_transmit_socket(CTLR *controller, int is_loopback, int forRead) -{ - int ans = 0; - /* close transmit socket if there is a change in the loopback setting */ - if (is_loopback ^ controller->line->transmit_is_loopback) - { - dmc_close_transmit(controller, "loopback change"); - } - - if (controller->line->socket == INVALID_SOCKET && ((int32)(time(NULL) - controller->line->last_connect_attempt)) > controller->connect_poll_interval) - { - char host_port_buf[CBUFSIZE]; - char *host_port = host_port_buf; - - controller->line->transmit_is_loopback = is_loopback; - - controller->line->last_connect_attempt = time(NULL); - if (is_loopback) - { - if (strrchr(controller->line->receive_port, ':')) - { - host_port = controller->line->receive_port; - } - else - { - sprintf(host_port_buf, "localhost:%s", controller->line->receive_port); - } - } - else - { - host_port = controller->line->peer; - } - - sim_debug(DBG_SOK, controller->device, "Trying to open transmit socket to address:port %s\n", host_port); - controller->line->last_connect_attempt = time(NULL); - controller->line->socket = sim_connect_sock(host_port, NULL, NULL); - if (controller->line->socket != INVALID_SOCKET) - { - sim_debug(DBG_SOK, controller->device, "Opened transmit socket to port %s\n", host_port); - controller->line->transmit_writeable = FALSE; - } - } - - if (controller->line->socket != INVALID_SOCKET) - { - int writeable = sim_check_conn(controller->line->socket, forRead); - if (writeable == 0) /* Still opening */ - { - //sim_debug(DBG_SOK, controller->device, "Waiting for transmit socket to become writeable\n"); - ans = 0; - } - else if (writeable == -1) /* Failed to open */ - { - dmc_close_transmit(controller, "failed to connect"); - ans = 0; - } - else /* connected */ - { - if (!controller->line->transmit_writeable) - { - sim_debug(DBG_CON, controller->device, "Transmit socket is now writeable\n"); - } - controller->line->transmit_writeable = TRUE; - ans = 1; - } - } - - return ans; -} - -void dmc_error_and_close_socket(CTLR *controller, char *format) -{ - int err = WSAGetLastError(); - char errmsg[80]; - sprintf(errmsg, format, err); - dmc_close_socket(controller, errmsg); -} - -void dmc_close_socket(CTLR *controller, char *reason) -{ - if (controller->line->isPrimary) - { - dmc_close_transmit(controller, reason); - } - else - { - dmc_close_receive(controller, reason, NULL); - } -} - -void dmc_close_receive(CTLR *controller, char *reason, char *from) -{ - if (controller->line->socket != INVALID_SOCKET) - { - sim_debug(DBG_SOK, controller->device, "Closing receive socket on port %s, reason: %s%s%s\n", controller->line->receive_port, reason, from ? " from " : "", from ? from : ""); - sim_close_sock(controller->line->socket, FALSE); - controller->line->socket = INVALID_SOCKET; - - if (controller->line->receive_readable) - { - sim_debug(DBG_CON, controller->device, "Readable receive socket closed, reason: %s\n", reason); - } - controller->line->receive_readable = FALSE; - } -} - -void dmc_close_transmit(CTLR *controller, char *reason) -{ - if (controller->line->socket != INVALID_SOCKET) - { - sim_debug(DBG_SOK, controller->device, "Closing transmit socket to port %s, socket %d, reason: %s\n", controller->line->peer, controller->line->socket, reason); - sim_close_sock(controller->line->socket, FALSE); - controller->line->socket = INVALID_SOCKET; - - if (controller->line->transmit_writeable) - { - sim_debug(DBG_CON, controller->device, "Writeable transmit socket closed, reason: %s\n", reason); - } - controller->line->transmit_writeable = FALSE; +control->sel6 = sel6; +if (controller->control_out) { + last = controller->control_out; + while (last->next) + last = last->next; + last->next = control; } +else + controller->control_out = control; } /* returns true if some data was received */ -int dmc_buffer_fill_receive_buffers(CTLR *controller) +t_bool dmc_buffer_fill_receive_buffers(CTLR *controller) { - int ans = FALSE; - SOCKET socket; - if (controller->state == Running) - { - BUFFER *buffer = dmc_buffer_queue_find_first_available(controller->receive_queue); - while (buffer != NULL && buffer->state == Available) - { - if (dmc_get_socket(controller, TRUE)) - { - int bytes_read = 0; - int lost_data = 0; +int ans = FALSE; - socket = controller->line->socket; - /* read block length and allocate buffer */ - if ((size_t)buffer->block_len_bytes_read < sizeof(buffer->actual_block_len)) - { - char *start_addr = ((char *)&buffer->actual_block_len) + buffer->block_len_bytes_read; - bytes_read = sim_read_sock(socket, start_addr, sizeof(buffer->actual_block_len) - buffer->block_len_bytes_read); - if (bytes_read >= 0) - { - buffer->block_len_bytes_read += bytes_read; - if (buffer->block_len_bytes_read == sizeof(buffer->actual_block_len)) - { - buffer->actual_block_len = ntohs(buffer->actual_block_len); - if (buffer->actual_block_len > buffer->count) - { - sim_debug(DBG_WRN, controller->device, "LOST DATA, buffer available has %d bytes, but the block is %d bytes\n", buffer->count, buffer->actual_block_len); - dmc_setreg(controller, 4, 0, 0); - dmc_setreg(controller, 6, 0, 0); - dmc_set_lost_data(controller); - dmc_start_control_output_transfer(controller); - lost_data = 1; - dmc_error_and_close_socket(controller, "oversized packet"); - } +if (controller->state == Running) { + BUFFER *buffer = dmc_buffer_queue_head(controller->rcv_queue); - if (buffer->actual_block_len > 0) - { - buffer->transfer_buffer = (uint8 *)malloc(buffer->actual_block_len); /* read full buffer regardless, so bad buffer is flushed */ - } - } - } - } - else - { - lost_data = buffer->actual_block_len > buffer->count; /* need to preserve this variable if need more than one attempt to read the buffer */ - } - - /* read the actual block */ - if (buffer->block_len_bytes_read == sizeof(buffer->actual_block_len)) - { - bytes_read = 0; - if (buffer->actual_block_len > 0) - { - int bytes_to_read = dmc_line_speed_calculate_byte_length(controller->line->bytes_received_in_last_second, buffer->actual_block_len - buffer->actual_bytes_transferred, controller->line->speed); - if (bytes_to_read > 0) - { - bytes_read = sim_read_sock(controller->line->socket, (char *)(buffer->transfer_buffer + buffer->actual_bytes_transferred), bytes_to_read); - } - } - - if (bytes_read >= 0) - { - buffer->actual_bytes_transferred += bytes_read; - controller->line->bytes_received_in_last_second += bytes_read; - - if (buffer->actual_bytes_transferred >= buffer->actual_block_len) - { - dmc_buffer_trace(controller, buffer->transfer_buffer, buffer->actual_bytes_transferred, "REC ", buffer->address); - controller->buffers_received_from_net++; - buffer->state = ContainsData; - if (!lost_data) - { - Map_WriteB(buffer->address, buffer->actual_bytes_transferred, buffer->transfer_buffer); - } - else - { - buffer->actual_block_len = 0; /* so an empty buffer is returned to the driver */ - } - - if (buffer->actual_block_len > 0) - { - free(buffer->transfer_buffer); - buffer->transfer_buffer = NULL; - } - - ans = TRUE; - } - } - } - - /* Only close the socket if there was an error or no more data */ - if (bytes_read < 0) - { - dmc_error_and_close_socket(controller, "read error, code=%d"); - break; - } - - /* if buffer is incomplete do not try to read any more buffers and continue filling this one later */ - if (buffer->state == Available) - { - break; /* leave buffer available and continue filling it later */ - } - } - else - { - break; - } - - buffer = buffer ->next; - } - } - - return ans; -} - -/* returns true if some data was actually sent */ -int dmc_buffer_send_transmit_buffers(CTLR *controller) -{ - int ans = FALSE; - /* when transmit buffer is queued it is marked as available, not as ContainsData */ - BUFFER *buffer = dmc_buffer_queue_find_first_available(controller->transmit_queue); - while (buffer != NULL) - { - if (dmc_get_socket(controller, FALSE)) // TODO: , buffer->is_loopback); - { - int bytes = 0; - int bytes_to_send; - uint16 block_len; - int total_buffer_len = (buffer->count > 0) ? buffer->count + sizeof(block_len) : 0; - - /* only send the buffer if it actually has some data, sometimes get zero length buffers - don't send these */ - if (total_buffer_len > 0) - { - if (buffer->transfer_buffer == NULL) - { - int n; - /* construct buffer and include block length bytes */ - buffer->transfer_buffer = (uint8 *)malloc(total_buffer_len); - block_len = htons(buffer->count); - memcpy(buffer->transfer_buffer, (char *)&block_len, sizeof(block_len)); - n = Map_ReadB(buffer->address, buffer->count, buffer->transfer_buffer + sizeof(block_len)); - if (n > 0) - { - sim_debug(DBG_WRN, controller->device, "DMA error\n"); - } - } - - bytes_to_send = dmc_line_speed_calculate_byte_length(controller->line->bytes_sent_in_last_second, buffer->count + sizeof(block_len) - buffer->actual_bytes_transferred, controller->line->speed); - if (bytes_to_send > 0) - { - bytes = sim_write_sock (controller->line->socket, (char *)(buffer->transfer_buffer + buffer->actual_bytes_transferred), bytes_to_send); - if (bytes >= 0) - { - buffer->actual_bytes_transferred += bytes; - controller->line->bytes_sent_in_last_second += bytes; - } - - if (buffer->actual_bytes_transferred >= total_buffer_len || bytes < 0) - { - dmc_buffer_trace(controller, buffer->transfer_buffer+sizeof(block_len), buffer->count, "TRAN", buffer->address); - free(buffer->transfer_buffer); - } - } - } - - if (buffer->actual_bytes_transferred >= total_buffer_len) - { - controller->buffers_transmitted_to_net++; - buffer->state = ContainsData; // so won't try to transmit again - ans = TRUE; - } - else if (bytes < 0) - { - int err = WSAGetLastError (); - char errmsg[80]; - sprintf(errmsg, "write failure, code=%d", err); - - dmc_close_transmit(controller, errmsg); - break; - } - else - { - break; /* poll again later to send more bytes */ - } - - } - else - { + while (buffer) { + ddcmp_tmxr_get_packet_ln (controller->line, (const uint8 **)&controller->link.rcv_pkt, &controller->link.rcv_pkt_size); + if (!controller->link.rcv_pkt) break; + ans = TRUE; + controller->buffers_received_from_net++; + controller->ddcmp_control_packets_received += (controller->link.rcv_pkt[0] == DDCMP_ENQ) ? 1 : 0; + ddcmp_dispatch(controller, DDCMP_EVENT_PKT_RCVD); + buffer = dmc_buffer_queue_head(controller->rcv_queue); } - - buffer = buffer ->next; } - - return ans; +return ans; } -void dmc_start_transfer_receive_buffer(CTLR *controller) +void dmc_start_transfer_buffer(CTLR *controller) { - BUFFER *head = dmc_buffer_queue_head(controller->receive_queue); - if (head != NULL) - { - if (head->state == ContainsData) - { - head->state = TransferInProgress; - dmc_start_data_output_transfer(controller, head->address, head->actual_block_len, TRUE); - } - } -} +BUFFER *head = dmc_buffer_queue_head(controller->completion_queue); +uint16 count; -void dmc_start_transfer_transmit_buffer(CTLR *controller) -{ - BUFFER *head = dmc_buffer_queue_head(controller->transmit_queue); - if (head != NULL) - { - if (head->state == ContainsData) - { - head->state = TransferInProgress; - dmc_start_data_output_transfer(controller, head->address, head->count, FALSE); - } +if ((!head) || + (controller->transfer_state != Idle) || + (dmc_is_rdyo_set(controller))) + return; +count = head->actual_bytes_transferred; +switch (head->type) { + case Receive: + sim_debug(DBG_INF, controller->device, "Starting data output transfer for receive, address=0x%08x, count=%d\n", head->address, count); + dmc_set_type_output(controller, DMC_C_TYPE_RBACC); + break; + case TransmitData: + sim_debug(DBG_INF, controller->device, "Starting data output transfer for transmit, address=0x%08x, count=%d\n", head->address, count); + dmc_set_type_output(controller, DMC_C_TYPE_XBACC); + break; + default: + break; } +dmc_set_addr(controller, head->address); +dmc_set_count(controller, count); +controller->transfer_state = OutputTransfer; +dmc_set_rdyo(controller); } void dmc_check_for_output_transfer_completion(CTLR *controller) { - if (!dmc_is_rdyo_set(controller)) - { - sim_debug(DBG_INF, controller->device, "Output transfer completed\n"); - controller->transfer_state = Idle; - if (dmc_get_output_transfer_type(controller) == TYPE_BACCO) - { - if (dmc_is_out_io_set(controller)) - { - dmc_buffer_queue_release_head(controller->receive_queue); - controller->receive_buffer_output_transfers_completed++; - } - else - { - dmc_buffer_queue_release_head(controller->transmit_queue); - controller->transmit_buffer_output_transfers_completed++; - } - } - dmc_process_command(controller); // check for any input transfers - } +BUFFER *buffer; + +if ((dmc_is_rdyo_set(controller)) || + (controller->transfer_state != OutputTransfer)) + return; +sim_debug(DBG_INF, controller->device, "Output transfer completed\n"); +buffer = (BUFFER *)remqueue (controller->completion_queue->hdr.next); +assert (insqueue (&buffer->hdr, controller->free_queue->hdr.prev)); +controller->transmit_buffer_output_transfers_completed++; +controller->transfer_state = Idle; +dmc_process_command(controller); // check for any other transfers +} + +void dmc_check_for_output_control_completion(CTLR *controller) +{ +CONTROL_OUT *control = controller->control_out; + +if ((dmc_is_rdyo_set(controller)) || + (controller->transfer_state != OutputControl)) + return; + +dmc_dumpregsel6(controller, DBG_INF, "Output command completed:", control->sel6); + +controller->transfer_state = Idle; +controller->control_out = control->next; +free(control); +controller->control_out_operations_completed++; +dmc_process_command(controller); // check for any other transfers } void dmc_process_input_transfer_completion(CTLR *controller) { - if (dmc_is_dmc(controller)) - { - if (!dmc_is_rqi_set(controller)) - { - uint16 sel4 = controller->csrs->sel4; - uint16 sel6 = controller->csrs->sel6; - dmc_clear_rdyi(controller); - if (controller->transfer_type == TYPE_BASEI) - { - uint32 baseaddr = ((sel6 >> 14) << 16) | sel4; - uint16 count = sel6 & 0x3FFF; - sim_debug(DBG_INF, controller->device, "Completing Base In input transfer, base address=0x%08x count=%d\n", baseaddr, count); - } - else if (controller->transfer_type == TYPE_BACCI) - { - uint32 addr = ((sel6 >> 14) << 16) | sel4; - uint16 count = sel6 & 0x3FFF; - if (controller->transfer_in_io != dmc_is_in_io_set(controller)) - { - sim_debug(DBG_TRC, controller->device, "IN IO MISMATCH\n"); - } +if (dmc_is_dmc(controller)) { + if (dmc_is_run_set(controller) && + (!dmc_is_rqi_set(controller))) { + /* Time to decode input command arguments */ + uint16 sel4 = *controller->csrs->sel4; + uint16 sel6 = *controller->csrs->sel6; + uint32 addr = dmc_get_addr(controller); + uint16 count = dmc_get_count(controller); - controller->transfer_in_io = dmc_is_in_io_set(controller); // using evdmc the flag is set when the transfer completes - not when it starts, evdca seems to set in only at the start of the transfer - clearing it when it completes - controller->state = Running; - if (controller->transfer_in_io) - { - dmc_buffer_queue_add(controller->receive_queue, addr, count); - dmc_buffer_fill_receive_buffers(controller); - controller->receive_buffer_input_transfers_completed++; - } - else - { - dmc_buffer_queue_add(controller->transmit_queue, addr, count); - dmc_buffer_send_transmit_buffers(controller); + controller->transfer_type = dmc_get_input_transfer_type(controller); + dmc_clear_rdyi(controller); + switch (controller->transfer_type) { + case DMC_C_TYPE_BASEIN: + *controller->baseaddr = dmc_get_addr(controller); + *controller->basesize = dmc_get_count(controller); + sim_debug(DBG_INF, controller->device, "Completing Base In input transfer, base address=0x%08x count=%d\n", *controller->baseaddr, *controller->basesize); + break; + case DMC_C_TYPE_XBACC: + if (1) { + BUFFER *buffer = dmc_buffer_queue_add(controller->xmt_queue, addr, count, TransmitData); + int n; + + /* construct buffer - leaving room for DDCMP header and CRCs */ + buffer->transfer_buffer = (uint8 *)realloc (buffer->transfer_buffer, buffer->count + DDCMP_HEADER_SIZE + DDCMP_CRC_SIZE); + memset (buffer->transfer_buffer, 0, DDCMP_HEADER_SIZE); + n = Map_ReadB (buffer->address, buffer->count, buffer->transfer_buffer + DDCMP_HEADER_SIZE); + if (n > 0) { + sim_debug(DBG_WRN, controller->device, "DMA error\n"); + } + buffer->count += DDCMP_HEADER_SIZE + DDCMP_CRC_SIZE; controller->transmit_buffer_input_transfers_completed++; - } + } + if (controller->state != Running) { + controller->state = Running; + dmc_set_modem_dtr (controller); + } + ddcmp_dispatch (controller, 0); + break; + case DMC_C_TYPE_RBACC: + dmc_buffer_queue_add(controller->rcv_queue, addr, count, Receive); + dmc_buffer_fill_receive_buffers(controller); + controller->receive_buffer_input_transfers_completed++; + ddcmp_dispatch (controller, 0); + break; + case DMC_C_TYPE_CNTL: + sim_debug(DBG_INF, controller->device, "Control In Command Received(%s MODE%s%s)\n", + (sel6&DMC_SEL6_M_MAINT) ? "MAINTENANCE" : "DDCMP", + (sel6&DMC_SEL6_M_HDX) ? ", Half Duples" : "", + (sel6&DMC_SEL6_M_LONGSTRT) ? ", Long Start Timer" : ""); + dmc_set_modem_dtr (controller); + controller->transfer_state = Idle; + ddcmp_dispatch (controller, (sel6&DMC_SEL6_M_MAINT) ? DDCMP_EVENT_MAINTMODE : 0); + return; + case DMC_C_TYPE_HALT: + sim_debug(DBG_INF, controller->device, "Halt Command Received\n"); + controller->state = Halted; + ddcmp_dispatch(controller, 0); + dmc_clr_modem_dtr(controller); + dmc_queue_control_out(controller, DMC_SEL6_M_HALTCOMP); + return; } - - controller->transfer_state = Idle; + controller->transfer_state = Idle; + dmc_process_command (controller); } } - else - { - if (!dmc_is_rdyi_set(controller)) - { - uint16 sel6 = controller->csrs->sel6; - if (controller->transfer_type == TYPE_DMP_MODE) - { - uint16 mode = sel6 & DMP_TYPE_INPUT_MASK; - char * duplex = (mode & 1) ? "Full-Duplex" : "Half-Duplex"; - char * config; - if (mode & 4) - { - config = "Point-to-point"; - } - else - { - config = (mode & 2) ? "Tributary station" : "Control Station"; - } +else { /* DMP */ + if (!dmc_is_rdyi_set(controller)) { + uint16 sel6 = *controller->csrs->sel6; - sim_debug(DBG_INF, controller->device, "Completing Mode input transfer, %s %s\n", duplex, config); - } - else if (controller->transfer_type == TYPE_DMP_CONTROL) - { - sim_debug(DBG_WRN, controller->device, "Control command (not processed yet)\n"); - } - else if (controller->transfer_type == TYPE_DMP_RECEIVE) - { - sim_debug(DBG_WRN, controller->device, "Receive Buffer command (not processed yet)\n"); - } - else if (controller->transfer_type == TYPE_DMP_TRANSMIT) - { - sim_debug(DBG_WRN, controller->device, "Transmit Buffer command (not processed yet)\n"); - } + if (controller->transfer_type == DMP_C_TYPE_MODE) { + uint16 mode = sel6 & DMP_TYPE_INPUT_MASK; + char * duplex = (mode & 1) ? "Full-Duplex" : "Half-Duplex"; + char * config; + + if (mode & 4) + config = "Point-to-point"; else - { - sim_debug(DBG_WRN, controller->device, "Unrecognised command code %hu\n", controller->transfer_type); - } + config = (mode & 2) ? "Tributary station" : "Control Station"; - controller->transfer_state = Idle; + sim_debug(DBG_INF, controller->device, "Completing Mode input transfer, %s %s\n", duplex, config); + } + else + if (controller->transfer_type == DMP_C_TYPE_CNTL) { + sim_debug(DBG_WRN, controller->device, "Control command (not processed yet)\n"); + } + else + if (controller->transfer_type == DMP_C_TYPE_RBACC) { + sim_debug(DBG_WRN, controller->device, "Receive Buffer command (not processed yet)\n"); + } + else + if (controller->transfer_type == DMP_C_TYPE_XBACC) { + sim_debug(DBG_WRN, controller->device, "Transmit Buffer command (not processed yet)\n"); + } + else { + sim_debug(DBG_WRN, controller->device, "Unrecognised command code %hu\n", controller->transfer_type); + } + + controller->transfer_state = Idle; } } } +typedef enum { + EQ, + NE, + LT, + LE, + GT, + GE +} CompareOP; + +/* DDCMP Modulo 256 compare - from Rob Jarratt */ +static int Mod256Cmp(uint8 a, uint8 b, size_t queue_size) +{ +int ans; +int abdiff = (int)b - (int)a; +int badiff = (int)a - (int)b; + +if (abdiff == 0) + ans = 0; +else + if (abdiff < 0) + ans = -1; + else + ans = 1; + +if (abs(badiff) <= (int)queue_size) + ans = -1 * ans; + +return ans; +} + +t_bool ddcmp_compare (uint8 a, CompareOP Op, uint8 b, CTLR *controller) +{ +int cmp = Mod256Cmp(a & 0xFF, b & 0xFF, controller->free_queue->size); + +switch (Op) { + case EQ: + return (cmp == 0); + case NE: + return (cmp != 0); + case LE: + if (cmp == 0) + return TRUE; + case LT: + return (cmp < 0); + case GE: + if (cmp == 0) + return TRUE; + case GT: + return (cmp > 0); + default: /* Never happens */ + return FALSE; + } +} + +void ddcmp_StartTimer (CTLR *controller) +{ +controller->link.TimerRunning = TRUE; +controller->link.TimeRemaining = DDCMP_PACKET_TIMEOUT; +sim_activate_after (controller->device->units+(controller->device->numunits-1), controller->link.TimeRemaining*1000000); +} +void ddcmp_StopTimer (CTLR *controller) +{ +controller->link.TimerRunning = FALSE; +} +void ddcmp_ResetVariables (CTLR *controller) +{ +size_t data_packets = 0; +BUFFER *buffer; + +controller->link.R = 0; +controller->link.N = 0; +controller->link.A = 0; +controller->link.T = 1; +controller->link.X = 0; +controller->link.SACK = FALSE; +controller->link.SNAK = FALSE; +controller->link.SREP = FALSE; +controller->link.state = Halt; +controller->link.nak_reason = 0; +/* Move any ack wait packets back to the transmit queue so they get + resent when the link is restored */ +while (controller->ack_wait_queue->count) { + buffer = (BUFFER *)remqueue (controller->ack_wait_queue->hdr.prev); + memset (buffer->transfer_buffer, 0, DDCMP_HEADER_SIZE); + assert (insqueue (&buffer->hdr, &controller->xmt_queue->hdr)); + } +/* Also make sure that the transmit queue has no control packets in it + and that any non transmit buffer(s) have zeroed headers so they will + be properly constructed when the link comes up */ +buffer = (BUFFER *)controller->xmt_queue->hdr.next; +while (controller->xmt_queue->count - data_packets) { + if (((BUFFER *)controller->xmt_queue->hdr.next)->transfer_buffer[0] == DDCMP_ENQ) { + BUFFER *buffer_next = (BUFFER *)buffer->hdr.next; + + buffer = (BUFFER *)remqueue (&buffer->hdr); + assert (insqueue (&buffer->hdr, &controller->free_queue->hdr)); + buffer = buffer_next; + continue; + } + ++data_packets; + memset (buffer->transfer_buffer, 0, DDCMP_HEADER_SIZE); + buffer = (BUFFER *)buffer->hdr.next; + } +} +void ddcmp_SendStrt (CTLR *controller) +{ +BUFFER *buffer = dmc_buffer_allocate(controller); + +buffer->transfer_buffer = (uint8 *)malloc (DDCMP_HEADER_SIZE); +buffer->count = DDCMP_HEADER_SIZE; +ddcmp_build_start_packet (buffer->transfer_buffer); +assert (insqueue (&buffer->hdr, &controller->xmt_queue->hdr)); +dmc_ddcmp_start_transmitter (controller); +} +void ddcmp_SendStack (CTLR *controller) +{ +BUFFER *buffer = dmc_buffer_allocate(controller); + +buffer->transfer_buffer = (uint8 *)malloc (DDCMP_HEADER_SIZE); +buffer->count = DDCMP_HEADER_SIZE; +ddcmp_build_start_ack_packet (buffer->transfer_buffer); +assert (insqueue (&buffer->hdr, &controller->xmt_queue->hdr)); +dmc_ddcmp_start_transmitter (controller); +} +void ddcmp_SendAck (CTLR *controller) +{ +BUFFER *buffer = dmc_buffer_allocate(controller); + +buffer->transfer_buffer = (uint8 *)malloc (DDCMP_HEADER_SIZE); +buffer->count = DDCMP_HEADER_SIZE; +ddcmp_build_ack_packet (buffer->transfer_buffer, controller->link.R, DDCMP_FLAG_SELECT); +assert (insqueue (&buffer->hdr, &controller->xmt_queue->hdr)); +dmc_ddcmp_start_transmitter (controller); +} +void ddcmp_SendNak (CTLR *controller) +{ +BUFFER *buffer = dmc_buffer_allocate(controller); + +buffer->transfer_buffer = (uint8 *)malloc (DDCMP_HEADER_SIZE); +buffer->count = DDCMP_HEADER_SIZE; +ddcmp_build_nak_packet (buffer->transfer_buffer, controller->link.nak_reason, controller->link.R, DDCMP_FLAG_SELECT); +assert (insqueue (&buffer->hdr, &controller->xmt_queue->hdr)); +dmc_ddcmp_start_transmitter (controller); +} +void ddcmp_SendRep (CTLR *controller) +{ +BUFFER *buffer = dmc_buffer_allocate(controller); + +buffer->transfer_buffer = (uint8 *)malloc (DDCMP_HEADER_SIZE); +buffer->count = DDCMP_HEADER_SIZE; +ddcmp_build_rep_packet (buffer->transfer_buffer, controller->link.N, DDCMP_FLAG_SELECT); +assert (insqueue (&buffer->hdr, &controller->xmt_queue->hdr)); +dmc_ddcmp_start_transmitter (controller); +} +void ddcmp_SetSACK (CTLR *controller) +{ +controller->link.SACK = TRUE; +} +void ddcmp_ClearSACK (CTLR *controller) +{ +controller->link.SACK = FALSE; +} +void ddcmp_SetSNAK (CTLR *controller) +{ +controller->link.SNAK = TRUE; +} +void ddcmp_ClearSNAK (CTLR *controller) +{ +controller->link.SNAK = FALSE; +} +void ddcmp_SetSREP (CTLR *controller) +{ +controller->link.SREP = TRUE; +} +void ddcmp_ClearSREP (CTLR *controller) +{ +controller->link.SREP = FALSE; +} +void ddcmp_IncrementR (CTLR *controller) +{ +controller->link.R++; +} +void ddcmp_SetAeqRESP (CTLR *controller) +{ +controller->link.A = controller->link.rcv_pkt[DDCMP_RESP_OFFSET]; +} +void ddcmp_SetTequalAplus1 (CTLR *controller) +{ +controller->link.T = controller->link.A + 1; +} +void ddcmp_IncrementT (CTLR *controller) +{ +controller->link.T++; +} +void ddcmp_SetNAKreason3 (CTLR *controller) +{ +controller->link.nak_reason = 3; +} +void ddcmp_SetNAKreason2 (CTLR *controller) +{ +controller->link.nak_reason = 2; +} +void ddcmp_NAKMissingPackets (CTLR *controller) +{ +uint8 R = controller->link.R; +QH *qh = &controller->xmt_queue->hdr; + +while (ddcmp_compare (controller->link.rcv_pkt[DDCMP_NUM_OFFSET], GE, R, controller)) { + BUFFER *buffer = dmc_buffer_allocate(controller); + buffer->transfer_buffer = (uint8 *)malloc (DDCMP_HEADER_SIZE); + buffer->count = DDCMP_HEADER_SIZE; + ddcmp_build_nak_packet (buffer->transfer_buffer, 2, R, DDCMP_FLAG_SELECT); + R = R + 1; + assert (insqueue (&buffer->hdr, qh)); + qh = &buffer->hdr; + } +dmc_ddcmp_start_transmitter (controller); +} +void ddcmp_IfTleAthenSetTeqAplus1 (CTLR *controller) +{ +if (ddcmp_compare (controller->link.T, LE, controller->link.A, controller)) + controller->link.T = controller->link.A + 1; +} +void ddcmp_IfAltXthenStartTimer (CTLR *controller) +{ +if (ddcmp_compare (controller->link.A, LT, controller->link.X, controller)) + ddcmp_StartTimer (controller); +} +void ddcmp_IfAgeXthenStopTimer (CTLR *controller) +{ +if (ddcmp_compare (controller->link.A, GE, controller->link.X, controller)) + ddcmp_StopTimer (controller); +} +void ddcmp_Ignore (CTLR *controller) +{ +} +void ddcmp_GiveBufferToUser (CTLR *controller) +{ +BUFFER *buffer = (BUFFER *)remqueue (controller->rcv_queue->hdr.next); + +if (!buffer) { + ddcmp_SetSNAK (controller); + controller->link.nak_reason = 8; /* buffer temporarily unavailable */ + return; + } + +buffer->actual_bytes_transferred = controller->link.rcv_pkt_size - (DDCMP_HEADER_SIZE + DDCMP_CRC_SIZE); +Map_WriteB(buffer->address, buffer->actual_bytes_transferred, controller->link.rcv_pkt + DDCMP_HEADER_SIZE); +assert (insqueue (&buffer->hdr, controller->completion_queue->hdr.prev)); /* Insert at tail */ +ddcmp_IncrementR (controller); +ddcmp_SetSACK (controller); +} +void ddcmp_CompleteAckedTransmits (CTLR *controller) +{ +BUFFER *buffer = dmc_buffer_queue_head(controller->ack_wait_queue); + +while (buffer != NULL) { + if ((!buffer->transfer_buffer) || + (!controller->link.rcv_pkt) || + ddcmp_compare (buffer->transfer_buffer[DDCMP_NUM_OFFSET], GT, controller->link.rcv_pkt[DDCMP_RESP_OFFSET], controller)) + break; + buffer = (BUFFER *)remqueue (&buffer->hdr); + assert (insqueue (&buffer->hdr, controller->completion_queue->hdr.prev)); /* Insert at tail */ + buffer = dmc_buffer_queue_head(controller->ack_wait_queue); + } +} +void ddcmp_ReTransmitMessageT (CTLR *controller) +{ +BUFFER *buffer = dmc_buffer_queue_head(controller->ack_wait_queue); +size_t i; + +for (i=0; i < controller->ack_wait_queue->count; ++i) { + if ((!buffer->transfer_buffer) || + ddcmp_compare (buffer->transfer_buffer[DDCMP_NUM_OFFSET], NE, controller->link.T, controller)) { + buffer = (BUFFER *)buffer->hdr.next; + continue; + } + ddcmp_build_data_packet (buffer->transfer_buffer, buffer->count - (DDCMP_HEADER_SIZE + DDCMP_CRC_SIZE), DDCMP_FLAG_SELECT|DDCMP_FLAG_QSYNC, controller->link.T, controller->link.R); + buffer = (BUFFER *)remqueue (&buffer->hdr); + assert (insqueue (&buffer->hdr, controller->xmt_queue->hdr.prev)); /* Insert at tail */ + break; + } +} +void ddcmp_NotifyDisconnect (CTLR *controller) +{ +dmc_queue_control_out(controller, DMC_SEL6_M_DISCONN); +} +void ddcmp_NotifyStartRcvd (CTLR *controller) +{ +dmc_queue_control_out(controller, DMC_SEL6_M_STRTRCVD); +} +void ddcmp_NotifyMaintRcvd (CTLR *controller) +{ +dmc_queue_control_out(controller, DMC_SEL6_M_MAINTRCV); +} +void ddcmp_SendDataMessage (CTLR *controller) +{ +BUFFER *buffer = dmc_buffer_queue_head(controller->xmt_queue); + +ddcmp_build_data_packet (buffer->transfer_buffer, buffer->count - (DDCMP_HEADER_SIZE + DDCMP_CRC_SIZE), DDCMP_FLAG_SELECT|DDCMP_FLAG_QSYNC, controller->link.N + 1, controller->link.R); +controller->link.N += 1; +controller->link.T = controller->link.N + 1; +controller->link.SACK = 0; +dmc_ddcmp_start_transmitter (controller); +} +void ddcmp_SendMaintMessage (CTLR *controller) +{ +BUFFER *buffer = dmc_buffer_queue_head(controller->xmt_queue); + +ddcmp_build_maintenance_packet (buffer->transfer_buffer, buffer->count - (DDCMP_HEADER_SIZE + DDCMP_CRC_SIZE)); +dmc_ddcmp_start_transmitter (controller); +} +void ddcmp_SetXSetNUM (CTLR *controller) +{ +controller->link.X = controller->link.xmt_done_buffer->transfer_buffer[DDCMP_NUM_OFFSET]; +} + +/* Conditions/Events */ + +t_bool ddcmp_UserHalt (CTLR *controller) +{ +return (controller->state == Halted); +} +t_bool ddcmp_UserStartup (CTLR *controller) +{ +return (*controller->modem & DMC_SEL4_M_DTR); +} +t_bool ddcmp_UserMaintenanceMode (CTLR *controller) +{ +return (controller->link.ScanningEvents & DDCMP_EVENT_MAINTMODE); +} +t_bool ddcmp_ReceiveStack (CTLR *controller) +{ +return ((controller->link.ScanningEvents & DDCMP_EVENT_PKT_RCVD) && + controller->link.rcv_pkt && + (controller->link.rcv_pkt[0] == DDCMP_ENQ) && + (controller->link.rcv_pkt[1] == DDCMP_CTL_STACK)); +} +t_bool ddcmp_ReceiveStrt (CTLR *controller) +{ +return ((controller->link.ScanningEvents & DDCMP_EVENT_PKT_RCVD) && + controller->link.rcv_pkt && + (controller->link.rcv_pkt[0] == DDCMP_ENQ) && + (controller->link.rcv_pkt[1] == DDCMP_CTL_STRT)); +} +t_bool ddcmp_TimerRunning (CTLR *controller) +{ +return (controller->link.TimerRunning); +} +t_bool ddcmp_TimerNotRunning (CTLR *controller) +{ +return (!controller->link.TimerRunning); +} +t_bool ddcmp_TimerExpired (CTLR *controller) +{ +return (controller->link.ScanningEvents & DDCMP_EVENT_TIMER); +} +t_bool ddcmp_ReceiveMaintMessage (CTLR *controller) +{ +return ((controller->link.ScanningEvents & DDCMP_EVENT_PKT_RCVD) && + controller->link.rcv_pkt && + (controller->link.rcv_pkt[0] == DDCMP_DLE)); +} +t_bool ddcmp_ReceiveAck (CTLR *controller) +{ +return ((controller->link.ScanningEvents & DDCMP_EVENT_PKT_RCVD) && + controller->link.rcv_pkt && + (controller->link.rcv_pkt[0] == DDCMP_ENQ) && + (controller->link.rcv_pkt[1] == DDCMP_CTL_ACK)); +} +t_bool ddcmp_ReceiveNak (CTLR *controller) +{ +return ((controller->link.ScanningEvents & DDCMP_EVENT_PKT_RCVD) && + controller->link.rcv_pkt && + (controller->link.rcv_pkt[0] == DDCMP_ENQ) && + (controller->link.rcv_pkt[1] == DDCMP_CTL_NAK)); +} +t_bool ddcmp_ReceiveRep (CTLR *controller) +{ +return ((controller->link.ScanningEvents & DDCMP_EVENT_PKT_RCVD) && + controller->link.rcv_pkt && + (controller->link.rcv_pkt[0] == DDCMP_ENQ) && + (controller->link.rcv_pkt[1] == DDCMP_CTL_REP)); +} +t_bool ddcmp_NUMEqRplus1 (CTLR *controller) +{ +t_bool breturn = (controller->link.rcv_pkt && + ddcmp_compare (controller->link.rcv_pkt[DDCMP_NUM_OFFSET], EQ, controller->link.R + 1, controller)); +return breturn; +} +t_bool ddcmp_NUMGtRplus1 (CTLR *controller) +{ +t_bool breturn = (controller->link.rcv_pkt && + ddcmp_compare (controller->link.rcv_pkt[DDCMP_NUM_OFFSET], GT, controller->link.R + 1, controller)); +return breturn; +} +t_bool ddcmp_ReceiveDataMsg (CTLR *controller) +{ +t_bool breturn = ((controller->link.ScanningEvents & DDCMP_EVENT_PKT_RCVD) && + controller->link.rcv_pkt && + (controller->link.rcv_pkt[0] == DDCMP_SOH)); +if (breturn) + return breturn; +return breturn; +} +t_bool ddcmp_ReceiveMaintMsg (CTLR *controller) +{ +t_bool breturn = ((controller->link.ScanningEvents & DDCMP_EVENT_PKT_RCVD) && + controller->link.rcv_pkt && + (controller->link.rcv_pkt[0] == DDCMP_DLE)); +if (breturn) + return breturn; +return breturn; +} +t_bool ddcmp_ALtRESPleN (CTLR *controller) +{ +return (controller->link.rcv_pkt && + ddcmp_compare (controller->link.A, LT, controller->link.rcv_pkt[DDCMP_RESP_OFFSET], controller) && + ddcmp_compare (controller->link.rcv_pkt[DDCMP_RESP_OFFSET], LE, controller->link.N, controller)); +} +t_bool ddcmp_ALeRESPleN (CTLR *controller) +{ +return (controller->link.rcv_pkt && + ddcmp_compare (controller->link.A, LE, controller->link.rcv_pkt[DDCMP_RESP_OFFSET], controller) && + ddcmp_compare (controller->link.rcv_pkt[DDCMP_RESP_OFFSET], LE, controller->link.N, controller)); +} +t_bool ddcmp_RESPleAOrRESPgtN (CTLR *controller) +{ +return (controller->link.rcv_pkt && + (ddcmp_compare (controller->link.rcv_pkt[DDCMP_RESP_OFFSET], LE, controller->link.A, controller) || + ddcmp_compare (controller->link.rcv_pkt[DDCMP_RESP_OFFSET], GT, controller->link.N, controller))); +} +t_bool ddcmp_TltNplus1 (CTLR *controller) +{ +return (ddcmp_compare (controller->link.T, LT, controller->link.N + 1, controller)); +} +t_bool ddcmp_TeqNplus1 (CTLR *controller) +{ +return (ddcmp_compare (controller->link.T, EQ, controller->link.N + 1, controller)); +} +t_bool ddcmp_ReceiveMessageError (CTLR *controller) +{ +if (controller->link.rcv_pkt && + ((0 != ddcmp_crc16 (0, controller->link.rcv_pkt, 8)) || + ((controller->link.rcv_pkt[0] != DDCMP_ENQ) && + (0 != ddcmp_crc16 (0, controller->link.rcv_pkt+8, controller->link.rcv_pkt_size-8))))) + return TRUE; +return FALSE; +} +t_bool ddcmp_NumEqR (CTLR *controller) +{ +return (controller->link.rcv_pkt && + ddcmp_compare (controller->link.rcv_pkt[DDCMP_NUM_OFFSET], EQ, controller->link.R, controller)); +} +t_bool ddcmp_NumNeR (CTLR *controller) +{ +return (controller->link.rcv_pkt && + ddcmp_compare (controller->link.rcv_pkt[DDCMP_NUM_OFFSET], NE, controller->link.R, controller)); +} +t_bool ddcmp_TransmitterIdle (CTLR *controller) +{ +return (NULL == controller->link.xmt_buffer); +} +t_bool ddcmp_TramsmitterBusy (CTLR *controller) +{ +return !ddcmp_TransmitterIdle(controller); +} +t_bool ddcmp_SACKisSet (CTLR *controller) +{ +return (controller->link.SACK); +} +t_bool ddcmp_SACKisClear (CTLR *controller) +{ +return (!(controller->link.SACK)); +} +t_bool ddcmp_SNAKisSet (CTLR *controller) +{ +return (controller->link.SNAK); +} +t_bool ddcmp_SNAKisClear (CTLR *controller) +{ +return (!(controller->link.SNAK)); +} +t_bool ddcmp_SREPisSet (CTLR *controller) +{ +return (controller->link.SREP); +} +t_bool ddcmp_SREPisClear (CTLR *controller) +{ +return (!(controller->link.SREP)); +} +t_bool ddcmp_UserSendMessage (CTLR *controller) +{ +BUFFER *buffer = dmc_buffer_queue_head(controller->xmt_queue); + +return (buffer && (buffer->transfer_buffer[0] == 0)); +} +t_bool ddcmp_LineConnected (CTLR *controller) +{ +return (*controller->modem & DMC_SEL4_M_CAR); +} +t_bool ddcmp_LineDisconnected (CTLR *controller) +{ +return (!(*controller->modem & DMC_SEL4_M_CAR)); +} +t_bool ddcmp_DataMessageSent (CTLR *controller) +{ +t_bool breturn = ((controller->link.ScanningEvents & DDCMP_EVENT_XMIT_DONE) && + controller->link.xmt_done_buffer && + (controller->link.xmt_done_buffer->transfer_buffer[0] == DDCMP_SOH)); +if (breturn) + return breturn; +return breturn; +} +t_bool ddcmp_REPMessageSent (CTLR *controller) +{ +return ((controller->link.ScanningEvents & DDCMP_EVENT_XMIT_DONE) && + controller->link.xmt_done_buffer && + (controller->link.xmt_done_buffer->transfer_buffer[0] == DDCMP_ENQ) && + (controller->link.xmt_done_buffer->transfer_buffer[1] == DDCMP_CTL_REP)); +} + +void ddcmp_dispatch(CTLR *controller, uint32 EventMask) +{ +DDCMP_STATETABLE *table; +static const char *states[] = {"Halt", "IStart", "AStart", "Run", "Maintenance"}; + +if (controller->link.Scanning) { + if (!controller->link.RecurseScan) { + controller->link.RecurseScan = TRUE; + controller->link.RecurseEventMask |= EventMask; + } + return; + } +controller->link.Scanning = TRUE; +controller->link.ScanningEvents |= EventMask; +for (table=DDCMP_TABLE; table->Conditions[0] != NULL; ++table) { + if ((table->State == controller->link.state) || + (table->State == All)) { + t_bool match = TRUE; + DDCMP_Condition_Routine *cond = table->Conditions; + DDCMP_LinkAction_Routine *action = table->Actions; + + while (match && *cond != NULL) { + match = (*cond)(controller); + ++cond; + } + if (!match) + continue; + sim_debug (DBG_INF, controller->device, "ddcmp_dispatch(%X) - %s conditions matching for rule %d, initiating actions\n", EventMask, states[table->State], table->RuleNumber); + while (*action != NULL) { + (*action)(controller); + ++action; + } + if (table->NewState != controller->link.state) { + sim_debug (DBG_INF, controller->device, "ddcmp_dispatch(%X) - state changing from %s to %s\n", EventMask, states[controller->link.state], states[table->NewState]); + controller->link.state = table->NewState; + } + } + } +controller->link.Scanning = FALSE; +controller->link.ScanningEvents &= ~EventMask; +if (controller->link.RecurseScan) { + controller->link.RecurseScan = FALSE; + EventMask = controller->link.RecurseEventMask; + controller->link.RecurseEventMask = 0; + ddcmp_dispatch (controller, EventMask); + dmc_svc (controller->unit); + } +} + + +void dmc_ddcmp_start_transmitter (CTLR *controller) +{ +BUFFER *buffer = dmc_buffer_queue_head(controller->xmt_queue); +t_stat r; + +if ((controller->link.xmt_buffer) || /* if Already Transmitting */ + (!buffer) || /* or nothing pending */ + (!controller->line->conn)) /* or not connected */ + return; /* Do nothing */ +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); + if (r == SCPE_OK) { + controller->link.xmt_buffer = buffer; + controller->ddcmp_control_packets_sent += (buffer->transfer_buffer[0] == DDCMP_ENQ) ? 1 : 0; + if (controller->byte_wait) { /* Speed limited? */ + buffer->buffer_return_time = buffer->count*controller->byte_wait + sim_grtime(); + sim_activate (controller->unit, buffer->count*controller->byte_wait); + break; + } + dmc_complete_transmit (controller); + buffer = dmc_buffer_queue_head(controller->xmt_queue); + continue; + } + break; + } +} + void dmc_process_command(CTLR *controller) { - if (dmc_is_master_clear_set(controller)) - { - dmc_process_master_clear(controller); +if (dmc_is_master_clear_set(controller)) { + dmc_process_master_clear(controller); + return; } - else - { - if (controller->transfer_state == InputTransfer) - { - dmc_process_input_transfer_completion(controller); +if (controller->transfer_state == InputTransfer) { + dmc_process_input_transfer_completion(controller); + return; + } +if (controller->transfer_state == OutputTransfer) { + dmc_check_for_output_transfer_completion(controller); + return; + } +if (controller->transfer_state == OutputControl) { + dmc_check_for_output_control_completion(controller); + return; + } +/* transfer_state Idle */ +if (dmc_is_rqi_set(controller)) { + dmc_start_input_transfer(controller); + return; + } +if (dmc_is_dmc (controller) && + (*controller->csrs->sel0 & DMC_SEL0_M_ROMI) && + (!dmc_is_run_set(controller)) && + (*controller->csrs->sel0 & DMC_SEL0_M_STEPUP)) { + /* DMC-11 or DMR-11, with RUN off and step and ROMI bits set. */ + switch (*controller->csrs->sel6) { + case DSPDSR: /* 0x22b3 (read line status instruction), set the DTR bit in SEL2. */ + dmc_setreg (controller, 2, 0x800, DBG_RGC); + break; + case DROPDTR: /* 0xa40b (drop DTR instruction) - VMS Driver uses this */ + dmc_clr_modem_dtr (controller); + break; + case UINST_CNF: /* 0x2296 (get configuration switches) - VMS Driver uses this */ + dmc_setreg (controller, 6, 0x0006, DBG_RGC); + break; + case UINST_RROM:/* 0x814d (read DMC ROM) - VMS Driver uses this */ + dmc_setreg (controller, 6, 0x0391, DBG_RGC); /* Not Low Speed uCode value (0x390) */ + break; + default: + sim_debug(DBG_WRN, controller->device, "dmc_process_command(). Unknown Microcode instruction 0x%04x", *controller->csrs->sel6); + break; } - else if (controller->transfer_state == OutputTransfer) - { - dmc_check_for_output_transfer_completion(controller); - } - else if (dmc_is_rqi_set(controller)) - { - dmc_start_input_transfer(controller); - } - else if (dmc_is_dmc (controller) && - controller->csrs->sel0 & ROMI_MASK && - controller->csrs->sel6 == DSPDSR) - /* DMC-11 or DMR-11, see if ROMI bit is set. If so, if SEL6 is - 0x22b3 (read line status instruction), set the DTR bit in SEL2. */ - { - dmc_setreg (controller, 2, 0x800, 0); + *controller->csrs->sel0 &= ~DMC_SEL0_M_STEPUP; + controller->transfer_state = Idle; + } +else { + if (dmc_is_run_set(controller)) { + dmc_start_control_output_transfer(controller); + dmc_start_transfer_buffer(controller); } } +if (tmxr_get_line_loopback (controller->line) ^ dmc_is_lu_loop_set (controller)) { + sim_debug(DBG_INF, controller->device, "%s loopback mode\n", dmc_is_lu_loop_set (controller) ? "Enabling" : "Disabling"); + tmxr_set_line_loopback (controller->line, dmc_is_lu_loop_set (controller)); + } } t_stat dmc_rd(int32 *data, int32 PA, int32 access) { - CTLR *controller = dmc_get_controller_from_address(PA); - sim_debug(DBG_TRC, controller->device, "dmc_rd(), addr=0x%x access=%d\n", PA, access); - *data = dmc_getreg(controller, PA, 1); +CTLR *controller = dmc_get_controller_from_address(PA); +int reg = PA & ((UNIBUS) ? 07 : 017); - return SCPE_OK; +*data = dmc_getreg(controller, PA, DBG_REG); +if (access == READ) { + sim_debug(DBG_REG, controller->device, "dmc_rd(), addr=0x%08x, SEL%d, data=0x%04x\n", PA, reg, *data); + } +else { + sim_debug(DBG_REG, controller->device, "dmc_rd(), addr=0x%08x, BSEL%d, data=%02x\n", PA, reg, *data & 0xFF); + } + +return SCPE_OK; } t_stat dmc_wr(int32 data, int32 PA, int32 access) { - CTLR *controller = dmc_get_controller_from_address(PA); - int reg = PA & 07; - uint16 oldValue = dmc_getreg(controller, PA, 0); - if (access == WRITE) - { - sim_debug(DBG_TRC, controller->device, "dmc_wr(), addr=0x%08x, SEL%d, data=0x%04x\n", PA, reg, data); +CTLR *controller = dmc_get_controller_from_address(PA); +int reg = PA & ((UNIBUS) ? 07 : 017); +uint16 oldValue = dmc_getreg(controller, PA, 0); + +if (access == WRITE) { + sim_debug(DBG_REG, controller->device, "dmc_wr(), addr=0x%08x, SEL%d, newdata=0x%04x, olddata=0x%04x\n", PA, reg, data, oldValue); } - else - { - sim_debug(DBG_TRC, controller->device, "dmc_wr(), addr=0x%08x, BSEL%d, data=%04x\n", PA, reg, data); +else { + sim_debug(DBG_REG, controller->device, "dmc_wr(), addr=0x%08x, BSEL%d, newdata=%02x, olddata=0x%02x\n", PA, reg, data & 0xFF, ((PA&1) ? oldValue>>8 : oldValue) & 0xFF); } - if (access == WRITE) - { - if (PA & 1) - { - sim_debug(DBG_WRN, controller->device, "dmc_wr(), Unexpected non-16-bit write access to SEL%d\n", reg); +if (access == WRITE) { + if (PA & 1) { + sim_debug(DBG_WRN, controller->device, "dmc_wr(), Unexpected non-16-bit write access to SEL%d\n", reg); } - dmc_setreg(controller, PA, data, 1); + dmc_setreg(controller, PA, data, DBG_REG); } - else - { - uint16 mask; - if (PA & 1) - { - mask = 0xFF00; - data = data << 8; +else { + uint16 mask; + if (PA & 1) { + mask = 0xFF00; + data = data << 8; } - else - { - mask = 0x00FF; + else { + mask = 0x00FF; } - dmc_setreg(controller, PA, (oldValue & ~mask) | (data & mask), 1); + dmc_setreg(controller, PA, (oldValue & ~mask) | (data & mask), DBG_REG); } - if (dmc_is_attached(controller->device->units) && (dmc_getsel(reg) == 0 || dmc_getsel(reg) == 1)) - { - dmc_process_command(controller); +if (dmc_is_attached(controller->unit) && + (dmc_getsel(reg) == 0 || dmc_getsel(reg) == 1)) {/* writes to SEL0 and SEL2 are actionable */ + if (0 == controller->dmc_wr_delay) { /* Not waiting? */ + controller->dmc_wr_delay = 10; /* Wait a bit before acting on the changed register */ + sim_activate_abs (controller->unit, controller->dmc_wr_delay); + } } - return SCPE_OK; +return SCPE_OK; } -int32 dmc_rxint (void) +int32 dmc_ininta (void) { - int i; - int32 ans = 0; /* no interrupt request active */ - for (i=0; irxi != 0) - { - DIB *dib = (DIB *)controller->device->ctxt; - ans = dib->vec; - dmc_clrrxint(controller); - break; +int i; +int32 ans = 0; /* no interrupt request active */ + +for (i=0; iin_int != 0) { + DIB *dib = (DIB *)controller->device->ctxt; + ans = dib->vec + (8 * (int)(controller->unit - controller->device->units)); + dmc_clrinint(controller); + sim_debug(DBG_INT, controller->device, "RXINTA Device %d - Vector: 0x%x\n", (int)(controller->unit-controller->device->units), ans); + break; } } - return ans; - } +return ans; +} -int32 dmc_txint (void) +int32 dmc_outinta (void) { - int i; - int32 ans = 0; /* no interrupt request active */ - for (i=0; itxi != 0) - { - DIB *dib = (DIB *)controller->device->ctxt; - ans = dib->vec + 4; - dmc_clrtxint(controller); - break; +int i; +int32 ans = 0; /* no interrupt request active */ + +for (i=0; iout_int != 0) { + DIB *dib = (DIB *)controller->device->ctxt; + ans = dib->vec + 4 + (8 * (int)(controller->unit - controller->device->units)); + dmc_clroutint(controller); + sim_debug(DBG_INT, controller->device, "TXINTA Device %d - Vector: 0x%x\n", (int)(controller->unit-controller->device->units), ans); + break; } } - return ans; +return ans; } t_stat dmc_reset (DEVICE *dptr) { - t_stat ans = SCPE_OK; - CTLR *controller = dmc_get_controller_from_device(dptr); +t_stat ans = SCPE_OK; +CTLR *controller; +uint32 i, j; - sim_debug(DBG_TRC, dptr, "dmc_reset()\n"); +sim_debug(DBG_TRC, dptr, "dmc_reset(%s)\n", dptr->name); - dmc_buffer_queue_init_all(controller); - dmc_clrrxint(controller); - dmc_clrtxint(controller); - sim_cancel (controller->device->units); /* stop poll */ - - if (!(dptr->flags & DEV_DIS)) - { - ans = auto_config (dptr->name, DMC_UNITSPERDEVICE); +/* Connect structures together */ +for (i=0; i < DMC_NUMDEVICE; i++) { + dmc_csrs[i].sel0 = &dmc_sel0[i]; + dmc_csrs[i].sel2 = &dmc_sel2[i]; + dmc_csrs[i].sel4 = &dmc_sel4[i]; + dmc_csrs[i].sel6 = &dmc_sel6[i]; + controller = &dmc_ctrls[i]; + controller->csrs = &dmc_csrs[i]; + controller->line = &dmc_desc.ldsc[i]; + controller->rcv_queue = &dmc_rcv_queues[i]; + controller->completion_queue = &dmc_completion_queues[i]; + controller->xmt_queue = &dmc_xmt_queues[i]; + controller->ack_wait_queue = &dmc_ack_wait_queues[i]; + controller->free_queue = &dmc_free_queues[i]; + controller->buffers = &dmc_buffers[i]; + controller->device = &dmc_dev; + controller->baseaddr = &dmc_baseaddr[i]; + controller->basesize = &dmc_basesize[i]; + controller->modem = &dmc_modem[i]; + controller->unit = &controller->device->units[i]; + controller->unit->ctlr = (void *)controller; + controller->index = i; } - return ans; +tmxr_connection_poll_interval (&dmc_desc, dmc_connect_poll); +for (i=0; i < DMP_NUMDEVICE; i++) { + dmp_csrs[i].sel0 = &dmp_sel0[i]; + dmp_csrs[i].sel2 = &dmp_sel2[i]; + dmp_csrs[i].sel4 = &dmp_sel4[i]; + dmp_csrs[i].sel6 = &dmp_sel6[i]; + dmp_csrs[i].sel10 = &dmp_sel10[i]; + controller = &dmc_ctrls[i+DMC_NUMDEVICE]; + controller->csrs = &dmp_csrs[i]; + controller->line = &dmp_desc.ldsc[i]; + controller->rcv_queue = &dmp_rcv_queues[i]; + controller->completion_queue = &dmp_completion_queues[i]; + controller->xmt_queue = &dmp_xmt_queues[i]; + controller->ack_wait_queue = &dmp_ack_wait_queues[i]; + controller->free_queue = &dmp_free_queues[i]; + controller->buffers = &dmp_buffers[i]; + controller->device = (UNIBUS) ? &dmp_dev : &dmv_dev; + controller->dev_type = DMP; + controller->baseaddr = &dmp_baseaddr[i]; + controller->basesize = &dmp_basesize[i]; + controller->modem = &dmp_modem[i]; + controller->unit = &controller->device->units[i]; + controller->unit->ctlr = (void *)controller; + controller->index = i + DMC_NUMDEVICE; + } +tmxr_connection_poll_interval (&dmp_desc, dmp_connect_poll); +if (0 == dmc_units[0].flags) { /* First Time Initializations */ + for (i=0; i < DMC_NUMDEVICE; i++) { + controller = &dmc_ctrls[i]; + controller->state = Uninitialised; + controller->transfer_state = Idle; + controller->control_out = NULL; + *controller->modem = 0; +#if defined (VM_PDP10) + controller->dev_type = DMR; +#else + controller->dev_type = DMC; +#endif + dmc_dev.units[i] = dmc_unit_template; + controller->unit->ctlr = (void *)controller; + } + 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].ctlr = dmc_units[0].ctlr; + dmc_units[dmc_dev.numunits-1] = dmc_timer_unit_template; + dmc_units[dmc_dev.numunits-1].ctlr = dmc_units[0].ctlr; + dmc_desc.notelnet = TRUE; /* We always want raw tcp socket */ + dmc_desc.dptr = &dmc_dev; /* Connect appropriate device */ + dmc_desc.uptr = dmc_units+dmc_desc.lines; /* Identify polling unit */ + for (i=0; i < DMP_NUMDEVICE; i++) { + controller = &dmc_ctrls[i+DMC_NUMDEVICE]; + controller->state = Uninitialised; + controller->transfer_state = Idle; + controller->control_out = NULL; + *controller->modem = 0; + dmp_dev.units[i] = dmc_unit_template; + controller->unit->ctlr = (void *)controller; + } + 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].ctlr = dmp_units[0].ctlr; + dmp_units[dmp_dev.numunits-1] = dmc_timer_unit_template; + dmp_units[dmp_dev.numunits-1].ctlr = dmp_units[0].ctlr; + dmp_desc.notelnet = TRUE; /* We always want raw tcp socket */ + dmp_desc.dptr = &dmp_dev; /* Connect appropriate device */ + dmp_desc.uptr = dmp_units+dmp_desc.lines; /* Identify polling unit */ + } +else { + BUFFER *buffer; + + /* Avoid memory leaks by moving all buffers back to the free queue + and then freeing any allocated transfer buffers for each buffer + on the free queue */ + while (controller->ack_wait_queue->count) { + buffer = (BUFFER *)remqueue (controller->ack_wait_queue->hdr.next); + assert (insqueue (&buffer->hdr, controller->free_queue->hdr.prev)); + } + while (controller->completion_queue->count) { + buffer = (BUFFER *)remqueue (controller->completion_queue->hdr.next); + assert (insqueue (&buffer->hdr, controller->free_queue->hdr.prev)); + } + while (controller->rcv_queue->count) { + buffer = (BUFFER *)remqueue (controller->rcv_queue->hdr.next); + assert (insqueue (&buffer->hdr, controller->free_queue->hdr.prev)); + } + while (controller->xmt_queue->count) { + buffer = (BUFFER *)remqueue (controller->xmt_queue->hdr.next); + assert (insqueue (&buffer->hdr, controller->free_queue->hdr.prev)); + } + for (i = 0; i < controller->free_queue->size; i++) { + buffer = (BUFFER *)remqueue (controller->free_queue->hdr.next); + free (buffer->transfer_buffer); + assert (insqueue (&buffer->hdr, controller->free_queue->hdr.prev)); + } + } + +ans = auto_config (dptr->name, (dptr->flags & DEV_DIS) ? 0 : dptr->numunits - 2); + +if (!(dptr->flags & DEV_DIS)) { + for (i = 0; i < DMC_NUMDEVICE + DMP_NUMDEVICE; i++) { + if (dmc_ctrls[i].device == dptr) { + controller = &dmc_ctrls[i]; + dmc_buffer_queue_init_all(controller); + dmc_clrinint(controller); + dmc_clroutint(controller); + for (j=0; jnumunits-1; j++) + sim_cancel (&dptr->units[j]); /* stop poll */ + dmc_process_master_clear(controller); + } + } + sim_activate_after (dptr->units+dptr->numunits-2, DMC_CONNECT_POLL*1000000);/* start poll */ + } + +return ans; } t_stat dmc_attach (UNIT *uptr, char *cptr) { - CTLR *controller = dmc_get_controller_from_unit(uptr); - t_stat ans = SCPE_OK; +DEVICE *dptr = (UNIBUS) ? ((&dmc_dev == find_dev_from_unit(uptr)) ? &dmc_dev : &dmp_dev) : &dmv_dev; +int32 dmc = (int32)(uptr-dptr->units); +TMXR *mp = (dptr == &dmc_dev) ? &dmc_desc : &dmp_desc; +t_stat ans = SCPE_OK; +char *peer = ((dptr == &dmc_dev)? &dmc_peer[dmc][0] : &dmp_peer[dmc][0]); +char *port = ((dptr == &dmc_dev)? &dmc_port[dmc][0] : &dmp_port[dmc][0]); +char attach_string[1024]; - if (dmc_is_attached(uptr)) - { - dmc_detach(uptr); +if (!cptr || !*cptr) + return SCPE_ARG; +if (!(uptr->flags & UNIT_ATTABLE)) + return SCPE_NOATT; +if (!peer[0]) { + printf ("Peer must be specified before attach\n"); + if (sim_log) + fprintf (sim_log, "Peer must be specified before attach\n"); + return SCPE_ARG; } - - ans = dmc_open_master_socket(controller, cptr); - if (ans == SCPE_OK) - { - controller->line->socket = INVALID_SOCKET; - uptr->flags = uptr->flags | UNIT_ATT; /* set unit attached flag */ - uptr->filename = (char *)malloc(strlen(cptr)+1); - strcpy(uptr->filename, cptr); - controller->line->receive_port = uptr->filename; - dmc_reset_unit_stats(controller->stats); - } - +sprintf (attach_string, "Line=%d,Buffered=16384,Connect=%s,%s", dmc, peer, cptr); +ans = tmxr_open_master (mp, attach_string); /* open master socket */ +if (ans != SCPE_OK) return ans; +strncpy (port, cptr, CBUFSIZE-1); +uptr->filename = (char *)malloc (strlen(port)+1); +strcpy (uptr->filename, port); +uptr->flags |= UNIT_ATT; +sim_activate_after (dptr->units+mp->lines, DMC_CONNECT_POLL*1000000);/* start poll */ +return ans; } t_stat dmc_detach (UNIT *uptr) { - CTLR *controller = dmc_get_controller_from_unit(uptr); - dmc_error_and_close_socket(controller, "Detach"); - dmc_close_master_socket(controller); - uptr->flags = uptr->flags & ~UNIT_ATT; /* clear unit attached flag */ - free(uptr->filename); - uptr->filename = NULL; - sim_cancel(uptr); +DEVICE *dptr = (UNIBUS) ? ((&dmc_dev == find_dev_from_unit(uptr)) ? &dmc_dev : &dmp_dev) : &dmv_dev; +int32 dmc = (int32)(uptr-dptr->units); +TMXR *mp = (dptr == &dmc_dev) ? &dmc_desc : &dmp_desc; +TMLN *lp = &mp->ldsc[dmc]; +int32 i, attached; +if (!(uptr->flags & UNIT_ATT)) /* attached? */ return SCPE_OK; +sim_cancel (uptr); +uptr->flags &= ~UNIT_ATT; +for (i=attached=0; ilines; i++) + if (dptr->units[i].flags & UNIT_ATT) + ++attached; +if (!attached) + sim_cancel (dptr->units+mp->lines); /* stop poll on last detach */ +free (uptr->filename); +uptr->filename = NULL; +return tmxr_detach_ln (lp); } char *dmc_description (DEVICE *dptr) - { - return "DMC11 Synchronous network controller"; - } +{ +#if defined (VM_PDP10) +return "DMR11 Synchronous network controller"; +#else +return "DMC11 Synchronous network controller"; +#endif +} char *dmp_description (DEVICE *dptr) - { - return "DMP11 Synchronous network controller"; - } +{ +return (UNIBUS) ? "DMP11 Synchronous network controller" + : "DMV11 Synchronous network controller"; +} diff --git a/PDP11/pdp11_dmc.h b/PDP11/pdp11_dmc.h deleted file mode 100644 index 49659053..00000000 --- a/PDP11/pdp11_dmc.h +++ /dev/null @@ -1,146 +0,0 @@ -/* pdp11_dmc.h: DMC11 Emulation - ------------------------------------------------------------------------------ - - Copyright (c) 2011, Robert M. A. Jarratt - - Permission is hereby granted, free of charge, to any person obtaining a - copy of this software and associated documentation files (the "Software"), - to deal in the Software without restriction, including without limitation - the rights to use, copy, modify, merge, publish, distribute, sublicense, - and/or sell copies of the Software, and to permit persons to whom the - Software is furnished to do so, subject to the following conditions: - - The above copyright notice and this permission notice shall be included in - all copies or substantial portions of the Software. - - THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR - IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, - FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL - THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER - IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN - CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. - - Except as contained in this notice, the name of the author shall not be - used in advertising or otherwise to promote the sale, use or other dealings - in this Software without prior written authorization from the author. - - ------------------------------------------------------------------------------ - - Modification history: - - 15-Jan-13 RJ Contribution from Paul Koning of support for RSTS using the ROM - INPUT (ROM I) command to get the DMC11 to report DSR status. - ------------------------------------------------------------------------------*/ - -// Notes -// Base address needs to be 760060 according to DMC11 manual, but SYSGEN seems to think CSR is 0760100. However if I use -// 0760100 I get a conflict with the DZ because the first 13 bits are still 00100. If I use 760060 VMS sees the XM device, but -// if I remove the DZ to prevent the conflict VMS does not see an XM device, but I do get lots of reads and writes, possibly -// because it thinks it is a different device. What worries me more though is that there seems to be overlap in the 13-bit base -// addresses of the DZ and DMC. - - -#ifndef PDP11_DMC_H -#define PDP11_DMC_H - -#if defined (VM_VAX) /* VAX version */ -#include "vax_defs.h" -extern int32 int_req[IPL_HLVL]; -#elif defined(VM_PDP10) -#include "pdp10_defs.h" -//#define IPL_HLVL 8 /* # int levels */ -extern int32 int_req; -#else /* PDP-11 version */ -#include "pdp11_defs.h" -extern int32 int_req[IPL_HLVL]; -#endif - -#include "sim_sock.h" - -#define DMC_NUMDEVICE 4 /* # DMC-11 devices */ -#define DMC_UNITSPERDEVICE 1 /* # units per DMC-11 */ - -#define DMP_NUMDEVICE 1 /* # DMP-11 devices */ -#define DMP_UNITSPERDEVICE 1 /* # units per DMP-11 */ - -#define DMC_RDX 8 - -/* debugging bitmaps */ -#define DBG_TRC 0x0001 /* trace routine calls */ -#define DBG_REG 0x0002 /* trace read/write registers */ -#define DBG_WRN 0x0004 /* display warnings */ -#define DBG_INF 0x0008 /* display informational messages (high level trace) */ -#define DBG_DAT 0x0010 /* display data buffer contents */ -#define DBG_DTS 0x0020 /* display data summary */ -#define DBG_SOK 0x0040 /* display socket open/close */ -#define DBG_CON 0x0080 /* display socket connection establishment */ - -#define TYPE_BACCI 0 -#define TYPE_CNTLI 1 -#define TYPE_BASEI 03 -#define TYPE_BACCO 0 -#define TYPE_CNTLO 1 - -#define TYPE_DMP_MODE 2 -#define TYPE_DMP_CONTROL 1 -#define TYPE_DMP_RECEIVE 0 -#define TYPE_DMP_TRANSMIT 4 - - -/* SEL0 */ -#define DMC_TYPE_INPUT_MASK 0x0003 -#define DMC_IN_IO_MASK 0x0004 -#define DMP_IEO_MASK 0x0010 -#define DMC_RQI_MASK 0x0020 -#define DMP_RQI_MASK 0x0080 -#define DMC_RDYI_MASK 0x0080 -#define DMC_IEI_MASK 0x0040 -#define DMP_IEI_MASK 0x0001 -#define ROMI_MASK 0x0200 -#define LU_LOOP_MASK 0x0800 -#define MASTER_CLEAR_MASK 0x4000 -#define RUN_MASK 0x8000 - -/* SEL2 */ -#define DMP_IN_IO_MASK 0x0004 -#define DMP_TYPE_INPUT_MASK 0x0007 -#define TYPE_OUTPUT_MASK 0x0003 -#define OUT_IO_MASK 0x0004 -#define DMC_RDYO_MASK 0x0080 -#define DMC_IEO_MASK 0x0040 -#define DMP_RDYI_MASK 0x0010 - -/* BSEL6 */ -#define LOST_DATA_MASK 0x0010 -#define DISCONNECT_MASK 0x0040 - -#define DSPDSR 0x22b3 /* KMC opcode to move line unit status to SEL2 */ - -#define SEL0_RUN_BIT 15 -#define SEL0_MCLR_BIT 14 -#define SEL0_LU_LOOP_BIT 11 -#define SEL0_ROMI_BIT 9 -#define SEL0_RDI_BIT 7 -#define SEL0_DMC_IEI_BIT 6 -#define SEL0_DMP_IEI_BIT 0 -#define SEL0_DMP_IEO_BIT 4 -#define SEL0_DMC_RQI_BIT 5 -#define SEL0_DMP_RQI_BIT 7 -#define SEL0_IN_IO_BIT 2 -#define SEL0_TYPEI_BIT 0 - -#define SEL2_TYPEO_BIT 0 -#define SEL2_RDO_BIT 7 -#define SEL2_IEO_BIT 6 -#define SEL2_OUT_IO_BIT 2 -#define SEL2_LINE_BIT 8 -#define SEL2_LINE_BIT_LENGTH 6 -#define SEL2_PRIO_BIT 14 -#define SEL2_PRIO_BIT_LENGTH 2 - -#define SEL6_LOST_DATA_BIT 4 -#define SEL6_DISCONNECT_BIT 6 - -#define BUFFER_QUEUE_SIZE 7 - -#endif /* _VAX_DMC_H */ diff --git a/PDP11/pdp11_dup.c b/PDP11/pdp11_dup.c index 6c6a6102..8d63a1ee 100644 --- a/PDP11/pdp11_dup.c +++ b/PDP11/pdp11_dup.c @@ -65,57 +65,68 @@ extern int32 tmxr_poll; /* calibrated delay */ extern int32 clk_tps; /* clock ticks per second */ extern int32 tmr_poll; /* instructions per tick */ -uint16 dup_rxcsr[DUP_LINES]; -uint16 dup_rxdbuf[DUP_LINES]; -uint16 dup_parcsr[DUP_LINES]; -uint16 dup_txcsr[DUP_LINES]; -uint16 dup_txdbuf[DUP_LINES]; -uint32 dup_rxi = 0; /* rcv interrupts */ -uint32 dup_txi = 0; /* xmt interrupts */ -uint32 dup_wait[DUP_LINES]; /* rcv/xmt byte delay */ -uint32 dup_speed[DUP_LINES]; /* line speed (bits/sec) */ -uint8 *dup_rcvpacket[DUP_LINES]; /* rcv buffer */ -uint16 dup_rcvpksize[DUP_LINES]; /* rcv buffer size */ -uint16 dup_rcvpkoffset[DUP_LINES]; /* rcv buffer offset */ -uint16 dup_rcvpkinoff[DUP_LINES]; /* rcv packet in offset */ -uint8 *dup_xmtpacket[DUP_LINES]; /* xmt buffer */ -uint16 dup_xmtpksize[DUP_LINES]; /* xmt buffer size */ -uint16 dup_xmtpkoffset[DUP_LINES]; /* xmt buffer offset */ -uint16 dup_xmtpkoutoff[DUP_LINES]; /* xmt packet out offset */ -t_bool dup_xmtpkrdy[DUP_LINES]; /* xmt packet ready */ +static uint16 dup_rxcsr[DUP_LINES]; +static uint16 dup_rxdbuf[DUP_LINES]; +static uint16 dup_parcsr[DUP_LINES]; +static uint16 dup_txcsr[DUP_LINES]; +static uint16 dup_txdbuf[DUP_LINES]; +static uint16 dup_W3[DUP_LINES]; +static uint16 dup_W5[DUP_LINES]; +static uint16 dup_W6[DUP_LINES]; +static uint32 dup_rxi = 0; /* rcv interrupts */ +static uint32 dup_txi = 0; /* xmt interrupts */ +static uint32 dup_wait[DUP_LINES]; /* rcv/xmt byte delay */ +static uint32 dup_speed[DUP_LINES]; /* line speed (bits/sec) */ +static uint8 *dup_rcvpacket[DUP_LINES]; /* rcv buffer */ +static uint16 dup_rcvpksize[DUP_LINES]; /* rcv buffer size */ +static uint16 dup_rcvpkbytes[DUP_LINES]; /* rcv buffer size of packet */ +static uint16 dup_rcvpkinoff[DUP_LINES]; /* rcv packet in offset */ +static uint8 *dup_xmtpacket[DUP_LINES]; /* xmt buffer */ +static uint16 dup_xmtpksize[DUP_LINES]; /* xmt buffer size */ +static uint16 dup_xmtpkoffset[DUP_LINES]; /* xmt buffer offset */ +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 */ -PACKET_RECEIVE_CALLBACK dup_rcv_packet_callback[DUP_LINES]; -PACKET_TRANSMIT_COMPLETE_CALLBACK dup_xmt_complete_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 MODEM_CHANGE_CALLBACK dup_modem_change_callback[DUP_LINES]; -t_stat dup_rd (int32 *data, int32 PA, int32 access); -t_stat dup_wr (int32 data, int32 PA, int32 access); -t_stat dup_set_modem (int32 dup, int32 rxcsr_bits); -t_stat dup_get_modem (int32 dup); -t_stat dup_svc (UNIT *uptr); -t_stat dup_poll_svc (UNIT *uptr); -t_stat dup_rcv_byte (int32 dup); -t_stat dup_reset (DEVICE *dptr); -t_stat dup_attach (UNIT *uptr, char *ptr); -t_stat dup_detach (UNIT *uptr); -t_stat dup_clear (int32 dup, t_bool flag); -int32 dup_rxinta (void); -int32 dup_txinta (void); -void dup_update_rcvi (void); -void dup_update_xmti (void); -void dup_clr_rxint (int32 dup); -void dup_set_rxint (int32 dup); -void dup_clr_txint (int32 dup); -void dup_set_txint (int32 dup); -t_stat dup_setnl (UNIT *uptr, int32 val, char *cptr, void *desc); -t_stat dup_setspeed (UNIT* uptr, int32 val, char* cptr, void* desc); -t_stat dup_showspeed (FILE* st, UNIT* uptr, int32 val, void* desc); -t_stat dup_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); -t_stat dup_help_attach (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr); -char *dup_description (DEVICE *dptr); +static t_stat dup_rd (int32 *data, int32 PA, int32 access); +static t_stat dup_wr (int32 data, int32 PA, int32 access); +static t_stat dup_set_modem (int32 dup, int32 rxcsr_bits); +static t_stat dup_get_modem (int32 dup); +static t_stat dup_svc (UNIT *uptr); +static t_stat dup_poll_svc (UNIT *uptr); +static t_stat dup_rcv_byte (int32 dup); +static t_stat dup_reset (DEVICE *dptr); +static t_stat dup_attach (UNIT *uptr, char *ptr); +static t_stat dup_detach (UNIT *uptr); +static t_stat dup_clear (int32 dup, t_bool flag); +static int32 dup_rxinta (void); +static int32 dup_txinta (void); +static void dup_update_rcvi (void); +static void dup_update_xmti (void); +static void dup_clr_rxint (int32 dup); +static void dup_set_rxint (int32 dup); +static void dup_clr_txint (int32 dup); +static void dup_set_txint (int32 dup); +static t_stat dup_setnl (UNIT *uptr, int32 val, 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_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); +static t_stat dup_show_W5 (FILE* st, UNIT* uptr, int32 val, void* desc); +static t_stat dup_set_W6 (UNIT* uptr, int32 val, char* cptr, void* desc); +static t_stat dup_show_W6 (FILE* st, UNIT* uptr, int32 val, void* desc); +static t_stat dup_help (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); +static char *dup_description (DEVICE *dptr); /* RXCSR - 16XXX0 - receiver control/status register */ -BITFIELD dup_rxcsr_bits[] = { +static BITFIELD dup_rxcsr_bits[] = { BIT(BDATSET), /* Data Set Change B */ #define RXCSR_V_BDATSET 0 #define RXCSR_M_BDATSET (1<> 1) & 03) { /* case on PA<2:1> */ case 00: /* RXCSR */ dup_get_modem (dup); *data = dup_rxcsr[dup]; + dup_rxcsr[dup] &= ~(RXCSR_M_DSCHNG|RXCSR_M_BDATSET); break; case 01: /* RXDBUF */ @@ -493,7 +526,7 @@ sim_debug_bits(DBG_REG, DUPDPTR, bitdefs[(PA >> 1) & 03], (uint32)(orig_val), (u return SCPE_OK; } -t_stat dup_wr (int32 data, int32 PA, int32 access) +static t_stat dup_wr (int32 data, int32 PA, int32 access) { static BITFIELD* bitdefs[] = {dup_rxcsr_bits, dup_parcsr_bits, dup_txcsr_bits, dup_txdbuf_bits}; static uint16 *regs[] = {dup_rxcsr, dup_parcsr, dup_txcsr, dup_txdbuf}; @@ -505,7 +538,7 @@ if (dup >= dup_desc.lines) /* validate line number orig_val = regs[(PA >> 1) & 03][dup]; if (PA & 1) /* unaligned byte access? */ - data = ((data << 8) & (orig_val & 0xFF)) & 0xFFFF; /* Merge with original word */ + data = ((data << 8) | (orig_val & 0xFF)) & 0xFFFF; /* Merge with original word */ else if (access == WRITEB) /* byte access? */ data = (orig_val & 0xFF00) | (data & 0xFF); /* Merge with original high word */ @@ -535,10 +568,11 @@ switch ((PA >> 1) & 03) { /* case on PA<2:1> */ } if ((!(dup_rxcsr[dup] & RXCSR_M_RCVEN)) && (orig_val & RXCSR_M_RCVEN)) { /* Downward transition of receiver enable */ - dup_rxcsr[dup] &= ~(RXCSR_M_RXDONE|RXCSR_M_RXACT); + dup_rxdbuf[dup] &= ~RXDBUF_M_RXDBUF; + dup_rxcsr[dup] &= ~RXCSR_M_RXACT; if ((dup_rcvpkinoff[dup] != 0) || - (dup_rcvpkoffset[dup] != 0)) - dup_rcvpkinoff[dup] = dup_rcvpkoffset[dup] = 0; + (dup_rcvpkbytes[dup] != 0)) + dup_rcvpkinoff[dup] = dup_rcvpkbytes[dup] = 0; } if ((!(dup_rxcsr[dup] & RXCSR_M_RXIE)) && (orig_val & RXCSR_M_RXIE)) /* Downward transition of receiver interrupt enable */ @@ -555,12 +589,40 @@ switch ((PA >> 1) & 03) { /* case on PA<2:1> */ case 02: /* TXCSR */ dup_txcsr[dup] &= ~TXCSR_WRITEABLE; dup_txcsr[dup] |= (data & TXCSR_WRITEABLE); - if ((!(dup_txcsr[dup] & TXCSR_M_SEND)) && (orig_val & TXCSR_M_SEND)) - dup_txcsr[dup] &= ~TXCSR_M_TXACT; if (dup_txcsr[dup] & TXCSR_M_DRESET) { - dup_clear(dup, FALSE); + dup_clear(dup, dup_W3[dup]); break; } + if (TXCSR_GETMAISEL(dup_txcsr[dup]) != TXCSR_GETMAISEL(orig_val)) { /* Maint Select Changed */ + switch (TXCSR_GETMAISEL(dup_txcsr[dup])) { + case 0: /* User/Normal Mode */ + tmxr_set_line_loopback (&dup_desc.ldsc[dup], FALSE); + break; + case 1: /* External Loopback Mode */ + case 2: /* Internal Loopback Mode */ + tmxr_set_line_loopback (&dup_desc.ldsc[dup], TRUE); + break; + case 3: /* System Test Mode */ + break; + } + } + if ((dup_txcsr[dup] & TXCSR_M_TXACT) && + (!(orig_val & TXCSR_M_TXACT)) && + (orig_val & TXCSR_M_TXDONE)) { + dup_txcsr[dup] &= ~TXCSR_M_TXDONE; + } + if ((!(dup_txcsr[dup] & TXCSR_M_SEND)) && + (orig_val & TXCSR_M_SEND)) { + dup_txcsr[dup] &= ~TXCSR_M_TXACT; + dup_put_msg_bytes (dup, NULL, 0, FALSE, TRUE); + } + if ((dup_txcsr[dup] & TXCSR_M_HALFDUP) ^ (orig_val & TXCSR_M_HALFDUP)) + tmxr_set_line_halfduplex (dup_desc.ldsc+dup, dup_txcsr[dup] & TXCSR_M_HALFDUP); + if ((dup_txcsr[dup] & TXCSR_M_TXIE) && + (!(orig_val & TXCSR_M_TXIE)) && + (dup_txcsr[dup] & TXCSR_M_TXDONE)) { + dup_set_txint (dup); + } break; case 03: /* TXDBUF */ @@ -576,10 +638,11 @@ switch ((PA >> 1) & 03) { /* case on PA<2:1> */ sim_debug(DBG_REG, DUPDPTR, "dup_wr(PA=0x%08X [%s], data=0x%X) ", PA, dup_wr_regs[(PA >> 1) & 03], data); sim_debug_bits(DBG_REG, DUPDPTR, bitdefs[(PA >> 1) & 03], (uint32)orig_val, (uint32)regs[(PA >> 1) & 03][dup], TRUE); +dup_get_modem (dup); return SCPE_OK; } -t_stat dup_set_modem (int32 dup, int32 rxcsr_bits) +static t_stat dup_set_modem (int32 dup, int32 rxcsr_bits) { int32 bits_to_set, bits_to_clear; @@ -591,30 +654,56 @@ tmxr_set_get_modem_bits (dup_desc.ldsc+dup, bits_to_set, bits_to_clear, NULL); return SCPE_OK; } -t_stat dup_get_modem (int32 dup) +static t_stat dup_get_modem (int32 dup) { int32 modem_bits; +uint16 old_rxcsr = dup_rxcsr[dup]; int32 old_rxcsr_a_modem_bits, new_rxcsr_a_modem_bits, old_rxcsr_b_modem_bits, new_rxcsr_b_modem_bits; TMLN *lp = &dup_desc.ldsc[dup]; +t_bool new_modem_change = FALSE; +if (dup_W5[dup]) + old_rxcsr_a_modem_bits = dup_rxcsr[dup] & (RXCSR_M_RING | RXCSR_M_CTS | RXCSR_M_DSR | RXCSR_M_DCD); +else + old_rxcsr_a_modem_bits = dup_rxcsr[dup] & (RXCSR_M_RING | RXCSR_M_CTS); +if (dup_W6[dup]) + old_rxcsr_b_modem_bits = dup_rxcsr[dup] & RXCSR_B_MODEM_BITS; +else + old_rxcsr_b_modem_bits = 0; old_rxcsr_a_modem_bits = dup_rxcsr[dup] & RXCSR_A_MODEM_BITS; old_rxcsr_b_modem_bits = dup_rxcsr[dup] & RXCSR_B_MODEM_BITS; tmxr_set_get_modem_bits (lp, 0, 0, &modem_bits); -new_rxcsr_a_modem_bits = (((modem_bits & TMXR_MDM_RNG) ? RXCSR_M_RING : 0) | - ((modem_bits & TMXR_MDM_DCD) ? RXCSR_M_DCD : 0) | - ((modem_bits & TMXR_MDM_CTS) ? RXCSR_M_CTS : 0)); -new_rxcsr_b_modem_bits = ((modem_bits & TMXR_MDM_DSR) ? RXCSR_M_DSR : 0); +if (dup_W5[dup]) + new_rxcsr_a_modem_bits = (((modem_bits & TMXR_MDM_RNG) ? RXCSR_M_RING : 0) | + ((modem_bits & TMXR_MDM_CTS) ? RXCSR_M_CTS : 0) | + ((modem_bits & TMXR_MDM_DSR) ? RXCSR_M_DSR : 0) | + ((modem_bits & TMXR_MDM_DCD) ? RXCSR_M_DCD : 0)); +else + new_rxcsr_a_modem_bits = (((modem_bits & TMXR_MDM_RNG) ? RXCSR_M_RING : 0) | + ((modem_bits & TMXR_MDM_CTS) ? RXCSR_M_CTS : 0)); +if (dup_W6[dup]) + new_rxcsr_b_modem_bits = (((modem_bits & TMXR_MDM_DSR) ? RXCSR_M_DSR : 0) | + ((modem_bits & TMXR_MDM_DCD) ? RXCSR_M_DCD : 0)); +else + new_rxcsr_b_modem_bits = 0; dup_rxcsr[dup] &= ~(RXCSR_A_MODEM_BITS | RXCSR_B_MODEM_BITS); dup_rxcsr[dup] |= new_rxcsr_a_modem_bits | new_rxcsr_b_modem_bits; -if (old_rxcsr_a_modem_bits != new_rxcsr_a_modem_bits) +if (old_rxcsr_a_modem_bits != new_rxcsr_a_modem_bits) { dup_rxcsr[dup] |= RXCSR_M_DSCHNG; -else - dup_rxcsr[dup] &= ~RXCSR_M_DSCHNG; -if (old_rxcsr_b_modem_bits != new_rxcsr_b_modem_bits) + new_modem_change = TRUE; + } +if (old_rxcsr_b_modem_bits != new_rxcsr_b_modem_bits) { dup_rxcsr[dup] |= RXCSR_M_BDATSET; -else - dup_rxcsr[dup] &= ~RXCSR_M_BDATSET; + new_modem_change = TRUE; + } +if (new_modem_change) { + sim_debug(DBG_MDM, DUPDPTR, "dup_get_modem() - Modem Signal Change "); + sim_debug_bits(DBG_MDM, DUPDPTR, dup_rxcsr_bits, (uint32)old_rxcsr, (uint32)dup_rxcsr[dup], TRUE); + } +if (dup_modem_change_callback[dup] && new_modem_change) + dup_modem_change_callback[dup](dup); if ((dup_rxcsr[dup] & RXCSR_M_DSCHNG) && + ((dup_rxcsr[dup] & RXCSR_M_DSCHNG) != (old_rxcsr & RXCSR_M_DSCHNG)) && (dup_rxcsr[dup] & RXCSR_M_DSCIE)) dup_set_rxint (dup); return SCPE_OK; @@ -630,97 +719,175 @@ DEVICE *dptr = DUPDPTR; DIB *dib = (DIB *)dptr->ctxt; CSRPA += IOPAGEBASE; -if ((dib->ba > (uint32)CSRPA) || ((uint32)CSRPA > (dib->ba + dib->lnt))) +if ((dib->ba > (uint32)CSRPA) || ((uint32)CSRPA > (dib->ba + dib->lnt)) || (DUPDPTR->flags & DEV_DIS)) return -1; -return ((uint32)CSRPA - dib->ba)/dib->lnt; +return ((uint32)CSRPA - dib->ba)/IOLN_DUP; } -void dup_set_callback_mode (int32 dup, PACKET_RECEIVE_CALLBACK receive, PACKET_TRANSMIT_COMPLETE_CALLBACK transmit) +void dup_set_callback_mode (int32 dup, PACKET_DATA_AVAILABLE_CALLBACK receive, PACKET_TRANSMIT_COMPLETE_CALLBACK transmit, MODEM_CHANGE_CALLBACK modem) { -if ((dup < 0) || (dup >= dup_desc.lines)) +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) return; -dup_rcv_packet_callback[dup] = receive; +dup_rcv_packet_data_callback[dup] = receive; dup_xmt_complete_callback[dup] = transmit; -} - -int32 dup_get_line_speed (int32 dup) -{ -if ((dup < 0) || (dup >= dup_desc.lines)) - return -1; -return dup_speed[dup]; +dup_modem_change_callback[dup] = modem; } int32 dup_get_DCD (int32 dup) { -if ((dup < 0) || (dup >= dup_desc.lines)) +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) return -1; return (dup_rxcsr[dup] & RXCSR_M_DCD) ? 1 : 0; } +int32 dup_get_DSR (int32 dup) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return -1; +return (dup_rxcsr[dup] & RXCSR_M_DSR) ? 1 : 0; +} + +int32 dup_get_CTS (int32 dup) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return -1; +return (dup_rxcsr[dup] & RXCSR_M_CTS) ? 1 : 0; +} + +int32 dup_get_RING (int32 dup) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return -1; +return (dup_rxcsr[dup] & RXCSR_M_RING) ? 1 : 0; +} + +int32 dup_get_RCVEN (int32 dup) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return -1; +return (dup_rxcsr[dup] & RXCSR_M_RCVEN) ? 1 : 0; +} + t_stat dup_set_DTR (int32 dup, t_bool state) { -if ((dup < 0) || (dup >= dup_desc.lines)) +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) return SCPE_IERR; -dup_set_modem (dup, state ? (RXCSR_M_DTR | RXCSR_M_RTS) : 0); -return SCPE_OK; -} - -t_stat dup_set_DDCMP (int32 dup, t_bool state) -{ -if ((dup < 0) || (dup >= dup_desc.lines)) - return SCPE_IERR; - -dup_rxcsr[dup] &= ~RXCSR_M_STRSYN; -dup_rxcsr[dup] |= (state ? 0: RXCSR_M_STRSYN); -dup_parcsr[dup] &= ~PARCSR_M_NOCRC; -dup_parcsr[dup] |= (state ? 0: PARCSR_M_NOCRC); -dup_parcsr[dup] &= ~PARCSR_M_DECMODE; -dup_parcsr[dup] |= (state ? PARCSR_M_DECMODE : 0); -return SCPE_OK; -} - -t_stat dup_rcv_byte (int32 dup) -{ -sim_debug (DBG_TRC, DUPDPTR, "dup_rcv_byte(dup=%d) - %s, byte %d of %d\n", dup, - (dup_rxcsr[dup] & RXCSR_M_RCVEN) ? "enabled" : "disabled", - dup_rcvpkinoff[dup], dup_rcvpkoffset[dup]); -if (!(dup_rxcsr[dup] & RXCSR_M_RCVEN) || (dup_rcvpkoffset[dup] == 0) || (dup_rxcsr[dup] & RXCSR_M_RXDONE)) - return SCPE_OK; -if (dup_rcv_packet_callback[dup]) { - dup_rcv_packet_callback[dup](dup, dup_rcvpacket[dup], dup_rcvpkoffset[dup]); - return SCPE_OK; - } -dup_rxcsr[dup] |= RXCSR_M_RXACT; -dup_rxdbuf[dup] &= ~RXDBUF_M_RCRCER; -dup_rxdbuf[dup] &= ~RXDBUF_M_RXDBUF; -dup_rxdbuf[dup] |= dup_rcvpacket[dup][dup_rcvpkinoff[dup]++]; -dup_rxcsr[dup] |= RXCSR_M_RXDONE; -if (((dup_rcvpkinoff[dup] == 8) || - (dup_rcvpkinoff[dup] >= dup_rcvpkoffset[dup])) && - (0 == ddcmp_crc16 (0, dup_rcvpacket[dup], dup_rcvpkinoff[dup]))) - dup_rxdbuf[dup] |= RXDBUF_M_RCRCER; +dup_set_modem (dup, (state ? RXCSR_M_DTR : 0) | (dup_rxcsr[dup] & RXCSR_M_RTS)); +if (state) + dup_rxcsr[dup] |= RXCSR_M_DTR; else - dup_rxdbuf[dup] &= ~RXDBUF_M_RCRCER; -if (dup_rcvpkinoff[dup] >= dup_rcvpkoffset[dup]) { - dup_rcvpkinoff[dup] = dup_rcvpkoffset[dup] = 0; - dup_rxcsr[dup] &= ~RXCSR_M_RXACT; - } -if (dup_rxcsr[dup] & RXCSR_M_RXIE) - dup_set_rxint (dup); + dup_rxcsr[dup] &= ~RXCSR_M_DTR; +dup_ldsc[dup].rcve = state; +dup_get_modem (dup); return SCPE_OK; } +t_stat dup_set_RTS (int32 dup, t_bool state) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return SCPE_IERR; +dup_set_modem (dup, (state ? RXCSR_M_RTS : 0) | (dup_rxcsr[dup] & RXCSR_M_DTR)); +if (state) + dup_rxcsr[dup] |= RXCSR_M_RTS; +else + dup_rxcsr[dup] &= ~RXCSR_M_RTS; +dup_get_modem (dup); +return SCPE_OK; +} + +t_stat dup_set_RCVEN (int32 dup, t_bool state) +{ +uint16 orig_val; + +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return SCPE_IERR; +orig_val = dup_rxcsr[dup]; +dup_rxcsr[dup] &= ~RXCSR_M_RCVEN; +dup_rxcsr[dup] |= (state ? RXCSR_M_RCVEN : 0); +if ((dup_rxcsr[dup] & RXCSR_M_RCVEN) && + (!(orig_val & RXCSR_M_RCVEN))) { /* Upward transition of receiver enable */ + UNIT *uptr = dup_units + dup; + + dup_poll_svc (uptr); /* start any pending receive */ + } +return SCPE_OK; +} + +t_stat dup_setup_dup (int32 dup, t_bool enable, t_bool protocol_DDCMP, t_bool crc_inhibit, t_bool halfduplex, uint8 station) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return SCPE_IERR; +if (!enable) { + dup_clear(dup, TRUE); + return SCPE_OK; + } +if (!protocol_DDCMP) { + return SCPE_NOFNC; /* only DDCMP for now */ + } +if (crc_inhibit) { + return SCPE_ARG; /* Must enable CRC for DDCMP */ + } +/* These settings reflect how RSX operates a bare DUP when used for + DECnet communications */ +dup_clear(dup, FALSE); +dup_rxcsr[dup] |= RXCSR_M_STRSYN | RXCSR_M_RCVEN; +dup_parcsr[dup] = PARCSR_M_DECMODE | (DDCMP_SYN << PARCSR_V_ADSYNC); +dup_txcsr[dup] &= TXCSR_M_HALFDUP; +dup_txcsr[dup] |= (halfduplex ? TXCSR_M_HALFDUP : 0); +tmxr_set_line_halfduplex (dup_desc.ldsc+dup, dup_txcsr[dup] & TXCSR_M_HALFDUP); +return dup_set_DTR (dup, TRUE); +} + +t_stat dup_reset_dup (int32 dup) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return SCPE_IERR; +dup_clear(dup, dup_W3[dup]); +return SCPE_OK; +} + +t_stat dup_set_W3_option (int32 dup, t_bool state) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return SCPE_IERR; +dup_W3[dup] = state; +return SCPE_OK; +} + +t_stat dup_set_W5_option (int32 dup, t_bool state) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return SCPE_IERR; +dup_W5[dup] = state; +return SCPE_OK; +} + +t_stat dup_set_W6_option (int32 dup, t_bool state) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return SCPE_IERR; +dup_W6[dup] = state; +return SCPE_OK; +} + + t_bool dup_put_msg_bytes (int32 dup, uint8 *bytes, size_t len, t_bool start, t_bool end) { t_bool breturn = FALSE; -if (!dup_xmtpkrdy[dup]) { /* Not Busy sending? */ - if (start) +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return FALSE; + +if (!tmxr_tpbusyln(&dup_ldsc[dup])) { /* Not Busy sending? */ + if (start) { dup_xmtpkoffset[dup] = 0; + dup_xmtpkdelaying[dup] = 0; + dup_xmtpkstart[dup] = sim_grtime(); + } if (dup_xmtpkoffset[dup] + 2 + len > dup_xmtpksize[dup]) { dup_xmtpksize[dup] += 2 + len; - dup_xmtpacket[dup] = realloc (dup_xmtpacket[dup], dup_xmtpksize[dup]); + dup_xmtpacket[dup] = (uint8 *)realloc (dup_xmtpacket[dup], dup_xmtpksize[dup]); } /* Strip sync bytes at the beginning of a message */ while (len && (dup_xmtpkoffset[dup] == 0) && (bytes[0] == DDCMP_SYN)) { @@ -741,60 +908,122 @@ if (!dup_xmtpkrdy[dup]) { /* Not Busy sending? */ dup_xmtpacket[dup][dup_xmtpkoffset[dup]++] = crc16 & 0xFF; dup_xmtpacket[dup][dup_xmtpkoffset[dup]++] = crc16 >> 8; - dup_xmtpkoutoff[dup] = 0; - dup_xmtpkrdy[dup] = TRUE; + 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]); + } } breturn = TRUE; } sim_debug (DBG_TRC, DUPDPTR, "dup_put_msg_bytes(dup=%d, len=%d, start=%s, end=%s) %s\n", dup, len, start ? "TRUE" : "FALSE", end ? "TRUE" : "FALSE", breturn ? "Good" : "Busy"); +if (breturn && (tmxr_tpbusyln (&dup_ldsc[dup]) || dup_xmtpkbytes[dup])) { + if (dup_xmt_complete_callback[dup]) + dup_svc(dup_units+dup); + } return breturn; } +t_stat dup_get_packet (int32 dup, const uint8 **pbuf, uint16 *psize) +{ +if ((dup < 0) || (dup >= dup_desc.lines) || (DUPDPTR->flags & DEV_DIS)) + return SCPE_IERR; + +if (*pbuf == &dup_rcvpacket[dup][0]) { + *pbuf = NULL; + *psize = 0; + dup_rcvpkinoff[dup] = dup_rcvpkbytes[dup] = 0; + dup_rxcsr[dup] &= ~RXCSR_M_RXACT; + } +if ((dup_rcvpkinoff[dup] == 0) && (dup_rcvpkbytes[dup] != 0)) { + *pbuf = &dup_rcvpacket[dup][0]; + *psize = dup_rcvpkbytes[dup]; + } +sim_debug (DBG_TRC, DUPDPTR, "dup_get_packet(dup=%d, psize=%d)\n", + dup, (int)*psize); +return SCPE_OK; +} + +static t_stat dup_rcv_byte (int32 dup) +{ +sim_debug (DBG_TRC, DUPDPTR, "dup_rcv_byte(dup=%d) - %s, byte %d of %d\n", dup, + (dup_rxcsr[dup] & RXCSR_M_RCVEN) ? "enabled" : "disabled", + dup_rcvpkinoff[dup], dup_rcvpkbytes[dup]); +if (!(dup_rxcsr[dup] & RXCSR_M_RCVEN) || (dup_rcvpkbytes[dup] == 0) || (dup_rxcsr[dup] & RXCSR_M_RXDONE)) + return SCPE_OK; +if (dup_rcv_packet_data_callback[dup]) { + sim_debug (DBG_TRC, DUPDPTR, "dup_rcv_byte(dup=%d, psize=%d) - Invoking Receive Data callback\n", + dup, (int)dup_rcvpkbytes[dup]); + dup_rcv_packet_data_callback[dup](dup, dup_rcvpkbytes[dup]); + return SCPE_OK; + } +dup_rxcsr[dup] |= RXCSR_M_RXACT; +dup_rxdbuf[dup] &= ~RXDBUF_M_RCRCER; +dup_rxdbuf[dup] &= ~RXDBUF_M_RXDBUF; +dup_rxdbuf[dup] |= dup_rcvpacket[dup][dup_rcvpkinoff[dup]++]; +dup_rxcsr[dup] |= RXCSR_M_RXDONE; +if (((dup_rcvpkinoff[dup] == 8) || + (dup_rcvpkinoff[dup] >= dup_rcvpkbytes[dup])) && + (0 == ddcmp_crc16 (0, dup_rcvpacket[dup], dup_rcvpkinoff[dup]))) + dup_rxdbuf[dup] |= RXDBUF_M_RCRCER; +else + dup_rxdbuf[dup] &= ~RXDBUF_M_RCRCER; +if (dup_rcvpkinoff[dup] >= dup_rcvpkbytes[dup]) { + dup_rcvpkinoff[dup] = dup_rcvpkbytes[dup] = 0; + dup_rxcsr[dup] &= ~RXCSR_M_RXACT; + } +if (dup_rxcsr[dup] & RXCSR_M_RXIE) + dup_set_rxint (dup); +return SCPE_OK; +} + /* service routine to delay device activity */ -t_stat dup_svc (UNIT *uptr) + +static t_stat dup_svc (UNIT *uptr) { DEVICE *dptr = DUPDPTR; int32 dup = (int32)(uptr-dptr->units); TMLN *lp = &dup_desc.ldsc[dup]; sim_debug(DBG_TRC, DUPDPTR, "dup_svc(dup=%d)\n", dup); -if (!(dup_txcsr[dup] & TXCSR_M_TXDONE) && (!dup_xmtpkrdy[dup])) { +if (!(dup_txcsr[dup] & TXCSR_M_TXDONE) && (!tmxr_tpbusyln (lp))) { uint8 data = dup_txdbuf[dup] & TXDBUF_M_TXDBUF; dup_put_msg_bytes (dup, &data, (dup_txdbuf[dup] & TXDBUF_M_TEOM) && (dptr == &dup_dev) ? 0 : 1, dup_txdbuf[dup] & TXDBUF_M_TSOM, (dup_txdbuf[dup] & TXDBUF_M_TEOM)); - if (dup_xmtpkrdy[dup]) { /* Packet ready to send? */ + if (tmxr_tpbusyln (lp)) { /* Packet ready to send? */ sim_debug(DBG_TRC, DUPDPTR, "dup_svc(dup=%d) - Packet Done %d bytes\n", dup, dup_xmtpkoffset[dup]); - ddcmp_packet_trace (DBG_PKT, DUPDPTR, ">>> XMT Packet", dup_xmtpacket[dup], dup_xmtpkoffset[dup], TRUE); } } -if (dup_xmtpkrdy[dup] && lp->xmte) { - t_stat st = SCPE_OK; - - while ((st == SCPE_OK) && (dup_xmtpkoutoff[dup] < dup_xmtpkoffset[dup])) { - st = tmxr_putc_ln (lp, dup_xmtpacket[dup][dup_xmtpkoutoff[dup]]); - if (st == SCPE_OK) - ++dup_xmtpkoutoff[dup]; +if ((tmxr_tpbusyln (lp) || dup_xmtpkbytes[dup]) && (lp->xmte || (!lp->conn))) { + int32 start = tmxr_tpbusyln (lp) ? tmxr_tpqln (lp) + tmxr_tqln (lp) : dup_xmtpkbytes[dup]; + int32 remain = tmxr_send_buffered_data (lp);/* send any buffered data */ + if (remain) { + sim_debug(DBG_PKT, DUPDPTR, "dup_svc(dup=%d) - Packet Transmission Stalled with %d bytes remaining\n", dup, remain); } - tmxr_send_buffered_data (lp); /* send any buffered data */ - if (st == SCPE_LOST) { /* line state transition? */ - dup_get_modem (dup); - dup_xmtpkrdy[dup] = FALSE; - dup_xmtpkoffset[dup] = 0; - } - else - if (st == SCPE_OK) { - sim_debug(DBG_PKT, DUPDPTR, "dup_svc(dup=%d) - %d byte packet transmission complete\n", dup, dup_xmtpkoutoff[dup]); - dup_xmtpkrdy[dup] = FALSE; - dup_xmtpkoffset[dup] = 0; + else { + if (!lp->conn) { + if (dup_xmtpkoffset[dup]) { + sim_debug(DBG_PKT, DUPDPTR, "dup_svc(dup=%d) - %d byte packet transmission with link down (dropped)\n", dup, dup_xmtpkoffset[dup]); + } + dup_get_modem (dup); } else { - sim_debug(DBG_PKT, DUPDPTR, "dup_svc(dup=%d) - Packet Transmission Stalled with %d bytes remaining\n", dup, (int)(dup_xmtpkoffset[dup]-dup_xmtpkoutoff[dup])); + sim_debug(DBG_PKT, DUPDPTR, "dup_svc(dup=%d) - %d byte packet transmission complete\n", dup, dup_xmtpkbytes[dup]); + } + dup_xmtpkoffset[dup] = 0; + } + if (!tmxr_tpbusyln (lp)) { /* Done transmitting? */ + if (((start - remain) > 0) && dup_speed[dup] && dup_xmt_complete_callback[dup] && !dup_xmtpkdelaying[dup]) { /* just done, and speed limited using packet interface? */ + dup_xmtpkdelaying[dup] = 1; + sim_activate_notbefore (uptr, dup_xmtpkstart[dup] + (uint32)((tmr_poll*clk_tps)*((double)dup_xmtpkbytes[dup]*8)/dup_speed[dup])); + } + else { + dup_txcsr[dup] &= ~TXCSR_M_TXACT; /* Set idle */ + dup_xmtpkbytes[dup] = 0; + dup_xmtpkdelaying[dup] = 0; + if (dup_xmt_complete_callback[dup]) + dup_xmt_complete_callback[dup](dup, (dup_rxcsr[dup] & RXCSR_M_DCD) ? 0 : 1); } - if (!dup_xmtpkrdy[dup]) { /* Done transmitting? */ - dup_txcsr[dup] &= ~TXCSR_M_TXACT; /* Set idle */ - if (dup_xmt_complete_callback[dup]) - dup_xmt_complete_callback[dup](dup, (dup_rxcsr[dup] & RXCSR_M_DCD) ? 0 : 1); } } if (dup_rxcsr[dup] & RXCSR_M_RXACT) @@ -802,19 +1031,13 @@ if (dup_rxcsr[dup] & RXCSR_M_RXACT) return SCPE_OK; } -t_stat dup_poll_svc (UNIT *uptr) +static t_stat dup_poll_svc (UNIT *uptr) { -int32 dup, active, attached, c; +int32 dup, active, attached; sim_debug(DBG_TRC, DUPDPTR, "dup_poll_svc()\n"); -dup = tmxr_poll_conn(&dup_desc); -if (dup >= 0) { /* new connection? */ - dup_rxcsr[dup] |= RXCSR_M_RING | ((dup_rxcsr[dup] & RXCSR_M_DTR) ? (RXCSR_M_DCD | RXCSR_M_CTS | RXCSR_M_DSR) : 0); - dup_rxcsr[dup] |= RXCSR_M_DSCHNG; - if (dup_rxcsr[dup] & RXCSR_M_DSCIE) - dup_set_rxint (dup); /* Interrupt */ - } +tmxr_poll_conn(&dup_desc); tmxr_poll_rx (&dup_desc); tmxr_poll_tx (&dup_desc); for (dup=active=attached=0; dup < dup_desc.lines; dup++) { @@ -825,56 +1048,32 @@ for (dup=active=attached=0; dup < dup_desc.lines; dup++) { if (dup_ldsc[dup].conn) ++active; dup_get_modem (dup); - if (lp->xmte && dup_xmtpkrdy[dup]) { - sim_debug(DBG_PKT, DUPDPTR, "dup_poll_svc(dup=%d) - Packet Transmission of remaining %d bytes restarting...\n", dup, (int)(dup_xmtpkoffset[dup]-dup_xmtpkoutoff[dup])); + if (lp->xmte && tmxr_tpbusyln(lp)) { + sim_debug(DBG_PKT, DUPDPTR, "dup_poll_svc(dup=%d) - Packet Transmission of remaining %d bytes restarting...\n", dup, tmxr_tpqln (lp)); dup_svc (&dup_units[dup]); /* Flush pending output */ } if (!(dup_rxcsr[dup] & RXCSR_M_RXACT)) { - while (TMXR_VALID & (c = tmxr_getc_ln (lp))) { - if (dup_rcvpkoffset[dup] + 1 > dup_rcvpksize[dup]) { - dup_rcvpksize[dup] += 512; - dup_rcvpacket[dup] = realloc (dup_rcvpacket[dup], dup_rcvpksize[dup]); - } - dup_rcvpacket[dup][dup_rcvpkoffset[dup]] = c; - dup_rcvpkoffset[dup] += 1; - if (dup_rcvpkoffset[dup] == 1) { /* Validate first byte in packet */ - if ((dup_rxcsr[dup] & RXCSR_M_STRSYN) && - (dup_rcvpacket[dup][0] == (dup_parcsr[dup] & PARCSR_M_ADSYNC))) { - dup_rcvpkoffset[dup] = 0; - continue; - } - if (dup_parcsr[dup] & PARCSR_M_DECMODE) { - switch (dup_rcvpacket[dup][0]) { - default: - sim_debug (DBG_PKT, DUPDPTR, "Ignoring unexpected byte 0%o in DDCMP mode\n", dup_rcvpacket[dup][0]); - dup_rcvpkoffset[dup] = 0; - case DDCMP_SOH: - case DDCMP_ENQ: - case DDCMP_DLE: - continue; - } - } - } - if (dup_rcvpkoffset[dup] >= 8) { - if (dup_rcvpacket[dup][0] == DDCMP_ENQ) { /* Control Message? */ - ddcmp_packet_trace (DBG_PKT, DUPDPTR, "<<< RCV Packet", dup_rcvpacket[dup], dup_rcvpkoffset[dup], TRUE); - dup_rcvpkinoff[dup] = 0; - dup_rxcsr[dup] |= RXCSR_M_RXACT; - dup_rcv_byte (dup); - break; - } - else { - int32 count = ((dup_rcvpacket[dup][2] & 0x3F) << 8)| dup_rcvpacket[dup][1]; + const uint8 *buf; + uint16 size; - if (dup_rcvpkoffset[dup] >= 10 + count) { - ddcmp_packet_trace (DBG_PKT, DUPDPTR, "<<< RCV Packet", dup_rcvpacket[dup], dup_rcvpkoffset[dup], TRUE); - dup_rcvpkinoff[dup] = 0; - dup_rxcsr[dup] |= RXCSR_M_RXACT; - dup_rcv_byte (dup); - break; - } - } + if (dup_parcsr[dup] & PARCSR_M_DECMODE) + ddcmp_tmxr_get_packet_ln (lp, &buf, &size); + else { + size_t size_t_size; + + tmxr_get_packet_ln (lp, &buf, &size_t_size); + size = (uint16)size_t_size; + } + if (buf) { + if (dup_rcvpksize[dup] < size) { + dup_rcvpksize[dup] = size; + dup_rcvpacket[dup] = (uint8 *)realloc (dup_rcvpacket[dup], dup_rcvpksize[dup]); } + memcpy (dup_rcvpacket[dup], buf, size); + dup_rcvpkbytes[dup] = size; + dup_rcvpkinoff[dup] = 0; + dup_rxcsr[dup] |= RXCSR_M_RXACT; + dup_rcv_byte (dup); } } } @@ -898,7 +1097,7 @@ return SCPE_OK; /* Interrupt routines */ -void dup_clr_rxint (int32 dup) +static void dup_clr_rxint (int32 dup) { dup_rxi = dup_rxi & ~(1 << dup); /* clr mux rcv int */ if (dup_rxi == 0) /* all clr? */ @@ -907,7 +1106,7 @@ else SET_INT (DUPRX); /* no, set intr */ return; } -void dup_set_rxint (int32 dup) +static void dup_set_rxint (int32 dup) { dup_rxi = dup_rxi | (1 << dup); /* set mux rcv int */ SET_INT (DUPRX); /* set master intr */ @@ -915,7 +1114,7 @@ sim_debug(DBG_INT, DUPDPTR, "dup_set_rxint(dup=%d)\n", dup); return; } -int32 dup_rxinta (void) +static int32 dup_rxinta (void) { int32 dup; @@ -929,7 +1128,7 @@ for (dup = 0; dup < dup_desc.lines; dup++) { /* find 1st mux */ return 0; } -void dup_clr_txint (int32 dup) +static void dup_clr_txint (int32 dup) { dup_txi = dup_txi & ~(1 << dup); /* clr mux xmt int */ if (dup_txi == 0) /* all clr? */ @@ -938,7 +1137,7 @@ else SET_INT (DUPTX); /* no, set intr */ return; } -void dup_set_txint (int32 dup) +static void dup_set_txint (int32 dup) { dup_txi = dup_txi | (1 << dup); /* set mux xmt int */ SET_INT (DUPTX); /* set master intr */ @@ -946,7 +1145,7 @@ sim_debug(DBG_INT, DUPDPTR, "dup_set_txint(dup=%d)\n", dup); return; } -int32 dup_txinta (void) +static int32 dup_txinta (void) { int32 dup; @@ -962,7 +1161,7 @@ return 0; /* Device reset */ -t_stat dup_clear (int32 dup, t_bool flag) +static t_stat dup_clear (int32 dup, t_bool flag) { sim_debug(DBG_TRC, DUPDPTR, "dup_clear(dup=%d,flag=%d)\n", dup, flag); @@ -971,10 +1170,12 @@ dup_txdbuf[dup] = 0; dup_parcsr[dup] = 0; /* no params */ dup_txcsr[dup] = TXCSR_M_TXDONE; /* clear CSR */ dup_wait[dup] = DUP_WAIT; /* initial/default byte delay */ -if (flag) /* INIT? clr all */ +if (flag) { /* INIT? clr all */ dup_rxcsr[dup] = 0; + dup_set_modem (dup, dup_rxcsr[dup]); /* push change out to line */ + } else - dup_rxcsr[dup] &= ~(RXCSR_M_DTR|RXCSR_M_RTS); /* else save dtr */ + dup_rxcsr[dup] &= ~(RXCSR_M_DTR|RXCSR_M_RTS); /* else save dtr & rts */ dup_clr_rxint (dup); /* clear int */ dup_clr_txint (dup); if (!dup_ldsc[dup].conn) /* set xmt enb */ @@ -983,7 +1184,7 @@ dup_ldsc[dup].rcve = 0; /* clr rcv enb */ return SCPE_OK; } -t_stat dup_reset (DEVICE *dptr) +static t_stat dup_reset (DEVICE *dptr) { int32 i, ndev; @@ -1008,10 +1209,16 @@ if ((!UNIBUS) && (dptr == &dup_dev)) { } if (dup_ldsc == NULL) { /* First time startup */ - dup_desc.ldsc = dup_ldsc = calloc (dup_desc.lines, sizeof(*dup_ldsc)); + dup_desc.ldsc = dup_ldsc = (TMLN *)calloc (dup_desc.lines, sizeof(*dup_ldsc)); for (i = 0; i < dup_desc.lines; i++) /* init each line */ dup_units[i] = dup_unit_template; dup_units[dup_desc.lines] = dup_poll_unit_template; + /* Initialize to standard factory Option Jumper Settings */ + for (i = 0; i < DUP_LINES; i++) { + dup_W3[i] = TRUE; + dup_W5[i] = FALSE; + dup_W6[i] = TRUE; + } } for (i = 0; i < dup_desc.lines; i++) /* init each line */ dup_clear (i, TRUE); @@ -1029,7 +1236,7 @@ if (ndev) return auto_config (dptr->name, ndev); /* auto config */ } -t_stat dup_attach (UNIT *uptr, char *cptr) +static t_stat dup_attach (UNIT *uptr, char *cptr) { t_stat r; DEVICE *dptr = DUPDPTR; @@ -1038,6 +1245,8 @@ char attach_string[512]; if (!cptr || !*cptr) return SCPE_ARG; +if (!(uptr->flags & UNIT_ATTABLE)) + return SCPE_NOATT; sprintf (attach_string, "Line=%d,Buffered=16384,%s", dup, cptr); r = tmxr_open_master (&dup_desc, attach_string); /* open master socket */ free (uptr->filename); @@ -1045,37 +1254,42 @@ uptr->filename = tmxr_line_attach_string(&dup_desc.ldsc[dup]); if (r != SCPE_OK) /* error? */ return r; uptr->flags |= UNIT_ATT; -sim_activate_after (dup_units+dup_desc.lines, 2000000); /* start poll */ +sim_activate_after (dup_units+dup_desc.lines, DUP_CONNECT_POLL*1000000);/* start poll */ return r; } -t_stat dup_detach (UNIT *uptr) +static t_stat dup_detach (UNIT *uptr) { DEVICE *dptr = DUPDPTR; int32 dup = (int32)(uptr-dptr->units); TMLN *lp = &dup_ldsc[dup]; +int32 i, attached; if (!(uptr->flags & UNIT_ATT)) /* attached? */ return SCPE_OK; +sim_cancel (uptr); uptr->flags &= ~UNIT_ATT; +for (i=attached=0; ifilename); uptr->filename = NULL; free (dup_rcvpacket[dup]); dup_rcvpacket[dup] = NULL; dup_rcvpksize[dup] = 0; -dup_rcvpkoffset[dup] = 0; +dup_rcvpkbytes[dup] = 0; free (dup_xmtpacket[dup]); dup_xmtpacket[dup] = NULL; dup_xmtpksize[dup] = 0; dup_xmtpkoffset[dup] = 0; -dup_xmtpkrdy[dup] = FALSE; -dup_xmtpkoutoff[dup] = 0; return tmxr_detach_ln (lp); } /* SET/SHOW SPEED processor */ -t_stat dup_showspeed (FILE* st, UNIT* uptr, int32 val, void* desc) +static t_stat dup_showspeed (FILE* st, UNIT* uptr, int32 val, void* desc) { DEVICE *dptr = DUPDPTR; int32 dup = (int32)(uptr-dptr->units); @@ -1087,7 +1301,7 @@ else return SCPE_OK; } -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) { DEVICE *dptr = DUPDPTR; int32 dup = (int32)(uptr-dptr->units); @@ -1103,9 +1317,78 @@ dup_speed[dup] = newspeed; return SCPE_OK; } +/* SET/SHOW W3 processor */ + +static t_stat dup_show_W3 (FILE* st, UNIT* uptr, int32 val, void* desc) +{ +DEVICE *dptr = DUPDPTR; +int32 dup = (int32)(uptr-dptr->units); + +if (dup_W3[dup]) + fprintf(st, "W3 Jumper Installed"); +else + fprintf(st, "W3 Jumper Removed"); +return SCPE_OK; +} + +static t_stat dup_set_W3 (UNIT* uptr, int32 val, char* cptr, void* desc) +{ +DEVICE *dptr = DUPDPTR; +int32 dup = (int32)(uptr-dptr->units); + +dup_W3[dup] = val; +return SCPE_OK; +} + +/* SET/SHOW W5 processor */ + +static t_stat dup_show_W5 (FILE* st, UNIT* uptr, int32 val, void* desc) +{ +DEVICE *dptr = DUPDPTR; +int32 dup = (int32)(uptr-dptr->units); + +if (dup_W5[dup]) + fprintf(st, "W5 Jumper Installed"); +else + fprintf(st, "W5 Jumper Removed"); +return SCPE_OK; +} + +static t_stat dup_set_W5 (UNIT* uptr, int32 val, char* cptr, void* desc) +{ +DEVICE *dptr = DUPDPTR; +int32 dup = (int32)(uptr-dptr->units); + +dup_W5[dup] = val; +return SCPE_OK; +} + +/* SET/SHOW W6 processor */ + +static t_stat dup_show_W6 (FILE* st, UNIT* uptr, int32 val, void* desc) +{ +DEVICE *dptr = DUPDPTR; +int32 dup = (int32)(uptr-dptr->units); + +if (dup_W6[dup]) + fprintf(st, "W6 Jumper Installed"); +else + fprintf(st, "W6 Jumper Removed"); +return SCPE_OK; +} + +static t_stat dup_set_W6 (UNIT* uptr, int32 val, char* cptr, void* desc) +{ +DEVICE *dptr = DUPDPTR; +int32 dup = (int32)(uptr-dptr->units); + +dup_W6[dup] = val; +return SCPE_OK; +} + /* SET LINES processor */ -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) { int32 newln, l; uint32 i; @@ -1124,7 +1407,7 @@ if (newln == 0) return SCPE_ARG; sim_cancel (dup_units + dup_desc.lines); dup_dib.lnt = newln * IOLN_DUP; /* set length */ -dup_desc.ldsc = dup_ldsc = realloc(dup_ldsc, newln*sizeof(*dup_ldsc)); +dup_desc.ldsc = dup_ldsc = (TMLN *)realloc(dup_ldsc, newln*sizeof(*dup_ldsc)); for (l=dup_desc.lines; l < newln; l++) { memset (&dup_ldsc[l], 0, sizeof(*dup_ldsc)); dup_units[l] = dup_unit_template; @@ -1135,7 +1418,7 @@ dptr->numunits = newln + 1; return dup_reset (dptr); /* setup lines and auto config */ } -t_stat dup_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr) +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); @@ -1155,22 +1438,23 @@ fprint_reg_help (st, dptr); return SCPE_OK; } -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"); fprintf (st, "connected to a user-specified port. The ATTACH command specifies the\n"); fprintf (st, "port to be used as well as the peer address:\n\n"); -fprintf (st, " sim> ATTACH %sn {interface:}port,Connect=peerhost:port\n\n", dptr->name); +fprintf (st, " 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; } -char *dup_description (DEVICE *dptr) +static char *dup_description (DEVICE *dptr) { return (UNIBUS) ? "DUP11 bit synchronous interface" : "DPV11 bit synchronous interface"; diff --git a/PDP11/pdp11_dup.h b/PDP11/pdp11_dup.h index 4e49d603..9dcdc489 100644 --- a/PDP11/pdp11_dup.h +++ b/PDP11/pdp11_dup.h @@ -34,17 +34,30 @@ #ifndef PDP11_DUP_H_ #define PDP11_DUP_H_ 0 -typedef void (*PACKET_RECEIVE_CALLBACK)(int32 dup, uint8 *buf, size_t len); +typedef void (*PACKET_DATA_AVAILABLE_CALLBACK)(int32 dup, int len); typedef void (*PACKET_TRANSMIT_COMPLETE_CALLBACK)(int32 dup, int status); +typedef void (*MODEM_CHANGE_CALLBACK)(int32 dup); -int32 dup_get_line_speed (int32 dup); +int32 dup_get_DSR (int32 dup); int32 dup_get_DCD (int32 dup); +int32 dup_get_CTS (int32 dup); +int32 dup_get_RING (int32 dup); +int32 dup_get_RCVEN (int32 dup); t_stat dup_set_DTR (int32 dup, t_bool state); -t_stat dup_set_DDCMP (int32 dup, t_bool state); +t_stat dup_set_RTS (int32 dup, t_bool state); +t_stat dup_set_W3_option (int32 dup, t_bool state); +t_stat dup_set_W5_option (int32 dup, t_bool state); +t_stat dup_set_W6_option (int32 dup, t_bool state); +t_stat dup_set_RCVEN (int32 dup, t_bool state); +t_stat dup_setup_dup (int32 dup, t_bool enable, t_bool protocol_DDCMP, t_bool crc_inhibit, t_bool halfduplex, uint8 station); +t_stat dup_reset_dup (int32 dup); + int32 dup_csr_to_linenum (int32 CSRPA); -void dup_set_callback_mode (int32 dup, PACKET_RECEIVE_CALLBACK receive, PACKET_TRANSMIT_COMPLETE_CALLBACK transmit); +void dup_set_callback_mode (int32 dup, PACKET_DATA_AVAILABLE_CALLBACK receive, PACKET_TRANSMIT_COMPLETE_CALLBACK transmit, MODEM_CHANGE_CALLBACK modem); t_bool dup_put_msg_bytes (int32 dup, uint8 *bytes, size_t len, t_bool start, t_bool end); -#endif /* PDP11_DUP_H_ */ \ No newline at end of file +t_stat dup_get_packet (int32 dup, const uint8 **pbuf, uint16 *psize); + +#endif /* PDP11_DUP_H_ */ diff --git a/PDP11/pdp11_kmc.c b/PDP11/pdp11_kmc.c new file mode 100644 index 00000000..d62db266 --- /dev/null +++ b/PDP11/pdp11_kmc.c @@ -0,0 +1,3225 @@ +/* pdp11_kmc.c: KMC11-A with COMM IOP-DUP microcode Emulation + ------------------------------------------------------------------------------ + + + Initial implementation 2002 by Johnny Eriksson + + Adapted to SIMH 3.? by Robert M. A. Jarratt in 2013 + + Completely rewritten by Timothe Litt for SimH 4.0 in 2013. + Implements all DDCMP functions, although the DUP11 doesn't currently support half-duplex. + + DUP separated into separate devices, the DUPs were created by Mark Pizzolato. + + This code is Copyright 2002, 2013 by the authors,all rights reserved. + It is licensed under the standard SimH license. + ------------------------------------------------------------------------------ + + Modification history: + + 05-Jun-13 TL Massive rewrite to split KMC/DUP, add missing functions, and + restructure so TOPS-10/20 & RSX are happy. Support multiple + KMCs. + 14-Apr-13 RJ Took original sources into latest source code. + 15-Feb-02 JE Massive changes/cleanups. + 23-Jan-02 JE Modify for version 2.9. + 17-Jan-02 JE First attempt. +------------------------------------------------------------------------------*/ +/* + * Loose ends, known problems etc: + * + * This implementation should do both full and half duplex DDCMP, but half duplex needs to be tested. + * + * DUP: Unexplained code to generate SYNC and RCVEN based on RTS transitions + * prevents RTS from working; using hacked version. + * Select final speed limit. + */ + +#if defined (VM_PDP10) /* PDP10 version */ +#include "pdp10_defs.h" + +#elif defined (VM_VAX) /* VAX version */ +#include "vax_defs.h" + +#else /* PDP-11 version */ +#include "pdp11_defs.h" +#endif + +#define KMC_RDX 8 + +#include +#include "pdp11_dup.h" +#include "pdp11_ddcmp.h" + +#define DIM(x) (sizeof(x)/sizeof((x)[0])) +#define UNUSED_ARG(x) (void)(x) + +/* Configuration macros + */ + +/* From host VM: DUP_LINES is the maximum number of DUP lines on the Unibus. + * The KMC may not control all of them, but the DUP API also uses the index in + * IO space to identify the line. Default to max KDP supported. + * + * Note: It is perfectly reasonable to have MANY more DUP_LINES than a single KMC + * can support. A configuration can have multiple KMCs, or DUPs that are controlled + * directly by the OS. DUP_LINES allocates space for the KMC to keep track of lines + * that some KMC instance can POTENTIALLY control. + */ +#ifndef DUP_LINES +#define DUP_LINES (MAX_LINE +1) +#endif + +/* Number of KMC devices possible. */ +#ifndef KMC_UNITS +#define KMC_UNITS 1 +#endif + +/* Number of KMC devices initially enabled. */ +#ifndef INITIAL_KMCS +#define INITIAL_KMCS 1 +#endif + +#if INITIAL_KMCS > KMC_UNITS +#undef INITIAL_KMCS +#define INITIAL_KMCS KMC_UNITS +#endif + +#if INITIAL_KMCS == 0 +#undef INITIAL_KMCS +#define INITIAL_KMCS 1 +#define KMC_DIS DEV_DIS +#else +#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 +#define DUP_RXRESYNC 1 + */ + +/* End of configuration */ + +/* Architectural structures and macros */ + +/* Maximum line speed + * KDP limit was about 19,200 BPS. + * Used to pace BD consumption. If too fast, too many messages can + * be outstanding and the OS will reject ACKs. *TODO* Pick a final limit. + */ +#define MAX_SPEED (1*1000*1000) + +#define DFLT_SPEED (19200) + + +/* Transmission (or reception) time of a buffer of n + * characters at speed bits/sec. 8 bit chars (sync line) + * result in microseconds. + */ +#define XTIME(n,speed) (((n)*8*1000*1000)/(speed)) + +/* Queue elements + * A queue is a double-linked list of element headers. + * The head of the queue is an element without a body. + * A queue is empty when the only element in the queue is + * the head. + * Each queue has an asociated count of the elements in the + * queue; this simplifies knowing its state. + * Each queue also has a maximum length. + * + * Queues are manipulated with initqueue, insqueue, and remqueue. + */ +struct queuehdr { + struct queuehdr *next; + struct queuehdr *prev; +}; +typedef struct queuehdr QH; + +/* bits, SEL0 */ + +#define SEL0_RUN 0100000 /* Run bit. */ +#define SEL0_MRC 0040000 /* Master clear. */ +#define SEL0_CWR 0020000 /* CRAM write. */ +#define SEL0_SLU 0010000 /* Step Line Unit. */ +#define SEL0_LUL 0004000 /* Line Unit Loop. */ +#define SEL0_RMO 0002000 /* ROM output. */ +#define SEL0_RMI 0001000 /* ROM input. */ +#define SEL0_SUP 0000400 /* Step microprocessor. */ +#define SEL0_RQI 0000200 /* Request input. */ +#define SEL0_IEO 0000020 /* Interrupt enable output. */ +#define SEL0_IEI 0000001 /* Interrupt enable input. */ + +/* bits, SEL2 */ + +#define SEL2_OVR 0100000 /* Completion queue overrun. */ +#define SEL2_V_LINE 8 /* Line number assigned by host */ +#define SEL2_LINE (0177 << SEL2_V_LINE) +#define MAX_LINE 017 /* Maximum line number allowed in BASE_IN */ +#define MAX_ACTIVE (MAX_LINE+1) +#define SEL2_RDO 0000200 /* Ready for output transaction. */ +#define SEL2_RDI 0000020 /* Ready for input transaction. */ +#define SEL2_IOT 0000004 /* I/O type, 1 = rx, 0 = tx. */ +#define SEL2_V_CMD 0 /* Command code */ +#define SEL2_CMD 0000003 /* Command code + * IN are commands TO the KMC + * OUT are command completions FROM the KMC. + */ +# define CMD_BUFFIN 0 /* BUFFER IN */ +# define CMD_CTRLIN 1 /* CONTROL IN */ +# define CMD_BASEIN 3 /* BASE IN */ +# define CMD_BUFFOUT 0 /* BUFFER OUT */ +# define CMD_CTRLOUT 1 /* CONTROL OUT */ + +#define SEL2_II_RESERVED (SEL2_OVR | 0354) /* Reserved: 15, 7:5, 3:2 */ +/* bits, SEL4 */ + +#define SEL4_CI_POLL 0377 /* DUP polling interval, 50 usec units */ + +#define SEL4_ADDR 0177777 /* Generic: Unibus address <15:0> */ + +/* bits, SEL6 */ + +#define SEL6_V_CO_XAD 14 /* Unibus extended address bits */ +#define SEL6_CO_XAD (3u << SEL6_V_CO_XAD) + +/* BASE IN */ +#define SEL6_II_DUPCSR 0017770 /* BASE IN: DUP CSR <12:3> */ + +/* BUFFER IN */ +#define SEL6_BI_ENABLE 0020000 /* BUFFER IN: Assign after KILL */ +#define SEL6_BI_KILL 0010000 /* BUFFER IN: Return all buffers */ + +/* BUFFER OUT */ +#define SEL6_BO_EOM 0010000 /* BUFFER OUT: End of message */ + +/* CONTROL OUT event codes */ +#define SEL6_CO_ABORT 006 /* Bit stuffing rx abort */ +#define SEL6_CO_HCRC 010 /* DDCMP Header CRC error */ +#define SEL6_CO_DCRC 012 /* DDCMP Data CRC/ BS frame CRC */ +#define SEL6_CO_NOBUF 014 /* No RX buffer available */ +#define SEL6_CO_DSRCHG 016 /* DSR changed (Initially OFF) */ +#define SEL6_CO_NXM 020 /* NXM */ +#define SEL6_CO_TXU 022 /* Transmitter underrun */ +#define SEL6_CO_RXO 024 /* Receiver overrun */ +#define SEL6_CO_KDONE 026 /* Kill complete */ + +/* CONTROL IN modifiers */ +#define SEL6_CI_V_DDCMP 15 /* Run DDCMP vs. bit-stuffing */ +#define SEL6_CI_DDCMP (1u << SEL6_CI_V_DDCMP) +#define SEL6_CI_V_HDX 13 /* Half-duplex */ +#define SEL6_CI_HDX (1u << SEL6_CI_V_HDX) +#define SEL6_CI_V_ENASS 12 /* Enable secondary station address filter */ +#define SEL6_CI_ENASS (1u << SEL6_CI_V_ENASS) +#define SEL6_CI_V_NOCRC 9 +#define SEL6_CI_NOCRC (1u << SEL6_CI_V_NOCRC) +#define SEL6_CI_V_ENABLE 8 +#define SEL6_CI_ENABLE (1u << SEL6_CI_V_ENABLE) +#define SEL6_CI_SADDR 0377 + +/* Buffer descriptor list bits */ + +#define BDL_LDS 0100000 /* Last descriptor in list. */ +#define BDL_RSY 0010000 /* Resync transmitter. */ +#define BDL_XAD 0006000 /* Buffer address bits 17 & 16. */ +#define BDL_S_XAD (16-10) /* Shift to position XAX in address */ +#define BDL_EOM 0001000 /* End of message. */ +#define BDL_SOM 0000400 /* Start of message. */ + +#define KMC_CRAMSIZE 1024 /* Size of CRAM (microcode control RAM). */ +#define KMC_DRAMSIZE 1024 +#define KMC_CYCLETIME 300 /* Microinstruction cycle time, nsec */ + +#define MAXQUEUE 2 /* Number of bdls that can be queued for tx and rx + * per line. (KDP ucode limits to 2) + */ + +struct buffer_list { /* BDL queue elements */ + QH hdr; + uint32 ba; +}; +typedef struct buffer_list BDL; + + struct workblock { + t_bool first; + uint32 bda; + uint16 bd[3]; + uint16 rcvc; + uint32 ba; + }; + typedef struct workblock WB; + +/* Each DUP in the system can potentially be assigned to a KMC. + * Since the total number of DUPs is relatively small, and in + * most configurations all DUPs will be assigned, a dup structure + * is allocated for all possible DUPs. This structure is common + * to ALL KMCs; a given DUP is assigned to at most one. + */ + +struct dupstate { + int32 kmc; /* Controlling KMC */ + int32 line; /* OS-assigned line number */ + int32 dupidx; /* DUP API Number amongst all DUP11's on Unibus (-1 == unassigned) */ + int32 linkstate; /* Line Link Status (i.e. 1 when DCD/DSR is on, 0 otherwise */ + #define LINK_DSR 1 + #define LINK_SEL 2 + uint16 ctrlFlags; + uint32 dupcsr; + uint32 linespeed; /* Effective line speed (bps) */ + BDL bdq[MAXQUEUE*2]; /* Queued TX and RX buffer lists */ + QH bdqh; /* Free queue */ + int32 bdavail; + + QH rxqh; /* Receive queue from host */ + int32 rxavail; + WB rx; + uint32 rxstate; +/* States. Note that these are ordered; there are < comparisions */ +#define RXIDLE 0 +#define RXBDL 1 +#define RXBUF 2 +#define RXDAT 3 +#define RXLAST 4 +#define RXFULL 5 +#define RXNOBUF 6 + + uint8 *rxmsg; + uint16 rxmlen; + uint16 rxdlen; + uint16 rxused; + + QH txqh; /* Transmit queue from host */ + int32 txavail; + WB tx; + uint32 txstate; +/* States. Note that these are ordered; there are < comparisions */ +#define TXIDLE 0 +#define TXDONE 1 +#define TXRTS 2 +#define TXSOM 3 +#define TXHDR 4 +#define TXHDRX 5 +#define TXDATA 6 +#define TXDATAX 7 +#define TXMRDY 8 +#define TXRDY 9 +#define TXACT 10 +#define TXKILL 11 +#define TXKILR 12 + + uint8 *txmsg; + size_t txmsize, txslen, txmlen; + }; + +typedef struct dupstate dupstate; + +/* State for every DUP that MIGHT be controlled. + * A DUP can be controlled by at most one KMC. + */ +static dupstate dupState[DUP_LINES] = {{ 0 }}; + +/* Flags defining sim_debug conditions. */ + +#define DF_CMD 00001 /* Trace commands. */ +#define DF_BFO 00002 /* Trace buffers out */ +#define DF_CTO 00004 /* Trace control out */ +#define DF_QUE 00010 /* Trace internal queues */ +#define DF_RGR 00020 /* Register reads */ +#define DF_RGW 00040 /* Register writes. */ +#define DF_INF 00100 /* Info */ +#define DF_ERR 00200 /* Error halts */ +#define DF_PKT 00400 /* Errors in packet (use DUP pkt trace for pkt data */ +#define DF_INT 01000 /* Interrupt delivery */ +#define DF_BUF 02000 /* Buffer service */ + +static DEBTAB kmc_debug[] = { + {"CMD", DF_CMD}, + {"BFO", DF_BFO}, + {"CTO", DF_CTO}, + {"QUE", DF_QUE}, + {"RGR", DF_RGR}, + {"RGW", DF_RGW}, + {"INF", DF_INF}, + {"ERR", DF_ERR}, + {"PKT", DF_PKT}, + {"BUF", DF_BUF}, + {"INT", DF_INT}, + {0} +}; + +/* These count the total pending interrupts for each vector + * across all KMCs. + */ + +static int32 AintPending = 0; +static int32 BintPending = 0; + +/* Per-KMC state */ + +/* To help make the code more readable, by convention the symbol 'k' + * is the number of the KMC that is the target of the current operation. + * The global state variables below have a #define of the short form + * of each name. Thus, instead of kmc_upc[kmcnum][j], write upc[j]. + * For this to work, k, a uint32 must be in scope and valid. + * + * k can be found in several ways: + * k is the offset into any of the tables. + * Given a UNIT pointer k = txup->unit_kmc. + * The KMC assigned to control a DUP is stored it its dupstate. + * k = dupState[dupno]->kmc; (-1 if not controlled by any KMC) + * The DUP associated with a line is stored in line2dup. + * k = line2dup[line]->kmc + * From the DEVICE pointer, dptr->units lists all the UNITs, and the + * number of units is dptr->numunits. + * From a CSR address: + * k = (PA - dib.ba) / IOLN_KMC + * + * Note that the UNIT arrays are indexed [line][kmc], so pointer + * math is not the best way to find k. (TX line 0 is the public + * UNIT for each KMC.) + * + */ + +/* Emulator error halt codes + * These are mostl error conditions that produce undefined + * results in the hardware. To help with debugging, unique + * codes are provided here. + */ + +#define HALT_STOP 0 /* Run bit cleared */ +#define HALT_MRC 1 /* Master clear */ +#define HALT_BADRES 2 /* Resume without initialization */ +#define HALT_LINE 3 /* Line number out of range */ +#define HALT_BADCMD 4 /* Undefined command received */ +#define HALT_BADCSR 5 /* BASE IN had non-zero MBZ */ +#define HALT_RCVOVF 6 /* Too many receive buffers assigned */ +#define HALT_MTRCV 7 /* Receive buffer descriptor has zero size */ +#define HALT_XMTOVF 8 /* Too many transmit buffers assigned */ +#define HALT_XSOM 9 /* Transmission didn't start with SOM */ +#define HALT_XSOM2 10 /* Data buffer didn't start with SOM */ +#define HALT_BADUC 11 /* No or unrecognized microcode loaded */ + +/* KMC event notifications are funneled through the small number of CSRs. + * Since the CSRs may not be available when an event happens, events are + * queued in these structures. An event is represented by the values to + * be exposed in BSEL2, BSEL4, and BSEL6. + * + * Queue overflow is signalled by setting the overflow bit in the entry + * at the tail of the completion queue at the time a new entry fails to + * be inserted. Note that the line number in that entry may not be + * the line whose event was lost. Effectively, that makes this a fatal + * error. + * + * The KMC microcode uses a queue depth of 29. + */ + +#define CQUEUE_MAX (29) + +struct cqueue { + QH hdr; + uint16 bsel2, bsel4, bsel6; +}; +typedef struct cqueue CQ; + +/* CSRs. These are known as SELn as words and BSELn as bytes */ +static uint16 kmc_sel0[KMC_UNITS]; /* CSR0 - BSEL 1,0 */ +#define sel0 kmc_sel0[k] +static uint16 kmc_sel2[KMC_UNITS]; /* CSR2 - BSEL 3,2 */ +#define sel2 kmc_sel2[k] +static uint16 kmc_sel4[KMC_UNITS]; /* CSR4 - BSEL 5,4 */ +#define sel4 kmc_sel4[k] +static uint16 kmc_sel6[KMC_UNITS]; /* CSR6 - BSEL 7,6 */ +#define sel6 kmc_sel6[k] + +/* Microprocessor state - subset exposed to the host */ +static uint16 kmc_upc[KMC_UNITS]; /* Micro PC */ +#define upc kmc_upc[k] +static uint16 kmc_mar[KMC_UNITS]; /* Micro Memory Address Register */ +#define mar kmc_mar[k] +static uint16 kmc_mna[KMC_UNITS]; /* Maintenance Address Register */ +#define mna kmc_mna[k] +static uint16 kmc_mni[KMC_UNITS]; /* Maintenance Instruction Register */ +#define mni kmc_mni[k] +static uint16 kmc_ucode[KMC_UNITS][KMC_CRAMSIZE]; +#define ucode kmc_ucode[k] +static uint16 kmc_dram[KMC_UNITS][KMC_DRAMSIZE]; +#define dram kmc_dram[k] + +static dupstate *kmc_line2dup[KMC_UNITS][MAX_ACTIVE]; +#define line2dup kmc_line2dup[k] + +/* General state booleans */ +static int kmc_gflags[KMC_UNITS]; /* Miscellaneous gflags */ +#define gflags kmc_gflags[k] +# define FLG_INIT 000001 /* Master clear has been done once. + * Data structures trustworthy. + */ +# define FLG_AINT 000002 /* Pending KMC "A" (INPUT) interrupt */ +# define FLG_BINT 000004 /* Pending KMC "B" (OUTPUT) interrupt */ +# define FLG_UCINI 000010 /* Ucode initialized, ucode structures and ints OK */ + +/* Completion queue elements, header and freelist */ +static CQ kmc_cqueue[KMC_UNITS][CQUEUE_MAX]; +#define cqueue kmc_cqueue[k] + +static QH kmc_cqueueHead[KMC_UNITS]; +#define cqueueHead kmc_cqueueHead[k] +static int32 kmc_cqueueCount[KMC_UNITS]; +#define cqueueCount kmc_cqueueCount[k] + +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 *** */ + +/* Forward declarations: simulator interface */ + + +static t_stat kmc_reset(DEVICE * dptr); +static t_stat kmc_readCsr(int32* data, int32 PA, int32 access); +static t_stat kmc_writeCsr(int32 data, int32 PA, int32 access); +static void kmc_doMicroinstruction (int32 k, uint16 instr); +static t_stat kmc_txService(UNIT * txup); +static t_stat kmc_rxService(UNIT * rxup); + +#if KMC_UNITS > 1 +static t_stat kmc_setDeviceCount (UNIT *txup, int32 val, char *cptr, void *desc); +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, + struct sim_unit *uptr, int32 flag, char *cptr); +static char *kmc_description (DEVICE *dptr); + +/* Global data */ + +extern int32 IREQ (HLVL); + +static int32 kmc_AintAck (void); +static int32 kmc_BintAck (void); + +#define IOLN_KMC 010 + +static DIB kmc_dib = { + IOBA_AUTO, /* ba - Base address */ + IOLN_KMC * INITIAL_KMCS, /* lnt - Length */ + &kmc_readCsr, /* rd - read IO */ + &kmc_writeCsr, /* wr - write IO */ + 2 * INITIAL_KMCS, /* vnum - number of Interrupt vectors */ + IVCL (KMCA), /* vloc - vector locator */ + VEC_AUTO, /* vec - auto */ + {&kmc_AintAck, /* ack - iack routines */ + &kmc_BintAck}, + IOLN_KMC, /* IO space per unit */ +}; + +/* One UNIT for each (possible) active line for transmit, and another for receive */ +static UNIT tx_units[MAX_ACTIVE][KMC_UNITS]; /* Line 0 is primary unit. txup references */ +#define unit_kmc u3 +#define unit_line u4 +#define unit_htime u5 + +/* Timers - in usec */ + +#define RXPOLL_DELAY (1*1000) /* Poll for a new message */ +#define RXBDL_DELAY (10*1000) /* Grace period for host to supply a BDL */ +#define RXNEWBD_DELAY (10) /* Delay changing descriptors */ +#define RXSTART_DELAY (50) /* Host provided BDL to receive start */ + + +static UNIT rx_units[MAX_ACTIVE][KMC_UNITS]; /* Secondary unit, used for RX. rxup references */ + +/* Timers - in usec */ + +#define TXSTART_DELAY (10) /* TX BUFFER IN to TX start */ +#define TXDONE_DELAY (10) /* Completion to to poll for next bd */ +#define TXCTS_DELAY (100*1000) /* Polling for CTS to appear */ +#define TXDUP_DELAY (1*1000*1000) /* Wait for DUP to accept data (SNH) */ + +static BITFIELD kmc_sel0_decoder[] = { + BIT (IEI), + BITNCF (3), + BIT (IEO), + BIT (RQI), + BITNCF (2), + BIT (SUP), + BIT (RMI), + BIT (RMO), + BIT (LUL), + BIT (SLU), + BIT (CWR), + BIT (MRC), + BIT (RUN), + ENDBITS +}; +static BITFIELD kmc_sel2_decoder[] = { + BITF (CMD,2), + BIT (IOT), + BITNCF (1), + BIT (RDI), + BITNCF (2), + BIT (RDO), + BITFFMT (LINE,7,"%u"), + BIT (CQOVF), + ENDBITS +}; +static REG kmc_reg[] = { + { BRDATADF (SEL0, kmc_sel0, KMC_RDX, 16, KMC_UNITS, "Initialization/control", kmc_sel0_decoder) }, + { BRDATADF (SEL2, kmc_sel2, KMC_RDX, 16, KMC_UNITS, "Command/line", kmc_sel2_decoder) }, + { ORDATA (SEL4, kmc_sel4, 16) }, + { ORDATA (SEL6, kmc_sel6, 16) }, + { NULL }, + }; + +MTAB kmc_mod[] = { + { MTAB_XTD|MTAB_VDV, 010, "ADDRESS", "ADDRESS", + &set_addr, &show_addr, NULL, "Bus address" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "VECTOR", "ADDRESS", + &set_vec, &show_vec, NULL, "Interrupt vector" }, + { MTAB_XTD|MTAB_VDV|MTAB_VALR|MTAB_NMO, 0, "SPEED", "SPEED=dup=bps", + &kmc_setLineSpeed, &kmc_showLineSpeed, NULL, "Line speed (bps)" }, + { MTAB_XTD|MTAB_VUN|MTAB_NMO, 1, "STATUS", NULL, NULL, &kmc_showStatus, NULL, "Display KMC status" }, +#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 }, + }; + +DEVICE kmc_dev = { + "KDP", + tx_units[0], + kmc_reg, /* Register decode tables */ + kmc_mod, /* Modifier table */ + INITIAL_KMCS, /* Number of units */ + KMC_RDX, /* Address radix */ + 13, /* Address width: 18 - <17:13> are 1s, omits UBA */ + 1, /* Address increment */ + KMC_RDX, /* Data radix */ + 8, /* Data width */ + NULL, /* examine routine */ + NULL, /* Deposit routine */ + &kmc_reset, /* reset routine */ + NULL, /* boot routine */ + NULL, /* attach routine */ + NULL, /* detach routine */ + &kmc_dib, /* context */ + DEV_UBUS | KMC_DIS /* Flags */ + | DEV_DISABLE + | DEV_DEBUG, + 0, /* debug control */ + kmc_debug, /* debug flag table */ + NULL, /* memory size routine */ + NULL, /* logical name */ + &kmc_help, /* help routine */ + NULL, /* attach help routine */ + NULL, /* help context */ + &kmc_description /* Device description routine */ +}; + +/* Forward declarations: not referenced in simulator data */ + +static void kmc_masterClear(int32 k); +static void kmc_startUcode (int32 k); +static void kmc_dispatchInputCmd(int32 k); + +/* Control functions */ +static void kmc_baseIn (int32 k, dupstate *d, uint16 cmdsel2, int line); +static void kmc_ctrlIn (int32 k, dupstate *d, int line); + +/* Receive functions */ +void kmc_rxBufferIn(dupstate *d, int32 ba, uint32 sel6v); +static void kdp_receive(int dupidx, int count); + +/* Transmit functions */ +static void kmc_txBufferIn(dupstate *d, int32 ba, uint32 sel6v); +static void kmc_txComplete (int32 dupidx, int status); +static t_bool kmc_txNewBdl(dupstate *d); +static t_bool kmc_txNewBd(dupstate *d); +static t_bool kmc_txAppendBuffer(dupstate *d); + +/* Completions */ +static void kmc_processCompletions (int32 k); + +static void kmc_ctrlOut (int32 k, uint8 code, uint16 rx, uint8 line, uint32 bda); +static void kmc_modemChange (int32 dupidx); +static t_bool kmc_updateDSR (dupstate *d); + +static t_bool kmc_bufferAddressOut (int32 k, uint16 flags, uint16 rx, uint8 line, uint32 bda); + +/* Buffer descriptor list utilities */ +static int32 kmc_updateBDCount(uint32 bda, uint16 *bd); + +/* Errors */ +static void kmc_halt (int32 k, int error); + +/* Interrupt management */ +static void kmc_updints(int32 k); +static int32 kmc_AintAck (void); +static int32 kmc_BintAck (void); + +/* DUP access */ + +/* Debug support */ +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); + +/* Queue management */ +static void initqueue (QH *head, int32 *count, int32 max, void *list, size_t size); +/* Convenience for initqueue() calls */ +# define MAX_LIST_SIZE(q) DIM(q), (q), sizeof(q[0]) +# define INIT_HDR_ONLY 0, NULL, 0 + +static t_bool insqueue (QH *entry, QH *pred, int32 *count, int32 max); +static void *remqueue (QH *entry, int32 *count); + + +/* + * Reset KMC device. This resets ALL the KMCs: + */ + +static t_stat kmc_reset(DEVICE* dptr) { + int32 k; + size_t i; + + if (sim_switches & SWMASK ('P')) { + for (i = 0; i < DIM (dupState); i++) { + dupstate *d = &dupState[i]; + d->kmc = -1; + 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++) { + sim_debug (DF_INF, dptr, "KMC%d: Reset\n", k); + + /* One-time initialization of UNITs, one/direction/line */ + for (i = 0; i < MAX_ACTIVE; i++) { + if (!tx_units[i][k].action) { + memset (&tx_units[i][k], 0, sizeof tx_units[0][0]); + memset (&rx_units[i][k], 0, sizeof tx_units[0][0]); + + tx_units[i][k].action = &kmc_txService; + tx_units[i][k].flags = 0; + tx_units[i][k].capac = 0; + tx_units[i][k].unit_kmc = k; + tx_units[i][k].unit_line = i; + + rx_units[i][k].action = &kmc_rxService; + rx_units[i][k].flags = 0; + rx_units[i][k].capac = 0; + rx_units[i][k].unit_kmc = k; + rx_units[i][k].unit_line = i; + } + } + kmc_masterClear (k); /* If previously running, halt */ + + if (sim_switches & SWMASK ('P')) + gflags &= ~FLG_INIT; + + if (!(gflags & FLG_INIT)) { /* Power-up reset */ + sel0 = 0x00aa; + sel2 = 0xa5a5; + sel4 = 0xdead; + sel6 = 0x5a5a; + memset (ucode, 0xcc, sizeof ucode); + memset (dram, 0xdd, sizeof dram); + gflags |= FLG_INIT; + gflags &= ~FLG_UCINI; + } + } + + return auto_config (dptr->name, ((dptr->flags & DEV_DIS)? 0: dptr->numunits)); /* auto config */ +} + + +/* + * Read registers: + */ + +static t_stat kmc_readCsr (int32* data, int32 PA, int32 access) { + int32 k; + + k = ((PA-((DIB *)kmc_dev.ctxt)->ba) / IOLN_KMC); + + switch ((PA >> 1) & 03) { + case 00: + *data = sel0; + break; + case 01: + *data = sel2; + break; + case 02: + if ((sel0 & SEL0_RMO) && (sel0 & SEL0_RMI)) { + *data = mni; + } else { + *data = sel4; + } + break; + case 03: + if (sel0 & SEL0_RMO) { + if (sel0 & SEL0_RMI) { + *data = mni; + } else { + *data = ucode[mna]; + } + } else { + *data = sel6; + } + break; + } + + sim_debug (DF_RGR, &kmc_dev, "KMC%u CSR rd: addr=0%06o SEL%d, data=%06o 0x%04x access=%d\n", + k, PA, PA & 07, *data, *data, access); + return SCPE_OK; +} + +/* + * Write registers: + */ + +static t_stat kmc_writeCsr (int32 data, int32 PA, int32 access) { + uint32 changed; + int reg = PA & 07; + int sel = (PA >> 1) & 03; + int32 k; + + k = ((PA-((DIB *)kmc_dev.ctxt)->ba) / IOLN_KMC); + + if (access == WRITE) { + sim_debug (DF_RGW, &kmc_dev, "KMC%u CSR wr: addr=0%06o SEL%d, data=%06o 0x%04x\n", + k, PA, reg, data, data); + } else { + sim_debug (DF_RGW, &kmc_dev, "KMC%u CSR wr: addr=0%06o BSEL%d, data=%06o 0x%04x\n", + k, PA, reg, data, data); + } + + switch (sel) { + case 00: /* SEL0 */ + if (access == WRITEB) { + data = (PA & 1) + ? (((data & 0377) << 8) | (sel0 & 0377)) + : ((data & 0377) | (sel0 & 0177400)); + } + changed = sel0 ^ data; + sel0 = data; + if (sel0 & SEL0_MRC) { + if (((sel0 & SEL0_RUN) == 0) && (changed & SEL0_RUN)) { + kmc_halt (k, HALT_MRC); + } + kmc_masterClear(k); + break; + } + if (!(data & SEL0_RUN)) { + if (data & SEL0_RMO) { + if ((changed & SEL0_CWR) && (data & SEL0_CWR)) { /* CWR rising */ + ucode[mna] = sel6; + sel4 = ucode[mna]; /* Copy contents to sel 4 */ + } + } else { + if (changed & SEL0_RMO) { /* RMO falling */ + sel4 = mna; + } + } + if ((data & SEL0_RMI) && (changed & SEL0_RMI)) { + mni = sel6; + } + if ((data & SEL0_SUP) && (changed & SEL0_SUP)) { + if (data & SEL0_RMI) { + kmc_doMicroinstruction(k, mni); + } else { + kmc_doMicroinstruction(k, ucode[upc++]); + } + } + } + if (changed & SEL0_RUN) { /* Changing the run bit? */ + if (sel0 & SEL0_RUN) { + kmc_startUcode (k); + } else { + kmc_halt (k, HALT_STOP); + } + } + if (changed & (SEL0_IEI | SEL0_IEO)) + kmc_updints (k); + + if ((sel0 & SEL0_RUN)) { + if ((sel0 & SEL0_RQI) && !(sel2 & SEL2_RDO)) + sel2 = (sel2 & 0xFF00) | SEL2_RDI; /* Clear command bits too */ + kmc_updints(k); + } + break; + case 01: /* SEL2 */ + if (access == WRITEB) { + data = (PA & 1) + ? (((data & 0377) << 8) | (sel2 & 0377)) + : ((data & 0377) | (sel2 & 0177400)); + } + if (sel0 & SEL0_RUN) { + /* Handle commands in and out. + * Output takes priority, but after servicing an + * output, an input request must be serviced even + * if another output command is ready. + */ + if ((sel2 & SEL2_RDO) && (!(data & SEL2_RDO))) { + sel2 = data; /* RDO clearing, RDI can't be set */ + if (sel0 & SEL0_RQI) { + sel2 = (sel2 & 0xFF00) | SEL2_RDI; + kmc_updints(k); + } else + kmc_processCompletions(k); + } else { + if ((sel2 & SEL2_RDI) && (!(data & SEL2_RDI))) { + sel2 = data; /* RDI clearing, RDO can't be set */ + kmc_dispatchInputCmd(k); /* Can set RDO */ + if ((sel0 & SEL0_RQI) && !(sel2 & SEL2_RDO)) + sel2 = (sel2 & 0xFF00) | SEL2_RDI; + kmc_updints(k); + } else { + sel2 = data; + } + } + } else { + sel2 = data; + } + break; + case 02: /* SEL4 */ + mna = data & (KMC_CRAMSIZE -1); + sel4 = data; + break; + case 03: /* SEL6 */ + if (sel0 & SEL0_RMI) { + mni = data; + } + sel6 = data; + break; + } + + return SCPE_OK; +} + +/* Microengine simulator + * + * This simulates a small subset of the KMC11-A's instruction set. + * This is necessary because the operating systems force microprocessor + * to execute these instructions in order to load (and extract) state. + * These are implemented here so that the OS tools are happy, and to + * give their error logging tools something to do. + */ + +static void kmc_doMicroinstruction (int32 k, uint16 instr) { + switch (instr) { + case 0041222: /* MOVE */ + sel2 = (sel2 & ~0xFF) | (dram[mar%KMC_DRAMSIZE] & 0xFF); + break; + case 0055222: /* MOVE */ + sel2 = (sel2 & ~0xFF) | (dram[mar%KMC_DRAMSIZE] & 0xFF); + mar = (mar +1)%KMC_DRAMSIZE; + break; + case 0122440: /* MOVE */ + dram[mar%KMC_DRAMSIZE] = sel2 & 0xFF; + break; + case 0136440: /* MOVE */ + dram[mar%KMC_DRAMSIZE] = sel2 & 0xFF; + mar = (mar +1)%KMC_DRAMSIZE; + break; + case 0121202: /* MOVE */ + case 0021002: /* MOVE */ + sel2 = (sel2 & ~0xFF) | 0; + break; + default: + if ((instr & 0160000) == 0000000) { /* MVI */ + switch (instr & 0174000) { + case 0010000: /* Load MAR LOW */ + mar = (mar & 0xFF00) | (instr & 0xFF); + break; + case 0004000: /* Load MAR HIGH */ + mar = (mar & 0x00FF) | ((instr & 0xFF)<<8); + break; + default: /* MVI NOP / MVI INC */ + break; + } + break; + } + if ((instr & 0163400) == 0100400) { + upc = ((instr & 0014000) >> 3) | (instr & 0377); + sim_debug (DF_INF, &kmc_dev, "KMC%u microcode start uPC %04o\n", k, upc); + break; + } + } + return; +} + +/* Transmitter service. + * + * Each line has a TX unit. This thread handles message transmission. + * + * A host-supplied buffer descriptor list is walked and the data handed off + * to the line. Because the DUP emulator can't abort a transmission in progress, + * and wants to receive formatted messages, the data from host memory is + * accumulated into an intermediate buffer. + * + * TX BUFFER OUT completions are delivered as the data is retrieved, which happens + * at approximately the 'line speed'; this does not indicate that the data is on + * the wire. Only a DDCMP ACK confirms receipt. + * + * TX CONTROL OUT completions are generated for errors. + * + * The buffer descriptor flags for resync, start and end of message are obeyed. + * + * The transmitter asserts RTS at the beginning of a message, and clears it when + * idle. + * + * No more than one message is on the wire at any time. + * + * The DUP may be speed limited, delivering transmit completions at line speed. + * In that case, the KDP and DUP limits will interact. + * + * This thread wakes: + * o When a new buffer descriptor is delivered by the host + * o When a state machine timeout expires + * o When a DUP transmit completion occurs. + * + */ + +static t_stat kmc_txService (UNIT *txup) { + int32 k = txup->unit_kmc; + dupstate *d = line2dup[txup->unit_line]; + t_bool more; + + assert ((k >= 0) && (k < (int32) kmc_dev.numunits) && (d->kmc == k) && + (d->line == txup->unit_line)); + + /* Provide the illusion of progress. */ + upc = 1 + ((upc + 1) % (KMC_CRAMSIZE -1)); + + /* Process one buffer descriptor per cycle + * CAUTION: this switch statement uses fall-through cases. + */ + +/* State control macros. + * + * All exit the current statement (think "break" or "continue") + * + * Change to newstate and process immediately + */ + +#define TXSTATE(newstate) { \ + d->txstate = newstate; \ + more = FALSE; \ + continue; } + +/* Change to newstate after a delay of time. */ +#define TXDELAY(newstate,time) { \ + txup->wait = time; \ + d->txstate = newstate; \ + more = FALSE; \ + break; } + +/* Stop processing an return to IDLE. + * This will NOT check for more work - it is used + * primarily in error conditions. + */ +#define TXSTOP { \ + d->txstate = TXIDLE; \ + more = FALSE; \ + break; } + + do { + more = TRUE; + + if (d->txstate > TXRTS) { + sim_debug (DF_BUF, &kmc_dev, "KMC%u line %u: transmit service %s state = %u\n", + k, txup->unit_line, (more? "continued": "activated"), d->txstate); + } + + switch (d->txstate) { + case TXDONE: /* Resume from completions */ + d->txstate = TXIDLE; + + case TXIDLE: /* Check for new BD */ + if (!kmc_txNewBdl(d)) { + TXSTOP; + } + d->txmlen = + d->txslen = 0; + d->txstate = TXRTS; + + if (dup_set_RTS (d->dupidx, TRUE) != SCPE_OK) { + sim_debug (DF_CTO, &kmc_dev, "KMC%u line %u: dup: %d DUP CSR NXM\n", + k, d->line, d->dupidx); + kmc_ctrlOut (k, SEL6_CO_NXM, 0, d->line, 0); + } + + + case TXRTS: /* Wait for CTS */ + if (dup_get_CTS (d->dupidx) <= 0) { + TXDELAY (TXRTS, TXCTS_DELAY); + } + + d->txstate = TXSOM; + + sim_debug (DF_BUF, &kmc_dev, "KMC%u line %u: transmitting bdl=%06o\n", + k, txup->unit_line, d->tx.bda); + case TXSOM: /* Start assembling a message */ + if (!(d->tx.bd[2] & BDL_SOM)) { + sim_debug (DF_ERR, &kmc_dev, "KMC%u line %u: TX BDL not SOM\n", k, d->line); + kmc_halt (k, HALT_XSOM); + TXSTOP; + } + + if (d->tx.bd[2] & BDL_RSY) { + static const uint8 resync[8] = { DDCMP_SYN, DDCMP_SYN, DDCMP_SYN, DDCMP_SYN, + DDCMP_SYN, DDCMP_SYN, DDCMP_SYN, DDCMP_SYN, }; + if (!d->txmsg || (d->txmsize < sizeof (resync))) { + d->txmsize = 8 + sizeof (resync); + d->txmsg = (uint8 *)realloc (d->txmsg, 8 + sizeof (resync)); + } + memcpy (d->txmsg, resync, sizeof (resync)); + d->txmlen = + d->txslen = sizeof (resync); + } + + d->txstate = TXHDR; + + case TXHDR: /* Assemble the header */ + if (!kmc_txAppendBuffer(d)) { /* NXM - Try next list */ + TXDELAY (TXDONE, TXDONE_DELAY); + } + TXDELAY (TXHDRX, XTIME (d->tx.bd[1], d->linespeed)); + + case TXHDRX: /* Report header descriptor done */ + if (!kmc_bufferAddressOut (k, 0, 0, d->line, d->tx.bda)) { + TXDELAY (TXDONE, TXDONE_DELAY); + } + if (!(d->tx.bd[2] & BDL_EOM)) { + if (kmc_txNewBd(d)) { + TXSTATE (TXHDR); + } + /* Not EOM, no more BDs - underrun or NXM. In theory + * this should have waited on char time before declaring + * underrun, but no sensible OS would live that dangerously. + */ + TXDELAY (TXDONE, TXDONE_DELAY); + } + + /* EOM. Control messages are always complete */ + if (d->txmsg[d->txslen+0] == DDCMP_ENQ) { + TXSTATE (TXRDY); + } + + /* EOM expecting data to follow. + * However, if the OS computes and includes HRC in a data/MOP message, this can + * be the last descriptor. In that case, this is EOM. + */ + if (d->tx.bd[2] & BDL_LDS) { + TXSTATE (TXMRDY); + break; + } + + /* Data sent in a separate descriptor */ + + if (!kmc_txNewBd(d)) { + TXDELAY (TXDONE, TXDONE_DELAY); + } + + if (!(d->tx.bd[2] & BDL_SOM)) { + kmc_halt (k, HALT_XSOM2); + sim_debug (DF_ERR, &kmc_dev, "KMC%u line %u: TX BDL not SOM\n", k, d->line); + TXSTOP; + } + d->txstate = TXDATA; + + case TXDATA: /* Assemble data/maint payload */ + if (!kmc_txAppendBuffer(d)) { /* NXM */ + TXDELAY (TXDONE, TXDONE_DELAY); + } + TXDELAY (TXDATAX, XTIME (d->tx.bd[1], d->linespeed)); + + case TXDATAX: /* Report BD completion */ + if (!kmc_bufferAddressOut (k, 0, 0, d->line, d->tx.bda)) { + TXDELAY (TXDONE, TXDONE_DELAY); + } + if (d->tx.bd[2] & BDL_EOM) { + TXSTATE (TXRDY); + } + if (!kmc_txNewBd(d)) { + TXDELAY (TXDONE, TXDONE_DELAY); + } + TXSTATE (TXDATA); + + /* These states hand-off the message to the DUP. + * txService suspends until transmit complete. + * Note that txComplete can happen within the calls to the DUP. + */ + 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])) == + (d->txmlen - (d->txslen + 8)))); + if (!dup_put_msg_bytes (d->dupidx, d->txmsg + d->txslen, d->txmlen - d->txslen, TRUE, TRUE)) { + sim_debug (DF_PKT, &kmc_dev, "KMC%u line %u: DUP%d refused TX packet\n", k, d->line, d->dupidx); + TXDELAY (TXMRDY, TXDUP_DELAY); + } + more = FALSE; + break; + + 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)) { + sim_debug (DF_PKT, &kmc_dev, "KMC%u line %u: DUP%d refused TX packet\n", k, d->line, d->dupidx); + TXDELAY (TXRDY, TXDUP_DELAY); + } + more = FALSE; + break; + } + + assert (((d->txmlen - d->txslen) > 6) && /* Data, length should match count */ + (((size_t)(((d->txmsg[d->txslen + 2] & 077) << 8) | d->txmsg[d->txslen + 1])) == + (d->txmlen - (d->txslen + 6)))); + if (!dup_put_msg_bytes (d->dupidx, d->txmsg, d->txslen + 6, TRUE, TRUE)) { + sim_debug (DF_PKT, &kmc_dev, "KMC%u line %u: DUP%d refused TX packet\n", k, d->line, d->dupidx); + TXDELAY (TXRDY, TXDUP_DELAY); + } + if (!dup_put_msg_bytes (d->dupidx, d->txmsg + d->txslen + 6, d->txmlen - (d->txslen + 6), FALSE, TRUE)) { + sim_debug (DF_PKT, &kmc_dev, "KMC%u line %u: DUP%d refused TX packet\n", k, d->line, d->dupidx); + TXDELAY (TXRDY, TXDUP_DELAY); + } + more = FALSE; + break; + + /* Active should never be reached as txService is not + * scheduled while transmission is in progress. + * txComplete will reset txstate based on the final descriptor. + */ + default: + case TXACT: + sim_debug (DF_PKT, &kmc_dev, "KMC%u line %u: kmc_txService called while active\n", k, d->line); + TXSTOP; + } + } while (more); +#undef TXSTATE +#undef TXDELAY +#undef TXSTOP + + if (d->txstate == TXIDLE) { + assert (!d->txavail); + if (dup_set_RTS (d->dupidx, FALSE) != SCPE_OK) { + sim_debug (DF_CTO, &kmc_dev, "KMC%u line %u: dup: %d DUP CSR NXM\n", + k, d->line, d->dupidx); + kmc_ctrlOut (k, SEL6_CO_NXM, 0, d->line, 0); + } + } else { + if (d->txstate != TXACT) + sim_activate_after(txup, txup->wait); + } + return SCPE_OK; +} + +/* Receiver service + * + * Each line has an RX unit. This service thread accepts incoming + * messages from the DUP, and delivers the data into buffer descriptors + * provided by the host. + * + * Polling is not necessary if the DUP provides input notification; otherwise + * it's at a rate of a few character times at 19,200 - the maximum line speed. + * + * Once a message has been accepted, the thread looks for a host-provided + * buffer descriptor. If one doesn't appear in a reasonable time (in hardware, + * it's one character time - this code is more generous), the message is dropped + * and a RX CONTROL OUT is issued. + * + * Then the message is parsed, validated and delivered to the host buffer. + * + * Any CRC errors generate RX CONTROL OUTs. RX BUFFER OUTs are generated for each + * descriptor filled, and at of message for the last descriptor used. + * + * If the line is configured for Secondary Station address matching, the + * message is discarded if the address does not match (and the CRC validated). + * + * After generating a RX BUFFER OUT, the thread suspends prior to filling the + * next descriptor. + * + * If reception is killed, the RX state machine is reset by the RX BUFFER IN. + * + * This thread wakes: + * o When a RX BUFFER IN delivers a buffer + * o When timeouts between states expire. + * o When the DUP notifies it of a new message received. + */ + +static t_stat kmc_rxService (UNIT *rxup) { + int32 k = rxup->unit_kmc; + dupstate *d = line2dup[rxup->unit_line]; + BDL *bdl; + t_stat r; + int32 xrem, seglen; + + assert ((k >= 0) && (k < (int32) kmc_dev.numunits) && (d->kmc == k) && + (d->line == rxup->unit_line)); + + if (d->rxstate > RXBDL) { + sim_debug (DF_BUF, &kmc_dev, "KMC%u line %u: receive service activated state = %u\n", + k, rxup->unit_line, d->rxstate); + } + + /* Provide the illusion of progress. */ + upc = 1 + ((upc + 1) % (KMC_CRAMSIZE -1)); + + rxup->wait = RXPOLL_DELAY; + + /* CAUTION: This switch statement uses fall-through cases + * + * The KILL logic in kmc_rxBufferIn tracks these states. + */ + + switch (d->rxstate) { + case RXIDLE: + rxup->wait = RXPOLL_DELAY; + + r = dup_get_packet (d->dupidx, (const uint8 **)&d->rxmsg, &d->rxmlen); + if (r == SCPE_LOST) { + kmc_updateDSR (d); + break; + } + if ((r != SCPE_OK) || (d->rxmsg == NULL)) { + break; + } + + if (!(d->ctrlFlags & SEL6_CI_ENABLE)) { + break; + } + + while (d->rxmlen && (d->rxmsg[0] == DDCMP_SYN)) { + d->rxmsg++; + d->rxmlen--; + } + if (d->rxmlen < 8) { + break; + } + + if (!((d->rxmsg[0] == DDCMP_SOH) || + (d->rxmsg[0] == DDCMP_ENQ) || + (d->rxmsg[0] == DDCMP_DLE))) { + + /* Toggling RCVEN causes the DUP receiver to resync. + */ +#ifdef DUP_RXRESYNC + dup_set_RCVEN (d->dupidx, FALSE); + dup_set_RCVEN (d->dupidx, TRUE); +#endif + 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; + + if (DEBUG_PRS (kmc_dev)) { + if (d->rxmsg[0] == DDCMP_ENQ) { + static const char *const ctlnames [] = { + "00", "ACK", "NAK", "REP", "04", "05", "STRT", "STACK" }; + uint8 type = d->rxmsg[1]; + + sim_debug (DF_BUF, &kmc_dev, "KMC%u line %u: receiving %s\n", + k, rxup->unit_line, + ((type >= DIM (ctlnames))? "UNKNOWN" : ctlnames[type])); + } else { + sim_debug (DF_BUF, &kmc_dev, "KMC%u line %u: receiving %s len=%u\n", + k, rxup->unit_line, + ((d->rxmsg[0] == DDCMP_SOH)? "DATA" : "MAINT"), d->rxmlen); + } + } + + case RXBDL: + if (!(bdl = (BDL *)remqueue(d->rxqh.next, &d->rxavail))) { + rxup->wait = RXBDL_DELAY; + d->rxstate = RXNOBUF; + break; + } + d->rx.bda = bdl->ba; + assert (insqueue (&bdl->hdr, d->bdqh.prev, &d->bdavail, DIM(d->bdq))); + + sim_debug (DF_BUF, &kmc_dev, "KMC%u line %u: receiving bdl=%06o\n", + k, rxup->unit_line, d->rx.bda); + + if (Map_ReadW (d->rx.bda, 3*2, d->rx.bd)) { + kmc_ctrlOut (k, SEL6_CO_NXM, SEL2_IOT, d->line, d->rx.bda); + d->rxstate = RXIDLE; + break; + } + d->rxstate = RXBUF; + + case RXBUF: + d->rx.ba = ((d->rx.bd[2] & BDL_XAD) << BDL_S_XAD) | d->rx.bd[0]; + if (d->rx.bd[1] == 0) { + sim_debug (DF_ERR, &kmc_dev, "KMC%u line %u: RX buffer descriptor size is zero\n", k, d->line); + kmc_halt (k, HALT_MTRCV); + d->rxstate = RXIDLE; + break; + } + d->rx.rcvc = 0; + d->rxdlen = 0; + d->rxstate = RXDAT; + + case RXDAT: + more: + if (d->rxused < 8) { + seglen = 6 - d->rxused; + } else { + seglen = d->rxmlen - (d->rxused +2); + } + if (seglen > d->rx.bd[1]) { + seglen = d->rx.bd[1]; + } + assert (seglen > 0); + + xrem = Map_WriteB (d->rx.ba, seglen, d->rxmsg + d->rxused); + if (xrem != 0) { + uint16 bd[3]; + memcpy (bd, &d->rx.bd, sizeof bd); + seglen -= xrem; + d->rx.rcvc += seglen; + bd[1] = d->rx.rcvc; + kmc_updateBDCount (d->rx.bda, bd); /* Unchecked because already reporting NXM */ + kmc_ctrlOut (k, SEL6_CO_NXM, SEL2_IOT, d->line, d->rx.bda); + d->rxstate = RXIDLE; + break; + } + d->rx.ba += seglen; + d->rx.rcvc += seglen; + d->rxused += seglen; + + if (d->rxused == 6) { + if (0 != ddcmp_crc16 (0, d->rxmsg, 8)) { + sim_debug (DF_PKT, &kmc_dev, "KMC%u line %u: HCRC Error for %d byte packet\n", + k, d->line, d->rxmlen); +#ifdef DUP_RXRESYNC + dup_set_RCVEN (d->dupidx, FALSE); + dup_set_RCVEN (d->dupidx, TRUE); +#endif + kmc_ctrlOut (k, SEL6_CO_HCRC, SEL2_IOT, d->line, d->rx.bda); + d->rxstate = RXIDLE; + break; + } + d->rxused += 2; + d->linkstate &= ~LINK_SEL; + if (d->rxmsg[2] & 0x80) { + d->linkstate |= LINK_SEL; + } + if (d->ctrlFlags & SEL6_CI_ENASS) { /* Note that spec requires first bd >= 6 if SS match enabled */ + if (!(d->rxmsg[5] == (d->ctrlFlags & SEL6_CI_SADDR))) { /* Also include SELECT? */ + assert ((bdl = (BDL *)remqueue(d->bdqh.prev, &d->bdavail)) != NULL); + assert (bdl->ba == d->rx.bda); + assert (insqueue (&bdl->hdr, &d->rxqh, &d->rxavail, MAXQUEUE)); + d->rxstate = RXIDLE; + break; + } + } + d->rxdlen = ((d->rxmsg[2] &~ 0300) << 8) | d->rxmsg[1]; + } + if (((d->rxused == 8) && (d->rxmsg[0] == DDCMP_ENQ)) || + (((d->rxused - 8) == d->rxdlen) && (d->rxmsg[0] != DDCMP_ENQ))) { /* End of message */ + + /* Issue completion after the nominal reception delay */ + + rxup->wait = XTIME (d->rx.rcvc+2, d->linespeed); + d->rxstate = RXLAST; + break; + } + if (d->rx.rcvc < d->rx.bd[1]) { + goto more; + } + + /* This descriptor is full. No need to update its bc. + * Issue the completion after the nominal reception delay. + */ + d->rxstate = RXFULL; + rxup->wait = XTIME (d->rx.bd[1], d->linespeed); + break; + + case RXLAST: + /* End of message. Update final BD count, check data CRC & report either + * BUFFER OUT (with EOM) or error CONTROL OUT (NXM or DCRC). + */ + d->rx.bd[1] = d->rx.rcvc; + if (kmc_updateBDCount (d->rx.bda, d->rx.bd)) { + kmc_ctrlOut (k, SEL6_CO_NXM, SEL2_IOT, d->line, d->rx.bda); + } else { + if ((d->rxmsg[0] != DDCMP_ENQ) && + (0 != ddcmp_crc16 (0, d->rxmsg +8, d->rxdlen+2))) { + sim_debug (DF_PKT, &kmc_dev, "KMC%u line %u: DCRC Error for %d byte packet\n", + k, d->line, d->rxmlen); +#ifdef DUP_RXRESYNC + dup_set_RCVEN (d->dupidx, FALSE); + dup_set_RCVEN (d->dupidx, TRUE); +#endif + kmc_ctrlOut (k, SEL6_CO_DCRC, SEL2_IOT, d->line, d->rx.bda); + } else { + kmc_bufferAddressOut (k, SEL6_BO_EOM, SEL2_IOT, d->line, d->rx.bda); +#ifdef DUP_RXRESYNC + if (d->rxmsg[2] & 0x40) { /* QSYNC? (Next message uses short sync) */ + dup_set_RCVEN (d->dupidx, FALSE); + dup_set_RCVEN (d->dupidx, TRUE); + } +#endif + } + } + rxup->wait = RXNEWBD_DELAY; + d->rxstate = RXIDLE; + break; + + case RXFULL: + kmc_bufferAddressOut (k, 0, SEL2_IOT, d->line, d->rx.bda); + + /* Advance to next descriptor */ + + if (d->rx.bd[2] & BDL_LDS) { + d->rxstate = RXBDL; + } else { + d->rx.bda += 3*2; + if (Map_ReadW (d->rx.bda, 3*2, d->rx.bd)) { + kmc_ctrlOut (k, SEL6_CO_NXM, SEL2_IOT, d->line, d->rx.bda); + d->rxstate = RXIDLE; + break; + } + sim_debug (DF_BUF, &kmc_dev, "KMC%u line %u: receiving bd=%06o\n", + k, rxup->unit_line, d->rx.bda); + d->rx.rcvc = 0; /* Set here in case of kill */ + d->rxstate = RXBUF; + } + rxup->wait = RXNEWBD_DELAY; + break; + + case RXNOBUF: + kmc_ctrlOut (k, SEL6_CO_NOBUF, SEL2_IOT, d->line, 0); + d->rxstate = RXIDLE; + break; + + default: + assert (FALSE); + } + + if ((d->rxstate != RXIDLE) || d->rxavail) { + sim_activate_after(rxup, rxup->wait); + } + + return SCPE_OK; +} + +/* + * master clear a KMC + * + * Master clear initializes the hardware, but does not clear any RAM. + * This includes the CSRs, which are a dual-ported RAM structure. + * + * There is no guarantee that any data structures are initialized. + */ + +static void kmc_masterClear(int32 k) { + + if (sim_deb) { + DEVICE *dptr = find_dev_from_unit (&tx_units[0][k]); + + sim_debug (DF_INF, dptr, "KMC%d: Master clear\n", k); + } + + if (sel0 & SEL0_RUN) { + kmc_halt (k, HALT_MRC); + } + + /* Clear SEL1 (maint reg) - done by HW reset. + * Clear IE (HW doesn't, but it simplifies + * things & every user to date writes zeroes with MRC. + */ + sel0 &= SEL0_MRC | (0x00FF & ~(SEL0_IEO | SEL0_IEI)); + upc = 0; + mar = 0; + mna = 0; + mni = 0; + + kmc_updints (k); +} + +/* Initialize the KMC state that is done by microcode */ + +static void kmc_startUcode (int32 k) { + int i; + const char *uname; + + if ((uname = kmc_verifyUcode (k)) == NULL) { + sim_debug (DF_INF, &kmc_dev, "KMC%u: microcode not loaded, won't run\n", k); + kmc_halt (k, HALT_BADUC); + return; + } + + sim_debug (DF_INF, &kmc_dev, "KMC%u started %s microcode at uPC %04o\n", + k, uname, upc); + + if (upc != 0) { /* Resume from cleared RUN */ + if (gflags & FLG_UCINI) { + for (i = 0; i < MAX_ACTIVE; i++) { + UNIT *up = &tx_units[i][k]; + + if (up->unit_htime) { + sim_activate (up, up->unit_htime); + } + up = &rx_units[i][k]; + if (up->unit_htime) { + sim_activate (up, up->unit_htime); + } + } + return; + } + kmc_halt (k, HALT_BADRES); + return; + } + + /* upc == 0: microcode initialization */ + + upc = 1; + + /* CSRs */ + + sel0 &= 0xFF00; + sel2 = 0; + sel4 = 0; + sel6 = 0; + + /* Line data */ + + /* Initialize OS mapping to least likely device. (To avoid validating everywhere.) */ + + for (i = 0; i < MAX_ACTIVE; i++) { + line2dup[i] = &dupState[DUP_LINES-1]; + } + + /* Initialize all the DUP structures, releasing any assigned to this KMC. + * + * Only touch the devices if they have previously been assigned to this KMC. + */ + + for (i = 0; i < DUP_LINES; i++) { + dupstate *d = dupState + i; + + if ((d->kmc == k) && (d->dupidx != -1)) { + /* Make sure no callbacks are issued. + * Hardware initialization is done by BASE IN. + */ + dup_set_callback_mode (i, NULL, NULL, NULL); + } + /* Initialize DUP state if dup is unassigned or previously assigned + * to this KMC. This releases the DUP, so restarting ucode will + * release devices, allowing another KMC to assign them if desired. + * This is a level of cooperation that the real devices don't have, + * but it helps catch configuration errors and keeps these data + * structures consistent. Don't deassign a device currently owned + * by another kmc! + */ + if ((d->kmc == k) || (d->kmc == -1)) { + d->dupidx = -1; + d->kmc = -1; + d->line = MAX_LINE; + + initqueue (&d->rxqh, &d->rxavail, INIT_HDR_ONLY); + initqueue (&d->txqh, &d->txavail, INIT_HDR_ONLY); + initqueue (&d->bdqh, &d->bdavail, MAX_LIST_SIZE(d->bdq)); + + d->rxstate = RXIDLE; + d->txstate = TXIDLE; + } + } + + /* Completion queue */ + + initqueue(&cqueueHead, &cqueueCount, INIT_HDR_ONLY); + initqueue(&freecqHead, &freecqCount, MAX_LIST_SIZE(cqueue)); + + gflags |= FLG_UCINI; + kmc_updints (k); + return; +} + +/* + * Perform an input command + * + * The host must request ownership of the CSRs by setting RQI. + * If enabled, it gets an interrupt when RDI sets, allowing it + * to write the command. RDI and RDO are mutually exclusive. + * + * The microcode sets RDI by writing the entire BSEL2 with just RDI. + * This works because RDO can not be set at the same time as RDI. + * + * Mark P found that VMS drivers rely on this and BIS the command code. + * The COM IOP-DUP manual does say that all bits of BSEL2 are + * cleared on completion of a command. However, an output command could + * leave other bits on. + * + * Input commands are processed by the KMC when the host + * clears RDI. Upon completion of a command, 'all bits of bsel2 + * are cleared by the KMC'. This is not implemented literally, since + * the processing of a command can result in an immediate completion, + * setting RDO and the other registers. + * Thus, although all bits are cleared before dispatching, RDO + * and the other other bits of BSEL2 may be set for a output command + * due to a completion if the host has cleared RQI. + */ + +static void kmc_dispatchInputCmd(int32 k) { + int line; + int32 ba; + int16 cmdsel2 = sel2; + dupstate* d; + + line = (cmdsel2 & SEL2_LINE) >> SEL2_V_LINE; + + sel2 &= ~0xFF; /* Clear BSEL2. */ + if (sel0 & SEL0_RQI) /* If RQI was left on, grant the input request */ + sel2 |= SEL2_RDI; /* here so any generated completions will block. */ + + if (line > MAX_LINE) { + sim_debug (DF_ERR, &kmc_dev, "KMC%u line %u: Line number is out of range\n", k, line); + kmc_halt (k, HALT_LINE); + return; + } + d = line2dup[line]; + ba = ((sel6 & SEL6_CO_XAD) << (16-SEL6_V_CO_XAD)) | sel4; + + sim_debug (DF_CMD, &kmc_dev, "KMC%u line %u: INPUT COMMAND sel2=%06o sel4=%06o sel6=%06o ba=%06o\n", k, line, + cmdsel2, sel4, sel6, ba); + + switch (cmdsel2 & (SEL2_IOT | SEL2_CMD)) { + case CMD_BUFFIN: /* TX BUFFER IN */ + kmc_txBufferIn(d, ba, sel6); + break; + case CMD_CTRLIN: /* CONTROL IN. */ + case SEL2_IOT | CMD_CTRLIN: + kmc_ctrlIn (k, d, line); + break; + + case CMD_BASEIN: /* BASE IN. */ + kmc_baseIn (k, d, cmdsel2, line); + break; + + case (SEL2_IOT | CMD_BUFFIN): /* Buffer in, receive buffer for us... */ + kmc_rxBufferIn(d, ba ,sel6); + break; + default: + kmc_halt (k, HALT_BADCMD); + break; + } + + return; +} +/* Process BASE IN command + * + * BASE IN assigns a line number to a DUP device, and marks it + * assigned by a KMC. The CSR address is expressed as bits <12:3> + * only. <17:13> are all set for IO page addresses and added by ucode. + * The DUP has 8 registers, so <2:1> must be zero. The other bits are + * reserved and must be zero. + * + * There is no way to release a line, short of re-starting the microcode. + * + */ +static void kmc_baseIn (int32 k, dupstate *d, uint16 cmdsel2, int line) { + uint32 csraddress; + int32 dupidx; + + /* Verify DUP is enabled and at specified address */ + + /* Ucode clears low three bits of CSR address, ors 1s into the high 3. + * CSR address here may include bits >17 (e.g. UBA number) + * + * The check for reserved bits is not done by the ucode, and can be + * removed. It's here for debugging any cases where this code is at fault. + */ + + csraddress = sel6 & SEL6_II_DUPCSR; + + if ((sel4 != 0) || (cmdsel2 & SEL2_II_RESERVED)) { + sim_debug (DF_ERR, &kmc_dev, "KMC%u: BASE IN reserved bits set\n"); + kmc_halt (k, HALT_BADCSR); + return; + } + csraddress |= IOPAGEBASE; + + dupidx = dup_csr_to_linenum (sel6); + if ((dupidx < 0) || (((size_t) dupidx) >= DIM(dupState))) { /* Call this a NXM so OS can recover */ + sim_debug (DF_ERR, &kmc_dev, "KMC%u line %u: BASE IN %06o 0x%05x is not an enabled DUP\n", + k, line, csraddress, csraddress); + kmc_ctrlOut (k, SEL6_CO_NXM, 0, line, 0); + return; + } + d = &dupState[dupidx]; + if ((d->kmc != -1) && (d->kmc != k)) { + sim_debug (DF_ERR, &kmc_dev, "KMC%u line %u: BASE IN %06o 0x%05x is already assigned to KMC%u\n", + k, line, csraddress, csraddress, + d->kmc); + kmc_ctrlOut (k, SEL6_CO_NXM, 0, line, 0); + return; + } + + /* OK to take ownership. + * Master clear the DUP. NXM will cause a control-out. + * + * The microcode takes no special action to clear DTR. + */ + + d->dupcsr = csraddress; + d->kmc = k; + line2dup[line] = d; + d->line = line; + + /* + * Jumper W3 Installed causes RTS,DTR, and SecTxD to be cleared on Device Reset or bus init. + * Jumper W3 is installed in factory DUPs, and in the KS10 config. + * Make sure the DUP emulation enables this option. + */ + dup_set_W3_option (dupidx, 1); + /* + * Reset the DUP device. This will clear DTR and RTS. + */ + if (dup_reset_dup (dupidx) != SCPE_OK) { + sim_debug (DF_CTO, &kmc_dev, "KMC%u line %u: BASE IN dup %d DUP TXCSR NXM\n", + k, line, dupidx); + d->kmc = -1; + return; + } + + /* Hardware requires a 2 usec delay before next access to DUP. + */ + + d->dupidx = dupidx; + + sim_debug (DF_INF, &kmc_dev, "KMC%u line %u: BASE IN DUP%u address=%06o 0x%05x assigned\n", + k, line, d->dupidx,csraddress, csraddress); + return; +} + +/* Process CONTROL IN command + * + * CONTROL IN establishes the characteristics of each communication line + * controlled by the KMC. At least one CONTROL IN must be issued for each + * DUP that is to communicate. + * + * CONTROL IN writes the DUP CSRs to configure it for the selected mode. + * + * Not implemented: + * o Polling count (no mapping to emulator) (SEL4_CI_POLL) + */ + +static void kmc_ctrlIn (int32 k, dupstate *d, int line) { + t_stat r; + + if (DEBUG_PRS (&kmc_dev)) { + sim_debug (DF_CMD, &kmc_dev, "KMC%u line %u: CONTROL IN ", k, line); + if (!(sel6 & SEL6_CI_ENABLE)) { + sim_debug (DF_CMD, &kmc_dev, "line disabled\n"); + } else { + sim_debug (DF_CMD, &kmc_dev, "enabled for %s in %s duplex", + (sel6 & SEL6_CI_DDCMP)? "DDCMP":"Bit-stuffing", + (sel6 & SEL6_CI_HDX)? "half" : "full"); + if (sel6 & SEL6_CI_ENASS) { + sim_debug (DF_CMD, &kmc_dev, " SS:%u", + (sel6 & SEL6_CI_SADDR), line); + } + sim_debug (DF_CMD, &kmc_dev, "\n"); + } + } + + /* BSEL4 has the polling count, which isn't needed for emulation */ + + d->linkstate &= ~(LINK_DSR|LINK_SEL);/* Initialize modem state reporting. */ + + d->ctrlFlags = sel6; + + /* Initialize DUP interface in the appropriate mode + * DTR will be turned on. + */ + r = dup_setup_dup (d->dupidx, (sel6 & SEL6_CI_ENABLE), + (sel6 & SEL6_CI_DDCMP), + (sel6 & SEL6_CI_NOCRC), + (sel6 & SEL6_CI_HDX), + (sel6 & SEL6_CI_ENASS) ? (sel6 & SEL6_CI_SADDR) : 0); + + /* If setup succeeded, enable packet callbacks. + * + * If setup failed, generate a CONTROL OUT. + */ + if (r == SCPE_OK) { + dup_set_callback_mode (d->dupidx, kdp_receive, kmc_txComplete, kmc_modemChange); + } else { + kmc_ctrlOut (k, SEL6_CO_NXM, 0, d->line, 0); + sim_debug (DF_CTO, &kmc_dev, "KMC%u line %u: CONTROL IN dup %d DUP CSR NXM\n", + k, line, d->dupidx); + } + + return; +} + +/* Process RX BUFFER IN command + * + * RX BUFFER IN delivers a buffer descriptor list from the host to + * the KMC. It may also deassign a buffer descriptor list. + * + * A buffer descriptor list is a sequential list of three word blocks in + * host memory space. Each 3-word block points to and describes the + * boundaries of a single buffer, which is also in host CPU memory. + * + * A buffer descriptor list is defined by its starting address. The + * end of a buffer descriptor list is marked in the list. A maximum of + * MAXQUEUE transmit and MAXQUEUE receive lists can be assigned to each + * COMM IOP-DUP communications line. + * + * The starting address of a buffer descriptor list must be word aligned. + * + * The buffers in this list will be filled with data received from the + * line and returned to the host by RX BUFFER OUT completions. + * + * Buffer descriptor lists are deassigned through use of the Kill bit + * and also reassigned when this bit is used in conjuntion with the + * Buffer Enable bit. When Kill is set, all buffer descriptor lists for + * the specified direction (RX or TX) are released. If the enable bit is + * also set, a new buffer descriptor list is assigned after the old lists + * are deassigned. In any case, RX kill places the associated DUP in sync + * search mode. TX kill brings the line to a mark hold state. + * + * Note that Buffer Enable is ignored unless Kill is also set. + * + */ + +void kmc_rxBufferIn(dupstate *d, int32 ba, uint32 sel6v) { + int32 k = d->kmc; + BDL *qe; + uint32 bda = 0; + UNIT *rxup; + + if (d->line == -1) + return; + + assert ((k >= 0) && (((unsigned int)k) < kmc_dev.numunits) && (d->dupidx != -1)); + + rxup = &rx_units[d->line][k]; + + if (!kmc_printBufferIn (k, &kmc_dev, d->line, TRUE, d->rxavail, ba, sel6v)) + return; + + if (sel6v & SEL6_BI_KILL) { + /* Kill all current RX buffers. + * Resync the DUP receiver. + */ +#ifdef DUP_RXRESYNC + dup_set_RCVEN (d->dupidx, FALSE); + dup_set_RCVEN (d->dupidx, TRUE); +#endif + if ((d->rxstate >= RXBUF) && (d->rxstate < RXFULL)) { + /* A bd is open (active). + * Updating bytes received should be done before the kill. TOPS-10 clears the UBA map + * before requesting it. But it doesn't look at bd. So don't report NXM. + * In these states, the bd is always cached. + */ + d->rx.bd[1] = d->rx.rcvc; + kmc_updateBDCount (d->rx.bda, d->rx.bd); + bda = d->rx.bda; + } else { + bda = 0; + } + d->rxstate = RXIDLE; + sim_cancel (rxup); + while ((qe = (BDL *)remqueue (d->rxqh.next, &d->rxavail)) != NULL) { + assert (insqueue (&qe->hdr, d->bdqh.prev, &d->bdavail, DIM(d->bdq))); + } + if (!(sel6v & SEL6_BI_ENABLE)) { + kmc_ctrlOut (k, SEL6_CO_KDONE, SEL2_IOT, d->line, bda); + return; + } + } + + /* Add new buffer to available for RX queue */ + + if ((qe = (BDL *)remqueue (d->bdqh.next, &d->bdavail)) == NULL) { + sim_debug (DF_ERR, &kmc_dev, "KMC%u line %u: Too many receive buffers from hostd\n", k, d->line); + kmc_halt (k, HALT_RCVOVF); + return; + } + qe->ba = ba; + assert (insqueue (&qe->hdr, d->rxqh.prev, &d->rxavail, MAXQUEUE)); + + if (sel6v & SEL6_BI_KILL) { /* KILL & Replace - ENABLE is set too */ + kmc_ctrlOut (k, SEL6_CO_KDONE, SEL2_IOT, d->line, bda); + } + + /* Start receiver if necessary */ + + if ((d->rxstate == RXIDLE) && !sim_is_active (rxup)) { + sim_activate_after (rxup, RXSTART_DELAY); + } + return; +} + +/* Message available callback + * + * The DUP calls this routine when a new message is available + * to be read. + * + * If the line's receive thread is idle, it is called to start the + * receive process. If the thread is busy, the message will be + * retrieved when the current receive process completes. + * + * This notification avoids the need for the receive thread to + * periodically poll for an incoming message when idle. + * + * The data and count arguments are unused - the function signature + * requires them for other modes. + */ + +static void kdp_receive(int dupidx, int count) { + int32 k; + dupstate* d; + UNIT *rxup; + UNUSED_ARG (count); + + assert ((dupidx >= 0) && (dupidx < DIM(dupState))); + d = &dupState[dupidx]; + assert (dupidx == d->dupidx); + k = d->kmc; + rxup = &rx_units[d->line][k]; + + if ((d->rxstate == RXIDLE) && !sim_is_active (rxup)) { + sim_activate_after (rxup, RXNEWBD_DELAY); + } + return; +} + +/* Process TX BUFFER IN command + * + * TX BUFFER IN delivers a buffer descriptor list from the host to + * the KMC. It may also deassign a buffer descriptor list. + * + * For a complete description of buffer descriptor list, see + * RX BUFFER IN. + * + * The buffers in this list contain data to be transmitted to the + * line, and are returned to the host by TX BUFFER OUT completions. + * + * TX buffer descriptors include control flags that indicate start + * and end of message. These determine when the DUP is told start/ + * stop accumulating data for and to insert CRCs. A resync flag + * indicates when sync characters must be inserted (after half-duplex + * line turn-around or CRC errors.) + * + */ + +void kmc_txBufferIn(dupstate *d, int32 ba, uint32 sel6v) { + int32 k = d->kmc; + BDL *qe; + + if (d->line == -1) + return; + + assert ((k >= 0) && (((unsigned int)k) < kmc_dev.numunits) && (d->dupidx != -1)); + + if (!kmc_printBufferIn (k, &kmc_dev, d->line, FALSE, d->txavail, ba, sel6v)) + return; + + if (sel6v & SEL6_BI_KILL) { + /* Kill all current TX buffers. The DUP can't abort transmission in simulation, so + * anything pending will stop when complete. The queue is reset here because + * the kill & replace option has to be able to enqueue the replacement BDL. + * If a tx is active, the DUP will issue a completion, which will report + * completion of the kill. A partial message that has been buffered, but + * not handed to the DUP can be stopped here. + */ + while ((qe = (BDL *)remqueue (d->txqh.next, &d->txavail)) != NULL) { + assert (insqueue (&qe->hdr, d->bdqh.prev, &d->bdavail, DIM(d->bdq))); + } + if (d->txstate < TXACT) { /* DUP is idle */ + sim_cancel (&tx_units[d->line][k]); /* Stop tx bdl walker */ + d->txstate = TXIDLE; + + if (!(sel6v & SEL6_BI_ENABLE)) { + kmc_ctrlOut (k, SEL6_CO_KDONE, 0, d->line, 0); + return; + } + /* Continue to replace buffer */ + } else { /* DUP is transmitting, defer */ + if (sel6v & SEL6_BI_ENABLE) /* Replace now, kill done later */ + d->txstate = TXKILR; + else { + d->txstate = TXKILL; + return; + } + } + } + + if (!(qe = (BDL *)remqueue (d->bdqh.next, &d->bdavail))) { + sim_debug (DF_ERR, &kmc_dev, "KMC%u line %u: Too many transmit buffers from host\n", k, d->line); + kmc_halt (k, HALT_XMTOVF); + return; + } + qe->ba = ba; + assert (insqueue (&qe->hdr, d->txqh.prev, &d->txavail, MAXQUEUE)); + if (d->txstate == TXIDLE) { + UNIT *txup = &tx_units[d->line][k]; + if (!sim_is_active (txup)) { + txup->wait = TXSTART_DELAY; + sim_activate_after (txup, txup->wait); + } + } + + return; +} + +/* Transmit complete callback from the DUP + * + * Called when the last byte of a packet has been transmitted. + * + * Handle any deferred kill and schedule the next message. + * + * Note that this may be called from within the txService when the + * DUP is not speed limited. The unconditional activation ensures + * that txService will not be called recursively. + */ + +static void kmc_txComplete (int32 dupidx, int status) { + dupstate *d; + UNIT *txup; + int32 k; + + assert ((dupidx >= 0) && (((size_t)dupidx) < DIM(dupState))); + + d = &dupState[dupidx]; + k = d->kmc; + txup = &tx_units[d->line][k]; + + if (status) { /* Failure is probably due to modem state change */ + kmc_updateDSR(d); /* Change does not stop transmission or release buffer */ + } + + if (d->txstate < TXACT) { /* DUP shouldn't call when KMC is not sending */ + sim_debug (DF_BUF, &kmc_dev, "KMC%u line %u: tx completion while inactive\n", + k, d->line); + return; + } + + d->txmlen = + d->txslen = 0; + if ((d->txstate == TXKILL) || (d->txstate == TXKILR)) { + /* If DUP could kill a partial transmission, would update bd here */ + d->txstate = TXDONE; + kmc_ctrlOut (k, SEL6_CO_KDONE, 0, d->line, d->tx.bda); + } else { + if (d->tx.bd[2] & BDL_LDS) + d->txstate = TXDONE; + else + d->txstate = TXSOM; + } + sim_cancel (txup); + sim_activate_after (txup, TXDONE_DELAY); + + return; +} + +/* Obtain a new buffer descriptor list from those queued by the host */ + +static t_bool kmc_txNewBdl(dupstate *d) { + BDL *qe; + + if (!(qe = (BDL *)remqueue (d->txqh.next, &d->txavail))) { + return FALSE; + } + d->tx.bda = qe->ba; + assert (insqueue (&qe->hdr, d->bdqh.prev, &d->bdavail, DIM(d->bdq))); + + d->tx.first = TRUE; + d->tx.bd[1] = 0; + return kmc_txNewBd(d); +} + +/* Obtain a new TX buffer descriptor. + * + * If the current descriptor is last, obtain a new list. + * (The new list case will recurse.) + * + */ + +static t_bool kmc_txNewBd(dupstate *d) { + int32 k = d->kmc; + + if (d->tx.first) + d->tx.first = FALSE; + else { + if (d->tx.bd[2] & BDL_LDS) { + if (!kmc_txNewBdl(d)) { + kmc_ctrlOut (k, SEL6_CO_TXU, 0, d->line, d->tx.bda); + return FALSE; + } + return TRUE; + } + d->tx.bda += 6; + } + if (Map_ReadW (d->tx.bda, 2*3, d->tx.bd)) { + kmc_ctrlOut (k, SEL6_CO_NXM, 0, d->line, d->tx.bda); + return FALSE; + } + + d->tx.ba = ((d->tx.bd[2] & BDL_XAD) << BDL_S_XAD) | d->tx.bd[0]; + return TRUE; +} + +/* Append data from a host buffer to the current message, as + * the DUP prefers to get the entire message in one swell foop. + */ + +static t_bool kmc_txAppendBuffer(dupstate *d) { + int32 k = d->kmc; + int32 rem; + + if (!d->txmsg || (d->txmsize < d->txmlen+d->tx.bd[1])) { + d->txmsize = d->txmlen+d->tx.bd[1]; + d->txmsg = (uint8 *)realloc(d->txmsg, d->txmsize); + assert (d->txmsg); + } + rem = Map_ReadB (d->tx.ba, d->tx.bd[1], d->txmsg+d->txmlen); + d->tx.bd[1] -= rem; + rem += kmc_updateBDCount (d->tx.bda, d->tx.bd); + if (rem) { + kmc_ctrlOut (k, SEL6_CO_NXM, 0, d->line, d->tx.bda); + return FALSE; + } + d->txmlen += d->tx.bd[1]; + return TRUE; +} + +/* Try to deliver a completion (OUTPUT command) + * + * Because the same CSRs are used for delivering commands to the KMC, + * the RDO and RDI bits, along with RQI arbitrate access to the CSRs. + * + * The KMC prioritizes completions over taking new commands. + * + * Thus, if RDO is set, the host has not taken the previous completion + * data from the CSRs. Output is not possible until the host clears RDO. + * + * If RDO is clear, RDI indicates that the host owns the CSRs, and + * should be writing a command to them. Output is not possible. + * + * If neither is set, the KMC takes ownership of the CSRs and updates + * them from the queue before setting RDO. + * + * There is aditional prioitization of RDI/RDO in the logic that detects + * RDO clearing. If RQI has been set by the host before clearing RDO, + * the KMC guarantees that RDI will set even if more completions are + * pending. + */ + +static void kmc_processCompletions (int32 k) { + CQ *qe; + + if (sel2 & (SEL2_RDO | SEL2_RDI)) /* CSRs available? */ + return; + + if (!(qe = (CQ *)remqueue (cqueueHead.next, &cqueueCount))) { + return; + } + + assert (insqueue (&qe->hdr, freecqHead.prev, &freecqCount, CQUEUE_MAX)); + sel2 = qe->bsel2; + sel4 = qe->bsel4; + sel6 = qe->bsel6; + + sim_debug (DF_QUE, &kmc_dev, "KMC%u line %u: %s %s delivered: sel2=%06o sel4=%06o sel6=%06o\n", + k, ((sel2 & SEL2_LINE)>>SEL2_V_LINE), + (sel2 & SEL2_IOT)? "RX":"TX", + ((sel2 & SEL2_CMD) == CMD_BUFFOUT)? "BUFFER OUT":"CONTROL OUT", + sel2, sel4, sel6); + + sel2 |= SEL2_RDO; + + kmc_updints (k); + + return; +} + +/* Queue a CONTROL OUT command to the host. + * + * All but one of these release one or more buffers to the host. + * + * code is the event (usually error) + * rx is TRUE for a receive buffer, false for transmit. + * line is the line number assigned by BASE IN + * bda is the address of the buffer descriptor that has been processed. + * + * Returns FALSE if the completion queue is full (a fatal error) + */ + +static void kmc_ctrlOut (int32 k, uint8 code, uint16 rx, uint8 line, uint32 bda) +{ + CQ *qe; + + if (DEBUG_PRS (kmc_dev)) { + static const char *const codenames[] = { + "Undef", "Abort", "HCRC", "DCRC", "NoBfr", "DSR", "NXM", "TXU", "RXO", "KillDun" }; + unsigned int idx = code; + idx = ((code < 06) || (code > 026))? 0: ((code/2)-2); + + sim_debug (DF_CTO, &kmc_dev, "KMC%u line %u: %s CONTROL OUT Code=%02o (%s) Address=%06o\n", + k, line, rx? "RX":"TX", code, codenames[idx], bda); + } + + if (!(qe = (CQ *)remqueue (freecqHead.next, &freecqCount))) { + sim_debug (DF_QUE, &kmc_dev, "KMC%u line %u: Completion queue overflow\n", k, line); + /* Set overflow status in last entry of queue */ + qe = (CQ *)cqueueHead.prev; + qe->bsel2 |= SEL2_OVR; + return; + } + qe->bsel2 = ((line << SEL2_V_LINE) & SEL2_LINE) | rx | CMD_CTRLOUT; + qe->bsel4 = bda & 0177777; + qe->bsel6 = ((bda >> (16-SEL6_V_CO_XAD)) & SEL6_CO_XAD) | code; + assert (insqueue (&qe->hdr, cqueueHead.prev, &cqueueCount, CQUEUE_MAX)); + kmc_processCompletions(k); + return; +} + +/* DUP device callback for modem state change. + * The DUP device provides this callback whenever + * any modem control signal changes state. + * + * The timing is not exact with respect to the data + * stream. + + * This can be used for HDX as well as DSR CHANGE> + * + */ +static void kmc_modemChange (int32 dupidx) { + dupstate *d; + + assert ((dupidx >= 0) && (((size_t)dupidx) < DIM(dupState))); + d = &dupState[dupidx]; + + if (d->dupidx != -1) { + kmc_updateDSR (d); + } + return; +} + +/* Check for and report DSR changes to the host. + * DSR is assumed false initially by the host. + * DSR Change Control-Out reports each change. + * No value is provided; the report simply toggles + * the host's view of the state. + * + * This is the ONLY CONTROL OUT that does not release + * a buffer. + * + * Returns TRUE if a change occurred. + */ +static t_bool kmc_updateDSR (dupstate *d) { + int32 k = d->kmc; + int32 status; + + status = dup_get_DSR(d->dupidx); + status = status? LINK_DSR : 0; + if (status ^ (d->linkstate & LINK_DSR)) { + d->linkstate = (d->linkstate &~LINK_DSR) | status; + kmc_ctrlOut (k, SEL6_CO_DSRCHG, 0, d->line, 0); + return TRUE; + } + return FALSE; +} + +/* Queue a BUFFER ADDRESS OUT command to the host. + * + * flags are applied to BSEL6 (e.g. receive EOM). + * rx is TRUE for a receive buffer, false for transmit. + * line is the line number assigned by BASE IN + * bda is the address of the buffer descriptor that has been processed. + * + * Returns FALSE if the completion queue is full (a fatal error) + */ + +static t_bool kmc_bufferAddressOut (int32 k, uint16 flags, uint16 rx, uint8 line, uint32 bda) { + CQ *qe; + + sim_debug (DF_BFO, &kmc_dev, "KMC%u line %u: %s BUFFER OUT Flags=%06o Address=%06o\n", + k, line, rx? "RX": "TX", flags, bda); + + if (!kmc_printBDL(k, DF_BFO, &kmc_dev, line, bda, rx? 6: 2)) + return FALSE; + if (!(qe = (CQ *)remqueue (freecqHead.next, &freecqCount))) { + sim_debug (DF_QUE, &kmc_dev, "KMC%u line %u: Completion queue overflow\n", k, line); + /* Set overflow status in last entry of queue */ + qe = (CQ *)cqueueHead.prev; + qe->bsel2 |= SEL2_OVR; + return FALSE; + } + qe->bsel2 = ((line << SEL2_V_LINE) & SEL2_LINE) | rx | CMD_BUFFOUT; + qe->bsel4 = bda & 0177777; + qe->bsel6 = ((bda >> (16-SEL6_V_CO_XAD)) & SEL6_CO_XAD) | flags; + assert (insqueue (&qe->hdr, cqueueHead.prev, &cqueueCount, CQUEUE_MAX)); + + kmc_processCompletions(k); + return TRUE; +} + +/* The UBA does not do a RPW cycle when byte 0 (of 4) + * on a -10 is written. (It can, but the OS doesn't program it that + * way. Thus, if the count word is in the left half-word, updating + * it will trash the 3rd word of that buffer descriptor. The hardware + * works. The KMC microcode does a word NPR write. At this writing + * how the hardware works is a mystery. The UBA documentation is quite + * clear that a write is done; the prints show zeros muxed into the bits. + * The OS is NOT setting 'read reverse'. The KMC ucode is not doing + * any magic. And the OS will kill the KMC if the 3rd word of the BD + * is trashed. Unless there is an undiscovered ECO to the KS10, there + * is no explanation for the fact that the KDP does work on that machine. + * Certainly, if the UBA always did RPW cycles, this would work, and + * probably no one would notice the performance loss. + * + * For now, this code writes the count, and also the 3rd word if the + * count is in byte 0. It's not the right solution, but it works for now. + * This does an extra write if the UBA trashed the following word, and not + * otherwise. Note that any BDL with two buffers is guaranteed to + * run into this issue. If the first BD is in byte 0, its count is OK, but + * the following BD will start in byte 2, putting its count in byte 0 of + * the following word, causing the write to step on that bd's flags. + */ + +static int32 kmc_updateBDCount(uint32 bda, uint16 *bd) { + + return Map_WriteW (bda+2, (((bda+2) & 2)? 2 : 4), &bd[1]); +} + +/* Halt a KMC. This happens for some errors that the real KMC + * may not detect, as well as when RUN is cleared. + * The kmc is halted & interrupts are disabled. + */ + +static void kmc_halt (int32 k, int error) { + int line; + + if (error){ + sel0 &= ~(SEL0_IEO|SEL0_IEI); + } + sel0 &= ~SEL0_RUN; + + kmc_updints (k); + for (line = 0; line < MAX_ACTIVE; line++) { + UNIT *up = &tx_units[line][k]; + + if (sim_is_active (up)) { + up->unit_htime = sim_activate_time (up); + sim_cancel (up); + } else { + up->unit_htime = 0; + } + up = &rx_units[line][k]; + if (sim_is_active (up)) { + up->unit_htime = sim_activate_time (up); + sim_cancel (up); + } else { + up->unit_htime = 0; + } + } + sim_debug (DF_INF, &kmc_dev, "KMC%u: Halted at uPC %04o reason=%d\n", k, upc, error); + return; +} +/* + * Update interrupts pending. + * + * Since the interrupt request is shared across all KMCs + * (a simplification), pending flags are kept per KMC, + * and a global request count across KMCs for the UBA. + * The UBA will clear the global request flag when it grants + * an interrupt; thus for set the global flag is always set + * if this KMC has a request. This doesn't quite match "with IEI + * set, only one interrupt is generated for each setting of RDI." + * An extra interrupt, however, should be harmless. + * + * Since interrupts are generated by microcode, do not touch the interrupt + * system unless microcode initialization has run. + */ + +static void kmc_updints(int32 k) { + if (!(gflags & FLG_UCINI)) { + return; + } + + if ((sel0 & SEL0_IEI) && (sel2 & SEL2_RDI)) { + if (!(gflags & FLG_AINT)) { + sim_debug (DF_INT, &kmc_dev, "KMC%u: set input interrupt pending\n", k); + gflags |= FLG_AINT; + AintPending++; + } + SET_INT(KMCA); + } else { + if (gflags & FLG_AINT) { + sim_debug (DF_INT, &kmc_dev, "KMC%u: cleared pending input interrupt\n", k); + gflags &= ~FLG_AINT; + if (--AintPending == 0) { + CLR_INT(KMCA); + } + } + } + + if ((sel0 & SEL0_IEO) && (sel2 & SEL2_RDO)) { + if (!(gflags & FLG_BINT)) { + sim_debug (DF_INT, &kmc_dev, "KMC%u: set output interrupt\n", k); + gflags |= FLG_BINT; + BintPending++; + } + SET_INT(KMCB); + } else { + if (gflags & FLG_BINT) { + sim_debug (DF_INT, &kmc_dev, "KKMC%u: clear output interrupt\n", k); + gflags &= ~FLG_BINT; + if (--BintPending == 0) { + CLR_INT(KMCB); + } + } + } + return; +} + +/* Interrupt acknowledge service. + * When the UBA grants an interrupt request, it + * requests the vector number from the device. + * + * These routines return the vector number from the + * interrupting KMC. Lower numbered KMCs have + * priority over higher numbered KMCs. + * + * Any one KMC should never have both input and output + * pending at the same time. + */ + +static int32 kmc_AintAck (void) { + int32 vec = 0; /* no interrupt request active */ + int32 k; + + for (k = 0; ((size_t)k) < DIM (kmc_gflags); k++) { + if (gflags & FLG_AINT) { + vec = kmc_dib.vec + (k*010); + gflags &= ~FLG_AINT; + if (--AintPending == 0) { + CLR_INT(KMCA); + } + break; + } + } + + if (vec) + sim_debug (DF_INT, &kmc_dev, "KMC%u input (A) interrupt ack vector %03o\n", k, vec); + else + sim_debug (DF_INT, &kmc_dev, "KMC input (A) passive release\n"); + + return vec; +} + +static int32 kmc_BintAck (void) { + int32 vec = 0; /* no interrupt request active */ + int32 k; + + for (k = 0; ((size_t)k) < DIM (kmc_gflags); k++) { + if (gflags & FLG_BINT) { + vec = kmc_dib.vec + 4 + (k*010); + gflags &= ~FLG_BINT; + if (--BintPending == 0) { + CLR_INT(KMCB); + } + break; + } + } + + if (vec) + sim_debug (DF_INT, &kmc_dev, "KMC%u output (B) interrupt ack vector %03o\n", k, vec); + else + sim_debug (DF_INT, &kmc_dev, "KMC output (B) passive release\n"); + + return vec; +} + +/* Debug: Log a BUFFER IN or BUFFER OUT command. + * returns FALSE if print encounters a NXM as (a) it's fatal and + * (b) only one completion per bdl. + */ + +static t_bool kmc_printBufferIn (int32 k, DEVICE *dev, int32 line, t_bool rx, + int32 count, int32 ba, uint16 sel6v) { + t_bool kill = ((sel6v & (SEL6_BI_KILL|SEL6_BI_ENABLE)) == SEL6_BI_KILL); + const char *dir = rx? "RX": "TX"; + + sim_debug (DF_CMD, &kmc_dev, "KMC%u line %u: %s BUFFER IN%s %d, bdl=%06o 0x%04x\n", k, line, dir, + (kill? "(Buffer kill)": ((sel6v & SEL6_BI_KILL)? "(Kill & replace)": "")), + count, ba, ba); + + if (kill) /* Just kill doesn't use BDL, may NXM if attempt to dump */ + return TRUE; + + /* Kill and replace supplies new BDL */ + + if (!kmc_printBDL(k, DF_CMD, dev, line, ba, rx? 5: 1)) + return FALSE; + + sim_debug (DF_QUE, &kmc_dev, "KMC%u line %u: %s BUFFER IN %d, bdl=%06o 0x%04X\n", k, line, dir, count, ba, ba); + + return TRUE; +} +/* + * Debug: Dump a BDL and a sample of its buffer. + * + * prbuf: non-zero to access buffer + * Bit 1 set to print just one descriptor (BFO), clear to do entire list (BFI) + * Bit 2 set if rx (rx bfi doesn't print data) + */ + +static t_bool kmc_printBDL(int32 k, uint32 dbits, DEVICE *dev, uint8 line, int32 ba, int prbuf) { + uint16 bd[3]; + int32 dp; + + if (!DEBUG_PRJ(dev,dbits)) + return TRUE; + + for (;;) { + if (Map_ReadW (ba, 3*2, bd) != 0) { + kmc_ctrlOut (k, SEL6_CO_NXM, 0, line, ba); + sim_debug (dbits,dev, "KMC%u line %u: NXM reading descriptor addr=%06o\n", k, line, ba); + return FALSE; + } + + dp = bd[0] | ((bd[2] & BDL_XAD) << BDL_S_XAD); + sim_debug (dbits, dev, " bd[0] = %06o 0x%04X Adr=%06o\n", bd[0], bd[0], dp); + sim_debug (dbits, dev, " bd[1] = %06o 0x%04X Len=%u.\n", bd[1], bd[1], bd[1]); + sim_debug (dbits, dev, " bd[2] = %06o 0x%04X", bd[2], bd[2]); + if (bd[2] & BDL_LDS) { + sim_debug (dbits, dev, " Last"); + } + if (bd[2] & BDL_RSY) { + sim_debug (dbits, dev, " Rsync"); + } + if (bd[2] & BDL_EOM) { + sim_debug (dbits, dev, " XEOM"); + } + if (bd[2] & BDL_SOM) { + sim_debug (dbits, dev, " XSOM"); + } + sim_debug (dbits, dev, "\n"); + + if (prbuf) { + uint8 buf[20]; + if (bd[1] > sizeof buf) + bd[1] = sizeof buf; + + if (Map_ReadB (dp, bd[1], buf) != 0) { + kmc_ctrlOut (k, SEL6_CO_NXM, 0, line, dp); + sim_debug (dbits, dev, "KMC%u line %u: NXM reading buffer %06o\n", k, line, dp); + return FALSE; + } + + if (prbuf != 5) { /* Don't print RX buffer in */ + for (dp = 0; dp < bd[1] ; dp++) { + sim_debug (dbits, dev, " %02x", buf[dp]); + } + sim_debug (dbits, dev, "\r\n"); + } + } + if ((bd[2] & BDL_LDS) || (prbuf & 2)) /* Last, or 1 only */ + break; + ba += 6; + } + 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 + * V1.0A microcode from sources and computing its CRC from the binary. + * + * RSX DECnet has a different binary; the version is unknown. + * + * The reason for this check is that there ARE other KMC microcodes. + * If some software thinks it's loading one of those, the results + * could be catastrophic - and hard to diagnose. (COMM IOP-DZ has + * a similar function; but there are other, stranger microcodes. + */ + +static const char *kmc_verifyUcode (int32 k) { + size_t i, n; + uint16 crc = 'T' << 8 | 'L'; + uint8 w[2]; + static const struct { + uint16 crc; + const char *name; + } known[] = { + { 0xc3cd, "COMM IOP-DUP V1.0A" }, + { 0x1a38, "COMM IOP-DUP RSX" }, + }; + + for (i = 0, n = 0; i < DIM (ucode); i++) { + if (ucode[i] != 0) + n++; + w[0] = ucode[i] >> 8; + w[1] = ucode[i] & 0xFF; + crc = ddcmp_crc16 (crc, &w, sizeof w); + } + if (n < ((3 * DIM (ucode))/4)) { + sim_debug (DF_INF, &kmc_dev, "KMC%u: Microcode not loaded\n", k); + return NULL; + } + for (i = 0; i < DIM (known); i++) { + if (crc == known[i].crc) { + sim_debug (DF_INF, &kmc_dev, "KMC%u: %s microcode loaded\n", k, known[i].name); + return known[i].name; + } + } + sim_debug (DF_INF, &kmc_dev, "KMC%u: Unknown microcode loaded\n", k); + return NULL; +} + +/* Initialize a queue to empty. + * + * Optionally, adds a list of elements to the queue. + * max, list and size are only used if list is non-NULL. + * + * Convenience macros: + * MAX_LIST_SIZE(q) specifies max, list and size for an array of elements q + * INIT_HDR_ONLY provides placeholders for these arguments when only the + * header and count are to be initialized. + */ + +static void initqueue (QH *head, int32 *count, int32 max, void *list, size_t size) { + head->next = head->prev = head; + *count = 0; + if (list == NULL) + return; + while (insqueue ((QH *)list, head->prev, count, max)) + list = (QH *)(((char *)list)+size); + return; +} + +/* Insert entry on queue after pred, if count < max. + * Increment count. + * To insert at head of queue, specify &head for predecessor. + * To insert at tail, specify head.pred + * + * returns FALSE if queue is full. + */ + +static t_bool insqueue (QH *entry, QH *pred, int32 *count, int32 max) { + if (*count >= max) + return FALSE; + entry-> next = pred->next; + entry->prev = pred; + pred->next->prev = entry; + pred->next = entry; + ++*count; + return TRUE; +} + +/* Remove entry from queue. + * Decrement count. + * To remove from head of queue, specify head.next. + * To remove form tail of queue, specify head.pred. + * + * returns FALSE if queue is empty. + */ + +static void *remqueue (QH *entry, int32 *count) { + if (*count <= 0) + return NULL; + entry->prev->next = entry->next; + entry->next->prev = entry->prev; + --*count; + return (void *)entry; +} + +/* Simulator UI functions */ + +/* SET KMC DEVICES processor + * + * Adjusts the size of I/O space and number of vectors to match the specified + * number of KMCs. + * The txup is that of unit zero. + */ + +#if KMC_UNITS > 1 +static t_stat kmc_setDeviceCount (UNIT *txup, int32 val, char *cptr, void *desc) { + int32 newln; + uint32 dupidx; + t_stat r; + DEVICE *dptr = find_dev_from_unit(txup); + + if (cptr == NULL) + return SCPE_ARG; + + for (dupidx = 0; dupidx < DIM (dupState); dupidx++) { + dupstate *d = &dupState[dupidx]; + if ((d->kmc != -1) || (d->dupidx != -1)) { + return SCPE_ALATT; + } + } + newln = (int32) get_uint (cptr, 10, KMC_UNITS, &r); + if ((r != SCPE_OK) || (newln == dptr->numunits)) + return r; + if (newln == 0) + return SCPE_ARG; + kmc_dib.lnt = newln * IOLN_KMC; /* set length */ + kmc_dib.vnum = newln * 2; /* set vectors */ + dptr->numunits = newln; + return kmc_reset (dptr); /* setup devices and auto config */ +} +#endif + +/* Report number of configured KMCs */ + +#if KMC_UNITS > 1 +static t_stat kmc_showDeviceCount (FILE *st, UNIT *txup, int32 val, void *desc) { + DEVICE *dev = find_dev_from_unit(txup); + + if (dev->flags & DEV_DIS) { + fprintf (st, "Disabled"); + return SCPE_OK; + } + + fprintf (st, "devices=%d", dev->numunits); + return SCPE_OK; +} +#endif + +/* Set line speed + * + * This is the speed at which the KMC processed data for/from a DUP. + * This limits the rate at which buffer descriptors are processed, even + * if the actual DUP speed is faster. If the DUP speed is slower, of + * course the DUP wins. This limit ensures that the infinite speed DUPs + * of simulation won't overrun the host's ability to process buffers. + * + * The speed range is expressed as line bits/sec for user convenience. + * The limits are 300 bps (a sync line won't realistically run slower than + * that), and 1,000,000 bps - the maximum speed of a DMR11. The KDP's + * practical limit was about 19,200 BPS. The higher limit is a rate that + * the typicaly host software could handle, even if the line couldn't. + * + * Note that the DUP line speed can also be set. + * + * To allow setting the speed before a DUP has been assigned to a KMC by + * the OS, the set (and show) commands reference the DUP and apply to any + * potential use of that DUP by a KMC. + */ + +static t_stat kmc_setLineSpeed (UNIT *txup, int32 val, char *cptr, void *desc) { + dupstate *d; + int32 dupidx, newspeed; + char gbuf[CBUFSIZE]; + t_stat r; + + if (!cptr || !*cptr) + return SCPE_ARG; + + cptr = get_glyph (cptr, gbuf, '='); /* get next glyph */ + if (*cptr == 0) /* should be speed */ + return SCPE_2FARG; + dupidx = (int32) get_uint (gbuf, 10, DUP_LINES, &r); /* Parse dup # */ + if ((r != SCPE_OK) || (dupidx < 0)) /* error? */ + return SCPE_ARG; + + cptr = get_glyph (cptr, gbuf, 0); /* get next glyph */ + if (*cptr != 0) /* should be end */ + return SCPE_2MARG; + cptr = gbuf; + if (!strcmp (cptr, "DUP")) + cptr += 3; + newspeed = (int32) get_uint (cptr, 10, MAX_SPEED, &r); + if ((r != SCPE_OK) || (newspeed < 300)) /* error? */ + return SCPE_ARG; + + d = &dupState[dupidx]; + d->linespeed = newspeed; + return SCPE_OK; +} + +static t_stat kmc_showLineSpeed (FILE *st, UNIT *txup, int32 val, void *desc) { + int dupidx; + + fprintf (st, "DUP KMC Line Speed\n" + "--- --- ---- --------\n"); + + for (dupidx =0; dupidx < DUP_LINES; dupidx++) { + dupstate *d = &dupState[dupidx]; + + fprintf (st, "%3u ", dupidx); + + if (d->kmc == -1) { + fprintf (st, " - - "); + } else { + fprintf (st, "%3u %3u", d->kmc, d->line); + } + fprintf (st, " %8u\n", d->linespeed); + } + + 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) { + int32 k = up->unit_kmc; + int32 line; + t_bool first = TRUE; + DEVICE *dev = find_dev_from_unit(up); + const char *ucname; + + if ((dev->flags & DEV_DIS) || (((uint32)k) >= dev->numunits)) { + fprintf (st, "KMC%u Disabled\n", k); + return SCPE_OK; + } + + ucname = kmc_verifyUcode (k); + + fprintf (st, "KMC%u ", k); + + if (!(sel0 & SEL0_RUN)) { + fprintf (st, "%s halted at uPC %04o\n", + (ucname?ucname: "(No or unknown microcode)"), upc); + return SCPE_OK; + } + + fprintf (st, "%s is running at uPC %04o\n", + (ucname?ucname: "(No or unknown microcode)"), upc); + + if (!(gflags & FLG_UCINI)) { + return SCPE_OK; + } + + for (line = 0; line <= MAX_LINE; line++) { + dupstate *d = line2dup[line]; + if (d->kmc == k) { + if (first) { + fprintf (st, " Line DUP CSR State\n"); + first = FALSE; + } + fprintf (st, " %3u %3u %06o %-8s %3s %s %s %s", + line, d->dupidx, d->dupcsr, + (d->ctrlFlags & SEL6_CI_ENABLE)? "enabled": "disabled", + (d->linkstate & LINK_DSR)? "DSR" : "OFF", + (d->ctrlFlags & SEL6_CI_DDCMP)? "DDCMP" : "Bit-Stuff", + (d->ctrlFlags & SEL6_CI_HDX)? "HDX " : "FDX", + (d->ctrlFlags & SEL6_CI_NOCRC)? "NOCRC": ""); + if (d->ctrlFlags & SEL6_CI_ENASS) + fprintf (st, " SS (%u) ", d->ctrlFlags & SEL6_CI_SADDR); + fprintf (st, "\n"); + } + } + if (first) + fprintf (st, " No DUPs assigned\n"); + + return SCPE_OK; +} + +/* Help for this device. + * + */ + +static t_stat kmc_help (FILE *st, struct sim_device *dptr, + struct sim_unit *uptr, int32 flag, char *cptr) { + const char *const text = +" The KMC11-A is a general purpose microprocessor that is used in\n" +" several DEC products. The KDP is an emulation of one of those\n" +" products: COMM IOP-DUP.\n" +"\n" +" The COMM IOP-DUP microcode controls and supervises 1 - 16 DUP-11\n" +" synchronous communications line interfaces, providing scatter/gather\n" +" DMA, message framing, modem control, CRC validation, receiver\n" +" resynchronization, and address recognition.\n" +"\n" +" The DUP-11 lines are assigned to the KMC11 by the (emulated) operating\n" +" system, but SimH must be told how to connect them. See the DUP HELP\n" +" for details.\n" +"1 Hardware Description\n" +" The KMC11-A microprocessor is a 16-bit Harvard architecture machine\n" +" optimized for data movement, character processing, address arithmetic\n" +" and other functions necessary for controlling I/O devices. It resides\n" +" on the UNIBUS and operates in parallel with the host CPU with a cycle\n" +" time of 300 nsec. It contains a 1024 word writable control store that\n" +" is loaded by the host, 1024 words of data memory, 16 8-bit scratchpad\n" +" registers, and 8 bytes of RAM that are dual-ported between the KMC11\n" +" and UNIBUS I/O space. It also has a timer and various internal busses\n" +" and registers.\n" +"\n" +" Seven of the eight bytes of dual-ported RAM have no fixed function;\n" +" they are defined by the microcode. The eighth register allows the\n" +" host to control the KMC11: the host can start, stop, examine state and\n" +" load microcode using this register.\n" +"\n" +" The microprocessor is capable of initiating DMA (NPR) UNIBUS cycles to\n" +" any UNIBUS address (memory and I/O space). It can interrupt the host\n" +" via one of two interrupt vectors.\n" +"\n" +" The microcodes operate other UNIBUS devices by reading and writing\n" +" their CSRs with UNIBUS DMA transactions, typically on a\n" +" character-by-character basis. There is no direct connection between\n" +" the KMC11 and the peripherals that it controls. The controlled\n" +" devices do not generate interrupts; all interrupts are generated by\n" +" the KMC11, which monitors the devices by polling their CSRs.\n" +"\n" +" By presenting the character-oriented peripherals to the host as\n" +" message-oriented devices, the KMC11 reduces the host's overhead in\n" +" operating the peripherals, relaxes the required interrupt response\n" +" times and increases the potential I/O throughput of a system.\n" +"\n" +" The hardware also has a private bus that can be used to control\n" +" dedicated peripherals (such as a DMC11 synchronous line unit) without\n" +" UNIBUS transactions. This feature is not emulated.\n" +"\n" +" This emulation does not execute the KMC microcode, but rather provides\n" +" a functional emulation.\n" +"\n" +" However, some of the microcode operators are emulated because system\n" +" loaders and OS diagnostics execute single instructions to initialize\n" +" or diagnose the device.\n" +"2 $Registers\n" +"2 Related devices\n" +" Other versions of the KMC11 have ROM microcode, which are used in such\n" +" devices as the DMC11 and DMR11 communications devices. This emulation\n" +" does not support those versions.\n" +"\n" +" Microcodes, not supported by this emulation, exist which control other\n" +" UNIBUS peripherals in a similar manner. These include:\n" +"\n" +"+DMA for DZ11 asynchronous lines (COMM IOP-DZ)\n" +"+DMA for line printers\n" +"+Arpanet IMP interface (AN22 on the KS10/TOPS-20)\n" +"\n" +" The KMC11 was also embedded in other products, such as the DX20 Massbus\n" +" to IBM channel adapter.\n" +"\n" +" The KMC11-B is an enhanced version of the KMC11-A. Note that microcode\n" +" loading is handled differently in that version, which is NOT emulated.\n" +"1 Configuration\n" +" Most configuration of KDP lines is done by the host OS and by SimH\n" +" configuration of the DUP11 lines.\n" +"\n" +#if KMC_TROLL +" The KDP has two configurable parameters.\n" +#else +" The KDP has one configurable parameter.\n" +#endif +" Line speed - this is the speed at which each communication line\n" +" operates. The DUP11's line speed should be set to 'unlimited' to\n" +" avoid unpredictable interactions.\n" +#if KMC_TROLL +" Troll - the KDP emulation includes a process that will intentionally\n" +" drop or corrupt some messages. This emulates the less-than-perfect\n" +" communications lines encountered in the real world, and enables\n" +" network monitoring software to see non-zero error counters.\n" +"\n" +" The troll selects messages with a probablility selected by the SET\n" +" TROLL 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.\n" +#endif +"2 $Set commands\n" +#if KMC_UNITS > 1 +" SET KDP DEVICES=n enables emulation of up to %1s KMC11s.\n" +#endif +"2 $Show commands\n" +"1 Operation\n" +" A KDP device consists of one or more DUP11s controlled by a KMC11.\n" +" The association of DUP11s to KMC11s is determined by the host OS.\n" +"\n" +" For RSX DECnet, use NCP:\n" +" +SET LINE KDP-kdp-line CSR address\n" +" +SET LINE KDP-kdp-line UNIT CSR address\n" +" where 'kdp' is the KDP number and 'line' is the line number on\n" +" that kdp. 'address' is the I/O page offset of the CSR; e.g.\n" +" 760050 is entered as 160050.\n" +"\n" +" For TOPS-10/20, the addresses are fixed.\n" +"\n" +" For VMS...\n" +"\n" +" Although the microcode is not directly executed by the emulated KMC11,\n" +" the correct microcode must be loaded by the host operating system.\n" +; + char kmc_units[10]; + + sprintf (kmc_units, "%u", KMC_UNITS); + + return scp_help (st, dptr, uptr, flag, text, cptr, kmc_units); +} + +/* Description of this device. + * Conventionally last function in the file. + */ +static char *kmc_description (DEVICE *dptr) { + return "KMC11-A Synchronous line controller supporting only COMM IOP/DUP microcode"; +} diff --git a/PDP11/pdp11_sys.c b/PDP11/pdp11_sys.c index 75be6ea1..4113b80e 100644 --- a/PDP11/pdp11_sys.c +++ b/PDP11/pdp11_sys.c @@ -108,9 +108,10 @@ extern DEVICE xq_dev, xqb_dev; extern DEVICE xu_dev, xub_dev; extern DEVICE ke_dev; extern DEVICE kg_dev; -extern DEVICE dmc_dev[]; +extern DEVICE dmc_dev; extern DEVICE dup_dev; extern DEVICE dpv_dev; +extern DEVICE kmc_dev; extern UNIT cpu_unit; extern REG cpu_reg[]; extern uint16 *M; @@ -180,12 +181,10 @@ DEVICE *sim_devices[] = { &xub_dev, &ke_dev, &kg_dev, - &dmc_dev[0], - &dmc_dev[1], - &dmc_dev[2], - &dmc_dev[3], + &dmc_dev, &dup_dev, &dpv_dev, + &kmc_dev, NULL }; diff --git a/README.md b/README.md index 7e6cbca8..f7ea35ea 100644 --- a/README.md +++ b/README.md @@ -42,7 +42,9 @@ A remote console session will close when an EOF character is entered (i.e. ^D or RQ device has a settable controller type (RQDX3, UDA50, KLESI, RUX50) RQ disks default to Autosize without regard to disk type RQ disks on PDP11 can have RAUSER size beyond 2GB - DMC11 DDCMP DECnet device simulation from Rob Jarratt. Up to 4 DMC11 devices are supported. + DMC11/DMR11 DDCMP DECnet device simulation. Up to 8 DMC devices are supported. + KDP11 on PDP11 for DECnet + DUP11 on PDP11 for DECnet connectivity to talk to DMC, KDP or other DUP devices DZ on Unibus systems can have up to 256 ports (default of 32), on Qbus systems 128 port limit (default of 16). DZ devices optionally support full modem control (and port speed settings @@ -54,6 +56,9 @@ A remote console session will close when an EOF character is entered (i.e. ^D or MicroVAX I has a SET CPU MODEL=(MicroVAX|VAXSTATION) command to change between system types MicroVAX II has a SET CPU MODEL=(MicroVAX|VAXSTATION) command to change between system types +#### PDP10 Enhancements + KDP11 (from Timothe Litt) for DECnet connectivity to simulators with DMC, DUP or KDP devices + #### Terminal Multiplexer additions Added support for TCP connections using IPv4 and/or IPv6. Logging - Traffic going out individual lines can be optionally logged to diff --git a/VAX/vax730_syslist.c b/VAX/vax730_syslist.c index 3aee3c09..5e7cf395 100644 --- a/VAX/vax730_syslist.c +++ b/VAX/vax730_syslist.c @@ -52,8 +52,7 @@ extern DEVICE tq_dev; extern DEVICE dz_dev; extern DEVICE vh_dev; extern DEVICE xu_dev, xub_dev; -extern DEVICE dmc_dev[]; -extern DEVICE dup_dev; +extern DEVICE dmc_dev; extern UNIT cpu_unit; extern void WriteB (uint32 pa, int32 val); @@ -85,11 +84,7 @@ DEVICE *sim_devices[] = { &tq_dev, &xu_dev, &xub_dev, - &dmc_dev[0], - &dmc_dev[1], - &dmc_dev[2], - &dmc_dev[3], - &dup_dev, + &dmc_dev, NULL }; diff --git a/VAX/vax750_syslist.c b/VAX/vax750_syslist.c index 4e378a0a..bb544ace 100644 --- a/VAX/vax750_syslist.c +++ b/VAX/vax750_syslist.c @@ -54,8 +54,7 @@ extern DEVICE tu_dev; extern DEVICE dz_dev; extern DEVICE vh_dev; extern DEVICE xu_dev, xub_dev; -extern DEVICE dmc_dev[]; -extern DEVICE dup_dev; +extern DEVICE dmc_dev; extern UNIT cpu_unit; extern void WriteB (uint32 pa, int32 val); @@ -90,11 +89,7 @@ DEVICE *sim_devices[] = { &tq_dev, &xu_dev, &xub_dev, - &dmc_dev[0], - &dmc_dev[1], - &dmc_dev[2], - &dmc_dev[3], - &dup_dev, + &dmc_dev, NULL }; diff --git a/VAX/vax780_syslist.c b/VAX/vax780_syslist.c index 28bf7a50..8cec8ef8 100644 --- a/VAX/vax780_syslist.c +++ b/VAX/vax780_syslist.c @@ -54,8 +54,7 @@ extern DEVICE tu_dev; extern DEVICE dz_dev; extern DEVICE vh_dev; extern DEVICE xu_dev, xub_dev; -extern DEVICE dmc_dev[]; -extern DEVICE dup_dev; +extern DEVICE dmc_dev; extern UNIT cpu_unit; extern void WriteB (uint32 pa, int32 val); @@ -92,11 +91,7 @@ DEVICE *sim_devices[] = { &tq_dev, &xu_dev, &xub_dev, - &dmc_dev[0], - &dmc_dev[1], - &dmc_dev[2], - &dmc_dev[3], - &dup_dev, + &dmc_dev, NULL }; diff --git a/VAX/vax860_syslist.c b/VAX/vax860_syslist.c index 49d7db0f..882fd5ce 100644 --- a/VAX/vax860_syslist.c +++ b/VAX/vax860_syslist.c @@ -54,8 +54,7 @@ extern DEVICE tu_dev; extern DEVICE dz_dev; extern DEVICE vh_dev; extern DEVICE xu_dev, xub_dev; -extern DEVICE dmc_dev[]; -extern DEVICE dup_dev; +extern DEVICE dmc_dev; extern UNIT cpu_unit; extern void WriteB (uint32 pa, int32 val); @@ -90,11 +89,7 @@ DEVICE *sim_devices[] = { &tq_dev, &xu_dev, &xub_dev, - &dmc_dev[0], - &dmc_dev[1], - &dmc_dev[2], - &dmc_dev[3], - &dup_dev, + &dmc_dev, NULL }; diff --git a/Visual Studio Projects/PDP10.vcproj b/Visual Studio Projects/PDP10.vcproj index e933830b..6cba1b42 100644 --- a/Visual Studio Projects/PDP10.vcproj +++ b/Visual Studio Projects/PDP10.vcproj @@ -239,6 +239,10 @@ RelativePath="..\PDP11\pdp11_cr.c" > + + @@ -247,6 +251,10 @@ RelativePath="..\PDP11\pdp11_dz.c" > + + diff --git a/Visual Studio Projects/PDP11.vcproj b/Visual Studio Projects/PDP11.vcproj index 8c256597..45aa4795 100644 --- a/Visual Studio Projects/PDP11.vcproj +++ b/Visual Studio Projects/PDP11.vcproj @@ -255,6 +255,10 @@ RelativePath="..\PDP11\pdp11_kg.c" > + + diff --git a/Visual Studio Projects/VAX730.vcproj b/Visual Studio Projects/VAX730.vcproj index b2f77bc2..23300fc9 100644 --- a/Visual Studio Projects/VAX730.vcproj +++ b/Visual Studio Projects/VAX730.vcproj @@ -206,10 +206,6 @@ RelativePath="..\PDP11\pdp11_dmc.c" > - - @@ -388,7 +384,7 @@ > - - @@ -396,7 +392,7 @@ > - - @@ -408,7 +404,7 @@ > - - @@ -400,7 +396,7 @@ >