First Commit of my working state
[simh.git] / PDP18B / pdp18b_tt1.c
1 /* pdp15_ttx.c: PDP-15 additional terminals simulator
2
3 Copyright (c) 1993-2007, Robert M Supnik
4
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:
11
12 The above copyright notice and this permission notice shall be included in
13 all copies or substantial portions of the Software.
14
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.
21
22 Except as contained in this notice, the name of Robert M Supnik shall not be
23 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.
25
26 ttix,ttox LT15/LT19 terminal input/output
27
28 18-Jun-07 RMS Added UNIT_IDLE flag
29 30-Sep-06 RMS Fixed handling of non-printable characters in KSR mode
30 22-Nov-05 RMS Revised for new terminal processing routines
31 29-Jun-05 RMS Added SET TTOXn DISCONNECT
32 21-Jun-05 RMS Fixed bug in SHOW CONN/STATS
33 14-Jan-04 RMS Cloned from pdp8_ttx.c
34
35 This module implements 16 individual serial interfaces similar in function
36 to the console. These interfaces are mapped to Telnet based connections as
37 though they were the four lines of a terminal multiplexor. The connection
38 polling mechanism is superimposed onto the keyboard of the first interface.
39 */
40
41 #include "pdp18b_defs.h"
42 #include "sim_sock.h"
43 #include "sim_tmxr.h"
44 #include <ctype.h>
45
46 #if defined (PDP15)
47 #define TTX_MAXL 16 /* max number of lines */
48 #elif defined (PDP9)
49 #define TTX_MAXL 4
50 #else
51 #define TTX_MAXL 1
52 #endif
53
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 */
61
62 extern int32 int_hwre[API_HLVL+1];
63 extern int32 tmxr_poll;
64 extern int32 stop_inst;
65
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);
85
86 /* TTIx data structures
87
88 ttix_dev TTIx device descriptor
89 ttix_unit TTIx unit descriptor
90 ttix_reg TTIx register list
91 ttix_mod TTIx modifiers list
92 */
93
94 DIB ttix_dib = {
95 DEV_TTO1, 8, NULL,
96 { &ttox, &ttix, &ttox, &ttix, &ttox, &ttix, &ttox, &ttix }
97 };
98
99 UNIT ttix_unit = { UDATA (&ttix_svc, UNIT_IDLE|UNIT_ATTABLE, 0), KBD_POLL_WAIT };
100
101 REG ttx_nlreg = { DRDATA (NLINES, ttx_lines, 4), PV_LEFT };
102
103 REG ttix_reg[] = {
104 { BRDATA (BUF, ttix_buf, 8, 8, TTX_MAXL) },
105 { ORDATA (DONE, ttix_done, TTX_MAXL) },
106 { FLDATA (INT, int_hwre[API_TTI1], INT_V_TTI1) },
107 { DRDATA (TIME, ttix_unit.wait, 24), REG_NZ + PV_LEFT },
108 { ORDATA (DEVNUM, ttix_dib.dev, 6), REG_HRO },
109 { NULL }
110 };
111
112 MTAB ttix_mod[] = {
113 { MTAB_XTD | MTAB_VDV | MTAB_VAL, 0, "lines", "LINES",
114 &ttx_vlines, NULL, &ttx_nlreg },
115 { UNIT_ATT, UNIT_ATT, "summary", NULL, NULL, &ttx_summ },
116 { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT",
117 &tmxr_dscln, NULL, &ttx_desc },
118 { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL,
119 NULL, &ttx_show, NULL },
120 { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL,
121 NULL, &ttx_show, NULL },
122 { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
123 &set_devno, &show_devno, NULL },
124 { 0 }
125 };
126
127 DEVICE tti1_dev = {
128 "TTIX", &ttix_unit, ttix_reg, ttix_mod,
129 1, 10, 31, 1, 8, 8,
130 &tmxr_ex, &tmxr_dep, &ttx_reset,
131 NULL, &ttx_attach, &ttx_detach,
132 &ttix_dib, DEV_NET | DEV_DISABLE
133 };
134
135 /* TTOx data structures
136
137 ttox_dev TTOx device descriptor
138 ttox_unit TTOx unit descriptor
139 ttox_reg TTOx register list
140 */
141
142 UNIT ttox_unit[] = {
143 { UDATA (&ttox_svc, TT_MODE_KSR, 0), SERIAL_OUT_WAIT },
144 { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
145 { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
146 { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
147 { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
148 { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
149 { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
150 { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
151 { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
152 { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
153 { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
154 { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
155 { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
156 { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
157 { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT },
158 { UDATA (&ttox_svc, TT_MODE_KSR+UNIT_DIS, 0), SERIAL_OUT_WAIT }
159 };
160
161 REG ttox_reg[] = {
162 { BRDATA (BUF, ttox_buf, 8, 8, TTX_MAXL) },
163 { ORDATA (DONE, ttox_done, TTX_MAXL) },
164 { FLDATA (INT, int_hwre[API_TTO1], INT_V_TTO1) },
165 { URDATA (TIME, ttox_unit[0].wait, 10, 24, 0,
166 TTX_MAXL, PV_LEFT) },
167 { NULL }
168 };
169
170 MTAB ttox_mod[] = {
171 { TT_MODE, TT_MODE_KSR, "KSR", "KSR", NULL },
172 { TT_MODE, TT_MODE_7B, "7b", "7B", NULL },
173 { TT_MODE, TT_MODE_8B, "8b", "8B", NULL },
174 { TT_MODE, TT_MODE_7P, "7p", "7P", NULL },
175 { MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT",
176 &tmxr_dscln, NULL, &ttx_desc },
177 { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG",
178 &tmxr_set_log, &tmxr_show_log, &ttx_desc },
179 { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG",
180 &tmxr_set_nolog, NULL, &ttx_desc },
181 { 0 }
182 };
183
184 DEVICE tto1_dev = {
185 "TTOX", ttox_unit, ttox_reg, ttox_mod,
186 TTX_MAXL, 10, 31, 1, 8, 8,
187 NULL, NULL, &ttx_reset,
188 NULL, NULL, NULL,
189 NULL, DEV_DISABLE
190 };
191
192 /* Terminal input: IOT routine */
193
194 int32 ttix (int32 dev, int32 pulse, int32 dat)
195 {
196 int32 ln = ttx_getln (dev, pulse); /* line # */
197
198 if (ln > ttx_lines) return dat;
199 if (pulse & 001) { /* KSF1 */
200 if (ttix_test_done (ln)) dat = dat | IOT_SKP;
201 }
202 if (pulse & 002) { /* KRB1 */
203 ttix_clr_done (ln); /* clear flag */
204 dat = dat | ttix_buf[ln]; /* return buffer */
205 }
206 return dat;
207 }
208
209 /* Unit service */
210
211 t_stat ttix_svc (UNIT *uptr)
212 {
213 int32 ln, c, temp;
214
215 if ((uptr->flags & UNIT_ATT) == 0) return SCPE_OK; /* attached? */
216 sim_activate (uptr, tmxr_poll); /* continue poll */
217 ln = tmxr_poll_conn (&ttx_desc); /* look for connect */
218 if (ln >= 0) ttx_ldsc[ln].rcve = 1; /* got one? rcv enab */
219 tmxr_poll_rx (&ttx_desc); /* poll for input */
220 for (ln = 0; ln < TTX_MAXL; ln++) { /* loop thru lines */
221 if (ttx_ldsc[ln].conn) { /* connected? */
222 if (temp = tmxr_getc_ln (&ttx_ldsc[ln])) { /* get char */
223 if (temp & SCPE_BREAK) c = 0; /* break? */
224 else c = sim_tt_inpcvt (temp, TT_GET_MODE (ttox_unit[ln].flags) | TTUF_KSR);
225 ttix_buf[ln] = c;
226 ttix_set_done (ln);
227 }
228 }
229 }
230 return SCPE_OK;
231 }
232
233 /* Interrupt handling routines */
234
235 t_bool ttix_test_done (int32 ln)
236 {
237 if (ttix_done & (1 << ln)) return TRUE;
238 return FALSE;
239 }
240
241 void ttix_set_done (int32 ln)
242 {
243 ttix_done = ttix_done | (1 << ln);
244 SET_INT (TTI1);
245 return;
246 }
247
248 void ttix_clr_done (int32 ln)
249 {
250 ttix_done = ttix_done & ~(1 << ln);
251 if (ttix_done) { SET_INT (TTI1); }
252 else {
253 CLR_INT (TTI1);
254 }
255 return;
256 }
257
258 /* Terminal output: IOT routine */
259
260 int32 ttox (int32 dev, int32 pulse, int32 dat)
261 {
262 int32 ln = ttx_getln (dev, pulse); /* line # */
263
264 if (ln > ttx_lines) return dat;
265 if (pulse & 001) { /* TSF */
266 if (ttox_test_done (ln)) dat = dat | IOT_SKP;
267 }
268 if (pulse & 002) ttox_clr_done (ln); /* clear flag */
269 if (pulse & 004) { /* load buffer */
270 sim_activate (&ttox_unit[ln], ttox_unit[ln].wait); /* activate unit */
271 ttox_buf[ln] = dat & 0377; /* load buffer */
272 }
273 return dat;
274 }
275
276 /* Unit service */
277
278 t_stat ttox_svc (UNIT *uptr)
279 {
280 int32 c, ln = uptr - ttox_unit; /* line # */
281
282 if (ttx_ldsc[ln].conn) { /* connected? */
283 if (ttx_ldsc[ln].xmte) { /* tx enabled? */
284 TMLN *lp = &ttx_ldsc[ln]; /* get line */
285 c = sim_tt_outcvt (ttox_buf[ln], TT_GET_MODE (ttox_unit[ln].flags) | TTUF_KSR);
286 if (c >= 0) tmxr_putc_ln (lp, c); /* output char */
287 tmxr_poll_tx (&ttx_desc); /* poll xmt */
288 }
289 else {
290 tmxr_poll_tx (&ttx_desc); /* poll xmt */
291 sim_activate (uptr, ttox_unit[ln].wait); /* wait */
292 return SCPE_OK;
293 }
294 }
295 ttox_set_done (ln); /* set done */
296 return SCPE_OK;
297 }
298
299 /* Interrupt handling routines */
300
301 t_bool ttox_test_done (int32 ln)
302 {
303 if (ttox_done & (1 << ln)) return TRUE;
304 return FALSE;
305 }
306
307 void ttox_set_done (int32 ln)
308 {
309 ttox_done = ttox_done | (1 << ln);
310 SET_INT (TTO1);
311 return;
312 }
313
314 void ttox_clr_done (int32 ln)
315 {
316 ttox_done = ttox_done & ~(1 << ln);
317 if (ttox_done) { SET_INT (TTO1); }
318 else { CLR_INT (TTO1); }
319 return;
320 }
321
322 /* Compute relative line number
323
324 This algorithm does not assign contiguous line numbers of ascending
325 LT19's. Rather, line numbers follow a simple progression based on
326 the relative IOT number and the subdevice select */
327
328 int32 ttx_getln (int32 dev, int32 pulse)
329 {
330 int32 rdno = ((dev - ttix_dib.dev) >> 1) & 3;
331
332 #if defined (PDP15) /* PDP-15? */
333 int32 sub = (pulse >> 4) & 3;
334 return (rdno * 4) + sub; /* use dev, subdev */
335 #else /* others */
336 return rdno; /* use dev only */
337 #endif
338 }
339
340 /* Reset routine */
341
342 t_stat ttx_reset (DEVICE *dptr)
343 {
344 int32 ln;
345
346 if (dptr->flags & DEV_DIS) { /* sync enables */
347 ttix_dev.flags = ttox_dev.flags | DEV_DIS;
348 ttox_dev.flags = ttox_dev.flags | DEV_DIS;
349 }
350 else {
351 ttix_dev.flags = ttix_dev.flags & ~DEV_DIS;
352 ttox_dev.flags = ttox_dev.flags & ~DEV_DIS;
353 }
354 if (ttix_unit.flags & UNIT_ATT) /* if attached, */
355 sim_activate (&ttix_unit, tmxr_poll); /* activate */
356 else sim_cancel (&ttix_unit); /* else stop */
357 for (ln = 0; ln < TTX_MAXL; ln++) ttx_reset_ln (ln); /* for all lines */
358 return SCPE_OK;
359 }
360
361 /* Reset line n */
362
363 void ttx_reset_ln (int32 ln)
364 {
365 ttix_buf[ln] = 0; /* clear buf, */
366 ttox_buf[ln] = 0;
367 ttix_clr_done (ln); /* clear done */
368 ttox_clr_done (ln);
369 sim_cancel (&ttox_unit[ln]); /* stop poll */
370 return;
371 }
372
373 /* Attach master unit */
374
375 t_stat ttx_attach (UNIT *uptr, char *cptr)
376 {
377 t_stat r;
378
379 r = tmxr_attach (&ttx_desc, uptr, cptr); /* attach */
380 if (r != SCPE_OK) return r; /* error */
381 sim_activate (uptr, tmxr_poll); /* start poll */
382 return SCPE_OK;
383 }
384
385 /* Detach master unit */
386
387 t_stat ttx_detach (UNIT *uptr)
388 {
389 int32 i;
390 t_stat r;
391
392 r = tmxr_detach (&ttx_desc, uptr); /* detach */
393 sim_cancel (uptr); /* stop poll */
394 for (i = 0; i < TTX_MAXL; i++) ttx_ldsc[i].rcve = 0; /* disable rcv */
395 return r;
396 }
397
398 /* Show summary processor */
399
400 t_stat ttx_summ (FILE *st, UNIT *uptr, int32 val, void *desc)
401 {
402 int32 i, t;
403
404 for (i = t = 0; i < TTX_MAXL; i++) t = t + (ttx_ldsc[i].conn != 0);
405 if (t == 1) fprintf (st, "1 connection");
406 else fprintf (st, "%d connections", t);
407 return SCPE_OK;
408 }
409
410 /* SHOW CONN/STAT processor */
411
412 t_stat ttx_show (FILE *st, UNIT *uptr, int32 val, void *desc)
413 {
414 int32 i, t;
415
416 for (i = t = 0; i < TTX_MAXL; i++) t = t + (ttx_ldsc[i].conn != 0);
417 if (t) {
418 for (i = 0; i < ttx_lines; i++) {
419 if (ttx_ldsc[i].conn) {
420 if (val) tmxr_fconns (st, &ttx_ldsc[i], i);
421 else tmxr_fstats (st, &ttx_ldsc[i], i);
422 }
423 }
424 }
425 else fprintf (st, "all disconnected\n");
426 return SCPE_OK;
427 }
428
429 /* Change number of lines */
430
431 t_stat ttx_vlines (UNIT *uptr, int32 val, char *cptr, void *desc)
432 {
433 int32 newln, i, t;
434 t_stat r;
435
436 if (cptr == NULL) return SCPE_ARG;
437 newln = get_uint (cptr, 10, TTX_MAXL, &r);
438 if ((r != SCPE_OK) || (newln == ttx_lines)) return r;
439 if (newln == 0) return SCPE_ARG;
440 if (newln < ttx_lines) {
441 for (i = newln, t = 0; i < ttx_lines; i++) t = t | ttx_ldsc[i].conn;
442 if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE))
443 return SCPE_OK;
444 for (i = newln; i < ttx_lines; i++) {
445 if (ttx_ldsc[i].conn) {
446 tmxr_linemsg (&ttx_ldsc[i], "\r\nOperator disconnected line\r\n");
447 tmxr_reset_ln (&ttx_ldsc[i]); /* reset line */
448 }
449 ttox_unit[i].flags = ttox_unit[i].flags | UNIT_DIS;
450 ttx_reset_ln (i);
451 }
452 }
453 else {
454 for (i = ttx_lines; i < newln; i++) {
455 ttox_unit[i].flags = ttox_unit[i].flags & ~UNIT_DIS;
456 ttx_reset_ln (i);
457 }
458 }
459 ttx_lines = newln;
460 return SCPE_OK;
461 }
462
463