/* vax4xx_dz.c: Built-in DZ terminal multiplexor simulator Copyright (c) 2019, Matt Burke This module incorporates code from SimH, Copyright (c) 2001-2008, Robert M Supnik 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(S) 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(s) of the author(s) 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(s). dz DZ terminal multiplexor */ #include "vax_defs.h" #include "sim_sock.h" #include "sim_tmxr.h" #include "vax_lk.h" #include "vax_vs.h" #define DZ_LINES 4 #define DZ_LNOMASK (DZ_LINES - 1) /* mask for lineno */ #define DZ_LMASK ((1 << DZ_LINES) - 1) /* mask of lines */ #define DZ_SILO_ALM 16 /* silo alarm level */ /* line functions */ #define DZ_TMXR 0 #define DZ_CONSOLE 1 #define DZ_KEYBOARD 2 #define DZ_MOUSE 3 /* DZCSR - 200A0000 - control/status register */ #define CSR_MAINT 0x0008 /* maint - NI */ #define CSR_CLR 0x0010 /* clear */ #define CSR_MSE 0x0020 /* master scan enb */ #define CSR_RDONE 0x0080 /* rcv done - RO */ #define CSR_V_TLINE 8 /* xmit line - RO */ #define CSR_TLINE (DZ_LNOMASK << CSR_V_TLINE) #define CSR_SAE 0x1000 /* silo alm enb */ #define CSR_SA 0x2000 /* silo alm - RO */ #define CSR_TRDY 0x8000 /* xmit rdy - RO */ #define CSR_RW (CSR_MAINT | CSR_MSE | CSR_SAE) #define CSR_MBZ (0xC07 | CSR_CLR) #define CSR_GETTL(x) (((x) >> CSR_V_TLINE) & DZ_LNOMASK) #define CSR_PUTTL(x,y) x = ((x) & ~CSR_TLINE) | (((y) & DZ_LNOMASK) << CSR_V_TLINE) BITFIELD dz_csr_bits[] = { BITNCF(3), /* not used */ BIT(MAINT), /* Maint */ BIT(CLR), /* clear */ BIT(MSE), /* naster scan enable */ BITNCF(1), /* not used */ BIT(RDONE), /* receive done */ BITF(TLINE,2), /* transmit line */ BITNCF(2), /* not used */ BIT(SAE), /* silo alarm enable */ BIT(SA), /* silo alarm */ BITNCF(1), /* not used */ BIT(TRDY), /* transmit ready */ ENDBITS }; /* DZRBUF - 200A0004 - receive buffer, read only */ #define RBUF_CHAR 0x00FF /* rcv char */ #define RBUF_V_RLINE 8 /* rcv line */ #define RBUF_RLINE (DZ_LNOMASK << RBUF_V_RLINE) #define RBUF_PARE 0x1000 /* parity err - NI */ #define RBUF_FRME 0x2000 /* frame err */ #define RBUF_OVRE 0x4000 /* overrun err - NI */ #define RBUF_VALID 0x8000 /* rcv valid */ #define RBUF_MBZ 0x0C00 #define RBUF_GETRL(x) (((x) >> RBUF_V_RLINE) & DZ_LNOMASK) #define RBUF_PUTRL(x,y) x = ((x) & ~RBUF_RLINE) | (((y) & DZ_LNOMASK) << RBUF_V_RLINE) BITFIELD dz_rbuf_bits[] = { BITFFMT(RBUF,8,"%02X"), /* Received Character */ BITF(RLINE,2), /* receive line */ BITNCF(2), /* not used */ BIT(PARE), /* parity error */ BIT(FRME), /* frame error */ BIT(OVRE), /* overrun error */ BIT(VALID), /* receive valid */ ENDBITS }; const char *dz_charsizes[] = {"5", "6", "7", "8"}; const char *dz_baudrates[] = {"50", "75", "110", "134.5", "150", "300", "600", "1200", "1800", "2000", "2400", "3600", "4800", "7200", "9600", "19200"}; const char *dz_parity[] = {"N", "E", "N", "O"}; const char *dz_stopbits[] = {"1", "2", "1", "1.5"}; /* DZLPR - 200A0004 - line parameter register, write only, word access only */ #define LPR_V_LINE 0 /* line */ #define LPR_V_SPEED 8 /* speed code */ #define LPR_M_SPEED 0x0F00 /* speed code mask */ #define LPR_V_CHARSIZE 3 /* char size code */ #define LPR_M_CHARSIZE 0x0018 /* char size code mask */ #define LPR_V_STOPBITS 5 /* stop bits code */ #define LPR_V_PARENB 6 /* parity enable */ #define LPR_V_PARODD 7 /* parity odd */ #define LPR_GETSPD(x) dz_baudrates[((x) & LPR_M_SPEED) >> LPR_V_SPEED] #define LPR_GETCHARSIZE(x) dz_charsizes[((x) & LPR_M_CHARSIZE) >> LPR_V_CHARSIZE] #define LPR_GETPARITY(x) dz_parity[(((x) >> LPR_V_PARENB) & 1) | (((x) >> (LPR_V_PARODD-1)) & 2)] #define LPR_GETSTOPBITS(x) dz_stopbits[(((x) >> LPR_V_STOPBITS) & 1) + (((((x) & LPR_M_CHARSIZE) >> LPR_V_CHARSIZE) == 0) ? 2 : 0)] #define LPR_LPAR 0x0FF8 /* line pars - NI */ #define LPR_RCVE 0x1000 /* receive enb */ #define LPR_GETLN(x) (((x) >> LPR_V_LINE) & DZ_LNOMASK) BITFIELD dz_lpr_bits[] = { BITF(LINE,2), /* line */ BITFNAM(CHARSIZE,2,dz_charsizes), /* character size */ BIT(STOPBITS), /* stop bits code */ BIT(PARENB), /* parity error */ BIT(PARODD), /* frame error */ BITFNAM(SPEED,4,dz_baudrates), /* speed code */ BIT(RCVE), /* receive enable */ BITNCF(3), /* not used */ ENDBITS }; /* DZTCR - 200A0008 - transmission control register */ #define TCR_V_XMTE 0 /* xmit enables */ #define TCR_V_RTS2 8 /* RTS (line 2) */ #define TCR_V_DSRS2 9 /* DSRS (line 2) */ #define TCR_V_DTR2 10 /* DTR (line 2) */ #define TCR_V_LLBK2 11 /* local loopback (line 2) */ #define TCR_MBZ 0xF0F0 BITFIELD dz_tcr_bits[] = { BITFFMT(XMTE,8,%02X), /* Transmit enable */ BIT(RTS2), /* RTS (line 2) */ BIT(DSRS2), /* DSRS (line 2) */ BIT(DTR2), /* DTR (line 2) */ BIT(LLBK2), /* local loopback (line 2) */ BITNCF(4), /* not used */ ENDBITS }; /* DZMSR - 200A000C - modem status register, read only */ #define MSR_V_TMI2 0 /* test mode indicate (line2) */ #define MSR_V_RI2 2 /* ring indicator (line 2) */ #define MSR_V_CTS2 8 /* CTS (line 2) */ #define MSR_V_DSR2 9 /* DSR (line 2) */ #define MSR_V_CD2 10 /* carrier detect (line 2) */ #define MSR_V_SPDI2 11 /* speed mode indicate (line 2) */ BITFIELD dz_msr_bits[] = { BIT(TMI2), /* test mode indicate (line2) */ BIT(RI2), /* ring indicator (line 2) */ BIT(CTS2), /* CTS (line 2) */ BIT(DSR2), /* DSR (line 2) */ BIT(CD2), /* carrier detect (line 2) */ BIT(SPDI2), /* speed mode indicate (line 2) */ BITNCF(4), ENDBITS }; /* DZTDR - 200A000C - transmit data, write only */ #define TDR_CHAR 0x00FF /* xmit char */ #define TDR_V_TBR 8 /* xmit break - NI */ BITFIELD dz_tdr_bits[] = { BITFFMT(CHAR,8,%02X), /* xmit char */ BITFFMT(TBR, 4,%02X), /* xmit break - NI */ BITNCF(4), ENDBITS }; extern int32 tmxr_poll; /* calibrated delay */ uint16 dz_csr = 0; /* csr */ uint16 dz_rbuf = 0; /* rcv buffer */ uint16 dz_lpr = 0; /* line param */ uint16 dz_tcr = 0; /* xmit control */ uint16 dz_msr = 0; /* modem status */ uint16 dz_tdr = 0; /* xmit data */ uint16 dz_silo[DZ_SILO_ALM] = { 0 }; /* silo */ uint16 dz_scnt = 0; /* silo used */ uint8 dz_sae = 0; /* silo alarm enabled */ int32 dz_mctl = 0; /* modem ctrl enabled */ int32 dz_auto = 0; /* autodiscon enabled */ uint32 dz_func[DZ_LINES] = { DZ_TMXR }; /* line function */ uint32 dz_char[DZ_LINES] = { 0 }; /* character buffer */ int32 dz_lnorder[DZ_LINES] = { 0 }; /* line order */ TMLN *dz_ldsc = NULL; /* line descriptors */ TMXR dz_desc = { DZ_LINES, 0, 0, NULL, dz_lnorder }; /* mux descriptor */ /* debugging bitmaps */ #define DBG_REG 0x0001 /* trace read/write registers */ #define DBG_INT 0x0002 /* display interrupt activities */ #define DBG_XMT TMXR_DBG_XMT /* display Transmitted Data */ #define DBG_RCV TMXR_DBG_RCV /* display Received Data */ #define DBG_RET TMXR_DBG_RET /* display Read Data */ #define DBG_MDM TMXR_DBG_MDM /* display Modem Signals */ #define DBG_CON TMXR_DBG_CON /* display connection activities */ #define DBG_TRC TMXR_DBG_TRC /* display trace routine calls */ #define DBG_ASY TMXR_DBG_ASY /* display Asynchronous Activities */ DEBTAB dz_debug[] = { { "REG", DBG_REG, "read/write registers" }, { "INT", DBG_INT, "interrupt activities" }, { "XMT", DBG_XMT, "Transmitted Data" }, { "RCV", DBG_RCV, "Received Data" }, { "RET", DBG_RET, "Read Data" }, { "MDM", DBG_MDM, "Modem Signals" }, { "CON", DBG_CON, "connection activities" }, { "TRC", DBG_TRC, "trace routine calls" }, { "ASY", DBG_ASY, "Asynchronous Activities" }, { 0 } }; t_stat dz_svc (UNIT *uptr); t_stat dz_xmt_svc (UNIT *uptr); t_stat dz_reset (DEVICE *dptr); t_stat dz_attach (UNIT *uptr, CONST char *cptr); t_stat dz_detach (UNIT *uptr); t_stat dz_clear (t_bool flag); uint16 dz_getc (void); t_stat dz_putc (int32 line, uint16 data); void dz_update_rcvi (void); void dz_update_xmti (void); t_stat dz_set_log (UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat dz_set_nolog (UNIT *uptr, int32 val, CONST char *cptr, void *desc); t_stat dz_show_log (FILE *st, UNIT *uptr, int32 val, CONST void *desc); t_stat dz_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); t_stat dz_help_attach (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); const char *dz_description (DEVICE *dptr); /* DZ data structures dz_dev DZ device descriptor dz_unit DZ unit list dz_reg DZ register list */ UNIT dz_unit[2] = { { UDATA (&dz_svc, UNIT_IDLE|UNIT_ATTABLE|TT_MODE_8B, 0) }, { UDATA (&dz_xmt_svc, UNIT_DIS, 0), SERIAL_OUT_WAIT } }; REG dz_reg[] = { { HRDATADF (CSR, dz_csr, 16, "control/status register", dz_csr_bits) }, { HRDATADF (RBUF, dz_rbuf, 16, "receive buffer", dz_rbuf_bits) }, { HRDATADF (LPR, dz_lpr, 16, "line parameter register", dz_lpr_bits) }, { HRDATADF (TCR, dz_tcr, 16, "transmission control register", dz_tcr_bits) }, { HRDATADF (MSR, dz_msr, 16, "modem status register", dz_msr_bits) }, { HRDATADF (TDR, dz_tdr, 16, "transmit data register", dz_tdr_bits) }, { HRDATAD (SAENB, dz_sae, 1, "silo alarm enabled") }, { DRDATAD (TIME, dz_unit[1].wait, 24, "output character delay"), PV_LEFT }, { FLDATAD (MDMCTL, dz_mctl, 0, "modem control enabled") }, { FLDATAD (AUTODS, dz_auto, 0, "autodisconnect enabled") }, { FLDATAD (TXINT, int_req[IPL_DZTX], INT_V_DZTX, "transmit interrupt pending flag") }, { FLDATAD (RXINT, int_req[IPL_DZRX], INT_V_DZRX, "receive interrupt pending flag") }, { NULL } }; MTAB dz_mod[] = { { TT_MODE, TT_MODE_7B, "7b", "7B", NULL, NULL, NULL, "7 bit mode" }, { TT_MODE, TT_MODE_8B, "8b", "8B", NULL, NULL, NULL, "8 bit mode" }, { TT_MODE, TT_MODE_7P, "7p", "7P", NULL, NULL, NULL, "7 bit mode - non printing suppressed" }, { MTAB_XTD|MTAB_VDV|MTAB_VALR, 1, NULL, "DISCONNECT", &tmxr_dscln, NULL, &dz_desc, "Disconnect a specific line" }, { UNIT_ATT, UNIT_ATT, "summary", NULL, NULL, &tmxr_show_summ, (void *) &dz_desc, "Display a summary of line states" }, { MTAB_XTD|MTAB_VDV|MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat, (void *) &dz_desc, "Display current connections" }, { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, (void *) &dz_desc, "Display multiplexer statistics" }, { MTAB_XTD | MTAB_VDV, 0, "LINES", NULL, NULL, &tmxr_show_lines, (void *) &dz_desc }, { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "LINES", NULL, NULL, &tmxr_show_lines, (void *) &dz_desc, "Display number of lines" }, { MTAB_XTD|MTAB_VDV|MTAB_NC, 0, NULL, "LOG=n=file", &dz_set_log, NULL, &dz_desc }, { MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, NULL, "NOLOG=n", &dz_set_nolog, NULL, &dz_desc, "Disable logging on designated line" }, { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "LOG", NULL, NULL, &dz_show_log, &dz_desc, "Display logging for all lines" }, { 0 } }; DEVICE dz_dev = { "DZ", dz_unit, dz_reg, dz_mod, 2, DEV_RDX, 8, 1, DEV_RDX, 8, &tmxr_ex, &tmxr_dep, &dz_reset, NULL, &dz_attach, &dz_detach, NULL, DEV_DISABLE | DEV_DEBUG | DEV_MUX, 0, dz_debug, NULL, NULL, &dz_help, &dz_help_attach, /* help and attach_help routines */ (void *)&dz_desc, /* help context variable */ &dz_description /* description routine */ }; /* Register names for Debug tracing */ static const char *dz_rd_regs[] = {"CSR ", "RBUF", "TCR ", "MSR " }; static const char *dz_wr_regs[] = {"CSR ", "LPR ", "TCR ", "TDR "}; /* IO dispatch routines */ int32 dz_rd (int32 pa) { int32 data = 0; switch ((pa >> 2) & 03) { /* case on PA<2:1> */ case 00: /* CSR */ data = dz_csr = dz_csr & ~CSR_MBZ; break; case 01: /* RBUF */ dz_csr = dz_csr & ~CSR_SA; /* clr silo alarm */ if (dz_csr & CSR_MSE) { /* scanner on? */ dz_rbuf = dz_getc (); /* get top of silo */ if (!dz_rbuf) /* empty? re-enable */ dz_sae = 1; tmxr_poll_rx (&dz_desc); /* poll input */ dz_update_rcvi (); /* update rx intr */ if (dz_rbuf) { /* Reschedule the next poll preceisely so that the the programmed input speed is observed. */ sim_clock_coschedule_abs (dz_unit, tmxr_poll); } } else { dz_rbuf = 0; /* no data */ dz_update_rcvi (); /* no rx intr */ } data = dz_rbuf; break; case 02: /* TCR */ data = dz_tcr = dz_tcr & ~TCR_MBZ; break; case 03: /* MSR */ if (dz_mctl) { int32 modem_bits; TMLN *lp; lp = &dz_ldsc[2]; /* get line desc */ tmxr_set_get_modem_bits (lp, 0, 0, &modem_bits); dz_msr &= ~(MSR_V_RI2 | MSR_V_CD2); dz_msr |= ((modem_bits&TMXR_MDM_RNG) ? MSR_V_RI2 : 0) | ((modem_bits&TMXR_MDM_DCD) ? MSR_V_CD2 : 0); } data = dz_msr; break; } sim_debug(DBG_REG, &dz_dev, "dz_rd(PA=0x%08X [%s], data=0x%X)\n", pa, dz_rd_regs[(pa >> 2) & 03], data); SET_IRQL; return data; } void dz_wr (int32 pa, int32 data, int32 access) { int32 line; char lineconfig[16]; TMLN *lp; sim_debug(DBG_REG, &dz_dev, "dz_wr(PA=0x%08X [%s], access=%d, data=0x%X)\n", pa, dz_wr_regs[(pa >> 2) & 03], access, data); switch ((pa >> 2) & 03) { /* case on PA<2:1> */ case 00: /* CSR */ if (access == L_BYTE) data = (pa & 1)? /* byte? merge */ (dz_csr & BMASK) | (data << 8): (dz_csr & ~BMASK) | data; if (data & CSR_CLR) /* clr? reset */ dz_clear (FALSE); if (data & CSR_MSE) /* MSE? start poll */ sim_clock_coschedule (&dz_unit[0], tmxr_poll); else dz_csr &= ~(CSR_SA | CSR_RDONE | CSR_TRDY); dz_csr = (dz_csr & ~CSR_RW) | (data & CSR_RW); break; case 01: /* LPR */ dz_lpr = data; line = LPR_GETLN (data); /* get line num */ lp = &dz_ldsc[line]; /* get line desc */ if (dz_lpr & LPR_RCVE) /* rcv enb? on */ lp->rcve = 1; else lp->rcve = 0; /* else line off */ sprintf(lineconfig, "%s-%s%s%s", LPR_GETSPD(data), LPR_GETCHARSIZE(data), LPR_GETPARITY(data), LPR_GETSTOPBITS(data)); if (!lp->serconfig || (0 != strcmp(lp->serconfig, lineconfig))) /* config changed? */ tmxr_set_config_line (lp, lineconfig); /* set it */ tmxr_poll_rx (&dz_desc); /* poll input */ dz_update_rcvi (); /* update rx intr */ break; case 02: /* TCR */ if (access == L_BYTE) data = (pa & 1)? /* byte? merge */ (dz_tcr & BMASK) | (data << 8): (dz_tcr & ~BMASK) | data; if (dz_mctl) { /* modem ctl? */ int32 changed = data ^ dz_tcr; for (line = 0; line < DZ_LINES; line++) { if (0 == (changed & (1 << (TCR_V_DTR2 + line)))) continue; /* line unchanged skip */ lp = &dz_ldsc[line]; /* get line desc */ if (data & (1 << (TCR_V_DTR2 + line))) tmxr_set_get_modem_bits (lp, TMXR_MDM_DTR|TMXR_MDM_RTS, 0, NULL); else if (dz_auto) tmxr_set_get_modem_bits (lp, 0, TMXR_MDM_DTR|TMXR_MDM_RTS, NULL); } } dz_tcr = data; tmxr_poll_tx (&dz_desc); /* poll output */ dz_update_xmti (); /* update int */ break; case 03: /* TDR */ if (pa & 1) { /* odd byte? */ dz_tdr = (dz_tdr & BMASK) | (data << 8); /* just save */ break; } dz_tdr = data; if (dz_csr & CSR_MSE) { /* enabled? */ line = CSR_GETTL (dz_csr); if (dz_csr & CSR_MAINT) { /* test mode? */ dz_char[line] = (dz_tdr & BMASK) | RBUF_VALID;/* loop data back */ dz_char[line] |= (line << RBUF_V_RLINE); if (dz_tdr & (1u << (TDR_V_TBR + line))) dz_char[line] = dz_char[line] | RBUF_FRME; dz_csr &= ~CSR_TRDY; sim_debug(DBG_REG, &dz_dev, "maint char for line %d : %X\n", line, dz_char[line]); break; } sim_activate (&dz_unit[1], 0); } break; } SET_IRQL; } /* Unit input service routine The DZ polls to see if asynchronous activity has occurred and now needs to be processed. The polling interval is controlled by the clock simulator, so for most environments, it is calibrated to real time. Typical polling intervals are 50-60 times per second. The simulator assumes that software enables all of the multiplexors, or none of them. */ t_stat dz_svc (UNIT *uptr) { int32 newln, muxln; if (dz_csr & CSR_MSE) { /* enabled? */ newln = tmxr_poll_conn (&dz_desc); /* poll connect */ if ((newln >= 0) && dz_mctl) { /* got a live one? */ muxln = newln % DZ_LINES; /* get line in mux */ if (muxln == 2) { if (dz_tcr & (1 << TCR_V_DTR2)) /* DTR set? */ dz_msr |= (1 << MSR_V_CD2); /* set cdet */ else dz_msr |= (1 << MSR_V_RI2); /* set ring */ } } tmxr_poll_rx (&dz_desc); /* poll input */ dz_update_rcvi (); /* upd rcv intr */ tmxr_poll_tx (&dz_desc); /* poll output */ dz_update_xmti (); /* upd xmt intr */ if ((dz_csr & CSR_RDONE) == 0) sim_clock_coschedule (uptr, tmxr_poll); /* reactivate */ } return SCPE_OK; } t_stat dz_xmt_svc (UNIT *uptr) { int32 line; line = CSR_GETTL (dz_csr); if (SCPE_STALL != dz_putc (line, dz_tdr)) { /* sent ok? */ tmxr_poll_tx (&dz_desc); /* poll output */ dz_update_xmti (); /* update int */ } else sim_activate (uptr, dz_unit[1].wait); /* come back later */ return SCPE_OK; } /* Put a character to the specified line */ t_stat dz_putc (int32 line, uint16 data) { t_stat r = SCPE_OK; int32 c; TMLN *lp; switch (dz_func[line]) { case DZ_TMXR: lp = &dz_ldsc[line]; /* get line desc */ c = sim_tt_outcvt (data, TT_GET_MODE (dz_unit[0].flags)); if (c >= 0) /* store char */ tmxr_putc_ln (lp, c); break; case DZ_CONSOLE: c = sim_tt_outcvt (data, TT_GET_MODE (dz_unit[0].flags)); if (c >= 0) r = sim_putchar_s (c); /* send to console */ break; case DZ_KEYBOARD: lk_wr ((uint8)data); /* send to keyboard */ break; case DZ_MOUSE: vs_wr ((uint8)data); /* send to mouse */ break; } return r; } /* Get first available character for mux, if any */ uint16 dz_getc (void) { uint16 ret; uint32 i; if (!dz_scnt) return 0; ret = dz_silo[0]; /* first fifo element */ for (i = 1; i < dz_scnt; i++) /* slide down remaining entries */ dz_silo[i-1] = dz_silo[i]; --dz_scnt; /* adjust count */ sim_debug (DBG_RCV, &dz_dev, "DZ Line %d - Received: 0x%X - '%c'\n", i, ret, sim_isprint(ret&0xFF) ? ret & 0xFF : '.'); return ret; } /* Update receive interrupts */ void dz_update_rcvi (void) { int32 line, c; TMLN *lp; if (dz_csr & CSR_MSE) { /* enabled? */ for (line = 0; line < DZ_LINES; line++) { /* poll lines */ if (dz_scnt >= DZ_SILO_ALM) break; c = 0; if ((dz_func[line] == DZ_TMXR) && ((dz_csr & CSR_MAINT) == 0)) { lp = &dz_ldsc[line]; /* get line desc */ c = tmxr_getc_ln (lp); /* test for input */ if (c & SCPE_BREAK) /* break? frame err */ c = RBUF_FRME; if (line == 2) { if (dz_mctl && !lp->conn) /* if disconn */ dz_msr &= ~(1 << MSR_V_CD2); /* reset car det */ } } else { switch (dz_func[line]) { case DZ_KEYBOARD: if (lk_rd ((uint8*)&c) == SCPE_OK) /* test for input */ c |= RBUF_VALID; break; case DZ_MOUSE: if (vs_rd ((uint8*)&c) == SCPE_OK) /* test for input */ c |= RBUF_VALID; break; case DZ_CONSOLE: if ((c = sim_poll_kbd ()) < SCPE_KFLAG) { if (SCPE_BARE_STATUS(c) == SCPE_OK) /* no char */ continue; else ABORT (c); /* error */ } if (c & SCPE_BREAK) { /* break? frame err */ hlt_pin = 1; c = RBUF_FRME; } else c = sim_tt_inpcvt (c, TT_GET_MODE (dz_unit[0].flags)); break; default: /* no action for other lines */ continue; } } if (c) { /* save in silo */ c = (c & (RBUF_CHAR | RBUF_FRME)) | RBUF_VALID;; RBUF_PUTRL (c, line); /* add line # */ dz_silo[dz_scnt] = (uint16)c; ++dz_scnt; } } } if (dz_scnt && (dz_csr & CSR_MSE)) { /* input & enabled? */ dz_csr |= CSR_RDONE; /* set done */ if (dz_sae && (dz_scnt >= DZ_SILO_ALM)) { /* alm enb & cnt hi? */ dz_csr |= CSR_SA; /* set status */ dz_sae = 0; /* disable alarm */ } } else dz_csr &= ~CSR_RDONE; /* no, clear done */ if (((dz_csr & CSR_SAE)? (dz_csr & CSR_SA): (dz_csr & CSR_RDONE))) SET_INT (DZRX); /* alm/done? */ else CLR_INT (DZRX); /* no, clear int */ return; } /* Update transmit interrupts */ void dz_update_xmti (void) { int32 linemask, i, line; linemask = dz_tcr & DZ_LMASK; /* enabled lines */ dz_csr &= ~CSR_TRDY; /* assume not rdy */ line = CSR_GETTL (dz_csr); /* start at current */ for (i = 0; i < DZ_LINES; i++) { /* loop thru lines */ line = (line + 1) & DZ_LNOMASK; /* next line */ if ((linemask & (1 << line)) && dz_ldsc[line].xmte) { CSR_PUTTL (dz_csr, line); /* put ln in csr */ dz_csr |= CSR_TRDY; /* set xmt rdy */ break; } } if (dz_csr & CSR_TRDY) /* ready? */ SET_INT (DZTX); else CLR_INT (DZTX); /* no int req */ return; } /* Device reset */ t_stat dz_clear (t_bool flag) { int32 i; dz_csr = 0; /* clear CSR */ dz_rbuf = 0; /* silo empty */ dz_lpr = 0; /* no params */ if (flag) /* INIT? clr all */ dz_tcr = 0; else dz_tcr &= ~0377; /* else save dtr */ dz_tdr = 0; dz_sae = 1; /* alarm on */ dz_scnt = 0; CLR_INT (DZRX); /* clear int */ CLR_INT (DZTX); for (i = 0; i < DZ_LINES; i++) { /* loop thru lines */ if (!dz_ldsc[i].conn) /* set xmt enb */ dz_ldsc[i].xmte = 1; dz_ldsc[i].rcve = 0; /* clr rcv enb */ } return SCPE_OK; } t_stat dz_reset (DEVICE *dptr) { int32 i; if (sys_model) { /* VAXstation? */ dz_func[0] = DZ_KEYBOARD; dz_func[1] = DZ_MOUSE; dz_func[2] = DZ_TMXR; dz_func[3] = DZ_TMXR; dz_lnorder[0] = 2; dz_lnorder[1] = 3; dz_lnorder[2] = 2; /* only 2 connections */ dz_lnorder[3] = 3; } else if (DZ_L3C) { /* no, MicroVAX */ dz_func[0] = DZ_TMXR; dz_func[1] = DZ_TMXR; dz_func[2] = DZ_TMXR; dz_func[3] = DZ_CONSOLE; dz_lnorder[0] = 0; dz_lnorder[1] = 1; dz_lnorder[2] = 2; dz_lnorder[3] = 0; /* only 3 connections */ } else { /* InfoServer */ dz_func[0] = DZ_CONSOLE; dz_func[1] = DZ_TMXR; dz_func[2] = DZ_TMXR; dz_func[3] = DZ_TMXR; dz_lnorder[0] = 1; dz_lnorder[1] = 2; dz_lnorder[2] = 3; dz_lnorder[3] = 1; /* only 3 connections */ } if (dz_ldsc != NULL) { for (i = 0; i < DZ_LINES; i++) { if (dz_func[i] != DZ_TMXR) { if (dz_ldsc[i].conn) { tmxr_linemsg (&dz_ldsc[i], "\r\nOperator disconnected line\r\n"); tmxr_send_buffered_data (&dz_ldsc[i]); } tmxr_detach_ln (&dz_ldsc[i]); /* completely reset line */ } } } else dz_desc.ldsc = dz_ldsc = (TMLN *)calloc (DZ_LINES, sizeof(*dz_ldsc)); dz_clear (TRUE); /* init mux */ CLR_INT (DZRX); CLR_INT (DZTX); sim_cancel (&dz_unit[0]); /* stop poll */ for (i = 0; i < DZ_LINES; i++) dz_char[i] = 0; return SCPE_OK; } /* Attach */ t_stat dz_attach (UNIT *uptr, CONST char *cptr) { int32 muxln; t_stat r; if (sim_switches & SWMASK ('M')) /* modem control? */ tmxr_set_modem_control_passthru (&dz_desc); r = tmxr_attach (&dz_desc, uptr, cptr); /* attach mux */ if (r != SCPE_OK) { /* error? */ tmxr_clear_modem_control_passthru (&dz_desc); return r; } if (sim_switches & SWMASK ('M')) { /* modem control? */ dz_mctl = 1; sim_printf ("Modem control activated\n"); if (sim_switches & SWMASK ('A')) { /* autodisconnect? */ dz_auto = 1; sim_printf ("Auto disconnect activated\n"); } } if (!dz_mctl || (0 == (dz_csr & CSR_MSE))) /* enabled? */ return SCPE_OK; for (muxln = 0; muxln < DZ_LINES; muxln++) { if (dz_tcr & (1 << (muxln + TCR_V_DTR2))) { TMLN *lp = &dz_ldsc[muxln]; tmxr_set_get_modem_bits (lp, TMXR_MDM_DTR|TMXR_MDM_RTS, 0, NULL); } } return SCPE_OK; } /* Detach */ t_stat dz_detach (UNIT *uptr) { dz_mctl = dz_auto = 0; /* modem ctl off */ return tmxr_detach (&dz_desc, uptr); } /* SET LOG processor */ t_stat dz_set_log (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { t_stat r; char gbuf[CBUFSIZE]; int32 ln; if (cptr == NULL) return SCPE_ARG; cptr = get_glyph (cptr, gbuf, '='); if ((cptr == NULL) || (*cptr == 0) || (gbuf[0] == 0)) return SCPE_ARG; ln = (int32) get_uint (gbuf, 10, dz_desc.lines, &r); if ((r != SCPE_OK) || (ln >= dz_desc.lines)) return SCPE_ARG; return tmxr_set_log (NULL, ln, cptr, desc); } /* SET NOLOG processor */ t_stat dz_set_nolog (UNIT *uptr, int32 val, CONST char *cptr, void *desc) { t_stat r; int32 ln; if (cptr == NULL) return SCPE_ARG; ln = (int32) get_uint (cptr, 10, dz_desc.lines, &r); if ((r != SCPE_OK) || (ln >= dz_desc.lines)) return SCPE_ARG; return tmxr_set_nolog (NULL, ln, NULL, desc); } /* SHOW LOG processor */ t_stat dz_show_log (FILE *st, UNIT *uptr, int32 val, CONST void *desc) { int32 i; for (i = 0; i < dz_desc.lines; i++) { fprintf (st, "line %d: ", i); tmxr_show_log (st, NULL, i, desc); fprintf (st, "\n"); } return SCPE_OK; } t_stat dz_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) { fprintf (st, "DZ Terminal Multiplexer (DZ)\n\n"); fprintf (st, "The DZ is a %d line terminal multiplexor.\n", DZ_LINES); fprintf (st, "For the MicroVAX, one of these lines is dedicated to the console and\n"); fprintf (st, "cannot be used with the Telnet multiplexer. For the VAXstation, two\n"); fprintf (st, "ports are dedicated to the keyboard and mouse.\n"); fprintf (st, "The DZ supports three character processing modes, 7P, 7B, and 8B:\n\n"); fprintf (st, " mode input characters output characters\n"); fprintf (st, " =============================================\n"); fprintf (st, " 7P high-order bit cleared high-order bit cleared,\n"); fprintf (st, " non-printing characters suppressed\n"); fprintf (st, " 7B high-order bit cleared high-order bit cleared\n"); fprintf (st, " 8B no changes no changes\n\n"); fprintf (st, "The default is 8B.\n\n"); fprintf (st, "The DZ supports logging on a per-line basis. The command\n\n"); fprintf (st, " sim> SET %s LOG=n=filename\n\n", dptr->name); fprintf (st, "enables logging for the specified line(n) to the indicated file. The command\n\n"); fprintf (st, " sim> SET %s NOLOG=line\n\n", dptr->name); fprintf (st, "disables logging for the specified line and closes any open log file. Finally,\n"); fprintf (st, "the command:\n\n"); fprintf (st, " sim> SHOW %s LOG\n\n", dptr->name); fprintf (st, "displays logging information for all %s lines.\n\n", dptr->name); fprintf (st, "Once the DZ is attached and the simulator is running, the DZ will listen for\n"); fprintf (st, "connections on the specified port. It assumes that the incoming connections\n"); fprintf (st, "are Telnet connections. The connection remains open until disconnected by the\n"); fprintf (st, "simulated program, the Telnet client, a SET %s DISCONNECT command, or a\n", dptr->name); fprintf (st, "DETACH %s command.\n\n", dptr->name); fprintf (st, "Other special %s commands:\n\n", dptr->name); fprintf (st, " sim> SHOW %s CONNECTIONS show current connections\n", dptr->name); fprintf (st, " sim> SHOW %s STATISTICS show statistics for active connections\n", dptr->name); fprintf (st, " sim> SET %s DISCONNECT=linenumber disconnects the specified line.\n\n\n", dptr->name); fprintf (st, "All open connections are lost when the simulator shuts down or the %s is\n", dptr->name); fprintf (st, "detached.\n"); return SCPE_OK; } t_stat dz_help_attach (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) { tmxr_attach_help (st, dptr, uptr, flag, cptr); fprintf (st, "The terminal lines perform input and output through Telnet sessions connected\n"); fprintf (st, "to a user-specified port. The ATTACH command specifies the port to be used:\n\n"); fprintf (st, " sim> ATTACH {-am} %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. The optional switch -m turns on the DZ's modem\n"); fprintf (st, "controls; the optional switch -a turns on active disconnects (disconnect\n"); fprintf (st, "session if computer clears Data Terminal Ready). Without modem control, the\n"); fprintf (st, "DZ behaves as though terminals were directly connected; disconnecting the\n"); fprintf (st, "Telnet session does not cause any operating system-visible change in line\n"); fprintf (st, "status.\n\n"); return SCPE_OK; } const char *dz_description (DEVICE *dptr) { return "DZ 4-line terminal multiplexer"; }