simh-testsetgenerator/PDP18B/pdp18b_g2.c

429 lines
14 KiB
C

/* pdp18b_g2.c: PDP-7/9 Bell Labs "GRAPHIC-2" subsystem (as a TTY!!)
from 13-Sep-15 version of pdp18b_tt1.c
Copyright (c) 1993-2015, Robert M Supnik
Copyright (c) 2016, Philip L Budne
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.
Doug McIlroy had this to say about the BTL PDP-7 Ken Thompson
created UNIX on:
The pdp7 was cast off by the visual and acoustics research department.
Bill Ninke et al. built graphic II on it -- a graphics attachment as big
as the pdp7 itself. The disk was an amazing thing about 2' in diameter,
mounted on a horizontal axis. Mystery crashes bedeviled it until somebody
realized that the axis was perpendicular to the loading dock 4 floors
below. A 90-degree turn solved the problem.
The graphics system consists of eleven PDP-7 "devices",
UNIX only uses six, and only three of the six are simulated here
(and *JUST* enough of those to figure out the text being displayed)!!
G2D1 GRAPHICS-2 display output
G2DS GRAPHICS-2 display status
G2KB GRAPHICS-2 keyboard
G2PB GRAPHICS-2 push buttons
17-Mar-16 PLB Cloned from 13-Sep-15 version of pdp18b_tt1.c
*/
#include "pdp18b_defs.h"
#ifdef GRAPHICS2
#include "sim_sock.h"
#include "sim_tmxr.h"
#include <ctype.h>
uint32 g2do_buf = 0; /* output char */
uint8 g2kb_done = 0; /* keyboard flag */
uint32 g2kb_buf = 0; /* keyboard buffer */
uint8 g2pb_done = 0; /* button flag */
uint32 g2pb_bbuf = 0; /* button buffer */
uint32 g2pb_lbuf = 0; /* button lights */
uint32 g2_dpyaddr = 0; /* display address */
int32 g2_dpycount = 0; /* character count */
/* terminal mux data */
TMLN g2_ldsc = { 0 }; /* line descriptor */
TMXR g2_desc = { 1, 0, 0, &g2_ldsc }; /* mux descriptor */
/* kernel display lists always start like this: */
static const int32 g2_expect[3] = {
0065057, /* PARAM: clear blink, clear light pen, scale=1, intensity=3 */
0147740, /* X-Y: invisible, no delay, Y=01740 (992) */
0160000 /* X-Y: invisible, settling delay, X=0 */
};
extern int32 *M;
extern int32 int_hwre[API_HLVL+1];
extern int32 api_vec[API_HLVL][32];
extern int32 tmxr_poll;
extern int32 stop_inst;
int32 g2d1 (int32 dev, int32 pulse, int32 dat);
int32 g2kb (int32 dev, int32 pulse, int32 dat);
int32 g2pb (int32 dev, int32 pulse, int32 dat);
t_stat g2kb_svc (UNIT *uptr);
t_bool g2kb_test_done ();
void g2kb_set_done ();
void g2kb_clr_done ();
t_bool g2pb_test_done ();
void g2pb_set_done ();
void g2pb_clr_done ();
t_stat g2d1_svc (UNIT *uptr);
t_stat g2_attach (UNIT *uptr, char *cptr);
t_stat g2_detach (UNIT *uptr);
t_stat g2_reset (DEVICE *dptr);
/* G2 keyboard data structures
g2kb_dev G2kb device descriptor
g2kb_unit G2kb unit descriptor
g2kb_reg G2kb register list
g2kb_mod G2kb modifiers list
*/
/* push button device number is contiguous with keyboard */
DIB g2kb_dib = { DEV_G2KB, 2, NULL, { &g2kb, &g2pb } };
UNIT g2kb_unit = {
UDATA (&g2kb_svc, UNIT_IDLE|UNIT_ATTABLE, 0), KBD_POLL_WAIT
};
REG g2kb_reg[] = {
{ ORDATA (BUF, g2kb_buf, 1) },
{ ORDATA (DONE, g2kb_done, 1) },
{ FLDATA (INT, int_hwre[API_G2], INT_V_G2) },
{ DRDATA (TIME, g2kb_unit.wait, 24), REG_NZ + PV_LEFT },
{ ORDATA (BUTTONS, g2pb_bbuf, 1) },
{ ORDATA (LITES, g2pb_lbuf, 1) },
{ NULL }
};
MTAB g2kb_mod[] = {
{ UNIT_ATT, UNIT_ATT, "summary", NULL,
NULL, &tmxr_show_summ, (void *) &g2_desc },
{ MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT",
&tmxr_dscln, NULL, (void *) &g2_desc },
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL,
NULL, &tmxr_show_cstat, (void *) &g2_desc },
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL,
NULL, &tmxr_show_cstat, (void *) &g2_desc },
{ MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
NULL, &show_devno, NULL },
{ 0 }
};
DEVICE g2kb_dev = {
"G2KB", /* name */
&g2kb_unit, /* units */
g2kb_reg, g2kb_mod, /* registers, modifiers */
1, /* numunits */
10, 31, /* aradix, awidth */
1, 8, 8, /* aincr, dradix, dwidth */
&tmxr_ex, &tmxr_dep, &g2_reset, /* examine, deposit, reset */
NULL, &g2_attach, &g2_detach, /* boot, attach, detach */
&g2kb_dib, DEV_MUX | DEV_DISABLE /* ctxt, flags */
};
/* G2 Display Output Device 1 data structures
g2d1_dev g2d1 device descriptor
g2d1_unit g2d1 unit descriptor
g2d1_reg g2d1 register list
*/
DIB g2d1_dib = { DEV_G2D1, 1, NULL, { &g2d1 } };
UNIT g2d1_unit = { UDATA (&g2d1_svc, 0, 0), SERIAL_OUT_WAIT };
REG g2d1_reg[] = {
{ ORDATA (DPYADDR, g2_dpyaddr, 1) },
{ FLDATA (INT, int_hwre[API_G2], INT_V_G2) },
{ URDATA (TIME, g2d1_unit.wait, 10, 24, 0, 1, PV_LEFT) },
{ NULL }
};
MTAB g2d1_mod[] = {
{ MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT",
&tmxr_dscln, NULL, &g2_desc },
{ MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG",
&tmxr_set_log, &tmxr_show_log, &g2_desc },
{ MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG",
&tmxr_set_nolog, NULL, &g2_desc },
{ MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
NULL, &show_devno, NULL },
{ 0 }
};
DEVICE g2d1_dev = {
"G2D1", /* name */
&g2d1_unit, /* units */
g2d1_reg, g2d1_mod, /* registers, modifiers */
1, /* numunits */
10, 31, /* aradix, awidth */
1, 8, 8, /* aincr, dradix, dwidth */
NULL, NULL, &g2_reset, /* examine, deposit, reset */
NULL, NULL, NULL, /* boot, attach, detach */
&g2d1_dib, DEV_DISABLE /* ctxt, flags */
};
/****************************************************************
* IOT routines
*/
/* Keyboard input IOT routine */
/* real device might have done bitwise decode?! */
int32 g2kb (int32 dev, int32 pulse, int32 dat)
{
if (pulse == 001) { /* sck */
if (g2kb_done) {
dat = dat | IOT_SKP;
}
}
else if (pulse == 002) { /* lck */
g2kb_clr_done (); /* clear flag */
dat = dat | g2kb_buf; /* return buffer */
}
else if (pulse == 004) { /* cck */
g2kb_clr_done (); /* clear flag */
}
return dat;
}
/* Push Button: IOT routine */
int32 g2pb (int32 dev, int32 pulse, int32 dat)
{
if (pulse & 020) { /* wbl */
/* XXX if light for pb 7, press button 7!! */
printf("G2: wbl %#o\r\n", dat);
g2pb_lbuf = dat;
}
if (pulse & 001) { /* spb */
if (g2pb_done) {
dat = dat | IOT_SKP;
}
}
if (pulse & 002) { /* lpb */
g2pb_clr_done (); /* clear flag */
dat = dat | g2pb_bbuf; /* return buttons */
}
if (pulse & 004) { /* cpb */
g2pb_clr_done (); /* clear flag */
}
}
/* Unit service */
t_stat g2kb_svc (UNIT *uptr)
{
int32 ln, c, temp;
if ((uptr->flags & UNIT_ATT) == 0) /* attached? */
return SCPE_OK;
sim_clock_coschedule (uptr, tmxr_poll); /* continue poll */
ln = tmxr_poll_conn (&g2_desc); /* look for connect */
if (ln >= 0) /* got one? rcv enab */
g2_ldsc.rcve = 1;
tmxr_poll_rx (&g2_desc); /* poll for input */
if (g2_ldsc.conn) { /* connected? */
if ((temp = tmxr_getc_ln (&g2_ldsc))) { /* get char */
if (temp & SCPE_BREAK) /* break? */
c = 0;
else c = sim_tt_inpcvt (temp, TT_GET_MODE(g2d1_unit.flags) );
g2kb_buf = c;
g2kb_set_done ();
}
}
return SCPE_OK;
}
/****************************************************************/
/* Interrupt handling routines */
t_bool g2kb_test_done ()
{
if (g2kb_done)
return TRUE;
return FALSE;
}
void g2kb_set_done ()
{
g2kb_done = 1;
SET_INT (G2);
return;
}
void g2kb_clr_done ()
{
g2kb_done = 0;
CLR_INT (G2);
return;
}
/****************/
t_bool g2pb_test_done ()
{
if (g2pb_done)
return TRUE;
return FALSE;
}
void g2pb_set_done ()
{
g2pb_done = 1;
SET_INT (G2);
return;
}
void g2pb_clr_done ()
{
g2pb_done = 0;
CLR_INT (G2);
return;
}
/****************************************************************/
/* Display Output: IOT routine */
/*
* UNIX text display command lists always end with a TRAP
* and display output is restarted periodicly in timer PI service code
*/
static void g2_putchar(char c)
{
if (g2_ldsc.conn && g2_ldsc.xmte) { /* connected, tx enabled? */
tmxr_putc_ln (&g2_ldsc, c);
if (c == '\n')
tmxr_putc_ln (&g2_ldsc, '\r');
g2_dpycount++; /* only consume if connected+enabled! */
}
}
int32 g2d1 (int32 dev, int32 pulse, int32 dat)
{
if (g2_ldsc.conn && g2_ldsc.xmte && pulse == 047) { /* beg */
int32 n = g2_dpycount, i;
g2_dpyaddr = dat & 017777;
for (i = g2_dpyaddr; i < 020000; i++) {
uint32 w = M[i] & 0777777;
if (w & 0400000) /* TRAP? */
break;
/* check first three words for expected setup commands */
int o = i - g2_dpyaddr;
if (o < sizeof(g2_expect)/sizeof(g2_expect[0])) {
if (w != g2_expect[o]) {
printf("g2: unexpected command at %#o: %#o expected %#o\r\n",
i, w, g2_expect[o]);
break;
}
continue;
}
if (w & 0300000) { /* not characters? */
printf("g2: unexpected command at %#o: %#o\r\n", i, w);
break;
}
if (--n < 0) /* new? */
g2_putchar( (w>>7) & 0177 );
if ((w & 0177) && --n < 0) /* char2 & new? */
g2_putchar( w & 0177 );
} /* for loop */
fflush(stdout); /* TEMP */
if (n > 0)
g2_dpycount = 0; /* didn't see as much as last time? */
} /* beg IOT */
return dat;
}
/* Unit service */
t_stat g2d1_svc (UNIT *uptr)
{
if (g2_ldsc.conn) { /* connected? */
tmxr_poll_tx (&g2_desc); /* poll xmt */
if (!g2_ldsc.xmte) { /* tx not enabled? */
sim_activate (uptr, g2d1_unit.wait); /* wait */
}
}
return SCPE_OK;
}
/* Reset routine */
t_stat g2_reset (DEVICE *dptr)
{
if (dptr->flags & DEV_DIS) { /* sync enables */
g2kb_dev.flags = g2kb_dev.flags | DEV_DIS;
g2d1_dev.flags = g2d1_dev.flags | DEV_DIS;
}
else {
g2kb_dev.flags = g2kb_dev.flags & ~DEV_DIS;
g2d1_dev.flags = g2d1_dev.flags & ~DEV_DIS;
}
if (g2kb_unit.flags & UNIT_ATT) /* if attached, */
sim_activate (&g2kb_unit, tmxr_poll); /* activate */
else sim_cancel (&g2kb_unit); /* else stop */
g2kb_buf = 0; /* clear buf */
g2pb_bbuf = 0; /* clear buttons */
g2pb_lbuf = 0; /* clear lites */
g2_dpyaddr = 0;
g2_dpycount = 0;
g2kb_clr_done (); /* clear done */
g2pb_clr_done ();
sim_cancel (&g2d1_unit); /* stop poll */
return SCPE_OK;
}
/* Attach master unit */
t_stat g2_attach (UNIT *uptr, char *cptr)
{
t_stat r;
r = tmxr_attach (&g2_desc, uptr, cptr); /* attach */
if (r != SCPE_OK) /* error */
return r;
sim_activate (uptr, 0); /* start poll at once */
return SCPE_OK;
}
/* Detach master unit */
t_stat g2_detach (UNIT *uptr)
{
t_stat r = tmxr_detach (&g2_desc, uptr); /* detach */
sim_cancel (uptr); /* stop poll */
g2_ldsc.rcve = 0;
return r;
}
#endif