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 ***************************************************************************************/
6 #include "ibm1130_defs.h"
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 */
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
20 #define IS_ONLINE(u) (((u)->flags & (UNIT_ATT|UNIT_DIS)) == UNIT_ATT)
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 */
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)
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
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);
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;
56 UNIT t2741_unit
[1] = {
57 { UDATA (&t2741_svc
, UNIT_ATTABLE
, 0) },
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 */
69 "T2741", t2741_unit
, t2741_reg
, NULL
,
71 NULL
, NULL
, t2741_reset
,
72 NULL
, t2741_attach
, t2741_detach
};
74 /* xio_t2741_terminal - XIO command interpreter for the terminal adapter */
76 void xio_t2741_terminal (int32 iocc_addr
, int32 iocc_func
, int32 iocc_mod
)
82 case XIO_READ
: /* read: return last character read */
83 code
= t2741_char
& 0xFF00;
84 M
[iocc_addr
& mem_mask
] = code
;
87 /* trace_both("T2741 %04x READ %02x %s", prev_IAR, code >> 8, t2741_to_ascii(code)); */
91 case XIO_WRITE
: /* write: initiate character send */
92 code
= M
[iocc_addr
& mem_mask
] & 0xFF00;
94 trace_both("T2741 %04x SEND %02x %s", prev_IAR
, code
>> 8, t2741_to_ascii(code
));
96 SETBIT(t2741_dsw
, T2741_DSW_TRANSMIT_NOT_READY
);
97 SETBIT(t2741_unit
->flags
, UNIT_SENDING
);
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
);
104 sim_activate(t2741_unit
, t2741_swait
); /* schedule interrupt */
107 case XIO_SENSE_DEV
: /* sense device status */
110 /* trace_both("T2741 %04x SENS %04x%s", prev_IAR, t2741_dsw, (iocc_mod & 0x01) ? " reset" : ""); */
112 if (iocc_mod
& 0x01) { /* reset interrupts */
113 CLRBIT(t2741_dsw
, T2741_DSW_READ_RESPONSE
);
114 CLRBIT(ILSW
[4], ILSW_4_T2741_TERMINAL
);
118 case XIO_CONTROL
: /* control: do something to interface */
120 trace_both("T2741 %04x CTRL %04x", prev_IAR
, iocc_mod
&0xFF);
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 */
130 sprintf(msg
, "Invalid T2741 XIO function %x", iocc_func
);
135 static void set_transmit_notready (void)
137 if (IS_ONLINE(t2741_unit
) && ! (t2741_unit
->flags
& UNIT_SENDING
))
138 CLRBIT(t2741_dsw
, T2741_DSW_TRANSMIT_NOT_READY
);
140 SETBIT(t2741_dsw
, T2741_DSW_TRANSMIT_NOT_READY
);
143 static t_stat
t2741_svc (UNIT
*uptr
)
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();
153 if (uptr
->flags
& UNIT_RECEIVING
) { /* rcv: fire interrupt */
156 if (t2741_char
== 0) { /* there is no 2nd character from previous ascii input */
157 if ((ch
= getc(t2741_unit
->fileref
)) == EOF
)
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
);
168 t2741_char
= CODE_LINEFEED
; /* attention key sends line feed character */
170 trace_both("T2741 ---- ATTENTION");
172 SETBIT(t2741_dsw
, T2741_DSW_ATTENTION
); /* no character returned ? */
175 t2741_char
= ascii_to_t2741(ch
); /* translate to 2741 code(s) */
180 code
= t2741_char
& 0xFF00;
182 if (t2741_char
!= 0) {
183 if (overrun
) /* previous character was not picked up! */
184 SETBIT(t2741_dsw
, T2741_DSW_READ_OVERRUN
);
186 SETBIT(t2741_dsw
, T2741_DSW_READ_RESPONSE
);
187 SETBIT(ILSW
[4], ILSW_4_T2741_TERMINAL
); /* issue interrupt */
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" : "");
196 overrun
= TRUE
; /* arm overrun flag */
199 if (t2741_char
== CODE_CIRCLEC
) /* end of line (CIRCLEC after RETURN) auto downshifts */
200 CLRBIT(t2741_unit
->flags
, UNIT_UPCASE
);
202 if (t2741_char
== 0 || code
== CODE_CIRCLEC
)
203 CLRBIT(uptr
->flags
, UNIT_RECEIVING
); /* on enter or EOF, stop typing */
205 sim_activate(t2741_unit
, t2741_rwait
); /* schedule next character to arrive */
211 static t_stat
t2741_attach (UNIT
*uptr
, char *cptr
)
215 if ((rval
= attach_unit(uptr
, cptr
)) == SCPE_OK
) { /* use standard attach */
219 CLRBIT(t2741_unit
->flags
, UNIT_UPCASE
);
221 if ((t2741_unit
->flags
& UNIT_RECEIVING
) && ! feof(t2741_unit
->fileref
))
222 sim_activate(t2741_unit
, t2741_rwait
); /* schedule interrupt */
225 set_transmit_notready();
230 static t_stat
t2741_detach (UNIT
*uptr
)
234 if (t2741_unit
->flags
& UNIT_RECEIVING
) /* if receive was pending, cancel interrupt */
235 sim_cancel(t2741_unit
);
240 rval
= detach_unit(uptr
); /* use standard detach */
242 set_transmit_notready();
247 static t_stat
t2741_reset (DEVICE
*dptr
)
249 sim_cancel(t2741_unit
);
251 CLRBIT(t2741_unit
->flags
, UNIT_SENDING
|UNIT_RECEIVING
|UNIT_UPCASE
);
257 set_transmit_notready();
259 CLRBIT(ILSW
[4], ILSW_4_T2741_TERMINAL
);
265 static struct tag_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
},
322 static uint16
ascii_to_t2741 (int ascii
)
329 if (ascii
== '\n') /* turn newlines into returns + CIRCLED? */
330 return CODE_RETURN
| (CODE_CIRCLEC
>> 8);
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);
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);
351 else if (t2741_map
[i
].lcase
== ascii
)
352 return t2741_map
[i
].code
;
358 static char * t2741_to_ascii (uint16 code
)
361 static char string
[2] = {'?', '\0'};
364 case CODE_SHIFTUP
: return "SHIFTUP";
365 case CODE_SHIFTDOWN
: return "SHIFTDN";
366 case CODE_CIRCLEC
: return "CIRCLEC";
367 case CODE_CIRCLED
: return "CIRCLED";
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
;
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";