1
0
mirror of https://github.com/simh/simh.git synced 2026-01-13 15:27:14 +00:00
simh.simh/PDP11/pdp11_dmc.c
2013-07-11 18:10:54 -04:00

2370 lines
82 KiB
C

/* 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:
25-Jan-13 RJ Error checking for already attached in dmc_settype() fixed.
25-Jan-13 RJ If attach when already attached then detach first.
23-Jan-13 RJ Don't do anything if not attached. See https://github.com/simh/simh/issues/28
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.
------------------------------------------------------------------------------
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: Test MOP.
// TODO: Implement actual DDCMP protocol.
#if defined (VM_PDP10) /* PDP10 version */
#include "pdp10_defs.h"
#elif defined (VM_VAX) /* VAX version */
#include "vax_defs.h"
#else /* PDP-11 version */
#include "pdp11_defs.h"
#endif
#include "sim_tmxr.h"
#include "pdp11_ddcmp.h"
#define DMC_CONNECT_POLL 2 /* Seconds */
extern int32 IREQ (HLVL);
extern int32 tmxr_poll; /* calibrated delay */
extern int32 clk_tps; /* clock ticks per second */
extern int32 tmr_poll; /* instructions per tick */
#if !defined(DMC_NUMDEVICE)
#define DMC_NUMDEVICE 8 /* MAX # DMC-11 devices */
#endif
#if !defined(DMP_NUMDEVICE)
#define DMP_NUMDEVICE 8 /* MAX # DMP-11/DMV-11 devices */
#endif
#define DMC_RDX 8
#define TYPE_BACCI 0
#define TYPE_CNTLI 1
#define TYPE_BASEI 03
#define TYPE_HALT 2
#define TYPE_BACCO 0
#define TYPE_CNTLO 1
#define TYPE_DMP_MODE 2
#define TYPE_DMP_CONTROL 1
#define TYPE_DMP_RECEIVE 0
#define TYPE_DMP_TRANSMIT 4
/* SEL0 */
#define DMC_TYPE_INPUT_MASK 0x0003
#define DMC_IN_HALT_MASK 0x0002
#define DMC_IN_IO_MASK 0x0004
#define DMP_IEO_MASK 0x0010
#define DMC_RQI_MASK 0x0020
#define DMP_RQI_MASK 0x0080
#define DMC_RDYI_MASK 0x0080
#define DMC_IEI_MASK 0x0040
#define DMP_IEI_MASK 0x0001
#define ROMI_MASK 0x0200
#define LU_LOOP_MASK 0x0800
#define MASTER_CLEAR_MASK 0x4000
#define RUN_MASK 0x8000
/* SEL2 */
#define DMP_IN_IO_MASK 0x0004
#define DMP_TYPE_INPUT_MASK 0x0007
#define TYPE_OUTPUT_MASK 0x0003
#define OUT_IO_MASK 0x0004
#define DMC_RDYO_MASK 0x0080
#define DMC_IEO_MASK 0x0040
#define DMP_RDYI_MASK 0x0010
/* BSEL6 */
#define HALT_COMP_MASK 0x0200
#define NXM_MASK 0x0100
#define START_RCVD_MASK 0x0080
#define DISCONNECT_MASK 0x0040
#define LOST_DATA_MASK 0x0010
#define MAINT_RCVD_MASK 0x0008
#define NOBUF_MASK 0x0004
#define TIMEOUT_MASK 0x0002
#define NACK_THRES_MASK 0x0001
#define DSPDSR 0x22b3 /* KMC opcode to move line unit status to SEL2 */
#define SEL0_RUN_BIT 15
#define SEL0_MCLR_BIT 14
#define SEL0_LU_LOOP_BIT 11
#define SEL0_ROMI_BIT 9
#define SEL0_RDI_BIT 7
#define SEL0_DMC_IEI_BIT 6
#define SEL0_DMP_IEI_BIT 0
#define SEL0_DMP_IEO_BIT 4
#define SEL0_DMC_RQI_BIT 5
#define SEL0_DMP_RQI_BIT 7
#define SEL0_IN_IO_BIT 2
#define SEL0_TYPEI_BIT 0
#define SEL2_TYPEO_BIT 0
#define SEL2_RDO_BIT 7
#define SEL2_IEO_BIT 6
#define SEL2_OUT_IO_BIT 2
#define SEL2_LINE_BIT 8
#define SEL2_LINE_BIT_LENGTH 6
#define SEL2_PRIO_BIT 14
#define SEL2_PRIO_BIT_LENGTH 2
#define SEL4_MDM_RI 0x80
#define SEL4_MDM_DTR 0x40
#define SEL4_MDM_RTS 0x20
#define SEL4_MDM_HDX 0x10
#define SEL4_MDM_DSR 0x08
#define SEL4_MDM_CTS 0x04
#define SEL4_MDM_STN 0x02
#define SEL4_MDM_CAR 0x01
#define SEL6_LOST_DATA_BIT 4
#define SEL6_DISCONNECT_BIT 6
#define BUFFER_QUEUE_SIZE 8
#define POLL 1000
#define TRACE_BYTES_PER_LINE 16
struct csrs {
uint16 *sel0;
uint16 *sel2;
uint16 *sel4;
uint16 *sel6;
uint16 *sel10;
};
typedef struct csrs CSRS;
typedef enum
{
Uninitialised, /* before MASTER CLEAR */
Initialised, /* after MASTER CLEAR */
Running, /* after any transmit or receive buffer has been supplied */
Halted /* after reciept of explicit halt input command */
} ControllerState;
typedef enum
{
Idle,
InputTransfer,
OutputTransferReceiveBuffer,
OutputTransferTransmitBuffer,
OutputControl
} TransferState;
typedef enum
{
Available, /* when empty or partially filled on read */
ContainsData,
TransferInProgress
} BufferState;
typedef struct control
{
struct control *next; /* Link */
uint16 sel6; /* Control Out Status Flags */
} CONTROL_OUT;
typedef struct buffer
{
uint32 address; /* unibus address of the buffer */
uint16 count; /* size of the buffer passed to the device by the driver */
uint8 *transfer_buffer; /* the buffer into which data is received or from which it is transmitted*/
int actual_bytes_transferred;/* the number of bytes from the actual block that have been read or written */
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 */
uint32 buffer_return_time;/* time to return buffer to host */
} 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 enum
{
DMC,
DMR,
DMP
} DEVTYPE;
typedef struct dmc_controller {
CSRS *csrs;
DEVICE *device;
UNIT *unit;
int index; /* Index in controller array */
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!
int connect_poll_interval;
TMLN *line;
BUFFER_QUEUE *receive_queue;
BUFFER_QUEUE *transmit_queue;
CONTROL_OUT *control_out;
DEVTYPE dev_type;
uint32 in_int;
uint32 out_int;
uint32 *baseaddr;
uint16 *basesize;
uint8 *modem;
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;
uint32 control_out_operations_completed;
uint32 byte_wait; /* rcv/xmt byte delay */
} CTLR;
#define ctlr up7 /* Unit back pointer to controller */
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_poll_svc(UNIT * uptr);
t_stat dmc_reset (DEVICE * dptr);
t_stat dmc_attach (UNIT * uptr, char * cptr);
t_stat dmc_detach (UNIT * uptr);
int32 dmc_ininta (void);
int32 dmc_outinta (void);
t_stat dmc_setnumdevices (UNIT *uptr, int32 val, char *cptr, void *desc);
t_stat dmc_shownumdevices (FILE *st, UNIT *uptr, int32 val, void *desc);
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_showqueues (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_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);
char *dmc_description (DEVICE *dptr);
char *dmp_description (DEVICE *dptr);
int dmc_is_attached(UNIT* uptr);
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);
uint8 dmc_get_modem(CTLR *controller);
void dmc_set_modem_dtr(CTLR *controller);
void dmc_clr_modem_dtr(CTLR *controller);
void dmc_process_command(CTLR *controller);
t_bool 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_start_transfer_transmit_buffer(CTLR *controller);
void dmc_queue_control_out(CTLR *controller, uint16 sel6);
/* debugging bitmaps */
#define DBG_TRC 0x0001 /* trace routine calls */
#define DBG_REG 0x0002 /* trace read/write registers */
#define DBG_WRN 0x0004 /* display warnings */
#define DBG_INF 0x0008 /* display informational messages (high level trace) */
#define DBG_DAT (TMXR_DBG_PXMT|TMXR_DBG_PRCV) /* display data buffer contents */
#define DBG_DTS 0x0020 /* display data summary */
#define DBG_MDM TMXR_DBG_MDM /* modem related transitions */
#define DBG_CON TMXR_DBG_CON /* display socket open/close, connection establishment */
#define DBG_INT 0x0040 /* display interrupt activites */
DEBTAB dmc_debug[] = {
{"TRACE", DBG_TRC},
{"WARN", DBG_WRN},
{"REG", DBG_REG},
{"INFO", DBG_INF},
{"DATA", DBG_DAT},
{"DATASUM", DBG_DTS},
{"MODEM", DBG_MDM},
{"CONNECT", DBG_CON},
{"INT", DBG_INT},
{0}
};
UNIT dmc_units[DMC_NUMDEVICE+1]; /* One per device and an I/O polling unit */
UNIT dmc_unit_template = { UDATA (&dmc_svc, UNIT_ATTABLE, 0) };
UNIT dmc_poll_unit_template = { UDATA (&dmc_poll_svc, UNIT_DIS, 0) };
UNIT dmp_units[DMP_NUMDEVICE+1]; /* One per device and an I/O polling unit */
CSRS dmc_csrs[DMC_NUMDEVICE];
uint16 dmc_sel0[DMC_NUMDEVICE];
uint16 dmc_sel2[DMC_NUMDEVICE];
uint16 dmc_sel4[DMC_NUMDEVICE];
uint16 dmc_sel6[DMC_NUMDEVICE];
uint32 dmc_speed[DMC_NUMDEVICE];
char dmc_peer[DMC_NUMDEVICE][CBUFSIZE];
char dmc_port[DMC_NUMDEVICE][CBUFSIZE];
uint32 dmc_baseaddr[DMC_NUMDEVICE];
uint16 dmc_basesize[DMC_NUMDEVICE];
uint8 dmc_modem[DMC_NUMDEVICE];
CSRS dmp_csrs[DMP_NUMDEVICE];
uint16 dmp_sel0[DMC_NUMDEVICE];
uint16 dmp_sel2[DMC_NUMDEVICE];
uint16 dmp_sel4[DMC_NUMDEVICE];
uint16 dmp_sel6[DMC_NUMDEVICE];
uint16 dmp_sel10[DMC_NUMDEVICE];
uint32 dmp_speed[DMP_NUMDEVICE];
char dmp_peer[DMP_NUMDEVICE][CBUFSIZE];
char dmp_port[DMP_NUMDEVICE][CBUFSIZE];
uint32 dmp_baseaddr[DMP_NUMDEVICE];
uint16 dmp_basesize[DMP_NUMDEVICE];
uint8 dmp_modem[DMP_NUMDEVICE];
TMLN dmc_ldsc[DMC_NUMDEVICE]; /* line descriptors */
TMXR dmc_desc = { 1, NULL, 0, dmc_ldsc }; /* mux descriptor */
uint32 dmc_ini_summary = 0; /* In Command Interrupt Summary for all controllers */
uint32 dmc_outi_summary = 0; /* Out Command Interrupt Summary for all controllers */
BUFFER_QUEUE dmc_receive_queues[DMC_NUMDEVICE];
BUFFER_QUEUE dmc_transmit_queues[DMC_NUMDEVICE];
TMLN dmp_ldsc[DMC_NUMDEVICE]; /* line descriptors */
TMXR dmp_desc = { 1, NULL, 0, dmp_ldsc }; /* mux descriptor */
BUFFER_QUEUE dmp_receive_queues[DMP_NUMDEVICE];
BUFFER_QUEUE dmp_transmit_queues[DMP_NUMDEVICE];
REG dmc_reg[] = {
{ GRDATA (RXINT, dmc_ini_summary, DEV_RDX, 32, 0) },
{ GRDATA (TXINT, dmc_outi_summary, DEV_RDX, 32, 0) },
{ BRDATA (SEL0, dmc_sel0, DEV_RDX, 16, DMC_NUMDEVICE) },
{ BRDATA (SEL2, dmc_sel2, DEV_RDX, 16, DMC_NUMDEVICE) },
{ BRDATA (SEL4, dmc_sel4, DEV_RDX, 16, DMC_NUMDEVICE) },
{ BRDATA (SEL6, dmc_sel6, DEV_RDX, 16, DMC_NUMDEVICE) },
{ BRDATA (SPEED, dmc_speed, DEV_RDX, 32, DMC_NUMDEVICE) },
{ BRDATA (PEER, dmc_peer, DEV_RDX, 8, DMC_NUMDEVICE*CBUFSIZE) },
{ BRDATA (PORT, dmc_port, DEV_RDX, 8, DMC_NUMDEVICE*CBUFSIZE) },
{ BRDATA (BASEADDR, dmc_baseaddr, DEV_RDX, 32, DMC_NUMDEVICE) },
{ BRDATA (BASESIZE, dmc_basesize, DEV_RDX, 16, DMC_NUMDEVICE) },
{ BRDATA (MODEM, dmc_modem, DEV_RDX, 8, DMC_NUMDEVICE) },
{ NULL } };
REG dmp_reg[] = {
{ BRDATA (SEL0, dmp_sel0, DEV_RDX, 16, DMP_NUMDEVICE) },
{ BRDATA (SEL2, dmp_sel2, DEV_RDX, 16, DMP_NUMDEVICE) },
{ BRDATA (SEL4, dmp_sel4, DEV_RDX, 16, DMP_NUMDEVICE) },
{ BRDATA (SEL6, dmp_sel6, DEV_RDX, 16, DMP_NUMDEVICE) },
{ BRDATA (SPEED, dmp_speed, DEV_RDX, 32, DMP_NUMDEVICE) },
{ BRDATA (PEER, dmp_peer, DEV_RDX, 8, DMP_NUMDEVICE*CBUFSIZE) },
{ BRDATA (PORT, dmp_port, DEV_RDX, 8, DMP_NUMDEVICE*CBUFSIZE) },
{ BRDATA (BASEADDR, dmp_baseaddr, DEV_RDX, 32, DMP_NUMDEVICE) },
{ BRDATA (BASESIZE, dmp_basesize, DEV_RDX, 16, DMP_NUMDEVICE) },
{ BRDATA (MODEM, dmp_modem, DEV_RDX, 8, DMP_NUMDEVICE) },
{ NULL } };
extern DEVICE dmc_dev;
MTAB dmc_mod[] = {
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "LINES", "LINES=n",
&dmc_setnumdevices, &dmc_shownumdevices, (void *) &dmc_dev, "Display number of devices" },
{ MTAB_XTD|MTAB_VUN, 0, "PEER", "PEER=address:port",
&dmc_setpeer, &dmc_showpeer, NULL, "Display destination/source depends on LINEMODE" },
{ MTAB_XTD|MTAB_VUN, 0, "SPEED", "SPEED=bits/sec (0=unrestricted)" ,
&dmc_setspeed, &dmc_showspeed, NULL, "Display rate limit" },
{ MTAB_XTD|MTAB_VUN|MTAB_VALR,0, "TYPE", "TYPE={DMR,DMC}" ,&dmc_settype, &dmc_showtype, NULL, "Set/Display device type" },
{ MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "STATS", "STATS",
&dmc_setstats, &dmc_showstats, NULL, "Display statistics" },
{ MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "QUEUES", "QUEUES",
NULL, &dmc_showqueues, NULL, "Display Queue state" },
{ MTAB_XTD|MTAB_VUN, 0, "CONNECTPOLL", "CONNECTPOLL=seconds",
&dmc_setconnectpoll, &dmc_showconnectpoll, NULL, "Display connection poll interval" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 020, "ADDRESS", "ADDRESS",
&set_addr, &show_addr, NULL, "Bus address" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 1, "VECTOR", "VECTOR",
&set_vec, &show_vec, NULL, "Interrupt vector" },
{ 0 },
};
extern DEVICE dmp_dev;
MTAB dmp_mod[] = {
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "LINES", "LINES=n",
&dmc_setnumdevices, &dmc_shownumdevices, (void *) &dmp_dev, "Display number of devices" },
{ MTAB_XTD|MTAB_VUN, 0, "PEER", "PEER=address:port",
&dmc_setpeer, &dmc_showpeer, NULL, "Display destination/source depends on LINEMODE" },
{ MTAB_XTD|MTAB_VUN, 0, "SPEED", "SPEED=bits/sec (0=unrestricted)" ,
&dmc_setspeed, &dmc_showspeed, NULL, "Display rate limit" },
{ MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "STATS", "STATS",
&dmc_setstats, &dmc_showstats, NULL, "Display statistics" },
{ MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "QUEUES", "QUEUES",
NULL, &dmc_showqueues, NULL, "Display Queue state" },
{ MTAB_XTD|MTAB_VUN, 0, "CONNECTPOLL", "CONNECTPOLL=seconds",
&dmc_setconnectpoll, &dmc_showconnectpoll, NULL, "Display connection poll interval" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 020, "ADDRESS", "ADDRESS",
&set_addr, &show_addr, NULL, "Bus address" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 1, "VECTOR", "VECTOR",
&set_vec, &show_vec, NULL, "Interrupt vector" },
{ 0 },
};
extern DEVICE dmv_dev;
MTAB dmv_mod[] = {
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 0, "LINES", "LINES=n",
&dmc_setnumdevices, &dmc_shownumdevices, (void *) &dmv_dev, "Display number of devices" },
{ MTAB_XTD|MTAB_VUN, 0, "PEER", "PEER=address:port",
&dmc_setpeer, &dmc_showpeer, NULL, "Display destination/source depends on LINEMODE" },
{ MTAB_XTD|MTAB_VUN, 0, "SPEED", "SPEED=bits/sec (0=unrestricted)" ,
&dmc_setspeed, &dmc_showspeed, NULL, "Display rate limit" },
{ MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "STATS", "STATS",
&dmc_setstats, &dmc_showstats, NULL, "Display statistics" },
{ MTAB_XTD|MTAB_VUN|MTAB_NMO, 0, "QUEUES", "QUEUES",
NULL, &dmc_showqueues, NULL, "Display Queue state" },
{ MTAB_XTD|MTAB_VUN, 0, "CONNECTPOLL", "CONNECTPOLL=seconds",
&dmc_setconnectpoll, &dmc_showconnectpoll, NULL, "Display connection poll interval" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 020, "ADDRESS", "ADDRESS",
&set_addr, &show_addr, NULL, "Bus address" },
{ MTAB_XTD|MTAB_VDV|MTAB_VALR, 1, "VECTOR", "VECTOR",
&set_vec, &show_vec, NULL, "Interrupt vector" },
{ 0 },
};
#define IOLN_DMC 010
DIB dmc_dib = { IOBA_AUTO, IOLN_DMC, &dmc_rd, &dmc_wr, 2, IVCL (DMCRX), VEC_AUTO, {&dmc_ininta, &dmc_outinta}, IOLN_DMC };
#define IOLN_DMP 010
DIB dmp_dib = { IOBA_AUTO, IOLN_DMP, &dmc_rd, &dmc_wr, 2, IVCL (DMCRX), VEC_AUTO, {&dmc_ininta, &dmc_outinta }, IOLN_DMP};
#define IOLN_DMV 020
DIB dmv_dib = { IOBA_AUTO, IOLN_DMV, &dmc_rd, &dmc_wr, 2, IVCL (DMCRX), VEC_AUTO, {&dmc_ininta, &dmc_outinta }, IOLN_DMV};
DEVICE dmc_dev =
{ "DMC", dmc_units, dmc_reg, dmc_mod, 2, DMC_RDX, 8, 1, DMC_RDX, 8,
NULL, NULL, &dmc_reset, NULL, &dmc_attach, &dmc_detach,
&dmc_dib, DEV_DISABLE | DEV_DIS | DEV_UBUS | DEV_DEBUG, 0, dmc_debug,
NULL, NULL, &dmc_help, &dmc_help_attach, NULL, &dmc_description };
/*
We have two devices defined here (dmp_dev and dmv_dev) which have the
same units. This would normally never be allowed since two devices can't
actually share units. This problem is avoided in this case since both
devices start out as disabled and the logic in dmc_reset allows only
one of these devices to be enabled at a time. The DMP device is allowed
on Unibus systems and the DMV device Qbus systems.
This monkey business is necessary due to the fact that although both
the DMP and DMV devices have almost the same functionality and almost
the same register programming interface, they are different enough that
they fall in different priorities in the autoconfigure address and vector
rules.
*/
DEVICE dmp_dev =
{ "DMP", dmp_units, dmp_reg, dmp_mod, 2, 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, &dmp_description };
DEVICE dmv_dev =
{ "DMV", dmp_units, dmp_reg, dmv_mod, 2, DMC_RDX, 8, 1, DMC_RDX, 8,
NULL, NULL, &dmc_reset, NULL, &dmc_attach, &dmc_detach,
&dmp_dib, DEV_DISABLE | DEV_DIS | DEV_QBUS | DEV_DEBUG, 0, dmc_debug,
NULL, NULL, &dmc_help, &dmc_help_attach, NULL, &dmp_description };
CTLR dmc_ctrls[DMC_NUMDEVICE + DMP_NUMDEVICE];
int dmc_is_attached(UNIT* uptr)
{
return uptr->flags & UNIT_ATT;
}
int dmc_is_dmc(CTLR *controller)
{
return controller->dev_type != DMP;
}
CTLR *dmc_get_controller_from_unit(UNIT *unit)
{
return (CTLR *)unit->ctlr;
}
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[(address - dib->ba) >> ((UNIBUS) ? 3 : 4)];
}
/* not found */
return 0;
}
t_stat dmc_showpeer (FILE* st, UNIT* uptr, int32 val, void* desc)
{
DEVICE *dptr = (UNIBUS) ? ((&dmc_dev == find_dev_from_unit(uptr)) ? &dmc_dev : &dmp_dev) : &dmv_dev;
int32 dmc = (int32)(uptr-dptr->units);
char *peer = ((dptr == &dmc_dev)? &dmc_peer[dmc][0] : &dmp_peer[dmc][0]);
if (peer[0])
fprintf(st, "peer=%s", peer);
else
fprintf(st, "peer=unspecified");
return SCPE_OK;
}
t_stat dmc_setpeer (UNIT* uptr, int32 val, char* cptr, void* desc)
{
DEVICE *dptr = (UNIBUS) ? ((&dmc_dev == find_dev_from_unit(uptr)) ? &dmc_dev : &dmp_dev) : &dmv_dev;
int32 dmc = (int32)(uptr-dptr->units);
char *peer = ((dptr == &dmc_dev)? &dmc_peer[dmc][0] : &dmp_peer[dmc][0]);
t_stat status = SCPE_OK;
char host[CBUFSIZE], port[CBUFSIZE];
CTLR *controller = dmc_get_controller_from_unit(uptr);
if ((!cptr) || (!*cptr))
return SCPE_ARG;
if (dmc_is_attached(uptr))
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(peer, cptr, CBUFSIZE-1);
return status;
}
t_stat dmc_showspeed (FILE* st, UNIT* uptr, int32 val, void* desc)
{
DEVICE *dptr = (UNIBUS) ? ((&dmc_dev == find_dev_from_unit(uptr)) ? &dmc_dev : &dmp_dev) : &dmv_dev;
int32 dmc = (int32)(uptr-dptr->units);
uint32 *speeds = ((dptr == &dmc_dev)? dmc_speed : dmp_speed);
if (speeds[dmc] > 0)
fprintf(st, "speed=%d bits/sec", speeds[dmc]);
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;
DEVICE *dptr = (UNIBUS) ? ((&dmc_dev == find_dev_from_unit(uptr)) ? &dmc_dev : &dmp_dev) : &dmv_dev;
int32 dmc = (int32)(uptr-dptr->units);
uint32 *speeds = ((dptr == &dmc_dev)? dmc_speed : dmp_speed);
t_stat r;
int32 newspeed;
if (cptr == NULL)
return SCPE_ARG;
newspeed = (int32) get_uint (cptr, 10, 100000000, &r);
if (r != SCPE_OK)
return r;
speeds[dmc] = newspeed;
return SCPE_OK;
}
t_stat dmc_showtype (FILE* st, UNIT* uptr, int32 val, void* desc)
{
DEVICE *dptr = (UNIBUS) ? ((&dmc_dev == find_dev_from_unit(uptr)) ? &dmc_dev : &dmp_dev) : &dmv_dev;
int32 dmc = (int32)(uptr-dptr->units);
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=%s", (UNIBUS) ? "DMP" : "DMV");
break;
}
default:
{
fprintf(st, "type=???");
break;
}
}
return SCPE_OK;
}
t_stat dmc_settype (UNIT* uptr, int32 val, char* cptr, void* desc)
{
char gbuf[80];
t_stat status = SCPE_OK;
CTLR *controller = dmc_get_controller_from_unit(uptr);
if ((!cptr) || (!*cptr))
return SCPE_2FARG;
if (dmc_is_attached(uptr))
return SCPE_ALATT;
cptr = get_glyph (cptr, gbuf, 0);
if (strcmp (gbuf,"DMC")==0)
{
controller->dev_type = DMC;
}
else if (strcmp (gbuf,"DMR")==0)
{
controller->dev_type = DMR;
}
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);
fprintf (st, "%s%d\n", controller->device->name, (int)(uptr-controller->device->units));
tmxr_fstats (st, controller->line, -1);
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);
fprintf(st, "Control Out operations processed=%d\n", controller->control_out_operations_completed);
return SCPE_OK;
}
void dmc_showqueue (FILE* st, BUFFER_QUEUE *queue, t_bool detail)
{
size_t i;
int states[3] = {0,0,0};
static const char *state_names[] = {"Available", "ContainsData", "TransferInProgress"};
fprintf (st, "%s Queue:\n", queue->name);
fprintf (st, " Count: %d\n", queue->count);
fprintf (st, " Head: %d\n", queue->head);
fprintf (st, " Tail: %d\n", queue->tail);
for (i=0; i<sizeof(queue->queue)/sizeof(queue->queue[0]); ++i)
{
++states[queue->queue[i].state];
}
fprintf (st, " Available: %d\n", states[Available]);
fprintf (st, " Contains Data: %d\n", states[ContainsData]);
fprintf (st, " Transfer In Progress: %d\n", states[TransferInProgress]);
if (detail)
{
for (i=0; i<sizeof(queue->queue)/sizeof(queue->queue[0]); ++i)
{
fprintf (st, "%s.queue[%d]\n", queue->name, i);
fprintf (st, " address: 0x%08X\n", queue->queue[i].address);
fprintf (st, " count: 0x%04X\n", queue->queue[i].count);
fprintf (st, " actual_bytes_transferred: 0x%04X\n", queue->queue[i].actual_bytes_transferred);
fprintf (st, " state: %s\n", state_names[queue->queue[i].state]);
if (queue->queue[i].is_loopback)
fprintf (st, " is_loopback: %s\n", queue->queue[i].is_loopback ? "true" : "false");
if (queue->queue[i].buffer_return_time)
fprintf (st, " buffer_return_time: 0x%08X\n", queue->queue[i].buffer_return_time);
}
}
}
t_stat dmc_showqueues (FILE* st, UNIT* uptr, int32 val, void* desc)
{
CTLR *controller = dmc_get_controller_from_unit(uptr);
static const char *states[] = {"Uninitialised", "Initialised", "Running", "Halted"};
static const char *tstates[] = {"Idle", "InputTransfer", "OutputTransferReceiveBuffer", "OutputTransferTransmitBuffer", "OutputControl"};
dmc_showstats (st, uptr, val, desc);
fprintf (st, "State: %s\n", states[controller->state]);
fprintf (st, "TransferState: %s\n", tstates[controller->transfer_state]);
dmc_showqueue (st, controller->transmit_queue, TRUE);
dmc_showqueue (st, controller->receive_queue, TRUE);
if (controller->control_out)
{
CONTROL_OUT *control;
fprintf (st, "Control Out Queue:\n");
for (control=controller->control_out; control; control=control->next)
{
fprintf (st, " SEL6: 0x%04X\n", control->sel6);
}
}
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);
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;
controller->control_out_operations_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;
}
/* SET LINES processor */
t_stat dmc_setnumdevices (UNIT *uptr, int32 val, char *cptr, void *desc)
{
int32 newln;
uint32 i, j;
t_stat r;
DEVICE *dptr = (DEVICE *)desc;
TMXR *mp = (dptr == &dmc_dev) ? &dmc_desc : &dmp_desc;
int maxunits = (&dmc_dev == dptr) ? DMC_NUMDEVICE : DMP_NUMDEVICE;
DIB *dibptr = (DIB *)dptr->ctxt;
int addrlnt = (UNIBUS) ? IOLN_DMC : IOLN_DMV;
for (j=0; j<2; j++)
{
dptr = (j == 0) ? &dmc_dev : &dmp_dev;
for (i=0; i<dptr->numunits; i++)
if (dptr->units[i].flags&UNIT_ATT)
return SCPE_ALATT;
}
dptr = (DEVICE *)desc;
for (i=0; i<dptr->numunits; i++)
if (dptr->units[i].flags&UNIT_ATT)
return SCPE_ALATT;
if (cptr == NULL)
return SCPE_ARG;
newln = (int32) get_uint (cptr, 10, maxunits, &r);
if ((r != SCPE_OK) || (newln == (dptr->numunits - 1)))
return r;
if (newln == 0)
return SCPE_ARG;
for (i=dptr->numunits; i<(uint32)newln; ++i)
{
dptr->units[i] = dmc_unit_template;
dptr->units[i].ctlr = &dmc_ctrls[(dptr == &dmc_dev) ? i : i+DMC_NUMDEVICE];
}
dibptr->lnt = newln * addrlnt; /* set length */
dptr->numunits = newln + 1;
dptr->units[newln] = dmc_poll_unit_template;
mp->lines = newln;
return dmc_reset ((DEVICE *)desc); /* setup devices and auto config */
}
t_stat dmc_shownumdevices (FILE *st, UNIT *uptr, int32 val, void *desc)
{
DEVICE *dptr = (UNIBUS) ? find_dev_from_unit (uptr) : &dmv_dev;
fprintf (st, "lines=%d", dptr->numunits-1);
return SCPE_OK;
}
t_stat dmc_help (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *cptr)
{
char devname[16];
sprintf(devname, "%s11", (dptr == &dmc_dev) ? "DMC" : ((UNIBUS) ? "DMP" : "DMV"));
fprintf(st, "The %s is a synchronous serial point-to-point communications device.\n", devname);
fprintf(st, "A real %s transports data using DDCMP, the emulated device makes a\n", devname);
fprintf(st, "TCP/IP connection to another emulated device and sends length-prefixed\n");
fprintf(st, "messages across the connection, each message representing a single buffer\n");
fprintf(st, "passed to the %s. The %s can be used for point-to-point DDCMP\n", devname, devname);
fprintf(st, "connections carrying DECnet and other types of networking, e.g. from ULTRIX\n");
fprintf(st, "or DSM.\n\n");
fprintf(st, "A total of %d %s devices can be simulated concurrently. The number\n", (dptr == &dmc_dev) ? DMC_NUMDEVICE : DMP_NUMDEVICE, devname);
fprintf(st, "of simulated %s devices or lines can be specified with command:\n", devname);
fprintf(st, "\n");
fprintf(st, " sim> SET %s LINES=n\n", dptr->name);
fprintf(st, "\n");
fprintf(st, "To set the host and port to which data is to be transmitted use the\n");
fprintf(st, "following command (required for PRIMARY and SECONDARY, secondary will check\n");
fprintf(st, "it is receiving from the configured primary):\n");
fprintf(st, "\n");
fprintf(st, " sim> SET %s0 PEER=host:port\n", dptr->name);
fprintf(st, "\n");
fprintf(st, "The device must be attached to a receive port, use the ATTACH command\n");
fprintf(st, "specifying 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\n");
fprintf(st, "using the following command:\n");
fprintf(st, "\n");
fprintf(st, " sim> SET %s0 CONNECTPOLL=n\n", dptr->name);
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\n");
fprintf(st, "can set the bit rate of the simulated line can be set using the following\n");
fprintf(st, "command:\n\n");
fprintf(st, " sim> SET %s0 SPEED=n\n", dptr->name);
fprintf(st, "\n");
fprintf(st, "Where n is the number of data bits per second that the simulated line runs\n");
fprintf(st, "at. In practice this is implemented as a delay in reading the bytes from\n");
fprintf(st, "the socket. Use a value of zero to run at full speed with no artificial\n");
fprintf(st, "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, " sim> SET %s ENABLE\n", dptr->name);
fprintf(st, " sim> SET %s0 PEER=LOCALHOST:2222\n", dptr->name);
fprintf(st, " sim> ATTACH %s0 1111\n", dptr->name);
fprintf(st, "\n");
fprintf(st, "Machine 2\n");
fprintf(st, " sim> SET %s ENABLE\n", dptr->name);
fprintf(st, " sim> SET %s0 PEER= LOCALHOST:1111\n", dptr->name);
fprintf(st, " sim> ATTACH %s0 2222\n", dptr->name);
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.\n");
fprintf(st, " 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, " sim> SET %s DEBUG\n", dptr->name);
fprintf(st, "\n");
fprintf(st, "However it is recommended to use the following when sending traces:\n");
fprintf(st, "\n");
fprintf(st, " sim> SET %s DEBUG=REG;INFO;WARN\n", dptr->name);
fprintf(st, "\n");
return SCPE_OK;
}
t_stat dmc_help_attach (FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, char *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, "listening port to be used when the incoming connection is established:\n\n");
fprintf (st, " sim> ATTACH %sn {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.\n\n");
return SCPE_OK;
}
void dmc_setinint(CTLR *controller)
{
if (!dmc_is_iei_set(controller))
return;
if (!controller->in_int)
sim_debug(DBG_INT, controller->device, "SET_INT(RX:%d)\n", controller->index);
controller->in_int = 1;
dmc_ini_summary |= (1u << controller->index);
SET_INT(DMCRX);
}
void dmc_clrinint(CTLR *controller)
{
controller->in_int = 0;
if (dmc_ini_summary & (1u << controller->index))
sim_debug(DBG_INT, controller->device, "CLR_INT(RX:%d)\n", controller->index);
dmc_ini_summary &= ~(1u << controller->index);
if (!dmc_ini_summary)
CLR_INT(DMCRX);
else
SET_INT(DMCRX);
}
void dmc_setoutint(CTLR *controller)
{
if (!dmc_is_ieo_set(controller))
return;
if (!controller->out_int)
sim_debug(DBG_INT, controller->device, "SET_INT(TX:%d)\n", controller->index);
controller->out_int = 1;
dmc_outi_summary |= (1u << controller->index);
SET_INT(DMCTX);
}
void dmc_clroutint(CTLR *controller)
{
controller->out_int = 0;
if (dmc_outi_summary & (1u << controller->index))
sim_debug(DBG_INT, controller->device, "CLR_INT(TX:%d)\n", controller->index);
dmc_outi_summary &= ~(1u << controller->index);
if (!dmc_outi_summary)
CLR_INT(DMCTX);
else
SET_INT(DMCTX);
CLR_INT(DMCTX);
}
int dmc_getsel(int addr)
{
return (addr >> 1) & ((UNIBUS) ? 03 : 07);
}
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)
{
if (dmc_is_rdyi_set(controller))
{
sim_debug(
trace_level,
controller->device,
"%s SEL4 (0x%04x) %s%s%s%s%s%s%s\n",
prefix,
data,
dmc_bitfld(data, /* SEL4_MDM_RI */ 7, 1) ? "RI " : "",
dmc_bitfld(data, /* SEL4_MDM_RTS */ 6, 1) ? "RTS " : "",
dmc_bitfld(data, /* SEL4_MDM_HDX */ 4, 1) ? "HDX " : "",
dmc_bitfld(data, /* SEL4_MDM_DSR */ 3, 1) ? "DSR " : "",
dmc_bitfld(data, /* SEL4_MDM_CTS */ 2, 1) ? "CTS " : "",
dmc_bitfld(data, /* SEL4_MDM_STN */ 1, 1) ? "STN " : "",
dmc_bitfld(data, /* SEL4_MDM_CAR */ 0, 1) ? "CAR " : "");
}
else
{
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)
{
if (dmc_is_rdyi_set(controller))
{
sim_debug(
trace_level,
controller->device,
"%s SEL6 (0x%04x) %s%s\n",
prefix,
data,
dmc_bitfld(data, SEL6_LOST_DATA_BIT, 1) ? "LOST_DATA " : "",
dmc_bitfld(data, SEL6_DISCONNECT_BIT, 1) ? "DISCONNECT " : "");
}
else
{
sim_debug(
trace_level,
controller->device,
"%s SEL6 (0x%04x)\n",
prefix,
data);
}
}
void dmc_dumpregsel10(CTLR *controller, int trace_level, char *prefix, uint16 data)
{
sim_debug(
trace_level,
controller->device,
"%s SEL10 (0x%04x) %s\n",
prefix,
data,
dmc_bitfld(data, SEL6_LOST_DATA_BIT, 1) ? "LOST_DATA " : "");
}
uint16 dmc_getreg(CTLR *controller, int reg)
{
uint16 ans = 0;
switch (dmc_getsel(reg))
{
case 00:
ans = *controller->csrs->sel0;
dmc_dumpregsel0(controller, DBG_REG, "Getting", ans);
break;
case 01:
ans = *controller->csrs->sel2;
dmc_dumpregsel2(controller, DBG_REG, "Getting", ans);
break;
case 02:
ans = *controller->csrs->sel4;
dmc_dumpregsel4(controller, DBG_REG, "Getting", ans);
break;
case 03:
ans = *controller->csrs->sel6;
dmc_dumpregsel6(controller, DBG_REG, "Getting", ans);
break;
case 04:
ans = *controller->csrs->sel10;
dmc_dumpregsel10(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)
{
char *trace = "Setting";
switch (dmc_getsel(reg))
{
case 00:
dmc_dumpregsel0(controller, DBG_REG, trace, data);
*controller->csrs->sel0 = data;
break;
case 01:
dmc_dumpregsel2(controller, DBG_REG, trace, data);
*controller->csrs->sel2 = data;
break;
case 02:
dmc_dumpregsel4(controller, DBG_REG, trace, data);
*controller->csrs->sel4 = data;
break;
case 03:
dmc_dumpregsel6(controller, DBG_REG, trace, data);
*controller->csrs->sel6 = data;
break;
case 04:
dmc_dumpregsel10(controller, DBG_REG, trace, data);
*controller->csrs->sel10 = 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_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);
dmc_setreg(controller, 4, *controller->modem);
dmc_setreg(controller, 6, *controller->modem & SEL4_MDM_DTR);
}
else
{
dmc_setreg(controller, 2, *controller->csrs->sel2 | DMP_RDYI_MASK);
}
dmc_setinint(controller);
}
void dmc_clear_rdyi(CTLR *controller)
{
if (dmc_is_dmc(controller))
{
dmc_setreg(controller, 0, *controller->csrs->sel0 & ~DMC_RDYI_MASK);
}
else
{
dmc_setreg(controller, 2, *controller->csrs->sel2 & ~DMP_RDYI_MASK);
}
}
void dmc_set_rdyo(CTLR *controller)
{
dmc_setreg(controller, 2, *controller->csrs->sel2 | DMC_RDYO_MASK);
dmc_setoutint(controller);
}
uint8 dmc_get_modem(CTLR *controller)
{
int32 modem_bits;
tmxr_set_get_modem_bits (controller->line, 0, 0, &modem_bits);
*controller->modem &= ~(SEL4_MDM_CTS|SEL4_MDM_CAR|SEL4_MDM_RI|SEL4_MDM_DSR);
*controller->modem |= (modem_bits&TMXR_MDM_DCD) ? SEL4_MDM_CAR : 0;
*controller->modem |= (modem_bits&TMXR_MDM_CTS) ? SEL4_MDM_CTS : 0;
*controller->modem |= (modem_bits&TMXR_MDM_DSR) ? SEL4_MDM_DSR : 0;
*controller->modem |= (modem_bits&TMXR_MDM_RNG) ? SEL4_MDM_RI : 0;
return (*controller->modem);
}
void dmc_set_modem_dtr(CTLR *controller)
{
if (dmc_is_attached(controller->unit) && (!(SEL4_MDM_DTR & *controller->modem)))
{
tmxr_set_get_modem_bits (controller->line, TMXR_MDM_DTR|TMXR_MDM_RTS, 0, NULL);
*controller->modem |= SEL4_MDM_DTR|SEL4_MDM_RTS;
controller->line->rcve = 1;
}
}
void dmc_clr_modem_dtr(CTLR *controller)
{
tmxr_set_get_modem_bits (controller->line, 0, TMXR_MDM_DTR|TMXR_MDM_RTS, NULL);
*controller->modem &= ~(SEL4_MDM_DTR|SEL4_MDM_CTS);
controller->line->rcve = 0;
}
void dmc_set_lost_data(CTLR *controller)
{
dmc_setreg(controller, 6, *controller->csrs->sel6 | LOST_DATA_MASK);
}
void dmc_clear_master_clear(CTLR *controller)
{
dmc_setreg(controller, 0, *controller->csrs->sel0 & ~MASTER_CLEAR_MASK);
}
void dmc_set_run(CTLR *controller)
{
dmc_setreg(controller, 0, *controller->csrs->sel0 | RUN_MASK);
}
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;
}
void dmc_set_type_output(CTLR *controller, int type)
{
dmc_setreg(controller, 2, *controller->csrs->sel2 | (type & TYPE_OUTPUT_MASK));
}
void dmc_set_out_io(CTLR *controller)
{
dmc_setreg(controller, 2, *controller->csrs->sel2 | OUT_IO_MASK);
}
void dmc_clear_out_io(CTLR *controller)
{
dmc_setreg(controller, 2, *controller->csrs->sel2 & ~OUT_IO_MASK);
}
void dmc_process_master_clear(CTLR *controller)
{
CONTROL_OUT *control;
sim_debug(DBG_INF, controller->device, "Master clear\n");
dmc_clear_master_clear(controller);
dmc_clr_modem_dtr(controller);
controller->state = Initialised;
while ((control = controller->control_out))
{
controller->control_out = control->next;
free (control);
}
controller->control_out = NULL;
dmc_setreg(controller, 0, 0);
if (controller->dev_type == DMR)
{
/* DMR-11 indicates microdiagnostics complete when this is set */
dmc_setreg(controller, 2, 0x8000);
}
else
{
/* preserve contents of BSEL3 if DMC-11 */
dmc_setreg(controller, 2, *controller->csrs->sel2 & 0xFF00);
}
if (controller->dev_type == DMP)
{
dmc_setreg(controller, 4, 077);
}
else
{
dmc_setreg(controller, 4, 0);
}
if (controller->dev_type == DMP)
{
dmc_setreg(controller, 6, 0305);
}
else
{
dmc_setreg(controller, 6, 0);
}
dmc_buffer_queue_init_all(controller);
controller->transfer_state = Idle;
dmc_set_run(controller);
}
void dmc_start_input_transfer(CTLR *controller)
{
sim_debug(DBG_INF, controller->device, "Starting input transfer\n");
controller->transfer_state = InputTransfer;
dmc_set_rdyi(controller);
}
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);
dmc_setreg(controller, 6, (((addr & 0x30000)) >> 2) | count);
controller->transfer_state = (is_receive) ? OutputTransferReceiveBuffer : OutputTransferTransmitBuffer;
dmc_set_type_output(controller, TYPE_BACCO);
dmc_set_rdyo(controller);
}
void dmc_start_control_output_transfer(CTLR *controller)
{
if ((!controller->control_out) ||
(controller->transfer_state != Idle) ||
(dmc_is_rdyo_set(controller)))
return;
sim_debug(DBG_INF, controller->device, "Starting control output transfer: SEL6 = 0x%04X\n", controller->control_out->sel6);
controller->transfer_state = OutputControl;
dmc_clear_out_io(controller);
dmc_set_type_output(controller, TYPE_CNTLO);
dmc_setreg (controller, 6, controller->control_out->sel6);
dmc_set_rdyo(controller);
}
t_stat dmc_svc(UNIT* uptr)
{
CTLR *controller = dmc_get_controller_from_unit(uptr);
if (dmc_is_attached(controller->unit))
{
dmc_start_control_output_transfer(controller);
dmc_buffer_send_transmit_buffers(controller);
if (controller->transfer_state == Idle)
dmc_start_transfer_transmit_buffer(controller);
dmc_buffer_fill_receive_buffers(controller);
if (controller->transfer_state == Idle)
dmc_start_transfer_receive_buffer(controller);
sim_activate (uptr, tmxr_poll);
}
return SCPE_OK;
}
static t_stat dmc_poll_svc (UNIT *uptr)
{
DEVICE *dptr = ((CTLR *)uptr->ctlr)->device;
int maxunits = (&dmc_dev == dptr) ? DMC_NUMDEVICE : DMP_NUMDEVICE;
DIB *dibptr = (DIB *)dptr->ctxt;
int addrlnt = (UNIBUS) ? IOLN_DMC : IOLN_DMV;
TMXR *mp = (dptr == &dmc_dev) ? &dmc_desc : &dmp_desc;
int32 dmc, active, attached;
sim_debug(DBG_TRC, dptr, "dmc_poll_svc()\n");
dmc = tmxr_poll_conn(mp);
if (dmc >= 0) /* new connection? */
{
dmc_get_modem ((CTLR *)dptr->units[dmc].ctlr);
}
tmxr_poll_rx (mp);
tmxr_poll_tx (mp);
for (dmc=active=attached=0; dmc < mp->lines; dmc++)
{
TMLN *lp = &mp->ldsc[dmc];
CTLR *controller = (CTLR *)dptr->units[dmc].ctlr;
uint8 old_modem, new_modem;
if (dptr->units[dmc].flags & UNIT_ATT)
++attached;
if (mp->ldsc[dmc].conn)
++active;
old_modem = *controller->modem;
new_modem = dmc_get_modem (controller);
if ((old_modem & SEL4_MDM_DSR) &&
(!(new_modem & SEL4_MDM_DSR)))
dmc_queue_control_out(controller, DISCONNECT_MASK);
if (lp->xmte && tmxr_tpbusyln(lp))
{
sim_debug(DBG_DAT, dptr, "dmc_poll_svc(dmc=%d) - Packet Transmission of remaining %d bytes restarting...\n", dmc, tmxr_tpqln (lp));
dmc_svc (&dptr->units[dmc]); /* Flush pending output */
}
dmc_buffer_fill_receive_buffers(controller);
}
if (active)
sim_clock_coschedule (uptr, tmxr_poll); /* reactivate */
else
{
for (dmc=0; dmc < mp->lines; dmc++)
{
uint32 *speeds = (dptr == &dmc_dev) ? dmc_speed : dmp_speed;
CTLR *controller = (CTLR *)dptr->units[dmc].ctlr;
if (speeds[dmc]/8)
controller->byte_wait = (tmr_poll*clk_tps)/(speeds[dmc]/8);
else
controller->byte_wait = 0;
}
if (attached)
sim_activate_after (uptr, DMC_CONNECT_POLL*1000000);/* periodic check for connections */
}
return SCPE_OK;
}
void dmc_buffer_trace_line(int tracelevel, CTLR *controller, const 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, const 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].transfer_buffer = NULL;
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)
{
free(q->queue[q->head].transfer_buffer);
q->queue[q->head].transfer_buffer = NULL;
q->queue[q->head].state = Available;
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_queue_control_out(CTLR *controller, uint16 sel6)
{
CONTROL_OUT *control = calloc(1, sizeof(*control));
CONTROL_OUT *last = NULL;
control->sel6 = sel6;
if (controller->control_out)
{
last = controller->control_out;
while (last->next)
{
last = last->next;
}
last->next = control;
}
else
controller->control_out = control;
}
/* returns true if some data was received */
t_bool dmc_buffer_fill_receive_buffers(CTLR *controller)
{
int ans = FALSE;
if (controller->state == Running)
{
BUFFER *buffer = dmc_buffer_queue_find_first_available(controller->receive_queue);
while ((buffer) && (buffer->state == Available))
{
const uint8 *pbuf;
size_t size;
tmxr_get_packet_ln (controller->line, &pbuf, &size);
if (!pbuf)
break;
buffer->actual_bytes_transferred = size;
controller->buffers_received_from_net++;
dmc_buffer_trace (controller, pbuf, buffer->actual_bytes_transferred, "REC ", buffer->address);
buffer->is_loopback = FALSE;
buffer->state = ContainsData;
Map_WriteB (buffer->address, buffer->actual_bytes_transferred, (uint8 *)pbuf);
ans = TRUE;
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) && (buffer->state == Available))
{
t_stat r;
/* only send the buffer if it actually has some data, sometimes get zero length buffers - don't send these */
if (buffer->count > 0)
{
if (buffer->transfer_buffer == NULL)
{
int n;
/* construct buffer */
buffer->transfer_buffer = (uint8 *)malloc (buffer->count);
n = Map_ReadB (buffer->address, buffer->count, buffer->transfer_buffer);
if (n > 0)
{
sim_debug(DBG_WRN, controller->device, "DMA error\n");
}
}
r = tmxr_put_packet_ln (controller->line, buffer->transfer_buffer, buffer->count);
if (r == SCPE_OK)
{
buffer->actual_bytes_transferred = buffer->count;
dmc_buffer_trace (controller, buffer->transfer_buffer, buffer->count, "TRAN", buffer->address);
free (buffer->transfer_buffer);
buffer->transfer_buffer = NULL;
controller->buffers_transmitted_to_net++;
buffer->state = ContainsData; // so won't try to transmit again
ans = TRUE;
if (controller->byte_wait)
{
buffer->buffer_return_time = sim_grtime() + controller->byte_wait*buffer->count;
sim_activate_notbefore(controller->unit, buffer->buffer_return_time);
}
}
else
break; /* poll again later to send more bytes */
}
else
buffer->state = ContainsData; // so won't try to transmit again
if (controller->byte_wait)
break; /* Pause until service routine completes buffer return */
buffer = buffer->next;
}
if (ans && (!controller->byte_wait))
dmc_svc (controller->unit);
return ans;
}
void dmc_start_transfer_receive_buffer(CTLR *controller)
{
BUFFER *head = dmc_buffer_queue_head(controller->receive_queue);
if ((!head) ||
(controller->transfer_state != Idle) ||
(dmc_is_rdyo_set(controller)))
return;
if (head->state == ContainsData)
{
head->state = TransferInProgress;
dmc_start_data_output_transfer(controller, head->address, head->actual_bytes_transferred, TRUE);
}
}
void dmc_start_transfer_transmit_buffer(CTLR *controller)
{
BUFFER *head = dmc_buffer_queue_head(controller->transmit_queue);
if ((!head) ||
(controller->transfer_state != Idle) ||
(dmc_is_rdyo_set(controller)))
return;
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)) ||
((controller->transfer_state != OutputTransferReceiveBuffer) &&
(controller->transfer_state != OutputTransferTransmitBuffer)))
return;
sim_debug(DBG_INF, controller->device, "Output transfer completed\n");
dmc_buffer_queue_release_head((controller->transfer_state == OutputTransferReceiveBuffer) ? controller->receive_queue : controller->transmit_queue);
controller->transmit_buffer_output_transfers_completed++;
controller->transfer_state = Idle;
dmc_process_command(controller); // check for any other transfers
}
void dmc_check_for_output_control_completion(CTLR *controller)
{
CONTROL_OUT *control = controller->control_out;
if ((dmc_is_rdyo_set(controller)) ||
(controller->transfer_state != OutputControl))
return;
dmc_dumpregsel6(controller, DBG_INF, "Output command completed:", control->sel6);
controller->transfer_state = Idle;
controller->control_out = control->next;
free(control);
controller->control_out_operations_completed++;
dmc_process_command(controller); // check for any other 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;
controller->transfer_type = dmc_get_input_transfer_type(controller);
dmc_clear_rdyi(controller);
if (controller->transfer_type == TYPE_BASEI)
{
*controller->baseaddr = ((sel6 >> 14) << 16) | sel4;
*controller->basesize = sel6 & 0x3FFF;
sim_debug(DBG_INF, controller->device, "Completing Base In input transfer, base address=0x%08x count=%d\n", *controller->baseaddr, *controller->basesize);
}
else if (controller->transfer_type == TYPE_BACCI)
{
uint32 addr = ((sel6 >> 14) << 16) | sel4;
uint16 count = sel6 & 0x3FFF;
controller->transfer_in_io = dmc_is_in_io_set(controller);
if (controller->state != Running)
{
controller->state = Running;
dmc_set_modem_dtr (controller);
}
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++;
}
}
else if (controller->transfer_type == TYPE_HALT)
{
sim_debug(DBG_INF, controller->device, "Halt Command Received\n");
controller->state = Halted;
dmc_clr_modem_dtr(controller);
dmc_queue_control_out(controller, HALT_COMP_MASK);
return;
}
controller->transfer_state = Idle;
dmc_process_command (controller);
}
}
else /* DMP */
{
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);
return;
}
if (controller->transfer_state == InputTransfer)
{
dmc_process_input_transfer_completion(controller);
return;
}
if ((controller->transfer_state == OutputTransferReceiveBuffer) ||
(controller->transfer_state == OutputTransferTransmitBuffer))
{
dmc_check_for_output_transfer_completion(controller);
return;
}
if (controller->transfer_state == OutputControl)
{
dmc_check_for_output_control_completion(controller);
return;
}
/* transfer_state Idle */
if (dmc_is_rqi_set(controller))
{
dmc_start_input_transfer(controller);
return;
}
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);
}
else
{
dmc_start_control_output_transfer(controller);
dmc_start_transfer_transmit_buffer(controller);
dmc_start_transfer_receive_buffer(controller);
}
}
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);
return SCPE_OK;
}
t_stat dmc_wr(int32 data, int32 PA, int32 access)
{
CTLR *controller = dmc_get_controller_from_address(PA);
int reg = PA & ((UNIBUS) ? 07 : 017);
uint16 oldValue = dmc_getreg(controller, PA);
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=%02x\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);
}
else
{
uint16 mask;
if (PA & 1)
{
mask = 0xFF00;
data = data << 8;
}
else
{
mask = 0x00FF;
}
dmc_setreg(controller, PA, (oldValue & ~mask) | (data & mask));
}
if (dmc_is_attached(controller->unit) && (dmc_getsel(reg) == 0 || dmc_getsel(reg) == 1))
{
dmc_process_command(controller);
}
return SCPE_OK;
}
int32 dmc_ininta (void)
{
int i;
int32 ans = 0; /* no interrupt request active */
for (i=0; i<DMC_NUMDEVICE+DMP_NUMDEVICE; i++)
{
CTLR *controller = &dmc_ctrls[i];
if (controller->in_int != 0)
{
DIB *dib = (DIB *)controller->device->ctxt;
ans = dib->vec + (8 * (int)(controller->unit - controller->device->units));
dmc_clrinint(controller);
sim_debug(DBG_INT, controller->device, "RXINTA Device %d - Vector: 0x%x\n", (int)(controller->unit-controller->device->units), ans);
break;
}
}
return ans;
}
int32 dmc_outinta (void)
{
int i;
int32 ans = 0; /* no interrupt request active */
for (i=0; i<DMC_NUMDEVICE+DMP_NUMDEVICE; i++)
{
CTLR *controller = &dmc_ctrls[i];
if (controller->out_int != 0)
{
DIB *dib = (DIB *)controller->device->ctxt;
ans = dib->vec + 4 + (8 * (int)(controller->unit - controller->device->units));
dmc_clroutint(controller);
sim_debug(DBG_INT, controller->device, "TXINTA Device %d - Vector: 0x%x\n", (int)(controller->unit-controller->device->units), ans);
break;
}
}
return ans;
}
t_stat dmc_reset (DEVICE *dptr)
{
t_stat ans = SCPE_OK;
CTLR *controller;
uint32 i, j;
sim_debug(DBG_TRC, dptr, "dmc_reset(%s)\n", dptr->name);
/* Connect structures together */
for (i=0; i < DMC_NUMDEVICE; i++)
{
dmc_csrs[i].sel0 = &dmc_sel0[i];
dmc_csrs[i].sel2 = &dmc_sel2[i];
dmc_csrs[i].sel4 = &dmc_sel4[i];
dmc_csrs[i].sel6 = &dmc_sel6[i];
controller = &dmc_ctrls[i];
controller->csrs = &dmc_csrs[i];
controller->line = &dmc_desc.ldsc[i];
controller->receive_queue = &dmc_receive_queues[i];
controller->transmit_queue = &dmc_transmit_queues[i];
controller->device = &dmc_dev;
controller->baseaddr = &dmc_baseaddr[i];
controller->basesize = &dmc_basesize[i];
controller->modem = &dmc_modem[i];
controller->unit = &controller->device->units[i];
controller->index = i;
}
for (i=0; i < DMP_NUMDEVICE; i++)
{
dmp_csrs[i].sel0 = &dmp_sel0[i];
dmp_csrs[i].sel2 = &dmp_sel2[i];
dmp_csrs[i].sel4 = &dmp_sel4[i];
dmp_csrs[i].sel6 = &dmp_sel6[i];
dmp_csrs[i].sel10 = &dmp_sel10[i];
controller = &dmc_ctrls[i+DMC_NUMDEVICE];
controller->csrs = &dmp_csrs[i];
controller->line = &dmp_desc.ldsc[i];
controller->receive_queue = &dmp_receive_queues[i];
controller->transmit_queue = &dmp_transmit_queues[i];
controller->device = (UNIBUS) ? &dmp_dev : &dmv_dev;
controller->dev_type = DMP;
controller->baseaddr = &dmp_baseaddr[i];
controller->basesize = &dmp_basesize[i];
controller->modem = &dmp_modem[i];
controller->unit = &controller->device->units[i];
controller->unit->ctlr = (void *)controller;
controller->index = i + DMC_NUMDEVICE;
}
if (0 == dmc_units[0].flags) /* First Time Initializations */
{
for (i=0; i < DMC_NUMDEVICE; i++)
{
controller = &dmc_ctrls[i];
controller->state = Uninitialised;
controller->transfer_state = Idle;
controller->control_out = NULL;
*controller->modem = 0;
controller->dev_type = DMC;
dmc_dev.units[i] = dmc_unit_template;
controller->unit->ctlr = (void *)controller;
}
tmxr_set_modem_control_passthru (&dmc_desc); /* We always want Modem Control */
dmc_units[dmc_dev.numunits-1] = dmc_poll_unit_template;
dmc_units[dmc_dev.numunits-1].ctlr = dmc_units[0].ctlr;
dmc_desc.notelnet = TRUE; /* We always want raw tcp socket */
dmc_desc.dptr = &dmc_dev; /* Connect appropriate device */
dmc_desc.uptr = dmc_units+dmc_desc.lines; /* Identify polling unit */
for (i=0; i < DMP_NUMDEVICE; i++)
{
controller = &dmc_ctrls[i+DMC_NUMDEVICE];
controller->state = Uninitialised;
controller->transfer_state = Idle;
controller->control_out = NULL;
*controller->modem = 0;
dmp_dev.units[i] = dmc_unit_template;
controller->unit->ctlr = (void *)controller;
}
tmxr_set_modem_control_passthru (&dmp_desc); /* We always want Modem Control */
dmp_units[dmp_dev.numunits-1] = dmc_poll_unit_template;
dmp_units[dmp_dev.numunits-1].ctlr = dmp_units[0].ctlr;
dmp_desc.notelnet = TRUE; /* We always want raw tcp socket */
dmp_desc.dptr = &dmp_dev; /* Connect appropriate device */
dmp_desc.uptr = dmp_units+dmp_desc.lines; /* Identify polling unit */
}
ans = auto_config (dptr->name, (dptr->flags & DEV_DIS) ? 0 : dptr->numunits - 1);
if (!(dptr->flags & DEV_DIS))
{
for (i = 0; i < DMC_NUMDEVICE + DMP_NUMDEVICE; i++)
{
if (dmc_ctrls[i].device == dptr)
{
controller = &dmc_ctrls[i];
dmc_buffer_queue_init_all(controller);
dmc_clrinint(controller);
dmc_clroutint(controller);
for (j=0; j<dptr->numunits-1; j++)
sim_cancel (&dptr->units[j]); /* stop poll */
}
}
sim_activate_after (dptr->units+dptr->numunits-1, DMC_CONNECT_POLL*1000000);/* start poll */
}
return ans;
}
t_stat dmc_attach (UNIT *uptr, char *cptr)
{
DEVICE *dptr = (UNIBUS) ? ((&dmc_dev == find_dev_from_unit(uptr)) ? &dmc_dev : &dmp_dev) : &dmv_dev;
int32 dmc = (int32)(uptr-dptr->units);
TMXR *mp = (dptr == &dmc_dev) ? &dmc_desc : &dmp_desc;
t_stat ans = SCPE_OK;
char *peer = ((dptr == &dmc_dev)? &dmc_peer[dmc][0] : &dmp_peer[dmc][0]);
char *port = ((dptr == &dmc_dev)? &dmc_port[dmc][0] : &dmp_port[dmc][0]);
char attach_string[1024];
if (!cptr || !*cptr)
return SCPE_ARG;
if (!(uptr->flags & UNIT_ATTABLE))
return SCPE_NOATT;
if (!peer[0])
{
printf ("Peer must be specified before attach\n");
if (sim_log)
fprintf (sim_log, "Peer must be specified before attach\n");
return SCPE_ARG;
}
sprintf (attach_string, "Line=%d,Buffered=16384,Connect=%s,%s", dmc, peer, cptr);
ans = tmxr_open_master (mp, attach_string); /* open master socket */
if (ans != SCPE_OK)
return ans;
strncpy (port, cptr, CBUFSIZE-1);
uptr->filename = (char *)malloc (strlen(port)+1);
strcpy (uptr->filename, port);
uptr->flags |= UNIT_ATT;
sim_activate_after (dptr->units+mp->lines, DMC_CONNECT_POLL*1000000);/* start poll */
return ans;
}
t_stat dmc_detach (UNIT *uptr)
{
DEVICE *dptr = (UNIBUS) ? ((&dmc_dev == find_dev_from_unit(uptr)) ? &dmc_dev : &dmp_dev) : &dmv_dev;
int32 dmc = (int32)(uptr-dptr->units);
TMXR *mp = (dptr == &dmc_dev) ? &dmc_desc : &dmp_desc;
TMLN *lp = &mp->ldsc[dmc];
int32 i, attached;
if (!(uptr->flags & UNIT_ATT)) /* attached? */
return SCPE_OK;
sim_cancel (uptr);
uptr->flags &= ~UNIT_ATT;
for (i=attached=0; i<mp->lines; i++)
if (dptr->units[i].flags & UNIT_ATT)
++attached;
if (!attached)
sim_cancel (dptr->units+mp->lines); /* stop poll on last detach */
free (uptr->filename);
uptr->filename = NULL;
return tmxr_detach_ln (lp);
}
char *dmc_description (DEVICE *dptr)
{
return "DMC11 Synchronous network controller";
}
char *dmp_description (DEVICE *dptr)
{
return (UNIBUS) ? "DMP11 Synchronous network controller"
: "DMV11 Synchronous network controller";
}