/* sel32_com.c: SEL 32 8-Line IOP communications controller

   Copyright (c) 2018-2023, James C. Bevier
   Portions provided by Richard Cornwell and other SIMH contributers

   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
   JAMES C. BEVIER 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.

*/

#include "sel32_defs.h"
#include "sim_sock.h"
#include "sim_tmxr.h"
#include <ctype.h>

#if NUM_DEVS_COM > 0

/* Constants */
#define COM_LINES       8                   /* lines defined */
#define COML_WAIT       4000
#define COM_WAIT        5000
#define COM_NUMLIN      com_desc.lines      /* curr # lines */

#define COMC            0                   /* channel thread */
#define COMI            1                   /* input thread */

/* Line status */
#define COML_XIA        0x01                /* xmt intr armed */
#define COML_XIR        0x02                /* xmt intr req */
#define COML_REP        0x04                /* rcv enable pend */
#define COML_RBP        0x10                /* rcv break pend */

struct _com_data
{
    uint8   incnt;                          /* char count */
    uint8   ibuff[120];                     /* Input line buffer */
}
com_data[COM_LINES];

uint8 com_rbuf[COM_LINES];                  /* rcv buf */
uint8 com_xbuf[COM_LINES];                  /* xmt buf */
uint8 com_stat[COM_LINES];                  /* status */

TMLN com_ldsc[COM_LINES] = { 0 };           /* line descrs */
TMXR com_desc = { COM_LINES, 0, 0, com_ldsc }; /* com descr */

#define CMD u3
/* Held in u3 is the device command and status */
#define COM_INCH    0x00                    /* Initialize channel command */
#define COM_WR      0x01                    /* Write terminal */
#define COM_RD      0x02                    /* Read terminal */
#define COM_NOP     0x03                    /* No op command */
#define COM_SNS     0x04                    /* Sense command */
#define COM_WRSCM   0x05                    /* Write w/Sub chan monitor */
#define COM_RDECHO  0x06                    /* Read with Echo */
#define COM_RDFC    0x0A                    /* Read w/flow control */
#define COM_DEFSC   0x0B                    /* Define special char */
#define COM_WRHFC   0x0D                    /* Write hardware flow control */
#define COM_RRDFLOW 0x0E                    /* Read w/hardware flow control only RTS */
#define COM_RDTR    0x13                    /* Reset DTR (ADVR) */
#define COM_SDTR    0x17                    /* Set DTR (ADVF) */
#define COM_RRTS    0x1B                    /* Reset RTS */
#define COM_SRTS    0x1F                    /* Set RTS */
#define COM_RBRK    0x33                    /* Reset BREAK */
#define COM_SBRK    0x37                    /* Set BREAK */
#define COM_SETFLOW 0x53                    /* Set transparent flow control mode */
#define COM_RDHFC   0x8E                    /* Read w/hardware flow control only DTR */
#define COM_SACE    0xFF                    /* Set ACE parameters */

#define COM_MSK     0xFF                    /* Command mask */

/* Status held in CMD (u3) */
/* controller/unit address in upper 16 bits */
#define COM_INPUT   0x0100                  /* Input ready for unit */
//#define COM_RDY     0x0200  /* SNS_DSRS */  /* Device is ready */
#define COM_SCD     0x0400                  /* Special char detect */
#define COM_EKO     0x0800                  /* Echo input character */
#define COM_OUTPUT  0x1000                  /* Output ready for unit */
#define COM_READ    0x2000                  /* Read mode selected */
#define COM_ACC     0x4000                  /* ASCII control char detect */
#define COM_CONN    0x8000  /* TMXR ATT */  /* Terminal connected */

/* ACE data kept in u4 */
/* ACE information in u4 */
#define ACE u4
#if 0
/* ACE byte 0  Modem Control/Operation status */
/* stored in u4 bytes 0-3 */
#define SNS_HALFD   0x80000000  /* Half-duplix operation set */
#define SNS_MRINGE  0x40000000  /* Modem ring enabled */
#define SNS_ACEFP   0x20000000  /* Forced parity 0=odd, 1=even */
#define SNS_ACEP    0x10000000  /* Parity 0=odd, 1=even */
#define SNS_ACEPE   0x08000000  /* Parity enable 0=dis, 1=enb */
#define SNS_ACESTOP 0x04000000  /* Stop bit 0=1, 1=1.5 or 2 */
#define SNS_ACECLEN 0x02000000  /* Character length 00=5, 01=6, 10=7, 11=8 */
#define SNS_ACECL2  0x01000000  /* 2nd bit for above */

/* ACE byte 1  Baud rate */
#define SNS_NODCDA  0x00800000  /* Enable Delta DCD Attention Interrupt */
#define SNS_WAITOLB 0x00400000  /* Wait on last byte enabled */
#define SNS_RINGCR  0x00200000  /* Ring or wakeup character recognition 0=enb, 1=dis */
#define SNS_DIAGL   0x00100000  /* Set diagnostic loopback */
#define SNS_BAUD    0x000F0000  /* Baud rate bits 4-7 */
#define BAUD50      0x00000000  /* 50 baud */
#define BAUD75      0x00010000  /* 75 baud */
#define BAUD110     0x00020000  /* 110 baud */
#define BAUD114     0x00030000  /* 134 baud */
#define BAUD150     0x00040000  /* 150 baud */
#define BAUD300     0x00050000  /* 300 baud */
#define BAUD600     0x00060000  /* 600 baud */
#define BAUD1200    0x00070000  /* 1200 baud */
#define BAUD1800    0x00080000  /* 1800 baud */
#define BAUD2000    0x00090000  /* 2000 baud */
#define BAUD2400    0x000A0000  /* 2400 baud */
#define BAUD3600    0x000B0000  /* 3600 baud */
#define BAUD4800    0x000C0000  /* 4800 baud */
#define BAUD7200    0x000D0000  /* 7200 baud */
#define BAUD9600    0x000E0000  /* 9600 baud */
#define BAUD19200   0x000F0000  /* 19200 baud */

/* ACE byte 2  Wake-up character */
#define ACE_WAKE    0x0000FF00              /* 8 bit wake-up character */
#endif
#define ACE_WAKE    0x0000FF00              /* 8 bit wake-up character in byte 2 of ACE */

#define SNS u5
/* in u5 packs sense byte 0, 1, 2 and 3 */
/* Sense byte 0 */
#define SNS_CMDREJ  0x80000000              /* Command reject */
#define SNS_INTVENT 0x40000000              /* Unit intervention required (N/U) */
#define SNS_BOCHK   0x20000000              /* Bus out check (IOP parity error */
#define SNS_EQUIPCK 0x10000000              /* Equipment check (device error) */
#define SNS_DATACK  0x08000000              /* Data check */
#define SNS_OVERRN  0x04000000              /* Overrun (N/U) */
#define SNS_NUB01   0x02000000              /* Zero (N/U) */
#define SNS_RDY     SNS_NUB01               /* SNS_RDY device ready */
#define SNS_NUB02   0x01000000              /* Zero (N/U) */
#define SNS_CONN    SNS_NUB02               /* SNS_CONN device connected */
/* Sense byte 1 */
#define SNS_ASCIICD 0x00800000              /* ASCII control char detected interrupt */
#define SNS_SPCLCD  0x00400000              /* Special char detected interrupt */
#define SNS_ETX     0x00200000              /* ETX interrupt */
#define SNS_BREAK   0x00100000              /* BREAK interrupt */
#define SNS_ACEFE   0x00080000              /* ACE framing error interrupt */
#define SNS_ACEPEI  0x00040000              /* ACE parity error interrupt */
#define SNS_ACEOVR  0x00020000              /* ACE overrun error interrupt */
#define SNS_RING    0x00010000              /* Ring character interrupt */
/* Sense byte 2  Modem status */
#define SNS_RLSDS   0x00008000              /* Received line signal detect */
#define SNS_RINGST  0x00004000              /* Ring indicator signal detect */
#define SNS_DSRS    0x00002000              /* DSR Data set ready line status */
#define SNS_CTSS    0x00001000              /* CTS Clear to send line status */
#define SNS_DELTA   0x00000800              /* Delta receive line signal detect failure interrupt */
#define SNS_MRING   0x00000400              /* RI Modem ring interrupt */
#define SNS_DELDSR  0x00000200              /* DSR failure interrupt */
#define SNS_DELCTS  0x00000100              /* CLS failure interrupt */
/* Sense byte 3  Modem Control/Operation status */
#define SNS_HALFD   0x00000080              /* Half-duplix operation set */
#define SNS_MRINGE  0x00000040              /* Modem ring enabled (1) */
#define SNS_ACEDEF  0x00000020              /* ACE parameters defined */
#define SNS_DIAGM   0x00000010              /* Diagnostic mode set */
#define SNS_AUXOL2  0x00000008              /* Auxiliary output level 2 */
#define SNS_AUXOL1  0x00000004              /* Auxiliary output level 1 */
#define SNS_RTS     0x00000002              /* RTS Request to send set */
#define SNS_DTR     0x00000001              /* DTR Data terminal ready set */
/* Sense byte 4  ACE Parameters status */
#define SNS_ACEDLE  0x80000000              /* Divisor latch enable 0=dis, 1=enb */
#define SNS_ACEBS   0x40000000              /* Break set 0=reset, 1=set */
#define SNS_ACEFP   0x20000000              /* Forced parity 0=odd, 1=even */
#define SNS_ACEP    0x10000000              /* Parity 0=odd, 1=even */
#define SNS_ACEPE   0x08000000              /* Parity enable 0=dis, 1=enb */
#define SNS_ACESTOP 0x04000000              /* Stop bit 0=1, 1=1.5 or 2 */
#define SNS_ACECLEN 0x02000000              /* Character length 00=5, 01=6, 11=7, 11=8 */
#define SNS_ACECL2  0x01000000              /* 2nd bit for above */
/* Sense byte 5  Baud rate */
#define SNS_NODCDA  0x00800000              /* Enable Delta DCD Attention Interrupt */
#define SNS_WAITOLB 0x00400000              /* Wait on last byte enabled */
#define SNS_RINGCR  0x00200000              /* Ring or wakeup character recognition 0=enb, 1=dis */
#define SNS_DIAGL   0x00100000              /* Set diagnostic loopback */
#define SNS_BAUD    0x000F0000              /* Baud rate bits 4-7 */
#define BAUD50      0x00000000              /* 50 baud */
#define BAUD75      0x00010000              /* 75 baud */
#define BAUD110     0x00020000              /* 110 baud */
#define BAUD114     0x00030000              /* 134 baud */
#define BAUD150     0x00040000              /* 150 baud */
#define BAUD300     0x00050000              /* 300 baud */
#define BAUD600     0x00060000              /* 600 baud */
#define BAUD1200    0x00070000              /* 1200 baud */
#define BAUD1800    0x00080000              /* 1800 baud */
#define BAUD2000    0x00090000              /* 2000 baud */
#define BAUD2400    0x000A0000              /* 2400 baud */
#define BAUD3600    0x000B0000              /* 3600 baud */
#define BAUD4800    0x000C0000              /* 4800 baud */
#define BAUD7200    0x000D0000              /* 7200 baud */
#define BAUD9600    0x000E0000              /* 9600 baud */
#define BAUD19200   0x000F0000              /* 19200 baud */
/* Sense byte 6  Firmware ID, Revision Level */
#define SNS_FID     0x00006200              /* ID part 1 */
/* Sense byte 7  Firmware ID, Revision Level */
#define SNS_REV     0x0000004f              /* ID part 2 plus 4 bit rev # */

