diff --git a/PDP10/kx10_defs.h b/PDP10/kx10_defs.h index 75da569..656cc78 100644 --- a/PDP10/kx10_defs.h +++ b/PDP10/kx10_defs.h @@ -542,6 +542,8 @@ extern DEVICE dup_dev; extern DEVICE tcu_dev; extern DEVICE ddc_dev; extern DEVICE tym_dev; +extern DEVICE ge_dev; +extern DEVICE gtyo_dev; #if KS @@ -750,6 +752,7 @@ extern void ka10_lights_clear_aux (int); #define NUM_DEVS_DSK 1 #define NUM_DEVS_DCS 1 #define NUM_DEVS_SLAVE PDP6 +#define NUM_DEVS_GE PDP6 #endif #if !(PDP6 | KS) #define NUM_DEVS_DC 1 diff --git a/PDP10/kx10_sys.c b/PDP10/kx10_sys.c index 6d2eb9a..ebce02d 100644 --- a/PDP10/kx10_sys.c +++ b/PDP10/kx10_sys.c @@ -245,6 +245,10 @@ DEVICE *sim_devices[] = { #endif #if NUM_DEVS_TYM > 0 &tym_dev, +#endif +#if NUM_DEVS_GE > 0 + &ge_dev, + >yo_dev, #endif NULL }; diff --git a/PDP10/pdp6_ge.c b/PDP10/pdp6_ge.c new file mode 100644 index 0000000..21aac50 --- /dev/null +++ b/PDP10/pdp6_ge.c @@ -0,0 +1,424 @@ +/* pdp6_ge.c: GE DATANET-760 with four consoles. + + Copyright (c) 2023, 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 LARS BRINKHOFF 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 implements the MIT AI lab interface to a GE DATANET-760 with + four consoles. It consists of two somewhat independent IO bus + devices: 070 GTYI for keyboard input, and 750 GTYO for display + output. This file presents the two as a single GE device to SIMH + users. +*/ + +#include "kx10_defs.h" +#include "sim_tmxr.h" + +#ifndef NUM_DEVS_GE +#define NUM_DEVS_GE 0 +#endif + +#if NUM_DEVS_GE > 0 + +#define GTYI_DEVNUM 0070 /* GE console input. */ +#define GTYO_DEVNUM 0750 /* GE console output. */ + +#define GE_CONSOLES 4 + +#define GTYI_PIA 00007 /* PI assignment. */ +#define GTYI_DONE 00010 /* Input data ready. */ +#define GTYI_STATUS (GTYI_PIA | GTYI_DONE) + +#define GTYO_PIA 00007 /* PI assignment. */ +#define GTYO_DONE 00100 /* Output data ready. */ +#define GTYO_FROB 00200 /* Set done? */ +#define GTYO_STATUS (GTYO_PIA | GTYO_DONE) + +#define STATUS u3 +#define DATA u4 +#define PORT u5 +#define LP u6 + +#define GE_SOH 001 /* Start of header/message. */ +#define GE_STX 002 /* Start of text. */ +#define GE_ETX 003 /* End of text. */ + + +static t_stat gtyi_svc(UNIT *uptr); +static t_stat gtyo_svc(UNIT *uptr); +static t_stat gtyi_devio(uint32 dev, uint64 *data); +static t_stat gtyo_devio(uint32 dev, uint64 *data); +static t_stat ge_reset(DEVICE *dptr); +static t_stat ge_attach(UNIT *uptr, CONST char *ptr); +static t_stat ge_detach(UNIT *uptr); +static t_stat ge_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr); +static const char *ge_description(DEVICE *dptr); + +static void gtyo_done(void); +static void gtyo_soh(char data); +static void gtyo_adr(char data); +static void gtyo_status(char data); +static void gtyo_stx(char data); +static void gtyo_text(char data); +static void gtyo_lp(char data); +static void (*gtyo_process)(char data) = gtyo_soh; + +DIB gtyi_dib = { GTYI_DEVNUM, 1, >yi_devio, NULL }; +DIB gtyo_dib = { GTYO_DEVNUM, 1, >yo_devio, NULL }; + +UNIT ge_unit[2] = { + { UDATA(>yi_svc, UNIT_IDLE|UNIT_ATTABLE, 0), 1000 }, + { UDATA(>yo_svc, UNIT_IDLE|UNIT_ATTABLE, 0), 1000 }, +}; +#define gtyi_unit (&ge_unit[0]) +#define gtyo_unit (&ge_unit[1]) + +static REG ge_reg[] = { + { NULL } +}; + +static MTAB ge_mod[] = { + { 0 } +}; + +#define DEBUG_TRC 0x0000400 + +static DEBTAB ge_debug[] = { + {"TRACE", DEBUG_TRC, "Routine trace"}, + {"CMD", DEBUG_CMD, "Command Processing"}, + {"CONO", DEBUG_CONO, "CONO instructions"}, + {"CONI", DEBUG_CONI, "CONI instructions"}, + {"DATAIO", DEBUG_DATAIO, "DATAI/O instructions"}, + {"IRQ", DEBUG_IRQ, "Debug IRQ requests"}, + {0}, +}; + +DEVICE ge_dev = { + "GE", ge_unit, ge_reg, ge_mod, + 2, 8, 18, 1, 8, 36, + NULL, NULL, &ge_reset, + NULL, &ge_attach, &ge_detach, + >yi_dib, DEV_DISABLE | DEV_DIS | DEV_DEBUG | DEV_MUX, + 0, ge_debug, + NULL, NULL, NULL, + &ge_attach_help, NULL, &ge_description +}; + +DEVICE gtyo_dev = { + "GTYO", NULL, NULL, NULL, + 0, 8, 18, 1, 8, 36, + NULL, NULL, NULL, + NULL, NULL, NULL, + >yo_dib, DEV_DIS | DEV_MUX, + 0, NULL, + NULL, NULL, NULL, + NULL, NULL, NULL +}; + +static TMLN ge_ldsc[GE_CONSOLES]; +static TMXR ge_desc = { GE_CONSOLES, 0, 0, ge_ldsc }; + +static t_stat ge_reset(DEVICE *dptr) +{ + sim_debug(DEBUG_TRC, dptr, "ge_reset()\n"); + + if (ge_dev.flags & DEV_DIS) + gtyo_dev.flags |= DEV_DIS; + else + gtyo_dev.flags &= ~DEV_DIS; + + if (gtyi_unit->flags & UNIT_ATT) { + sim_activate(gtyi_unit, 10); + } else { + sim_cancel(gtyi_unit); + sim_cancel(gtyo_unit); + } + + return SCPE_OK; +} + +static t_stat ge_attach(UNIT *uptr, CONST char *cptr) +{ + t_stat r; + + if (!cptr || !*cptr) + return SCPE_ARG; + ge_desc.buffered = 1000; + r = tmxr_attach(&ge_desc, uptr, cptr); + if (r != SCPE_OK) + return r; + sim_debug(DEBUG_TRC, &ge_dev, "activate connection\n"); + gtyi_unit->STATUS = 0; + gtyo_unit->STATUS = 0; + gtyo_process = gtyo_soh; + sim_activate(gtyi_unit, 10); + + return SCPE_OK; +} + +static t_stat ge_detach(UNIT *uptr) +{ + t_stat r; + + if (!(uptr->flags & UNIT_ATT)) + return SCPE_OK; + sim_cancel(gtyi_unit); + sim_cancel(gtyo_unit); + r = tmxr_detach(&ge_desc, uptr); + uptr->filename = NULL; + return r; +} + +static void gtyi_poll(UNIT *uptr) +{ + int32 ch; + int i; + + tmxr_poll_rx(&ge_desc); + for (i = 0; i < GE_CONSOLES; i++) { + if (!ge_ldsc[i].rcve) + continue; + + if (!ge_ldsc[i].conn) { + ge_ldsc[i].rcve = 0; + tmxr_reset_ln(&ge_ldsc[i]); + sim_debug(DEBUG_CMD, &ge_dev, "Port %d connection lost\n", i); + continue; + } + + ch = tmxr_getc_ln(&ge_ldsc[i]); + if (ch & TMXR_VALID) { + ch &= 0177; + sim_debug(DEBUG_CMD, &ge_dev, "Port %d got %03o\n", i, ch); + if (ch >= 0141 && ch <= 0172) + ch -= 0100; + else if (ch == 0140 || (ch >= 0173 && ch <= 0174)) { + sim_debug(DEBUG_CMD, &ge_dev, "Discard invalid character\n"); + continue; + } + + uptr->DATA = ch; + uptr->PORT = i; + uptr->STATUS |= GTYI_DONE; + if (uptr->STATUS & 7) + sim_debug(DEBUG_CMD, &ge_dev, "GTYI interrupt on channel %d\n", uptr->STATUS & 7); + set_interrupt(GTYI_DEVNUM, uptr->STATUS); + ge_ldsc[i].rcve = 0; + break; + } + } +} + +static t_stat gtyi_svc(UNIT *uptr) +{ + int32 n; + + n = tmxr_poll_conn(&ge_desc); + if (n >= 0) { + sim_debug(DEBUG_CMD, &ge_dev, "got connection\n"); + ge_ldsc[n].rcve = 1; + } + + if ((uptr->STATUS & GTYI_DONE) == 0) + gtyi_poll(uptr); + + sim_activate_after(uptr, 10000); + return SCPE_OK; +} + +static t_stat gtyo_svc(UNIT *uptr) +{ + switch (tmxr_putc_ln(&ge_ldsc[uptr->PORT], uptr->DATA)) { + case SCPE_OK: + sim_debug(DEBUG_CMD, &ge_dev, "Sent %03o to console %d\n", + uptr->DATA, uptr->PORT); + gtyo_done(); + break; + case SCPE_LOST: + ge_ldsc[uptr->PORT].rcve = 0; + tmxr_reset_ln(&ge_ldsc[uptr->PORT]); + sim_debug(DEBUG_CMD, &ge_dev, "lost\n"); + break; + case SCPE_STALL: + sim_debug(DEBUG_CMD, &ge_dev, "stall\n"); + sim_clock_coschedule(uptr, 1000); + break; + default: + break; + } + + tmxr_poll_tx(&ge_desc); + return SCPE_OK; +} + +static t_stat ge_attach_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr) +{ +const char helpString[] = + /* The '*'s in the next line represent the standard text width of a help line */ + /****************************************************************************/ + " The %D device connects a secondary processor that is sharing memory with the.\n" + " primary.\n\n" + " The device must be attached to a receive port, this is done by using the\n" + " ATTACH command to specify the receive port number.\n" + "\n" + "+sim> ATTACH %U port\n" + "\n" + ; + + return scp_help(st, dptr, uptr, flag, helpString, cptr); +} + +static const char *ge_description(DEVICE *dptr) +{ + return "GE DATANET-760"; +} + +static void gtyo_done(void) +{ + gtyo_unit->STATUS |= GTYO_DONE; + set_interrupt(GTYO_DEVNUM, gtyo_unit->STATUS); + if (gtyo_unit->STATUS & 7) + sim_debug(DEBUG_CMD, &ge_dev, "GTYO interrupt on channel %d\n", gtyo_unit->STATUS & 7); +} + +static void gtyo_soh(char data) { + if (data == GE_SOH) { + gtyo_process = gtyo_adr; + gtyo_unit->LP = 0; + } + gtyo_done(); +} + +static void gtyo_adr(char data) { + switch(data) { + case 0140: + case 0150: + case 0160: + case 0170: + gtyo_unit->PORT = (data >> 3) & 3; + gtyo_process = gtyo_status; + break; + default: + gtyo_process = gtyo_soh; + break; + } + gtyo_done(); +} + +static void gtyo_status(char data) { + if (data == 0) + gtyo_process = gtyo_stx; + else + gtyo_process = gtyo_soh; + gtyo_done(); +} + +static void gtyo_stx(char data) { + if (data == GE_STX) + gtyo_process = gtyo_text; + gtyo_done(); +} + +static void gtyo_text(char data) { + if (data == GE_ETX) { + gtyo_process = gtyo_lp; + gtyo_done(); + } else { + gtyo_unit->DATA = data; + sim_activate_after(gtyo_unit, 10000); + } +} + +static void gtyo_lp(char data) { + if (gtyo_unit->LP != 0) + sim_debug(DEBUG_CMD, &ge_dev, "Checksum mismatch\n"); + gtyo_process = gtyo_soh; + gtyo_done(); +} + +t_stat gtyi_devio(uint32 dev, uint64 *data) +{ + UNIT *uptr = gtyi_unit; + + switch(dev & 03) { + case CONO: + sim_debug(DEBUG_CONO, &ge_dev, "GTYI %012llo\n", *data); + uptr->STATUS &= ~GTYI_PIA; + uptr->STATUS |= *data & GTYI_PIA; + break; + case CONI: + *data = uptr->STATUS & GTYI_STATUS; + sim_debug(DEBUG_CONI, &ge_dev, "GTYI %012llo\n", *data); + break; + case DATAI: + *data = uptr->DATA | (uptr->PORT << 18); + sim_debug(DEBUG_DATAIO, &ge_dev, "GTYI %012llo\n", *data); + uptr->STATUS &= ~GTYI_DONE; + sim_debug(DEBUG_IRQ, &ge_dev, "Clear GTYI interrupt\n"); + clr_interrupt(GTYI_DEVNUM); + ge_ldsc[uptr->PORT].rcve = 1; + sim_activate(gtyi_unit, 10); + break; + } + + return SCPE_OK; +} + +t_stat gtyo_devio(uint32 dev, uint64 *data) +{ + UNIT *uptr = gtyo_unit; + int ch; + + switch(dev & 03) { + case CONO: + sim_debug(DEBUG_CONO, &ge_dev, "GTYO %012llo\n", *data); + sim_debug(DEBUG_IRQ, &ge_dev, "Clear GTYO interrupt\n"); + clr_interrupt(GTYO_DEVNUM); + uptr->STATUS &= ~GTYO_PIA; + uptr->STATUS |= *data & GTYO_PIA; + if (*data & GTYO_FROB) + gtyo_done(); + break; + case CONI: + *data = uptr->STATUS & GTYO_STATUS; + sim_debug(DEBUG_CONI, &ge_dev, "GTYO %012llo\n", *data); + break; + case DATAO: + sim_debug(DEBUG_DATAIO, &ge_dev, "GTYO %012llo\n", *data); + if (uptr->STATUS & GTYO_DONE) { + sim_debug(DEBUG_IRQ, &ge_dev, "Clear GTYO interrupt\n"); + clr_interrupt(GTYO_DEVNUM); + uptr->STATUS &= ~GTYO_DONE; + ch = *data & 0177; + ch ^= 0177; + ch = ((ch << 1) | (ch >> 6)) & 0177; + if (ch >= 040 && ch <= 0137) + sim_debug(DEBUG_DATAIO, &ge_dev, "Character %03o %c\n", ch, ch); + else + sim_debug(DEBUG_DATAIO, &ge_dev, "Character %03o\n", ch); + uptr->LP ^= ch; + sim_debug(DEBUG_DATAIO, &ge_dev, "LP %03o\n", uptr->LP); + gtyo_process(ch); + } + break; + } + + return SCPE_OK; +} +#endif diff --git a/makefile b/makefile index 867985c..8826ddb 100644 --- a/makefile +++ b/makefile @@ -1421,7 +1421,8 @@ PDP6 = ${PDP6D}/kx10_cpu.c ${PDP6D}/kx10_sys.c ${PDP6D}/kx10_cty.c \ ${PDP6D}/kx10_lp.c ${PDP6D}/kx10_pt.c ${PDP6D}/kx10_cr.c \ ${PDP6D}/kx10_cp.c ${PDP6D}/pdp6_dct.c ${PDP6D}/pdp6_dtc.c \ ${PDP6D}/pdp6_mtc.c ${PDP6D}/pdp6_dsk.c ${PDP6D}/pdp6_dcs.c \ - ${PDP6D}/kx10_dpy.c ${PDP6D}/pdp6_slave.c ${DISPLAYL} ${DISPLAY340} + ${PDP6D}/kx10_dpy.c ${PDP6D}/pdp6_slave.c ${PDP6D}/pdp6_ge.c \ + ${DISPLAYL} ${DISPLAY340} PDP6_OPT = -DPDP6=1 -DUSE_INT64 -I ${PDP6D} -DUSE_SIM_CARD ${DISPLAY_OPT} ${PDP6_DISPLAY_OPT} ifneq (${PANDA_LIGHTS},) # ONLY for Panda display.