1
/* pdp15_ttx.c: PDP-15 additional terminals simulator
3
Copyright (c) 1993-2004, Robert M Supnik
5
Permission is hereby granted, free of charge, to any person obtaining a
6
copy of this software and associated documentation files (the "Software"),
7
to deal in the Software without restriction, including without limitation
8
the rights to use, copy, modify, merge, publish, distribute, sublicense,
9
and/or sell copies of the Software, and to permit persons to whom the
10
Software is furnished to do so, subject to the following conditions:
12
The above copyright notice and this permission notice shall be included in
13
all copies or substantial portions of the Software.
15
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
18
ROBERT M SUPNIK BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
19
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
Except as contained in this notice, the name of Robert M Supnik shall not
23
be used in advertising or otherwise to promote the sale, use or other dealings
24
in this Software without prior written authorization from Robert M Supnik.
26
ttix,ttox LT15/LT19 terminal input/output
28
14-Jan-04 RMS Cloned from pdp8_ttx.c
30
This module implements 16 individual serial interfaces similar in function
31
to the console. These interfaces are mapped to Telnet based connections as
32
though they were the four lines of a terminal multiplexor. The connection
33
polling mechanism is superimposed onto the keyboard of the first interface.
36
#include "pdp18b_defs.h"
42
#define TTX_MAXL 16 /* max number of lines */
49
#define UNIT_V_8B (UNIT_V_UF + 0) /* 8B */
50
#define UNIT_V_KSR (UNIT_V_UF + 1) /* KSR33 */
51
#define UNIT_8B (1 << UNIT_V_8B)
52
#define UNIT_KSR (1 << UNIT_V_KSR)
54
uint32 ttix_done = 0; /* input flags */
55
uint32 ttox_done = 0; /* output flags */
56
uint8 ttix_buf[TTX_MAXL] = { 0 }; /* input buffers */
57
uint8 ttox_buf[TTX_MAXL] = { 0 }; /* output buffers */
58
TMLN ttx_ldsc[TTX_MAXL] = { 0 }; /* line descriptors */
59
TMXR ttx_desc = { 1, 0, 0, ttx_ldsc }; /* mux descriptor */
60
#define ttx_lines ttx_desc.lines /* current number of lines */
62
extern int32 int_hwre[API_HLVL+1];
63
extern int32 tmxr_poll;
64
extern int32 stop_inst;
66
DEVICE ttix_dev, ttox_dev;
67
int32 ttix (int32 dev, int32 pulse, int32 dat);
68
int32 ttox (int32 dev, int32 pulse, int32 dat);
69
t_stat ttix_svc (UNIT *uptr);
70
t_bool ttix_test_done (int32 ln);
71
void ttix_set_done (int32 ln);
72
void ttix_clr_done (int32 ln);
73
t_stat ttox_svc (UNIT *uptr);
74
t_bool ttox_test_done (int32 ln);
75
void ttox_set_done (int32 ln);
76
void ttox_clr_done (int32 ln);
77
int32 ttx_getln (int32 dev, int32 pulse);
78
t_stat ttx_attach (UNIT *uptr, char *cptr);
79
t_stat ttx_detach (UNIT *uptr);
80
t_stat ttx_reset (DEVICE *dptr);
81
void ttx_reset_ln (int32 i);
82
t_stat ttx_summ (FILE *st, UNIT *uptr, int32 val, void *desc);
83
t_stat ttx_show (FILE *st, UNIT *uptr, int32 val, void *desc);
84
t_stat ttx_vlines (UNIT *uptr, int32 val, char *cptr, void *desc);
86
/* TTIx data structures
88
ttix_dev TTIx device descriptor
89
ttix_unit TTIx unit descriptor
90
ttix_reg TTIx register list
91
ttix_mod TTIx modifiers list
94
DIB ttix_dib = { DEV_TTO1, 8, NULL,
95
{ &ttox, &ttix, &ttox, &ttix, &ttox, &ttix, &ttox, &ttix } };
97
UNIT ttix_unit = { UDATA (&ttix_svc, UNIT_ATTABLE, 0), KBD_POLL_WAIT };
99
REG ttx_nlreg = { DRDATA (NLINES, ttx_lines, 4), PV_LEFT };
102
{ BRDATA (BUF, ttix_buf, 8, 8, TTX_MAXL) },
103
{ ORDATA (DONE, ttix_done, TTX_MAXL) },
104
{ FLDATA (INT, int_hwre[API_TTI1], INT_V_TTI1) },
105
{ DRDATA (TIME, ttix_unit.wait, 24), REG_NZ + PV_LEFT },
106
{ ORDATA (DEVNUM, ttix_dib.dev, 6), REG_HRO },
110
{ MTAB_XTD | MTAB_VDV | MTAB_VAL, 0, "lines", "LINES",
111
&ttx_vlines, NULL, &ttx_nlreg },
112
{ UNIT_ATT, UNIT_ATT, "summary", NULL, NULL, &ttx_summ },
113
{ MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT",
114
&tmxr_dscln, NULL, &ttx_desc },
115
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL,
116
NULL, &ttx_show, NULL },
117
{ MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL,
118
NULL, &ttx_show, NULL },
119
{ MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
120
&set_devno, &show_devno, NULL },
121
{ MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG",
122
&tmxr_set_log, &tmxr_show_log, &ttx_desc },
123
{ MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG",
124
&tmxr_set_nolog, NULL, &ttx_desc },
128
"TTIX", &ttix_unit, ttix_reg, ttix_mod,
130
&tmxr_ex, &tmxr_dep, &ttx_reset,
131
NULL, &ttx_attach, &ttx_detach,
132
&ttix_dib, DEV_NET | DEV_DISABLE };
134
/* TTOx data structures
136
ttox_dev TTOx device descriptor
137
ttox_unit TTOx unit descriptor
138
ttox_reg TTOx register list
142
{ UDATA (&ttox_svc, UNIT_KSR, 0), SERIAL_OUT_WAIT },
143
{ UDATA (&ttox_svc, UNIT_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
144
{ UDATA (&ttox_svc, UNIT_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
145
{ UDATA (&ttox_svc, UNIT_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
146
{ UDATA (&ttox_svc, UNIT_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
147
{ UDATA (&ttox_svc, UNIT_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
148
{ UDATA (&ttox_svc, UNIT_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
149
{ UDATA (&ttox_svc, UNIT_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
150
{ UDATA (&ttox_svc, UNIT_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
151
{ UDATA (&ttox_svc, UNIT_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
152
{ UDATA (&ttox_svc, UNIT_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
153
{ UDATA (&ttox_svc, UNIT_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
154
{ UDATA (&ttox_svc, UNIT_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
155
{ UDATA (&ttox_svc, UNIT_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
156
{ UDATA (&ttox_svc, UNIT_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
157
{ UDATA (&ttox_svc, UNIT_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT } };
160
{ BRDATA (BUF, ttox_buf, 8, 8, TTX_MAXL) },
161
{ ORDATA (DONE, ttox_done, TTX_MAXL) },
162
{ FLDATA (INT, int_hwre[API_TTO1], INT_V_TTO1) },
163
{ URDATA (TIME, ttox_unit[0].wait, 10, 24, 0,
164
TTX_MAXL, PV_LEFT) },
168
{ UNIT_KSR+UNIT_8B, UNIT_KSR, "KSR", "KSR", NULL },
169
{ UNIT_KSR+UNIT_8B, 0 , "7b" , "7B" , NULL },
170
{ UNIT_KSR+UNIT_8B, UNIT_8B , "8b" , "8B" , NULL },
174
"TTOX", ttox_unit, ttox_reg, ttox_mod,
175
TTX_MAXL, 10, 31, 1, 8, 8,
176
NULL, NULL, &ttx_reset,
180
/* Terminal input: IOT routine */
182
int32 ttix (int32 dev, int32 pulse, int32 dat)
184
int32 ln = ttx_getln (dev, pulse); /* line # */
186
if (ln > ttx_lines) return dat;
187
if (pulse & 001) { /* KSF1 */
188
if (ttix_test_done (ln)) dat = dat | IOT_SKP; }
189
if (pulse & 002) { /* KRB1 */
190
ttix_clr_done (ln); /* clear flag */
191
dat = dat | ttix_buf[ln]; } /* return buffer */
197
t_stat ttix_svc (UNIT *uptr)
201
if ((uptr->flags & UNIT_ATT) == 0) return SCPE_OK; /* attached? */
202
sim_activate (uptr, tmxr_poll); /* continue poll */
203
ln = tmxr_poll_conn (&ttx_desc); /* look for connect */
204
if (ln >= 0) ttx_ldsc[ln].rcve = 1; /* got one? rcv enab */
205
tmxr_poll_rx (&ttx_desc); /* poll for input */
206
for (ln = 0; ln < TTX_MAXL; ln++) { /* loop thru lines */
207
if (ttx_ldsc[ln].conn) { /* connected? */
208
if (temp = tmxr_getc_ln (&ttx_ldsc[ln])) { /* get char */
209
if (temp & SCPE_BREAK) c = 0; /* break? */
210
else if (ttox_unit[ln].flags & UNIT_KSR) { /* KSR? */
212
if (islower (c)) c = toupper (c);
214
else c = temp & ((ttox_unit[ln].flags & UNIT_8B)? 0377: 0177);
216
ttix_set_done (ln); } } }
220
/* Interrupt handling routines */
222
t_bool ttix_test_done (int32 ln)
224
if (ttix_done & (1 << ln)) return TRUE;
228
void ttix_set_done (int32 ln)
230
ttix_done = ttix_done | (1 << ln);
235
void ttix_clr_done (int32 ln)
237
ttix_done = ttix_done & ~(1 << ln);
238
if (ttix_done) { SET_INT (TTI1); }
239
else { CLR_INT (TTI1); }
243
/* Terminal output: IOT routine */
245
int32 ttox (int32 dev, int32 pulse, int32 dat)
247
int32 ln = ttx_getln (dev, pulse); /* line # */
249
if (ln > ttx_lines) return dat;
250
if (pulse & 001) { /* TSF */
251
if (ttox_test_done (ln)) dat = dat | IOT_SKP; }
252
if (pulse & 002) ttox_clr_done (ln); /* clear flag */
253
if (pulse & 004) { /* load buffer */
254
sim_activate (&ttox_unit[ln], ttox_unit[ln].wait); /* activate unit */
255
ttox_buf[ln] = dat & 0377; } /* load buffer */
261
t_stat ttox_svc (UNIT *uptr)
263
int32 c, ln = uptr - ttox_unit; /* line # */
265
if (ttx_ldsc[ln].conn) { /* connected? */
266
if (ttx_ldsc[ln].xmte) { /* tx enabled? */
267
TMLN *lp = &ttx_ldsc[ln]; /* get line */
268
if (ttox_unit[ln].flags & UNIT_KSR) { /* KSR mode? */
269
c = ttox_buf[ln] & 0177; /* get char */
270
if (islower (c)) c = toupper (c);
271
if ((c < 007) || (c > 0137)) c = -1; }
272
else c = ttox_buf[ln] & ((ttox_unit[ln].flags & UNIT_8B)? 0377: 0177);
273
if (c >= 0) tmxr_putc_ln (lp, c); /* output char */
274
tmxr_poll_tx (&ttx_desc); } /* poll xmt */
276
tmxr_poll_tx (&ttx_desc); /* poll xmt */
277
sim_activate (uptr, ttox_unit[ln].wait); /* wait */
279
ttox_set_done (ln); /* set done */
283
/* Interrupt handling routines */
285
t_bool ttox_test_done (int32 ln)
287
if (ttox_done & (1 << ln)) return TRUE;
291
void ttox_set_done (int32 ln)
293
ttox_done = ttox_done | (1 << ln);
298
void ttox_clr_done (int32 ln)
300
ttox_done = ttox_done & ~(1 << ln);
301
if (ttox_done) { SET_INT (TTO1); }
302
else { CLR_INT (TTO1); }
306
/* Compute relative line number
308
This algorithm does not assign contiguous line numbers of ascending
309
LT19's. Rather, line numbers follow a simple progression based on
310
the relative IOT number and the subdevice select */
312
int32 ttx_getln (int32 dev, int32 pulse)
314
int32 rdno = ((dev - ttix_dib.dev) >> 1) & 3;
316
#if defined (PDP15) /* PDP-15? */
317
int32 sub = (pulse >> 4) & 3;
318
return (rdno * 4) + sub; /* use dev, subdev */
320
return rdno; /* use dev only */
326
t_stat ttx_reset (DEVICE *dptr)
330
if (dptr->flags & DEV_DIS) { /* sync enables */
331
ttix_dev.flags = ttox_dev.flags | DEV_DIS;
332
ttox_dev.flags = ttox_dev.flags | DEV_DIS; }
333
else { ttix_dev.flags = ttix_dev.flags & ~DEV_DIS;
334
ttox_dev.flags = ttox_dev.flags & ~DEV_DIS; }
335
if (ttix_unit.flags & UNIT_ATT) /* if attached, */
336
sim_activate (&ttix_unit, tmxr_poll); /* activate */
337
else sim_cancel (&ttix_unit); /* else stop */
338
for (ln = 0; ln < TTX_MAXL; ln++) ttx_reset_ln (ln); /* for all lines */
344
void ttx_reset_ln (int32 ln)
346
ttix_buf[ln] = 0; /* clear buf, */
348
ttix_clr_done (ln); /* clear done */
350
sim_cancel (&ttox_unit[ln]); /* stop poll */
354
/* Attach master unit */
356
t_stat ttx_attach (UNIT *uptr, char *cptr)
360
r = tmxr_attach (&ttx_desc, uptr, cptr); /* attach */
361
if (r != SCPE_OK) return r; /* error */
362
sim_activate (uptr, tmxr_poll); /* start poll */
366
/* Detach master unit */
368
t_stat ttx_detach (UNIT *uptr)
373
r = tmxr_detach (&ttx_desc, uptr); /* detach */
374
sim_cancel (uptr); /* stop poll */
375
for (i = 0; i < TTX_MAXL; i++) ttx_ldsc[i].rcve = 0; /* disable rcv */
379
/* Show summary processor */
381
t_stat ttx_summ (FILE *st, UNIT *uptr, int32 val, void *desc)
385
for (i = t = 0; i < TTX_MAXL; i++) t = t + (ttx_ldsc[i].conn != 0);
386
if (t == 1) fprintf (st, "1 connection");
387
else fprintf (st, "%d connections", t);
391
/* SHOW CONN/STAT processor */
393
t_stat ttx_show (FILE *st, UNIT *uptr, int32 val, void *desc)
397
for (i = 0; (i < ttx_lines) && (ttx_ldsc[i].conn == 0); i++) ;
399
for (i = 0; i < ttx_lines; i++) {
400
if (ttx_ldsc[i].conn)
401
if (val) tmxr_fconns (st, &ttx_ldsc[i], i);
402
else tmxr_fstats (st, &ttx_ldsc[i], i); } }
403
else fprintf (st, "all disconnected\n");
407
/* Change number of lines */
409
t_stat ttx_vlines (UNIT *uptr, int32 val, char *cptr, void *desc)
414
if (cptr == NULL) return SCPE_ARG;
415
newln = get_uint (cptr, 10, TTX_MAXL, &r);
416
if ((r != SCPE_OK) || (newln == ttx_lines)) return r;
417
if (newln == 0) return SCPE_ARG;
418
if (newln < ttx_lines) {
419
for (i = newln, t = 0; i < ttx_lines; i++) t = t | ttx_ldsc[i].conn;
420
if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE))
422
for (i = newln; i < ttx_lines; i++) {
423
if (ttx_ldsc[i].conn) {
424
tmxr_linemsg (&ttx_ldsc[i], "\r\nOperator disconnected line\r\n");
425
tmxr_reset_ln (&ttx_ldsc[i]); } /* reset line */
426
ttox_unit[i].flags = ttox_unit[i].flags | UNIT_DIS;
429
else { for (i = ttx_lines; i < newln; i++) {
430
ttox_unit[i].flags = ttox_unit[i].flags & ~UNIT_DIS;