/* pdp11_dmc.c: DMC11 Emulation
  ------------------------------------------------------------------------------

   Copyright (c) 2011, Robert M. A. Jarratt

   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
   THE AUTHOR 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 the author shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from the author.

  ------------------------------------------------------------------------------

  Modification history:

  23-Jan-13  RJ   Clock co-scheduling move to generic framework (from Mark Pizzolato)
  21-Jan-13  RJ   Added help.
  15-Jan-13  RJ   Contribution from Paul Koning:
  
                  Support for RSTS using the ROM INPUT (ROM I) command to get
                  the DMC11 to report DSR status.

                  Don't accept any data from the peer until a buffer has been
                  made available.

                  Also added shadow CSRs. The code was using the CSRs to check
                  the command being executed, but the driver could end up
                  changing the bits, so a shadow set is used to do this.
  ------------------------------------------------------------------------------


I/O is done through sockets so that the remote system can be on the same host machine. The device starts polling for
incoming connections when it receives its first read buffer. The device opens the connection for writing when it receives
the first write buffer.

Transmit and receive buffers are added to their respective queues and the polling method in dmc_svc() checks for input
and sends any output.

On the wire the format is a 2-byte block length followed by that number of bytes. Some of the diagnostics expect to receive
the same number of bytes in a buffer as were sent by the other end. Using sockets without a block length can cause the
buffers to coalesce and then the buffer lengths in the diagnostics fail. The block length is always sent in network byte
order.

Tested with two diagnostics. To run the diagnostics set the default directory to SYS$MAINTENANCE, run ESSAA and then
configure it for the DMC-11 with the following commands:

The above commands can be put into a COM file in SYS$MAINTENANCE (works on VMS 3.0 but not 4.6, not sure why).

ATT DW780 SBI DW0 3 4
ATT DMC11 DW0 XMA0 760070 300 5
SELECT XMA0
(if putting these into a COM file to be executed by ESSAA add a "DS> " prefix)


The first is EVDCA which takes no parameters. Invoke it with the command R EVDCA. This diagnostic uses the DMC-11 loopback
functionality and the transmit port is not used when LU LOOP is enabled. Seems to work only under later versions of VMS
such as 4.6, does not work on 3.0.

The second is EVDMC, invoke this with the command R EVDMC. For this I used the following commands inside the diagnostic:

RUN MODE=TRAN on one machine
RUN MODE=REC on the other (unless the one instance is configured with the ports looping back).

You can add /PASS=n to the above commands to get the diagnostic to send and receive more buffers.

The other test was to configure DECnet on VMS 4.6 and do SET HOST.
*/

// TODO: Avoid need for manifests and newest runtime, compile with 2003
// TODO: Investigate line number and set parameters at the unit level (?)
// TODO: Multipoint. In this case perhaps don't need transmit port, allow all lines to connect to port on control node.
// TODO: Show active connections like DZ does, for multipoint.
// TODO: Test MOP.
// TODO: Implement actual DDCMP protocol and run over UDP.
// TODO: Allow NCP SHOW COUNTERS to work (think this is the base address thing). Since fixing how I get the addresses this should work now.

#include <time.h>
#include <ctype.h>

#include "pdp11_dmc.h"

#define POLL 1000
#define TRACE_BYTES_PER_LINE 16

struct csrs {
    uint16 sel0;
    uint16 sel2;
    uint16 sel4;
    uint16 sel6;
};

typedef struct csrs CSRS;

typedef enum
{
    Initialised, /* after MASTER CLEAR */
    Running      /* after any transmit or receive buffer has been supplied */
} ControllerState;

typedef enum
{ 
    Idle,
    InputTransfer,
    OutputTransfer
} TransferState;

typedef enum
{
    Available, /* when empty or partially filled on read */
    ContainsData,
    TransferInProgress
} BufferState;

typedef struct
{
    int32 isPrimary;
    SOCKET socket; // socket used bidirectionally
    int receive_readable;
    char *receive_port;
    int transmit_writeable;
    char peer[CBUFSIZE];
    int transmit_is_loopback; /* if true the transmit socket is the loopback to the receive */
    int32 speed; /* bits per second in each direction, 0 for no limit */
    int last_second;
    int bytes_sent_in_last_second;
    int bytes_received_in_last_second;
    time_t last_connect_attempt;
} LINE;

/* A partially filled buffer (during a read from the socket) will have block_len_bytes_read = 1 or actual_bytes_transferred < actual_block_len */
typedef struct buffer
{
    uint32 address;           /* unibus address of the buffer */
    uint16 count;             /* size of the buffer passed to the device by the driver */
    uint16 actual_block_len;  /* actual length of the received block */
    uint8 *transfer_buffer;   /* the buffer into which data is received or from which it is transmitted*/
    int block_len_bytes_read; /* the number of bytes read so far for the block length */
    int actual_bytes_transferred;    /* the number of bytes from the actual block that have been read or written so far*/
    struct buffer *next;      /* next buffer in the queue */
    BufferState state;        /* state of this buffer */
    int is_loopback;          /* loopback was requested when this buffer was queued */
} BUFFER;

typedef struct
{
    char * name;
    BUFFER queue[BUFFER_QUEUE_SIZE];
    int head;
    int tail;
    int count;
    struct dmc_controller *controller; /* back pointer to the containing controller */
} BUFFER_QUEUE;

typedef struct
{
    int started;
    clock_t start_time;
    clock_t cumulative_time;
} TIMER;

typedef struct
{
    TIMER between_polls_timer;
    TIMER poll_timer;
    uint32 poll_count;

} UNIT_STATS;

typedef enum
{
    DMC,
    DMR,
    DMP
} DEVTYPE;

struct dmc_controller {
    CSRS *csrs;
    CSRS *shadow_csrs;
    DEVICE *device;
    ControllerState state;
    TransferState transfer_state; /* current transfer state (type of transfer) */
    int transfer_type;
    int transfer_in_io; // remembers IN I/O setting at start of input transfer as host changes it during transfer!
    LINE *line;
    BUFFER_QUEUE *receive_queue;
    BUFFER_QUEUE *transmit_queue;
    UNIT_STATS *stats;
    SOCKET master_socket;
    int32 connect_poll_interval;
    DEVTYPE dev_type;
    uint32 rxi;
    uint32 txi;
    uint32 buffers_received_from_net;
    uint32 buffers_transmitted_to_net;
    uint32 receive_buffer_output_transfers_completed;
    uint32 transmit_buffer_output_transfers_completed;
    uint32 receive_buffer_input_transfers_completed;
    uint32 transmit_buffer_input_transfers_completed;
};

typedef struct dmc_controller CTLR;

t_stat dmc_rd(int32* data, int32 PA, int32 access);
t_stat dmc_wr(int32  data, int32 PA, int32 access);
t_stat dmc_svc(UNIT * uptr);
t_stat dmc_reset (DEVICE * dptr);
t_stat dmc_attach (UNIT * uptr, char * cptr);
int dmc_isattached(CTLR *controller);
t_stat dmc_detach (UNIT * uptr);
int32 dmc_rxint (void);
int32 dmc_txint (void);
t_stat dmc_setpeer (UNIT* uptr, int32 val, char* cptr, void* desc);
t_stat dmc_showpeer (FILE* st, UNIT* uptr, int32 val, void* desc);
t_stat dmc_setspeed (UNIT* uptr, int32 val, char* cptr, void* desc);
t_stat dmc_showspeed (FILE* st, UNIT* uptr, int32 val, void* desc);
t_stat dmc_settype (UNIT* uptr, int32 val, char* cptr, void* desc);
t_stat dmc_showtype (FILE* st, UNIT* uptr, int32 val, void* desc);
t_stat dmc_setstats (UNIT* uptr, int32 val, char* cptr, void* desc);
t_stat dmc_showstats (FILE* st, UNIT* uptr, int32 val, void* desc);
t_stat dmc_setconnectpoll (UNIT* uptr, int32 val, char* cptr, void* desc);
t_stat dmc_showconnectpoll (FILE* st, UNIT* uptr, int32 val, void* desc);
t_stat dmc_setlinemode (UNIT* uptr, int32 val, char* cptr, void* desc);
t_stat dmc_showlinemode (FILE* st, UNIT* uptr, int32 val, void* desc);
t_stat dmc_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
t_stat dmc_help_attach (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr);
int dmc_is_dmc(CTLR *controller);
int dmc_is_rqi_set(CTLR *controller);
int dmc_is_rdyi_set(CTLR *controller);
int dmc_is_iei_set(CTLR *controller);
int dmc_is_ieo_set(CTLR *controller);
void dmc_process_command(CTLR *controller);
int dmc_buffer_fill_receive_buffers(CTLR *controller);
void dmc_start_transfer_receive_buffer(CTLR *controller);
int dmc_buffer_send_transmit_buffers(CTLR *controller);
void dmc_buffer_queue_init(CTLR *controller, BUFFER_QUEUE *q, char *name);
void dmc_buffer_queue_init_all(CTLR *controller);
BUFFER *dmc_buffer_queue_head(BUFFER_QUEUE *q);
int dmc_buffer_queue_full(BUFFER_QUEUE *q);
void dmc_buffer_queue_get_stats(BUFFER_QUEUE *q, int *available, int *contains_data, int *transfer_in_progress);
void dmc_start_transfer_transmit_buffer(CTLR *controller);
void dmc_error_and_close_socket(CTLR *controller, char *format);
void dmc_close_socket(CTLR *controller, char *reason);
void dmc_close_receive(CTLR *controller, char *reason, char *from);
void dmc_close_transmit(CTLR *controller, char *reason);
int dmc_get_socket(CTLR *controller, int forRead);
int dmc_get_receive_socket(CTLR *controller, int forRead);
int dmc_get_transmit_socket(CTLR *controller, int is_loopback, int forRead);
void dmc_line_update_speed_stats(LINE *line);

DEBTAB dmc_debug[] = {
    {"TRACE",  DBG_TRC},
    {"WARN",   DBG_WRN},
    {"REG",    DBG_REG},
    {"INFO",   DBG_INF},
    {"DATA",   DBG_DAT},
    {"DATASUM",DBG_DTS},
    {"SOCKET", DBG_SOK},
    {"CONNECT", DBG_CON},
    {0}
};

UNIT dmc0_unit = { UDATA (&dmc_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 0) };
UNIT dmc1_unit = { UDATA (&dmc_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 0) };
UNIT dmc2_unit = { UDATA (&dmc_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 0) };
UNIT dmc3_unit = { UDATA (&dmc_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 0) };

UNIT dmpa_unit = { UDATA (&dmc_svc, UNIT_IDLE|UNIT_ATTABLE|UNIT_DISABLE, 0) };

CSRS dmc_csrs[DMC_NUMDEVICE];
CSRS dmc_shadow_csrs[DMC_NUMDEVICE];

CSRS dmp_csrs[DMP_NUMDEVICE];
CSRS dmp_shadow_csrs[DMP_NUMDEVICE];

LINE dmc_line[DMC_NUMDEVICE] =
{
    { 0, INVALID_SOCKET },
    { 0, INVALID_SOCKET },
    { 0, INVALID_SOCKET },
    { 0, INVALID_SOCKET }
};

BUFFER_QUEUE dmc_receive_queues[DMC_NUMDEVICE];
BUFFER_QUEUE dmc_transmit_queues[DMC_NUMDEVICE];

