899 lines
38 KiB
C
899 lines
38 KiB
C
/* 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";
|
|
}
|