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