LINE dmp_line[DMP_NUMDEVICE] =
{
    { 0, INVALID_SOCKET }
};

BUFFER_QUEUE dmp_receive_queues[DMP_NUMDEVICE];
BUFFER_QUEUE dmp_transmit_queues[DMP_NUMDEVICE];

UNIT_STATS dmc_stats[DMC_NUMDEVICE];
UNIT_STATS dmp_stats[DMP_NUMDEVICE];

REG dmca_reg[] = {
    { HRDATA (SEL0, dmc_csrs[0].sel0, 16) },
    { HRDATA (SEL2, dmc_csrs[0].sel2, 16) },
    { HRDATA (SEL4, dmc_csrs[0].sel4, 16) },
    { HRDATA (SEL6, dmc_csrs[0].sel6, 16) },
    { GRDATA (BSEL0, dmc_csrs[0].sel0, DEV_RDX, 8, 0) },
    { GRDATA (BSEL1, dmc_csrs[0].sel0, DEV_RDX, 8, 8) },
    { GRDATA (BSEL2, dmc_csrs[0].sel2, DEV_RDX, 8, 0) },
    { GRDATA (BSEL3, dmc_csrs[0].sel2, DEV_RDX, 8, 8) },
    { GRDATA (BSEL4, dmc_csrs[0].sel4, DEV_RDX, 8, 0) },
    { GRDATA (BSEL5, dmc_csrs[0].sel4, DEV_RDX, 8, 8) },
    { GRDATA (BSEL6, dmc_csrs[0].sel6, DEV_RDX, 8, 0) },
    { GRDATA (BSEL7, dmc_csrs[0].sel6, DEV_RDX, 8, 8) },
    { HRDATA (SHADOWSEL0, dmc_shadow_csrs[0].sel0, 16), REG_HRO },
    { HRDATA (SHADOWSEL2, dmc_shadow_csrs[0].sel2, 16), REG_HRO },
    { HRDATA (SHADOWSEL4, dmc_shadow_csrs[0].sel4, 16), REG_HRO },
    { HRDATA (SHADOWSEL6, dmc_shadow_csrs[0].sel6, 16), REG_HRO },
    { BRDATA (PEER, dmc_line[0].peer, 16, 8, sizeof(dmc_line[0].peer)), REG_HRO},
    { HRDATA (MODE, dmc_line[0].isPrimary, 32), REG_HRO },
    { HRDATA (SPEED, dmc_line[0].speed, 32), REG_HRO },
    { NULL }  };

REG dmcb_reg[] = {
    { HRDATA (SEL0, dmc_csrs[1].sel0, 16) },
    { HRDATA (SEL2, dmc_csrs[1].sel2, 16) },
    { HRDATA (SEL4, dmc_csrs[1].sel4, 16) },
    { HRDATA (SEL6, dmc_csrs[1].sel6, 16) },
    { GRDATA (BSEL0, dmc_csrs[1].sel0, DEV_RDX, 8, 0) },
    { GRDATA (BSEL1, dmc_csrs[1].sel0, DEV_RDX, 8, 8) },
    { GRDATA (BSEL2, dmc_csrs[1].sel2, DEV_RDX, 8, 0) },
    { GRDATA (BSEL3, dmc_csrs[1].sel2, DEV_RDX, 8, 8) },
    { GRDATA (BSEL4, dmc_csrs[1].sel4, DEV_RDX, 8, 0) },
    { GRDATA (BSEL5, dmc_csrs[1].sel4, DEV_RDX, 8, 8) },
    { GRDATA (BSEL6, dmc_csrs[1].sel6, DEV_RDX, 8, 0) },
    { GRDATA (BSEL7, dmc_csrs[1].sel6, DEV_RDX, 8, 8) },
    { HRDATA (SHADOWSEL0, dmc_shadow_csrs[1].sel0, 16), REG_HRO },
    { HRDATA (SHADOWSEL2, dmc_shadow_csrs[1].sel2, 16), REG_HRO },
    { HRDATA (SHADOWSEL4, dmc_shadow_csrs[1].sel4, 16), REG_HRO },
    { HRDATA (SHADOWSEL6, dmc_shadow_csrs[1].sel6, 16), REG_HRO },
    { BRDATA (PEER, dmc_line[1].peer, 16, 8, sizeof(dmc_line[1].peer)), REG_HRO},
    { HRDATA (MODE, dmc_line[1].isPrimary, 32), REG_HRO },
    { HRDATA (SPEED, dmc_line[1].speed, 32), REG_HRO },
    { NULL }  };

REG dmcc_reg[] = {
    { HRDATA (SEL0, dmc_csrs[2].sel0, 16) },
    { HRDATA (SEL2, dmc_csrs[2].sel2, 16) },
    { HRDATA (SEL4, dmc_csrs[2].sel4, 16) },
    { HRDATA (SEL6, dmc_csrs[2].sel6, 16) },
    { GRDATA (BSEL0, dmc_csrs[2].sel0, DEV_RDX, 8, 0) },
    { GRDATA (BSEL1, dmc_csrs[2].sel0, DEV_RDX, 8, 8) },
    { GRDATA (BSEL2, dmc_csrs[2].sel2, DEV_RDX, 8, 0) },
    { GRDATA (BSEL3, dmc_csrs[2].sel2, DEV_RDX, 8, 8) },
    { GRDATA (BSEL4, dmc_csrs[2].sel4, DEV_RDX, 8, 0) },
    { GRDATA (BSEL5, dmc_csrs[2].sel4, DEV_RDX, 8, 8) },
    { GRDATA (BSEL6, dmc_csrs[2].sel6, DEV_RDX, 8, 0) },
    { GRDATA (BSEL7, dmc_csrs[2].sel6, DEV_RDX, 8, 8) },
    { HRDATA (SHADOWSEL0, dmc_shadow_csrs[2].sel0, 16), REG_HRO },
    { HRDATA (SHADOWSEL2, dmc_shadow_csrs[2].sel2, 16), REG_HRO },
    { HRDATA (SHADOWSEL4, dmc_shadow_csrs[2].sel4, 16), REG_HRO },
    { HRDATA (SHADOWSEL6, dmc_shadow_csrs[2].sel6, 16), REG_HRO },
    { BRDATA (PEER, dmc_line[2].peer, 16, 8, sizeof(dmc_line[2].peer)), REG_HRO},
    { HRDATA (MODE, dmc_line[2].isPrimary, 32), REG_HRO },
    { HRDATA (SPEED, dmc_line[2].speed, 32), REG_HRO },
    { NULL }  };

REG dmcd_reg[] = {
    { HRDATA (SEL0, dmc_csrs[3].sel0, 16) },
    { HRDATA (SEL2, dmc_csrs[3].sel2, 16) },
    { HRDATA (SEL4, dmc_csrs[3].sel4, 16) },
    { HRDATA (SEL6, dmc_csrs[3].sel6, 16) },
    { GRDATA (BSEL0, dmc_csrs[3].sel0, DEV_RDX, 8, 0) },
    { GRDATA (BSEL1, dmc_csrs[3].sel0, DEV_RDX, 8, 8) },
    { GRDATA (BSEL2, dmc_csrs[3].sel2, DEV_RDX, 8, 0) },
    { GRDATA (BSEL3, dmc_csrs[3].sel2, DEV_RDX, 8, 8) },
    { GRDATA (BSEL4, dmc_csrs[3].sel4, DEV_RDX, 8, 0) },
    { GRDATA (BSEL5, dmc_csrs[3].sel4, DEV_RDX, 8, 8) },
    { GRDATA (BSEL6, dmc_csrs[3].sel6, DEV_RDX, 8, 0) },
    { GRDATA (BSEL7, dmc_csrs[3].sel6, DEV_RDX, 8, 8) },
    { HRDATA (SHADOWSEL0, dmc_shadow_csrs[3].sel0, 16), REG_HRO },
    { HRDATA (SHADOWSEL2, dmc_shadow_csrs[3].sel2, 16), REG_HRO },
    { HRDATA (SHADOWSEL4, dmc_shadow_csrs[3].sel4, 16), REG_HRO },
    { HRDATA (SHADOWSEL6, dmc_shadow_csrs[3].sel6, 16), REG_HRO },
    { BRDATA (PEER, dmc_line[3].peer, 16, 8, sizeof(dmc_line[3].peer)), REG_HRO},
    { HRDATA (MODE, dmc_line[3].isPrimary, 32), REG_HRO },
    { HRDATA (SPEED, dmc_line[3].speed, 32), REG_HRO },
    { NULL }  };

REG dmp_reg[] = {
    { HRDATA (SEL0, dmc_csrs[3].sel0, 16) },
    { HRDATA (SEL2, dmc_csrs[3].sel2, 16) },
    { HRDATA (SEL4, dmc_csrs[3].sel4, 16) },
    { HRDATA (SEL6, dmc_csrs[3].sel6, 16) },
    { GRDATA (BSEL0, dmc_csrs[3].sel0, DEV_RDX, 8, 0) },
    { GRDATA (BSEL1, dmc_csrs[3].sel0, DEV_RDX, 8, 8) },
    { GRDATA (BSEL2, dmc_csrs[3].sel2, DEV_RDX, 8, 0) },
    { GRDATA (BSEL3, dmc_csrs[3].sel2, DEV_RDX, 8, 8) },
    { GRDATA (BSEL4, dmc_csrs[3].sel4, DEV_RDX, 8, 0) },
    { GRDATA (BSEL5, dmc_csrs[3].sel4, DEV_RDX, 8, 8) },
    { GRDATA (BSEL6, dmc_csrs[3].sel6, DEV_RDX, 8, 0) },
    { GRDATA (BSEL7, dmc_csrs[3].sel6, DEV_RDX, 8, 8) },
    { HRDATA (SHADOWSEL0, dmp_shadow_csrs[0].sel0, 16), REG_HRO },
    { HRDATA (SHADOWSEL2, dmp_shadow_csrs[0].sel2, 16), REG_HRO },
    { HRDATA (SHADOWSEL4, dmp_shadow_csrs[0].sel4, 16), REG_HRO },
    { HRDATA (SHADOWSEL6, dmp_shadow_csrs[0].sel6, 16), REG_HRO },
    { BRDATA (PEER, dmp_line[0].peer, 16, 8, sizeof(dmp_line[0].peer)), REG_HRO},
    { HRDATA (MODE, dmp_line[0].isPrimary, 32), REG_HRO },
    { HRDATA (SPEED, dmp_line[0].speed, 32), REG_HRO },
    { NULL }  };

MTAB dmc_mod[] = {
    { MTAB_XTD | MTAB_VDV, 0, "PEER", "PEER=address:port" ,&dmc_setpeer, &dmc_showpeer, NULL },
    { MTAB_XTD | MTAB_VDV, 0, "SPEED", "SPEED=bits/sec (0=unrestricted)" ,&dmc_setspeed, &dmc_showspeed, NULL },
#ifdef DMP
    { MTAB_XTD | MTAB_VDV, 0, "TYPE", "TYPE" ,&dmc_settype, &dmc_showtype, NULL },
#endif
    { MTAB_XTD | MTAB_VDV, 0, "LINEMODE", "LINEMODE={PRIMARY|SECONDARY}" ,&dmc_setlinemode, &dmc_showlinemode, NULL },
    { MTAB_XTD | MTAB_VDV | MTAB_NMO, 0, "STATS", "STATS" ,&dmc_setstats, &dmc_showstats, NULL },
    { MTAB_XTD | MTAB_VDV, 0, "CONNECTPOLL", "CONNECTPOLL=seconds" ,&dmc_setconnectpoll, &dmc_showconnectpoll, NULL },
    { MTAB_XTD | MTAB_VDV, 006, "ADDRESS", "ADDRESS", &set_addr, &show_addr, NULL },
    { MTAB_XTD  |MTAB_VDV, 0, "VECTOR", "VECTOR", &set_vec, &show_vec, NULL },
    { 0 },
};

