These changes facilitate more robust parameter type checking and helps to identify unexpected coding errors. Most simulators can now also be compiled with a C++ compiler without warnings. Additionally, these changes have also been configured to facilitate easier backporting of simulator and device simulation modules to run under the simh v3.9+ SCP framework.
603 lines
20 KiB
C
603 lines
20 KiB
C
/* pdp18b_g2tty.c: PDP-7/9 Bell Labs "GRAPHIC-2" subsystem as a TTY via TELNET
|
|
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.
|
|
|
|
GRAPHICS-2 was a command list based graphics display system,
|
|
and included a light pen, a "button box" and status bits
|
|
for a "dataphone" interface to speak to a GECOS system.
|
|
|
|
The UNIX-7 system driver only uses text display, and reserves 269
|
|
words (holding two characters each; the buffer is 273 words, but
|
|
three contain display "setup" commands, and the final word in the
|
|
buffer must be a display "TRAP" instruction that ends the display
|
|
list).
|
|
|
|
The UNIX system code triggers a refresh every 10 60Hz "ticks" of
|
|
the real time clock. This driver attempts to do detect new text
|
|
and send it to a user who has TELNETed in.
|
|
|
|
Thoughts on implementing a web interface:
|
|
|
|
538 characters redisplaying at 6Hz (every 10 "ticks") gives a
|
|
bandwith requirement of only 26Kbit, and most refreshes won't
|
|
change the screen and could be suppressed. So it seems like it
|
|
would be reasonable to create a web interface.
|
|
|
|
Make a SIMH TCP server port which implements a tiny HTTP server.
|
|
The base URL serves up a skeletal page with (lighted) buttons,
|
|
and a "display window".
|
|
|
|
And either:
|
|
|
|
1) Use "AJAX": an (invisible) <iframe> on in the HTML served by the
|
|
"base URL" keeps a never-ending connection that is sent "script"
|
|
tags to alter the "screen" (div) contents, and light push buttons.
|
|
Keypresses and buttons could be implemented using "onclick"
|
|
actions which trigger a GET (or a POST) on a URLs which
|
|
open new (temporary) HTTP connections to SIMH. The key/button
|
|
press URL could contain a session UUID which has to match
|
|
a value sent in the initial page.
|
|
|
|
2) Have the "home" page HTML establish a bi-directional WebSocket
|
|
connection to Encapsulate all the traffic (screen contents,
|
|
button lighting, key & button presses).
|
|
|
|
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)
|
|
|
|
20-Mar-16 PLB Works, mostly
|
|
19-Mar-16 PLB Working (up to a screen full)
|
|
17-Mar-16 PLB Cloned from 13-Sep-15 version of pdp18b_tt1.c
|
|
*/
|
|
|
|
/*
|
|
|
|
* GRAPHICS-2 was vector graphics hardware, UNIX-7 uses it as a
|
|
* "Glass TTY" for a "second seat".
|
|
*
|
|
* This simulation ONLY handles text display; the one program
|
|
* found that uses the "capt" (capture?) call to set a user
|
|
* supplied display list will not work here.
|
|
*
|
|
* When the display buffer or screen is filled, the UNIX "display"
|
|
* driver lights "push button 7" (PB7), and waits for the user to
|
|
* press the button. UNIX then clears the screen, and output
|
|
* continues. If the program outputs a "Form Feed" character the
|
|
* display is also cleared.
|
|
*
|
|
* This simulation automatically presses PB7 when lit, without
|
|
* bothering the user (a more accurate simulation might prompt
|
|
* the user to press any key)!
|
|
*/
|
|
|
|
#include "pdp18b_defs.h"
|
|
#ifdef GRAPHICS2
|
|
#include "sim_tmxr.h"
|
|
|
|
int debug = 0;
|
|
|
|
/* hardware registers */
|
|
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 */
|
|
#define PB7 02000
|
|
|
|
/* not hardware registers: */
|
|
uint32 g2out_count = 0;
|
|
uint8 g2out_stuffcr = 0; /* need to stuff a CR */
|
|
|
|
|
|
/* keep old and new version of characters to display
|
|
* a count & checksum of the "old" screen contents might suffice,
|
|
* time will tell....
|
|
*/
|
|
uint8 g2out_which = 0;
|
|
#define OLD g2out_which
|
|
#define NEW (g2out_which ^ 1)
|
|
|
|
#define MAXBUFCHARS 700 /* larger than kernel display list */
|
|
static struct dspbuf {
|
|
uint16 count;
|
|
char buffer[MAXBUFCHARS]; /* 7-bit ASCII */
|
|
} g2out_dspbufs[2];
|
|
|
|
/* 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 */
|
|
static void g2out_clear ();
|
|
static void g2out_process_display_list ();
|
|
static int g2out_send_new ();
|
|
|
|
/* both G2IN/G2OUT: */
|
|
t_stat g2_attach (UNIT *uptr, CONST 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 */
|
|
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"/"opb" -- or 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 == 024) { /* "wbl" -- write buttons lights */
|
|
if (dat == 0)
|
|
g2out_clear(); /* UNIX has ack'ed button press */
|
|
g2bb_lbuf = dat;
|
|
}
|
|
return dat;
|
|
}
|
|
|
|
/* Input side Unit service */
|
|
t_stat g2in_svc (UNIT *uptr)
|
|
{
|
|
int32 ln, c;
|
|
|
|
if ((uptr->flags & UNIT_ATT) == 0) /* attached? */
|
|
return SCPE_OK;
|
|
|
|
if (g2bb_lbuf & PB7) { /* button 7 lit? */
|
|
/* yes: try sending anything new */
|
|
g2out_process_display_list();
|
|
g2out_send_new();
|
|
|
|
g2bb_bbuf |= PB7; /* press it to clear screen! */
|
|
g2bb_set_flag ();
|
|
}
|
|
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 ((c = tmxr_getc_ln (&g2_ldsc))) { /* get char */
|
|
if (c & SCPE_BREAK) /* break? */
|
|
c = 0;
|
|
else {
|
|
c &= 0177;
|
|
if (c == '\r') /* translate CR but not ESC! */
|
|
c = '\n';
|
|
else if ((c & 0155) == 055) /* kernel swaps around -?/= */
|
|
c ^= 020; /* pre-swap!! */
|
|
}
|
|
g2kb_buf = c;
|
|
g2kb_set_done ();
|
|
}
|
|
} /* connected */
|
|
else {
|
|
/* not connected; next connection sees entire "screen" */
|
|
g2out_stuffcr = 0;
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Interrupt handling routines */
|
|
|
|
t_bool g2kb_test_done ()
|
|
{
|
|
return g2kb_done != 0;
|
|
}
|
|
|
|
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 ()
|
|
{
|
|
return g2bb_flag != 0;
|
|
}
|
|
|
|
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 */
|
|
static void g2pc(char c) {
|
|
//if (debug) putchar(c);
|
|
tmxr_putc_ln (&g2_ldsc, c);
|
|
}
|
|
|
|
/* send a character from the display. adds CR after LF */
|
|
/* returns 1 if "c" was sent; 0 means try again later */
|
|
static int g2out_putchar(char c)
|
|
{
|
|
if (!g2_ldsc.conn || !g2_ldsc.xmte) /* connected, tx enabled? */
|
|
return 0;
|
|
|
|
if (g2out_stuffcr) { /* need to stuff a CR? */
|
|
g2pc ('\r');
|
|
g2out_stuffcr = 0;
|
|
if (!g2_ldsc.xmte) /* full? */
|
|
return 0; /* yes: wait until next time */
|
|
}
|
|
|
|
g2pc (c);
|
|
|
|
if (c == '\n') { /* was it a NL? */
|
|
if (g2_ldsc.xmte) /* transmitter enabled? */
|
|
g2pc ('\r'); /* send CR now */
|
|
else
|
|
g2out_stuffcr = 1; /* wait until next time */
|
|
}
|
|
return 1;
|
|
}
|
|
|
|
/* 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" */
|
|
g2out_addr = dat & 017777;
|
|
g2out_process_display_list();
|
|
g2out_send_new();
|
|
g2out_which ^= 1; /* swap buffers */
|
|
} /* beg IOT */
|
|
return dat;
|
|
}
|
|
|
|
/****************
|
|
* display buffer management/process
|
|
* we're informed when UNIX wants to clear the screen (PB7 lit)
|
|
* we then press the button
|
|
* UNIX does a "cpb" to ACK/clear the interrupt.
|
|
*
|
|
* *BUT* UNIX clears the screen when a FF (014) char is output,
|
|
* which just resets the buffer (and not issuing any IOTs)
|
|
*/
|
|
|
|
static void g2out_clear() {
|
|
g2out_stuffcr = 0;
|
|
g2out_which = 0;
|
|
g2out_count = 0;
|
|
g2out_dspbufs[0].count = g2out_dspbufs[1].count = 0;
|
|
}
|
|
|
|
/* interpret display list; save characters into "new" dspbuf
|
|
* quits early if display list doesn't conform to what's expected
|
|
*/
|
|
static void g2out_process_display_list() {
|
|
uint32 i;
|
|
struct dspbuf *dp = g2out_dspbufs + NEW;
|
|
|
|
dp->count = 0;
|
|
for (i = g2out_addr; i < 020000; i++) {
|
|
uint32 w = M[i] & 0777777;
|
|
int offset = i - g2out_addr;
|
|
char c;
|
|
|
|
if (w & 0400000) /* TRAP (end of display list) */
|
|
return;
|
|
|
|
/* check first three words for expected setup commands */
|
|
if (offset < sizeof(g2_expect)/sizeof(g2_expect[0])) {
|
|
if (w != g2_expect[offset])
|
|
return;
|
|
continue;
|
|
}
|
|
if (w & 0300000) /* not characters? */
|
|
return;
|
|
|
|
c = (w >> 7) & 0177;
|
|
if (c)
|
|
dp->buffer[dp->count++] = c;
|
|
c = w & 0177;
|
|
if (c)
|
|
dp->buffer[dp->count++] = c;
|
|
}
|
|
}
|
|
|
|
/* figure out what to send on TELNET connection
|
|
* truncates new->count to the number sent so far
|
|
* returns number of new characters sent
|
|
*/
|
|
static int g2out_send_new() {
|
|
struct dspbuf *old = g2out_dspbufs + OLD;
|
|
struct dspbuf *New = g2out_dspbufs + NEW;
|
|
char *cp = New->buffer;
|
|
int cur = 0;
|
|
int start;
|
|
|
|
/* nothing in newest refresh?
|
|
* COULD have had undisplayed stuff on last screen before it was cleared??
|
|
* would need to have a transmit queue??
|
|
*/
|
|
if (New->count == 0)
|
|
return 0;
|
|
|
|
if (old->count && /* have old chars */
|
|
memcmp(old->buffer, New->buffer, old->count) == 0) { /* and a prefix */
|
|
cur = old->count;
|
|
cp += cur;
|
|
}
|
|
/* loop for chars while connected & tx enabled */
|
|
start = cur;
|
|
while (cur < New->count && g2_ldsc.conn && g2_ldsc.xmte) {
|
|
if (g2out_putchar(*cp)) {
|
|
cp++;
|
|
cur++;
|
|
}
|
|
}
|
|
New->count = cur; /* only remember what's been sent */
|
|
return cur - start; /* remember number sent */
|
|
}
|
|
|
|
/****************************************************************
|
|
* 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_clear();
|
|
sim_cancel (&g2out_unit); /* stop poll */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Attach master unit */
|
|
t_stat g2_attach (UNIT *uptr, CONST 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
|