1 /* pdp15_ttx.c: PDP-15 additional terminals simulator
3 Copyright (c) 1993-2007, 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 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.
26 ttix,ttox LT15/LT19 terminal input/output
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
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.
41 #include "pdp18b_defs.h"
47 #define TTX_MAXL 16 /* max number of lines */
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
96 { &ttox
, &ttix
, &ttox
, &ttix
, &ttox
, &ttix
, &ttox
, &ttix
}
99 UNIT ttix_unit
= { UDATA (&ttix_svc
, UNIT_IDLE
|UNIT_ATTABLE
, 0), KBD_POLL_WAIT
};
101 REG ttx_nlreg
= { DRDATA (NLINES
, ttx_lines
, 4), PV_LEFT
};
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
},
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
},
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
135 /* TTOx data structures
137 ttox_dev TTOx device descriptor
138 ttox_unit TTOx unit descriptor
139 ttox_reg TTOx register list
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
}
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
) },
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
},
185 "TTOX", ttox_unit
, ttox_reg
, ttox_mod
,
186 TTX_MAXL
, 10, 31, 1, 8, 8,
187 NULL
, NULL
, &ttx_reset
,
192 /* Terminal input: IOT routine */
194 int32
ttix (int32 dev
, int32 pulse
, int32 dat
)
196 int32 ln
= ttx_getln (dev
, pulse
); /* line # */
198 if (ln
> ttx_lines
) return dat
;
199 if (pulse
& 001) { /* KSF1 */
200 if (ttix_test_done (ln
)) dat
= dat
| IOT_SKP
;
202 if (pulse
& 002) { /* KRB1 */
203 ttix_clr_done (ln
); /* clear flag */
204 dat
= dat
| ttix_buf
[ln
]; /* return buffer */
211 t_stat
ttix_svc (UNIT
*uptr
)
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
);
233 /* Interrupt handling routines */
235 t_bool
ttix_test_done (int32 ln
)
237 if (ttix_done
& (1 << ln
)) return TRUE
;
241 void ttix_set_done (int32 ln
)
243 ttix_done
= ttix_done
| (1 << ln
);
248 void ttix_clr_done (int32 ln
)
250 ttix_done
= ttix_done
& ~(1 << ln
);
251 if (ttix_done
) { SET_INT (TTI1
); }
258 /* Terminal output: IOT routine */
260 int32
ttox (int32 dev
, int32 pulse
, int32 dat
)
262 int32 ln
= ttx_getln (dev
, pulse
); /* line # */
264 if (ln
> ttx_lines
) return dat
;
265 if (pulse
& 001) { /* TSF */
266 if (ttox_test_done (ln
)) dat
= dat
| IOT_SKP
;
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 */
278 t_stat
ttox_svc (UNIT
*uptr
)
280 int32 c
, ln
= uptr
- ttox_unit
; /* line # */
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 */
290 tmxr_poll_tx (&ttx_desc
); /* poll xmt */
291 sim_activate (uptr
, ttox_unit
[ln
].wait
); /* wait */
295 ttox_set_done (ln
); /* set done */
299 /* Interrupt handling routines */
301 t_bool
ttox_test_done (int32 ln
)
303 if (ttox_done
& (1 << ln
)) return TRUE
;
307 void ttox_set_done (int32 ln
)
309 ttox_done
= ttox_done
| (1 << ln
);
314 void ttox_clr_done (int32 ln
)
316 ttox_done
= ttox_done
& ~(1 << ln
);
317 if (ttox_done
) { SET_INT (TTO1
); }
318 else { CLR_INT (TTO1
); }
322 /* Compute relative line number
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 */
328 int32
ttx_getln (int32 dev
, int32 pulse
)
330 int32 rdno
= ((dev
- ttix_dib
.dev
) >> 1) & 3;
332 #if defined (PDP15) /* PDP-15? */
333 int32 sub
= (pulse
>> 4) & 3;
334 return (rdno
* 4) + sub
; /* use dev, subdev */
336 return rdno
; /* use dev only */
342 t_stat
ttx_reset (DEVICE
*dptr
)
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
;
351 ttix_dev
.flags
= ttix_dev
.flags
& ~DEV_DIS
;
352 ttox_dev
.flags
= ttox_dev
.flags
& ~DEV_DIS
;
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 */
363 void ttx_reset_ln (int32 ln
)
365 ttix_buf
[ln
] = 0; /* clear buf, */
367 ttix_clr_done (ln
); /* clear done */
369 sim_cancel (&ttox_unit
[ln
]); /* stop poll */
373 /* Attach master unit */
375 t_stat
ttx_attach (UNIT
*uptr
, char *cptr
)
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 */
385 /* Detach master unit */
387 t_stat
ttx_detach (UNIT
*uptr
)
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 */
398 /* Show summary processor */
400 t_stat
ttx_summ (FILE *st
, UNIT
*uptr
, int32 val
, void *desc
)
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
);
410 /* SHOW CONN/STAT processor */
412 t_stat
ttx_show (FILE *st
, UNIT
*uptr
, int32 val
, void *desc
)
416 for (i
= t
= 0; i
< TTX_MAXL
; i
++) t
= t
+ (ttx_ldsc
[i
].conn
!= 0);
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
);
425 else fprintf (st
, "all disconnected\n");
429 /* Change number of lines */
431 t_stat
ttx_vlines (UNIT
*uptr
, int32 val
, char *cptr
, void *desc
)
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
))
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 */
449 ttox_unit
[i
].flags
= ttox_unit
[i
].flags
| UNIT_DIS
;
454 for (i
= ttx_lines
; i
< newln
; i
++) {
455 ttox_unit
[i
].flags
= ttox_unit
[i
].flags
& ~UNIT_DIS
;