/* pdp18b_g2tty.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 Bell Labs 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 responds as ten 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), | |
as two SIMH DEVICES, G2OUT and G2IN: | |
G2OUT: | |
G2D1 005 GRAPHICS-2 display output | |
G2IN: | |
G2KB 043 GRAPHICS-2 keyboard | |
G2BB 044 GRAPHICS-2 button box (lighted bush buttons) | |
17-Mar-16 PLB Cloned from 13-Sep-15 version of pdp18b_tt1.c | |
*/ | |
#include "pdp18b_defs.h" | |
#ifdef GRAPHICS2 | |
#include "sim_tmxr.h" | |
#include <ctype.h> | |
uint8 g2kb_done = 0; /* keyboard flag */ | |
uint32 g2kb_buf = 0; /* keyboard buffer */ | |
uint8 g2bb_flag = 0; /* button flag */ | |
uint32 g2bb_bbuf = 0; /* button buffer */ | |
uint32 g2bb_lbuf = 0; /* button lights buffer */ | |
uint32 g2out_addr = 0; /* display address */ | |
int32 g2out_count = 0; /* character count (not a hw reg) */ | |
/* 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; | |
/* SIMH G2IN DEVICE */ | |
t_bool g2kb_test_done (); | |
void g2kb_set_done (); | |
void g2kb_clr_done (); | |
int32 g2kb_iot (int32 dev, int32 pulse, int32 dat); /* device 043 */ | |
t_bool g2bb_test_flag (); | |
void g2bb_set_flag (); | |
void g2bb_clr_flag (); | |
int32 g2bb_iot (int32 dev, int32 pulse, int32 dat); /* device 044 */ | |
t_stat g2in_svc (UNIT *uptr); | |
/* SIMH G2OUT DEVICE */ | |
int32 g2d1_iot (int32 dev, int32 pulse, int32 dat); /* device 05 */ | |
/* both G2IN/G2OUT: */ | |
t_stat g2_attach (UNIT *uptr, char *cptr); | |
t_stat g2_detach (UNIT *uptr); | |
t_stat g2_reset (DEVICE *dptr); | |
/**************************************************************** | |
* SIMH G2IN (keyboard/buttons) DEVICE data structures | |
* | |
* g2in_dev G2IN device descriptor | |
* g2in_unit G2IN unit descriptor | |
* g2in_reg G2IN register list | |
* g2in_mod G2IN modifiers list | |
*/ | |
DIB g2in_dib = { DEV_G2KB, 2, NULL, { &g2kb_iot, &g2bb_iot } }; | |
UNIT g2in_unit = { | |
UDATA (&g2in_svc, UNIT_IDLE|UNIT_ATTABLE, 0), KBD_POLL_WAIT | |
}; | |
REG g2in_reg[] = { | |
{ ORDATA (KBBUF, g2kb_buf, 1) }, | |
{ ORDATA (KBDONE, g2kb_done, 1) }, | |
{ FLDATA (INT, int_hwre[API_G2], INT_V_G2) }, | |
{ DRDATA (TIME, g2in_unit.wait, 24), REG_NZ + PV_LEFT }, | |
{ ORDATA (BBBBUF, g2bb_bbuf, 1) }, /* button box button buffer */ | |
{ ORDATA (BBFLAG, g2bb_flag, 1) }, /* button box IRQ */ | |
{ ORDATA (BBLBUF, g2bb_lbuf, 1) }, /* button box lights buffer */ | |
{ NULL } | |
}; | |
MTAB g2in_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_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 } | |
}; | |
/* SIMH G2IN device descriptor (GRAPHICS-2 keyboard & button box) */ | |
DEVICE g2in_dev = { | |
"G2IN", /* name */ | |
&g2in_unit, /* units */ | |
g2in_reg, g2in_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 */ | |
&g2in_dib, DEV_MUX | DEV_DISABLE /* ctxt, flags */ | |
}; | |
/**************************************************************** | |
* SIMH G2OUT (display output) DEVICE data structures | |
* Only needed to hold "iot" routine, since DIB's can't represent | |
* devices with register sets as sparse as GRAPHICS-2 | |
* | |
* g2out_dev G2OUT device descriptor | |
* g2out_unit G2OUT unit descriptor | |
* g2out_reg G2OUT register list | |
* g2out_mod G2OUT modifiers list | |
*/ | |
DIB g2out_dib = { DEV_G2D1, 1, NULL, { &g2d1_iot } }; | |
UNIT g2out_unit = { UDATA (NULL, 0, 0) }; | |
REG g2out_reg[] = { | |
{ ORDATA (DPYADDR, g2out_addr, 1) }, | |
{ NULL } | |
}; | |
MTAB g2out_mod[] = { | |
{ MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT", | |
&tmxr_dscln, NULL, &g2_desc }, | |
{ MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO", | |
NULL, &show_devno, NULL }, | |
{ 0 } | |
}; | |
/* SIMH G2OUT device descriptor (simulates just one of many display IOTs!) */ | |
DEVICE g2out_dev = { | |
"G2OUT", /* name */ | |
&g2out_unit, /* units */ | |
g2out_reg, g2out_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 */ | |
&g2out_dib, DEV_DISABLE /* ctxt, flags */ | |
}; | |
/**************************************************************** | |
* IOT routines | |
*/ | |
/* Keyboard input IOT routine */ | |
/* real device could have done bitwise decode?! */ | |
int32 g2kb_iot (int32 dev, int32 pulse, int32 dat) | |
{ | |
if (pulse == 001) { /* sck */ | |
if (g2kb_done) { | |
dat = dat | IOT_SKP; | |
} | |
} | |
else if (pulse == 002) { /* lck */ | |
dat = dat | g2kb_buf; /* return buffer */ | |
} | |
else if (pulse == 004) { /* cck */ | |
g2kb_clr_done (); /* clear flag */ | |
} | |
return dat; | |
} | |
/* Button Box IOT routine */ | |
/* real device could have done bitwise decode? */ | |
int32 g2bb_iot (int32 dev, int32 pulse, int32 dat) | |
{ | |
if (pulse == 001) { /* "spb" -- skip on push button flag */ | |
if (g2bb_flag) | |
dat = dat | IOT_SKP; | |
} | |
else if (pulse == 002) /* "lpb" -- load push buttons */ | |
dat = dat | g2bb_bbuf; /* return buttons */ | |
else if (pulse == 004) /* "cpb" -- clear push button flag */ | |
g2bb_clr_flag (); /* clear flag */ | |
else if (pulse == 020) { /* "wbl" -- write buttons lights */ | |
printf("G2: wbl %#o\r\n", dat); /* TEMP */ | |
g2bb_lbuf = dat; | |
} | |
return dat; | |
} | |
/* Input side Unit service */ | |
t_stat g2in_svc (UNIT *uptr) | |
{ | |
int32 ln, c, temp; | |
if ((uptr->flags & UNIT_ATT) == 0) /* attached? */ | |
return SCPE_OK; | |
/* XXX if light for button 7 lit (screen full), press button 7!! */ | |
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? */ | |
tmxr_poll_tx (&g2_desc); /* PLB: poll xmt */ | |
if ((temp = tmxr_getc_ln (&g2_ldsc))) { /* get char */ | |
if (temp & SCPE_BREAK) /* break? */ | |
c = 0; | |
else if (c == '\r') /* translate CR but not ESC */ | |
c = '\n'; | |
g2kb_buf = c; | |
g2kb_set_done (); | |
} | |
} /* connected */ | |
else | |
g2out_count = 0; /* not connected; next connections sees entire "screen" */ | |
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 g2bb_test_flag () | |
{ | |
if (g2bb_flag) | |
return TRUE; | |
return FALSE; | |
} | |
void g2bb_set_flag () | |
{ | |
g2bb_flag = 1; | |
SET_INT (G2); | |
return; | |
} | |
void g2bb_clr_flag () | |
{ | |
g2bb_flag = 0; | |
CLR_INT (G2); | |
return; | |
} | |
/**************************************************************** | |
* SIMH G2OUT (Display Output) DEVICE routines | |
*/ | |
/* helper to put 7-bit display character: | |
* characters are only consumed if TELNET user connected & output ready/enabled | |
*/ | |
static void g2out_putchar(char c) | |
{ | |
if (g2_ldsc.conn && g2_ldsc.xmte) { /* connected, tx enabled? */ | |
tmxr_putc_ln (&g2_ldsc, c); | |
g2out_count++; /* consumed */ | |
} | |
} | |
/* Device 05 IOT routine */ | |
int32 g2d1_iot (int32 dev, int32 pulse, int32 dat) | |
{ | |
/* | |
* UNIX text display command lists always end with a TRAP | |
* and display output is restarted periodicly in timer PI service code | |
*/ | |
if (g2_ldsc.conn && g2_ldsc.xmte && pulse == 047) { /* conn&ready, "beg" */ | |
int32 n = g2out_count, i; | |
g2out_addr = dat & 017777; | |
for (i = g2out_addr; i < 020000; i++) { | |
uint32 w = M[i] & 0777777; | |
int offset = i - g2out_addr; | |
if (w & 0400000) /* TRAP (stops display engine)? */ | |
break; | |
/* check first three words for expected setup commands */ | |
if (offset < sizeof(g2_expect)/sizeof(g2_expect[0])) { | |
if (w != g2_expect[offset]) { | |
/* TEMP: */ | |
printf("G2: unexpected command at %#o: %#o expected %#o\r\n", | |
i, w, g2_expect[offset]); | |
break; | |
} | |
continue; | |
} | |
if (w & 0300000) { /* not characters? */ | |
printf("G2: unexpected command at %#o: %#o\r\n", i, w); /* TEMP */ | |
break; | |
} | |
if (--n < 0) /* new? */ | |
g2out_putchar( (w>>7) & 0177 ); | |
if ((w & 0177) && --n < 0) /* char2 & new? */ | |
g2out_putchar( w & 0177 ); | |
} /* for loop */ | |
if (n > 0) | |
g2out_count = 0; /* didn't see as much as last time? */ | |
} /* beg IOT */ | |
return dat; | |
} | |
/**************************************************************** | |
* subsystem common routines (used by both G2IN and G2OUT SIMH DEVICEs) | |
*/ | |
/* Reset routine */ | |
t_stat g2_reset (DEVICE *dptr) | |
{ | |
if (dptr->flags & DEV_DIS) { /* sync enables */ | |
g2in_dev.flags = g2in_dev.flags | DEV_DIS; | |
g2out_dev.flags = g2out_dev.flags | DEV_DIS; | |
} | |
else { | |
g2in_dev.flags = g2in_dev.flags & ~DEV_DIS; | |
g2out_dev.flags = g2out_dev.flags & ~DEV_DIS; | |
} | |
if (g2in_unit.flags & UNIT_ATT) /* if attached, */ | |
sim_activate (&g2in_unit, tmxr_poll); /* activate */ | |
else sim_cancel (&g2in_unit); /* else stop */ | |
g2kb_buf = 0; /* clear buf */ | |
g2kb_clr_done (); /* clear done */ | |
g2bb_bbuf = 0; /* clear buttons */ | |
g2bb_lbuf = 0; /* clear lights */ | |
g2bb_clr_flag (); | |
g2out_addr = 0; | |
g2out_count = 0; | |
sim_cancel (&g2out_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 |