simh-testsetgenerator/PDP10/ka10_dpk.c
2020-10-31 11:56:24 -04:00

426 lines
14 KiB
C
Raw Blame History

This file contains invisible Unicode characters

This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

/* ka10_dpk.c: Systems Concepts DK-10, Datapoint kludge.
Copyright (c) 2018-2020, 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.
The Systems Concepts DK-10, also known as the Datapoint kludge, is
a device with 16 terminal ports. It's specific to the MIT AI lab
PDP-10.
*/
#include "sim_defs.h"
#include "sim_tmxr.h"
#include "kx10_defs.h"
#define DPK_NAME "DPK"
#define DPK_DEVNUM 0604
#define DPK_LINES 16
#define DPK_IEN 04000000 /* Interrupt enable. */
#define DPK_PIA 000000007 /* PI channel assignment */
#define DPK_IDONE 000000010 /* Input char available. */
#define DPK_NXM 000000020 /* NXM. */
#define DPK_PAR 000000040 /* Parity error. */
#define DPK_BUSY 000000100 /* Ouput line busy. */
#define DPK_IN 000000200 /* State of input line. */
#define DPK_ODONE 000000400 /* Output buffer done. */
#define DPK_OLINE 017000000 /* Line number, output. */
#define DPK_CONI_BITS (DPK_PIA | DPK_IDONE | DPK_PAR | DPK_NXM | \
DPK_BUSY | DPK_IN | DPK_ODONE | DPK_OLINE)
#define DPK_FN 000000700 /* Function. */
#define DPK_SET_ODONE 000000000 /* Set output done. */
#define DPK_OSTART 000000100 /* Start output. */
#define DPK_ISTOP 000000200 /* Stop input. */
#define DPK_ISTART 000000300 /* Start input. */
#define DPK_OSTOP 000000400 /* Stop output, clear output done. */
#define DPK_OSPEED 000000500 /* Set output speed, start output. */
#define DPK_ISPEED_STOP 000000600 /* Set input speed, stop input. */
#define DPK_ISPEED_START 000000700 /* Set input speed, start input. */
#define DPK_SPEED 000007000 /* Speed code. */
#define DPK_ILINE 000170000 /* Line number. */
#define DPK_MANY 000200000 /* Apply to selected line through highest. */
#define DPK_RESET 000400000 /* Master clear. */
#define PORT_OUTPUT 1
#define PORT_INPUT 2
static t_stat dpk_devio(uint32 dev, uint64 *data);
static t_stat dpk_input_svc (UNIT *uptr);
static t_stat dpk_output_svc (UNIT *uptr);
static t_stat dpk_reset (DEVICE *dptr);
static t_stat dpk_attach (UNIT *uptr, CONST char *cptr);
static t_stat dpk_detach (UNIT *uptr);
static const char *dpk_description (DEVICE *dptr);
static t_stat dpk_help (FILE *st, DEVICE *dptr, UNIT *uptr,
int32 flag, const char *cptr);
extern int32 tmxr_poll;
TMLN dpk_ldsc[DPK_LINES] = { 0 };
TMXR dpk_desc = { DPK_LINES, 0, 0, dpk_ldsc };
static int dpk_ien = 0;
static int dpk_base = 0;
static int dpk_status = 0;
static int dpk_port[16];
static int dpk_ibuf[16];
static int dpk_ird = 0;
static int dpk_iwr = 0;
static int dpk_port_done = 0;
UNIT dpk_unit[] = {
{UDATA(dpk_input_svc, TT_MODE_8B|UNIT_IDLE|UNIT_ATTABLE, 0)},
{UDATA(dpk_output_svc, UNIT_DIS|UNIT_IDLE, 0)},
};
DIB dpk_dib = {DPK_DEVNUM, 1, &dpk_devio, NULL};
MTAB dpk_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, &dpk_desc, "Disconnect a specific line" },
{ UNIT_ATT, UNIT_ATT, "SUMMARY", NULL,
NULL, &tmxr_show_summ, (void *) &dpk_desc, "Display a summary of line states" },
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 1, "CONNECTIONS", NULL,
NULL, &tmxr_show_cstat, (void *) &dpk_desc, "Display current connections" },
{ MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, "STATISTICS", NULL,
NULL, &tmxr_show_cstat, (void *) &dpk_desc, "Display multiplexer statistics" },
{ 0 }
};
DEVICE dpk_dev = {
DPK_NAME, dpk_unit, NULL, dpk_mod,
2, 8, 0, 1, 8, 36,
NULL, NULL, dpk_reset, NULL, dpk_attach, dpk_detach,
&dpk_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_MUX, 0, dev_debug,
NULL, NULL, dpk_help, NULL, NULL, dpk_description
};
static void dpk_set_ospeed (int port, uint64 data)
{
static const char *map[] =
{ "134", "600", "110", "150", "300", "1200", "2400", "4800" };
static const char *speed;
speed = map[(data & DPK_SPEED) >> 9];
sim_debug(DEBUG_CMD, &dpk_dev, "Set port %d output speed %s\n",
port, speed);
tmxr_set_line_speed(&dpk_ldsc[port], speed);
}
static t_stat dpk_devio(uint32 dev, uint64 *data)
{
static int scan = 0;
int port;
switch(dev & 07) {
case CONO|4:
sim_debug(DEBUG_CONO, &dpk_dev, "%012llo\n", *data);
port = (*data & DPK_ILINE) >> 12;
if (*data & DPK_RESET)
dpk_reset (&dpk_dev);
if (*data & DPK_IDONE) {
dpk_status &= ~DPK_IDONE;
dpk_iwr = dpk_ird = 0;
}
if (*data & DPK_PAR)
dpk_status &= ~DPK_PAR;
if (*data & DPK_NXM)
dpk_status &= ~DPK_NXM;
switch (*data & DPK_FN) {
case DPK_SET_ODONE:
dpk_status |= DPK_ODONE;
break;
case DPK_OSTART:
dpk_port[port] |= PORT_OUTPUT;
dpk_port_done &= ~(1 << port);
if (dpk_port_done == 0)
dpk_status &= ~DPK_ODONE;
sim_activate_abs (&dpk_unit[1], 0);
break;
case DPK_ISTOP:
dpk_port[port] &= ~PORT_INPUT;
break;
case DPK_ISTART:
dpk_port[port] |= PORT_INPUT;
break;
case DPK_OSTOP:
dpk_port[port] &= ~PORT_OUTPUT;
dpk_port_done &= ~(1 << port);
if (dpk_port_done == 0)
dpk_status &= ~DPK_ODONE;
break;
case DPK_OSPEED:
dpk_set_ospeed (port, *data);
dpk_port[port] |= PORT_OUTPUT;
sim_activate_abs (&dpk_unit[1], 0);
break;
case DPK_ISPEED_STOP:
dpk_port[port] &= ~PORT_INPUT;
goto ispeed;
case DPK_ISPEED_START:
dpk_port[port] |= PORT_INPUT;
ispeed:
sim_debug(DEBUG_CMD, &dpk_dev, "Set port %d input speed %lld\n",
port, (*data & DPK_SPEED) >> 9);
break;
default:
fprintf (stderr, "Unknown function: %llo\n", *data);
exit (1);
break;
}
dpk_status &= ~DPK_PIA;
dpk_status |= *data & DPK_PIA;
break;
case CONI|4:
dpk_status &= ~DPK_OLINE;
for (port = 0; port < DPK_LINES; port++) {
scan = (scan + 1) & 017;
if (dpk_port_done & (1 << scan)) {
dpk_status |= scan << 18;
break;
}
}
*data = dpk_status & DPK_CONI_BITS;
sim_debug(DEBUG_CONI, &dpk_dev, "%07llo\n", *data);
break;
case DATAO|4:
dpk_base = *data & 03777777;
dpk_ien = ((*data & DPK_IEN) != 0);
sim_debug(DEBUG_DATAIO, &dpk_dev, "DATAO %06llo\n", *data);
break;
case DATAI|4:
if (dpk_ird == dpk_iwr) {
*data = 0;
break;
}
*data = dpk_ibuf[dpk_ird++];
dpk_ird &= 15;
sim_debug(DEBUG_DATAIO, &dpk_dev, "DATAI %06llo\n", *data);
if (dpk_ird == dpk_iwr) {
dpk_status &= ~DPK_IDONE;
}
break;
}
if (dpk_ien && (dpk_status & (DPK_IDONE | DPK_ODONE)))
set_interrupt(DPK_DEVNUM, dpk_status & DPK_PIA);
else
clr_interrupt(DPK_DEVNUM);
return SCPE_OK;
}
/* Note, the byte pointer used by the hardware has halfwords swapped. */
static int ildb (uint64 *pointer)
{
uint64 bp = *pointer;
int pos, ch, addr;
again:
pos = (bp >> 12) & 077;
pos -= 7;
addr = (bp >> 18) & 0777777;
if (pos < 0) {
pos = 36 - 7;
addr++;
addr &= 0777777;
}
if (M[addr] & 1) {
bp = M[addr];
goto again;
} else
*pointer = ((uint64)addr << 18) | (pos << 12) | 7 << 6;
ch = (M[addr] >> pos) & 0177;
return ch;
}
static void dpk_output (int port, TMLN *lp)
{
uint64 count;
int ch;
if ((dpk_port[port] & PORT_OUTPUT) == 0)
return;
if (tmxr_txdone_ln (lp) == 0)
return;
if (M[dpk_base + 2*port] == 0777777777777LL) {
dpk_port[port] &= ~PORT_OUTPUT;
dpk_status |= DPK_ODONE;
dpk_port_done |= 1 << port;
if (dpk_ien)
set_interrupt(DPK_DEVNUM, dpk_status & DPK_PIA);
return;
}
ch = ildb (&M[dpk_base + 2*port + 1]);
ch = sim_tt_outcvt(ch & 0377, TT_GET_MODE (dpk_unit[0].flags));
tmxr_putc_ln (lp, ch);
count = M[dpk_base + 2*port] - 1;
M[dpk_base + 2*port] = count & 0777777777777LL;
}
static t_stat dpk_input_svc (UNIT *uptr)
{
static int scan = 0;
int32 ch;
int i;
sim_clock_coschedule (uptr, 1000);
i = tmxr_poll_conn (&dpk_desc);
if (i >= 0) {
dpk_ldsc[i].rcve = 1;
dpk_ldsc[i].xmte = 1;
sim_debug(DEBUG_CMD, &dpk_dev, "Connect %d\n", i);
}
tmxr_poll_rx (&dpk_desc);
for (i = 0; i < DPK_LINES; i++) {
/* Round robin scan 16 lines. */
scan = (scan + 1) & 017;
ch = tmxr_getc_ln(&dpk_ldsc[scan]);
if (ch & TMXR_VALID) {
if ((dpk_port[scan] & PORT_INPUT) == 0)
continue;
dpk_ibuf[dpk_iwr++] = (scan << 18) | (ch & 0177);
dpk_iwr &= 15;
dpk_status |= DPK_IDONE;
if (dpk_ien)
set_interrupt(DPK_DEVNUM, dpk_status & DPK_PIA);
break;
}
}
return SCPE_OK;
}
static t_stat dpk_output_svc (UNIT *uptr)
{
int i;
for (i = 0; i < DPK_LINES; i++) {
dpk_output (i, &dpk_ldsc[i]);
}
tmxr_poll_tx (&dpk_desc);
sim_activate_after (uptr, 1000000);
return SCPE_OK;
}
static t_stat dpk_reset (DEVICE *dptr)
{
int i;
sim_debug(DEBUG_CMD, &dpk_dev, "Reset\n");
if (dpk_unit->flags & UNIT_ATT)
sim_activate (&dpk_unit[0], tmxr_poll);
else {
sim_cancel (&dpk_unit[0]);
sim_cancel (&dpk_unit[1]);
}
dpk_ien = 0;
dpk_base = 0;
dpk_status = 0;
dpk_ird = dpk_iwr = 0;
memset (dpk_port, 0, sizeof dpk_port);
dpk_port_done = 0;
clr_interrupt(DPK_DEVNUM);
for (i = 0; i < DPK_LINES; i++) {
tmxr_set_line_unit (&dpk_desc, i, &dpk_unit[0]);
tmxr_set_line_output_unit (&dpk_desc, i, &dpk_unit[1]);
}
return SCPE_OK;
}
static t_stat dpk_attach (UNIT *uptr, CONST char *cptr)
{
t_stat stat;
int i;
stat = tmxr_attach (&dpk_desc, uptr, cptr);
for (i = 0; i < DPK_LINES; i++) {
dpk_ldsc[i].rcve = 0;
dpk_ldsc[i].xmte = 0;
}
if (stat == SCPE_OK)
sim_activate (uptr, tmxr_poll);
return stat;
}
static t_stat dpk_detach (UNIT *uptr)
{
t_stat stat = tmxr_detach (&dpk_desc, uptr);
int i;
for (i = 0; i < DPK_LINES; i++) {
dpk_ldsc[i].rcve = 0;
dpk_ldsc[i].xmte = 0;
}
dpk_status = 0;
sim_cancel (&dpk_unit[0]);
sim_cancel (&dpk_unit[1]);
return stat;
}
static t_stat dpk_help (FILE *st, DEVICE *dptr, UNIT *uptr,
int32 flag, const char *cptr)
{
fprintf (st, "DPK Datapoint kludge terminal multiplexer\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 DPK 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 DPK DISCONNECT command, or a DETACH DPK command.\n\n");
fprintf (st, "Other special commands:\n\n");
fprintf (st, " sim> SHOW DPK CONNECTIONS show current connections\n");
fprintf (st, " sim> SHOW DPK STATISTICS show statistics for active connections\n");
fprintf (st, " sim> SET DPKn 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 DPK is detached.\n");
return SCPE_OK;
}
static const char *dpk_description (DEVICE *dptr)
{
return "Systems Concepts DK-10, Datapoint kludge";
}