/* ka10_tk10.c: MTY, Morton multiplex box: Terminal multiplexor. Copyright (c) 2018-2019, Lars Brinkhoff 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 RICHARD CORNWELL 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. This is a device with 32 high-speed terminal lines. It's specific to the MIT Mathlab and Dynamic Modeling PDP-10s. */ #include "sim_defs.h" #include "sim_tmxr.h" #include "kx10_defs.h" #ifndef NUM_DEVS_MTY #define NUM_DEVS_MTY 0 #endif #if (NUM_DEVS_MTY > 0) #define MTY_NAME "MTY" #define MTY_DEVNUM 0400 #define MTY_LINES 32 #define MTY_FIRST C1 /* Frist character in output word. */ #define MTY_PIA 0000007 /* PI channel assignment */ #define MTY_RQINT 0000010 /* Request interrupt. */ #define MTY_ODONE 0000010 /* Output done. */ #define MTY_IDONE 0000040 /* Input done. */ #define MTY_STOP 0000200 /* Clear output done. */ #define MTY_LINE 0370000 /* Line number. */ #define MTY_DONE (MTY_IDONE | MTY_ODONE) #define MTY_CONI_BITS (MTY_PIA | MTY_DONE | MTY_LINE) #define MTY_CONO_BITS (MTY_PIA | MTY_LINE) static t_stat mty_devio(uint32 dev, uint64 *data); static t_stat mty_input_svc (UNIT *uptr); static t_stat mty_output_svc (UNIT *uptr); static t_stat mty_reset (DEVICE *dptr); static t_stat mty_attach (UNIT *uptr, CONST char *cptr); static t_stat mty_detach (UNIT *uptr); static const char *mty_description (DEVICE *dptr); static t_stat mty_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); extern int32 tmxr_poll; static uint32 mty_active_bitmask; static uint64 mty_output_word[MTY_LINES]; static int32 mty_input_character; TMLN mty_ldsc[MTY_LINES] = { 0 }; TMXR mty_desc = { MTY_LINES, 0, 0, mty_ldsc }; static uint64 status = 0; UNIT mty_unit[] = { {UDATA(mty_input_svc, TT_MODE_7B|UNIT_IDLE|UNIT_ATTABLE, 0)}, /* 0 */ {UDATA(mty_output_svc, UNIT_DIS|UNIT_IDLE, 0)}, /* 0 */ }; DIB mty_dib = {MTY_DEVNUM, 1, &mty_devio, NULL}; MTAB mty_mod[] = { { TT_MODE, TT_MODE_7B, "7b", "7B", NULL, NULL, NULL, "7 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, &mty_desc, "Disconnect a specific line" }, { UNIT_ATT, UNIT_ATT, "SUMMARY", NULL, NULL, &tmxr_show_summ, (void *) &mty_desc, "Display a summary of line states" }, { MTAB_XTD|MTAB_VDV|MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat, (void *) &mty_desc, "Display current connections" }, { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, (void *) &mty_desc, "Display multiplexer statistics" }, { 0 } }; DEVICE mty_dev = { MTY_NAME, mty_unit, NULL, mty_mod, 2, 8, 0, 1, 8, 36, NULL, NULL, mty_reset, NULL, mty_attach, mty_detach, &mty_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_MUX, 0, dev_debug, NULL, NULL, mty_help, NULL, NULL, mty_description }; static t_stat mty_devio(uint32 dev, uint64 *data) { int line; uint64 word; switch(dev & 07) { case CONO: sim_debug(DEBUG_CONO, &mty_dev, "%06llo\n", *data); status &= ~MTY_CONO_BITS; status |= *data & MTY_CONO_BITS; line = (status & MTY_LINE) >> 12; if (*data & MTY_STOP) { status &= ~MTY_ODONE; sim_debug(DEBUG_CMD, &mty_dev, "Clear output done line %d\n", line); } if (*data & MTY_RQINT) { status |= MTY_ODONE; sim_debug(DEBUG_CMD, &mty_dev, "Request interrupt line %d\n", line); } if ((*data & (MTY_STOP | MTY_RQINT)) == 0) sim_debug(DEBUG_CMD, &mty_dev, "Select line %d\n", line); break; case CONI: *data = status & MTY_CONI_BITS; sim_debug(DEBUG_CONI, &mty_dev, "%06llo\n", *data); break; case DATAO: line = (status & MTY_LINE) >> 12; word = *data; sim_debug(DEBUG_DATAIO, &mty_dev, "DATAO line %d -> %012llo\n", line, word); mty_output_word[line] = word | MTY_FIRST; mty_active_bitmask |= 1 << line; sim_activate_abs (&mty_unit[1], 0); status &= ~MTY_ODONE; break; case DATAI: line = (status & MTY_LINE) >> 12; *data = mty_input_character; sim_debug(DEBUG_DATAIO, &mty_dev, "DATAI line %d -> %012llo\n", line, *data); status &= ~MTY_IDONE; sim_activate_abs (&mty_unit[0], 0); break; } if (status & MTY_DONE) set_interrupt(MTY_DEVNUM, status & MTY_PIA); else clr_interrupt(MTY_DEVNUM); return SCPE_OK; } static t_stat mty_input_svc (UNIT *uptr) { static int scan = 0; int32 ch; int i; sim_clock_coschedule (uptr, 1000); i = tmxr_poll_conn (&mty_desc); if (i >= 0) { mty_ldsc[i].rcve = 1; mty_ldsc[i].xmte = 1; sim_debug(DEBUG_CMD, &mty_dev, "Connect %d\n", i); } tmxr_poll_rx (&mty_desc); if (status & MTY_DONE) return SCPE_OK; for (i = 0; i < MTY_LINES; i++) { /* Round robin scan 32 lines. */ scan = (scan + 1) & 037; ch = tmxr_getc_ln (&mty_ldsc[scan]); if (ch & TMXR_VALID) { mty_input_character = ch & 0177; sim_debug(DEBUG_DETAIL, &mty_dev, "Input ready line %d\n", scan); status &= ~MTY_LINE; status |= scan << 12; status |= MTY_IDONE; set_interrupt(MTY_DEVNUM, status & MTY_PIA); /* No more scanning until DATAI has read this character. */ sim_cancel (&mty_unit[0]); break; } } return SCPE_OK; } static t_stat mty_output_svc (UNIT *uptr) { static int scan = 0; uint64 word; int i, ch; int32 txdone; if (status & MTY_DONE) return SCPE_OK; for (i = 0; i < MTY_LINES; i++) { /* Round robin scan 32 lines. */ scan = (scan + 1) & 037; if ((mty_active_bitmask & (1 << scan)) == 0 || (txdone = tmxr_txdone_ln (&mty_ldsc[scan])) == 0) continue; /* Write up to five characters extracted from a word. NUL can only be in the first character. */ word = mty_output_word[scan]; if (word != 0) { ch = (word >> 29) & 0177; ch = sim_tt_outcvt(ch, TT_GET_MODE (mty_unit[0].flags)); if (tmxr_putc_ln (&mty_ldsc[scan], ch) != SCPE_STALL) mty_output_word[scan] = (word << 7) & FMASK; } else if (txdone == 1) { sim_debug(DEBUG_DETAIL, &mty_dev, "Output ready line %d\n", scan); status &= ~MTY_LINE; status |= scan << 12; status |= MTY_ODONE; set_interrupt(MTY_DEVNUM, status & MTY_PIA); mty_active_bitmask &= ~(1 << scan); /* Stop scanning; can only signal output done for one line at a time. */ break; } } tmxr_poll_tx (&mty_desc); if ((status & MTY_ODONE) == 0) { /* SIMH will actually schedule this UNIT when output is due according to the line speed. */ sim_activate_after (uptr, 1000000); } return SCPE_OK; } static t_stat mty_reset (DEVICE *dptr) { int i; sim_debug(DEBUG_CMD, &mty_dev, "Reset\n"); if (mty_unit->flags & UNIT_ATT) { sim_activate (&mty_unit[0], tmxr_poll); sim_activate_after (&mty_unit[1], 100); } else { sim_cancel (&mty_unit[0]); sim_cancel (&mty_unit[1]); } status = 0; clr_interrupt(MTY_DEVNUM); for (i = 0; i < MTY_LINES; i++) { tmxr_set_line_unit (&mty_desc, i, &mty_unit[0]); tmxr_set_line_output_unit (&mty_desc, i, &mty_unit[1]); tmxr_set_line_speed(&mty_ldsc[i], "80000"); } return SCPE_OK; } static t_stat mty_attach (UNIT *uptr, CONST char *cptr) { t_stat stat; int i; stat = tmxr_attach (&mty_desc, uptr, cptr); for (i = 0; i < MTY_LINES; i++) { mty_ldsc[i].rcve = 0; mty_ldsc[i].xmte = 0; } if (stat == SCPE_OK) { status = 0; sim_activate (uptr, tmxr_poll); } mty_active_bitmask = 0; return stat; } static t_stat mty_detach (UNIT *uptr) { t_stat stat = tmxr_detach (&mty_desc, uptr); int i; for (i = 0; i < MTY_LINES; i++) { mty_ldsc[i].rcve = 0; mty_ldsc[i].xmte = 0; } status = 0; sim_cancel (&mty_unit[0]); sim_cancel (&mty_unit[1]); return stat; } static t_stat mty_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) { fprintf (st, "MTY Morton box terminal multiplexor\n\n"); fprintf (st, "The MTY supported 32 high-speed lines at up to 80 bits/second.\n"); fprintf (st, "this simulation.\n\n"); fprintf (st, "The ATTACH command specifies the port to be used:\n\n"); tmxr_attach_help (st, dptr, uptr, flag, cptr); fprintf (st, "Terminals can be set to one of three modes: 7P, 7B, or 8B.\n\n"); fprintf (st, " mode input characters output characters\n\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 mode is 7B.\n\n"); fprintf (st, "Once MTY is attached and the simulator is running, the terminals listen for\n"); fprintf (st, "connections on the specified port. They assume that the incoming connections\n"); fprintf (st, "are Telnet connections. The connections remain open until disconnected either\n"); fprintf (st, "by the Telnet client, a SET MTY DISCONNECT command, or a DETACH MTY command.\n\n"); fprintf (st, "Other special commands:\n\n"); fprintf (st, " sim> SHOW MTY CONNECTIONS show current connections\n"); fprintf (st, " sim> SHOW MTY STATISTICS show statistics for active connections\n"); fprintf (st, " sim> SET MTYn DISCONNECT disconnects the specified line.\n"); fprint_reg_help (st, &dc_dev); fprintf (st, "\nThe terminals do not support save and restore. All open connections\n"); fprintf (st, "are lost when the simulator shuts down or MTY is detached.\n"); return SCPE_OK; } static const char *mty_description (DEVICE *dptr) { return "Morton box: Terminal multiplexor"; } #endif