#define IOLN_DMC        010
DIB dmc0_dib = { IOBA_AUTO, IOLN_DMC, &dmc_rd, &dmc_wr, 2, IVCL (DMCRX), VEC_AUTO, {&dmc_rxint, &dmc_txint} };
DIB dmc1_dib = { IOBA_AUTO, IOLN_DMC, &dmc_rd, &dmc_wr, 2, IVCL (DMCRX), VEC_AUTO, {&dmc_rxint, &dmc_txint} };
DIB dmc2_dib = { IOBA_AUTO, IOLN_DMC, &dmc_rd, &dmc_wr, 2, IVCL (DMCRX), VEC_AUTO, {&dmc_rxint, &dmc_txint} };
DIB dmc3_dib = { IOBA_AUTO, IOLN_DMC, &dmc_rd, &dmc_wr, 2, IVCL (DMCRX), VEC_AUTO, {&dmc_rxint, &dmc_txint} };

#define IOLN_DMP        010

DIB dmp_dib = { IOBA_AUTO, IOLN_DMP, &dmc_rd, &dmc_wr, 2, IVCL (DMCRX), VEC_AUTO, {&dmc_rxint, &dmc_txint }};

DEVICE dmc_dev[] =
{
    { "DMC0", &dmc0_unit, dmca_reg, dmc_mod, DMC_UNITSPERDEVICE, DMC_RDX, 8, 1, DMC_RDX, 8,
    NULL,NULL,&dmc_reset,NULL,&dmc_attach,&dmc_detach,
    &dmc0_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG, 0, dmc_debug,
    NULL, NULL, &dmc_help, &dmc_help_attach, NULL },
    { "DMC1", &dmc1_unit, dmcb_reg, dmc_mod, DMC_UNITSPERDEVICE, DMC_RDX, 8, 1, DMC_RDX, 8,
    NULL,NULL,&dmc_reset,NULL,&dmc_attach,&dmc_detach,
    &dmc1_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG, 0, dmc_debug,
    NULL, NULL, &dmc_help, &dmc_help_attach, NULL },
    { "DMC2", &dmc2_unit, dmcc_reg, dmc_mod, DMC_UNITSPERDEVICE, DMC_RDX, 8, 1, DMC_RDX, 8,
    NULL,NULL,&dmc_reset,NULL,&dmc_attach,&dmc_detach,
    &dmc2_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG, 0, dmc_debug,
    NULL, NULL, &dmc_help, &dmc_help_attach, NULL },
    { "DMC3", &dmc3_unit, dmcd_reg, dmc_mod, DMC_UNITSPERDEVICE, DMC_RDX, 8, 1, DMC_RDX, 8,
    NULL,NULL,&dmc_reset,NULL,&dmc_attach,&dmc_detach,
    &dmc3_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG, 0, dmc_debug,
    NULL, NULL, &dmc_help, &dmc_help_attach, NULL }
};

#ifdef DMP
DEVICE dmp_dev[] =
{
    { "DMP", &dmp_unit, dmp_reg, dmc_mod, DMP_UNITSPERDEVICE, DMC_RDX, 8, 1, DMC_RDX, 8,
    NULL,NULL,&dmc_reset,NULL,&dmc_attach,&dmc_detach,
    &dmp_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG, 0, dmc_debug,
    NULL, NULL, &dmc_help, &dmc_help_attach, NULL }
};
#endif

CTLR dmc_ctrls[] =
{
    { &dmc_csrs[0], &dmc_shadow_csrs[0], &dmc_dev[0], Initialised, Idle, 0, 0, &dmc_line[0], &dmc_receive_queues[0], &dmc_transmit_queues[0], &dmc_stats[0], INVALID_SOCKET, 30, DMC },
    { &dmc_csrs[1], &dmc_shadow_csrs[1], &dmc_dev[1], Initialised, Idle, 0, 0, &dmc_line[1], &dmc_receive_queues[1], &dmc_transmit_queues[1], &dmc_stats[1], INVALID_SOCKET, 30, DMC },
    { &dmc_csrs[2], &dmc_shadow_csrs[2], &dmc_dev[2], Initialised, Idle, 0, 0, &dmc_line[2], &dmc_receive_queues[2], &dmc_transmit_queues[2], &dmc_stats[2], INVALID_SOCKET, 30, DMC },
    { &dmc_csrs[3], &dmc_shadow_csrs[3], &dmc_dev[3], Initialised, Idle, 0, 0, &dmc_line[3], &dmc_receive_queues[3], &dmc_transmit_queues[3], &dmc_stats[3], INVALID_SOCKET, 30, DMC },
#ifdef DMP
    { &dmp_csrs[0], &dmp_shadow_csrs[0], &dmp_dev[0], Initialised, Idle, 0, 0, &dmp_line[0], &dmp_receive_queues[0], &dmp_transmit_queues[0], &dmp_stats[0], INVALID_SOCKET, -1, 30, DMP }
#endif
};

extern int32 tmxr_poll; /* calibrated delay */

void dmc_reset_unit_stats(UNIT_STATS *s)
{
    s->between_polls_timer.started = FALSE;
    s->poll_timer.started = FALSE;
    s->poll_count = 0;
}

int dmc_timer_started(TIMER *t)
{
    return t->started;
}

void dmc_timer_start(TIMER *t)
{
    t->start_time = clock();
    t->cumulative_time = 0;
    t->started = TRUE;
}

void dmc_timer_stop(TIMER *t)
{
    clock_t end_time = clock();
    t->cumulative_time += end_time - t->start_time;
}

void dmc_timer_resume(TIMER *t)
{
    t->start_time = clock();
}

double dmc_timer_cumulative_seconds(TIMER *t)
{
    return (double)t->cumulative_time/CLOCKS_PER_SEC;
}

int dmc_is_dmc(CTLR *controller)
{
    return controller->dev_type != DMP;
}

CTLR *dmc_get_controller_from_unit(UNIT *unit)
{
    int i;
    CTLR *ans = NULL;
    for (i = 0; i < DMC_NUMDEVICE + DMP_NUMDEVICE; i++)
    {
        if (dmc_ctrls[i].device->units == unit)
        {
            ans = &dmc_ctrls[i];
            break;
        }
    }

    return ans;
}

CTLR* dmc_get_controller_from_address(uint32 address)
{
    int i;
    for (i=0; i<DMC_NUMDEVICE + DMP_NUMDEVICE; i++)
    {
        DIB *dib = (DIB *)dmc_ctrls[i].device->ctxt;
        if ((address >= dib->ba) && (address < (dib->ba + dib->lnt)))
            return &dmc_ctrls[i];
    }
    /* not found */
    return 0;
}

CTLR *dmc_get_controller_from_device(DEVICE *device)
{
    int i;
    CTLR *ans = NULL;
    for (i = 0; i < DMC_NUMDEVICE + DMP_NUMDEVICE; i++)
    {
        if (dmc_ctrls[i].device == device)
        {
            ans = &dmc_ctrls[i];
            break;
        }
    }

    return ans;
}

t_stat dmc_showpeer (FILE* st, UNIT* uptr, int32 val, void* desc)
{
    CTLR *controller = dmc_get_controller_from_unit(uptr);
    if (controller->line->peer[0])
    {
        fprintf(st, "peer=%s", controller->line->peer);
    }

    return SCPE_OK;
}

t_stat dmc_setpeer (UNIT* uptr, int32 val, char* cptr, void* desc)
{
    t_stat status = SCPE_OK;
    char host[CBUFSIZE], port[CBUFSIZE];
    CTLR *controller = dmc_get_controller_from_unit(uptr);

    if (!cptr) return SCPE_IERR;
    if (uptr->flags & UNIT_ATT) return SCPE_ALATT;
    status = sim_parse_addr (cptr, host, sizeof(host), NULL, port, sizeof(port), NULL, NULL);
    if (status != SCPE_OK)
        return status;
    if (host[0] == '\0')
        return SCPE_ARG;
    strncpy(controller->line->peer, cptr, sizeof(controller->line->peer)-1);

    return status;
}

t_stat dmc_showspeed (FILE* st, UNIT* uptr, int32 val, void* desc)
{
    CTLR *controller = dmc_get_controller_from_unit(uptr);
    if (controller->line->speed > 0)
    {
        fprintf(st, "speed=%d bits/sec", controller->line->speed);
    }
    else
    {
        fprintf(st, "speed=0 (unrestricted)");
    }

    return SCPE_OK;
}

t_stat dmc_setspeed (UNIT* uptr, int32 val, char* cptr, void* desc)
{
    t_stat status = SCPE_OK;
    CTLR *controller = dmc_get_controller_from_unit(uptr);

    if (!cptr) return SCPE_IERR;
    if (uptr->flags & UNIT_ATT) return SCPE_ALATT;
    if (sscanf(cptr, "%d", &controller->line->speed) != 1)
    {
        status = SCPE_ARG;
    }

    return status;
}

t_stat dmc_showtype (FILE* st, UNIT* uptr, int32 val, void* desc)
{
    CTLR *controller = dmc_get_controller_from_unit(uptr);
    switch (controller->dev_type)
    {
    case DMC:
        {
            fprintf(st, "type=DMC");
            break;
        }
    case DMR:
        {
            fprintf(st, "type=DMR");
            break;
        }
    case DMP:
        {
            fprintf(st, "type=DMP");
            break;
        }
    default:
        {
            fprintf(st, "type=???");
            break;
        }
    }
    return SCPE_OK;
}

t_stat dmc_settype (UNIT* uptr, int32 val, char* cptr, void* desc)
{
    char buf[80];
    t_stat status = SCPE_OK;
    CTLR *controller = dmc_get_controller_from_unit(uptr);

    if (!cptr) return SCPE_IERR;
    if (uptr->flags & UNIT_ATT) return SCPE_ALATT;
    if (sscanf(cptr, "%s", buf) != 1)
    {
        status = SCPE_ARG;
    }
    else
    {
        if (strcmp(buf,"DMC")==0)
        {
            controller->dev_type = DMC;
        }
        else if  (strcmp(buf,"DMR")==0)
        {
            controller->dev_type = DMR;
        }
        else if  (strcmp(buf,"DMP")==0)
        {
            controller->dev_type = DMP;
        }
        else
        {
            status = SCPE_ARG;
        }
    }

    return status;
}

