blob: 963f465c4c4866b4cf702c414df3b26efe559ff6 [file] [log] [blame] [raw]
/*
Copyright (c) 2015-2017, John Forecast
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
JOHN FORECAST 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 John Forecast shall not
be used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from John Forecast.
*/
/* cdc1700_dc.c: CDC1700 Buffered data channel support
* Simh devices: dca, dcb, dcc
*/
#include "cdc1700_defs.h"
extern char INTprefix[];
extern uint16 Areg, Preg, Qreg, IOAreg, IOQreg, M[];
extern t_bool IOFWinitialized;
extern DEVICE *IOdev[];
extern UNIT cpu_unit;
extern uint16 LoadFromMem(uint16);
extern t_bool IOStoreToMem(uint16, uint16, t_bool);
extern void rebuildPending(void);
extern void RaiseExternalInterrupt(DEVICE *);
extern IO_DEVICE *fw_findChanDevice(IO_DEVICE *, uint16);
extern enum IOstatus fw_doIO(DEVICE *, t_bool);
extern enum IOstatus fw_doBDCIO(IO_DEVICE *, uint16 *, t_bool, uint8);
extern uint16 LoadFromMem(uint16);
extern t_bool IOStoreToMem(uint16, uint16, t_bool);
t_stat set_intr(UNIT *uptr, int32 val, CONST char *, void *);
t_stat show_intr(FILE *, UNIT *, int32, CONST void *);
t_stat show_target(FILE *, UNIT *, int32, CONST void *);
t_stat dc_svc(UNIT *);
t_stat dc_reset(DEVICE *);
void DCstate(const char *, DEVICE *, IO_DEVICE *);
t_bool DCreject(IO_DEVICE *, t_bool, uint8);
enum IOstatus DCin(IO_DEVICE *, uint8);
enum IOstatus DCout(IO_DEVICE *, uint8);
t_stat dc_help(FILE *, DEVICE *, UNIT *, int32, const char *);
/*
1706-A Buffered Data Channel
Addresses (A maximum of 3 1706-A's may be attached to a 1700 series system)
Computer Instruction
Q Register Output From A Input To A
(Bits 11-15)
#1 #2 #3
00010 00111 01100 Direct Output Direct Input
00011 01000 01101 Function Terminate Buffer
00100 01001 01110 Buffered Output 1706-A Status
00101 01010 01111 Buffered Input 1706-A Current Address
Operations:
Function
15 14 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| | | | | | | | | | | | | | | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| | | |
| +---------------------------------------------------+ EOP Interrupt
| | Request
| Not defined
Set/Clear condition bits 0 - 14
Status Response:
Status
15 10 7 5 3 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| X | X | X | X | X | X | | | X | | X | | X | | | |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| | | | | | |
| | | | | | Ready
| | | | | Busy
| | | | Interrupt
| | | End of Operation
| | Program Protect Fault
| Device Reject
Device Reply
*/
IO_DEVICE DCAdev = IODEV(NULL, "1706-A", DC, 0, 0xFF, IO_1706_1_A,
DCreject, DCin, DCout, NULL, NULL,
DCstate, NULL, NULL, NULL, NULL, NULL,
0x7F, 4,
MASK_REGISTER0 | MASK_REGISTER1 | \
MASK_REGISTER2 | MASK_REGISTER3,
MASK_REGISTER2, 0, 0, DEVICE_DC, 0, NULL);
IO_DEVICE DCBdev = IODEV(NULL, "1706-A", DC, 0, 0xFF, IO_1706_2_A,
DCreject, DCin, DCout, NULL, NULL,
DCstate, NULL, NULL, NULL, NULL, NULL,
0x7F, 4,
MASK_REGISTER0 | MASK_REGISTER1 | \
MASK_REGISTER2 | MASK_REGISTER3,
MASK_REGISTER2, 0, 0, DEVICE_DC, 0, NULL);
IO_DEVICE DCCdev = IODEV(NULL, "1706-A", DC, 0, 0xFF, IO_1706_3_A,
DCreject, DCin, DCout, NULL, NULL,
DCstate, NULL, NULL, NULL, NULL, NULL,
0x7F, 4,
MASK_REGISTER0 | MASK_REGISTER1 | \
MASK_REGISTER2 | MASK_REGISTER3,
MASK_REGISTER2, 0, 0, DEVICE_DC, 0, NULL);
/*
* Define usage for "private" IO_DEVICE data areas.
*/
#define iod_lastIO iod_private
#define iod_target iod_private2
#define iod_svcstate iod_private3
#define iod_CWA iod_private6
#define iod_LWA iod_private7
#define iod_nextAddr iod_private8
#define iod_reg iod_private9
/*
* Define current state of the 1706-A with respect to the Direct Storage
* Access Bus.
*/
#define IO_BDC_IDLE 0x00
#define IO_BDC_STARTR 0x01 /* Start read sequence */
#define IO_BDC_STARTW 0x02 /* Start write sequence */
#define IO_BDC_READING 0x03 /* Read sequence in progress */
#define IO_BDC_WRITING 0x04 /* Write sequence in progress */
#define IO_BDC_DONE 0x05 /* Transfer has completed */
/* Buffered Data Channel (DC) data structures
dca_dev DC device descriptor
dcb_dev DC device descriptor
dcc_dev DC device descriptor
dca_unit DC unit
dcb_unit DC unit
dcc_unit DC unit
dc_reg DC register list
dc_mod DC modifier list
*/
UNIT dca_unit[] = {
{UDATA(&dc_svc, UNIT_DISABLE, 0) },
{UDATA(&dc_svc, UNIT_DISABLE, 0) },
{UDATA(&dc_svc, UNIT_DISABLE, 0) },
{UDATA(&dc_svc, UNIT_DISABLE, 0) },
{UDATA(&dc_svc, UNIT_DISABLE, 0) },
{UDATA(&dc_svc, UNIT_DISABLE, 0) },
{UDATA(&dc_svc, UNIT_DISABLE, 0) },
{UDATA(&dc_svc, UNIT_DISABLE, 0) }
};
UNIT dcb_unit[] = {
{UDATA(&dc_svc, UNIT_DISABLE, 0) },
{UDATA(&dc_svc, UNIT_DISABLE, 0) },
{UDATA(&dc_svc, UNIT_DISABLE, 0) },
{UDATA(&dc_svc, UNIT_DISABLE, 0) },
{UDATA(&dc_svc, UNIT_DISABLE, 0) },
{UDATA(&dc_svc, UNIT_DISABLE, 0) },
{UDATA(&dc_svc, UNIT_DISABLE, 0) },
{UDATA(&dc_svc, UNIT_DISABLE, 0) }
};
UNIT dcc_unit[] = {
{UDATA(&dc_svc, UNIT_DISABLE, 0) },
{UDATA(&dc_svc, UNIT_DISABLE, 0) },
{UDATA(&dc_svc, UNIT_DISABLE, 0) },
{UDATA(&dc_svc, UNIT_DISABLE, 0) },
{UDATA(&dc_svc, UNIT_DISABLE, 0) },
{UDATA(&dc_svc, UNIT_DISABLE, 0) },
{UDATA(&dc_svc, UNIT_DISABLE, 0) },
{UDATA(&dc_svc, UNIT_DISABLE, 0) }
};
REG dca_reg[] = {
{ HRDATAD(STATUS, DCAdev.iod_readR[2], 16, "1706 Status") },
{ HRDATAD(CWA, DCAdev.iod_CWA, 16, "1706 Current Address") },
{ HRDATAD(NEXT, DCAdev.iod_nextAddr, 16, "Next transfer address") },
{ HRDATAD(LWA, DCAdev.iod_LWA, 16, "Last word address") },
{ HRDATAD(IENABLE, DCAdev.IENABLE, 16, "Interrupt enabled") },
{ NULL }
};
REG dcb_reg[] = {
{ HRDATAD(STATUS, DCBdev.iod_readR[2], 16, "1706 Status") },
{ HRDATAD(CWA, DCBdev.iod_CWA, 16, "1706 Current Address") },
{ HRDATAD(NEXT, DCBdev.iod_nextAddr, 16, "Next transfer address") },
{ HRDATAD(LWA, DCBdev.iod_LWA, 16, "Last word address") },
{ HRDATAD(IENABLE, DCBdev.IENABLE, 16, "Interrupt enabled") },
{ NULL }
};
REG dcc_reg[] = {
{ HRDATAD(STATUS, DCCdev.iod_readR[2], 16, "1706 Status") },
{ HRDATAD(CWA, DCCdev.iod_CWA, 16, "1706 Current Address") },
{ HRDATAD(NEXT, DCCdev.iod_nextAddr, 16, "Next transfer address") },
{ HRDATAD(LWA, DCCdev.iod_LWA, 16, "Last word address") },
{ HRDATAD(IENABLE, DCCdev.IENABLE, 16, "Interrupt enabled") },
{ NULL }
};
MTAB dc_mod[] = {
{ MTAB_XTD|MTAB_VDV, 0, "1706-A Buffered Data Channel" },
{ MTAB_XTD|MTAB_VDV, 0, "TARGET", NULL,
NULL, &show_target, NULL, "Display devices attached to the data channel" },
{ MTAB_XTD|MTAB_VDV, 0, "INTERRUPT", "INTERRUPT=hexValue",
&set_intr, &show_intr, NULL, "Display data channel interrupt" },
{ 0 }
};
DEBTAB dc_deb[] = {
{ "TRACE", DBG_DTRACE, "Trace device I/O requests" },
{ "STATE", DBG_DSTATE, "Display device state changes" },
{ "INTR", DBG_DINTR, "Display device interrupt requests" },
{ "LOCATION", DBG_DLOC, "Display address for I/O instructions" },
{ "ALL", DBG_DTRACE | DBG_DSTATE | DBG_DINTR | DBG_DLOC },
{ NULL }
};
DEVICE dca_dev = {
"DCA", dca_unit, dca_reg, dc_mod,
0, 16, 16, 1, 16, 16,
NULL, NULL, &dc_reset,
NULL, NULL, NULL,
&DCAdev,
DEV_DEBUG | DEV_NOEQUIP | DEV_INDEV | DEV_OUTDEV, 0, dc_deb,
NULL, NULL, &dc_help, NULL, NULL, NULL
};
DEVICE dcb_dev = {
"DCB", dcb_unit, dcb_reg, dc_mod,
0, 16, 16, 1, 16, 16,
NULL, NULL, &dc_reset,
NULL, NULL, NULL,
&DCBdev,
DEV_DEBUG | DEV_NOEQUIP | DEV_INDEV | DEV_OUTDEV, 0, dc_deb,
NULL, NULL, &dc_help, NULL, NULL, NULL
};
DEVICE dcc_dev = {
"DCC", dcc_unit, dcc_reg, dc_mod,
0, 16, 16, 1, 16, 16,
NULL, NULL, &dc_reset,
NULL, NULL, NULL,
&DCCdev,
DEV_DEBUG | DEV_NOEQUIP | DEV_INDEV | DEV_OUTDEV, 0, dc_deb,
NULL, NULL, &dc_help, NULL, NULL, NULL
};
static DEVICE *dc_devices[IO_1706_MAX] = {
&dca_dev, &dcb_dev, &dcc_dev
};
/*
* Dump the current state of the Buffered Data Channel.
*/
const char *DCstateStr[] = {
"Idle", "StartR", "StartW", "Read", "Write", "Done"
};
void DCstate(const char *where, DEVICE *dev, IO_DEVICE *iod)
{
fprintf(DBGOUT,
"%s[%s %s: Sta: %04X, %s, ena: %04X, cur: %04X, next: %04X, last: %04X, reg: %d]\r\n",
INTprefix, dev->name, where,
DCSTATUS(iod), DCstateStr[iod->iod_svcstate], ENABLED(iod),
iod->iod_CWA, iod->iod_nextAddr, iod->iod_LWA, iod->iod_reg);
}
/*
* Display device description.
*/
static const char *description(DEVICE *dptr)
{
return "1706-A";
}
/*
* Unit service
*/
t_stat dc_svc(UNIT *uptr)
{
DEVICE *dptr;
enum IOstatus status;
uint16 temp = 0;
if ((dptr = find_dev_from_unit(uptr)) != NULL) {
IO_DEVICE *iod = (IO_DEVICE *)dptr->ctxt;
IO_DEVICE *target = (IO_DEVICE *)iod->iod_target;
if ((dptr->dctrl & DBG_DSTATE) != 0)
DCstate("dc_svc() entry", iod->iod_indev, iod);
switch (iod->iod_svcstate) {
case IO_BDC_IDLE:
return SCPE_OK;
case IO_BDC_STARTR:
case IO_BDC_STARTW:
if ((dptr->dctrl & DBG_DTRACE) != 0)
fprintf(DBGOUT,
"%s%s - Start %s on %s, current: %04X, last: %04X\r\n",
INTprefix, dptr->name,
iod->iod_svcstate == IO_BDC_STARTR ? "input" : "output",
target == NULL ? "no device" : target->iod_indev->name,
iod->iod_CWA, iod->iod_LWA);
iod->iod_svcstate =
iod->iod_svcstate == IO_BDC_STARTR ? IO_BDC_READING : IO_BDC_WRITING;
sim_activate(uptr, DC_IO_WAIT);
if ((dptr->dctrl & DBG_DSTATE) != 0)
DCstate("dc_svc() - started", iod->iod_indev, iod);
return SCPE_OK;
case IO_BDC_READING:
if (target != NULL) {
if ((target->STATUS & IO_ST_EOP) != 0)
goto ioDone;
if (iod->iod_CWA == iod->iod_LWA) {
/*
* Transfer complete - complete status change and, optionally,
* generate interrupt.
*/
iod->iod_svcstate = IO_BDC_DONE;
sim_activate(uptr, DC_EOP_WAIT);
if ((dptr->dctrl & DBG_DSTATE) != 0)
DCstate("dc_svc() - read complete", iod->iod_indev, iod);
return SCPE_OK;
}
DCSTATUS(iod) &= ~(IO_1706_REPLY | IO_1706_REJECT);
iod->iod_nextAddr = iod->iod_CWA + 1;
status = fw_doBDCIO(target, &temp, FALSE, iod->iod_reg);
switch (status) {
case IO_REPLY:
DCSTATUS(iod) |= IO_1706_REPLY;
if (!IOStoreToMem(iod->iod_CWA, temp, TRUE)) {
DCSTATUS(iod) |= IO_1706_PROT;
/*** TODO: Signal protect fault ***/
}
iod->iod_CWA++;
if ((dptr->dctrl & DBG_DTRACE) != 0)
fprintf(DBGOUT,
"%s%s - Read %04X\r\n",
INTprefix, dptr->name, temp);
break;
case IO_REJECT:
case IO_INTERNALREJECT:
DCSTATUS(iod) |= IO_1706_REJECT;
break;
}
} else DCSTATUS(iod) |= IO_1706_REJECT;
sim_activate(uptr, DC_IO_WAIT);
if ((dptr->dctrl & DBG_DSTATE) != 0)
DCstate("dc_svc() - reading", iod->iod_indev, iod);
return SCPE_OK;
case IO_BDC_WRITING:
if (target != NULL) {
if ((target->STATUS & IO_ST_EOP) != 0)
goto ioDone;
if (iod->iod_CWA == iod->iod_LWA) {
/*
* Transfer complete - complete status change and, optionally,
* generate interrupt.
*/
iod->iod_svcstate = IO_BDC_DONE;
sim_activate(uptr, DC_EOP_WAIT);
if ((dptr->dctrl & DBG_DSTATE) != 0)
DCstate("dc_svc() - write complete", iod->iod_indev, iod);
return SCPE_OK;
}
DCSTATUS(iod) &= ~(IO_1706_REPLY | IO_1706_REJECT);
iod->iod_nextAddr = iod->iod_CWA + 1;
temp = LoadFromMem(iod->iod_CWA);
status = fw_doBDCIO(target, &temp, TRUE, iod->iod_reg);
switch (status) {
case IO_REPLY:
DCSTATUS(iod) |= IO_1706_REPLY;
iod->iod_CWA++;
break;
case IO_REJECT:
case IO_INTERNALREJECT:
DCSTATUS(iod) |= IO_1706_REJECT;
break;
}
} else DCSTATUS(iod) |= IO_1706_REJECT;
sim_activate(uptr, DC_IO_WAIT);
if ((dptr->dctrl & DBG_DSTATE) != 0)
DCstate("dc_svc() - writing", iod->iod_indev, iod);
return SCPE_OK;
case IO_BDC_DONE:
/*
* The transfer has completed as far as the 1706-A is concerned.
*/
ioDone:
iod->iod_svcstate = IO_BDC_IDLE;
DCSTATUS(iod) |= IO_ST_EOP;
DCSTATUS(iod) &= ~IO_ST_BUSY;
if (ISENABLED(iod, IO_DIR_EOP) && (iod->iod_equip != 0)) {
DEVICE *dptr = iod->iod_indev;
if ((dptr->dctrl & DBG_DINTR) != 0)
fprintf(DBGOUT,
"%s%s - Generate EOP interrupt\r\n",
INTprefix, dptr->name);
DCSTATUS(iod) |= IO_ST_INT;
RaiseExternalInterrupt(dptr);
}
if ((dptr->dctrl & DBG_DSTATE) != 0)
DCstate("dc_svc() - EOP set", iod->iod_indev, iod);
return SCPE_OK;
}
}
return SCPE_NXDEV;
}
/*
* Reset routine
*/
t_stat dc_reset(DEVICE *dptr)
{
IO_DEVICE *iod = (IO_DEVICE *)dptr->ctxt;
DEVRESET(iod);
DCSTATUS(iod) = IO_ST_READY;
return SCPE_OK;
}
/*
* Set the interrupt level for a buffered data channel.
*/
t_stat set_intr(UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
IO_DEVICE *iod = (IO_DEVICE *)uptr->up7;
t_value v;
t_stat r;
if (cptr == NULL)
return SCPE_ARG;
v = get_uint(cptr, DEV_RDX, 15, &r);
if (r != SCPE_OK)
return r;
if (v == 0)
return SCPE_ARG;
iod->iod_equip = v;
iod->iod_interrupt = 1 << v;
return SCPE_OK;
}
/*
* Display the current interrupt level.
*/
t_stat show_intr(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
IO_DEVICE *iod = (IO_DEVICE *)uptr->up7;
if (iod->iod_equip != 0) {
fprintf(st, "Interrupt: ");
fprint_val(st, (t_value)iod->iod_equip, DEV_RDX, 8, PV_LEFT);
} else fprintf(st, "Interrupt: None");
return SCPE_OK;
}
/*
* Display buffered data channel target device and equipment address
*/
t_stat show_target(FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
IO_DEVICE *iod;
if (uptr == NULL)
return SCPE_IERR;
if ((iod = (IO_DEVICE *)uptr->up8) != NULL) {
DEVICE *dptr = iod->iod_indev;
fprintf(st, "\n\tTarget: %s (%s), Equip: %d",
sim_dname(dptr), iod->iod_model, iod->iod_equip);
}
return SCPE_OK;
}
/*
* Check if I/O should be rejected. I/O allowed if:
*
* Reg. Write (OUT) Read (INP)
*
* 00 Not busy Not busy
* 01 Not busy Always allowed
* 02 Not busy Always allowed
* 03 Not busy Always allowed
*/
t_bool DCreject(IO_DEVICE *iod, t_bool output, uint8 reg)
{
if (output || (reg == 0))
return (DCSTATUS(iod) & IO_ST_BUSY) != 0;
return FALSE;
}
/*
* Start a buffered data channel transfer. Note that target may be NULL if
* an attempt is made to access a device which is not connected to the
* buffered data channel. We need to delay starting the transaction so that
* there is sufficient time to grab the current bufferered data channel
* status and terminate the transfer before starting the actual transfer.
* The diagnostics check for this particular case.
*/
enum IOstatus DCxfer(IO_DEVICE *iod, IO_DEVICE *target, t_bool output)
{
DEVICE *dptr = (DEVICE *)iod->iod_indev;
iod->iod_LWA = LoadFromMem(IOAreg);
iod->iod_CWA = iod->iod_nextAddr = ++IOAreg;
iod->iod_target = target;
if (target != NULL)
iod->iod_reg = IOQreg & target->iod_rmask;
DCSTATUS(iod) &= ~IO_ST_EOP;
DCSTATUS(iod) |= IO_ST_BUSY;
iod->iod_svcstate = output ? IO_BDC_STARTW : IO_BDC_STARTR;
sim_activate(&dptr->units[0], DC_START_WAIT);
if ((dptr->dctrl & DBG_DTRACE) != 0)
fprintf(DBGOUT,
"%s%s - starting %s transfer, cur: %04X, last: %04X\r\n",
INTprefix, dptr->name, output ? "output" : "input",
iod->iod_CWA, iod->iod_LWA);
return IO_REPLY;
}
/*
* Perform a buffered data channel input operation
*/
enum IOstatus DCin(IO_DEVICE *iod, uint8 reg)
{
IO_DEVICE *target;
enum IOstatus status;
/*
* If the "Continue" bit is set in Q, use the last I/O address and treat the
* request as a direct input/output operation.
*/
if ((IOQreg & IO_CONTINUE) != 0) {
IOQreg = iod->iod_lastIO;
reg = 0;
} else iod->iod_lastIO = IOQreg;
/*
* The framework filters out INP requests for the status register.
*/
switch (reg) {
/*
* Perform a direct input request from the target device.
*/
case 0x00:
/*
* Find the target device to be used.
*/
if ((target = fw_findChanDevice(iod, IOQreg)) == NULL)
return IO_REJECT;
if ((target->iod_indev->dctrl & DBG_DSTATE) != 0)
if (target->iod_state != NULL)
(*target->iod_state)("before direct in", target->iod_indev, target);
status = fw_doIO(target->iod_indev, FALSE);
if ((target->iod_indev->dctrl & DBG_DSTATE) != 0)
if (target->iod_state != NULL)
(*target->iod_state)("after direct in", target->iod_indev, target);
return status;
/*
* Terminate buffer, 1706 Current Address.
*/
case 0x01:
iod->iod_svcstate = IO_BDC_IDLE;
DCSTATUS(iod) &= ~IO_ST_BUSY;
/* FALLTHROUGH */
/*
* 1706 Current Address. May be the next address depending on where we
* are in the actual transfer sequence.
*/
case 0x03:
Areg = iod->iod_nextAddr;
return IO_REPLY;
}
return IO_REJECT;
}
/*
* Perform a buffered data channel output operation
*/
enum IOstatus DCout(IO_DEVICE *iod, uint8 reg)
{
IO_DEVICE *target;
enum IOstatus status;
/*
* If the "Continue" bit is set in Q, use the last I/O address and treat the
* request as a direct input/output operation.
*/
if ((IOQreg & IO_CONTINUE) != 0) {
IOQreg = iod->iod_lastIO;
reg = 0;
} else iod->iod_lastIO = IOQreg;
/*
* Find the target device to be used. If the target device is not connected
* to the buffered data channel, the REJECT will eventually be processed
* in dc_svc().
*/
target = fw_findChanDevice(iod, IOQreg);
if ((target == NULL) && (reg == 0x00))
return IO_REJECT;
switch (reg) {
/*
* Perform a direct output request to the target device.
*/
case 0x00:
if ((target->iod_indev->dctrl & DBG_DSTATE) != 0)
if (target->iod_state != NULL)
(*target->iod_state)("before direct out", target->iod_indev, target);
status = fw_doIO(target->iod_indev, TRUE);
if ((target->iod_indev->dctrl & DBG_DSTATE) != 0)
if (target->iod_state != NULL)
(*target->iod_state)("after direct out", target->iod_indev, target);
return status;
/*
* Command function to the 1706-A.
*/
case 0x01:
if ((IOAreg & IO_1706_EOP) != 0) {
iod->OLDIENABLE = iod->IENABLE;
if ((IOAreg & IO_1706_SET) != 0)
iod->IENABLE |= IO_DIR_EOP;
else iod->IENABLE &= ~IO_DIR_EOP;
DCSTATUS(iod) &= ~(IO_ST_INT | IO_ST_EOP);
rebuildPending();
}
return IO_REPLY;
/*
* Initiate buffered output on the 1706-A.
*/
case 0x02:
return DCxfer(iod, target, TRUE);
/*
* Initiate buffered input on the 1706-A.
*/
case 0x03:
return DCxfer(iod, target, FALSE);
}
return IO_REJECT;
}
/*
* Build the buffered data channel tables.
*/
void buildDCtables(void)
{
int i;
uint8 chan;
DEVICE *dptr;
UNIT *uptr;
dca_dev.numunits = 0;
dcb_dev.numunits = 0;
dcc_dev.numunits = 0;
dca_dev.units[0].up7 = &DCAdev;
dcb_dev.units[0].up7 = &DCBdev;
dcc_dev.units[0].up7 = &DCCdev;
for (i = 0; i < 16; i++) {
if ((dptr = IOdev[i]) != NULL) {
IO_DEVICE *iod = (IO_DEVICE *)dptr->ctxt;
if (((chan = iod->iod_dc) != 0) &&
((iod->iod_flags & AQ_ONLY) == 0)) {
dptr = dc_devices[IDX_FROM_CHAN(chan)];
uptr = &dptr->units[dptr->numunits];
if (dptr->numunits < IO_1706_DEVS) {
uptr->up8 = iod;
dptr->numunits++;
}
}
}
}
}
/*
* Create bit map of interrupts asserted by the Buffered Data Channels.
*/
uint16 dcINTR(void)
{
uint16 result = 0;
if ((DCSTATUS(&DCAdev) & IO_ST_INT) != 0)
result |= DCAdev.iod_interrupt;
if ((DCSTATUS(&DCBdev) & IO_ST_INT) != 0)
result |= DCBdev.iod_interrupt;
if ((DCSTATUS(&DCCdev) & IO_ST_INT) != 0)
result |= DCCdev.iod_interrupt;
return result;
}
t_stat dc_help(FILE *st, DEVICE *dptr, UNIT *uptr, int32 flag, const char *cptr)
{
const char helpString[] =
" The %D device is a 1706-A buffered data channel.\n"
"1 Hardware Description\n"
" The 1706-A consists of a controller which can control up to 8 other\n"
" controllers to provide them with direct memory access. Up to 3\n"
" 1706-A's may be connected to the CPU. All 3 buffered data channels are\n"
" available in the simulator but only channel 1 (DCA) is connected to a\n"
" peripheral (the magtape controller) and only if that controller is\n"
" configured as a 1732-A. Unlike actual hardware, the simulator allows\n"
" access to the magtape controller either through the A/Q channel or\n"
" through a 1706-A.\n\n"
" By default, the simulator does not assign an interrupt to a 1706-A. An\n"
" interrupt may be assigned with the command:\n\n"
"+sim> SET %D INTERRUPT=hexValue\n"
"2 Equipment Address\n"
" Unlike most peripherals, buffered data channels use private addresses\n"
" outside the normal 1 - 15 address range.\n"
"2 $Registers\n"
"1 Configuration\n"
" A %D device is configured with various simh SET commands\n"
"2 $Set commands\n";
return scp_help(st, dptr, uptr, flag, helpString, cptr);
}