/* u6 */
#define CNT u6

/* forward definitions */
t_stat      coml_preio(UNIT *uptr, uint16 chan);
t_stat      coml_startcmd(UNIT *uptr, uint16 chan,  uint8 cmd);
t_stat      coml_haltio(UNIT *uptr);
void        com_ini(UNIT *, t_bool);
void        coml_ini(UNIT *, t_bool);
t_stat      coml_rschnlio(UNIT *uptr);
t_stat      com_rschnlio(UNIT *uptr);
t_stat      comi_srv(UNIT *uptr);
t_stat      como_srv(UNIT *uptr);
t_stat      comc_srv(UNIT *uptr);
t_stat      com_reset(DEVICE *dptr);
t_stat      com_attach(UNIT *uptr, CONST char *cptr);
t_stat      com_detach(UNIT *uptr);
void        com_reset_ln(int32 ln);
const char  *com_description(DEVICE *dptr);     /* device description */

/* COM data structures
    com_chp     COM channel program information
    com_dev     COM device descriptor
    com_unit    COM unit descriptor
    com_reg     COM register list
    com_mod     COM modifiers list
*/

//#define COM_UNITS 2
#define COM_UNITS 1

/* channel program information */
CHANP           com_chp[COM_UNITS] = {0};

/* dummy mux for 16 lines */
MTAB            com_mod[] = {
    {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr, &show_dev_addr, NULL},
    {MTAB_XTD | MTAB_VDV, 1, NULL, "DISCONNECT", &tmxr_dscln, NULL, &com_desc},
    {UNIT_ATT, UNIT_ATT, "SUMMARY", NULL, NULL, &tmxr_show_summ, (void *) &com_desc},
    {MTAB_XTD | MTAB_VDV | MTAB_NMO, 1, "CONNECTIONS", NULL, NULL, &tmxr_show_cstat,(void *)&com_desc},
    {MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATISTICS", NULL, NULL, &tmxr_show_cstat, (void *)&com_desc},
    { 0 }
};

UNIT            com_unit[] = {
    {UDATA(&comc_srv, UNIT_ATTABLE|UNIT_IDLE, 0), COM_WAIT, UNIT_ADDR(0x0000)},       /* 0 */
};

DIB             com_dib = {
    NULL,           /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/  /* Pre Start I/O */
    NULL,           /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command */
    NULL,           /* t_stat (*halt_io)(UNIT *uptr) */         /* Halt I/O */
    NULL,           /* t_stat (*stop_io)(UNIT *uptr) */         /* Stop I/O */
    NULL,           /* t_stat (*test_io)(UNIT *uptr) */         /* Test I/O */
    NULL,           /* t_stat (*rsctl_io)(UNIT *uptr) */        /* Reset Controller */
    com_rschnlio,   /* t_stat (*rschnl_io)(UNIT *uptr) */       /* Reset Channel */
    NULL,           /* t_stat (*iocl_io)(CHANP *chp, int32 tic_ok)) */  /* Process IOCL */
    com_ini,        /* void  (*dev_ini)(UNIT *, t_bool) */      /* init function */
    com_unit,       /* UNIT* units */                           /* Pointer to units structure */
    com_chp,        /* CHANP* chan_prg */                       /* Pointer to chan_prg structure */
    NULL,           /* IOCLQ *ioclq_ptr */                      /* IOCL entries, 1 per UNIT */
    COM_UNITS,      /* uint8 numunits */                        /* number of units defined */
    0x0f,           /* uint8 mask */                            /* 16 devices - device mask */
    0x7E00,         /* uint16 chan_addr */                      /* parent channel address */
    0,              /* uint32 chan_fifo_in */                   /* fifo input index */
    0,              /* uint32 chan_fifo_out */                  /* fifo output index */
    {0}             /* uint32 chan_fifo[FIFO_SIZE] */           /* interrupt status fifo for channel */
};

REG             com_reg[] = {
    { BRDATAD (STA, com_stat, 16, 8, COM_LINES, "status buffers, lines 0 to 7") },
    { BRDATAD (RBUF, com_rbuf, 16, 8, COM_LINES, "input buffer, lines 0 to 7") },
    { BRDATAD (XBUF, com_xbuf, 16, 8, COM_LINES, "output buffer, lines 0 to 7") },
    { NULL }
    };

/* devices for channel 0x7ecx */
DEVICE          com_dev = {
    "COMC", com_unit, com_reg, com_mod,
    COM_UNITS, 8, 15, 1, 8, 8,
    &tmxr_ex, &tmxr_dep, &com_reset, NULL, &com_attach, &com_detach,
    /* ctxt is the DIB pointer */
    &com_dib, DEV_MUX|DEV_DISABLE|DEV_DEBUG, 0, dev_debug,
    NULL, NULL, NULL, NULL, NULL, &com_description,
};

/* COML data structures
    coml_dev    COM device descriptor
    coml_unit   COM unit descriptor
    coml_reg    COM register list
    coml_mod    COM modifiers list
*/

//#define UNIT_COML UNIT_IDLE|UNIT_DISABLE|TT_MODE_7B
#define UNIT_COML UNIT_DISABLE|TT_MODE_7B

/* channel program information */
CHANP           coml_chp[COM_LINES*2] = {0};

UNIT            coml_unit[] = {
    /* 0-7 is input, 8-f is output */
    {UDATA(&comi_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EA0)},  /* 0 */
    {UDATA(&comi_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EA1)},  /* 1 */
    {UDATA(&comi_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EA2)},  /* 2 */
    {UDATA(&comi_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EA3)},  /* 3 */
    {UDATA(&comi_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EA4)},  /* 4 */
    {UDATA(&comi_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EA5)},  /* 5 */
    {UDATA(&comi_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EA6)},  /* 6 */
    {UDATA(&comi_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EA7)},  /* 7 */
    {UDATA(&como_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EA8)},  /* 8 */
    {UDATA(&como_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EA9)},  /* 9 */
    {UDATA(&como_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EAA)},  /* A */
    {UDATA(&como_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EAB)},  /* B */
    {UDATA(&como_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EAC)},  /* C */
    {UDATA(&como_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EAD)},  /* D */
    {UDATA(&como_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EAE)},  /* E */
    {UDATA(&como_srv, UNIT_COML, 0), COML_WAIT, UNIT_ADDR(0x7EAF)},  /* F */
};

DIB             coml_dib = {
    coml_preio,     /* t_stat (*pre_io)(UNIT *uptr, uint16 chan)*/  /* Pre Start I/O */
    coml_startcmd,  /* t_stat (*start_cmd)(UNIT *uptr, uint16 chan, uint8 cmd)*/ /* Start command */
    coml_haltio,    /* t_stat (*halt_io)(UNIT *uptr) */         /* Halt I/O */
    NULL,           /* t_stat (*stop_io)(UNIT *uptr) */         /* Stop I/O */
    NULL,           /* t_stat (*test_io)(UNIT *uptr) */         /* Test I/O */
    NULL,           /* t_stat (*rsctl_io)(UNIT *uptr) */        /* Reset Controller */
    coml_rschnlio,  /* t_stat (*rschnl_io)(UNIT *uptr) */       /* Reset Channel */
    NULL,           /* t_stat (*iocl_io)(CHANP *chp, int32 tic_ok)) */  /* Process IOCL */
    coml_ini,       /* void  (*dev_ini)(UNIT *, t_bool) */      /* init function */
    coml_unit,      /* UNIT* units */                           /* Pointer to units structure */
    coml_chp,       /* CHANP* chan_prg */                       /* Pointer to chan_prg structure */
    NULL,           /* IOCLQ *ioclq_ptr */                      /* IOCL entries, 1 per UNIT */
    COM_LINES*2,    /* uint8 numunits */                        /* number of units defined */
    0x0f,           /* uint8 mask */                            /* 16 devices - device mask */
    0x7E00,         /* uint16 chan_addr */                      /* parent channel address */
    0,              /* uint32 chan_fifo_in */                   /* fifo input index */
    0,              /* uint32 chan_fifo_out */                  /* fifo output index */
    {0}             /* uint32 chan_fifo[FIFO_SIZE] */           /* interrupt status fifo for channel */
};

REG             coml_reg[] = {
    { URDATA (TIME, coml_unit[0].wait, 10, 24, 0, COM_LINES, REG_NZ + PV_LEFT) },
    { NULL }
};

MTAB            coml_mod[] = {
    {MTAB_XTD | MTAB_VUN | MTAB_VALR, 0, "DEV", "DEV", &set_dev_addr, &show_dev_addr, NULL},
    { TT_MODE, TT_MODE_UC, "UC", "UC", NULL },
    { TT_MODE, TT_MODE_7B, "7b", "7B", NULL },
    { TT_MODE, TT_MODE_8B, "8b", "8B", NULL },
    { TT_MODE, TT_MODE_7P, "7p", "7P", NULL },
    { MTAB_XTD|MTAB_VUN, 0, NULL, "DISCONNECT",
      &tmxr_dscln, NULL, &com_desc },
    { 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 }
    };

DEVICE          coml_dev = {
    "COML", coml_unit, coml_reg, coml_mod,
    COM_LINES*2, 10, 31, 1, 8, 8,
    NULL, NULL, &com_reset,
    NULL, NULL, NULL,
    /* ctxt is the DIB pointer */
    &coml_dib, DEV_DISABLE|DEV_DEBUG, 0, dev_debug,
    NULL, NULL, NULL, NULL, NULL, &com_description
};

/* 8-line serial routines */
void coml_ini(UNIT *uptr, t_bool f)
{
    /* set SNS_RLSDS SNS_DSRS SNS_CTSS SNS_RTS SNS_CTS */
    uptr->SNS = 0x0000b003;                     /* status is online & ready */
    uptr->CMD &= LMASK;                         /* leave only chsa */
    sim_cancel(uptr);                           /* stop any timer */
}

/* handle rschnlio cmds for coml */
t_stat  coml_rschnlio(UNIT *uptr) {
    DEVICE  *dptr = get_dev(uptr);
    uint16  chsa = GET_UADDR(uptr->CMD);
    int     cmd = uptr->CMD & COM_MSK;

    sim_debug(DEBUG_EXP, dptr,
        "coml_rschnl chsa %04x cmd = %02x\n", chsa, cmd);
    coml_ini(uptr, 0);                          /* reset the unit */
    return SCPE_OK;
}

/* 8-line serial routines */
void com_ini(UNIT *uptr, t_bool f)
{
    DEVICE *dptr = get_dev(uptr);

    sim_debug(DEBUG_CMD, dptr,
        "COM init device %s controller 0x7e00\n", dptr->name);
    sim_cancel(uptr);                           /* stop input poll */
    sim_activate(uptr, 1000);                   /* start input poll */
}

/* handle rschnlio cmds for com */
t_stat  com_rschnlio(UNIT *uptr) {
    DEVICE  *dptr = get_dev(uptr);
    uint16  chsa = GET_UADDR(uptr->CMD);
    int     cmd = uptr->CMD & COM_MSK;

    sim_debug(DEBUG_EXP, dptr,
        "com_rschnl chsa %04x cmd = %02x\n", chsa, cmd);
    com_ini(uptr, 0);                           /* reset the unit */
    return SCPE_OK;
}

/* start a com operation */
t_stat coml_preio(UNIT *uptr, uint16 chan) {
    DEVICE      *dptr = get_dev(uptr);
    int         unit = (uptr - dptr->units);
    uint16      chsa = GET_UADDR(uptr->CMD);    /* get channel/sub-addr */
    UNIT        *ruptr = &dptr->units[unit&7];  /* read uptr */
    UNIT        *wuptr = &dptr->units[(unit&7)+8];  /* write uptr */

    sim_debug(DEBUG_CMD, dptr,
        "coml_preio CMD %08x unit %02x chsa %04x\n",
        uptr->CMD, unit, chsa);
    sim_debug(DEBUG_CMD, dptr,
        "coml_preio chsa %04x ln %1x conn %x rcve %x xmte %x SNS %08x SNS %08x\n",
        chsa, unit, com_ldsc[unit&7].conn, com_ldsc[unit&7].rcve,
        com_ldsc[unit&7].xmte, ruptr->SNS, wuptr->SNS);

    if ((uptr->CMD & COM_MSK) != 0) {           /* just return if busy */
        sim_debug(DEBUG_CMD, dptr,
            "coml_preio unit %02x chsa %04x BUSY\n", unit, chsa);
        return SNS_BSY;
    }

    sim_debug(DEBUG_CMD, dptr,
        "coml_preio unit %02x chsa %04x OK\n", unit, chsa);
    return SCPE_OK;                             /* good to go */
}

/* called from sel32_chan to start an I/O operation */
t_stat  coml_startcmd(UNIT *uptr, uint16 chan, uint8 cmd)
{
    DEVICE      *dptr = get_dev(uptr);
    int         unit = (uptr - dptr->units);
    UNIT        *ruptr = &dptr->units[unit&7];  /* read uptr */
    UNIT        *wuptr = &dptr->units[(unit&7)+8];  /* write uptr */
    uint16      chsa = (((uptr->CMD & LMASK) >> 16) | (chan << 8));
    uint8       ch, fcb[3];

    if ((uptr->CMD & COM_MSK) != 0) {           /* is unit busy */
        return SNS_BSY;                         /* yes, return busy */
    }

    sim_debug(DEBUG_CMD, dptr,
        "coml_startcmd chsa %04x line %1x cmd %02x conn %x rcve %x xmte %x SNS %08x SNS %08x\n",
        chsa, unit, cmd, com_ldsc[unit&7].conn, com_ldsc[unit&7].rcve,
        com_ldsc[unit&7].xmte, ruptr->SNS, wuptr->SNS);

    uptr->CMD &= LMASK;                         /* clear any flags that are set */
    /* process the commands */
    switch (cmd & 0xFF) {
    case COM_INCH:      /* 00 */                /* INCH command */
        sim_debug(DEBUG_CMD, dptr, "coml_startcmd chsa %04x: CMD INCH\n", chsa);
        uptr->CMD &= LMASK;                     /* leave only chsa */
        uptr->CMD |= (0x7f & COM_MSK);          /* save 0x7f as INCH cmd command */
        uptr->SNS |= SNS_RDY;                   /* status is online & ready */
        sim_activate(uptr, 500);                /* start us up */
        break;

    /* write commands must use address 8-f */
    case COM_WR:        /* 0x01 */              /* Write command */
    case COM_WRSCM:     /* 0x05 */              /* Write w/ input sub channel monitor */
    case COM_WRHFC:     /* 0x0D */              /* Write w/hardware flow control only */
        sim_debug(DEBUG_CMD, dptr,
            "coml_startcmd chsa %04x: Cmd WRITE %02x\n", chsa, cmd);

        /* see if DSR is set, if not give unit check error */
        if (((ruptr->SNS & SNS_DSRS) == 0) || ((ruptr->SNS & SNS_CONN) == 0)) {
            ruptr->SNS &= ~SNS_RDY;             /* status is not ready */
            wuptr->SNS &= ~SNS_RDY;             /* status is not ready */
            ruptr->SNS |= SNS_CMDREJ;           /* command reject */
            wuptr->SNS |= SNS_CMDREJ;           /* command reject */
            sim_debug(DEBUG_CMD, dptr,
                "coml_startcmd chsa %04x: Cmd WRITE %02x unit check\n", chsa, cmd);
            return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK;   /* good return */
        }
        uptr->CMD &= LMASK;                     /* leave only chsa */
        uptr->CMD |= (cmd & COM_MSK);           /* save command */
        uptr->SNS |= SNS_RDY;                   /* status is online & ready */
        sim_activate(uptr, 250);                /* TRY 08-13-18 */
        return 0;                               /* no status change */
        break;

    /* read commands must use address 0-7 */
    /* DSR must be set when a read command is issued, else it is unit check */
    /* bit 1-3 (ASP) of command has more definition */
    /* bit 1 A=1 ASCII control character  detect (7-char mode only) */
    /* bit 2 S=1 Special character detect (7-char mode only) */
    /* bit 3 P=1 Purge input buffer */
    case COM_RD:        /* 0x02 */              /* Read command */
    case 0x22:          /* 0x22 */              /* Read command w/ASCII CC */
    case 0x32:          /* 0x32 */              /* Read command w/ASCII CC & Purge input */
    case COM_RDECHO:    /* 0x06 */              /* Read command w/ECHO */
    case 0x46:          /* 0x46 */              /* Read command w/ECHO & ASCII CC*/
    case 0x56:          /* 0x56 */              /* Read command w/ECHO & ASCII CC & Purge input */
    /* if bit 0 set for COM_RDFC, use DTR for flow, else use RTS for flow control */
    case COM_RDFC:      /* 0x0A */              /* Read command w/flow control */
    case COM_RDHFC:     /* 0x8E */              /* Read command w/hardware flow control only */

        /* see if DSR is set, if not give unit check error */
        if (((ruptr->SNS & SNS_DSRS) == 0) || ((ruptr->SNS & SNS_CONN) == 0)) {
            ruptr->SNS &= ~SNS_RDY;             /* status is not ready */
            wuptr->SNS &= ~SNS_RDY;             /* status is not ready */
            ruptr->SNS |= SNS_CMDREJ;           /* command reject */
            wuptr->SNS |= SNS_CMDREJ;           /* command reject */
            /* SNS_DSRS will be 0 */
/*UTX*/     ruptr->SNS |= SNS_DELDSR;           /* set attention status */
/*UTX*/     wuptr->SNS |= SNS_DELDSR;           /* set attention status */
            sim_debug(DEBUG_CMD, dptr,
                "coml_startcmd chsa %04x: Cmd READ %02x unit check\n", chsa, cmd);
            return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK;   /* good return */
        }
        unit &= 0x7;                            /* make unit 0-7 */
        uptr->CMD &= ~COM_EKO;                  /* clear echo status */
        sim_debug(DEBUG_CMD, dptr, "coml_startcmd chsa %04x: Cmd read\n", chsa);
        uptr->CMD &= LMASK;                     /* leave only chsa */
        uptr->CMD |= (cmd & COM_MSK);           /* save command */
        if ((cmd & 0x0f) == COM_RDECHO)         /* echo command? */
            uptr->CMD |= COM_EKO;               /* save echo status */
        if (cmd & 0x10) {                       /* purge input request? */
            uptr->CNT = 0;                      /* no input count */
            com_data[unit].incnt = 0;           /* no input data */
            com_rbuf[unit&7] = 0;               /* clear read buffer */
        }
        uptr->CMD |= COM_READ;                  /* show read mode */
        uptr->SNS |= SNS_RDY;                   /* status is online & ready */
        sim_debug(DEBUG_CMD, dptr,
            "coml_startcmd chsa %04x: input cnt = %04x\n",
            chsa, coml_chp[unit].ccw_count);
        sim_activate(uptr, 250);                /* TRY 08-13-18 */
        return 0;
        break;

    case COM_NOP:       /* 0x03 */              /* NOP has do nothing */
        sim_debug(DEBUG_CMD, dptr,
            "coml_startcmd chsa %04x: Cmd %02x NOP\n", chsa, cmd);
        uptr->SNS |= SNS_RDY;                   /* status is online & ready */
        uptr->CMD &= LMASK;                     /* leave only chsa */
        uptr->CMD |= (cmd & COM_MSK);           /* save command */
        sim_activate(uptr, 250);                /* start us up */
        break;

    case COM_SNS:       /* 0x04 */              /* Sense (8 bytes) */
        unit &= 0x7;                            /* make unit 0-7 */
        /* status is in SNS (u5) */
        /* ACE is in ACE (u4) */

        sim_debug(DEBUG_CMD, dptr,
            "coml_startcmd SENSE chsa %04x: unit %02x Cmd Sense SNS %08x ACE %08x\n",
            chsa, unit, uptr->SNS, uptr->ACE);

        /* byte 0 device status */
        ch = (uptr->SNS >> 24) & 0xff;          /* no bits in byte 0 */
        chan_write_byte(chsa, &ch);             /* write status */

        /* byte 1 line status and error conditions */
        ch = (uptr->SNS >> 16) & 0xff;          /* no bits in byte 1 */
        chan_write_byte(chsa, &ch);             /* write status */

        /* byte 2 modem status */
        // SNS_DELDSR will be set if just connected, clear at end
        ch = (uptr->SNS >> 8) & 0xff;           /* CTS & DSR bits in byte 2 */
        chan_write_byte(chsa, &ch);             /* write status */

        /* byte 3 modem control/operation mode */
        ch = uptr->SNS & 0xff;                  /* maybe DTR bit in byte 3 */
        chan_write_byte(chsa, &ch);             /* write status */

        /* byte 4 ACE byte 0 parameters (parity, stop bits, char len */
        ch = (uptr->ACE >> 24) & 0xff;          /* ACE byte 0 */
        chan_write_byte(chsa, &ch);             /* write status */

        /* byte 5 ACE byte 1 parameters (baud rate) */
        ch = (uptr->ACE >> 16) & 0xff;          /* ACE byte 1 */
        chan_write_byte(chsa, &ch);             /* write status */

        /* byte 6 ACE parameters (Firmware ID 0x62) */
        ch = 0x62;                              /* ACE IOP firmware byte 0 */
//      ch = 0x19;                              /* ACE MFP firmware byte 0 */
        chan_write_byte(chsa, &ch);             /* write status */

        /* byte 7 ACE parameters (Revision Level 0x4?) */
//      Firmware 0x44 supports RTS flow control */
//      Firmware 0x45 supports DCD modem control */
        ch = 0x43;                              /* ACE firmware byte 1 */
        chan_write_byte(chsa, &ch);             /* write status */

        ruptr->SNS &= ~SNS_RING;                /* reset ring attention status */
        ruptr->SNS &= ~SNS_MRING;               /* reset ring attention status */
        ruptr->SNS &= ~SNS_ASCIICD;             /* reset ASCII attention status */
        ruptr->SNS &= ~SNS_DELDSR;              /* reset attention status */
//MPX   ruptr->SNS &= ~SNS_RLSDS;               /* reset rec'd line signal detect */
        ruptr->SNS &= ~SNS_CMDREJ;              /* command reject */
/*MPX*/ ruptr->SNS &= ~SNS_DELTA;               /* reset attention status */

        wuptr->SNS &= ~SNS_RING;                /* reset ring attention status */
        wuptr->SNS &= ~SNS_MRING;               /* reset ring attention status */
        wuptr->SNS &= ~SNS_ASCIICD;             /* reset ASCII attention status */
        wuptr->SNS &= ~SNS_DELDSR;              /* reset attention status */
        wuptr->SNS &= ~SNS_CMDREJ;              /* command reject */
/*MPX*/ wuptr->SNS &= ~SNS_DELTA;               /* reset attention status */

        uptr->CMD &= LMASK;                     /* nothing left, command complete */
        sim_debug(DEBUG_CMD, dptr,
            "coml_startcmd CMD SENSE return chsa %04x: unit %02x Cmd Sense SNS %08x ACE %08x\n",
            chsa, unit, uptr->SNS, uptr->ACE);
        return SNS_CHNEND|SNS_DEVEND;           /* good return */
        break;

    case COM_DEFSC:     /* 0x0B */              /* Define special char */
        sim_debug(DEBUG_CMD, dptr,
            "coml_startcmd chsa %04x: Cmd %02x DEFSC\n", chsa, cmd);
        if (chan_read_byte(GET_UADDR(uptr->CMD), &ch)) {    /* read char from memory */
            /* nothing to read, error */
            return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK;   /* good return */
        }
        uptr->ACE &= ~ACE_WAKE;                 /* clear out old wake char */
        uptr->ACE |= ((uint32)ch << 8);         /* insert special char */
        ruptr->ACE = uptr->ACE;                 /* set special char in read unit */
        wuptr->ACE = uptr->ACE;                 /* set special char in write unit */
        sim_debug(DEBUG_CMD, dptr,
            "coml_startcmd chsa %04x: Cmd %02x DEFSC char %02x SNS %08x ACE %08x\n",
            chsa, cmd, ch, uptr->SNS, uptr->ACE);
        return SNS_CHNEND|SNS_DEVEND;           /* good return */
        break;

    case COM_RRTS:      /* 0x1B */              /* Reset RTS */
        sim_debug(DEBUG_CMD, dptr, "coml_startcmd chsa %04x: Cmd %02x RRTS\n", chsa, cmd);
        uptr->SNS &= ~SNS_RTS;                  /* Request to send not ready */
        return SNS_CHNEND|SNS_DEVEND;           /* good return */
        break;

    case COM_SRTS:      /* 0x1F */              /* Set RTS */
        sim_debug(DEBUG_CMD, dptr, "coml_startcmd chsa %04x: Cmd %02x SRTS\n", chsa, cmd);
        uptr->SNS |= SNS_RTS;                   /* Request to send ready */
        return SNS_CHNEND|SNS_DEVEND;           /* good return */
        break;

    case COM_RBRK:      /* 0x33 */              /* Reset BREAK */
        sim_debug(DEBUG_CMD, dptr, "coml_startcmd chsa %04x: Cmd %02x RBRK\n", chsa, cmd);
        uptr->SNS &= ~SNS_BREAK;                /* Request to send not ready */
        return SNS_CHNEND|SNS_DEVEND;           /* good return */
        break;

    case COM_SBRK:      /* 0x37 */              /* Set BREAK */
        sim_debug(DEBUG_CMD, dptr, "coml_startcmd chsa %04x: Cmd %02x SBRK\n", chsa, cmd);
        uptr->SNS |= SNS_BREAK;                 /* Requestd to send ready */
        return SNS_CHNEND|SNS_DEVEND;           /* good return */
        break;

    case COM_RDTR:      /* 0x13 */              /* Reset DTR (ADVR) */
        sim_debug(DEBUG_CMD, dptr, "coml_startcmd chsa %04x: Cmd %02x RDTR\n", chsa, cmd);
        uptr->SNS &= ~SNS_DTR;                  /* Data terminal not ready */
        return SNS_CHNEND|SNS_DEVEND;           /* good return */
        break;

    case COM_SDTR:      /* 0x17 */              /* Set DTR (ADVF) */
        sim_debug(DEBUG_CMD, dptr, "coml_startcmd chsa %04x: Cmd %02x SDTR\n", chsa, cmd);
        uptr->SNS |= SNS_DTR;                   /* Data terminal ready */
        return SNS_CHNEND|SNS_DEVEND;           /* good return */
        break;

    case COM_SACE:      /* 0xff */              /* Set ACE parameters (3 chars) */
        sim_debug(DEBUG_CMD, dptr,
            "coml_startcmd chsa %04x: Cmd %02x SACE\n", chsa, cmd);

        if (chan_read_byte(GET_UADDR(uptr->CMD), &ch)) {    /* read char 0 */
            /* nothing to read, error */
            return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK;   /* good return */
        }
        uptr->ACE = ((uint32)ch)<<24;           /* byte 0 of ACE data */
        sim_debug(DEBUG_CMD, dptr,
            "coml_startcmd chsa %04x: Cmd %02x ACE byte 0 %02x\n",
            chsa, cmd, ch);

        if (chan_read_byte(GET_UADDR(uptr->CMD), &ch)) {    /* read char 1 */
            /* nothing to read, error */
            return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK;   /* good return */
        }
        uptr->ACE |= ((uint32)ch)<<16;          /* byte 1 of ACE data */
        sim_debug(DEBUG_CMD, dptr,
            "coml_startcmd chsa %04x: Cmd %02x ACE byte 1 %02x\n",
            chsa, cmd, ch);

        if (chan_read_byte(GET_UADDR(uptr->CMD), &ch)) {    /* read char 2 */
            /* nothing to read, error */
            return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK;   /* good return */
        }
        uptr->ACE |= ((uint32)ch)<<8;           /* byte 2 of ACE data */
        uptr->SNS |= SNS_ACEDEF;                /* show ACE defined */
        if (uptr->SNS & SNS_CONN) {
            if (!(uptr->ACE & SNS_MRINGE)) {    /* see if RING enabled */
                uptr->SNS |= (SNS_DTR | SNS_RTS); /* set DTR & DSR if yes */
            }
        }
        ruptr->SNS |= SNS_RDY;                  /* status is online & ready */
        if (uptr == wuptr) {
            ruptr->ACE = uptr->ACE;             /* set ACE in read uptr */
            ruptr->SNS = uptr->SNS;             /* set status to read uptr */
        } else {
            wuptr->ACE = uptr->ACE;             /* set ACE in write uptr */
            wuptr->SNS = uptr->SNS;             /* set status to write uptr */
        }
        sim_debug(DEBUG_CMD, dptr,
            "coml_startcmd chsa %04x: Cmd %02x ACE byte 2 %02x\n",
            chsa, cmd, ch);
        sim_debug(DEBUG_CMD, dptr,
            "coml_startcmd ACE DONE chsa %04x: Cmd %02x ACE bytes %08x\n",
            chsa, cmd, uptr->ACE);

        uptr->CMD &= LMASK;                     /* nothing left, command complete */
        return SNS_CHNEND|SNS_DEVEND;           /* good return */
        break;

    /* Set transparent flow control mode */
    case COM_SETFLOW:   /* 0x53 */              /* Set flow control (3 chars) */
        sim_debug(DEBUG_CMD, dptr,
            "coml_startcmd chsa %04x: Cmd %02x SETFLOW\n", chsa, cmd);

        if (chan_read_byte(GET_UADDR(uptr->CMD), &ch)) { /* read char 0 */
            /* nothing to read, error */
            return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK;   /* good return */
        }
        fcb[0] = ch;                            /* byte 0 of Flow Cont data */
        sim_debug(DEBUG_CMD, dptr,
            "coml_startcmd chsa %04x: Cmd %02x SETFLOW byte 0 %02x\n",
            chsa, cmd, ch);

        if (chan_read_byte(GET_UADDR(uptr->CMD), &ch)) {    /* read char 1 */
            /* nothing to read, error */
            return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK;   /* good return */
        }
        fcb[1] = ch;                            /* byte 1 of Flow Cont data */
        sim_debug(DEBUG_CMD, dptr,
            "coml_startcmd chsa %04x: Cmd %02x SETFLOW byte 1 %02x\n",
            chsa, cmd, ch);

        if (chan_read_byte(GET_UADDR(uptr->CMD), &ch)) {    /* read char 2 */
            /* nothing to read, error */
            return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK;   /* good return */
        }
        fcb[2] = ch;                            /* byte 2 of Flow Cont data */
        ruptr->SNS |= SNS_RDY;                  /* status is online & ready */
        sim_debug(DEBUG_CMD, dptr,
            "coml_startcmd chsa %04x: Cmd %02x SETFLOW byte 2 %02x\n",
            chsa, cmd, ch);
        sim_debug(DEBUG_CMD, dptr,
            "coml_startcmd SETFLOW DONE chsa %04x: Cmd %02x FCB bytes %02x%02x%02x\n",
            chsa, cmd, fcb[0], fcb[1], fcb[2]);

        uptr->CMD &= LMASK;                     /* nothing left, command complete */
        return SNS_CHNEND|SNS_DEVEND;           /* good return */
        break;
    default:                                    /* invalid command */
        uptr->SNS |= SNS_CMDREJ;                /* command rejected */
        sim_debug(DEBUG_CMD, dptr,
            "coml_startcmd chsa %04x: Cmd Invalid %02x status %02x\n",
            chsa, cmd, uptr->u5);
        return SNS_CHNEND|SNS_DEVEND|STATUS_PCHK;   /* program check */
        break;
    }

    if (uptr->u5 & 0xff)
        return SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK;
    return SNS_CHNEND|SNS_DEVEND;
}

/* Unit service - polled input
   Poll for new connections
   Poll all connected lines for input
*/
t_stat comc_srv(UNIT *uptr)
{
    uint8   ch;
    DEVICE *dptr = get_dev(uptr);
    int32   newln, ln, c;
    uint16  chsa = GET_UADDR(coml_unit[0].CMD); /* get channel/sub-addr */

    /* see if comc attached */
    if ((com_unit[COMC].flags & UNIT_ATT) == 0){    /* attached? */
        return SCPE_OK;
    }
    /* poll for any input from com lines, units 0-7 */
    newln = tmxr_poll_conn(&com_desc);          /* look for connect */
    if (newln >= 0) {                           /* rcv enb pending? */
        uint16  chsa = GET_UADDR(coml_unit[newln].CMD); /* get read channel/sub-addr */
        uint16  wchsa = GET_UADDR(coml_unit[newln+8].CMD); /* get write channel/sub-addr */
        UNIT    *nuptr = coml_unit+newln;       /* get uptr for coml line */
        UNIT    *wuptr = coml_unit+newln+8;     /* get output uptr for coml line */
        com_ldsc[newln].rcve = 1;               /* enable rcv */
        com_ldsc[newln].xmte = 1;               /* enable xmt for output line */
        com_stat[newln] |= COML_RBP;            /* connected */
        com_stat[newln] &= ~COML_REP;           /* not pending */

        sim_debug(DEBUG_CMD, &com_dev,
            "comc_srv conn b4 wakeup on read chsa %04x line %02x SNS %08x ACE %08x\n",
            chsa, newln, nuptr->SNS, nuptr->ACE);
        sim_debug(DEBUG_CMD, &com_dev,
            "comc_srv conn b4 wakeup on write chsa %04x line %02x SNS %08x ACE %08x\n",
            wchsa, newln+8, wuptr->SNS, wuptr->ACE);

        /* send attention to OS here for this channel */
        /* set DSR, CTS and delta DSR status */
        nuptr->SNS |= SNS_CONN;                 /* status is now connected */
        /* UTX says this is an error if set, so do not set SNS_DELDSR */
/*MPX*/ nuptr->SNS |= (SNS_DSRS | SNS_CTSS | SNS_RING);  /* set the read bits */
        nuptr->SNS |= (SNS_RTS | SNS_DTR);      /* set RTS & DTR */
/*MPX*/ nuptr->SNS |= SNS_MRING;                /* set RING interrupt */
        if (nuptr->SNS & SNS_ACEDEF) {          /* ACE defined */
            /* this must be set to login for UTX after system is up */
/*UTX*/     nuptr->SNS |= SNS_DELDSR;           /* set delta dsr status */
            nuptr->SNS |= SNS_RLSDS;            /* set rec'd line signal detect */
        } else {
            nuptr->SNS |= SNS_DELDSR;           /* set delta dsr status */
            nuptr->SNS |= SNS_RLSDS;            /* set rec'd line signal detect */
        }
        nuptr->SNS &= ~SNS_CMDREJ;              /* no command reject */
        wuptr->SNS = nuptr->SNS;                /* set write line too */
        wuptr->ACE = nuptr->ACE;                /* set write line too */
        sim_debug(DEBUG_CMD, &com_dev,
            "comc_srv conn wakeup on chsa %04x line %02x SNS %08x ACE %08x\n",
            chsa, newln, nuptr->SNS, nuptr->ACE);
        set_devwake(chsa, SNS_ATTN|SNS_DEVEND|SNS_CHNEND);  /* tell user */
    }
    /* poll all devices for input */
    tmxr_poll_rx(&com_desc);                    /* poll for input */
    for (ln = 0; ln < COM_NUMLIN; ln++) {       /* loop thru lines */
        UNIT    *nuptr = coml_unit+ln;          /* get uptr for coml line */
        int     cmd = nuptr->CMD & 0xff;        /* get the active cmd */
        uint16  chsa = GET_UADDR(nuptr->CMD);   /* get channel/sub-addr */

        if (com_ldsc[ln].conn)                  /* connected? */
            sim_debug(DEBUG_DETAIL, &com_dev,
                "comc_srv conn poll input chsa %04x line %02x SNS %08x ACE %08x\n",
                chsa, ln, nuptr->SNS, nuptr->ACE);

        if ((com_ldsc[ln].conn) &&              /* connected? */
            (c = tmxr_getc_ln(&com_ldsc[ln]))) {    /* get char */
            ch = c & 0x7f;
            if (ch == '\n')                     /* convert newline */
                ch = '\r';                      /* to C/R */
            sim_debug(DEBUG_CMD, &com_dev,
                "comc_srv read %02x (%02x) chsa %04x line %02x SNS %08x ACE %08x CMD %08x\n",
                c, ch, chsa, ln, nuptr->SNS, nuptr->ACE, nuptr->CMD);
            /* tmxr says break is 0x80??, but SCPE_BREAK is 0x800000?? */
            if (c & SCPE_BREAK) {               /* break? */
                nuptr->SNS |= SNS_BREAK;        /* set received break bit */
                com_stat[ln] |= COML_RBP;       /* set rcv brk */
                set_devwake(chsa, SNS_ATTN|SNS_DEVEND|SNS_CHNEND);
                continue;
            }
            /* normal char */
            nuptr->SNS &= ~SNS_BREAK;           /* reset received break bit */
            com_stat[ln] &= ~COML_RBP;          /* clr rcv brk */

            /* convert to user requested input */
            ch = sim_tt_inpcvt(ch, TT_GET_MODE(coml_unit[ln].flags));
            com_rbuf[ln] = ch;                  /* save char */

            /* Special char detect? */
            if ((ch & 0x7f) == ((nuptr->ACE >> 8) & 0xff)) { /* is it spec char */
                nuptr->CMD |= COM_SCD;          /* set special char detected */
                nuptr->SNS |= SNS_SPCLCD;       /* set special char detected */
//              nuptr->SNS |= SNS_RLSDS;        /* set rec'd line signal detect */
                nuptr->SNS |= SNS_RING;         /* set ring attention status */
                sim_debug(DEBUG_CMD, &com_dev,
                    "comc_srv user ACE wakeup on chsa %04x line %02x cmd %02x SNS %08x ACE %08x\n",
                    chsa, ln, cmd, nuptr->SNS, nuptr->ACE);
                set_devwake(chsa, SNS_ATTN|SNS_DEVEND|SNS_CHNEND);
                continue;
            }

            /* put char in buffer */
            com_data[ln].ibuff[com_data[ln].incnt++] = ch;

            /* see if at max, if so reset to start */
            if (com_data[ln].incnt >= sizeof(com_data[ln].ibuff))
                com_data[ln].incnt = 0;         /* reset buffer cnt */

            nuptr->CMD |= COM_INPUT;            /* we have a char available */
            sim_debug(DEBUG_CMD, dptr,
                "comc_srv readch ln %02x: CMD %08x read %02x CNT %02x incnt %02x c %04x\n",
                ln, nuptr->CMD, ch, nuptr->CNT, com_data[ln].incnt, c);
        }
        else                                    /* end if conn */
        /* if we were connected and not now, reset serial line */
        if ((nuptr->SNS & SNS_CONN) && (com_ldsc[ln].conn == 0)) {
            UNIT    *wuptr = coml_unit+ln+8;    /* get output uptr for coml line */
            sim_debug(DEBUG_CMD, &com_dev,
                "comc_srv disconnect on chsa %04x line %02x cmd %02x SNS %08x ACE %08x\n",
                chsa, ln, cmd, nuptr->SNS, nuptr->ACE);
            com_ldsc[ln].rcve = 0;              /* disable rcv */
            com_ldsc[ln].xmte = 0;              /* disable xmt for output line */
            com_stat[ln] &= ~COML_RBP;          /* disconnected */
            com_stat[ln] |= COML_REP;           /* set pending */
            nuptr->SNS &= ~(SNS_RTS | SNS_DTR); /* reset RTS & DTR */
            nuptr->SNS &= ~(SNS_DSRS);          /* status is not connected */
            nuptr->SNS |= (SNS_DELDSR);         /* status is not connected */
            nuptr->SNS |= (SNS_DELTA);          /* status is not connected */
            nuptr->SNS &= ~(SNS_RDY|SNS_CONN);  /* status is not connected */
            wuptr->SNS = nuptr->SNS;            /* set write channel too */
            set_devwake(chsa, SNS_ATTN|SNS_DEVEND|SNS_CHNEND);
        }
    }                                           /* end for */

    sim_debug(DEBUG_DETAIL, &com_dev,
        "comc_srv POLL DONE on chsa %04x\n", chsa);
    return sim_clock_coschedule(uptr, 5000);    /* continue poll */
}

/* Unit service - input transfers */
t_stat comi_srv(UNIT *uptr)
{
    DEVICE *dptr = get_dev(uptr);
    uint16  chsa = GET_UADDR(uptr->CMD);        /* get channel/sub-addr */
    uint32  ln = (uptr - coml_unit) & 0x7;      /* use line # 0-7 for 8-15 */
    CHANP   *chp = find_chanp_ptr(chsa);        /* find the chanp pointer */
    int     cmd = uptr->CMD & 0xff;             /* get active cmd */
    uint8   ch, cc;

    /* handle NOP and INCH cmds */
    sim_debug(DEBUG_CMD, dptr,
        "comi_srv entry chsa %04x line %04x cmd %02x conn %x rcve %x xmte %x SNS %08x\n",
        chsa, ln, cmd, com_ldsc[ln].conn, com_ldsc[ln].rcve, com_ldsc[ln].xmte, uptr->SNS);

    if (com_ldsc[ln].conn) {                    /* connected? */
        if ((uptr->CNT != com_data[ln].incnt) ||    /* input empty */
            (uptr->CMD & COM_INPUT)) {          /* input waiting? */
            ch = com_data[ln].ibuff[uptr->CNT]; /* get char from read buffer */
            sim_debug(DEBUG_CMD, dptr,
                "com_srvi readbuf unit %02x: CMD %08x read %02x incnt %02x CNT %02x len %02x\n",
                ln, uptr->CMD, ch, com_data[ln].incnt, uptr->CNT, chp->ccw_count);

            if (uptr->CNT != com_data[ln].incnt) { /* input available */
                /* process any characters */
                if (ch == 0x7f)                     /* if rub out, make backspace */
                    ch = 0x08;
                /* this fixes mpx1x time entry on startup */
                if (uptr->CMD & COM_EKO) {      /* ECHO requested */
                    /* echo the char out */
                    /* convert to user requested output */
                    sim_debug(DEBUG_CMD, &com_dev,
                        "comi_srv echo char %02x on chsa %04x line %02x cmd %02x ACE %08x\n",
                        ch, chsa, ln, cmd, uptr->ACE);
//                  cc = sim_tt_outcvt(c, TT_GET_MODE(coml_unit[ln].flags));
                    if (ch != 0x08 && ch != 0x7f) {
                        tmxr_putc_ln(&com_ldsc[ln], ch); /* output char */
                        tmxr_poll_tx(&com_desc);    /* poll xmt to send */
                    }
                }
                if (chan_write_byte(chsa, &ch)) {   /* write byte to memory */
                    /* write error */
                    cmd = 0;                    /* no cmd now */
                    sim_debug(DEBUG_CMD, dptr,
                        "comi_srv write error ln %02x: CMD %08x read %02x CNT %02x ccw_count %02x\n",
                        ln, uptr->CMD, ch, uptr->CNT, chp->ccw_count);
                    uptr->CMD &= ~COM_MSK;      /* remove old CMD */
                    uptr->CMD &= ~COM_INPUT;    /* input waiting? */
                    chan_end(chsa, SNS_CHNEND|SNS_DEVEND);  /* we done */
                    return SCPE_OK;
                }
                /* character accepted, bump buffer pointer */
                uptr->CNT++;                    /* next char position */

                sim_debug(DEBUG_CMD, dptr,
                    "comi_srv write to mem line %02x: CMD %08x read %02x CNT %02x incnt %02x\n",
                     ln, uptr->CMD, ch, uptr->CNT, com_data[ln].incnt);

                /* see if at end of buffer */
                if (uptr->CNT >= (int32)sizeof(com_data[ln].ibuff))
                    uptr->CNT = 0;              /* reset pointer */

                cc = ch & 0x7f;                 /* clear parity bit */
                /* Special char detected? (7 bit read only) */
                if (cc == ((uptr->ACE >> 8) & 0xff)) { /* is it spec char */
                    uptr->SNS |= SNS_SPCLCD;    /* set special char detected */
                    sim_debug(DEBUG_CMD, &com_dev,
                        "comi_srv user ACE %02x wakeup on chsa %04x line %02x cmd %02x ACE %08x\n",
                        cc, chsa, ln, cmd, uptr->ACE);
                    uptr->CMD &= LMASK;         /* nothing left, command complete */
                    sim_debug(DEBUG_CMD, dptr,
                        "comi_srv read done chsa %04x ln %04x: chnend|devend\n", chsa, ln);
                    chan_end(chsa, SNS_CHNEND|SNS_DEVEND);  /* done */
                    return SCPE_OK;
                }

                /* ASCII control char (7 bit read only) */
                /* see if control char detected */
                if (uptr->CMD & 0x40) {         /* is ASCII ctrl char test bit set */
                    if (((cc & 0x60) == 0) || (cc == 0x7f)) {
                        uptr->SNS |= SNS_ASCIICD;   /* ASCII ctrl char detected */
                        sim_debug(DEBUG_CMD, &com_dev,
                    "comi_srv user ASCII %02x wakeup on chsa %04x line %02x cmd %02x ACE %08x\n",
                            cc, chsa, ln, cmd, uptr->ACE);
                        uptr->CMD &= LMASK;     /* nothing left, command complete */
                        sim_debug(DEBUG_CMD, dptr,
                            "comi_srv read CC done chsa %04x ln %04x: chnend|devend\n", chsa, ln);
                        chan_end(chsa, SNS_CHNEND|SNS_DEVEND);  /* done */
                        return SCPE_OK;
                    }
                }

                /* user want more data? */
                if ((test_write_byte_end(chsa)) == 0) {
                    sim_debug(DEBUG_CMD, dptr,
                        "comi_srv need more line %02x CMD %08x CNT %02x ccw_count %02x incnt %02x\n",
                        ln, uptr->CMD, uptr->CNT, chp->ccw_count, com_data[ln].incnt);
                    /* user wants more, look next time */
                    if (uptr->CNT == com_data[ln].incnt) {  /* input empty */
                        uptr->CMD &= ~COM_INPUT;    /* no input available */
                    }
                    sim_activate(uptr, uptr->wait); /* wait */
                    return SCPE_OK;
                }
                /* command is completed */
                sim_debug(DEBUG_CMD, dptr,
                    "comi_srv read done line %02x CMD %08x read %02x CNT %02x ccw_count %02x incnt %02x\n",
                    ln, uptr->CMD, ch, uptr->CNT, chp->ccw_count, com_data[ln].incnt);
                uptr->CMD &= LMASK;             /* nothing left, command complete */
                if (uptr->CNT != com_data[ln].incnt) {  /* input empty */
                    uptr->CMD |= COM_INPUT;     /* input still available */
                }
                chan_end(chsa, SNS_CHNEND|SNS_DEVEND);  /* we done */
            }
        }
        sim_clock_coschedule(uptr, 1000);       /* continue poll */
        return SCPE_OK;
    }
    /* not connected, so dump chars on ground */
    uptr->CNT = 0;                              /* no input count */
    com_data[ln].incnt = 0;                     /* no input data */
    uptr->CMD &= LMASK;                         /* nothing left, command complete */
    uptr->SNS |= 0x00003003;                    /* status is online & ready */
    uptr->SNS &= SNS_DSRS;                      /* reset DSR */
    uptr->SNS |= SNS_DELDSR;                    /* give change status */
    uptr->SNS |= SNS_MRING;                     /* give RING status */
    sim_debug(DEBUG_CMD, dptr,
        "comi_srv read dump DONE line %04x status %04x cmd %02x SNS %08x\n",
        ln, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK, cmd, uptr->SNS);
    /* if line active, abort cmd */
    chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);   /* error return */
    return SCPE_OK;
}

