| 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 |
| 12 | static TMLN t2741_ldsc = { 0 }; /* line descr for telnet attachment */\r |
| 13 | static 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 |
| 41 | static t_stat t2741_svc (UNIT *uptr);\r |
| 42 | static t_stat t2741_reset (DEVICE *dptr);\r |
| 43 | static t_stat t2741_attach (UNIT *uptr, char *cptr);\r |
| 44 | static t_stat t2741_detach (UNIT *uptr);\r |
| 45 | static uint16 ascii_to_t2741 (int ascii);\r |
| 46 | static char * t2741_to_ascii (uint16 code);\r |
| 47 | static void set_transmit_notready (void);\r |
| 48 | \r |
| 49 | static uint16 t2741_dsw = T2741_DSW_TRANSMIT_NOT_READY; /* device status word */\r |
| 50 | static uint32 t2741_swait = 200; /* character send wait */\r |
| 51 | static uint32 t2741_rwait = 2000; /* character receive wait */\r |
| 52 | static uint16 t2741_char = 0; /* last character received */\r |
| 53 | static int overrun = FALSE;\r |
| 54 | static uint32 t2741_socket = 1130;\r |
| 55 | \r |
| 56 | UNIT t2741_unit[1] = {\r |
| 57 | { UDATA (&t2741_svc, UNIT_ATTABLE, 0) },\r |
| 58 | };\r |
| 59 | \r |
| 60 | REG 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 |
| 68 | DEVICE 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 |
| 76 | void 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 |
| 135 | static 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 |
| 143 | static 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 |
| 211 | static 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 |
| 230 | static 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 |
| 247 | static 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 |
| 265 | static 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 |
| 322 | static 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 |
| 358 | static 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 |