1321 lines
47 KiB
C
1321 lines
47 KiB
C
/* i7090_com.c: IBM 7094 7750 communications interface simulator
|
|
Derived from Bob Supnik's i7094_com.c
|
|
|
|
|
|
Copyright (c) 2005-2009, Robert M Supnik
|
|
Copyright (c) 2010-2016, Richard Cornwell
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a
|
|
copy of this software and associated documentation files (the "Software"),
|
|
to deal in the Software without restriction, including without limitation
|
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
and/or sell copies of the Software, and to permit persons to whom the
|
|
Software is furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
RICHARD CORNWELL BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of Robert Supnik or Richard Cornwell
|
|
shall not be used in advertising or otherwise to promote the sale, use or other
|
|
dealings in this Software without prior written authorization from Robert Supnik
|
|
or Richard Cornwell
|
|
|
|
com 7750 controller
|
|
coml 7750 lines
|
|
|
|
This module implements an abstract simulator for the IBM 7750 communications
|
|
computer as used by the CTSS system. The 7750 supports up to 112 lines;
|
|
the simulator supports 33. The 7750 can handle both high-speed lines, in
|
|
6b and 12b mode, and normal terminals, in 12b mode only; the simulator
|
|
supports only terminals. The 7750 can handle many different kinds of
|
|
terminals; the simulator supports only a limited subset.
|
|
|
|
Input is asynchronous. The 7750 sets ATN1 to signal availability of input.
|
|
When the 7094 issues a CTLRN, the 7750 gathers available input characters
|
|
into a message. The message has a 12b sequence number, followed by 12b line
|
|
number/character pairs, followed by end-of-medium (03777). Input characters
|
|
can either be control characters (bit 02000 set) or data characters. Data
|
|
characters are 1's complemented and are 8b wide: 7 data bits and 1 parity
|
|
bit (which may be 0).
|
|
|
|
Output is synchronous. When the 7094 issues a CTLWN, the 7750 interprets
|
|
the channel output as a message. The message has a 12b line number, followed
|
|
by a 12b character count, followed by characters, followed by end-of-medium.
|
|
If bit 02000 of the line number is set, the characters are 12b wide. If
|
|
bit 01000 is set, the message is a control message. 12b characters consist
|
|
of 7 data bits, 1 parity bit, and 1 start bit. Data characters are 1's
|
|
complemented. Data character 03777 is special and causes the 7750 to
|
|
repeat the previous bit for the number of bit times specified in the next
|
|
character. This is used to generate delays for positioning characters.
|
|
|
|
The 7750 supports flow control for output. To help the 7094 account for
|
|
usage of 7750 buffer memory, the 7750 sends 'character output completion'
|
|
messages for every 'n' characters output on a line, where n <= 31.
|
|
|
|
Note that the simulator console is mapped in as line n+1.
|
|
|
|
Sense word based on 7074 Principles of Operation.
|
|
|
|
1 A Reserved.
|
|
3 4 Program Check Summary byte
|
|
4 2 Exceptional Condtion Summary byte
|
|
5 1 Data Check Summary byte
|
|
7 A Reserved
|
|
9 4 Message Length Check Program Check
|
|
10 2 Channel Hold Program Check
|
|
11 1 Channel Queue Full Program Check
|
|
13 A Reserved
|
|
15 4 Reserved
|
|
16 2 Reserved
|
|
17 1 Interface Timeout Data Check
|
|
19 A Reserved
|
|
21 4 Data Message Ready Exceptional Condition
|
|
22 2 Input space available Exceptional Condition
|
|
23 1 Service Message Ready Exceptional Condition
|
|
|
|
*/
|
|
|
|
#include "i7000_defs.h"
|
|
#include "sim_timer.h"
|
|
#include "sim_sock.h"
|
|
#include "sim_tmxr.h"
|
|
#include <ctype.h>
|
|
|
|
#ifdef NUM_DEVS_COM
|
|
#define COM_MLINES 32 /* mux lines */
|
|
#define COM_TLINES (COM_MLINES)
|
|
#define COM_BUFSIZ 120 /* max chan transfer */
|
|
#define COM_PKTSIZ 16384 /* character buffer */
|
|
|
|
#define UNIT_V_2741 (UNIT_V_UF + 0) /* 2741 - ni */
|
|
#define UNIT_V_K35 (UNIT_V_UF + 1) /* KSR-35 */
|
|
#define UNIT_2741 (1 << UNIT_V_2741)
|
|
#define UNIT_K35 (1 << UNIT_V_K35)
|
|
|
|
#define TMR_COM 2
|
|
|
|
#define CONN u3 /* line is connected */
|
|
#define NEEDID u4 /* need to send ID */
|
|
#define ECHO u5 /* echoing output */
|
|
|
|
#define COM_INIT_POLL 8000 /* polling interval */
|
|
#define COMC_WAIT 2 /* channel delay time */
|
|
#define COML_WAIT 500 /* char delay time */
|
|
#define COM_LBASE 4 /* start of lines */
|
|
|
|
/* Input threads */
|
|
|
|
#define COM_PLU 0 /* multiplexor poll */
|
|
#define COM_CIU 1 /* console input */
|
|
#define COM_CHU 2 /* console output */
|
|
|
|
/* Communications input */
|
|
|
|
#define COMI_LCOMP 002000 /* line complete */
|
|
#define COMI_DIALUP 002001 /* dialup */
|
|
#define COMI_ENDID 002002 /* end ID */
|
|
#define COMI_INTR 002003 /* interrupt */
|
|
#define COMI_QUIT 002004 /* quit */
|
|
#define COMI_HANGUP 002005 /* hangup */
|
|
#define COMI_EOM 013777 /* end of medium */
|
|
#define COMI_COMP(x) ((uint16) (03000 + ((x) & COMI_CMAX)))
|
|
#define COMI_K35 6 /* KSR-35 ID */
|
|
#define COMI_K37 7 /* KSR-37 ID */
|
|
#define COMI_2741 8 /* 2741 ID */
|
|
#define COMI_CMAX 31 /* max chars returned */
|
|
#define COMI_PARITY 00200 /* parity bit */
|
|
#define COMI_BMAX 50 /* buffer max, words */
|
|
#define COMI_12BMAX ((3 * COMI_BMAX) - 1) /* last 12b char */
|
|
|
|
/* Communications output - characters */
|
|
|
|
#define COMO_LIN12B 02000 /* line is 12b */
|
|
#define COMO_LINCTL 01000 /* control msg */
|
|
#define COMO_GETLN(x) ((x) & 0777)
|
|
#define COMO_CTLRST 07777 /* control reset */
|
|
#define COMO_BITRPT 03777 /* bit repeat */
|
|
#define COMO_EOM12B 07777 /* end of medium */
|
|
#define COMO_EOM6B 077 /* end of medium */
|
|
#define COMO_BMAX 94 /* buffer max, words */
|
|
#define COMO_12BMAX ((3 * COMO_BMAX) - 1)
|
|
|
|
/* Report variables */
|
|
|
|
#define COMR_FQ 1 /* free queue */
|
|
#define COMR_IQ 2 /* input queue */
|
|
#define COMR_OQ 4 /* output queue */
|
|
|
|
/* Sense word flags */
|
|
#define EXPT_SRVRDY 0x1001 /* Service message available */
|
|
#define EXPT_INAVAIL 0x1002 /* Input available */
|
|
#define EXPT_DATRDY 0x1004 /* Data ready. */
|
|
#define DATA_TIMEOUT 0x2010 /* Timeout */
|
|
#define PROG_FULL 0x4100 /* No more space to send message */
|
|
#define PROG_HOLD 0x4200 /* Channel hold */
|
|
#define PROG_MSGLEN 0x4400 /* Invalid message length */
|
|
|
|
/* Input and output ring buffers */
|
|
uint16 in_buff[256];
|
|
int in_head;
|
|
int in_tail;
|
|
int in_count; /* Number of entries in queue */
|
|
int in_delay = 5000;
|
|
|
|
|
|
typedef struct
|
|
{
|
|
uint16 link;
|
|
uint16 data;
|
|
} OLIST;
|
|
|
|
uint32 com_posti = 0; /* Posted a IRQ */
|
|
uint32 com_active = 0; /* Channel active */
|
|
uint32 com_ocnt = 0; /* Number of characters to output */
|
|
uint32 com_oln = 0; /* Output line number */
|
|
uint32 com_o12b = 0; /* Outputing 12 bit */
|
|
uint32 com_enab = 0; /* 7750 enabled */
|
|
uint32 com_msgn = 0; /* next input msg num */
|
|
uint32 com_sta = 0; /* 7750 state */
|
|
uint32 com_quit = 3; /* quit code */
|
|
uint32 com_intr = 4; /* interrupt code */
|
|
uint32 com_tps = 50; /* polls/second */
|
|
uint8 com_out_inesc[COM_TLINES];
|
|
uint16 com_out_head[COM_TLINES];
|
|
uint16 com_out_tail[COM_TLINES];
|
|
uint16 com_comp_cnt[COM_TLINES];
|
|
int com_line; /* Current line */
|
|
uint16 com_free; /* free list */
|
|
OLIST com_buf[10240];
|
|
TMLN com_ldsc[COM_TLINES]; /* line descriptors */
|
|
TMXR com_desc = { COM_TLINES, 0, 0, com_ldsc }; /* mux descriptor */
|
|
uint32 com_sense = 0; /* Sense word */
|
|
uint16 com_data;
|
|
uint8 com_dflg = 0;
|
|
|
|
|
|
/* 2741 convertion table */
|
|
static const uint8 com_2741_out[256] = {
|
|
/* Upper case */
|
|
/* 0 1 2 3 4 5 6 7 */
|
|
' ', '-', '2', '+', '*', 'Q', 'Y', 'H', /* 000 */
|
|
':', 'M', 'U', 'D', '_', '_', '_', '_', /* 010 */
|
|
'@', 'K', 'S', 'B', ')', '_', '_', '_', /* 020 */
|
|
'\'', 'O', 'W', 'F','\n','\b', ' ', '_', /* 030 */
|
|
'=', 'J', '?', 'A', '(', 'R', 'Z', 'I', /* 040 */
|
|
'%', 'N', 'V', 'E', '_','\n','\r', '\t', /* 050 */
|
|
';', 'L', 'T', 'C', '#', '$', ',', '.', /* 060 */
|
|
'"', 'P', 'X', 'G', '_','\t', '<', '\0', /* 070 */
|
|
' ', '-', '@', '&', '8', 'q', 'y', 'h', /* 100 */
|
|
'4', 'm', 'u', 'd', '_', '_', '_', '_', /* 110 */
|
|
'2', 'k', 's', 'b', '0', '_', '_', '_', /* 120 */
|
|
'6', 'o', 'w', 'f', '_','\b', ' ', '_', /* 130 */
|
|
'1', 'j', '/', 'a', '9', 'r', 'z', 'i', /* 140 */
|
|
'5', 'n', 'v', 'e','\n','\n','\r', '\t', /* 150 */
|
|
'3', 'l', 't', 'c', '_', '!', ',', '.', /* 160 */
|
|
'7', 'p', 'x', 'g', '_','\t', '_','\0', /* 170 */
|
|
};
|
|
|
|
/* 76 43 177 15 */
|
|
/* 76 23 177 16 */
|
|
static const uint8 com_2741_in[128] = {
|
|
/* Control */
|
|
0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000, /*0-37*/
|
|
/*Control*/
|
|
0135, 0057, 0155, 0000, 0000, 0155, 0000, 0000,
|
|
/*Control*/
|
|
0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,
|
|
/*Control*/
|
|
0000, 0000, 0000, 0000, 0000, 0000, 0000, 0000,
|
|
/* sp ! " # $ % & ' */
|
|
0100, 0365, 0070, 0264, 0165, 0150, 0303, 0130, /* 40 - 77 */
|
|
/* ( ) * + , - . / */
|
|
0144, 0124, 0004, 0203, 0166, 0001, 0067, 0342,
|
|
/* 0 1 2 3 4 5 6 7 */
|
|
0324, 0240, 0220, 0360, 0210, 0350, 0330, 0270,
|
|
/* 8 9 : ; < = > ? */
|
|
0204, 0344, 0010, 0160, 0000, 0040, 0000, 0142,
|
|
/* @ A B C D E F G */
|
|
0202, 0043, 0023, 0163, 0013, 0153, 0133, 0073, /* 100 - 137 */
|
|
/* H I J K L M N O */
|
|
0007, 0147, 0141, 0121, 0061, 0111, 0051, 0031,
|
|
/* P Q R S T U V W */
|
|
0171, 0105, 0045, 0122, 0062, 0112, 0052, 0032,
|
|
/* X Y Z [ \ ] ^ _ */
|
|
0172, 0106, 0046, 0000, 0000, 0000, 0000, 0000,
|
|
/* ` a b c d e f g */
|
|
0000, 0243, 0223, 0363, 0213, 0353, 0333, 0273, /* 140 - 177 */
|
|
/* h i j k l m n o */
|
|
0207, 0347, 0341, 0321, 0261, 0311, 0251, 0231,
|
|
/* p q r s t u v w */
|
|
0371, 0305, 0245, 0322, 0262, 0312, 0252, 0232,
|
|
/* x y z { | } ~ del*/
|
|
0372, 0306, 0246, 0000, 0000, 0000, 0000, 0177
|
|
};
|
|
|
|
|
|
|
|
uint32 com_cmd(UNIT * uptr, uint16 cmd, uint16 dev);
|
|
t_stat com_svc(UNIT * uptr);
|
|
t_stat comi_svc(UNIT * uptr);
|
|
t_stat como_svc(UNIT * uptr);
|
|
t_stat comti_svc(UNIT * uptr);
|
|
t_stat comto_svc(UNIT * uptr);
|
|
t_stat com_reset(DEVICE * dptr);
|
|
t_stat com_attach(UNIT * uptr, CONST char *cptr);
|
|
t_stat com_detach(UNIT * uptr);
|
|
t_stat com_summ(FILE * st, UNIT * uptr, int32 val, CONST void *desc);
|
|
t_stat com_show(FILE * st, UNIT * uptr, int32 val, CONST void *desc);
|
|
void com_reset_ln(uint32 i);
|
|
t_stat com_queue_in(uint32 ln, uint16 ch);
|
|
uint32 com_queue_out(uint32 ln, uint16 * c1);
|
|
t_stat com_send_id(uint32 ln);
|
|
t_stat com_send_ccmp(uint32 ln);
|
|
void com_skip_outc(uint32 ln);
|
|
t_bool com_get(int ln, uint16 *ch);
|
|
t_bool com_put(int ln, uint16 ch);
|
|
void com_post_eom();
|
|
t_bool com_inp_msg(uint32 ln, uint16 msg);
|
|
const char *com_description(DEVICE *dptr);
|
|
t_stat com_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
|
|
const char *coml_description(DEVICE *dptr);
|
|
t_stat coml_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr);
|
|
|
|
|
|
/* COM data structures
|
|
|
|
com_dev COM device descriptor
|
|
com_unit COM unit descriptor
|
|
com_reg COM register list
|
|
com_mod COM modifiers list
|
|
*/
|
|
#ifdef I7010
|
|
#define COM_CHAN 4
|
|
#else
|
|
#define COM_CHAN 5
|
|
#endif
|
|
|
|
UNIT com_unit[] = {
|
|
{UDATA(&comi_svc, UNIT_S_CHAN(COM_CHAN) | UNIT_ATTABLE, 0), COM_INIT_POLL},
|
|
{UDATA(&comti_svc, UNIT_S_CHAN(COM_CHAN) | UNIT_DIS, 0), KBD_POLL_WAIT},
|
|
{UDATA(&com_svc, UNIT_S_CHAN(COM_CHAN) | UNIT_DIS, 0), COMC_WAIT},
|
|
};
|
|
|
|
REG com_reg[] = {
|
|
{FLDATA(ENABLE, com_enab, 0)},
|
|
{ORDATA(STATE, com_sta, 6)},
|
|
{ORDATA(MSGNUM, com_msgn, 12)},
|
|
{NULL}
|
|
};
|
|
|
|
MTAB com_mod[] = {
|
|
{MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "CHAN", "CHAN",
|
|
&set_chan, &get_chan, NULL, "Set channel"},
|
|
#ifndef I7010
|
|
{MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "SELECT", "SELECT",
|
|
&chan9_set_select, &chan9_get_select, NULL, "Set selection channel"},
|
|
#endif
|
|
{UNIT_ATT, UNIT_ATT, "connections", NULL, NULL, &com_summ},
|
|
{MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL,
|
|
NULL, &com_show, NULL},
|
|
{MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL,
|
|
NULL, &com_show, NULL},
|
|
{0}
|
|
};
|
|
|
|
DEVICE com_dev = {
|
|
"COM", com_unit, com_reg, com_mod,
|
|
3, 10, 31, 1, 16, 8,
|
|
&tmxr_ex, &tmxr_dep, &com_reset,
|
|
NULL, &com_attach, &com_detach,
|
|
&com_dib, DEV_DISABLE| DEV_DEBUG|DEV_NET, 0, dev_debug,
|
|
NULL, NULL, &com_help, NULL, NULL, &com_description
|
|
};
|
|
|
|
/* COMLL data structures
|
|
|
|
coml_dev COML device descriptor
|
|
coml_unit COML unit descriptor
|
|
coml_reg COML register list
|
|
coml_mod COML modifiers list
|
|
*/
|
|
|
|
UNIT coml_unit[] = {
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 0 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 1 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 2 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 3 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 4 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 5 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 6 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 7 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 8 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 9 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 0 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 1 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 2 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 3 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 4 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 5 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 6 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 7 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 8 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 9 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 0 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 1 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 2 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 3 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 4 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 5 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 6 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 7 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 8 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 9 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 0 */
|
|
{UDATA(&como_svc, 0, 0), COML_WAIT}, /* 1 */
|
|
};
|
|
|
|
MTAB coml_mod[] = {
|
|
{UNIT_K35 + UNIT_2741, 0, "KSR-37", "KSR-37", NULL, NULL, NULL,
|
|
"Standard KSR"},
|
|
{UNIT_K35 + UNIT_2741, UNIT_K35, "KSR-35", "KSR-35", NULL, NULL, NULL,
|
|
"Upper case only KSR"},
|
|
{UNIT_K35 + UNIT_2741, UNIT_2741, "2741", "2741", NULL, NULL, NULL,
|
|
"IBM 2741 terminal"},
|
|
{MTAB_XTD | MTAB_VUN, 0, NULL, "DISCONNECT",
|
|
&tmxr_dscln, NULL, &com_desc, "Disconnect line"},
|
|
{MTAB_XTD | MTAB_VUN | MTAB_NC, 0, "LOG", "LOG",
|
|
&tmxr_set_log, &tmxr_show_log, &com_desc},
|
|
{MTAB_XTD | MTAB_VUN | MTAB_NC, 0, NULL, "NOLOG",
|
|
&tmxr_set_nolog, NULL, &com_desc},
|
|
{0}
|
|
};
|
|
|
|
REG coml_reg[] = {
|
|
{URDATA(TIME, coml_unit[0].wait, 16, 24, 0,
|
|
COM_TLINES, REG_NZ + PV_LEFT)},
|
|
{NULL}
|
|
};
|
|
|
|
DEVICE coml_dev = {
|
|
"COML", coml_unit, coml_reg, coml_mod,
|
|
COM_TLINES, 10, 31, 1, 16, 8,
|
|
NULL, NULL, &com_reset, NULL, NULL, NULL,
|
|
NULL, DEV_DISABLE, 0, NULL, NULL,
|
|
NULL, &coml_help, NULL, NULL, &coml_description
|
|
};
|
|
|
|
/* COM: channel select */
|
|
uint32 com_cmd(UNIT * uptr, uint16 cmd, uint16 dev)
|
|
{
|
|
/* Activate the com device */
|
|
sim_activate(&com_unit[COM_CHU], 10);
|
|
#if 0 /* Commented out until I can detect hangup signal */
|
|
if (!sim_is_active(&com_unit[COM_CIU])) /* console */
|
|
sim_activate(&com_unit[COM_CIU], com_unit[COM_CIU].wait);
|
|
if (!sim_is_active(&com_unit[COM_PLU])) {
|
|
if (com_unit[COM_PLU].flags & UNIT_ATT) { /* master att? */
|
|
int32 t =
|
|
sim_rtcn_init(com_unit[COM_PLU].wait, TMR_COM);
|
|
sim_activate(&com_unit[COM_PLU], t);
|
|
}
|
|
}
|
|
#endif
|
|
com_sta = 1;
|
|
com_dflg = 0;
|
|
com_active = 1;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Unit service - channel program */
|
|
t_stat com_svc(UNIT * uptr)
|
|
{
|
|
int chan = UNIT_G_CHAN(uptr->flags);
|
|
int sel = (uptr->flags & UNIT_SELECT) ? 1 : 0;
|
|
uint8 ch;
|
|
|
|
if (sel != chan_test(chan, CTL_SEL))
|
|
return SCPE_OK;
|
|
|
|
/* Handle disconnect */
|
|
if (com_sta != 0 && chan_stat(chan, DEV_DISCO)) {
|
|
chan_clear(chan, DEV_WEOR|DEV_SEL);
|
|
com_sta = 0;
|
|
com_active = 0;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
if (chan_test(chan, CTL_SNS)) {
|
|
int eor = (com_sta == 4)?DEV_REOR:0;
|
|
|
|
ch = (com_sense >> ((4 - com_sta) * 4)) & 0xf;
|
|
if (ch & 010) /* Move A bit over one */
|
|
ch ^= 030;
|
|
sim_debug(DEBUG_SNS, &com_dev, "sense unit=%02x\n", ch);
|
|
switch(chan_write_char(chan, &ch, eor)) {
|
|
case TIME_ERROR:
|
|
case END_RECORD:
|
|
com_sta = -1;
|
|
com_sense = 0;
|
|
break;
|
|
case DATA_OK:
|
|
com_sta++;
|
|
}
|
|
sim_activate(uptr, 50);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Start a command, only do read/write */
|
|
if (chan_test(chan, CTL_CNTL)) {
|
|
chan_clear(chan, DEV_FULL);
|
|
chan_set(chan, DEV_REOR|DEV_SEL);
|
|
sim_activate(uptr, 50);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Send down next buffer word */
|
|
if (chan_test(chan, CTL_READ)) {
|
|
/* Send low order character if one */
|
|
if (com_dflg) {
|
|
ch = com_data & 0377;
|
|
sim_debug(DEBUG_DATA, &com_dev, "sent=%02o\n", ch);
|
|
switch (chan_write_char(chan, &ch, (com_sta == 3)?DEV_REOR:0)) {
|
|
case DATA_OK:
|
|
case END_RECORD:
|
|
com_dflg = 0;
|
|
break;
|
|
case TIME_ERROR:
|
|
com_sense |= DATA_TIMEOUT;
|
|
}
|
|
sim_activate(uptr, 50);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
switch (com_sta) {
|
|
case 1:
|
|
com_data = com_msgn; /* 1st char is msg num */
|
|
com_msgn = (com_msgn + 1) & 03777; /* incr msg num */
|
|
com_sta++;
|
|
com_posti = 0;
|
|
chan9_clear_error(chan, sel);
|
|
break;
|
|
case 2:
|
|
/* Check if queue empty. */
|
|
if (in_head == in_tail) {
|
|
com_data = COMI_EOM;
|
|
com_sta++;
|
|
} else {
|
|
/* Grab next entry. */
|
|
in_head++;
|
|
/* Wrap around end of ring */
|
|
if (in_head >= (sizeof(in_buff)/sizeof(uint16)))
|
|
in_head = 0;
|
|
com_data = in_buff[in_head];
|
|
/* Check if end of current transfer */
|
|
if (com_data == COMI_EOM)
|
|
com_sta++;
|
|
in_count--;
|
|
}
|
|
break;
|
|
case 3:
|
|
chan_set(chan, DEV_REOR|CTL_END);
|
|
sim_activate(uptr, 50);
|
|
com_posti = 0;
|
|
com_sta++;
|
|
return SCPE_OK; /* q empty, done */
|
|
}
|
|
sim_debug(DEBUG_DATA, &com_dev, "send data=%04o\n", com_data);
|
|
ch = (com_data >> 6) & 077;
|
|
com_data &= 077;
|
|
switch (chan_write_char(chan, &ch, 0)) {
|
|
case DATA_OK:
|
|
case END_RECORD:
|
|
com_dflg = 1;
|
|
break;
|
|
case TIME_ERROR:
|
|
com_sense |= DATA_TIMEOUT;
|
|
}
|
|
sim_activate(uptr, 50);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
if (chan_test(chan, CTL_WRITE)) {
|
|
uint32 ln;
|
|
|
|
/* Read in two characters */
|
|
if (com_dflg == 0) {
|
|
switch(chan_read_char(chan, &ch, 0)) {
|
|
case DATA_OK:
|
|
com_dflg = 1;
|
|
com_data = (ch & 077) << 6;
|
|
break;
|
|
case END_RECORD:
|
|
case TIME_ERROR:
|
|
com_sense |= DATA_TIMEOUT;
|
|
}
|
|
sim_activate(uptr, 50);
|
|
return SCPE_OK;
|
|
} else {
|
|
switch(chan_read_char(chan, &ch, 0)) {
|
|
case DATA_OK:
|
|
com_dflg = 0;
|
|
com_data |= (ch & 077);
|
|
break;
|
|
case END_RECORD:
|
|
case TIME_ERROR:
|
|
com_sense |= DATA_TIMEOUT;
|
|
sim_activate(uptr, 50);
|
|
return SCPE_OK;
|
|
}
|
|
}
|
|
sim_debug(DEBUG_DATA, &com_dev, "recieved=%04o\n", com_data);
|
|
switch (com_sta) {
|
|
case 1:
|
|
com_oln = com_data;
|
|
if (com_data == 07777) { /* turn on? */
|
|
sim_debug(DEBUG_DETAIL, &com_dev, "enable\n");
|
|
com_enab = 1; /* enable 7750 */
|
|
in_delay = 200;
|
|
com_msgn = 0; /* init message # */
|
|
com_sta = 4;
|
|
chan_set(chan, DEV_REOR|CTL_END);
|
|
} else if (com_data & COMO_LINCTL) { /* control message? */
|
|
ln = COMO_GETLN(com_data); /* line number */
|
|
sim_debug(DEBUG_DETAIL, &com_dev, "line %d\n", ln);
|
|
if (ln >= (COM_TLINES + COM_LBASE)) /* invalid line? */
|
|
return STOP_INVLIN;
|
|
if (ln > COM_LBASE) /* valid line? */
|
|
com_reset_ln(ln - COM_LBASE);
|
|
com_sta = 4;
|
|
chan_set(chan, DEV_REOR|CTL_END);
|
|
} else /* data message */
|
|
com_sta++;
|
|
break;
|
|
case 2:
|
|
com_ocnt = (com_data & 07777) + 1; /* char count plus EOM */
|
|
if (com_oln & COMO_LIN12B) {
|
|
com_ocnt = com_ocnt << 1; /* 12b double */
|
|
com_o12b = 1;
|
|
} else
|
|
com_o12b = 0;
|
|
com_oln = COMO_GETLN(com_oln); /* line number */
|
|
sim_debug(DEBUG_DETAIL, &com_dev, "output line %d\n", com_oln);
|
|
com_sta++; /* next state */
|
|
break;
|
|
case 3: /* other words */
|
|
ln = com_oln; /* effective line */
|
|
/* unpack chars */
|
|
if (com_o12b) {
|
|
com_ocnt -= 2;
|
|
if (com_data == COMO_EOM12B) {
|
|
com_sta++;
|
|
if (com_ocnt != 0) {
|
|
chan9_set_error(chan, SNS_UEND);
|
|
com_sense |= PROG_MSGLEN;
|
|
}
|
|
chan_set(chan, DEV_REOR|CTL_END); /* end, last state */
|
|
break; /* EOM? */
|
|
}
|
|
} else {
|
|
com_ocnt--;
|
|
if (((com_data >> 6) & 077) == COMO_EOM6B) {
|
|
com_sta++;
|
|
if (com_ocnt != 0) {
|
|
sim_debug(DEBUG_EXP, &com_dev, "messge length error %d\n", com_ocnt);
|
|
chan9_set_error(chan, SNS_UEND);
|
|
com_sense |= PROG_MSGLEN;
|
|
}
|
|
chan_set(chan, DEV_REOR|CTL_END); /* end, last state */
|
|
break; /* EOM? */
|
|
}
|
|
sim_debug(DEBUG_DETAIL, &com_dev, "queing %o %d\n", (com_data >> 6) & 077, com_ocnt);
|
|
if (com_put(ln, (com_data >> 6) & 077)) {
|
|
sim_debug(DEBUG_EXP, &com_dev, "Insert error\n");
|
|
chan9_set_error(chan, SNS_UEND);
|
|
com_sense |= PROG_FULL;
|
|
}
|
|
com_ocnt--;
|
|
com_data &= 077;
|
|
if (com_data == COMO_EOM6B) {
|
|
com_sta++;
|
|
if (com_ocnt != 0) {
|
|
sim_debug(DEBUG_EXP, &com_dev, "messge length error %d\n", com_ocnt);
|
|
chan9_set_error(chan, SNS_UEND);
|
|
com_sense |= PROG_MSGLEN;
|
|
}
|
|
chan_set(chan, DEV_REOR|CTL_END); /* end, last state */
|
|
break; /* EOM? */
|
|
}
|
|
}
|
|
sim_debug(DEBUG_DETAIL, &com_dev, "queing %o %d\n", com_data, com_ocnt);
|
|
if (com_put(ln, com_data)) {
|
|
sim_debug(DEBUG_EXP, &com_dev, "Insert error\n");
|
|
chan9_set_error(chan, SNS_UEND);
|
|
com_sense |= PROG_FULL;
|
|
}
|
|
break;
|
|
}
|
|
sim_activate(uptr, 50);
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Unit service - console receive - always running, even if device is not */
|
|
|
|
t_stat
|
|
comti_svc(UNIT * uptr)
|
|
{
|
|
int32 c;
|
|
t_stat r;
|
|
|
|
sim_activate(uptr, uptr->wait); /* continue poll */
|
|
c = sim_poll_kbd(); /* get character */
|
|
if (c && (c < SCPE_KFLAG))
|
|
return c; /* error? */
|
|
if (((com_unit[COM_PLU].flags & UNIT_ATT) == 0) || /* not att, not enab, */
|
|
!com_enab || (c & SCPE_BREAK))
|
|
return SCPE_OK; /* break? done */
|
|
c = c & 0177;
|
|
if (c) {
|
|
r = com_queue_in(0, c);
|
|
if (r != SCPE_OK)
|
|
return r;
|
|
sim_putchar(c);
|
|
if (c == '\r')
|
|
sim_putchar('\n');
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Unit service - receive side
|
|
|
|
Poll all active lines for input
|
|
Poll for new connections */
|
|
|
|
t_stat
|
|
comi_svc(UNIT * uptr)
|
|
{
|
|
int32 c, ln, t;
|
|
t_stat r;
|
|
|
|
if ((uptr->flags & UNIT_ATT) == 0)
|
|
return SCPE_OK; /* attached? */
|
|
if (in_delay-- <= 0) { /* Check for any inputs to send over. */
|
|
in_delay = 50; /* Time to wait for polling again. */
|
|
if (!com_active && in_count > 0)
|
|
com_post_eom();
|
|
}
|
|
t = sim_rtcn_calb(com_tps, TMR_COM); /* calibrate */
|
|
sim_activate(uptr, t); /* continue poll */
|
|
ln = tmxr_poll_conn(&com_desc); /* look for connect */
|
|
if (ln >= 0) { /* got one? */
|
|
com_ldsc[ln].rcve = 1; /* rcv enabled */
|
|
coml_unit[ln].CONN = 1; /* flag connected */
|
|
coml_unit[ln].NEEDID = 1; /* need ID */
|
|
coml_unit[ln].ECHO = 1; /* echoing output */
|
|
}
|
|
if (!com_enab)
|
|
return SCPE_OK; /* not enabled? exit */
|
|
tmxr_poll_rx(&com_desc); /* poll for input */
|
|
for (ln = 0; ln < COM_TLINES; ln++) { /* loop thru mux */
|
|
if (com_ldsc[ln].conn) { /* connected? */
|
|
if (coml_unit[ln].NEEDID)
|
|
com_send_id(ln);
|
|
c = tmxr_getc_ln(&com_ldsc[ln]); /* get char */
|
|
if (c) { /* any char? */
|
|
c = c & 0177; /* mask to 7b */
|
|
r = com_queue_in(ln, c);
|
|
if (r != SCPE_OK)
|
|
return r; /* queue char, err? */
|
|
if (coml_unit[ln].ECHO && com_ldsc[ln].xmte) { /* output enabled? */
|
|
if (coml_unit[ln].flags & UNIT_K35) { /* KSR-35? */
|
|
if (islower(c))
|
|
c = toupper(c); /* convert LC to UC */
|
|
}
|
|
tmxr_putc_ln(&com_ldsc[ln], c); /* echo char */
|
|
if (c == '\r') /* add LF after CR */
|
|
tmxr_putc_ln(&com_ldsc[ln], '\n');
|
|
} /* end if enabled */
|
|
} /* end if char */
|
|
} /* end if conn */
|
|
else if (coml_unit[ln].CONN) { /* not conn, was conn? */
|
|
coml_unit[ln].CONN = 0; /* clear connected */
|
|
coml_unit[ln].NEEDID = 0; /* clear need id */
|
|
if (com_inp_msg(ln, COMI_HANGUP)) /* hangup message */
|
|
return STOP_NOIFREE;
|
|
}
|
|
} /* end for */
|
|
tmxr_poll_tx(&com_desc); /* poll xmt */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Unit service - console transmit */
|
|
|
|
t_stat
|
|
comto_svc(UNIT * uptr)
|
|
{
|
|
uint16 c, c1;
|
|
|
|
if (com_out_head[0] == 0)
|
|
return com_send_ccmp(0); /* Send out a completion code */
|
|
c = com_queue_out(0, &c1); /* get character, cvt */
|
|
if (c)
|
|
sim_putchar(c); /* printable? output */
|
|
if (c1)
|
|
sim_putchar(c1); /* second char? output */
|
|
sim_activate(uptr, uptr->wait); /* next char */
|
|
if (com_comp_cnt[0] >= COMI_CMAX) /* completion needed? */
|
|
return com_send_ccmp(0); /* generate msg */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Unit service - transmit side */
|
|
|
|
t_stat
|
|
como_svc(UNIT * uptr)
|
|
{
|
|
uint16 c, c1;
|
|
int32 ln = uptr - coml_unit; /* line # */
|
|
|
|
if (com_out_head[ln] == 0) /* no more characters? */
|
|
return com_send_ccmp(ln); /* free any remaining */
|
|
if (com_ldsc[ln].conn) { /* connected? */
|
|
if (com_ldsc[ln].xmte) { /* output enabled? */
|
|
c = com_queue_out(ln, &c1); /* get character, cvt */
|
|
if (c)
|
|
tmxr_putc_ln(&com_ldsc[ln], c); /* printable? output */
|
|
if (c1)
|
|
tmxr_putc_ln(&com_ldsc[ln], c1); /* print second */
|
|
} /* end if */
|
|
tmxr_poll_tx(&com_desc); /* poll xmt */
|
|
sim_activate(uptr, uptr->wait); /* next char */
|
|
if (com_comp_cnt[ln] >= COMI_CMAX) /* completion needed? */
|
|
return com_send_ccmp(ln); /* generate msg */
|
|
} /* end if conn */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Send ID sequence on input */
|
|
|
|
t_stat
|
|
com_send_id(uint32 ln)
|
|
{
|
|
com_inp_msg(ln, COMI_DIALUP); /* input message: */
|
|
if (coml_unit[ln].flags & UNIT_2741) /* dialup, ID, endID */
|
|
com_inp_msg(ln, COMI_2741);
|
|
else if (coml_unit[ln].flags & UNIT_K35)
|
|
com_inp_msg(ln, COMI_K35);
|
|
else
|
|
com_inp_msg(ln, COMI_K37);
|
|
com_inp_msg(ln, 0);
|
|
com_inp_msg(ln, 0);
|
|
com_inp_msg(ln, 0);
|
|
com_queue_in(ln, 'T');
|
|
com_queue_in(ln, '0' + ((ln+1) / 10));
|
|
com_queue_in(ln, '0' + ((ln+1) % 10));
|
|
if (com_inp_msg(ln, COMI_ENDID)) /* make sure there */
|
|
return STOP_NOIFREE; /* was room for msg */
|
|
coml_unit[ln].NEEDID = 0;
|
|
com_sense |= EXPT_SRVRDY;
|
|
return SCPE_OK;
|
|
}
|
|
/* Translate and queue input character */
|
|
|
|
t_stat
|
|
com_queue_in(uint32 ln, uint16 c)
|
|
{
|
|
uint16 out;
|
|
uint16 parity;
|
|
|
|
if (c == com_intr)
|
|
out = COMI_INTR;
|
|
else if (c == com_quit)
|
|
out = COMI_QUIT;
|
|
else {
|
|
if (coml_unit[ln].flags & UNIT_K35) { /* KSR-35? */
|
|
if (islower(c))
|
|
c = toupper(c); /* convert LC to UC */
|
|
}
|
|
if ((coml_unit[ln].flags & UNIT_K35) == 0) { /* KSR-37 or 2741 */
|
|
if (c == '\r')
|
|
c = '\n';
|
|
}
|
|
if (coml_unit[ln].flags & UNIT_2741) {
|
|
c = com_2741_in[c];
|
|
if (c & 0200) { /* Check if lower case */
|
|
if ((com_out_inesc[ln] & 2) == 0) { /* In upper case? */
|
|
uint8 c2;
|
|
c2 = com_2741_out[c & 077]; /* Check if no need to change */
|
|
if (c2 != com_2741_out[(c&077)|0100]) {
|
|
com_inp_msg(ln, 0034); /* Switch case */
|
|
com_out_inesc[ln] &= 1;
|
|
}
|
|
}
|
|
} else {
|
|
if (com_out_inesc[ln] & 2) { /* In lower case? */
|
|
uint8 c2;
|
|
c2 = com_2741_out[c & 077]; /* Check if no need to change */
|
|
if (c2 != com_2741_out[(c&077)|0100]) {
|
|
com_inp_msg(ln, 0037); /* Switch case */
|
|
com_out_inesc[ln] |= 2;
|
|
}
|
|
}
|
|
}
|
|
c &= 0177;
|
|
}
|
|
|
|
out = (~c) & 0177; /* 1's complement */
|
|
parity = out ^ (out >> 4);
|
|
parity = parity ^ (parity >> 2);
|
|
parity = parity ^ (parity >> 1);
|
|
if (parity & 1)
|
|
out |= COMI_PARITY; /* add even parity */
|
|
}
|
|
if (com_inp_msg(ln, out))
|
|
return STOP_NOIFREE; /* input message */
|
|
com_sense |= EXPT_DATRDY;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Retrieve and translate output character */
|
|
|
|
uint32
|
|
com_queue_out(uint32 ln, uint16 * c1)
|
|
{
|
|
uint16 c, raw;
|
|
|
|
*c1 = 0; /* assume non-printing */
|
|
if (com_get(ln, &raw)) /* Try to get character */
|
|
return 0;
|
|
if (raw == COMO_BITRPT) { /* insert delay? */
|
|
com_skip_outc(ln);
|
|
return 0;
|
|
}
|
|
c = (~raw >> 1) & 0177; /* remove start, parity */
|
|
if (coml_unit[ln].flags & UNIT_2741) {
|
|
uint8 c2;
|
|
c2 = c & 077;
|
|
if (com_out_inesc[ln] & 4) {
|
|
com_out_inesc[ln] &= 3;
|
|
switch (c) {
|
|
case '\043': /* Red */
|
|
tmxr_putc_ln(&com_ldsc[ln], '\033');
|
|
tmxr_putc_ln(&com_ldsc[ln], '[');
|
|
tmxr_putc_ln(&com_ldsc[ln], '3');
|
|
tmxr_putc_ln(&com_ldsc[ln], '1');
|
|
tmxr_putc_ln(&com_ldsc[ln], 'm');
|
|
return 0;
|
|
case '\023': /* Black */
|
|
tmxr_putc_ln(&com_ldsc[ln], '\033');
|
|
tmxr_putc_ln(&com_ldsc[ln], '[');
|
|
tmxr_putc_ln(&com_ldsc[ln], '0');
|
|
tmxr_putc_ln(&com_ldsc[ln], 'm');
|
|
return 0;
|
|
}
|
|
*c1 = c;
|
|
return '\033';
|
|
}
|
|
switch (c2) {
|
|
case '\034': com_out_inesc[ln] &= 2; return 0; /* UC */
|
|
case '\037': com_out_inesc[ln] |= 1; return 0; /* LC */
|
|
case '\076': com_out_inesc[ln] |= 4; return 0; /* Esc */
|
|
case '\016': coml_unit[ln].ECHO = FALSE; return 0; /* Poff */
|
|
case '\015': coml_unit[ln].ECHO = TRUE; return 0; /* Pon */
|
|
}
|
|
c2 = com_2741_out[(com_out_inesc[ln]&1)? (0100|c2): c2];
|
|
sim_debug(DEBUG_DETAIL, &com_dev, "printing %d %04o '%c' %o\n",
|
|
ln, c, (c2>= ' ')?c2: 0, com_out_inesc[ln]&1);
|
|
if (c2 == '\n')
|
|
*c1 = '\r';
|
|
return c2;
|
|
}
|
|
if (com_out_inesc[ln]) {
|
|
com_out_inesc[ln] = 0;
|
|
switch (c) {
|
|
case '3': /* Red */
|
|
tmxr_putc_ln(&com_ldsc[ln], '\033');
|
|
tmxr_putc_ln(&com_ldsc[ln], '[');
|
|
tmxr_putc_ln(&com_ldsc[ln], '3');
|
|
tmxr_putc_ln(&com_ldsc[ln], '1');
|
|
tmxr_putc_ln(&com_ldsc[ln], 'm');
|
|
return 0;
|
|
case '4': /* Black */
|
|
tmxr_putc_ln(&com_ldsc[ln], '\033');
|
|
tmxr_putc_ln(&com_ldsc[ln], '[');
|
|
tmxr_putc_ln(&com_ldsc[ln], '0');
|
|
tmxr_putc_ln(&com_ldsc[ln], 'm');
|
|
return 0;
|
|
case ':': /* Poff */
|
|
coml_unit[ln].ECHO = FALSE;
|
|
return 0;
|
|
case ';': /* Pon */
|
|
coml_unit[ln].ECHO = TRUE;
|
|
return 0;
|
|
}
|
|
*c1 = c;
|
|
return '\033';
|
|
}
|
|
sim_debug(DEBUG_DETAIL, &com_dev, "printing %d %04o '%c'\n", ln, c, (c >= ' ')?c:0);
|
|
if (c >= 040) { /* printable? */
|
|
if (c == 0177)
|
|
return 0; /* DEL? ignore */
|
|
if ((coml_unit[ln].flags & UNIT_K35) && islower(c)) /* KSR-35 LC? */
|
|
c = toupper(c); /* cvt to UC */
|
|
return c;
|
|
}
|
|
switch (c) {
|
|
case '\033': /* Escape character */
|
|
com_out_inesc[ln] = 1;
|
|
return 0;
|
|
|
|
case '\t':
|
|
case '\f':
|
|
case '\b':
|
|
case '\a': /* valid ctrls */
|
|
return c;
|
|
|
|
case '\r': /* carriage return? */
|
|
if (coml_unit[ln].flags & UNIT_K35) /* KSR-35? */
|
|
*c1 = '\n'; /* lf after cr */
|
|
return c;
|
|
|
|
case '\n': /* line feed? */
|
|
if (!(coml_unit[ln].flags & UNIT_K35)) { /* KSR-37? */
|
|
*c1 = '\n'; /* lf after cr */
|
|
return '\r';
|
|
}
|
|
return c; /* print lf */
|
|
|
|
#if 0
|
|
case 022: /* DC2 */
|
|
if (!(com_unit[ln].flags & UNIT_K35)) { /* KSR-37? */
|
|
com_skip_outc(ln); /* skip next */
|
|
return '\n'; /* print lf */
|
|
}
|
|
break;
|
|
|
|
case 024: /* DC4 */
|
|
if (!(com_unit[ln].flags & UNIT_K35)) /* KSR-37? */
|
|
com_skip_outc(ln); /* skip next */
|
|
break;
|
|
#endif
|
|
}
|
|
|
|
return 0; /* ignore others */
|
|
}
|
|
|
|
/* Generate completion message, if needed */
|
|
|
|
t_stat
|
|
com_send_ccmp(uint32 ln)
|
|
{
|
|
uint32 t;
|
|
|
|
t = com_comp_cnt[ln];
|
|
if (t != 0) { /* chars not returned? */
|
|
if (t > COMI_CMAX)
|
|
t = COMI_CMAX; /* limit to max */
|
|
com_comp_cnt[ln] -= t; /* keep count */
|
|
if (com_inp_msg(ln, COMI_COMP(t))) /* gen completion msg */
|
|
return STOP_NOIFREE;
|
|
}
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Skip next char in output queue */
|
|
|
|
void
|
|
com_skip_outc(uint32 ln)
|
|
{
|
|
uint16 tmp;
|
|
if (com_get(ln, &tmp))
|
|
com_comp_cnt[ln]++; /* count it */
|
|
return;
|
|
}
|
|
|
|
/* List routines - remove from head and free */
|
|
|
|
t_bool
|
|
com_get(int ln, uint16 *ch)
|
|
{
|
|
uint16 ent;
|
|
|
|
ent = com_out_head[ln];
|
|
if (ent == 0) /* Check if anything to send. */
|
|
return TRUE;
|
|
*ch = com_buf[ent].data;
|
|
com_comp_cnt[ln]++;
|
|
com_out_head[ln] = com_buf[ent].link; /* Get next char */
|
|
com_buf[ent].link = com_free; /* Put back on free list */
|
|
com_free = ent;
|
|
if (com_out_head[ln] == 0) { /* done with queue? */
|
|
com_out_tail[ln] = 0;
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* Put a character onto output queue for a line */
|
|
t_bool
|
|
com_put(int ln, uint16 ch)
|
|
{
|
|
uint16 ent;
|
|
|
|
ln -= COM_LBASE;
|
|
ent = com_free; /* Get a character spot */
|
|
if (ent == 0)
|
|
return TRUE; /* No room */
|
|
com_free = com_buf[ent].link; /* Next free character */
|
|
com_buf[ent].data = ch;
|
|
com_buf[ent].link = 0;
|
|
if (com_out_tail[ln] == 0) { /* Idle line */
|
|
com_out_head[ln] = ent;
|
|
} else { /* Active line */
|
|
com_buf[com_out_tail[ln]].link = ent;
|
|
}
|
|
com_out_tail[ln] = ent;
|
|
/* Activate line if not already running */
|
|
if (!sim_is_active(&coml_unit[ln]))
|
|
sim_activate(&coml_unit[ln], coml_unit[ln].wait);
|
|
return FALSE;
|
|
}
|
|
|
|
/* Put EOM on input queue and post interupt to wake up CPU */
|
|
void
|
|
com_post_eom()
|
|
{
|
|
int ent;
|
|
int chan = UNIT_G_CHAN(com_unit[0].flags);
|
|
int sel = (com_unit[0].flags & UNIT_SELECT) ? 1 : 0;
|
|
/* See if we can insert a EOM message */
|
|
if (in_buff[in_tail] != COMI_EOM) {
|
|
sim_debug(DEBUG_EXP, &com_dev, "inserting eom %d %d %d\n",
|
|
in_head, in_tail, in_count);
|
|
ent = in_tail + 1;
|
|
if (ent >= (sizeof(in_buff)/sizeof(uint16))) /* Wrap around */
|
|
ent = 0;
|
|
if (ent != in_head) { /* If next element would be head, queue is full */
|
|
/* If we can't put one on, handler will do it for us */
|
|
in_buff[ent] = COMI_EOM;
|
|
in_tail = ent;
|
|
in_count++;
|
|
}
|
|
}
|
|
chan9_set_attn(chan, sel);
|
|
com_posti = 1;
|
|
}
|
|
|
|
/* Insert line and message into input queue */
|
|
|
|
t_bool
|
|
com_inp_msg(uint32 ln, uint16 msg)
|
|
{
|
|
int ent1, ent2;
|
|
|
|
sim_debug(DEBUG_EXP, &com_dev, "inserting %d %04o %d %d %d\n", ln, msg,
|
|
in_head, in_tail, in_count);
|
|
ent1 = in_tail + 1;
|
|
if (ent1 >= (sizeof(in_buff)/sizeof(uint16))) /* Wrap around */
|
|
ent1 = 0;
|
|
if (ent1 == in_head) /* If next element would be head, queue is full */
|
|
return TRUE;
|
|
ent2 = ent1 + 1;
|
|
if (ent2 >= (sizeof(in_buff)/sizeof(uint16))) /* Wrap around */
|
|
ent2 = 0;
|
|
if (ent2 == in_head) /* If next element would be head, queue is full */
|
|
return TRUE;
|
|
/* Ok we have room to put this message in. */
|
|
ln += COM_LBASE;
|
|
in_buff[ent1] = 02000 + ln;
|
|
in_buff[ent2] = msg;
|
|
in_count += 2;
|
|
in_tail = ent2;
|
|
/* Check if over limit */
|
|
if (!com_active && in_count > 150) {
|
|
com_post_eom();
|
|
}
|
|
return FALSE;
|
|
}
|
|
|
|
/* Reset routine */
|
|
|
|
t_stat
|
|
com_reset(DEVICE * dptr)
|
|
{
|
|
uint32 i;
|
|
|
|
if (dptr->flags & DEV_DIS) { /* disabled? */
|
|
com_dev.flags = com_dev.flags | DEV_DIS; /* disable lines */
|
|
coml_dev.flags = coml_dev.flags | DEV_DIS;
|
|
} else {
|
|
com_dev.flags = com_dev.flags & ~DEV_DIS; /* enable lines */
|
|
coml_dev.flags = coml_dev.flags & ~DEV_DIS;
|
|
}
|
|
#if 0 /* Commented out until I can detect hangup signal */
|
|
sim_activate(&com_unit[COM_CIU], com_unit[COM_CIU].wait); /* console */
|
|
sim_cancel(&com_unit[COM_CHU]);
|
|
#endif
|
|
sim_cancel(&com_unit[COM_PLU]);
|
|
if (com_unit[COM_PLU].flags & UNIT_ATT) { /* master att? */
|
|
int32 t =
|
|
|
|
sim_rtcn_init(com_unit[COM_PLU].wait, TMR_COM);
|
|
sim_activate(&com_unit[COM_PLU], t);
|
|
}
|
|
com_enab = 0;
|
|
com_msgn = 0;
|
|
com_sta = 0;
|
|
com_sense = 0;
|
|
/* Put dummy message on Queue before first login */
|
|
in_head = 0;
|
|
in_tail = 0;
|
|
in_count = 0;
|
|
for (i = 0; i < COM_TLINES; i++) {
|
|
com_out_tail[i] = 0;
|
|
com_out_head[i] = 0;
|
|
com_reset_ln(i);
|
|
}
|
|
com_free = sizeof(com_buf)/(sizeof(OLIST));
|
|
for (i = 1; i < com_free; i++) {
|
|
com_buf[i].link = i + 1;
|
|
com_buf[i].data = 0;
|
|
}
|
|
com_buf[com_free - 1].link = 0; /* end of free list */
|
|
com_free = 1;
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Attach master unit */
|
|
|
|
t_stat
|
|
com_attach(UNIT * uptr, CONST char *cptr)
|
|
{
|
|
t_stat r;
|
|
|
|
r = tmxr_attach(&com_desc, uptr, cptr); /* attach */
|
|
if (r != SCPE_OK)
|
|
return r; /* error */
|
|
sim_rtcn_init(uptr->wait, TMR_COM);
|
|
sim_activate(uptr, 100); /* quick poll */
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Detach master unit */
|
|
|
|
t_stat
|
|
com_detach(UNIT * uptr)
|
|
{
|
|
uint32 i;
|
|
t_stat r;
|
|
|
|
r = tmxr_detach(&com_desc, uptr); /* detach */
|
|
for (i = 0; i < COM_MLINES; i++)
|
|
com_ldsc[i].rcve = 0; /* disable rcv */
|
|
sim_cancel(uptr); /* stop poll */
|
|
return r;
|
|
}
|
|
|
|
/* Show summary processor */
|
|
|
|
t_stat
|
|
com_summ(FILE * st, UNIT * uptr, int32 val, CONST void *desc)
|
|
{
|
|
uint32 i, t;
|
|
|
|
t = 0;
|
|
for (i = 0; i < COM_TLINES; i++)
|
|
t = t + (com_ldsc[i].conn != 0);
|
|
if (t == 1)
|
|
fprintf(st, "1 connection");
|
|
else
|
|
fprintf(st, "%d connections", t);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* SHOW CONN/STAT processor */
|
|
|
|
t_stat
|
|
com_show(FILE * st, UNIT * uptr, int32 val, CONST void *desc)
|
|
{
|
|
int32 i, cc;
|
|
|
|
for (cc = 0; (cc < COM_MLINES) && com_ldsc[cc].conn; cc++) ;
|
|
if (cc) {
|
|
for (i = 0; i < COM_MLINES; i++) {
|
|
if (com_ldsc[i].conn) {
|
|
if (val)
|
|
tmxr_fconns(st, &com_ldsc[i], i);
|
|
else
|
|
tmxr_fstats(st, &com_ldsc[i], i);
|
|
}
|
|
}
|
|
} else
|
|
fprintf(st, "all disconnected\n");
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Reset an individual line */
|
|
|
|
void
|
|
com_reset_ln(uint32 ln)
|
|
{
|
|
uint16 ch;
|
|
|
|
while (!com_get(ln, &ch)) ;
|
|
com_comp_cnt[ln] = 0;
|
|
com_out_inesc[ln] = 0;
|
|
sim_cancel(&coml_unit[ln]);
|
|
if ((ln < COM_MLINES) && (com_ldsc[ln].conn == 0))
|
|
coml_unit[ln].CONN = 0;
|
|
return;
|
|
}
|
|
const char *
|
|
coml_description(DEVICE *dptr)
|
|
{
|
|
return "IBM 7750 terminal";
|
|
}
|
|
|
|
t_stat
|
|
coml_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
|
|
{
|
|
fprintf (st, "Each COM line can be set to a given type of terminal\n\n");
|
|
fprintf (st, " sim> SET COMLn KSR-37 Standard connection\n");
|
|
fprintf (st, " sim> SET COMLn KSR-35 Allows only upper case\n");
|
|
fprintf (st, " sim> SET COMLn 2741 Set to look like a 2741\n");
|
|
fprint_set_help (st, dptr);
|
|
fprint_show_help (st, dptr);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
const char *
|
|
com_description(DEVICE *dptr)
|
|
{
|
|
return "IBM 7750 terminal controller";
|
|
}
|
|
|
|
t_stat
|
|
com_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
|
|
{
|
|
fprintf (st, "IBM 7750 terminal controller, this is required for CTSS to run.\n");
|
|
fprintf (st, "The ATTACH command specifies the port to be used:\n\n");
|
|
tmxr_attach_help (st, dptr, uptr, flag, cptr);
|
|
help_set_chan_type(st, dptr, "IBM 7750");
|
|
#ifndef I7010
|
|
fprintf (st, "Each device on the channel can be at either select 0 or 1, \n");
|
|
fprintf (st, "this is set with the\n\n");
|
|
fprintf (st, " sim> SET COM SELECT=n\n\n");
|
|
#endif
|
|
fprint_set_help (st, dptr);
|
|
fprint_show_help (st, dptr);
|
|
return SCPE_OK;
|
|
}
|
|
|
|
#endif
|
|
|
|
|