/* Unit service - output transfers */
t_stat como_srv(UNIT *uptr)
{
    DEVICE *dptr = get_dev(uptr);
    uint16  chsa = GET_UADDR(uptr->CMD);        /* get channel/sub-addr */
    uint32  ln = (uptr - coml_unit) & 0x7;      /* use line # 0-7 for 8-15 */
    UNIT    *ruptr = &dptr->units[ln&7];        /* read uptr */
    uint32  done;
    int     cmd = uptr->CMD & 0xff;             /* get active cmd */
    uint8   ch;

    sim_debug(DEBUG_CMD, dptr,
        "como_srv entry chsa %04x line %04x cmd %02x conn %x rcve %x xmte %x\n",
        chsa, ln, cmd, com_ldsc[ln].conn, com_ldsc[ln].rcve, com_ldsc[ln].xmte);

    if (com_dev.flags & DEV_DIS) {              /* disabled */
        sim_debug(DEBUG_CMD, dptr,
            "como_srv chsa %04x line %02x SNS %08x DEV_DIS set\n", chsa, ln, uptr->SNS);
            sim_debug(DEBUG_CMD, dptr,
                "como_srv Write forced DONE %04x status %04x\n",
                ln, SNS_CHNEND|SNS_DEVEND);
            uptr->CMD &= LMASK;                 /* nothing left, command complete */
            ruptr->SNS &= SNS_DSRS;             /* reset DSR */
            ruptr->SNS |= SNS_DELDSR;           /* give change status */
            chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);   /* error return */
            return SCPE_OK;                     /* return */
    }

    /* handle NOP and INCH cmds */
    if (cmd == COM_NOP || cmd == 0x7f) {        /* check for NOP or INCH */
        uptr->CMD &= LMASK;                     /* leave only chsa */
        sim_debug(DEBUG_CMD, &com_dev,
            "como_srv NOP or INCH done chsa %04x line %04x cmd %02x\n",
            chsa, ln, cmd);
        chan_end(chsa, SNS_CHNEND|SNS_DEVEND);  /* done */
        return SCPE_OK;                         /* return */
    }

    /* handle SACE, 3 char already read, so we are done */
    if (cmd == COM_SACE) {                      /* check for SACE 0xff */
        uptr->CMD &= LMASK;                     /* leave only chsa */
        sim_debug(DEBUG_CMD, &com_dev,
            "como_srv SACE done chsa %04x line %02x cmd %02x ACE %08x\n",
            chsa, ln, cmd, uptr->ACE);
        chan_end(chsa, SNS_CHNEND|SNS_DEVEND);  /* done */
        return SCPE_OK;                         /* return */
    }

    if (cmd == 0)
        /* all done, so stop polling */
        return SCPE_OK;

    if (com_ldsc[ln].conn == 0) {               /* connected? */
        /* not connected, so dump char on ground */
        sim_debug(DEBUG_CMD, dptr,
            "como_srv write dump DONE line %04x status %04x cmd %02x\n",
            ln, SNS_CHNEND|SNS_DEVEND, cmd);
        uptr->CMD &= LMASK;                     /* nothing left, command complete */

        uptr->SNS |= 0x00003003;                /* status is online & ready */
        ruptr->SNS &= SNS_DSRS;                 /* reset DSR */
        ruptr->SNS |= SNS_DELDSR;               /* give change status */
        uptr->SNS |= SNS_MRING;                 /* give RING status */
        /* if line not active, abort cmd */
        chan_end(chsa, SNS_CHNEND|SNS_DEVEND|SNS_UNITCHK);   /* error return */
        return SCPE_OK;
    }

    sim_debug(DEBUG_CMD, dptr,
        "como_srv entry 1 chsa %04x line %04x cmd %02x\n", chsa, ln, cmd);
    /* get a user byte from memory */