t_stat dmc_showstats (FILE* st, UNIT* uptr, int32 val, void* desc)
{
    CTLR *controller = dmc_get_controller_from_unit(uptr);
    TIMER *poll_timer = &controller->stats->poll_timer;
    TIMER *between_polls_timer = &controller->stats->between_polls_timer;
    uint32 poll_count = controller->stats->poll_count;

    if (dmc_timer_started(between_polls_timer) && poll_count > 0)
    {
        fprintf(st, "Average time between polls=%f (sec)\n", dmc_timer_cumulative_seconds(between_polls_timer)/poll_count);
    }
    else
    {
        fprintf(st, "Average time between polls=n/a\n");
    }

    if (dmc_timer_started(poll_timer) && poll_count > 0)
    {
        fprintf(st, "Average time within poll=%f (sec)\n", dmc_timer_cumulative_seconds(poll_timer)/poll_count);
    }
    else
    {
        fprintf(st, "Average time within poll=n/a\n");
    }

    fprintf(st, "Buffers received from the network=%d\n", controller->buffers_received_from_net);
    fprintf(st, "Buffers sent to the network=%d\n", controller->buffers_transmitted_to_net);
    fprintf(st, "Output transfers completed for receive buffers=%d\n", controller->receive_buffer_output_transfers_completed);
    fprintf(st, "Output transfers completed for transmit buffers=%d\n", controller->transmit_buffer_output_transfers_completed);
    fprintf(st, "Input transfers completed for receive buffers=%d\n", controller->receive_buffer_input_transfers_completed);
    fprintf(st, "Input transfers completed for transmit buffers=%d\n", controller->transmit_buffer_input_transfers_completed);

    return SCPE_OK;
}

t_stat dmc_setstats (UNIT* uptr, int32 val, char* cptr, void* desc)
{
    t_stat status = SCPE_OK;
    CTLR *controller = dmc_get_controller_from_unit(uptr);

    dmc_reset_unit_stats(controller->stats);

    controller->receive_buffer_output_transfers_completed = 0;
    controller->transmit_buffer_output_transfers_completed = 0;
    controller->receive_buffer_input_transfers_completed = 0;
    controller->transmit_buffer_input_transfers_completed = 0;

    printf("Statistics reset\n" );

    return status;
}

t_stat dmc_showconnectpoll (FILE* st, UNIT* uptr, int32 val, void* desc)
{
    CTLR *controller = dmc_get_controller_from_unit(uptr);
    fprintf(st, "connectpoll=%d", controller->connect_poll_interval);
    return SCPE_OK;
}

t_stat dmc_setconnectpoll (UNIT* uptr, int32 val, char* cptr, void* desc)
{
    t_stat status = SCPE_OK;
    CTLR *controller = dmc_get_controller_from_unit(uptr);

    if (!cptr) return SCPE_IERR;
    if (sscanf(cptr, "%d", &controller->connect_poll_interval) != 1)
    {
        status = SCPE_ARG;
    }

    return status;
}

t_stat dmc_showlinemode (FILE* st, UNIT* uptr, int32 val, void* desc)
{
    CTLR *controller = dmc_get_controller_from_unit(uptr);
    fprintf(st, "linemode=%s", controller->line->isPrimary? "PRIMARY" : "SECONDARY");
    return SCPE_OK;
}

t_stat dmc_setlinemode (UNIT* uptr, int32 val, char* cptr, void* desc)
{
    t_stat status = SCPE_OK;
    CTLR *controller = dmc_get_controller_from_unit(uptr);

    if (!cptr) return SCPE_IERR;
    if (uptr->flags & UNIT_ATT) return SCPE_ALATT;

    if (MATCH_CMD(cptr, "PRIMARY") == 0)
    {
        controller->line->isPrimary = 1;
    }
    else if (MATCH_CMD(cptr, "SECONDARY") == 0)
    {
        controller->line->isPrimary = 0;
    }
    else
    {
        status = SCPE_ARG;
    }

    return status;
}

t_stat dmc_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
{
    fprintf(st, "The DMC11 is a synchronous serial point-to-point communications device.");
    fprintf(st, "A real DMC11 transports data using DDCMP, the emulated device makes a");
    fprintf(st, "TCP/IP connection to another emulated device and sends length-prefixed");
    fprintf(st, "messages across the connection, each message representing a single buffer");
    fprintf(st, "passed to the DMC11. The DMC11 can be used for point-to-point DDCMP");
    fprintf(st, "connections carrying DECnet and other types of networking, e.g. from ULTRIX or DSM.\n");
    fprintf(st, "\n");
    fprintf(st, "The line mode of the two ends of the link must be set. One end must always");
    fprintf(st, "be primary and one end always secondary, setting both to primary or both");
    fprintf(st, "to secondary will not work. If there are firewall problems at one side, set");
    fprintf(st, "that side to be primary as the primary always initiates the TCP/IP connection.\n");
    fprintf(st, "\n");
    fprintf(st, "SET DMC0 LINEMODE= {PRIMARY|SECONDARY}\n");
    fprintf(st, "\n");
    fprintf(st, "To set the host and port to which data is to be transmitted use the following");
    fprintf(st, "command (required for PRIMARY and SECONDARY, secondary will check it is");
    fprintf(st, "receiving from the configured primary):\n");
    fprintf(st, "\n");
    fprintf(st, "SET DMC0 PEER=host:port\n");
    fprintf(st, "\n");
    fprintf(st, "The device must be attached to a receive port, use the ATTACH command specifying");
    fprintf(st, "the receive port number, even if the line mode is primary.\n");
    fprintf(st, "\n");
    fprintf(st, "The minimum interval between attempts to connect to the other side is set using");
    fprintf(st, "the following command:\n");
    fprintf(st, "\n");
    fprintf(st, "SET DMC0 CONNECTPOLL=n\n");
    fprintf(st, "\n");
    fprintf(st, "Where n is the number of seconds. The default is 30 seconds.\n");
    fprintf(st, "\n");
    fprintf(st, "If you want to experience the actual data rates of the physical hardware you can");
    fprintf(st, "set the bit rate of the simulated line can be set using the following command:\n");
    fprintf(st, "\n");
    fprintf(st, "SET DMC0 SPEED=n\n");
    fprintf(st, "\n");
    fprintf(st, "Where n is the number of data bits per second that the simulated line runs at.");
    fprintf(st, "In practice this is implemented as a delay in reading the bytes from the socket.");
    fprintf(st, "Use a value of zero to run at full speed with no artificial throttling.\n");
    fprintf(st, "\n");
    fprintf(st, "To configure two simulators to talk to each other use the following example:\n");
    fprintf(st, "\n");
    fprintf(st, "Machine 1\n");
    fprintf(st, "SET DMC0 ENABLE\n");
    fprintf(st, "SET DMC0 PRIMARY\n");
    fprintf(st, "SET DMC0 PEER=LOCALHOST:2222\n");
    fprintf(st, "ATTACH DMC0 1111\n");
    fprintf(st, "\n");
    fprintf(st, "Machine 2\n");
    fprintf(st, "SET DMC0 ENABLE\n");
    fprintf(st, "SET DMC0 SECONDARY\n");
    fprintf(st, "SET DMC0 PEER= LOCALHOST:1111\n");
    fprintf(st, "ATTACH DMC0 2222\n");
    fprintf(st, "\n");
    fprintf(st, "Debugging\n");
    fprintf(st, "=========\n");
    fprintf(st, "The simulator has a number of debug options, these are:\n");
    fprintf(st, "•	REG. Shows whenever a CSR is read or written and the current value.\n");
    fprintf(st, "•	INFO. Shows higher-level tracing only.\n");
    fprintf(st, "•	WARN. Shows any warnings.\n");
    fprintf(st, "•	TRACE. Shows more detailed trace information.\n");
    fprintf(st, "•	DATA. Shows the actual data sent and received.\n");
    fprintf(st, "•	DATASUM. Brief summary of each received and transmitted buffer. Ignored if DATA is set.\n");
    fprintf(st, "•	SOCKET. Shows socket opens and closes.\n");
    fprintf(st, "•	CONNECT. Shows sockets actually connecting.\n");
    fprintf(st, "\n");
    fprintf(st, "To get a full trace use\n");
    fprintf(st, "\n");
    fprintf(st, "SET DMC0 DEBUG\n");
    fprintf(st, "\n");
    fprintf(st, "However it is recommended to use the following when sending traces:\n");
    fprintf(st, "\n");
    fprintf(st, "SET DMC0 DEBUG=REG;INFO;WARN\n");
    fprintf(st, "\n");
    fprintf(st, "\n");

    return SCPE_OK;
}

