First Commit of my working state
[simh.git] / PDP1 / pdp1_dcs.c
1 /* pdp1_dcs.c: PDP-1D terminal multiplexor simulator
2
3 Copyright (c) 2006, 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 dcs Type 630 data communications subsystem
27
28 This module implements up to 32 individual serial interfaces.
29 */
30
31 #include "pdp1_defs.h"
32 #include "sim_sock.h"
33 #include "sim_tmxr.h"
34
35 #define DCS_LINES 32 /* lines */
36 #define DCS_LINE_MASK (DCS_LINES - 1)
37 #define DCSL_WAIT 1000 /* output wait */
38 #define DCS_NUMLIN dcs_desc.lines
39
40 int32 dcs_sbs = 0; /* SBS level */
41 uint32 dcs_send = 0; /* line for send */
42 uint32 dcs_scan = 0; /* line for scanner */
43 uint8 dcs_flg[DCS_LINES]; /* line flags */
44 uint8 dcs_buf[DCS_LINES]; /* line bufffers */
45
46 extern int32 iosta, stop_inst;
47 extern int32 tmxr_poll;
48
49 TMLN dcs_ldsc[DCS_LINES] = { 0 }; /* line descriptors */
50 TMXR dcs_desc = { DCS_LINES, 0, 0, dcs_ldsc }; /* mux descriptor */
51
52 t_stat dcsi_svc (UNIT *uptr);
53 t_stat dcso_svc (UNIT *uptr);
54 t_stat dcs_reset (DEVICE *dptr);
55 t_stat dcs_attach (UNIT *uptr, char *cptr);
56 t_stat dcs_detach (UNIT *uptr);
57 t_stat dcs_summ (FILE *st, UNIT *uptr, int32 val, void *desc);
58 t_stat dcs_show (FILE *st, UNIT *uptr, int32 val, void *desc);
59 t_stat dcs_vlines (UNIT *uptr, int32 val, char *cptr, void *desc);
60 void dcs_reset_ln (int32 ln);
61 void dcs_scan_next (t_bool unlk);
62
63 /* DCS data structures
64
65 dcs_dev DCS device descriptor
66 dcs_unit DCS unit descriptor
67 dcs_reg DCS register list
68 dcs_mod DCS modifiers list
69 */
70
71 REG dcs_nlreg = { DRDATA (NLINES, DCS_NUMLIN, 6), PV_LEFT };
72
73 UNIT dcs_unit = { UDATA (&dcsi_svc, UNIT_ATTABLE, 0) };
74
75 REG dcs_reg[] = {
76 { BRDATA (BUF, dcs_buf, 8, 8, DCS_LINES) },
77 { BRDATA (FLAGS, dcs_flg, 8, 1, DCS_LINES) },
78 { FLDATA (SCNF, iosta, IOS_V_DCS) },
79 { ORDATA (SCAN, dcs_scan, 5) },
80 { ORDATA (SEND, dcs_send, 5) },
81 { DRDATA (SBSLVL, dcs_sbs, 4), REG_HRO },
82 { NULL }
83 };
84
85 MTAB dcs_mod[] = {
86 { MTAB_XTD|MTAB_VDV, 0, "SBSLVL", "SBSLVL",
87 &dev_set_sbs, &dev_show_sbs, (void *) &dcs_sbs },
88 { MTAB_XTD | MTAB_VDV | MTAB_VAL, 0, "lines", "LINES",
89 &dcs_vlines, NULL, &dcs_nlreg },
90 { MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT",
91 &tmxr_dscln, NULL, &dcs_desc },
92 { UNIT_ATT, UNIT_ATT, "connections", NULL, NULL, &dcs_summ },
93 { MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL,
94 NULL, &dcs_show, NULL },
95 { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL,
96 NULL, &dcs_show, NULL },
97 { 0 }
98 };
99
100 DEVICE dcs_dev = {
101 "DCS", &dcs_unit, dcs_reg, dcs_mod,
102 1, 10, 31, 1, 8, 8,
103 &tmxr_ex, &tmxr_dep, &dcs_reset,
104 NULL, &dcs_attach, &dcs_detach,
105 NULL, DEV_NET | DEV_DISABLE | DEV_DIS
106 };
107
108 /* DCSL data structures
109
110 dcsl_dev DCSL device descriptor
111 dcsl_unit DCSL unit descriptor
112 dcsl_reg DCSL register list
113 dcsl_mod DCSL modifiers list
114 */
115
116 UNIT dcsl_unit[] = {
117 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
118 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
119 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
120 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
121 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
122 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
123 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
124 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
125 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
126 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
127 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
128 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
129 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
130 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
131 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
132 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
133 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
134 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
135 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
136 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
137 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
138 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
139 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
140 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
141 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
142 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
143 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
144 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
145 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
146 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
147 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT },
148 { UDATA (&dcso_svc, TT_MODE_UC, 0), DCSL_WAIT }
149 };
150
151 MTAB dcsl_mod[] = {
152 { TT_MODE, TT_MODE_UC, "UC", "UC", NULL },
153 { TT_MODE, TT_MODE_7B, "7b", "7B", NULL },
154 { TT_MODE, TT_MODE_8B, "8b", "8B", NULL },
155 { TT_MODE, TT_MODE_7P, "7p", "7P", NULL },
156 { MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT",
157 &tmxr_dscln, NULL, &dcs_desc },
158 { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, "LOG", "LOG",
159 &tmxr_set_log, &tmxr_show_log, &dcs_desc },
160 { MTAB_XTD|MTAB_VUN|MTAB_NC, 0, NULL, "NOLOG",
161 &tmxr_set_nolog, NULL, &dcs_desc },
162 { 0 }
163 };
164
165 REG dcsl_reg[] = {
166 { URDATA (TIME, dcsl_unit[0].wait, 10, 24, 0,
167 DCS_LINES, REG_NZ + PV_LEFT) },
168 { NULL }
169 };
170
171 DEVICE dcsl_dev = {
172 "DCSL", dcsl_unit, dcsl_reg, dcsl_mod,
173 DCS_LINES, 10, 31, 1, 8, 8,
174 NULL, NULL, &dcs_reset,
175 NULL, NULL, NULL,
176 NULL, DEV_DIS
177 };
178
179 /* DCS IOT routine */
180
181 int32 dcs (int32 inst, int32 dev, int32 dat)
182 {
183 int32 pls = (inst >> 6) & 077;
184
185 if (dcs_dev.flags & DEV_DIS) /* disabled? */
186 return (stop_inst << IOT_V_REASON) | dat; /* illegal inst */
187 if (pls & 020) dat = 0; /* pulse 20? clr IO */
188
189 switch (pls & 057) { /* case IR<6,8:11> */
190
191 case 000: /* RCH */
192 dat |= dcs_buf[dcs_scan]; /* return line buf */
193 dcs_flg[dcs_scan] = 0; /* clr line flag */
194 break;
195
196 case 001: /* RRC */
197 dat |= dcs_scan; /* return line num */
198 break;
199
200 case 010: /* RCC */
201 dat |= dcs_buf[dcs_scan]; /* return line buf */
202 dcs_flg[dcs_scan] = 0; /* clr line flag */
203 /* fall through */
204 case 011: /* RSC */
205 dcs_scan_next (TRUE); /* unlock scanner */
206 break;
207
208 case 040: /* TCB */
209 dcs_buf[dcs_send] = dat & 0377; /* load buffer */
210 dcs_flg[dcs_send] = 0; /* clr line flag */
211 sim_activate (&dcsl_unit[dcs_send], dcsl_unit[dcs_send].wait);
212 break;
213
214 case 041: /* SSB */
215 dcs_send = dat & DCS_LINE_MASK; /* load line num */
216 break;
217
218 case 050: /* TCC */
219 dcs_buf[dcs_scan] = dat & 0377; /* load buffer */
220 dcs_flg[dcs_scan] = 0; /* clr line flag */
221 sim_activate (&dcsl_unit[dcs_scan], dcsl_unit[dcs_scan].wait);
222 dcs_scan_next (TRUE); /* unlock scanner */
223 break;
224
225 default:
226 return (stop_inst << IOT_V_REASON) | dat; /* illegal inst */
227 } /* end case */
228
229 return dat;
230 }
231
232 /* Unit service - receive side
233
234 Poll all active lines for input
235 Poll for new connections
236 */
237
238 t_stat dcsi_svc (UNIT *uptr)
239 {
240 int32 ln, c, out;
241
242 if ((uptr->flags & UNIT_ATT) == 0) return SCPE_OK; /* attached? */
243 if (dcs_dev.flags & DEV_DIS) return SCPE_OK;
244 sim_activate (uptr, tmxr_poll); /* continue poll */
245 ln = tmxr_poll_conn (&dcs_desc); /* look for connect */
246 if (ln >= 0) { /* got one? */
247 dcs_ldsc[ln].rcve = 1; /* set rcv enable */
248 }
249 tmxr_poll_rx (&dcs_desc); /* poll for input */
250 for (ln = 0; ln < DCS_NUMLIN; ln++) { /* loop thru lines */
251 if (dcs_ldsc[ln].conn) { /* connected? */
252 if (c = tmxr_getc_ln (&dcs_ldsc[ln])) { /* get char */
253 if (c & SCPE_BREAK) c = 0; /* break? */
254 else c = sim_tt_inpcvt (c, TT_GET_MODE (dcsl_unit[ln].flags)|TTUF_KSR);
255 dcs_buf[ln] = c; /* save char */
256 dcs_flg[ln] = 1; /* set line flag */
257 dcs_scan_next (FALSE); /* kick scanner */
258 out = sim_tt_outcvt (c & 0177, TT_GET_MODE (dcsl_unit[ln].flags));
259 if (out >= 0) {
260 tmxr_putc_ln (&dcs_ldsc[ln], out); /* echo char */
261 tmxr_poll_tx (&dcs_desc); /* poll xmt */
262 }
263 }
264 }
265 else dcs_ldsc[ln].rcve = 0; /* disconnected */
266 } /* end for */
267 return SCPE_OK;
268 }
269
270 /* Unit service - transmit side */
271
272 t_stat dcso_svc (UNIT *uptr)
273 {
274 int32 c;
275 uint32 ln = uptr - dcsl_unit; /* line # */
276
277 if (dcs_dev.flags & DEV_DIS) return SCPE_OK;
278 if (dcs_ldsc[ln].conn) { /* connected? */
279 if (dcs_ldsc[ln].xmte) { /* xmt enabled? */
280 c = sim_tt_outcvt (dcs_buf[ln] & 0177, TT_GET_MODE (uptr->flags));
281 if (c >= 0) tmxr_putc_ln (&dcs_ldsc[ln], c); /* output char */
282 tmxr_poll_tx (&dcs_desc); /* poll xmt */
283 }
284 else { /* buf full */
285 tmxr_poll_tx (&dcs_desc); /* poll xmt */
286 sim_activate (uptr, uptr->wait); /* reschedule */
287 return SCPE_OK;
288 }
289 }
290 dcs_flg[ln] = 1; /* set line flag */
291 dcs_scan_next (FALSE); /* kick scanner */
292 return SCPE_OK;
293 }
294
295 /* Kick scanner */
296
297 void dcs_scan_next (t_bool unlk)
298 {
299 int32 i;
300
301 if (unlk) iosta &= ~IOS_DCS; /* unlock? */
302 else if (iosta & IOS_DCS) return; /* no, locked? */
303 for (i = 0; i < DCS_LINES; i++) { /* scan flags */
304 dcs_scan = (dcs_scan + 1) & DCS_LINE_MASK; /* next flag */
305 if (dcs_flg[dcs_scan] != 0) { /* flag set? */
306 iosta |= IOS_DCS; /* lock scanner */
307 dev_req_int (dcs_sbs); /* request intr */
308 return;
309 }
310 }
311 return;
312 }
313
314 /* Reset routine */
315
316 t_stat dcs_reset (DEVICE *dptr)
317 {
318 int32 i;
319
320 if (dcs_dev.flags & DEV_DIS) /* master disabled? */
321 dcsl_dev.flags = dcsl_dev.flags | DEV_DIS; /* disable lines */
322 else dcsl_dev.flags = dcsl_dev.flags & ~DEV_DIS;
323 if (dcs_unit.flags & UNIT_ATT) /* master att? */
324 sim_activate_abs (&dcs_unit, tmxr_poll); /* activate */
325 else sim_cancel (&dcs_unit); /* else stop */
326 for (i = 0; i < DCS_LINES; i++) dcs_reset_ln (i); /* reset lines */
327 dcs_send = 0;
328 dcs_scan = 0;
329 iosta &= ~IOS_DCS; /* clr intr req */
330 return SCPE_OK;
331 }
332
333 /* Attach master unit */
334
335 t_stat dcs_attach (UNIT *uptr, char *cptr)
336 {
337 t_stat r;
338
339 r = tmxr_attach (&dcs_desc, uptr, cptr); /* attach */
340 if (r != SCPE_OK) return r; /* error */
341 sim_activate_abs (uptr, tmxr_poll); /* start poll */
342 return SCPE_OK;
343 }
344
345 /* Detach master unit */
346
347 t_stat dcs_detach (UNIT *uptr)
348 {
349 int32 i;
350 t_stat r;
351
352 r = tmxr_detach (&dcs_desc, uptr); /* detach */
353 for (i = 0; i < DCS_LINES; i++) dcs_ldsc[i].rcve = 0; /* disable rcv */
354 sim_cancel (uptr); /* stop poll */
355 return r;
356 }
357
358 /* Show summary processor */
359
360 t_stat dcs_summ (FILE *st, UNIT *uptr, int32 val, void *desc)
361 {
362 int32 i, t;
363
364 for (i = t = 0; i < DCS_LINES; i++) t = t + (dcs_ldsc[i].conn != 0);
365 if (t == 1) fprintf (st, "1 connection");
366 else fprintf (st, "%d connections", t);
367 return SCPE_OK;
368 }
369
370 /* SHOW CONN/STAT processor */
371
372 t_stat dcs_show (FILE *st, UNIT *uptr, int32 val, void *desc)
373 {
374 int32 i, t;
375
376 for (i = t = 0; i < DCS_LINES; i++) t = t + (dcs_ldsc[i].conn != 0);
377 if (t) {
378 for (i = 0; i < DCS_LINES; i++) {
379 if (dcs_ldsc[i].conn) {
380 if (val) tmxr_fconns (st, &dcs_ldsc[i], i);
381 else tmxr_fstats (st, &dcs_ldsc[i], i);
382 }
383 }
384 }
385 else fprintf (st, "all disconnected\n");
386 return SCPE_OK;
387 }
388
389 /* Change number of lines */
390
391 t_stat dcs_vlines (UNIT *uptr, int32 val, char *cptr, void *desc)
392 {
393 int32 newln, i, t;
394 t_stat r;
395
396 if (cptr == NULL) return SCPE_ARG;
397 newln = get_uint (cptr, 10, DCS_LINES, &r);
398 if ((r != SCPE_OK) || (newln == DCS_NUMLIN)) return r;
399 if (newln == 0) return SCPE_ARG;
400 if (newln < DCS_LINES) {
401 for (i = newln, t = 0; i < DCS_NUMLIN; i++) t = t | dcs_ldsc[i].conn;
402 if (t && !get_yn ("This will disconnect users; proceed [N]?", FALSE))
403 return SCPE_OK;
404 for (i = newln; i < DCS_NUMLIN; i++) {
405 if (dcs_ldsc[i].conn) {
406 tmxr_linemsg (&dcs_ldsc[i], "\r\nOperator disconnected line\r\n");
407 tmxr_reset_ln (&dcs_ldsc[i]); /* reset line */
408 }
409 dcsl_unit[i].flags = dcsl_unit[i].flags | UNIT_DIS;
410 dcs_reset_ln (i);
411 }
412 }
413 else {
414 for (i = DCS_NUMLIN; i < newln; i++) {
415 dcsl_unit[i].flags = dcsl_unit[i].flags & ~UNIT_DIS;
416 dcs_reset_ln (i);
417 }
418 }
419 DCS_NUMLIN = newln;
420 return SCPE_OK;
421 }
422
423 /* Reset an individual line */
424
425 void dcs_reset_ln (int32 ln)
426 {
427 sim_cancel (&dcsl_unit[ln]);
428 dcs_buf[ln] = 0;
429 dcs_flg[ln] = 0;
430 return;
431 }