doagain:
    done = chan_read_byte(chsa, &ch);           /* get byte from memory */
    if (done) {
        uptr->CMD &= LMASK;                     /* leave only chsa */
        sim_debug(DEBUG_CMD, dptr,
            "como_srv Write DONE %01x chsa %04x line %04x status %04x\n",
            done, chsa, ln, SNS_CHNEND|SNS_DEVEND);
        tmxr_poll_tx(&com_desc);                /* send out data */
        chan_end(chsa, SNS_CHNEND|SNS_DEVEND);  /* done */
        return SCPE_OK;                         /* return */
    }

    /* not done */
    sim_debug(DEBUG_DETAIL, dptr,
        "como_srv poll chsa %04x line %02x SNS %08x ACE %08x\n",
        chsa, ln, uptr->SNS, uptr->ACE);

    /* convert to user requested output */
    ch = sim_tt_outcvt(ch, TT_GET_MODE(coml_unit[ln].flags));
    /* send the next char out */
    tmxr_putc_ln(&com_ldsc[ln], ch);            /* output char */
    sim_debug(DEBUG_CMD, dptr,
        "como_srv writing char 0x%02x to ln %04x\n", ch, ln);
    goto doagain;                               /* keep going */
}

/* haltxio routine */
t_stat coml_haltio(UNIT *uptr) {
    uint16  chsa = GET_UADDR(uptr->CMD);
    int     cmd = uptr->CMD & COM_MSK;
    int     unit = (uptr - coml_unit);          /* unit # 0 is read, 1 is write */
    CHANP   *chp = find_chanp_ptr(chsa);        /* find the chanp pointer */

    sim_debug(DEBUG_EXP, &com_dev, "coml_haltio enter chsa %04x cmd = %02x\n", chsa, cmd);

    /* terminate any input command */
    /* UTX wants SLI bit, but no unit exception */
    /* status must not have an error bit set */
    /* otherwise, UTX will panic with "bad status" */
    if ((uptr->CMD & COM_MSK) != 0) {           /* is unit busy */
        sim_debug(DEBUG_CMD, &coml_dev,
            "coml_haltio HIO chsa %04x cmd = %02x ccw_count %02x\n", chsa, cmd, chp->ccw_count);
        /* stop any I/O and post status and return error status */
        chp->ccw_count = 0;                     /* zero the count */
        chp->ccw_flags &= ~(FLAG_DC|FLAG_CC);   /* reset chaining bits */
        uptr->CMD &= LMASK;                     /* make non-busy */
        uptr->CNT = 0;                          /* no I/O yet */
        com_data[unit].incnt = 0;               /* no input data */
        sim_cancel(uptr);                       /* stop timer */
        sim_debug(DEBUG_CMD, &coml_dev,
            "coml_haltio HIO I/O stop chsa %04x cmd = %02x\n", chsa, cmd);
        chan_end(chsa, SNS_CHNEND|SNS_DEVEND);  /* force end */
        return 1;                               /* tell chan code to post status */
    }
    uptr->CNT = 0;                              /* no I/O yet */
    com_data[unit].incnt = 0;                   /* no input data */
    uptr->CMD &= LMASK;                         /* make non-busy */
    return SCPE_OK;                             /* not busy */
}

