/* | |
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); | |
} |