Fix divide of 0 on KS10 processor. Fix write access for auxiliary processor memory. Updated support new PIDP10 panel. Allow eight Unibux ports on the TEN device. Added GE DATANET-760 device to PDP6 Removed USER mode restriction for idle detection. Added Data Disc 6600 Television Display System.
424 lines
11 KiB
C
424 lines
11 KiB
C
/* 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
|