/* Reset routine */
t_stat com_reset (DEVICE *dptr)
{
    int32 i;

    if (com_dev.flags & DEV_DIS)                /* master disabled? */
        com_dev.flags |= DEV_DIS;               /* disable lines */
    else
        com_dev.flags &= ~DEV_DIS;
    if (com_unit[COMC].flags & UNIT_ATT)        /* master att? */
        sim_clock_coschedule(&com_unit[0], 200);    /* activate */
    for (i = 0; i < COM_LINES; i++)             /* reset lines */
        com_reset_ln(i);
    return SCPE_OK;
}


/* attach master unit */
t_stat com_attach(UNIT *uptr, CONST char *cptr)
{
    DEVICE  *dptr = get_dev(uptr);
    t_stat  r;

    r = tmxr_attach(&com_desc, uptr, cptr);     /* attach */
    if (r != SCPE_OK)                           /* error? */
        return r;                               /* return error */
    sim_debug(DEBUG_CMD, dptr, "com_srv comc is now attached\n");
    sim_activate(uptr, 100);                    /* start poll at once */
    return SCPE_OK;
}

/* detach master unit */
t_stat com_detach(UNIT *uptr)
{
    int32 i;
    t_stat r;

    r = tmxr_detach(&com_desc, uptr);           /* detach */
    for (i = 0; i < COM_LINES; i++)             /* disable rcv */
        com_reset_ln(i);                        /* reset the line */
    sim_cancel(uptr);                           /* stop poll, cancel timer */
    return r;
}

