First Commit of my working state
[simh.git] / Interdata / id_tt.c
1 /* id_tt.c: Interdata teletype
2
3 Copyright (c) 2000-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 tt console
27
28 18-Jun-07 RMS Added UNIT_IDLE flag to console input
29 18-Oct-06 RMS Sync keyboard to LFC clock
30 30-Sep-06 RMS Fixed handling of non-printable characters in KSR mode
31 22-Nov-05 RMS Revised for new terminal processing routines
32 29-Dec-03 RMS Added support for console backpressure
33 25-Apr-03 RMS Revised for extended file support
34 11-Jan-03 RMS Added TTP support
35 22-Dec-02 RMS Added break support
36 */
37
38 #include "id_defs.h"
39 #include <ctype.h>
40
41 /* Device definitions */
42
43 #define TTI 0
44 #define TTO 1
45
46 #define STA_OVR 0x80 /* overrun */
47 #define STA_BRK 0x20 /* break */
48 #define STA_MASK (STA_OVR | STA_BRK | STA_BSY) /* status mask */
49 #define SET_EX (STA_OVR | STA_BRK) /* set EX */
50
51 #define CMD_V_FDPX 4 /* full/half duplex */
52 #define CMD_V_RD 2 /* read/write */
53
54 extern uint32 int_req[INTSZ], int_enb[INTSZ];
55 extern int32 lfc_poll;
56
57 uint32 tt_sta = STA_BSY; /* status */
58 uint32 tt_fdpx = 1; /* tt mode */
59 uint32 tt_rd = 1, tt_chp = 0; /* tt state */
60 uint32 tt_arm = 0; /* int arm */
61
62 uint32 tt (uint32 dev, uint32 op, uint32 dat);
63 t_stat tti_svc (UNIT *uptr);
64 t_stat tto_svc (UNIT *uptr);
65 t_stat tt_reset (DEVICE *dptr);
66 t_stat tt_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc);
67 t_stat tt_set_break (UNIT *uptr, int32 val, char *cptr, void *desc);
68 t_stat tt_set_enbdis (UNIT *uptr, int32 val, char *cptr, void *desc);
69
70 /* TT data structures
71
72 tt_dev TT device descriptor
73 tt_unit TT unit descriptors
74 tt_reg TT register list
75 tt_mod TT modifiers list
76 */
77
78 DIB tt_dib = { d_TT, -1, v_TT, NULL, &tt, NULL };
79
80 UNIT tt_unit[] = {
81 { UDATA (&tti_svc, TT_MODE_KSR|UNIT_IDLE, 0), 0 },
82 { UDATA (&tto_svc, TT_MODE_KSR, 0), SERIAL_OUT_WAIT }
83 };
84
85 REG tt_reg[] = {
86 { HRDATA (STA, tt_sta, 8) },
87 { HRDATA (KBUF, tt_unit[TTI].buf, 8) },
88 { DRDATA (KPOS, tt_unit[TTI].pos, T_ADDR_W), PV_LEFT },
89 { DRDATA (KTIME, tt_unit[TTI].wait, 24), PV_LEFT },
90 { HRDATA (TBUF, tt_unit[TTO].buf, 8) },
91 { DRDATA (TPOS, tt_unit[TTO].pos, T_ADDR_W), PV_LEFT },
92 { DRDATA (TTIME, tt_unit[TTO].wait, 24), REG_NZ + PV_LEFT },
93 { FLDATA (IREQ, int_req[l_TT], i_TT) },
94 { FLDATA (IENB, int_enb[l_TT], i_TT) },
95 { FLDATA (IARM, tt_arm, 0) },
96 { FLDATA (RD, tt_rd, 0) },
97 { FLDATA (FDPX, tt_fdpx, 0) },
98 { FLDATA (CHP, tt_chp, 0) },
99 { HRDATA (DEVNO, tt_dib.dno, 8), REG_HRO },
100 { NULL }
101 };
102
103 MTAB tt_mod[] = {
104 { TT_MODE, TT_MODE_KSR, "KSR", "KSR", &tt_set_mode },
105 { TT_MODE, TT_MODE_7B, "7b", "7B", &tt_set_mode },
106 { TT_MODE, TT_MODE_8B, "8b", "8B", &tt_set_mode },
107 { TT_MODE, TT_MODE_7P, "7p", "7P", &tt_set_mode },
108 { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, NULL, "ENABLED",
109 &tt_set_enbdis, NULL, NULL },
110 { MTAB_XTD|MTAB_VDV|MTAB_NMO, DEV_DIS, NULL, "DISABLED",
111 &tt_set_enbdis, NULL, NULL },
112 { MTAB_XTD|MTAB_VDV|MTAB_NMO, 0, NULL, "BREAK",
113 &tt_set_break, NULL, NULL },
114 { MTAB_XTD|MTAB_VDV, 0, "DEVNO", "DEVNO",
115 &set_dev, &show_dev, &tt_dib },
116 { 0 }
117 };
118
119 DEVICE tt_dev = {
120 "TT", tt_unit, tt_reg, tt_mod,
121 2, 10, 31, 1, 16, 8,
122 NULL, NULL, &tt_reset,
123 NULL, NULL, NULL,
124 &tt_dib, 0
125 };
126
127 /* Terminal: IO routine */
128
129 uint32 tt (uint32 dev, uint32 op, uint32 dat)
130 {
131 uint32 old_rd, t;
132
133 switch (op) { /* case IO op */
134
135 case IO_ADR: /* select */
136 return BY; /* byte only */
137
138 case IO_OC: /* command */
139 old_rd = tt_rd;
140 tt_arm = int_chg (v_TT, dat, tt_arm); /* upd int ctrl */
141 tt_fdpx = io_2b (dat, CMD_V_FDPX, tt_fdpx); /* upd full/half */
142 tt_rd = io_2b (dat, CMD_V_RD, tt_rd); /* upd rd/write */
143 if (tt_rd != old_rd) { /* rw change? */
144 if (tt_rd? tt_chp: !sim_is_active (&tt_unit[TTO])) {
145 tt_sta = 0; /* busy = 0 */
146 if (tt_arm) SET_INT (v_TT); /* req intr */
147 }
148 else {
149 tt_sta = STA_BSY; /* busy = 1 */
150 CLR_INT (v_TT); /* clr int */
151 }
152 }
153 else tt_sta = tt_sta & ~STA_OVR; /* clr ovflo */
154 break;
155
156 case IO_RD: /* read */
157 tt_chp = 0; /* clear pend */
158 if (tt_rd) tt_sta = (tt_sta | STA_BSY) & ~STA_OVR;
159 return (tt_unit[TTI].buf & 0xFF);
160
161 case IO_WD: /* write */
162 tt_unit[TTO].buf = dat & 0xFF; /* save char */
163 if (!tt_rd) tt_sta = tt_sta | STA_BSY; /* set busy */
164 sim_activate (&tt_unit[TTO], tt_unit[TTO].wait);
165 break;
166
167 case IO_SS: /* status */
168 t = tt_sta & STA_MASK; /* get status */
169 if (t & SET_EX) t = t | STA_EX; /* test for EX */
170 return t;
171 }
172
173 return 0;
174 }
175
176 /* Unit service routines */
177
178 t_stat tti_svc (UNIT *uptr)
179 {
180 int32 out, temp;
181
182 sim_activate (uptr, KBD_WAIT (uptr->wait, lfc_poll)); /* continue poll */
183 tt_sta = tt_sta & ~STA_BRK; /* clear break */
184 if ((temp = sim_poll_kbd ()) < SCPE_KFLAG) return temp; /* no char or error? */
185 if (tt_rd) { /* read mode? */
186 tt_sta = tt_sta & ~STA_BSY; /* clear busy */
187 if (tt_arm) SET_INT (v_TT); /* if armed, intr */
188 if (tt_chp) tt_sta = tt_sta | STA_OVR; /* got char? overrun */
189 }
190 tt_chp = 1; /* char pending */
191 out = temp & 0x7F; /* echo is 7B */
192 if (temp & SCPE_BREAK) { /* break? */
193 tt_sta = tt_sta | STA_BRK; /* set status */
194 uptr->buf = 0; /* no character */
195 }
196 else uptr->buf = sim_tt_inpcvt (temp, TT_GET_MODE (uptr->flags) | TTUF_KSR);
197 uptr->pos = uptr->pos + 1; /* incr count */
198 if (!tt_fdpx) { /* half duplex? */
199 out = sim_tt_outcvt (out, TT_GET_MODE (uptr->flags) | TTUF_KSR);
200 if (out >= 0) { /* valid echo? */
201 sim_putchar (out); /* write char */
202 tt_unit[TTO].pos = tt_unit[TTO].pos + 1;
203 }
204 }
205 return SCPE_OK;
206 }
207
208 t_stat tto_svc (UNIT *uptr)
209 {
210 int32 ch;
211 t_stat r;
212
213 ch = sim_tt_outcvt (uptr->buf, TT_GET_MODE (uptr->flags) | TTUF_KSR);
214 if (ch >= 0) {
215 if ((r = sim_putchar_s (ch)) != SCPE_OK) { /* output; error? */
216 sim_activate (uptr, uptr->wait); /* try again */
217 return ((r == SCPE_STALL)? SCPE_OK: r);
218 }
219 }
220 if (!tt_rd) { /* write mode? */
221 tt_sta = tt_sta & ~STA_BSY; /* clear busy */
222 if (tt_arm) SET_INT (v_TT); /* if armed, intr */
223 }
224 uptr->pos = uptr->pos + 1; /* incr count */
225 return SCPE_OK;
226 }
227
228 /* Reset routine */
229
230 t_stat tt_reset (DEVICE *dptr)
231 {
232 if (dptr->flags & DEV_DIS) sim_cancel (&tt_unit[TTI]); /* dis? cancel poll */
233 else sim_activate_abs (&tt_unit[TTI], KBD_WAIT (tt_unit[TTI].wait, lfc_poll));
234 sim_cancel (&tt_unit[TTO]); /* cancel output */
235 tt_rd = tt_fdpx = 1; /* read, full duplex */
236 tt_chp = 0; /* no char */
237 tt_sta = STA_BSY; /* buffer empty */
238 CLR_INT (v_TT); /* clear int */
239 CLR_ENB (v_TT); /* disable int */
240 tt_arm = 0; /* disarm int */
241 return SCPE_OK;
242 }
243
244 /* Make mode flags uniform */
245
246 t_stat tt_set_mode (UNIT *uptr, int32 val, char *cptr, void *desc)
247 {
248 tt_unit[TTO].flags = (tt_unit[TTO].flags & ~TT_MODE) | val;
249 if (val == TT_MODE_7P) val = TT_MODE_7B;
250 tt_unit[TTI].flags = (tt_unit[TTI].flags & ~TT_MODE) | val;
251 return SCPE_OK;
252 }
253
254 /* Set input break */
255
256 t_stat tt_set_break (UNIT *uptr, int32 val, char *cptr, void *desc)
257 {
258 if (tt_dev.flags & DEV_DIS) return SCPE_NOFNC;
259 tt_sta = tt_sta | STA_BRK;
260 if (tt_rd) { /* read mode? */
261 tt_sta = tt_sta & ~STA_BSY; /* clear busy */
262 if (tt_arm) SET_INT (v_TT); /* if armed, intr */
263 }
264 sim_cancel (&tt_unit[TTI]); /* restart TT poll */
265 sim_activate (&tt_unit[TTI], tt_unit[TTI].wait); /* so brk is seen */
266 return SCPE_OK;
267 }
268
269 /* Set enabled/disabled */
270
271 t_stat tt_set_enbdis (UNIT *uptr, int32 val, char *cptr, void *desc)
272 {
273 extern DEVICE ttp_dev;
274 extern t_stat ttp_reset (DEVICE *dptr);
275
276 tt_dev.flags = (tt_dev.flags & ~DEV_DIS) | val;
277 ttp_dev.flags = (ttp_dev.flags & ~DEV_DIS) | (val ^ DEV_DIS);
278 tt_reset (&tt_dev);
279 ttp_reset (&ttp_dev);
280 return SCPE_OK;
281 }