t_stat dmc_help_attach (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
{
    //tmxr_attach_help (st, dptr, uptr, flag, cptr);
    fprintf (st, "The communication line performs input and output through a TCP session\n");
    fprintf (st, "connected to a user-specified port.  The ATTACH command specifies the");
    fprintf (st, "port to be used:\n\n");
    fprintf (st, "   sim> ATTACH %s {interface:}port		set up listening port\n\n", dptr->name);
    fprintf (st, "where port is a decimal number between 1 and 65535 that is not being used for\n");
    fprintf (st, "other TCP/IP activities. An ATTACH is required even if in PRIMARY mode. \n\n");
    return SCPE_OK;
}

void dmc_setrxint(CTLR *controller)
{
    controller->rxi = 1;
    SET_INT(DMCRX);
}

void dmc_clrrxint(CTLR *controller)
{
    controller->rxi = 0;
    CLR_INT(DMCRX);
}

void dmc_settxint(CTLR *controller)
{
    controller->txi = 1;
    SET_INT(DMCTX);
}

void dmc_clrtxint(CTLR *controller)
{
    controller->txi = 0;
    CLR_INT(DMCTX);
}

int dmc_getsel(int addr)
{
    return (addr >> 1) & 03;
}

uint16 dmc_bitfld(int data, int start_bit, int length)
{
    uint16 ans = data >> start_bit;
    uint32 mask = (1 << (length))-1;
    ans &= mask;
    return ans;
}

void dmc_dumpregsel0(CTLR *controller, int trace_level, char * prefix, uint16 data)
{
    char *type_str = "";
    uint16 type = dmc_bitfld(data, SEL0_TYPEI_BIT, 2);

    if (dmc_is_dmc(controller))
    {
        if (dmc_is_rqi_set(controller))
        {
            if (type==TYPE_BACCI)
                type_str = "BA/CC I";
            else if (type==TYPE_CNTLI)
                type_str = "CNTL I";
            else if (type==TYPE_BASEI)
                type_str = "BASE I";
            else
                type_str = "?????";
        }

        sim_debug(
            trace_level,
            controller->device,
            "%s SEL0 (0x%04x) %s%s%s%s%s%s%s%s%s\n",
            prefix,
            data,
            dmc_bitfld(data, SEL0_RUN_BIT, 1) ? "RUN " : "",
            dmc_bitfld(data, SEL0_MCLR_BIT, 1) ? "MCLR " : "",
            dmc_bitfld(data, SEL0_LU_LOOP_BIT, 1) ? "LU LOOP " : "",
            dmc_bitfld(data, SEL0_ROMI_BIT, 1) ? "ROMI " : "",
            dmc_bitfld(data, SEL0_RDI_BIT, 1) ? "RDI " : "",
            dmc_bitfld(data, SEL0_DMC_IEI_BIT, 1) ? "IEI " : "",
            dmc_bitfld(data, SEL0_DMC_RQI_BIT, 1) ? "RQI " : "",
            dmc_bitfld(data, SEL0_IN_IO_BIT, 1) ? "IN I/O " : "",
            type_str
            );
    }
    else
    {
        sim_debug(
            trace_level,
            controller->device,
            "%s SEL0 (0x%04x) %s%s%s%s%s%s\n",
            prefix,
            data,
            dmc_bitfld(data, SEL0_RUN_BIT, 1) ? "RUN " : "",
            dmc_bitfld(data, SEL0_MCLR_BIT, 1) ? "MCLR " : "",
            dmc_bitfld(data, SEL0_LU_LOOP_BIT, 1) ? "LU LOOP " : "",
            dmc_bitfld(data, SEL0_DMP_RQI_BIT, 1) ? "RQI " : "",
            dmc_bitfld(data, SEL0_DMP_IEO_BIT, 1) ? "IEO " : "",
            dmc_bitfld(data, SEL0_DMP_IEI_BIT, 1) ? "IEI " : ""
            );
    }
}

void dmc_dumpregsel2(CTLR *controller, int trace_level, char *prefix, uint16 data)
{
    char *type_str = "";
    uint16 type = dmc_bitfld(data, SEL2_TYPEO_BIT, 2);

    if (type==TYPE_BACCO)
        type_str = "BA/CC O";
    else if (type==TYPE_CNTLO)
        type_str = "CNTL O";
    else
        type_str = "?????";

    sim_debug(
        trace_level,
        controller->device,
        "%s SEL2 (0x%04x) PRIO=%d LINE=%d %s%s%s%s\n",
        prefix,
        data,
        dmc_bitfld(data, SEL2_PRIO_BIT, SEL2_PRIO_BIT_LENGTH),
        dmc_bitfld(data, SEL2_LINE_BIT, SEL2_LINE_BIT_LENGTH),
        dmc_bitfld(data, SEL2_RDO_BIT, 1) ? "RDO " : "",
        dmc_bitfld(data, SEL2_IEO_BIT, 1) ? "IEO " : "",
        dmc_bitfld(data, SEL2_OUT_IO_BIT, 1) ? "OUT I/O " : "",
        type_str
        );
}

void dmc_dumpregsel4(CTLR *controller, int trace_level, char *prefix, uint16 data)
{
    sim_debug(trace_level, controller->device, "%s SEL4 (0x%04x)\n", prefix, data);
}

void dmc_dumpregsel6(CTLR *controller, int trace_level, char *prefix, uint16 data)
{
    sim_debug(
        trace_level,
        controller->device,
        "%s SEL6 (0x%04x) %s\n",
        prefix,
        data,
        dmc_bitfld(data, SEL6_LOST_DATA_BIT, 1) ? "LOST_DATA " : "");
}

uint16 dmc_getreg(CTLR *controller, int reg, int ext)
{
    uint16 ans = 0;
    switch (dmc_getsel(reg))
    {
    case 00:
        ans = controller->csrs->sel0;
        if (ext) dmc_dumpregsel0(controller, DBG_REG, "Getting", ans);
        break;
    case 01:
        ans = controller->csrs->sel2;
        if (ext) dmc_dumpregsel2(controller, DBG_REG, "Getting", ans);
        break;
    case 02:
        ans = controller->csrs->sel4;
        if (ext) dmc_dumpregsel4(controller, DBG_REG, "Getting", ans);
        break;
    case 03:
        ans = controller->csrs->sel6;
        if (ext) dmc_dumpregsel6(controller, DBG_REG, "Getting", ans);
        break;
    default:
        {
            sim_debug(DBG_WRN, controller->device, "dmc_getreg(). Invalid register %d", reg);
        }
    }

    return ans;
}

void dmc_setreg(CTLR *controller, int reg, uint16 data, int ext)
{
    char *trace = (ext) ? "Writing" : "Setting";
    switch (dmc_getsel(reg))
    {
    case 00:
        dmc_dumpregsel0(controller, DBG_REG, trace, data);
        controller->csrs->sel0 = data;
        if (!ext)
        {
            controller->shadow_csrs->sel0 = data;
        }
        break;
    case 01:
        dmc_dumpregsel2(controller, DBG_REG, trace, data);
        controller->csrs->sel2 = data;
        if (!ext)
        {
            controller->shadow_csrs->sel2 = data;
        }
        break;
    case 02:
        dmc_dumpregsel4(controller, DBG_REG, trace, data);
        controller->csrs->sel4 = data;
        if (!ext)
        {
            controller->shadow_csrs->sel4 = data;
        }
        break;
    case 03:
        dmc_dumpregsel6(controller, DBG_REG, trace, data);
        controller->csrs->sel6 = data;
        if (!ext)
        {
            controller->shadow_csrs->sel6 = data;
        }
        break;
    default:
        {
            sim_debug(DBG_WRN, controller->device, "dmc_setreg(). Invalid register %d", reg);
        }
    }
}

int dmc_is_master_clear_set(CTLR *controller)
{
    return controller->csrs->sel0 & MASTER_CLEAR_MASK;
}

int dmc_is_lu_loop_set(CTLR *controller)
{
    return controller->csrs->sel0 & LU_LOOP_MASK;
}

int dmc_is_rqi_set(CTLR *controller)
{
    int ans = 0;
    if (dmc_is_dmc(controller))
    {
        ans = controller->csrs->sel0 & DMC_RQI_MASK;
    }
    else
    {
        ans = controller->csrs->sel0 & DMP_RQI_MASK;
    }
    return ans;
}

int dmc_is_rdyi_set(CTLR *controller)
{
    int ans = 0;
    if (dmc_is_dmc(controller))
    {
        ans = controller->csrs->sel0 & DMC_RDYI_MASK;
    }
    else
    {
        ans = controller->csrs->sel2 & DMP_RDYI_MASK;
    }
    return ans;
}

int dmc_is_iei_set(CTLR *controller)
{
    int ans = 0;
    if (dmc_is_dmc(controller))
    {
        ans = controller->csrs->sel0 & DMC_IEI_MASK;
    }
    else
    {
        ans = controller->csrs->sel0 & DMP_IEI_MASK;
    }

    return ans;
}
int dmc_is_ieo_set(CTLR *controller)
{
    int ans = 0;
    if (dmc_is_dmc(controller))
    {
        ans = controller->csrs->sel2 & DMC_IEO_MASK;
    }
    else
    {
        ans = controller->csrs->sel0 & DMP_IEO_MASK;
    }

    return ans;
}
int dmc_is_in_io_set(CTLR *controller)
{
    int ans = 0;
    if (dmc_is_dmc(controller))
    {
        ans = controller->csrs->sel0 & DMC_IN_IO_MASK;
    }
    else
    {
        ans = !controller->csrs->sel2 & DMP_IN_IO_MASK;
    }

    return ans;
}
int dmc_is_out_io_set(CTLR *controller)
{
    int ans = controller->shadow_csrs->sel2 & OUT_IO_MASK;
    return ans;
}

int dmc_is_rdyo_set(CTLR *controller)
{
    return controller->csrs->sel2 & DMC_RDYO_MASK;
}

void dmc_set_rdyi(CTLR *controller)
{
    if (dmc_is_dmc(controller))
    {
        dmc_setreg(controller, 0, controller->csrs->sel0 | DMC_RDYI_MASK, 0);
    }
    else
    {
        dmc_setreg(controller, 2, controller->csrs->sel2 | DMP_RDYI_MASK, 0);
    }

    if (dmc_is_iei_set(controller))
    {
        dmc_setrxint(controller);
    }
}

void dmc_clear_rdyi(CTLR *controller)
{
    if (dmc_is_dmc(controller))
    {
        dmc_setreg(controller, 0, controller->csrs->sel0 & ~DMC_RDYI_MASK, 0);
    }
    else
    {
        dmc_setreg(controller, 2, controller->csrs->sel2 & ~DMP_RDYI_MASK, 0);
    }
}

void dmc_set_rdyo(CTLR *controller)
{
    dmc_setreg(controller, 2, controller->csrs->sel2 | DMC_RDYO_MASK, 0);

    if (dmc_is_ieo_set(controller))
    {
        dmc_settxint(controller);
    }
}

void dmc_set_lost_data(CTLR *controller)
{
    dmc_setreg(controller, 6, controller->csrs->sel6 | LOST_DATA_MASK, 0);
}

void dmc_clear_master_clear(CTLR *controller)
{
    dmc_setreg(controller, 0, controller->csrs->sel0 & ~MASTER_CLEAR_MASK, 0);
}

void dmc_set_run(CTLR *controller)
{
    dmc_setreg(controller, 0, controller->csrs->sel0 | RUN_MASK, 0);
}

int dmc_get_input_transfer_type(CTLR *controller)
{
    int ans = 0;

    if (dmc_is_dmc(controller))
    {
        ans = controller->csrs->sel0 & DMC_TYPE_INPUT_MASK;
    }
    else
    {
        ans = controller->csrs->sel2 & DMP_TYPE_INPUT_MASK;
    }

    return ans;
}
int dmc_get_output_transfer_type(CTLR *controller)
{
    return controller->shadow_csrs->sel2 & TYPE_OUTPUT_MASK;
}
void dmc_set_type_output(CTLR *controller, int type)
{
    dmc_setreg(controller, 2, controller->csrs->sel2 | (type & TYPE_OUTPUT_MASK), 0);
}

void dmc_set_out_io(CTLR *controller)
{
    dmc_setreg(controller, 2, controller->csrs->sel2 | OUT_IO_MASK, 0);
}

void dmc_clear_out_io(CTLR *controller)
{
    dmc_setreg(controller, 2, controller->csrs->sel2 & ~OUT_IO_MASK, 0);
}

void dmc_process_master_clear(CTLR *controller)
{
    sim_debug(DBG_INF, controller->device, "Master clear\n");
    dmc_clear_master_clear(controller);
    dmc_close_socket(controller, "Master clear"); /* to resynch both ends */
    controller->state = Initialised;
    dmc_setreg(controller, 0, 0, 0);
    if (controller->dev_type == DMR)
    {
         /* DMR-11 indicates microdiagnostics complete when this is set */
        dmc_setreg(controller, 2, 0x8000, 0);
    }
    else
    {
        /* preserve contents of BSEL3 if DMC-11 */
        dmc_setreg(controller, 2, controller->csrs->sel2 & 0xFF00, 0);
    }
    if (controller->dev_type == DMP)
    {
        dmc_setreg(controller, 4, 077, 0);
    }
    else
    {
        dmc_setreg(controller, 4, 0, 0);
    }

    if (controller->dev_type == DMP)
    {
        dmc_setreg(controller, 6, 0305, 0);
    }
    else
    {
        dmc_setreg(controller, 6, 0, 0);
    }
    dmc_buffer_queue_init_all(controller);

    controller->transfer_state = Idle;
    dmc_set_run(controller);

    sim_cancel (controller->device->units);                                  /* stop poll */
    sim_clock_coschedule (controller->device->units, tmxr_poll);             /* reactivate */
}

void dmc_start_input_transfer(CTLR *controller)
{
    int ok = 1;
    int type = dmc_get_input_transfer_type(controller);

    /* if this is a BA/CC I then check that the relevant queue has room first */
    if (type == TYPE_BACCI)
    {
        ok = (dmc_is_in_io_set(controller) && !dmc_buffer_queue_full(controller->receive_queue))
            ||
            (!dmc_is_in_io_set(controller) && !dmc_buffer_queue_full(controller->transmit_queue));
    }

    if (ok)
    {
        sim_debug(DBG_INF, controller->device, "Starting input transfer\n");
        controller->transfer_state = InputTransfer;
        controller->transfer_type = type;
        controller->transfer_in_io = dmc_is_in_io_set(controller);
        dmc_set_rdyi(controller);
    }
    else
    {
        sim_debug(DBG_WRN, controller->device, "Input transfer request not granted as queue is full\n");
    }
}

void dmc_start_data_output_transfer(CTLR *controller, uint32 addr, int16 count, int is_receive)
{
    if (is_receive)
    {
        sim_debug(DBG_INF, controller->device, "Starting data output transfer for receive, address=0x%08x, count=%d\n", addr, count);
        dmc_set_out_io(controller);
    }
    else
    {
        sim_debug(DBG_INF, controller->device, "Starting data output transfer for transmit, address=0x%08x, count=%d\n", addr, count);
        dmc_clear_out_io(controller); 
    }

    dmc_setreg(controller, 4, addr & 0xFFFF, 0);
    dmc_setreg(controller, 6, (((addr & 0x30000)) >> 2) | count, 0);
    controller->transfer_state = OutputTransfer;
    dmc_set_type_output(controller, TYPE_BACCO);
    dmc_set_rdyo(controller);
}

void dmc_start_control_output_transfer(CTLR *controller)
{
    sim_debug(DBG_INF, controller->device, "Starting control output transfer\n");
    controller->transfer_state = OutputTransfer;
    dmc_set_type_output(controller, TYPE_CNTLO);
    dmc_set_rdyo(controller);
}

t_stat dmc_svc(UNIT* uptr)
{
    CTLR *controller;
    TIMER *poll_timer;
    TIMER *between_polls_timer;

    controller = dmc_get_controller_from_unit(uptr);

    poll_timer = &controller->stats->poll_timer;
    between_polls_timer = &controller->stats->between_polls_timer;

    if (dmc_timer_started(between_polls_timer))
    {
        dmc_timer_stop(between_polls_timer);
    }

    if (dmc_timer_started(poll_timer))
    {
        dmc_timer_resume(poll_timer);
    }
    else
    {
        dmc_timer_start(poll_timer);
    }

    if (dmc_isattached(controller))
    {
        dmc_line_update_speed_stats(controller->line);

        dmc_buffer_fill_receive_buffers(controller);
        if (controller->transfer_state == Idle) dmc_start_transfer_receive_buffer(controller);

        dmc_buffer_send_transmit_buffers(controller);
        if (controller->transfer_state == Idle) dmc_start_transfer_transmit_buffer(controller);
    }

    /* resubmit service timer */
    sim_clock_coschedule (controller->device->units, tmxr_poll);

    dmc_timer_stop(poll_timer);
    if (dmc_timer_started(between_polls_timer))
    {
        dmc_timer_resume(between_polls_timer);
    }
    else
    {
        dmc_timer_start(between_polls_timer);
    }
    controller->stats->poll_count++;

    return SCPE_OK;
}

void dmc_line_update_speed_stats(LINE *line)
{
    clock_t current = clock();
    int current_second = current / CLOCKS_PER_SEC;
    if (current_second != line->last_second)
    {
        line->bytes_received_in_last_second = 0;
        line->bytes_sent_in_last_second = 0;
        line->last_second = current_second;
    }
}

/* given the number of  bytes sent/received in the last second, the number of bytes to send or receive and the line speed, calculate how many bytes can be sent/received now */
int dmc_line_speed_calculate_byte_length(int bytes_in_last_second, int num_bytes, int speed)
{
    int ans;

    if (speed == 0)
    {
        ans = num_bytes;
    }
    else
    {
        int clocks_this_second = clock() % CLOCKS_PER_SEC;
        int allowable_bytes_to_date = ((speed/8) * clocks_this_second)/CLOCKS_PER_SEC;
        int allowed_bytes = allowable_bytes_to_date - bytes_in_last_second;
        if (allowed_bytes < 0)
        {
            allowed_bytes = 0;
        }

        if (num_bytes > allowed_bytes)
        {
            ans = allowed_bytes;
        }
        else
        {
            ans = num_bytes;
        }
//sim_debug(DBG_WRN, dmc_ctrls[0].device, "Bytes in last second %4d, clocks this sec %3d allowable bytes %4d, requested %4d allowed %4d\n", bytes_in_last_second, clocks_this_second, allowable_bytes_to_date, num_bytes, ans);
    }

    return ans;
}

void dmc_buffer_trace_line(int tracelevel, CTLR *controller, uint8 *buf, int length, char *prefix)
{
    char hex[TRACE_BYTES_PER_LINE*3+1];
    char ascii[TRACE_BYTES_PER_LINE+1];
    int i;
    hex[0] = 0;
    ascii[TRACE_BYTES_PER_LINE] = 0;

    for (i = 0; i<TRACE_BYTES_PER_LINE; i++)
    {
        if (i>=length)
        {
            strcat(hex, "   ");
            ascii[i] = ' ';
        }
        else
        {
            char hexByte[4];
            sprintf(hexByte, "%02X ", buf[i]);
            strcat(hex, hexByte);
            if (isprint(buf[i]))
            {
                ascii[i] = (char)buf[i];
            }
            else
            {
                ascii[i] = '.';
            }
        }
    }

    sim_debug(tracelevel, controller->device, "%s %s  %s\n", prefix, hex, ascii);
}

void dmc_buffer_trace(CTLR *controller, uint8 *buf, int length, char *prefix, uint32 address)
{
    int i;
    if (length >= 0 && controller->device->dctrl & DBG_DAT)
    {
        sim_debug(DBG_DAT, controller->device, "%s Buffer address 0x%08x (%d bytes)\n", prefix, address, length);
        for(i = 0; i < length / TRACE_BYTES_PER_LINE; i++)
        {
            dmc_buffer_trace_line(DBG_DAT, controller, buf + i*TRACE_BYTES_PER_LINE, TRACE_BYTES_PER_LINE, prefix);
        }

        if (length %TRACE_BYTES_PER_LINE > 0)
        {
            dmc_buffer_trace_line(DBG_DAT, controller, buf + length/TRACE_BYTES_PER_LINE, length % TRACE_BYTES_PER_LINE, prefix);
        }
    }
    else if (length >= 0 && controller->device->dctrl & DBG_DTS)
    {
        char prefix2[80];
        sprintf(prefix2, "%s (len=%d)", prefix, length);
        dmc_buffer_trace_line(DBG_DTS, controller, buf, (length > TRACE_BYTES_PER_LINE)? TRACE_BYTES_PER_LINE : length, prefix2);
    }
}

void dmc_buffer_queue_init(CTLR *controller, BUFFER_QUEUE *q, char *name)
{
    q->name = name;
    q->head = 0;
    q->tail = 0;
    q->count = 0;
    q->controller = controller;
}

void dmc_buffer_queue_init_all(CTLR *controller)
{
    dmc_buffer_queue_init(controller, controller->receive_queue, "receive");
    dmc_buffer_queue_init(controller, controller->transmit_queue, "transmit");
}

int dmc_buffer_queue_full(BUFFER_QUEUE *q)
{
    return q->count > BUFFER_QUEUE_SIZE;
}

void dmc_buffer_queue_add(BUFFER_QUEUE *q, uint32 address, uint16 count)
{
    if (!dmc_buffer_queue_full(q))
    {
        int new_buffer = 0;
        if (q->count > 0)
        {
            int last_buffer = q->tail;
            new_buffer = (q->tail +1) % BUFFER_QUEUE_SIZE;

            /* Link last buffer to the new buffer */
            q->queue[last_buffer].next = &q->queue[new_buffer];
        }
        else
        {
            q->head = 0;
            new_buffer = 0;
        }

        q->tail = new_buffer;
        q->queue[new_buffer].address = address;
        q->queue[new_buffer].count = count;
        q->queue[new_buffer].actual_block_len = 0;
        q->queue[new_buffer].transfer_buffer = NULL;
        q->queue[new_buffer].block_len_bytes_read = 0;
        q->queue[new_buffer].actual_bytes_transferred = 0;
        q->queue[new_buffer].next = NULL;
        q->queue[new_buffer].state = Available;
        q->queue[new_buffer].is_loopback = dmc_is_lu_loop_set(q->controller);
        q->count++;
        sim_debug(DBG_INF, q->controller->device, "Queued %s buffer address=0x%08x count=%d\n", q->name, address, count);
    }
    else
    {
        sim_debug(DBG_WRN, q->controller->device, "Failed to queue %s buffer address=0x%08x, queue full\n", q->name, address);
        // TODO: Report error here.
    }
}

void dmc_buffer_queue_release_head(BUFFER_QUEUE *q)
{
    if (q->count > 0)
    {
        q->head = (q->head + 1) % BUFFER_QUEUE_SIZE;
        q->count--;
    }
    else
    {
        sim_debug(DBG_INF, q->controller->device, "Failed to release %s buffer, queue already empty\n", q->name);
    }
}

BUFFER *dmc_buffer_queue_head(BUFFER_QUEUE *q)
{
    BUFFER *ans = NULL;
    if (q->count >0)
    {
        ans = &q->queue[q->head];
    }

    return ans;
}

BUFFER *dmc_buffer_queue_find_first_available(BUFFER_QUEUE *q)
{
    BUFFER *ans = dmc_buffer_queue_head(q);
    while (ans != NULL)
    {
        if (ans->state == Available)
        {
            break;
        }

        ans = ans->next;
    }

    return ans;
}

BUFFER *dmc_buffer_queue_find_first_contains_data(BUFFER_QUEUE *q)
{
    BUFFER *ans = dmc_buffer_queue_head(q);
    while (ans != NULL)
    {
        if (ans->state == ContainsData)
        {
            break;
        }

        ans = ans->next;
    }

    return ans;
}

void dmc_buffer_queue_get_stats(BUFFER_QUEUE *q, int *available, int *contains_data, int *transfer_in_progress)
{
    BUFFER *buf = dmc_buffer_queue_head(q);
    *available = 0;
    *contains_data = 0;
    *transfer_in_progress = 0;

    while (buf != NULL)
    {
        switch (buf->state)
        {
        case Available:
            {
                (*available)++;
                break;
            }

        case ContainsData:
            {
                (*contains_data)++;
                break;
            }

        case TransferInProgress:
            {
                (*transfer_in_progress)++;
                break;
            }
        }

        buf = buf->next;
    }
}

t_stat dmc_open_master_socket(CTLR *controller, char *port)
{
    t_stat ans;
    ans = SCPE_OK;
    if (controller->master_socket == INVALID_SOCKET)
    {
        controller->master_socket = sim_master_sock(port, &ans);
        if (controller->master_socket == INVALID_SOCKET)
        {
            sim_debug(DBG_WRN, controller->device, "Failed to open master socket on port %s\n", port);
            ans = SCPE_OPENERR;
        }
        else
        {
            printf ("DMC-11 %s listening on port %s\n", controller->device->name, port);
        }
    }

    return ans;
}

t_stat dmc_close_master_socket(CTLR *controller)
{
    sim_close_sock (controller->master_socket, TRUE);
    controller->master_socket = INVALID_SOCKET;
    return SCPE_OK;
}

// Gets the bidirectional socket and handles arbitration of determining which socket to use.
int dmc_get_socket(CTLR *controller, int forRead)
{
    int ans = 0;
    if (controller->line->isPrimary)
    {
        ans = dmc_get_transmit_socket(controller, 0, forRead); // TODO: After change to single socket, loopback may not work.
    }
    else
    {
        ans = dmc_get_receive_socket(controller, forRead); // TODO: After change to single socket, loopback may not work.
    }
    return ans;
}

int dmc_get_receive_socket(CTLR *controller, int forRead)
{
    int ans = 0;
    if (controller->line->socket == INVALID_SOCKET)
    {
        char *ipaddr;
        //sim_debug(DBG_SOK, controller->device, "Trying to open receive socket\n");
        controller->line->socket = sim_accept_conn (controller->master_socket, &ipaddr); /* poll connect */
        if (controller->line->socket != INVALID_SOCKET)
        {
            char host[sizeof(controller->line->peer)];

            if (sim_parse_addr (controller->line->peer, host, sizeof(host), NULL, NULL, 0, NULL, ipaddr))
            {
                sim_debug(DBG_WRN, controller->device, "Received connection from unexpected source IP %s. Closing the connection.\n", ipaddr);
                dmc_close_receive(controller, "Unathorized connection", ipaddr);
            }
            else
            {
                sim_debug(DBG_SOK, controller->device, "Opened receive socket %d\n", controller->line->socket);
                controller->line->receive_readable = FALSE;
            }
            free(ipaddr);
        }
    }

    if (controller->line->socket != INVALID_SOCKET)
    {
        int readable = sim_check_conn(controller->line->socket, forRead);
        if (readable == 0) /* Still opening */
        {
            // Socket is still being opened, or is open but there is no data ready to be read.
            ans = 0;
        }
        else if (readable == -1) /* Failed to open */
        {
            dmc_close_receive(controller, "failed to connect", NULL);
            ans = 0;
        }
        else /* connected */
        {
            if (!controller->line->receive_readable)
            {
                sim_debug(DBG_CON, controller->device, "Receive socket is now readable\n");
            }
            controller->line->receive_readable = TRUE;
            ans = 1;
        }
    }

    return ans;
}

int dmc_get_transmit_socket(CTLR *controller, int is_loopback, int forRead)
{
    int ans = 0;
    /* close transmit socket if there is a change in the loopback setting */
    if (is_loopback ^ controller->line->transmit_is_loopback)
    {
        dmc_close_transmit(controller, "loopback change");
    }

    if (controller->line->socket == INVALID_SOCKET && ((int32)(time(NULL) - controller->line->last_connect_attempt)) > controller->connect_poll_interval)
    {
        char host_port_buf[CBUFSIZE];
        char *host_port = host_port_buf;

        controller->line->transmit_is_loopback = is_loopback;
        
        controller->line->last_connect_attempt = time(NULL);
        if (is_loopback)
        {
            if (strrchr(controller->line->receive_port, ':'))
            {
                host_port = controller->line->receive_port;
            }
            else
            {
                sprintf(host_port_buf, "localhost:%s", controller->line->receive_port);
            }
        }
        else
        {
            host_port = controller->line->peer;
        }

        sim_debug(DBG_SOK, controller->device, "Trying to open transmit socket to address:port %s\n", host_port);
        controller->line->last_connect_attempt = time(NULL);
        controller->line->socket = sim_connect_sock(host_port, NULL, NULL);
        if (controller->line->socket != INVALID_SOCKET)
        {
            sim_debug(DBG_SOK, controller->device, "Opened transmit socket to port %s\n", host_port);
            controller->line->transmit_writeable = FALSE;
        }
    }

    if (controller->line->socket != INVALID_SOCKET)
    {
        int writeable = sim_check_conn(controller->line->socket, forRead);
        if (writeable == 0) /* Still opening */
        {
            //sim_debug(DBG_SOK, controller->device, "Waiting for transmit socket to become writeable\n");
            ans = 0;
        }
        else if (writeable == -1) /* Failed to open */
        {
            dmc_close_transmit(controller, "failed to connect");
            ans = 0;
        }
        else /* connected */
        {
            if (!controller->line->transmit_writeable)
            {
                sim_debug(DBG_CON, controller->device, "Transmit socket is now writeable\n");
            }
            controller->line->transmit_writeable = TRUE;
            ans = 1;
        }
    }

    return ans;
}

void dmc_error_and_close_socket(CTLR *controller, char *format)
{
    int err = WSAGetLastError(); 
    char errmsg[80];
    sprintf(errmsg, format, err);
    dmc_close_socket(controller, errmsg);
}

void dmc_close_socket(CTLR *controller, char *reason)
{
    if (controller->line->isPrimary)
    {
        dmc_close_transmit(controller, reason);
    }
    else
    {
        dmc_close_receive(controller, reason, NULL);
    }
}

void dmc_close_receive(CTLR *controller, char *reason, char *from)
{
    if (controller->line->socket != INVALID_SOCKET)
    {
        sim_debug(DBG_SOK, controller->device, "Closing receive socket on port %s, reason: %s%s%s\n", controller->line->receive_port, reason, from ? " from " : "", from ? from : "");
        sim_close_sock(controller->line->socket, FALSE);
        controller->line->socket = INVALID_SOCKET;

        if (controller->line->receive_readable)
        {
            sim_debug(DBG_CON, controller->device, "Readable receive socket closed, reason: %s\n", reason);
        }
        controller->line->receive_readable = FALSE;
    }
}

void dmc_close_transmit(CTLR *controller, char *reason)
{
    if (controller->line->socket != INVALID_SOCKET)
    {
        sim_debug(DBG_SOK, controller->device, "Closing transmit socket to port %s, socket %d, reason: %s\n", controller->line->peer, controller->line->socket, reason);
        sim_close_sock(controller->line->socket, FALSE);
        controller->line->socket = INVALID_SOCKET;

        if (controller->line->transmit_writeable)
        {
            sim_debug(DBG_CON, controller->device, "Writeable transmit socket closed, reason: %s\n", reason);
        }
        controller->line->transmit_writeable = FALSE;
    }
}

/* returns true if some data was received */
int dmc_buffer_fill_receive_buffers(CTLR *controller)
{
    int ans = FALSE;
    SOCKET socket;
    if (controller->state == Running)
    {
        BUFFER *buffer = dmc_buffer_queue_find_first_available(controller->receive_queue);
        while (buffer != NULL && buffer->state == Available)
        {
            if (dmc_get_socket(controller, TRUE))
            {
                int bytes_read = 0;
                int lost_data = 0;

                socket = controller->line->socket;
                /* read block length and allocate buffer */
                if ((size_t)buffer->block_len_bytes_read < sizeof(buffer->actual_block_len))
                {
                    char *start_addr = ((char *)&buffer->actual_block_len) + buffer->block_len_bytes_read;
                    bytes_read = sim_read_sock(socket, start_addr, sizeof(buffer->actual_block_len) - buffer->block_len_bytes_read);
                    if (bytes_read >= 0)
                    {
                        buffer->block_len_bytes_read += bytes_read;
                        if (buffer->block_len_bytes_read == sizeof(buffer->actual_block_len))
                        {
                            buffer->actual_block_len = ntohs(buffer->actual_block_len);
                            if (buffer->actual_block_len > buffer->count)
                            {
                                sim_debug(DBG_WRN, controller->device, "LOST DATA, buffer available has %d bytes, but the block is %d bytes\n", buffer->count, buffer->actual_block_len);
                                dmc_setreg(controller, 4, 0, 0);
                                dmc_setreg(controller, 6, 0, 0);
                                dmc_set_lost_data(controller);
                                dmc_start_control_output_transfer(controller);
                                lost_data = 1;
                                dmc_error_and_close_socket(controller, "oversized packet");
                            }

                            if (buffer->actual_block_len > 0)
                            {
                                buffer->transfer_buffer = (uint8 *)malloc(buffer->actual_block_len); /* read full buffer regardless, so bad buffer is flushed */
                            }
                        }
                    }
                }
                else
                {
                    lost_data = buffer->actual_block_len > buffer->count; /* need to preserve this variable if need more than one attempt to read the buffer */
                }

                /* read the actual block */
                if (buffer->block_len_bytes_read == sizeof(buffer->actual_block_len))
                {
                    bytes_read = 0;
                    if (buffer->actual_block_len > 0)
                    {
                        int bytes_to_read = dmc_line_speed_calculate_byte_length(controller->line->bytes_received_in_last_second, buffer->actual_block_len - buffer->actual_bytes_transferred, controller->line->speed);
                        if (bytes_to_read > 0)
                        {
                            bytes_read = sim_read_sock(controller->line->socket, (char *)(buffer->transfer_buffer + buffer->actual_bytes_transferred), bytes_to_read);
                        }
                    }

                    if (bytes_read >= 0)
                    {
                        buffer->actual_bytes_transferred += bytes_read;
                        controller->line->bytes_received_in_last_second += bytes_read;

                        if (buffer->actual_bytes_transferred >= buffer->actual_block_len)
                        {
                            dmc_buffer_trace(controller, buffer->transfer_buffer, buffer->actual_bytes_transferred, "REC ", buffer->address);
                            controller->buffers_received_from_net++;
                            buffer->state = ContainsData;
                            if (!lost_data)
                            {
                                Map_WriteB(buffer->address, buffer->actual_bytes_transferred, buffer->transfer_buffer);
                            }
                            else
                            {
                                buffer->actual_block_len = 0; /* so an empty buffer is returned to the driver */
                            }

                            if (buffer->actual_block_len > 0)
                            {
                                free(buffer->transfer_buffer);
                                buffer->transfer_buffer = NULL;
                            }

                            ans = TRUE;
                        }
                    }
                }

                /* Only close the socket if there was an error or no more data */
                if (bytes_read < 0)
                {
                    dmc_error_and_close_socket(controller, "read error, code=%d");
                    break;
                }

                /* if buffer is incomplete do not try to read any more buffers and continue filling this one later */
                if (buffer->state == Available)
                {
                    break; /* leave buffer available and continue filling it later */
                }
            }
            else
            {
                break;
            }

            buffer = buffer ->next;
        }
    }

    return ans;
}

/* returns true if some data was actually sent */
int dmc_buffer_send_transmit_buffers(CTLR *controller)
{
    int ans = FALSE;
    /* when transmit buffer is queued it is marked as available, not as ContainsData */
    BUFFER *buffer = dmc_buffer_queue_find_first_available(controller->transmit_queue);
    while (buffer != NULL)
    {
        if (dmc_get_socket(controller, FALSE)) // TODO: , buffer->is_loopback);
        {
            int bytes = 0;
            int bytes_to_send;
            uint16 block_len;
            int total_buffer_len = (buffer->count > 0) ? buffer->count + sizeof(block_len) : 0;

            /* only send the buffer if it actually has some data, sometimes get zero length buffers - don't send these */
            if (total_buffer_len > 0)
            {
                if (buffer->transfer_buffer == NULL)
                {
                    int n;
                    /* construct buffer and include block length bytes */
                    buffer->transfer_buffer = (uint8 *)malloc(total_buffer_len);
                    block_len = htons(buffer->count);
                    memcpy(buffer->transfer_buffer, (char *)&block_len, sizeof(block_len));
                    n = Map_ReadB(buffer->address, buffer->count, buffer->transfer_buffer + sizeof(block_len));
                    if (n > 0)
                    {
                        sim_debug(DBG_WRN, controller->device, "DMA error\n");
                    }
                }

                bytes_to_send = dmc_line_speed_calculate_byte_length(controller->line->bytes_sent_in_last_second, buffer->count + sizeof(block_len) - buffer->actual_bytes_transferred, controller->line->speed);
                if (bytes_to_send > 0)
                {
                    bytes = sim_write_sock (controller->line->socket, (char *)(buffer->transfer_buffer + buffer->actual_bytes_transferred), bytes_to_send);
                    if (bytes >= 0)
                    {
                        buffer->actual_bytes_transferred += bytes;
                        controller->line->bytes_sent_in_last_second += bytes;
                    }

                    if (buffer->actual_bytes_transferred >= total_buffer_len || bytes < 0)
                    {
                        dmc_buffer_trace(controller, buffer->transfer_buffer+sizeof(block_len), buffer->count, "TRAN", buffer->address);
                        free(buffer->transfer_buffer);
                    }
                }
            }

            if (buffer->actual_bytes_transferred >= total_buffer_len)
            {
                controller->buffers_transmitted_to_net++;
                buffer->state = ContainsData; // so won't try to transmit again
                ans = TRUE;
            }
            else if (bytes < 0)
            {
                int err = WSAGetLastError (); 
                char errmsg[80];
                sprintf(errmsg, "write failure, code=%d", err);

                dmc_close_transmit(controller, errmsg);
                break;
            }
            else
            {
                break; /* poll again later to send more bytes */
            }

        }
        else
        {
            break;
        }

        buffer = buffer ->next;
    }

    return ans;
}

void dmc_start_transfer_receive_buffer(CTLR *controller)
{
    BUFFER *head = dmc_buffer_queue_head(controller->receive_queue);
    if (head != NULL)
    {
        if (head->state == ContainsData)
        {
            head->state = TransferInProgress;
            dmc_start_data_output_transfer(controller, head->address, head->actual_block_len, TRUE);
        }
    }
}

void dmc_start_transfer_transmit_buffer(CTLR *controller)
{
    BUFFER *head = dmc_buffer_queue_head(controller->transmit_queue);
    if (head != NULL)
    {
        if (head->state == ContainsData)
        {
            head->state = TransferInProgress;
            dmc_start_data_output_transfer(controller, head->address, head->count, FALSE);
        }
    }
}

void dmc_check_for_output_transfer_completion(CTLR *controller)
{
    if (!dmc_is_rdyo_set(controller))
    {
        sim_debug(DBG_INF, controller->device, "Output transfer completed\n");
        controller->transfer_state = Idle;
        if (dmc_get_output_transfer_type(controller) == TYPE_BACCO)
        {
            if (dmc_is_out_io_set(controller))
            {
                dmc_buffer_queue_release_head(controller->receive_queue);
                controller->receive_buffer_output_transfers_completed++;
            }
            else
            {
                dmc_buffer_queue_release_head(controller->transmit_queue);
                controller->transmit_buffer_output_transfers_completed++;
            }
        }
        dmc_process_command(controller); // check for any input transfers
    }
}

void dmc_process_input_transfer_completion(CTLR *controller)
{
    if (dmc_is_dmc(controller))
    {
        if (!dmc_is_rqi_set(controller))
        {
            uint16 sel4 = controller->csrs->sel4;
            uint16 sel6 = controller->csrs->sel6;
            dmc_clear_rdyi(controller);
            if (controller->transfer_type == TYPE_BASEI)
            {
                uint32 baseaddr = ((sel6 >> 14) << 16) | sel4;
                uint16 count = sel6 & 0x3FFF;
                sim_debug(DBG_INF, controller->device, "Completing Base In input transfer, base address=0x%08x count=%d\n", baseaddr, count);
            }
            else if (controller->transfer_type == TYPE_BACCI)
            {
                uint32 addr = ((sel6 >> 14) << 16) | sel4;
                uint16 count = sel6 & 0x3FFF;
                if (controller->transfer_in_io != dmc_is_in_io_set(controller))
                {
                    sim_debug(DBG_TRC, controller->device, "IN IO MISMATCH\n");
                }

                controller->transfer_in_io = dmc_is_in_io_set(controller); // using evdmc the flag is set when the transfer completes - not when it starts, evdca seems to set in only at the start of the transfer - clearing it when it completes
                controller->state = Running;
                if (controller->transfer_in_io)
                {
                    dmc_buffer_queue_add(controller->receive_queue, addr, count);
                    dmc_buffer_fill_receive_buffers(controller);
                    controller->receive_buffer_input_transfers_completed++;
                }
                else
                {
                    dmc_buffer_queue_add(controller->transmit_queue, addr, count);
                    dmc_buffer_send_transmit_buffers(controller);
                    controller->transmit_buffer_input_transfers_completed++;
                }
            }

            controller->transfer_state = Idle;
        }
    }
    else
    {
        if (!dmc_is_rdyi_set(controller))
        {
            uint16 sel6 = controller->csrs->sel6;
            if (controller->transfer_type == TYPE_DMP_MODE)
            {
                uint16 mode = sel6 & DMP_TYPE_INPUT_MASK;
                char * duplex = (mode & 1) ? "Full-Duplex" : "Half-Duplex";
                char * config;
                if (mode & 4)
                {
                    config = "Point-to-point";
                }
                else
                {
                    config = (mode & 2) ? "Tributary station" : "Control Station";
                }

                sim_debug(DBG_INF, controller->device, "Completing Mode input transfer, %s %s\n", duplex, config);
            }
            else if (controller->transfer_type == TYPE_DMP_CONTROL)
            {
                sim_debug(DBG_WRN, controller->device, "Control command (not processed yet)\n");
            }
            else if (controller->transfer_type == TYPE_DMP_RECEIVE)
            {
                sim_debug(DBG_WRN, controller->device, "Receive Buffer command (not processed yet)\n");
            }
            else if (controller->transfer_type == TYPE_DMP_TRANSMIT)
            {
                sim_debug(DBG_WRN, controller->device, "Transmit Buffer command (not processed yet)\n");
            }
            else
            {
                sim_debug(DBG_WRN, controller->device, "Unrecognised command code %hu\n", controller->transfer_type);
            }

            controller->transfer_state = Idle;
        }
    }
}

void dmc_process_command(CTLR *controller)
{
    if (dmc_is_master_clear_set(controller))
    {
        dmc_process_master_clear(controller);
    }
    else
    {
        if (controller->transfer_state == InputTransfer)
        {
            dmc_process_input_transfer_completion(controller);
        }
        else if (controller->transfer_state == OutputTransfer)
        {
            dmc_check_for_output_transfer_completion(controller);
        }
        else if (dmc_is_rqi_set(controller))
        {
            dmc_start_input_transfer(controller);
        }
        else if (dmc_is_dmc (controller) &&
                 controller->csrs->sel0 & ROMI_MASK &&
                 controller->csrs->sel6 == DSPDSR)
            /* DMC-11 or DMR-11, see if ROMI bit is set.  If so, if SEL6 is
               0x22b3 (read line status instruction), set the DTR bit in SEL2.  */
        {
            dmc_setreg (controller, 2, 0x800, 0);
        }
    }
}

t_stat dmc_rd(int32 *data, int32 PA, int32 access)
{
    CTLR *controller = dmc_get_controller_from_address(PA);
    sim_debug(DBG_TRC, controller->device, "dmc_rd(), addr=0x%x access=%d\n", PA, access);
    *data = dmc_getreg(controller, PA, 1);

    return SCPE_OK;
}

t_stat dmc_wr(int32 data, int32 PA, int32 access)
{
    CTLR *controller = dmc_get_controller_from_address(PA);
    int reg = PA & 07;
    uint16 oldValue = dmc_getreg(controller, PA, 0);
    if (access == WRITE)
    {
        sim_debug(DBG_TRC, controller->device, "dmc_wr(), addr=0x%08x, SEL%d, data=0x%04x\n", PA, reg, data);
    }
    else
    {
        sim_debug(DBG_TRC, controller->device, "dmc_wr(), addr=0x%08x, BSEL%d, data=%04x\n", PA, reg, data);
    }

    if (access == WRITE)
    {
        if (PA & 1)
            sim_debug(DBG_WRN, controller->device, "dmc_wr(), Unexpected non-16-bit write access to SEL%d\n", reg);
        dmc_setreg(controller, PA, data, 1);
    }
    else
    {
        uint16 mask;
        if (PA & 1)
        {
            mask = 0xFF00;
            data = data << 8;
        }
        else
        {
            mask = 0x00FF;
        }

        dmc_setreg(controller, PA, (oldValue & ~mask) | (data & mask), 1);
    }

    if (dmc_getsel(reg) == 0 || dmc_getsel(reg) == 1)
    {
        dmc_process_command(controller);
    }

    return SCPE_OK;
}

int32 dmc_rxint (void)
{
    int i;
    int32 ans = 0; /* no interrupt request active */
    for (i=0; i<DMC_NUMDEVICE; i++)
    {
        CTLR *controller = &dmc_ctrls[i];
        if (controller->rxi != 0)
        {
            DIB *dib = (DIB *)controller->device->ctxt;
            ans = dib->vec;
            dmc_clrrxint(controller);
            break;
        }
    }

    return ans;
    }

int32 dmc_txint (void)
{
    int i;
    int32 ans = 0; /* no interrupt request active */
    for (i=0; i<DMC_NUMDEVICE; i++)
    {
        CTLR *controller = &dmc_ctrls[i];
        if (controller->txi != 0)
        {
            DIB *dib = (DIB *)controller->device->ctxt;
            ans = dib->vec + 4;
            dmc_clrtxint(controller);
            break;
        }
    }

    return ans;
}

t_stat dmc_reset (DEVICE *dptr)
{
    t_stat ans = SCPE_OK;
    CTLR *controller = dmc_get_controller_from_device(dptr);

    sim_debug(DBG_TRC, dptr, "dmc_reset()\n");

    dmc_buffer_queue_init_all(controller);
    dmc_clrrxint(controller);
    dmc_clrtxint(controller);
    sim_cancel (controller->device->units); /* stop poll */

    if (!(dptr->flags & DEV_DIS))
    {
        ans = auto_config (dptr->name, DMC_UNITSPERDEVICE);
    }
    return ans;
}

t_stat dmc_attach (UNIT *uptr, char *cptr)
{
    CTLR *controller = dmc_get_controller_from_unit(uptr);
    t_stat ans = SCPE_OK;

    ans = dmc_open_master_socket(controller, cptr);
    if (ans == SCPE_OK)
    {
        controller->line->socket = INVALID_SOCKET;
        uptr->flags = uptr->flags | UNIT_ATT; /* set unit attached flag */
        uptr->filename = (char *)malloc(strlen(cptr)+1);
        strcpy(uptr->filename, cptr);
        controller->line->receive_port = uptr->filename;
        dmc_reset_unit_stats(controller->stats);
    }

    return ans;
}

int dmc_isattached(CTLR *controller)
{
    return controller->master_socket != INVALID_SOCKET;
}

t_stat dmc_detach (UNIT *uptr)
{
    CTLR *controller = dmc_get_controller_from_unit(uptr);
    dmc_error_and_close_socket(controller, "Detach");
    dmc_close_master_socket(controller);
    uptr->flags = uptr->flags & ~UNIT_ATT; /* clear unit attached flag */
    free(uptr->filename);
    uptr->filename = NULL;
    sim_cancel(uptr);

    return SCPE_OK;
}