/* Reset an individual line */
void com_reset_ln (int32 ln)
{
    sim_cancel(&coml_unit[ln]);
    com_stat[ln] = 0;
    com_stat[ln] |= COML_REP;                   /* set pending */
    com_rbuf[ln] = 0;                           /* clear read buffer */
    com_xbuf[ln] = 0;                           /* clear write buffer */
    com_ldsc[ln].rcve = 0;
    com_ldsc[ln].xmte = 0;
    coml_unit[ln].CNT = 0;                      /* no input count */
    com_data[ln].incnt = 0;                     /* no input data */
    return;
}

t_stat com_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
fprintf (st, "SEL32 8512 8-Line Async Controller Terminal Interfaces\n\n");
fprintf (st, "Terminals perform input and output through Telnet sessions connected to a \n");
fprintf (st, "user-specified port.\n\n");
fprintf (st, "The ATTACH command specifies the port to be used:\n\n");
tmxr_attach_help (st, dptr, uptr, flag, cptr);
fprintf (st, "The additional terminals can be set to one of four modes: UC, 7P, 7B, or 8B.\n\n");
fprintf (st, "  mode  input characters        output characters\n\n");
fprintf (st, "  UC    lower case converted    lower case converted to upper case,\n");
fprintf (st, "        to upper case,          high-order bit cleared,\n");
fprintf (st, "        high-order bit cleared  non-printing characters suppressed\n");
fprintf (st, "  7P    high-order bit cleared  high-order bit cleared,\n");
fprintf (st, "                                non-printing characters suppressed\n");
fprintf (st, "  7B    high-order bit cleared  high-order bit cleared\n");
fprintf (st, "  8B    no changes              no changes\n\n");
fprintf (st, "The default mode is 7P.  In addition, each line can be configured to\n");
fprintf (st, "behave as though it was attached to a dataset, or hardwired to a terminal:\n\n");
fprintf (st, "   sim> SET COMLn DATASET        simulate attachment to a dataset (modem)\n");
fprintf (st, "   sim> SET COMLn NODATASET      simulate direct attachment to a terminal\n\n");
fprintf (st, "Finally, each line supports output logging.  The SET COMLn LOG command enables\n");
fprintf (st, "logging on a line:\n\n");
fprintf (st, "   sim> SET COMLn LOG=filename   log output of line n to filename\n\n");
fprintf (st, "The SET COMLn NOLOG command disables logging and closes the open log file,\n");
fprintf (st, "if any.\n\n");
fprintf (st, "Once DCI is attached and the simulator is running, the terminals listen for\n");
fprintf (st, "connections on the specified port.  They assume that the incoming connections\n");
fprintf (st, "are Telnet connections.  The connections remain open until disconnected either\n");
fprintf (st, "by the Telnet client, a SET DCI DISCONNECT command, or a DETACH DCI command.\n\n");
fprintf (st, "Other special commands:\n\n");
fprintf (st, "   sim> SHOW COMC CONNECTIONS    show current connections\n");
fprintf (st, "   sim> SHOW COMC STATISTICS     show statistics for active connections\n");
fprintf (st, "   sim> SET COMLn DISCONNECT     disconnects the specified line.\n");
fprintf (st, "\nThe additional terminals do not support save and restore.  All open connections\n");
fprintf (st, "are lost when the simulator shuts down or DCI is detached.\n");
    fprint_set_help (st, dptr);
    fprint_show_help (st, dptr);
    return SCPE_OK;
}

/* description of controller */
const char *com_description (DEVICE *dptr)
{
    return "SEL-32 8512 8-Line async communications controller";
}

#endif
