First Commit of my working state
[simh.git] / Ibm1130 / ibm1130_t2741.c
1 /***************************************************************************************
2 * Nonstandard serial attachment for remote 2741 terminal (IO selectric) used by APL\1130
3 * This implementation may be incomplete and/or incorrect
4 ***************************************************************************************/
5
6 #include "ibm1130_defs.h"
7 #include "sim_sock.h"
8 #include "sim_tmxr.h"
9
10 #define DEBUG_T2741
11
12 static TMLN t2741_ldsc = { 0 }; /* line descr for telnet attachment */
13 static TMXR t2741_tmxr = { 1, 0, 0, &t2741_ldsc }; /* line mux for telnet attachment */
14
15 #define T2741_DSW_TRANSMIT_NOT_READY 0x4000
16 #define T2741_DSW_READ_RESPONSE 0x1000
17 #define T2741_DSW_READ_OVERRUN 0x0800
18 #define T2741_DSW_ATTENTION 0x0010
19
20 #define IS_ONLINE(u) (((u)->flags & (UNIT_ATT|UNIT_DIS)) == UNIT_ATT)
21
22 #define UNIT_V_PHYSICAL_TERM (UNIT_V_UF + 0) /* indicates not telnet but attachment to real terminal */
23 #define UNIT_V_UPCASE (UNIT_V_UF + 1) /* indicates upshift performed */
24 #define UNIT_V_SENDING (UNIT_V_UF + 2) /* indicates not telnet but attachment to real terminal */
25 #define UNIT_V_RECEIVING (UNIT_V_UF + 3) /* indicates not telnet but attachment to real terminal */
26
27 #define UNIT_PHYSICAL_TERM (1u << UNIT_V_PHYSICAL_TERM)
28 #define UNIT_UPCASE (1u << UNIT_V_UPCASE)
29 #define UNIT_SENDING (1u << UNIT_V_SENDING)
30 #define UNIT_RECEIVING (1u << UNIT_V_RECEIVING)
31
32 #define CODE_SHIFTUP 0x1C00
33 #define CODE_SHIFTDOWN 0x7C00
34 #define CODE_CIRCLEC 0x1F00
35 #define CODE_CIRCLED 0x1600
36 #define CODE_RETURN 0x5B00
37 #define CODE_LINEFEED 0x3B00
38 #define CODE_ATTENTION 0x0001 /* pseudocode, never really returned as a received character */
39 #define CODE_UNKNOWN 0x0000
40
41 static t_stat t2741_svc (UNIT *uptr);
42 static t_stat t2741_reset (DEVICE *dptr);
43 static t_stat t2741_attach (UNIT *uptr, char *cptr);
44 static t_stat t2741_detach (UNIT *uptr);
45 static uint16 ascii_to_t2741 (int ascii);
46 static char * t2741_to_ascii (uint16 code);
47 static void set_transmit_notready (void);
48
49 static uint16 t2741_dsw = T2741_DSW_TRANSMIT_NOT_READY; /* device status word */
50 static uint32 t2741_swait = 200; /* character send wait */
51 static uint32 t2741_rwait = 2000; /* character receive wait */
52 static uint16 t2741_char = 0; /* last character received */
53 static int overrun = FALSE;
54 static uint32 t2741_socket = 1130;
55
56 UNIT t2741_unit[1] = {
57 { UDATA (&t2741_svc, UNIT_ATTABLE, 0) },
58 };
59
60 REG t2741_reg[] = {
61 { HRDATA (DSW, t2741_dsw, 16) }, /* device status word */
62 { DRDATA (RTIME, t2741_rwait, 24), PV_LEFT }, /* character receive wait */
63 { DRDATA (STIME, t2741_swait, 24), PV_LEFT }, /* character send wait */
64 { DRDATA (SOCKET, t2741_socket,16), PV_LEFT }, /* socket number */
65 { HRDATA (LASTCHAR, t2741_char, 16), PV_LEFT }, /* last character read */
66 { NULL } };
67
68 DEVICE t2741_dev = {
69 "T2741", t2741_unit, t2741_reg, NULL,
70 1, 16, 16, 1, 16, 16,
71 NULL, NULL, t2741_reset,
72 NULL, t2741_attach, t2741_detach};
73
74 /* xio_t2741_terminal - XIO command interpreter for the terminal adapter */
75
76 void xio_t2741_terminal (int32 iocc_addr, int32 iocc_func, int32 iocc_mod)
77 {
78 char msg[80];
79 uint16 code;
80
81 switch (iocc_func) {
82 case XIO_READ: /* read: return last character read */
83 code = t2741_char & 0xFF00;
84 M[iocc_addr & mem_mask] = code;
85 overrun = FALSE;
86 #ifdef DEBUG_T2741
87 /* trace_both("T2741 %04x READ %02x %s", prev_IAR, code >> 8, t2741_to_ascii(code)); */
88 #endif
89 break;
90
91 case XIO_WRITE: /* write: initiate character send */
92 code = M[iocc_addr & mem_mask] & 0xFF00;
93 #ifdef DEBUG_T2741
94 trace_both("T2741 %04x SEND %02x %s", prev_IAR, code >> 8, t2741_to_ascii(code));
95 #endif
96 SETBIT(t2741_dsw, T2741_DSW_TRANSMIT_NOT_READY);
97 SETBIT(t2741_unit->flags, UNIT_SENDING);
98
99 if (code == CODE_SHIFTUP)
100 SETBIT(t2741_unit->flags, UNIT_UPCASE);
101 else if (code == CODE_SHIFTDOWN)
102 CLRBIT(t2741_unit->flags, UNIT_UPCASE);
103
104 sim_activate(t2741_unit, t2741_swait); /* schedule interrupt */
105 break;
106
107 case XIO_SENSE_DEV: /* sense device status */
108 ACC = t2741_dsw;
109 #ifdef DEBUG_T2741
110 /* trace_both("T2741 %04x SENS %04x%s", prev_IAR, t2741_dsw, (iocc_mod & 0x01) ? " reset" : ""); */
111 #endif
112 if (iocc_mod & 0x01) { /* reset interrupts */
113 CLRBIT(t2741_dsw, T2741_DSW_READ_RESPONSE);
114 CLRBIT(ILSW[4], ILSW_4_T2741_TERMINAL);
115 }
116 break;
117
118 case XIO_CONTROL: /* control: do something to interface */
119 #ifdef DEBUG_T2741
120 trace_both("T2741 %04x CTRL %04x", prev_IAR, iocc_mod &0xFF);
121 #endif
122 SETBIT(t2741_unit->flags, UNIT_RECEIVING); /* set mode to receive mode */
123 if (IS_ONLINE(t2741_unit) && (t2741_char != 0 || ! feof(t2741_unit->fileref))) {
124 sim_activate(t2741_unit, t2741_rwait);
125 t2741_char = (CODE_CIRCLED >> 8); /* first character received after turnaround is circled */
126 }
127 break;
128
129 default:
130 sprintf(msg, "Invalid T2741 XIO function %x", iocc_func);
131 xio_error(msg);
132 }
133 }
134
135 static void set_transmit_notready (void)
136 {
137 if (IS_ONLINE(t2741_unit) && ! (t2741_unit->flags & UNIT_SENDING))
138 CLRBIT(t2741_dsw, T2741_DSW_TRANSMIT_NOT_READY);
139 else
140 SETBIT(t2741_dsw, T2741_DSW_TRANSMIT_NOT_READY);
141 }
142
143 static t_stat t2741_svc (UNIT *uptr)
144 {
145 int ch = EOF;
146 uint16 code;
147
148 if (uptr->flags & UNIT_SENDING) { /* xmit: no interrupt, as far as I know. just clr busy bit */
149 CLRBIT(uptr->flags, UNIT_SENDING);
150 set_transmit_notready();
151 }
152
153 if (uptr->flags & UNIT_RECEIVING) { /* rcv: fire interrupt */
154 t2741_char <<= 8;
155
156 if (t2741_char == 0) { /* there is no 2nd character from previous ascii input */
157 if ((ch = getc(t2741_unit->fileref)) == EOF)
158 t2741_char = 0;
159 else {
160 if (ch == '\r') { /* if we get CR, jump to LF */
161 if ((ch = getc(t2741_unit->fileref)) != '\n') {
162 ungetc(ch, t2741_unit->fileref);
163 ch = '\r';
164 }
165 }
166
167 if (ch == '\027') {
168 t2741_char = CODE_LINEFEED; /* attention key sends line feed character */
169 #ifdef DEBUG_T2741
170 trace_both("T2741 ---- ATTENTION");
171 #endif
172 SETBIT(t2741_dsw, T2741_DSW_ATTENTION); /* no character returned ? */
173 }
174 else {
175 t2741_char = ascii_to_t2741(ch); /* translate to 2741 code(s) */
176 }
177 }
178 }
179
180 code = t2741_char & 0xFF00;
181
182 if (t2741_char != 0) {
183 if (overrun) /* previous character was not picked up! */
184 SETBIT(t2741_dsw, T2741_DSW_READ_OVERRUN);
185
186 SETBIT(t2741_dsw, T2741_DSW_READ_RESPONSE);
187 SETBIT(ILSW[4], ILSW_4_T2741_TERMINAL); /* issue interrupt */
188 calc_ints();
189
190 #ifdef DEBUG_T2741
191 trace_both("T2741 ---- RCVD %02x '%s' RDRESP%s%s", code >> 8, t2741_to_ascii(code),
192 (t2741_dsw & T2741_DSW_READ_OVERRUN) ? "|OVERRUN" : "",
193 (t2741_dsw & T2741_DSW_ATTENTION) ? "|ATTENTION" : "");
194 #endif
195
196 overrun = TRUE; /* arm overrun flag */
197 }
198
199 if (t2741_char == CODE_CIRCLEC) /* end of line (CIRCLEC after RETURN) auto downshifts */
200 CLRBIT(t2741_unit->flags, UNIT_UPCASE);
201
202 if (t2741_char == 0 || code == CODE_CIRCLEC)
203 CLRBIT(uptr->flags, UNIT_RECEIVING); /* on enter or EOF, stop typing */
204 else
205 sim_activate(t2741_unit, t2741_rwait); /* schedule next character to arrive */
206 }
207
208 return SCPE_OK;
209 }
210
211 static t_stat t2741_attach (UNIT *uptr, char *cptr)
212 {
213 int rval;
214
215 if ((rval = attach_unit(uptr, cptr)) == SCPE_OK) { /* use standard attach */
216 t2741_char = 0;
217 overrun = FALSE;
218
219 CLRBIT(t2741_unit->flags, UNIT_UPCASE);
220
221 if ((t2741_unit->flags & UNIT_RECEIVING) && ! feof(t2741_unit->fileref))
222 sim_activate(t2741_unit, t2741_rwait); /* schedule interrupt */
223 }
224
225 set_transmit_notready();
226
227 return rval;
228 }
229
230 static t_stat t2741_detach (UNIT *uptr)
231 {
232 t_stat rval;
233
234 if (t2741_unit->flags & UNIT_RECEIVING) /* if receive was pending, cancel interrupt */
235 sim_cancel(t2741_unit);
236
237 t2741_char = 0;
238 overrun = FALSE;
239
240 rval = detach_unit(uptr); /* use standard detach */
241
242 set_transmit_notready();
243
244 return rval;
245 }
246
247 static t_stat t2741_reset (DEVICE *dptr)
248 {
249 sim_cancel(t2741_unit);
250
251 CLRBIT(t2741_unit->flags, UNIT_SENDING|UNIT_RECEIVING|UNIT_UPCASE);
252
253 t2741_char = 0;
254 t2741_dsw = 0;
255 overrun = FALSE;
256
257 set_transmit_notready();
258
259 CLRBIT(ILSW[4], ILSW_4_T2741_TERMINAL);
260 calc_ints();
261
262 return SCPE_OK;
263 }
264
265 static struct tag_t2741_map {
266 int code;
267 int lcase, ucase;
268 t_bool shifts;
269 } t2741_map[] = {
270 {0x4F00, 'A', 'a', TRUE},
271 {0x3700, 'B', 'b', TRUE},
272 {0x2F00, 'C', 'c', TRUE},
273 {0x2A00, 'D', 'd', TRUE},
274 {0x2900, 'E', 'e', TRUE},
275 {0x6700, 'F', '_', TRUE},
276 {0x6200, 'G', 'g', TRUE},
277 {0x3200, 'H', 'h', TRUE},
278 {0x4C00, 'I', 'i', TRUE},
279 {0x6100, 'J', 'j', TRUE},
280 {0x2C00, 'K', '\'', TRUE},
281 {0x3100, 'L', 'l', TRUE},
282 {0x4300, 'M', '|', TRUE},
283 {0x2500, 'N', 'n', TRUE},
284 {0x5100, 'O', 'o', TRUE},
285 {0x6800, 'P', '*', TRUE},
286 {0x6D00, 'Q', '?', TRUE},
287 {0x4A00, 'R', 'r', TRUE},
288 {0x5200, 'S', 's', TRUE},
289 {0x2000, 'T', '~', TRUE},
290 {0x2600, 'U', 'u', TRUE},
291 {0x4600, 'V', 'v', TRUE},
292 {0x5700, 'W', 'w', TRUE},
293 {0x2300, 'X', 'x', TRUE},
294 {0x7300, 'Y', 'y', TRUE},
295 {0x1500, 'Z', 'z', TRUE},
296 {0x1300, '0', '&', TRUE},
297 {0x0200, '1', '?', TRUE},
298 {0x0400, '2', '?', TRUE},
299 {0x0700, '3', '<', TRUE},
300 {0x1000, '4', '?', TRUE},
301 {0x0800, '5', '=', TRUE},
302 {0x0D00, '6', '?', TRUE},
303 {0x0B00, '7', '>', TRUE},
304 {0x0E00, '8', '?', TRUE},
305 {0x1600, '9', '|', TRUE},
306 {0x7000, '/', '\\', TRUE},
307 {0x7600, '+', '-', TRUE},
308 {0x6400, '?', '?', TRUE},
309 {0x4000, '<', '>', TRUE},
310 {0x6B00, '[', '(', TRUE},
311 {0x4900, ']', ')', TRUE},
312 {0x6E00, ',', ';', TRUE},
313 {0x4500, '.', ':', TRUE},
314 {0x0100, ' ', 0, FALSE},
315 {0x5B00, '\r', 0, FALSE},
316 {0x3B00, '\n', 0, FALSE},
317 {0x5D00, '\b', 0, FALSE},
318 {0x5E00, '\t', 0, FALSE},
319 {0x0001, '\027', 0, FALSE},
320 };
321
322 static uint16 ascii_to_t2741 (int ascii)
323 {
324 int i;
325 uint16 rval = 0;
326
327 ascii &= 0xFF;
328
329 if (ascii == '\n') /* turn newlines into returns + CIRCLED? */
330 return CODE_RETURN | (CODE_CIRCLEC >> 8);
331
332 for (i = sizeof(t2741_map)/sizeof(t2741_map[0]); --i >= 0; ) {
333 if (t2741_map[i].shifts) {
334 if (t2741_map[i].lcase == ascii) {
335 rval = t2741_map[i].code;
336 if (t2741_unit->flags & UNIT_UPCASE) {
337 CLRBIT(t2741_unit->flags, UNIT_UPCASE);
338 rval = CODE_SHIFTDOWN | (rval >> 8);
339 }
340 return rval;
341 }
342 if (t2741_map[i].ucase == ascii) {
343 rval = t2741_map[i].code;
344 if (! (t2741_unit->flags & UNIT_UPCASE)) {
345 SETBIT(t2741_unit->flags, UNIT_UPCASE);
346 rval = CODE_SHIFTUP | (rval >> 8);
347 }
348 return rval;
349 }
350 }
351 else if (t2741_map[i].lcase == ascii)
352 return t2741_map[i].code;
353 }
354
355 return CODE_UNKNOWN;
356 }
357
358 static char * t2741_to_ascii (uint16 code)
359 {
360 int i;
361 static char string[2] = {'?', '\0'};
362
363 switch (code) {
364 case CODE_SHIFTUP: return "SHIFTUP";
365 case CODE_SHIFTDOWN: return "SHIFTDN";
366 case CODE_CIRCLEC: return "CIRCLEC";
367 case CODE_CIRCLED: return "CIRCLED";
368 }
369
370 for (i = sizeof(t2741_map)/sizeof(t2741_map[0]); --i >= 0; ) {
371 if (t2741_map[i].code == code) {
372 if (t2741_map[i].shifts) {
373 string[0] = (t2741_unit->flags & UNIT_UPCASE) ? t2741_map[i].ucase : t2741_map[i].lcase;
374 return string;
375 }
376 switch (t2741_map[i].lcase) {
377 case ' ': return " ";
378 case '\r': return "RETURN";
379 case '\n': return "LINEFEED";
380 case '\b': return "BS";
381 case '\t': return "IDLE";
382 }
383 break;
384 }
385 }
386
387 return "?";
388 }