388 lines
14 KiB
C
388 lines
14 KiB
C
/***************************************************************************************
|
|
* Nonstandard serial attachment for remote 2741 terminal (IO selectric) used by APL\1130
|
|
* This implementation may be incomplete and/or incorrect
|
|
***************************************************************************************/
|
|
|
|
#include "ibm1130_defs.h"
|
|
#include "sim_sock.h"
|
|
#include "sim_tmxr.h"
|
|
|
|
#define DEBUG_T2741
|
|
|
|
static TMLN t2741_ldsc = { 0 }; /* line descr for telnet attachment */
|
|
static TMXR t2741_tmxr = { 1, 0, 0, &t2741_ldsc }; /* line mux for telnet attachment */
|
|
|
|
#define T2741_DSW_TRANSMIT_NOT_READY 0x4000
|
|
#define T2741_DSW_READ_RESPONSE 0x1000
|
|
#define T2741_DSW_READ_OVERRUN 0x0800
|
|
#define T2741_DSW_ATTENTION 0x0010
|
|
|
|
#define IS_ONLINE(u) (((u)->flags & (UNIT_ATT|UNIT_DIS)) == UNIT_ATT)
|
|
|
|
#define UNIT_V_PHYSICAL_TERM (UNIT_V_UF + 0) /* indicates not telnet but attachment to real terminal */
|
|
#define UNIT_V_UPCASE (UNIT_V_UF + 1) /* indicates upshift performed */
|
|
#define UNIT_V_SENDING (UNIT_V_UF + 2) /* indicates not telnet but attachment to real terminal */
|
|
#define UNIT_V_RECEIVING (UNIT_V_UF + 3) /* indicates not telnet but attachment to real terminal */
|
|
|
|
#define UNIT_PHYSICAL_TERM (1u << UNIT_V_PHYSICAL_TERM)
|
|
#define UNIT_UPCASE (1u << UNIT_V_UPCASE)
|
|
#define UNIT_SENDING (1u << UNIT_V_SENDING)
|
|
#define UNIT_RECEIVING (1u << UNIT_V_RECEIVING)
|
|
|
|
#define CODE_SHIFTUP 0x1C00
|
|
#define CODE_SHIFTDOWN 0x7C00
|
|
#define CODE_CIRCLEC 0x1F00
|
|
#define CODE_CIRCLED 0x1600
|
|
#define CODE_RETURN 0x5B00
|
|
#define CODE_LINEFEED 0x3B00
|
|
#define CODE_ATTENTION 0x0001 /* pseudocode, never really returned as a received character */
|
|
#define CODE_UNKNOWN 0x0000
|
|
|
|
static t_stat t2741_svc (UNIT *uptr);
|
|
static t_stat t2741_reset (DEVICE *dptr);
|
|
static t_stat t2741_attach (UNIT *uptr, CONST char *cptr);
|
|
static t_stat t2741_detach (UNIT *uptr);
|
|
static uint16 ascii_to_t2741 (int ascii);
|
|
static const char * t2741_to_ascii (uint16 code);
|
|
static void set_transmit_notready (void);
|
|
|
|
static uint16 t2741_dsw = T2741_DSW_TRANSMIT_NOT_READY; /* device status word */
|
|
static uint32 t2741_swait = 200; /* character send wait */
|
|
static uint32 t2741_rwait = 2000; /* character receive wait */
|
|
static uint16 t2741_char = 0; /* last character received */
|
|
static int overrun = FALSE;
|
|
static uint32 t2741_socket = 1130;
|
|
|
|
UNIT t2741_unit[1] = {
|
|
{ UDATA (&t2741_svc, UNIT_ATTABLE, 0) },
|
|
};
|
|
|
|
REG t2741_reg[] = {
|
|
{ HRDATA (DSW, t2741_dsw, 16) }, /* device status word */
|
|
{ DRDATA (RTIME, t2741_rwait, 24), PV_LEFT }, /* character receive wait */
|
|
{ DRDATA (STIME, t2741_swait, 24), PV_LEFT }, /* character send wait */
|
|
{ DRDATA (SOCKET, t2741_socket,16), PV_LEFT }, /* socket number */
|
|
{ HRDATA (LASTCHAR, t2741_char, 16), PV_LEFT }, /* last character read */
|
|
{ NULL } };
|
|
|
|
DEVICE t2741_dev = {
|
|
"T2741", t2741_unit, t2741_reg, NULL,
|
|
1, 16, 16, 1, 16, 16,
|
|
NULL, NULL, t2741_reset,
|
|
NULL, t2741_attach, t2741_detach};
|
|
|
|
/* xio_t2741_terminal - XIO command interpreter for the terminal adapter */
|
|
|
|
void xio_t2741_terminal (int32 iocc_addr, int32 iocc_func, int32 iocc_mod)
|
|
{
|
|
char msg[80];
|
|
uint16 code;
|
|
|
|
switch (iocc_func) {
|
|
case XIO_READ: /* read: return last character read */
|
|
code = t2741_char & 0xFF00;
|
|
M[iocc_addr & mem_mask] = code;
|
|
overrun = FALSE;
|
|
#ifdef DEBUG_T2741
|
|
/* trace_both("T2741 %04x READ %02x %s", prev_IAR, code >> 8, t2741_to_ascii(code)); */
|
|
#endif
|
|
break;
|
|
|
|
case XIO_WRITE: /* write: initiate character send */
|
|
code = M[iocc_addr & mem_mask] & 0xFF00;
|
|
#ifdef DEBUG_T2741
|
|
trace_both("T2741 %04x SEND %02x %s", prev_IAR, code >> 8, t2741_to_ascii(code));
|
|
#endif
|
|
SETBIT(t2741_dsw, T2741_DSW_TRANSMIT_NOT_READY);
|
|
SETBIT(t2741_unit->flags, UNIT_SENDING);
|
|
|
|
if (code == CODE_SHIFTUP)
|
|
SETBIT(t2741_unit->flags, UNIT_UPCASE);
|
|
else if (code == CODE_SHIFTDOWN)
|
|
CLRBIT(t2741_unit->flags, UNIT_UPCASE);
|
|
|
|
sim_activate(t2741_unit, t2741_swait); /* schedule interrupt */
|
|
break;
|
|
|
|
case XIO_SENSE_DEV: /* sense device status */
|
|
ACC = t2741_dsw;
|
|
#ifdef DEBUG_T2741
|
|
/* trace_both("T2741 %04x SENS %04x%s", prev_IAR, t2741_dsw, (iocc_mod & 0x01) ? " reset" : ""); */
|
|
#endif
|
|
if (iocc_mod & 0x01) { /* reset interrupts */
|
|
CLRBIT(t2741_dsw, T2741_DSW_READ_RESPONSE);
|
|
CLRBIT(ILSW[4], ILSW_4_T2741_TERMINAL);
|
|
}
|
|
break;
|
|
|
|
case XIO_CONTROL: /* control: do something to interface */
|
|
#ifdef DEBUG_T2741
|
|
trace_both("T2741 %04x CTRL %04x", prev_IAR, iocc_mod &0xFF);
|
|
#endif
|
|
SETBIT(t2741_unit->flags, UNIT_RECEIVING); /* set mode to receive mode */
|
|
if (IS_ONLINE(t2741_unit) && (t2741_char != 0 || ! feof(t2741_unit->fileref))) {
|
|
sim_activate(t2741_unit, t2741_rwait);
|
|
t2741_char = (CODE_CIRCLED >> 8); /* first character received after turnaround is circled */
|
|
}
|
|
break;
|
|
|
|
default:
|
|
sprintf(msg, "Invalid T2741 XIO function %x", iocc_func);
|
|
xio_error(msg);
|
|
}
|
|
}
|
|
|
|
static void set_transmit_notready (void)
|
|
{
|
|
if (IS_ONLINE(t2741_unit) && ! (t2741_unit->flags & UNIT_SENDING))
|
|
CLRBIT(t2741_dsw, T2741_DSW_TRANSMIT_NOT_READY);
|
|
else
|
|
SETBIT(t2741_dsw, T2741_DSW_TRANSMIT_NOT_READY);
|
|
}
|
|
|
|
static t_stat t2741_svc (UNIT *uptr)
|
|
{
|
|
int ch = EOF;
|
|
uint16 code;
|
|
|
|
if (uptr->flags & UNIT_SENDING) { /* xmit: no interrupt, as far as I know. just clr busy bit */
|
|
CLRBIT(uptr->flags, UNIT_SENDING);
|
|
set_transmit_notready();
|
|
}
|
|
|
|
if (uptr->flags & UNIT_RECEIVING) { /* rcv: fire interrupt */
|
|
t2741_char <<= 8;
|
|
|
|
if (t2741_char == 0) { /* there is no 2nd character from previous ascii input */
|
|
if ((ch = getc(t2741_unit->fileref)) == EOF)
|
|
t2741_char = 0;
|
|
else {
|
|
if (ch == '\r') { /* if we get CR, jump to LF */
|
|
if ((ch = getc(t2741_unit->fileref)) != '\n') {
|
|
ungetc(ch, t2741_unit->fileref);
|
|
ch = '\r';
|
|
}
|
|
}
|
|
|
|
if (ch == '\027') {
|
|
t2741_char = CODE_LINEFEED; /* attention key sends line feed character */
|
|
#ifdef DEBUG_T2741
|
|
trace_both("T2741 ---- ATTENTION");
|
|
#endif
|
|
SETBIT(t2741_dsw, T2741_DSW_ATTENTION); /* no character returned ? */
|
|
}
|
|
else {
|
|
t2741_char = ascii_to_t2741(ch); /* translate to 2741 code(s) */
|
|
}
|
|
}
|
|
}
|
|
|
|
code = t2741_char & 0xFF00;
|
|
|
|
if (t2741_char != 0) {
|
|
if (overrun) /* previous character was not picked up! */
|
|
SETBIT(t2741_dsw, T2741_DSW_READ_OVERRUN);
|
|
|
|
SETBIT(t2741_dsw, T2741_DSW_READ_RESPONSE);
|
|
SETBIT(ILSW[4], ILSW_4_T2741_TERMINAL); /* issue interrupt */
|
|
calc_ints();
|
|
|
|
#ifdef DEBUG_T2741
|
|
trace_both("T2741 ---- RCVD %02x '%s' RDRESP%s%s", code >> 8, t2741_to_ascii(code),
|
|
(t2741_dsw & T2741_DSW_READ_OVERRUN) ? "|OVERRUN" : "",
|
|
(t2741_dsw & T2741_DSW_ATTENTION) ? "|ATTENTION" : "");
|
|
#endif
|
|
|
|
overrun = TRUE; /* arm overrun flag */
|
|
}
|
|
|
|
if (t2741_char == CODE_CIRCLEC) /* end of line (CIRCLEC after RETURN) auto downshifts */
|
|
CLRBIT(t2741_unit->flags, UNIT_UPCASE);
|
|
|
|
if (t2741_char == 0 || code == CODE_CIRCLEC)
|
|
CLRBIT(uptr->flags, UNIT_RECEIVING); /* on enter or EOF, stop typing */
|
|
else
|
|
sim_activate(t2741_unit, t2741_rwait); /* schedule next character to arrive */
|
|
}
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static t_stat t2741_attach (UNIT *uptr, CONST char *cptr)
|
|
{
|
|
int rval;
|
|
|
|
if ((rval = attach_unit(uptr, cptr)) == SCPE_OK) { /* use standard attach */
|
|
t2741_char = 0;
|
|
overrun = FALSE;
|
|
|
|
CLRBIT(t2741_unit->flags, UNIT_UPCASE);
|
|
|
|
if ((t2741_unit->flags & UNIT_RECEIVING) && ! feof(t2741_unit->fileref))
|
|
sim_activate(t2741_unit, t2741_rwait); /* schedule interrupt */
|
|
}
|
|
|
|
set_transmit_notready();
|
|
|
|
return rval;
|
|
}
|
|
|
|
static t_stat t2741_detach (UNIT *uptr)
|
|
{
|
|
t_stat rval;
|
|
|
|
if (t2741_unit->flags & UNIT_RECEIVING) /* if receive was pending, cancel interrupt */
|
|
sim_cancel(t2741_unit);
|
|
|
|
t2741_char = 0;
|
|
overrun = FALSE;
|
|
|
|
rval = detach_unit(uptr); /* use standard detach */
|
|
|
|
set_transmit_notready();
|
|
|
|
return rval;
|
|
}
|
|
|
|
static t_stat t2741_reset (DEVICE *dptr)
|
|
{
|
|
sim_cancel(t2741_unit);
|
|
|
|
CLRBIT(t2741_unit->flags, UNIT_SENDING|UNIT_RECEIVING|UNIT_UPCASE);
|
|
|
|
t2741_char = 0;
|
|
t2741_dsw = 0;
|
|
overrun = FALSE;
|
|
|
|
set_transmit_notready();
|
|
|
|
CLRBIT(ILSW[4], ILSW_4_T2741_TERMINAL);
|
|
calc_ints();
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
static struct tag_t2741_map {
|
|
int code;
|
|
int lcase, ucase;
|
|
t_bool shifts;
|
|
} t2741_map[] = {
|
|
{0x4F00, 'A', 'a', TRUE},
|
|
{0x3700, 'B', 'b', TRUE},
|
|
{0x2F00, 'C', 'c', TRUE},
|
|
{0x2A00, 'D', 'd', TRUE},
|
|
{0x2900, 'E', 'e', TRUE},
|
|
{0x6700, 'F', '_', TRUE},
|
|
{0x6200, 'G', 'g', TRUE},
|
|
{0x3200, 'H', 'h', TRUE},
|
|
{0x4C00, 'I', 'i', TRUE},
|
|
{0x6100, 'J', 'j', TRUE},
|
|
{0x2C00, 'K', '\'', TRUE},
|
|
{0x3100, 'L', 'l', TRUE},
|
|
{0x4300, 'M', '|', TRUE},
|
|
{0x2500, 'N', 'n', TRUE},
|
|
{0x5100, 'O', 'o', TRUE},
|
|
{0x6800, 'P', '*', TRUE},
|
|
{0x6D00, 'Q', '?', TRUE},
|
|
{0x4A00, 'R', 'r', TRUE},
|
|
{0x5200, 'S', 's', TRUE},
|
|
{0x2000, 'T', '~', TRUE},
|
|
{0x2600, 'U', 'u', TRUE},
|
|
{0x4600, 'V', 'v', TRUE},
|
|
{0x5700, 'W', 'w', TRUE},
|
|
{0x2300, 'X', 'x', TRUE},
|
|
{0x7300, 'Y', 'y', TRUE},
|
|
{0x1500, 'Z', 'z', TRUE},
|
|
{0x1300, '0', '&', TRUE},
|
|
{0x0200, '1', '?', TRUE},
|
|
{0x0400, '2', '?', TRUE},
|
|
{0x0700, '3', '<', TRUE},
|
|
{0x1000, '4', '?', TRUE},
|
|
{0x0800, '5', '=', TRUE},
|
|
{0x0D00, '6', '?', TRUE},
|
|
{0x0B00, '7', '>', TRUE},
|
|
{0x0E00, '8', '?', TRUE},
|
|
{0x1600, '9', '|', TRUE},
|
|
{0x7000, '/', '\\', TRUE},
|
|
{0x7600, '+', '-', TRUE},
|
|
{0x6400, '?', '?', TRUE},
|
|
{0x4000, '<', '>', TRUE},
|
|
{0x6B00, '[', '(', TRUE},
|
|
{0x4900, ']', ')', TRUE},
|
|
{0x6E00, ',', ';', TRUE},
|
|
{0x4500, '.', ':', TRUE},
|
|
{0x0100, ' ', 0, FALSE},
|
|
{0x5B00, '\r', 0, FALSE},
|
|
{0x3B00, '\n', 0, FALSE},
|
|
{0x5D00, '\b', 0, FALSE},
|
|
{0x5E00, '\t', 0, FALSE},
|
|
{0x0001, '\027', 0, FALSE},
|
|
};
|
|
|
|
static uint16 ascii_to_t2741 (int ascii)
|
|
{
|
|
int i;
|
|
uint16 rval = 0;
|
|
|
|
ascii &= 0xFF;
|
|
|
|
if (ascii == '\n') /* turn newlines into returns + CIRCLED? */
|
|
return CODE_RETURN | (CODE_CIRCLEC >> 8);
|
|
|
|
for (i = sizeof(t2741_map)/sizeof(t2741_map[0]); --i >= 0; ) {
|
|
if (t2741_map[i].shifts) {
|
|
if (t2741_map[i].lcase == ascii) {
|
|
rval = t2741_map[i].code;
|
|
if (t2741_unit->flags & UNIT_UPCASE) {
|
|
CLRBIT(t2741_unit->flags, UNIT_UPCASE);
|
|
rval = CODE_SHIFTDOWN | (rval >> 8);
|
|
}
|
|
return rval;
|
|
}
|
|
if (t2741_map[i].ucase == ascii) {
|
|
rval = t2741_map[i].code;
|
|
if (! (t2741_unit->flags & UNIT_UPCASE)) {
|
|
SETBIT(t2741_unit->flags, UNIT_UPCASE);
|
|
rval = CODE_SHIFTUP | (rval >> 8);
|
|
}
|
|
return rval;
|
|
}
|
|
}
|
|
else if (t2741_map[i].lcase == ascii)
|
|
return t2741_map[i].code;
|
|
}
|
|
|
|
return CODE_UNKNOWN;
|
|
}
|
|
|
|
static const char * t2741_to_ascii (uint16 code)
|
|
{
|
|
int i;
|
|
static char string[2] = {'?', '\0'};
|
|
|
|
switch (code) {
|
|
case CODE_SHIFTUP: return "SHIFTUP";
|
|
case CODE_SHIFTDOWN: return "SHIFTDN";
|
|
case CODE_CIRCLEC: return "CIRCLEC";
|
|
case CODE_CIRCLED: return "CIRCLED";
|
|
}
|
|
|
|
for (i = sizeof(t2741_map)/sizeof(t2741_map[0]); --i >= 0; ) {
|
|
if (t2741_map[i].code == code) {
|
|
if (t2741_map[i].shifts) {
|
|
string[0] = (t2741_unit->flags & UNIT_UPCASE) ? t2741_map[i].ucase : t2741_map[i].lcase;
|
|
return string;
|
|
}
|
|
switch (t2741_map[i].lcase) {
|
|
case ' ': return " ";
|
|
case '\r': return "RETURN";
|
|
case '\n': return "LINEFEED";
|
|
case '\b': return "BS";
|
|
case '\t': return "IDLE";
|
|
}
|
|
break;
|
|
}
|
|
}
|
|
|
|
return "?";
|
|
}
|