/* pdp11_dl.c: PDP-11 multiple terminal interface simulator | |
Copyright (c) 1993-2006, 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 | |
ROBERT M SUPNIK 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 Robert M Supnik shall not be | |
used in advertising or otherwise to promote the sale, use or other dealings | |
in this Software without prior written authorization from Robert M Supnik. | |
ttix,ttox DL11 terminal input/output | |
*/ | |
#if defined (VM_PDP10) /* PDP10 version */ | |
#error "DL11 is not supported on the PDP-10!" | |
#elif defined (VM_VAX) /* VAX version */ | |
#error "DL11 is not supported on the VAX!" | |
#else /* PDP-11 version */ | |
#include "pdp11_defs.h" | |
#endif | |
#include "sim_sock.h" | |
#include "sim_tmxr.h" | |
#define TTX_MASK (TTX_LINES - 1) | |
#define TTIXCSR_IMP (CSR_DONE + CSR_IE) /* terminal input */ | |
#define TTIXCSR_RW (CSR_IE) | |
#define TTIXBUF_ERR 0100000 | |
#define TTIXBUF_OVR 0040000 | |
#define TTIXBUF_RBRK 0020000 | |
#define TTOXCSR_IMP (CSR_DONE + CSR_IE) /* terminal output */ | |
#define TTOXCSR_RW (CSR_IE) | |
extern int32 int_req[IPL_HLVL]; | |
extern int32 tmxr_poll; | |
uint16 ttix_csr[TTX_LINES] = { 0 }; /* control/status */ | |
uint16 ttix_buf[TTX_LINES] = { 0 }; | |
uint32 ttix_ireq = 0; | |
uint16 ttox_csr[TTX_LINES] = { 0 }; /* control/status */ | |
uint8 ttox_buf[TTX_LINES] = { 0 }; | |
uint32 ttox_ireq = 0; | |
TMLN ttx_ldsc[TTX_LINES] = { 0 }; /* line descriptors */ | |
TMXR ttx_desc = { TTX_LINES, 0, 0, ttx_ldsc }; /* mux descriptor */ | |
t_stat ttx_rd (int32 *data, int32 PA, int32 access); | |
t_stat ttx_wr (int32 data, int32 PA, int32 access); | |
t_stat ttx_reset (DEVICE *dptr); | |
t_stat ttix_svc (UNIT *uptr); | |
t_stat ttox_svc (UNIT *uptr); | |
t_stat ttx_attach (UNIT *uptr, char *cptr); | |
t_stat ttx_detach (UNIT *uptr); | |
t_stat ttx_summ (FILE *st, UNIT *uptr, int32 val, void *desc); | |
t_stat ttx_show (FILE *st, UNIT *uptr, int32 val, void *desc); | |
t_stat ttx_show_vec (FILE *st, UNIT *uptr, int32 val, void *desc); | |
t_stat ttx_set_lines (UNIT *uptr, int32 val, char *cptr, void *desc); | |
t_stat ttx_show_lines (FILE *st, UNIT *uptr, int32 val, void *desc); | |
void ttx_enbdis (int32 dis); | |
void ttix_clr_int (uint32 ln); | |
void ttix_set_int (int32 ln); | |
int32 ttix_iack (void); | |
void ttox_clr_int (int32 ln); | |
void ttox_set_int (int32 ln); | |
int32 ttox_iack (void); | |
void ttx_reset_ln (uint32 ln); | |
/* TTIX data structures | |
ttix_dev TTIX device descriptor | |
ttix_unit TTIX unit descriptor | |
ttix_reg TTIX register list | |
*/ | |
DIB ttix_dib = { | |
IOBA_TTIX, IOLN_TTIX, &ttx_rd, &ttx_wr, | |
2, IVCL (TTIX), VEC_TTIX, { &ttix_iack, &ttox_iack } | |
}; | |
UNIT ttix_unit = { UDATA (&ttix_svc, 0, 0), KBD_POLL_WAIT }; | |
REG ttix_reg[] = { | |
{ BRDATA (BUF, ttix_buf, DEV_RDX, 16, TTX_LINES) }, | |
{ BRDATA (CSR, ttix_csr, DEV_RDX, 16, TTX_LINES) }, | |
{ GRDATA (IREQ, ttix_ireq, DEV_RDX, TTX_LINES, 0) }, | |
{ DRDATA (LINES, ttx_desc.lines, 6), REG_HRO }, | |
{ GRDATA (DEVADDR, ttix_dib.ba, DEV_RDX, 32, 0), REG_HRO }, | |
{ GRDATA (DEVVEC, ttix_dib.vec, DEV_RDX, 16, 0), REG_HRO }, | |
{ NULL } | |
}; | |
MTAB ttix_mod[] = { | |
{ UNIT_ATT, UNIT_ATT, "summary", NULL, NULL, &ttx_summ }, | |
{ MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", | |
&tmxr_dscln, NULL, &ttx_desc }, | |
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, | |
NULL, &ttx_show, NULL }, | |
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, | |
NULL, &ttx_show, NULL }, | |
{ MTAB_XTD|MTAB_VDV, 0, "ADDRESS", NULL, | |
&set_addr, &show_addr, NULL }, | |
{ MTAB_XTD|MTAB_VDV, 0, "VECTOR", NULL, | |
&set_vec, &ttx_show_vec, NULL }, | |
{ MTAB_XTD | MTAB_VDV, 0, "lines", "LINES", | |
&ttx_set_lines, &ttx_show_lines }, | |
{ 0 } | |
}; | |
DEVICE ttix_dev = { | |
"TTIX", &ttix_unit, ttix_reg, ttix_mod, | |
1, 10, 31, 1, 8, 8, | |
NULL, NULL, &ttx_reset, | |
NULL, &ttx_attach, &ttx_detach, | |
&ttix_dib, DEV_UBUS | DEV_QBUS | DEV_DISABLE | DEV_DIS | |
}; | |
/* TTOX data structures | |
ttox_dev TTOX device descriptor | |
ttox_unit TTOX unit descriptor | |
ttox_reg TTOX register list | |
*/ | |
UNIT ttox_unit[] = { | |
{ UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, | |
{ UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, | |
{ UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, | |
{ UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, | |
{ UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, | |
{ UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, | |
{ UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, | |
{ UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, | |
{ UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, | |
{ UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, | |
{ UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, | |
{ UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, | |
{ UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, | |
{ UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, | |
{ UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT }, | |
{ UDATA (&ttox_svc, TT_MODE_UC, 0), SERIAL_OUT_WAIT } | |
}; | |
REG ttox_reg[] = { | |
{ BRDATA (BUF, ttox_buf, DEV_RDX, 8, TTX_LINES) }, | |
{ BRDATA (CSR, ttox_csr, DEV_RDX, 16, TTX_LINES) }, | |
{ GRDATA (IREQ, ttox_ireq, DEV_RDX, TTX_LINES, 0) }, | |
{ URDATA (TIME, ttox_unit[0].wait, 10, 31, 0, | |
TTX_LINES, PV_LEFT) }, | |
{ NULL } | |
}; | |
MTAB ttox_mod[] = { | |
{ TT_MODE, TT_MODE_UC, "UC", "UC", NULL }, | |
{ TT_MODE, TT_MODE_7B, "7b", "7B", NULL }, | |
{ TT_MODE, TT_MODE_8B, "8b", "8B", NULL }, | |
{ TT_MODE, TT_MODE_7P, "7p", "7P", NULL }, | |
{ MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT", | |
&tmxr_dscln, NULL, &ttx_desc }, | |
{ MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG", | |
&tmxr_set_log, &tmxr_show_log, &ttx_desc }, | |
{ MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG", | |
&tmxr_set_nolog, NULL, &ttx_desc }, | |
{ 0 } | |
}; | |
DEVICE ttox_dev = { | |
"TTOX", ttox_unit, ttox_reg, ttox_mod, | |
1, 10, 31, 1, 8, 8, | |
NULL, NULL, &ttx_reset, | |
NULL, NULL, NULL, | |
NULL, DEV_UBUS | DEV_QBUS | DEV_DISABLE | DEV_DIS | |
}; | |
/* Terminal input routines */ | |
t_stat ttx_rd (int32 *data, int32 PA, int32 access) | |
{ | |
uint32 ln = ((PA - ttix_dib.ba) >> 3) & TTX_MASK; | |
switch ((PA >> 1) & 03) { /* decode PA<2:1> */ | |
case 00: /* tti csr */ | |
*data = ttix_csr[ln] & TTIXCSR_IMP; | |
return SCPE_OK; | |
case 01: /* tti buf */ | |
ttix_csr[ln] &= ~CSR_DONE; | |
ttix_clr_int (ln); | |
*data = ttix_buf[ln]; | |
return SCPE_OK; | |
case 02: /* tto csr */ | |
*data = ttox_csr[ln] & TTOXCSR_IMP; | |
return SCPE_OK; | |
case 03: /* tto buf */ | |
*data = ttox_buf[ln]; | |
return SCPE_OK; | |
} /* end switch PA */ | |
return SCPE_NXM; | |
} | |
t_stat ttx_wr (int32 data, int32 PA, int32 access) | |
{ | |
uint32 ln = ((PA - ttix_dib.ba) >> 3) & TTX_MASK; | |
switch ((PA >> 1) & 03) { /* decode PA<2:1> */ | |
case 00: /* tti csr */ | |
if (PA & 1) return SCPE_OK; | |
if ((data & CSR_IE) == 0) | |
ttix_clr_int (ln); | |
else if ((ttix_csr[ln] & (CSR_DONE + CSR_IE)) == CSR_DONE) | |
ttix_set_int (ln); | |
ttix_csr[ln] = (uint16) ((ttix_csr[ln] & ~TTIXCSR_RW) | (data & TTIXCSR_RW)); | |
return SCPE_OK; | |
case 01: /* tti buf */ | |
return SCPE_OK; | |
case 02: /* tto csr */ | |
if (PA & 1) return SCPE_OK; | |
if ((data & CSR_IE) == 0) | |
ttox_clr_int (ln); | |
else if ((ttox_csr[ln] & (CSR_DONE + CSR_IE)) == CSR_DONE) | |
ttox_set_int (ln); | |
ttox_csr[ln] = (uint16) ((ttox_csr[ln] & ~TTOXCSR_RW) | (data & TTOXCSR_RW)); | |
return SCPE_OK; | |
case 03: /* tto buf */ | |
if ((PA & 1) == 0) | |
ttox_buf[ln] = data & 0377; | |
ttox_csr[ln] &= ~CSR_DONE; | |
ttox_clr_int (ln); | |
sim_activate (&ttox_unit[ln], ttox_unit[ln].wait); | |
return SCPE_OK; | |
} /* end switch PA */ | |
return SCPE_NXM; | |
} | |
/* Terminal input service */ | |
t_stat ttix_svc (UNIT *uptr) | |
{ | |
int32 ln, c, temp; | |
if ((uptr->flags & UNIT_ATT) == 0) return SCPE_OK; /* attached? */ | |
sim_activate (uptr, tmxr_poll); /* continue poll */ | |
ln = tmxr_poll_conn (&ttx_desc); /* look for connect */ | |
if (ln >= 0) ttx_ldsc[ln].rcve = 1; /* got one? rcv enb */ | |
tmxr_poll_rx (&ttx_desc); /* poll for input */ | |
for (ln = 0; ln < TTX_LINES; ln++) { /* loop thru lines */ | |
if (ttx_ldsc[ln].conn) { /* connected? */ | |
if (temp = tmxr_getc_ln (&ttx_ldsc[ln])) { /* get char */ | |
if (temp & SCPE_BREAK) /* break? */ | |
c = TTIXBUF_ERR|TTIXBUF_RBRK; | |
else c = sim_tt_inpcvt (temp, TT_GET_MODE (ttox_unit[ln].flags)); | |
if (ttix_csr[ln] & CSR_DONE) | |
c |= TTIXBUF_ERR|TTIXBUF_OVR; | |
else { | |
ttix_csr[ln] |= CSR_DONE; | |
if (ttix_csr[ln] & CSR_IE) ttix_set_int (ln); | |
} | |
ttix_buf[ln] = c; | |
} | |
} | |
} | |
return SCPE_OK; | |
} | |
/* Terminal output service */ | |
t_stat ttox_svc (UNIT *uptr) | |
{ | |
int32 c; | |
uint32 ln = uptr - ttox_unit; /* line # */ | |
if (ttx_ldsc[ln].conn) { /* connected? */ | |
if (ttx_ldsc[ln].xmte) { /* tx enabled? */ | |
TMLN *lp = &ttx_ldsc[ln]; /* get line */ | |
c = sim_tt_outcvt (ttox_buf[ln], TT_GET_MODE (ttox_unit[ln].flags)); | |
if (c >= 0) tmxr_putc_ln (lp, c); /* output char */ | |
tmxr_poll_tx (&ttx_desc); /* poll xmt */ | |
} | |
else { | |
tmxr_poll_tx (&ttx_desc); /* poll xmt */ | |
sim_activate (uptr, ttox_unit[ln].wait); /* wait */ | |
return SCPE_OK; | |
} | |
} | |
ttox_csr[ln] |= CSR_DONE; /* set done */ | |
if (ttox_csr[ln] & CSR_IE) ttox_set_int (ln); | |
return SCPE_OK; | |
} | |
/* Interrupt routines */ | |
void ttix_clr_int (uint32 ln) | |
{ | |
ttix_ireq &= ~(1 << ln); /* clr mux rcv int */ | |
if (ttix_ireq == 0) CLR_INT (TTIX); /* all clr? */ | |
else SET_INT (TTIX); /* no, set intr */ | |
return; | |
} | |
void ttix_set_int (int32 ln) | |
{ | |
ttix_ireq |= (1 << ln); /* clr mux rcv int */ | |
SET_INT (TTIX); /* set master intr */ | |
return; | |
} | |
int32 ttix_iack (void) | |
{ | |
int32 ln; | |
for (ln = 0; ln < TTX_LINES; ln++) { /* find 1st line */ | |
if (ttix_ireq & (1 << ln)) { | |
ttix_clr_int (ln); /* clear intr */ | |
return (ttix_dib.vec + (ln * 010)); /* return vector */ | |
} | |
} | |
return 0; | |
} | |
void ttox_clr_int (int32 ln) | |
{ | |
ttox_ireq &= ~(1 << ln); /* clr mux rcv int */ | |
if (ttox_ireq == 0) CLR_INT (TTOX); /* all clr? */ | |
else SET_INT (TTOX); /* no, set intr */ | |
return; | |
} | |
void ttox_set_int (int32 ln) | |
{ | |
ttox_ireq |= (1 << ln); /* clr mux rcv int */ | |
SET_INT (TTOX); /* set master intr */ | |
return; | |
} | |
int32 ttox_iack (void) | |
{ | |
int32 ln; | |
for (ln = 0; ln < TTX_LINES; ln++) { /* find 1st line */ | |
if (ttox_ireq & (1 << ln)) { | |
ttox_clr_int (ln); /* clear intr */ | |
return (ttix_dib.vec + (ln * 010) + 4); /* return vector */ | |
} | |
} | |
return 0; | |
} | |
/* Reset */ | |
t_stat ttx_reset (DEVICE *dptr) | |
{ | |
int32 ln; | |
ttx_enbdis (dptr->flags & DEV_DIS); /* sync enables */ | |
sim_cancel (&ttix_unit); /* assume stop */ | |
if (ttix_unit.flags & UNIT_ATT) /* if attached, */ | |
sim_activate (&ttix_unit, tmxr_poll); /* activate */ | |
for (ln = 0; ln < TTX_LINES; ln++) /* for all lines */ | |
ttx_reset_ln (ln); | |
return auto_config (ttix_dev.name, ttx_desc.lines); /* auto config */ | |
} | |
/* Reset individual line */ | |
void ttx_reset_ln (uint32 ln) | |
{ | |
ttix_buf[ln] = 0; /* clear buf, */ | |
ttix_csr[ln] = CSR_DONE; | |
ttox_buf[ln] = 0; /* clear buf */ | |
ttox_csr[ln] = CSR_DONE; | |
sim_cancel (&ttox_unit[ln]); /* deactivate */ | |
ttix_clr_int (ln); | |
ttox_clr_int (ln); | |
return; | |
} | |
/* Attach master unit */ | |
t_stat ttx_attach (UNIT *uptr, char *cptr) | |
{ | |
t_stat r; | |
r = tmxr_attach (&ttx_desc, uptr, cptr); /* attach */ | |
if (r != SCPE_OK) return r; /* error */ | |
sim_activate (uptr, tmxr_poll); /* start poll */ | |
return SCPE_OK; | |
} | |
/* Detach master unit */ | |
t_stat ttx_detach (UNIT *uptr) | |
{ | |
int32 i; | |
t_stat r; | |
r = tmxr_detach (&ttx_desc, uptr); /* detach */ | |
for (i = 0; i < TTX_LINES; i++) /* all lines, */ | |
ttx_ldsc[i].rcve = 0; /* disable rcv */ | |
sim_cancel (uptr); /* stop poll */ | |
return r; | |
} | |
/* Show summary processor */ | |
t_stat ttx_summ (FILE *st, UNIT *uptr, int32 val, void *desc) | |
{ | |
int32 i, t; | |
for (i = t = 0; i < TTX_LINES; i++) t = t + (ttx_ldsc[i].conn != 0); | |
if (t == 1) fprintf (st, "1 connection"); | |
else fprintf (st, "%d connections", t); | |
return SCPE_OK; | |
} | |
/* SHOW CONN/STAT processor */ | |
t_stat ttx_show (FILE *st, UNIT *uptr, int32 val, void *desc) | |
{ | |
int32 i, t; | |
for (i = t = 0; i < TTX_LINES; i++) t = t + (ttx_ldsc[i].conn != 0); | |
if (t) { | |
for (i = 0; i < TTX_LINES; i++) { | |
if (ttx_ldsc[i].conn) { | |
if (val) tmxr_fconns (st, &ttx_ldsc[i], i); | |
else tmxr_fstats (st, &ttx_ldsc[i], i); | |
} | |
} | |
} | |
else fprintf (st, "all disconnected\n"); | |
return SCPE_OK; | |
} | |
/* Enable/disable device */ | |
void ttx_enbdis (int32 dis) | |
{ | |
if (dis) { | |
ttix_dev.flags = ttox_dev.flags | DEV_DIS; | |
ttox_dev.flags = ttox_dev.flags | DEV_DIS; | |
} | |
else { | |
ttix_dev.flags = ttix_dev.flags & ~DEV_DIS; | |
ttox_dev.flags = ttox_dev.flags & ~DEV_DIS; | |
} | |
return; | |
} | |
/* SHOW VECTOR processor */ | |
t_stat ttx_show_vec (FILE *st, UNIT *uptr, int32 val, void *desc) | |
{ | |
return show_vec (st, uptr, ttx_desc.lines * 2, desc); | |
} | |
/* Change number of lines */ | |
t_stat ttx_set_lines (UNIT *uptr, int32 val, char *cptr, void *desc) | |
{ | |
int32 newln, i, t; | |
t_stat r; | |
if (cptr == NULL) return SCPE_ARG; | |
newln = get_uint (cptr, 10, TTX_LINES, &r); | |
if ((r != SCPE_OK) || (newln == ttx_desc.lines)) return r; | |
if (newln == 0) return SCPE_ARG; | |
if (newln < ttx_desc.lines) { | |
for (i = newln, t = 0; i < ttx_desc.lines; i++) t = t | ttx_ldsc[i].conn; | |
if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE)) | |
return SCPE_OK; | |
for (i = newln; i < ttx_desc.lines; i++) { | |
if (ttx_ldsc[i].conn) { | |
tmxr_linemsg (&ttx_ldsc[i], "\r\nOperator disconnected line\r\n"); | |
tmxr_reset_ln (&ttx_ldsc[i]); /* reset line */ | |
} | |
ttox_unit[i].flags |= UNIT_DIS; | |
ttx_reset_ln (i); | |
} | |
} | |
else { | |
for (i = ttx_desc.lines; i < newln; i++) { | |
ttox_unit[i].flags &= ~UNIT_DIS; | |
ttx_reset_ln (i); | |
} | |
} | |
ttx_desc.lines = newln; | |
return auto_config (ttix_dev.name, newln); /* auto config */ | |
} | |
/* Show number of lines */ | |
t_stat ttx_show_lines (FILE *st, UNIT *uptr, int32 val, void *desc) | |
{ | |
fprintf (st, "lines=%d", ttx_desc.lines); | |
return SCPE_OK; | |
} |