/* hp2100_di.c: HP 12821A HP-IB Disc Interface simulator | |
Copyright (c) 2010-2017, J. David Bryan | |
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. | |
DI 12821A Disc Interface | |
15-Mar-17 JDB Converted debug fprintfs to tpprintfs | |
10-Mar-17 JDB Added IOBUS to the debug table | |
17-Jan-17 JDB Changed to use new byte accessors in hp2100_defs.h | |
13-May-16 JDB Modified for revised SCP API function parameter types | |
24-Dec-14 JDB Added casts for explicit downward conversions | |
Removed redundant global declarations | |
13-Feb-12 JDB First release | |
15-Dec-11 JDB Added dummy DC device for diagnostics | |
09-Oct-10 JDB Created DI simulation | |
References: | |
- HP 12821A Disc Interface Installation and Service Manual | |
(12821-90006, February 1985) | |
- IEEE Standard Digital Interface for Programmable Instrumentation | |
(IEEE-488A-1980, September 1979) | |
The 12821A was a high-speed implementation of the Hewlett-Packard Interface | |
Bus (HP-IB, formalized as IEEE Std. 488-1978). It was used to interface | |
HP-IB disc and tape devices, such as the HP 7906H, 7908A, and 7974A, to the | |
HP 1000 running RTE-IVB or RTE-6/VM. Three device command protocols were | |
supported by the I/O drivers: Amigo discs by driver DVA32, CS/80 discs by | |
DVM33, and Amigo tapes by DVS23. | |
In an RTE environment, the 12821A was the system controller. While | |
electrically compatible with the HP-IB specification and capable of receiving | |
addressing commands from the bus, the 12821A did not use the full IEEE-488 | |
protocol. Card talker and listener states were set by bits in the control | |
register, rather than by receiving talk and listen commands over the bus. | |
The bus address of the card could be set via DIP switches, but this feature | |
was only used by the diagnostic. | |
The card supported packed and unpacked transfers across the bus. Up to four | |
devices could be connected to each card; this limit was imposed by the | |
maximum electrical loading on the bus compatible with the high data rate. | |
The 12821A had a 16-word FIFO buffer and could sustain DCPC transfers of one | |
megabyte per second. Burst transfers by the CPU to fill or empty the FIFO | |
could run at the full bandwidth of the I/O backplane. This could hold off | |
lower-priority devices for 10-15 microseconds until the card slowed down to | |
the rate of the disc or tape. | |
Card assembly 12821-60003 was revised to add a DCPC pacing option. Placing | |
jumper W1 in position A inhibited SRQ for one I/O cycle in six to allow a | |
lower-priority interface card to transfer one word. Position B allowed SRQ | |
to assert continuously as it did on the earlier card assembly 12821-60001. | |
The simulator is logically partitioned into three sets of functions: the | |
interface card simulation, the HP-IB bus simulation, and the device | |
simulation. This is the card simulation and the card portion of the HP-IB | |
simulation. Separate modules for the tape and disc devices contain the | |
device simulations and the device portions of the HP-IB simulations. | |
This simulator is written to allow the definition of multiple DI cards in a | |
system. The RTE operating system provided separate I/O drivers for the Amigo | |
disc, Amigo tape, and CS/80 disc devices. As only one I/O driver could | |
control a given interface, separate interfaces were required if more than one | |
device class was installed. For example, it was not possible to control an | |
Amigo disc and an Amigo tape connected to the same interface card. | |
Implementation notes: | |
1. The simulator behaves as though card switches S1-S7 are initially closed, | |
providing a card bus address of 0. The address may be changed with the | |
SET <dev> ADDRESS=n command. Only addresses 0-7 are supported, and the | |
address may duplicate a device bus address without conflict, as the | |
address is only used during the diagnostic when devices are disconnected. | |
2. The simulator behaves as though card switch S8 is open, enabling the card | |
to be the system controller. This cannot be changed by the user. | |
3. The simulator behaves as though card jumper W1 (DCPC pacing) is in | |
position B. This currently cannot be changed by the user. | |
*/ | |
#include "hp2100_defs.h" | |
#include "hp2100_di.h" | |
/* Program constants */ | |
#define SW8_SYSCTL 1 /* card is always the system controller (switch 8) */ | |
#define IFC_TIMEOUT 157 /* 157 instructions = ~ 100 microseconds */ | |
#define CONTROLLER 31 /* dummy unit number for DI */ | |
/* Character constants */ | |
#define LF '\012' | |
/* Control Word Register */ | |
#define CNTL_SRQ 0100000 /* enable service request interrupt */ | |
#define CNTL_IFC 0040000 /* assert IFC or enable IFC interrupt */ | |
#define CNTL_REN 0020000 /* assert remote enable */ | |
#define CNTL_IRL 0010000 /* enable input-register-loaded interrupt */ | |
#define CNTL_LBO 0004000 /* enable last-byte-out interrupt */ | |
#define CNTL_LF 0002000 /* enable line feed terminator */ | |
#define CNTL_EOI 0001000 /* assert end or identify */ | |
#define CNTL_ATN 0000400 /* assert attention */ | |
#define CNTL_DIAG 0000200 /* diagnostic loopback */ | |
#define CNTL_NRFD 0000100 /* assert not ready for data */ | |
#define CNTL_PPE 0000040 /* parallel poll enable */ | |
#define CNTL_ODD 0000020 /* odd number of bytes */ | |
#define CNTL_PACK 0000010 /* packed data transfer */ | |
#define CNTL_LSTN 0000004 /* listen */ | |
#define CNTL_TALK 0000002 /* talk */ | |
#define CNTL_CIC 0000001 /* controller in charge */ | |
/* Status Word Register */ | |
#define STAT_SRQBUS 0100000 /* service request bus state */ | |
#define STAT_IFCBUS 0040000 /* interface clear bus state */ | |
#define STAT_RENBUS 0020000 /* remote enable bus state */ | |
#define STAT_IRL 0010000 /* input register loaded */ | |
#define STAT_LBO 0004000 /* last byte out */ | |
#define STAT_LBI 0002000 /* last byte in */ | |
#define STAT_EOIBUS 0001000 /* end or identify bus state */ | |
#define STAT_ATNBUS 0000400 /* attention bus state */ | |
#define STAT_IFC 0000200 /* interface clear seen */ | |
#define STAT_ODD 0000020 /* odd number of bytes */ | |
#define STAT_SYSCTL 0000010 /* system controller */ | |
#define STAT_LSTN 0000004 /* listener */ | |
#define STAT_TALK 0000002 /* talker */ | |
#define STAT_CIC 0000001 /* controller in charge */ | |
/* Data word */ | |
#define DATA_LBO 0100000 /* last byte out */ | |
#define DATA_EOI 0001000 /* end or identify */ | |
#define DATA_ATN 0000400 /* attention */ | |
/* Tag word */ | |
#define BUS_SHIFT 16 /* left shift count to align BUS_ATN, EOI with tag */ | |
#define DATA_SHIFT 8 /* left shift count to align DATA_ATN, EOI with tag */ | |
#define TAG_ATN 0000200000 /* bit 16: attention */ | |
#define TAG_EOI 0000400000 /* bit 17: end or identify */ | |
#define TAG_EDT 0001000000 /* bit 18: end of data transfer */ | |
#define TAG_LBR 0002000000 /* bit 19: last byte received */ | |
#define TAG_MASK (TAG_ATN | TAG_EOI | TAG_EDT | TAG_LBR) | |
static const BITSET_NAME tag_names [] = { /* Bus signal names */ | |
"ATN", /* bit 16 */ | |
"EOI", /* bit 17 */ | |
"EDT", /* bit 18 */ | |
"LBR" /* bit 19 */ | |
}; | |
static const BITSET_FORMAT tag_format = /* names, offset, direction, alternates, bar */ | |
{ FMT_INIT (tag_names, 16, lsb_first, no_alt, no_bar) }; | |
/* Bus signals */ | |
static const BITSET_NAME bus_names [] = { /* Bus signal names */ | |
"ATN", /* bit 0 = attention */ | |
"EOI", /* bit 1 = end or identify */ | |
"DAV", /* bit 2 = data available */ | |
"NRFD", /* bit 3 = not ready for data */ | |
"NDAC", /* bit 4 = not data accepted */ | |
"REN", /* bit 5 = remote enable */ | |
"IFC", /* bit 6 = interface clear */ | |
"SRQ" /* bit 7 = service request */ | |
}; | |
static const BITSET_FORMAT bus_format = /* names, offset, direction, alternates, bar */ | |
{ FMT_INIT (bus_names, 0, lsb_first, no_alt, no_bar) }; | |
/* FIFO access modes */ | |
#define FIFO_EMPTY (di_card->fifo_count == 0) /* FIFO empty test */ | |
#define FIFO_FULL (di_card->fifo_count == FIFO_SIZE) /* FIFO full test */ | |
typedef enum { | |
bus_access, /* per-byte access */ | |
cpu_access, /* per-word access */ | |
diag_access /* mixed access */ | |
} FIFO_ACCESS; | |
/* Disc interface state variables */ | |
DI_STATE di [card_count]; /* per-card state */ | |
/* Disc interface local bus routines */ | |
static t_bool di_bus_accept (CARD_ID card, uint8 data); | |
static void di_bus_respond (CARD_ID card, uint8 cntl); | |
static void di_bus_poll (CARD_ID card); | |
/* Disc interface local utility routines */ | |
static void master_reset (CARD_ID card); | |
static void update_state (CARD_ID card); | |
static void fifo_load (CARD_ID card, uint16 data, FIFO_ACCESS access); | |
static uint16 fifo_unload (CARD_ID card, FIFO_ACCESS access); | |
/* Dummy DC device. | |
This temporary dummy device allows the DI diagnostic to test inter-card | |
signals. Test 15 can only be performed if there are two DIs available. | |
This device provides a second "bare" card. Normally, it is disabled and | |
cannot be enabled by the user. Enabling or disabling DIAG mode on the DA | |
device automatically enables or disables the DC device. The select code of | |
the DC device is fixed at 45B and cannot be changed. | |
*/ | |
DIB dc_dib = { &di_io, DI_DC, dc }; | |
REG dc_reg [] = { | |
{ BRDATA (FIFO, di [dc].fifo, 8, 20, FIFO_SIZE), REG_CIRC }, /* needed for "qptr" */ | |
{ NULL } | |
}; | |
DEVICE dc_dev = { | |
"DC", /* device name */ | |
NULL, /* unit array */ | |
dc_reg, /* register array */ | |
NULL, /* modifier array */ | |
0, /* number of units */ | |
10, /* address radix */ | |
31, /* address width */ | |
1, /* address increment */ | |
8, /* data radix */ | |
8, /* data width */ | |
NULL, /* examine routine */ | |
NULL, /* deposit routine */ | |
&di_reset, /* reset routine */ | |
NULL, /* boot routine */ | |
NULL, /* attach routine */ | |
NULL, /* detach routine */ | |
&dc_dib, /* device information block */ | |
DEV_DEBUG | DEV_DIS, /* device flags */ | |
0, /* debug control flags */ | |
di_deb, /* debug flag name table */ | |
NULL, /* memory size change routine */ | |
NULL /* logical device name */ | |
}; | |
/* DI data structures. | |
*dptrs device pointers | |
*bus_accept device acceptor function pointers | |
*bus_respond device responder function pointers | |
di_deb DI debug table | |
The first three pointer arrays have elements that correspond one-for-one with | |
the supported devices. These allow the DI simulator to work with multiple | |
cards. The actual devices are defined in the individual device simulators. | |
Note that the DC and MA devices are reserved for future use. Until one or | |
the other is fully implemented, a dummy DC device is provided above for use | |
by the diagnostic only. | |
*/ | |
extern DEVICE da_dev; | |
static DEVICE *dptrs [card_count] = { &da_dev, &dc_dev, NULL }; | |
static ACCEPTOR *bus_accept [card_count] = { &da_bus_accept, NULL, NULL }; | |
static RESPONDER *bus_respond [card_count] = { &da_bus_respond, NULL, NULL }; | |
DEBTAB di_deb [] = { | |
{ "RWSC", DEB_RWSC }, | |
{ "CMDS", DEB_CMDS }, | |
{ "CPU", DEB_CPU }, | |
{ "BUF", DEB_BUF }, | |
{ "XFER", DEB_XFER }, | |
{ "SERV", DEB_SERV }, | |
{ "IOBUS", TRACE_IOBUS }, | |
{ NULL, 0 } | |
}; | |
/* Disc interface global VM routines */ | |
/* I/O signal handler. | |
The card has two input and two output registers. The Input Data Register and | |
Output Data Register are addressed when the control flip-flop is set. The | |
Status Word and the Control Word Register are addressed when the control | |
flip-flop is clear. The card has the usual control, flag buffer, flag, and | |
SRQ flip-flops, though flag and SRQ are decoupled to allow the full DCPC | |
transfer rate. | |
In hardware, the presence of the card FIFO, which is necessary to obtain full | |
DCPC bandwidth, implies a delay between CPU actions, such as outputting the | |
last word in a data transfer, and device actions, such as accepting the last | |
word of a disc write. Four flip-flops are used to monitor FIFO status: | |
- EDT (End of Data Transfer) | |
- LBO (Last Byte Out) | |
- LBI (Last Byte In) | |
- EOR (End of Record) | |
The EDT signal indicates that the final data word of a transfer is being | |
written to the FIFO. The flip-flop is set by the EDT backplane signal when | |
the last cycle of a DCPC transfer is executing, or during programmed output | |
transfers when CLF does not accompany IOO in packed mode, or when bit 15 of | |
the data word is set in unpacked mode. It remains set until it is cleared by | |
a master reset. The output of the EDT flip-flop drives the EDT tag input of | |
the FIFO. | |
The LBO signal indicates that the final data byte of a transfer has been | |
sourced to the bus. The flip-flop is set when the last byte of the entry | |
tagged with EDT has been unloaded from the FIFO. It is cleared by a master | |
reset or when an entry not tagged with EDT is unloaded. The output of the | |
LBO flip-flop drives the LBO bit in the Status Word. | |
The LBI signal indicates that the final byte of an input transfer has been | |
accepted from the bus. The flip-flop is set when a byte tagged with EOI is | |
received and the EOI bit in the control register is set, or a line-feed byte | |
is received and the LF bit in the control register is set. It is cleared by | |
a master reset or when neither of these conditions is true. The input of the | |
LBI flip-flop also drives the LBR (last byte received) tag input of the FIFO, | |
and the output of the flip-flop drives the LBI bit in the Status Word. | |
The EOR signal indicates that the final data word of a transfer is available | |
in the Input Data Register. The flip-flop is set when the last byte of the | |
entry tagged with LBR has been unloaded from the FIFO and written to the IDR. | |
It is cleared by a master reset or when an entry not tagged with LBR is | |
unloaded and written to the IDR. The output of the EOR flip-flop sets the | |
flag flip-flop when the IDR is unloaded. | |
Implementation notes: | |
1. In hardware, the Status Word consists of individual flip-flops and status | |
signals that are enabled onto the I/O backplane. In simulation, the | |
individual status values are collected into a Status Word Register, and | |
the Output Data Register does not exist (output data is written directly | |
to the FIFO buffer). | |
2. The DIAG, T, and L control bits enable a data loopback path on the card. | |
An IOO issued to the card unloads a word from the FIFO and then loads the | |
lower byte back into both bytes of the FIFO. The data word output with | |
the IOO instruction is not used. | |
In hardware, IOO triggers the FIFO unload and reload; T and L are | |
required only for the loopback path. If L is not asserted, then the FIFO | |
is loaded with 177777 due to the floating bus. If L is asserted and T is | |
not, then the FIFO is loaded with 000000 due to pullups on the DIO lines. | |
In simulation, we look only for DIAG and assume that T/L are set | |
properly, i.e., unloaded data is reloaded. | |
3. In hardware, the SRQ and NRFD lines are open-collector and may be driven | |
simultaneously from several bus devices. Simulating this fully would | |
require keeping the state of the lines for each device and deriving the | |
common bus signals from the logical OR of the state values. Fortunately, | |
some simplifications are possible. | |
The DI asserts SRQ only if control word bit 15 is 1 and bit 0 is 0. | |
Other bit combinations deny SRQ; as neither the Amigo nor CS/80 protocols | |
use SRQ and serial polls, there will be no other driver. | |
In hardware, every listener drives NRFD, but in practice there is only | |
one listener at a time. When the card is the listener, it asserts NRFD | |
if the FIFO becomes full. In simulation, we assert NRFD on the bus if | |
NRFD is set in the control register, or we are listening and the FIFO is | |
full. We deny NRFD if NRFD had been set in the control register but is | |
no longer, or if we had been a listener but are no longer. That is, we | |
assume that if we have forced NRFD or set it as a listener, then no one | |
else will be asserting NRFD, so it's safe for us to deny NRFD when the | |
override is removed or we are no longer a listener. | |
We also deny NRFD when a CRS is issued if NRFD had been explicitly | |
requested or the card had been listening. The rationale is the same: | |
only a listener can assert NRFD, so if we were listening, it's safe to | |
deny it, because only we could have set it. | |
4. In hardware, the IRL, LBO, LBI, and IFC status bits are driven by | |
corresponding flip-flops. In simulation, the status bits themselves hold | |
the equivalent states and are set and cleared as indicated. | |
5. The card state must be updated during status read (IOI) processing | |
because the 7974 boot ROM watches the IFC line to determine when IFC | |
assertion ends. | |
6. DCPC performance is optimized by recognizing that the normal cases (an | |
input that empties the FIFO or an output that fills the FIFO) do not | |
alter the card state, and so the usual update_state call may be omitted. | |
7. The gcc compiler (at least as of version 4.6.2) does not optimize | |
repeated use of array-of-structures accesses. Instead, it recalculates | |
the index each time, even though the index is a constant within the | |
function. To avoid this performance penalty, we use a pointer to the | |
selected DI_STATE structure. Note that VC++ 2008 does perform this | |
optimization. | |
*/ | |
uint32 di_io (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data) | |
{ | |
static const char * const output_state [] = { "Control", "Data" }; | |
static const char * const input_state [] = { "Status", "Data" }; | |
const char * const hold_or_clear = (signal_set & ioCLF ? ",C" : ""); | |
const CARD_ID card = (CARD_ID) (dibptr->card_index); | |
DI_STATE * const di_card = &di [card]; | |
uint8 assert, deny; /* new bus control states */ | |
uint16 data; | |
t_bool update_required = TRUE; /* TRUE if CLF must update the card state */ | |
IOSIGNAL signal; | |
IOCYCLE working_set = IOADDSIR (signal_set); /* add ioSIR if needed */ | |
while (working_set) { | |
signal = IONEXT (working_set); /* isolate the next signal */ | |
switch (signal) { /* dispatch an I/O signal */ | |
case ioCLF: /* clear flag flip-flop */ | |
di_card->flag = CLEAR; /* clear the flag */ | |
di_card->flagbuf = CLEAR; /* and flag buffer */ | |
tpprintf (dptrs [card], DEB_CMDS, "[CLF] Flag cleared\n"); | |
if (update_required) /* if the card state has changed */ | |
update_state (card); /* then update the state */ | |
break; | |
case ioSTF: /* set flag flip-flop */ | |
tpprintf (dptrs [card], DEB_CMDS, "[STF] Flag set\n"); | |
/* fall into ENF handler */ | |
case ioENF: /* enable flag */ | |
di_card->flag = SET; /* set the flag */ | |
di_card->flagbuf = SET; /* and flag buffer */ | |
break; | |
case ioSFC: /* skip if flag is clear */ | |
setstdSKF (di [card]); | |
break; | |
case ioSFS: /* skip if flag is set */ | |
setstdSKF (di [card]); | |
break; | |
case ioIOI: /* I/O data input */ | |
if (di_card->control == SET) { /* is the card in data mode? */ | |
data = di_card->input_data_register; /* read the input data register */ | |
di_card->status_register &= ~STAT_IRL; /* clear the input register loaded status */ | |
if (FIFO_EMPTY && di_card->eor == CLEAR) { /* is the FIFO empty and end of record not seen? */ | |
if (di_card->srq == SET) | |
tpprintf (dptrs [card], DEB_CMDS, "SRQ cleared\n"); | |
di_card->srq = CLEAR; /* clear SRQ */ | |
update_required = FALSE; /* the card state does not change */ | |
} | |
} | |
else { /* the card is in status mode */ | |
di_card->status_register &= /* clear the values to be computed, */ | |
STAT_IRL | STAT_LBO /* preserving those set elsewhere */ | |
| STAT_LBI | STAT_IFC; | |
di_card->status_register |= /* set T/L/C status from control register */ | |
di_card->cntl_register /* (T/L are ORed, as MTA or MLA can also set) */ | |
& (CNTL_CIC | CNTL_TALK | CNTL_LSTN); | |
if (SW8_SYSCTL) /* if SW8 is set, */ | |
di_card->status_register |= STAT_SYSCTL; /* the card is the system controller */ | |
if (di_card->ibp == lower) /* if lower byte input is next */ | |
di_card->status_register |= STAT_ODD; /* then the last transfer was odd */ | |
di_card->status_register |= /* set the bus status bits */ | |
(di_card->bus_cntl /* from the corresponding bus control lines */ | |
& (BUS_SRQ | BUS_IFC | BUS_REN | |
| BUS_EOI | BUS_ATN)) << DATA_SHIFT; | |
data = di_card->status_register; /* return the status word */ | |
} | |
tpprintf (dptrs [card], DEB_CPU, "[LIx%s] %s = %06o\n", | |
hold_or_clear, input_state [di_card->control], data); | |
if (update_required && !(signal_set & ioCLF)) /* if an update is required and CLF is not present, */ | |
update_state (card); /* update the state, else ioCLF will update it */ | |
stat_data = IORETURN (SCPE_OK, data); /* merge in the return status */ | |
break; | |
case ioIOO: /* I/O data output */ | |
data = IODATA (stat_data); /* get the data value */ | |
tpprintf (dptrs [card], DEB_CPU, "[OTx%s] %s = %06o\n", | |
hold_or_clear, output_state [di_card->control], data); | |
if (di_card->control == SET) { /* is the card in data mode? */ | |
if (signal_set & ioEDT) /* if end of DCPC transfer */ | |
di_card->edt = SET; /* set the EDT flip-flop */ | |
else if (di_card->cntl_register & CNTL_PACK) { /* is this a packed transfer? */ | |
if (!(signal_set & ioCLF)) /* and CLF not given? */ | |
di_card->edt = SET; /* set the EDT flip-flop */ | |
} | |
else /* it's an unpacked transfer */ | |
if (data & DATA_LBO) /* is the last byte out? */ | |
di_card->edt = SET; /* set the EDT flip-flop */ | |
if (di_card->cntl_register & CNTL_DIAG) { /* set for DIAG loopback? */ | |
data = fifo_unload (card, diag_access); /* unload data from the FIFO */ | |
fifo_load (card, data, diag_access); /* and load it back in */ | |
} | |
else { /* the card is set for normal operation */ | |
fifo_load (card, data, cpu_access); /* load the data word into the FIFO */ | |
if (FIFO_FULL && (di_card->bus_cntl & BUS_NRFD)) { /* FIFO full and listener not ready? */ | |
if (di_card->srq == SET) | |
tpprintf (dptrs [card], DEB_CMDS, "SRQ cleared\n"); | |
di_card->srq = CLEAR; /* clear SRQ */ | |
update_required = FALSE; /* the card state does not change */ | |
} | |
} | |
} | |
else { /* the card is in control mode */ | |
assert = 0; /* initialize bus control assertions */ | |
deny = 0; /* and denials */ | |
if (!(data & CNTL_PACK)) /* unpacked mode always sets */ | |
di_card->ibp = di_card->obp = lower; /* byte selectors to the lower byte */ | |
if (data & CNTL_TALK) { /* talking enables ATN and EOI outputs */ | |
if ((data & (CNTL_PPE | CNTL_CIC)) /* if parallel poll is enabled */ | |
== (CNTL_PPE | CNTL_CIC)) /* and the card is CIC */ | |
assert = BUS_PPOLL; /* then conduct a parallel poll */ | |
else if ((di_card->cntl_register /* if PP was enabled */ | |
& (CNTL_PPE | CNTL_CIC)) /* but is not now */ | |
== (CNTL_PPE | CNTL_CIC)) | |
deny = BUS_PPOLL; /* then end the parallel poll */ | |
else if ((data /* if packed mode */ | |
& (CNTL_PACK | CNTL_CIC | CNTL_ATN)) /* and the card is CIC */ | |
== (CNTL_PACK | CNTL_CIC | CNTL_ATN)) /* then the ATN control output */ | |
assert = BUS_ATN; /* is coupled to the bus */ | |
else /* if none of the above */ | |
deny = BUS_ATN; /* then ATN is not driven */ | |
} | |
else /* the card is not talking */ | |
deny = BUS_ATN | BUS_EOI; /* so ATN and EOI are disabled */ | |
if (data & CNTL_NRFD) /* is card not ready set explicitly? */ | |
assert |= BUS_NRFD; /* assert NRFD on the bus */ | |
else if (di_card->cntl_register & CNTL_NRFD) /* NRFD was set but is not now? */ | |
deny |= BUS_NRFD; /* deny NRFD on the bus */ | |
if (FIFO_FULL) /* is the FIFO full? */ | |
if (data & CNTL_LSTN) /* is card now listening? */ | |
assert |= BUS_NRFD; /* listener and a full FIFO asserts NRFD */ | |
else if (di_card->cntl_register & CNTL_LSTN) /* was card a listener but is not now? */ | |
deny |= BUS_NRFD; /* deny NRFD on the bus */ | |
if (SW8_SYSCTL) { /* system controller drives REN and IFC */ | |
if (data & CNTL_REN) /* REN control */ | |
assert |= BUS_REN; /* output is */ | |
else /* coupled to */ | |
deny |= BUS_REN; /* the bus */ | |
if (data & CNTL_IFC) { /* is IFC set? */ | |
assert |= BUS_IFC; /* assert IFC on the bus */ | |
di_card->status_register = | |
di_card->status_register | |
& ~(STAT_LSTN | STAT_TALK) /* clear listen and talk status */ | |
| STAT_IFC; /* and set IFC status */ | |
di_card->ifc_timer = /* start the IFC timer by calculating */ | |
sim_gtime () + IFC_TIMEOUT; /* the IFC stop time (now + 100 microseconds) */ | |
} | |
} | |
if ((data & (CNTL_SRQ | CNTL_CIC)) == CNTL_SRQ) /* if service request and not the controller */ | |
assert |= BUS_SRQ; /* then assert SRQ on the bus */ | |
else /* else */ | |
deny |= BUS_SRQ; /* deny SRQ on the bus */ | |
di_card->cntl_register = data; /* save the control word */ | |
di_bus_control (card, CONTROLLER, assert, deny); /* update the bus control state */ | |
} | |
if (update_required && !(signal_set & ioCLF)) /* if update required and CLF is not present, */ | |
update_state (card); /* update the state, else ioCLF will update it */ | |
break; | |
case ioPOPIO: /* power-on preset to I/O */ | |
di_card->flag = SET; /* set the flag */ | |
di_card->flagbuf = SET; /* and flag buffer */ | |
tpprintf (dptrs [card], DEB_CMDS, "[POPIO] Flag set\n"); | |
break; | |
case ioCRS: /* control reset */ | |
tpprintf (dptrs [card], DEB_CMDS, "[CRS] Master reset\n"); | |
di_card->status_register &= /* clear listen and talk status */ | |
~(STAT_LSTN | STAT_TALK); | |
deny = BUS_SRQ | BUS_REN | BUS_ATN | BUS_EOI; /* clear the lines driven by the control register */ | |
if (di_card->cntl_register & (CNTL_NRFD | CNTL_LSTN)) /* if asserting NRFD or listening */ | |
deny |= BUS_NRFD; /* then deny because we're clearing */ | |
di_card->cntl_register = 0; /* clear the control word register */ | |
di_card->control = CLEAR; /* clear control */ | |
di_card->srq = CLEAR; /* clear SRQ */ | |
master_reset (card); /* perform a master reset */ | |
di_bus_control (card, CONTROLLER, 0, deny); /* update the bus control state */ | |
update_state (card); /* update the card state */ | |
break; | |
case ioCLC: /* clear control flip-flop */ | |
di_card->control = CLEAR; /* clear control */ | |
tpprintf (dptrs [card], DEB_CMDS, "[CLC%s] Control cleared (configure mode)%s\n", | |
hold_or_clear, (signal_set & ioCLF ? ", master reset" : "")); | |
if (signal_set & ioCLF) /* if ioCLF is given, */ | |
master_reset (card); /* then do a master reset */ | |
break; /* (ioCLF will call update_state for us) */ | |
case ioSTC: /* set control flip-flop */ | |
di_card->control = SET; /* set control */ | |
tpprintf (dptrs [card], DEB_CMDS, "[STC%s] Control set (data mode)\n", | |
hold_or_clear); | |
break; | |
case ioEDT: /* end data transfer */ | |
tpprintf (dptrs [card], DEB_CPU, "[EDT] DCPC transfer ended\n"); | |
break; | |
case ioSIR: /* set interrupt request */ | |
setstdPRL (di [card]); /* set the standard PRL signal */ | |
setstdIRQ (di [card]); /* set the standard IRQ signal */ | |
setSRQ (dibptr->select_code, /* set the SRQ signal if control and SRQ are set */ | |
di_card->srq == SET && di_card->control == SET); | |
break; | |
case ioIAK: /* interrupt acknowledge */ | |
di_card->flagbuf = CLEAR; /* clear the flag buffer */ | |
break; | |
default: /* all other signals */ | |
break; /* are ignored */ | |
} | |
working_set = working_set & ~signal; /* remove the current signal from the set */ | |
} | |
return stat_data; | |
} | |
/* Reset the simulator. | |
During a hardware PRESET, POPIO sets the flag buffer and flag flip-flops, and | |
CRS clears the control flip-flop and Control Word Register. In addition, CRS | |
performs a master reset on the card. | |
PON is not used by the card. | |
Implementation notes: | |
1. During a power-on reset, a pointer to the FIFO simulation register is | |
saved to allow access to the "qptr" field during FIFO loading and | |
unloading. This enables SCP to view the FIFO as a circular queue, so | |
that the bottom word of the FIFO is always displayed as FIFO[0], | |
regardless of where it is in the actual FIFO array. | |
*/ | |
t_stat di_reset (DEVICE *dptr) | |
{ | |
DIB *dibptr = (DIB *) dptr->ctxt; /* get the DIB pointer */ | |
const CARD_ID card = (CARD_ID) (dibptr->card_index); /* get the card number */ | |
if (sim_switches & SWMASK ('P')) { /* is this a power-on reset? */ | |
di [card].fifo_reg = find_reg ("FIFO", NULL, dptr); /* find the FIFO register entry */ | |
if (di [card].fifo_reg == NULL) /* if not there */ | |
return SCPE_IERR; /* then this is a programming error! */ | |
else /* found it */ | |
di [card].fifo_reg->qptr = 0; /* so reset the FIFO bottom index */ | |
di [card].status_register = 0; /* clear the status word */ | |
di [card].bus_cntl = 0; /* deny the HP-IB control lines */ | |
di [card].listeners = 0; /* clear the map of listeners */ | |
di [card].talker = 0; /* clear the map of talker */ | |
di [card].poll_response = 0; /* clear the map of parallel poll responses */ | |
di [card].ifc_timer = 0.0; /* clear the IFC timer */ | |
} | |
IOPRESET (dibptr); /* PRESET the device */ | |
return SCPE_OK; | |
} | |
/* Disc interface global SCP routines */ | |
/* Set a unit's bus address. | |
Bus addresses range from 0-7 and are initialized to the unit number. All | |
units of a device must have unique bus addresses. In addition, the card also | |
has a bus address, although this is only used for the diagnostic. The card | |
address may be the same as a unit address, as all units are disconnected | |
during a diagnostic run. | |
The "value" parameter indicates whether the routine is setting a unit's bus | |
address (0) or a card's bus address (1). | |
Implementation notes: | |
1. To ensure that each address is unique, a check is made of the other units | |
for conflicting addresses. An "invalid argument" error is returned if | |
the desired address duplicates another. This means that addresses cannot | |
be exchanged without first assigning one of them to an unused address. | |
Also, an address cannot be set that duplicates the address of a disabled | |
unit (which cannot be displayed without enabling it). | |
An alternate implementation would be to set the new assignments into a | |
"shadow array" that is set into the unit flags (and checked for validity) | |
only when a power-on reset is done. This would follow the disc and tape | |
controller hardware, which reads the HP-IB address switch settings only | |
at power-up. | |
*/ | |
t_stat di_set_address (UNIT *uptr, int32 value, CONST char *cptr, void *desc) | |
{ | |
t_stat status; | |
uint32 index, new_address; | |
uint32 old_address = GET_BUSADR (uptr->flags); | |
DEVICE *dptr = (DEVICE *) desc; | |
if (cptr == NULL) /* if the address is not given */ | |
return SCPE_ARG; /* report a missing argument */ | |
new_address = (uint32) get_uint (cptr, 10, 7, &status); /* parse the address value */ | |
if (status == SCPE_OK) { /* is the parse OK? */ | |
if (value) /* are we setting the card address? */ | |
dptr->flags = dptr->flags & ~DEV_BUSADR /* store the new address in the device flags */ | |
| SET_DIADR (new_address); | |
else { /* we are setting a unit address */ | |
for (index = 0; index < dptr->numunits; index++) /* look through the units */ | |
if (new_address != old_address /* to ensure that the address is unique */ | |
&& new_address == GET_BUSADR (dptr->units [index].flags)) { | |
printf ("Bus address conflict: DA%d\n", index); | |
if (sim_log) | |
fprintf (sim_log, "Bus address conflict: DA%d\n", index); | |
return SCPE_NOFNC; /* a duplicate address gives an error */ | |
} | |
uptr->flags = uptr->flags & ~UNIT_BUSADR /* the address is valid; change it */ | |
| SET_BUSADR (new_address); /* in the unit flags */ | |
} | |
} | |
return status; /* return the result of the parse */ | |
} | |
/* Show a unit's bus address. | |
The "value" parameter indicates whether the routine is showing a unit's bus | |
address (0) or a card's bus address (1). | |
*/ | |
t_stat di_show_address (FILE *st, UNIT *uptr, int32 value, CONST void *desc) | |
{ | |
const DEVICE *dptr = (const DEVICE *) desc; | |
if (value) /* do we want the card address? */ | |
fprintf (st, "address=%d", GET_DIADR (dptr->flags)); /* get it from the device flags */ | |
else /* we want the unit address */ | |
fprintf (st, "bus=%d", GET_BUSADR (uptr->flags)); /* get it from the unit flags */ | |
return SCPE_OK; | |
} | |
/* Set the bus cable connection. | |
In normal use, the various tape and disc devices are connected together and | |
to the disc interface card by HP-IB cables. For the diagnostic, two disc | |
interface cards are connected by a single cable. | |
The "value" parameter indicates whether the routine is connecting the | |
cable to devices for normal use (0) or to another card for diagnostics (1). | |
Implementation notes: | |
1. Initially, only one card and peripheral set is simulated: the ICD disc | |
family (DA device). For diagnostic use, a second, dummy card is enabled | |
(DC device). Once a second card simulation is implemented, this code | |
will no longer be necessary. | |
*/ | |
t_stat di_set_cable (UNIT *uptr, int32 value, CONST char *cptr, void *desc) | |
{ | |
DEVICE *dptr = (DEVICE *) desc; | |
if (value) { /* is the diagnostic cable selected? */ | |
dptr->flags |= DEV_DIAG; /* set the diagnostic flag */ | |
dc_dev.flags &= ~DEV_DIS; /* enable the dummy device */ | |
dc_dev.flags |= DEV_DIAG; /* and set its flag as well */ | |
} | |
else { /* the peripheral cable is selected */ | |
dptr->flags &= ~DEV_DIAG; /* clear the diagnostic flag */ | |
dc_dev.flags |= DEV_DIS; /* disable the dummy device */ | |
dc_dev.flags &= ~DEV_DIAG; /* and clear its flag */ | |
} | |
return SCPE_OK; | |
} | |
/* Show the bus cable connection. | |
The "value" parameter indicates whether the cable is connected to devices for | |
normal use (0) or to another card for diagnostics (1). | |
*/ | |
t_stat di_show_cable (FILE *st, UNIT *uptr, int32 value, CONST void *desc) | |
{ | |
const DEVICE *dptr = (const DEVICE *) desc; | |
if (dptr->flags & DEV_DIAG) /* is the cable connected for diagnostics? */ | |
fputs ("diagnostic cable", st); /* report it */ | |
else /* the cable is connected for device use */ | |
fputs ("HP-IB cable", st); /* report the condition */ | |
return SCPE_OK; | |
} | |
/* Disc interface global bus routines. | |
In hardware, the HP-IB bus consists of eight control lines and eight data | |
lines. Signals are asserted on the control lines to establish communication | |
between a source and one or more acceptors. For commands, the source is | |
always the controller (the 12821A card), and the acceptors are all of the | |
connected devices. For data, the source is the current talker, and the | |
acceptors are one or more current listeners. A three-wire interlocking | |
handshake enables communication at the rate of the slowest of the multiple | |
acceptors. The controller conducts a parallel poll by asserting ATN and EOI | |
together. Devices whose parallel poll responses are enabled each assert one | |
of the data lines to indicate that service is required. | |
In simulation, a disabled or detached unit logically is not connected to the | |
bus. The card maintains a bitmap of acceptors (all devices currently | |
attached), listeners (all devices currently addressed to listen), the talker | |
(the device currently addressed to talk), and the enabled parallel poll | |
responses. Changes in control line state are communicated to all acceptors | |
via control/respond function calls, and data is exchanged between talker and | |
listeners via source/acceptor function calls. Data bytes are sent to all | |
current listeners in bus-address order. The card conducts a parallel poll by | |
checking the response bitmap; devices must set and clear their poll responses | |
appropriately in advance of the poll. | |
Not all of the HP-IB control lines are simulated. The DAV and NDAC handshake | |
lines are never asserted; instead, they are simulated by the bus source | |
function calling one or more bus acceptor functions. SRQ and REN are | |
asserted as directed by the system controller but are not otherwise used (no | |
HP disc or tape devices assert SRQ or respond to REN). IFC, ATN, EOI, and | |
NRFD are asserted and tested by the controller and devices. In particular, | |
asserting NRFD will hold off a pending data transmission until it is denied. | |
The functions that simulate the HP-IB (where "*" is "di", "da", etc.) are: | |
di_bus_source -- Source a data byte to the bus. Returns TRUE if the | |
byte was accepted (i.e., there were one or more | |
listeners) and FALSE if it was not. Called by the | |
controller to send commands to devices, and called by | |
the current talker to send data to the listener(s). ATN | |
and EOI should be asserted as required on the bus before | |
calling. | |
*_bus_accept -- Accept a data byte from the bus. Returns TRUE if the | |
byte was accepted and FALSE if it was not. Called by | |
di_bus_source to handshake between source and acceptor. | |
If ATN is asserted on the bus, the byte is a command; | |
otherwise, it is data. If EOI is asserted for a data | |
byte, it is the last byte of a transmission. | |
di_bus_control -- Set the control lines on the bus. Called by the system | |
controller to assert or deny REN or IFC, by the current | |
controller to assert or deny SRQ, NRFD, or ATN and EOI | |
(to conduct or conclude a parallel poll), and by the | |
current listener to assert or deny NRFD. All connected | |
devices on the bus are notified of the changes. It is | |
not necessary to call di_bus_control for changes to ATN | |
and EOI that accompany a command or data byte. | |
*_bus_respond -- Respond to changes in the control lines on the bus. | |
Called by di_bus_control to inform each connected device | |
of a change in control state. | |
di_poll_response -- Set a device's poll response. Called by a device to | |
enable or disable its response to a future parallel | |
poll. | |
*/ | |
/* Source a byte to the bus. | |
This routine is called to send bytes to devices on the bus connected to the | |
specified card. If the card is in diagnostic mode, which simulate two cards | |
connected by an HP-IB cable, then the byte is sent to another card in the | |
card cage that is also in diagnostic mode and enabled to receive. If the | |
card is not in diagnostic mode, then the byte is sent to all acceptors (if a | |
command) or to all listeners (if data) on the bus. | |
The return value indicates whether or not there were any acceptors on the | |
bus. | |
Implementation notes: | |
1. If the responses from a previously conducted parallel poll are not | |
cleared from the FIFO before enabling the card to transmit, the card will | |
appear to conduct a new parallel poll because the FIFO tags cause ATN and | |
EOI to be asserted. This "fake" parallel poll is ignored (a real | |
parallel poll does not source data onto the bus). | |
*/ | |
t_bool di_bus_source (CARD_ID card, uint8 data) | |
{ | |
CARD_ID other; | |
uint32 acceptors, unit; | |
t_bool accepted = FALSE; | |
tpprintf (dptrs [card], DEB_XFER, "HP-IB DIO %03o signals %s available\n", | |
data, fmt_bitset (di [card].bus_cntl, bus_format)); | |
if (dptrs [card]->flags & DEV_DIAG) /* is this a diagnostic run? */ | |
for (other = first_card; other <= last_card; other++) { /* look through the list of cards */ | |
if (other != card && dptrs [other] /* for the other card */ | |
&& (dptrs [other]->flags & DEV_DIAG) /* that is configured for diagnostic mode */ | |
&& (di [other].cntl_register & CNTL_LSTN)) /* and is listening */ | |
accepted = di_bus_accept (other, data); /* call the interface acceptor for the other card */ | |
} | |
else if ((di [card].bus_cntl & BUS_PPOLL) != BUS_PPOLL) { /* this is a normal run; not a fake poll? */ | |
if (di [card].cntl_register & CNTL_LSTN) /* is the card a listener? */ | |
accepted = di_bus_accept (card, data); /* call the interface acceptor for this card */ | |
acceptors = di [card].acceptors; /* get the map of acceptors */ | |
if (!(di [card].bus_cntl & BUS_ATN) /* if a data transfer, */ | |
|| (data & BUS_COMMAND) == BUS_ACG) /* or an addressed command, e.g., SDC */ | |
acceptors = di [card].listeners; /* then limit just to listeners */ | |
for (unit = 0; acceptors; unit++) { /* loop through the units */ | |
if (acceptors & 1) /* is the current unit accepting? */ | |
accepted |= (*bus_accept [card]) (unit, data); /* call the acceptor for this card */ | |
acceptors = acceptors >> 1; /* move to the next acceptor */ | |
} | |
} | |
if (!accepted) | |
tpprintf (dptrs [card], DEB_XFER, "HP-IB no acceptors\n"); | |
return accepted; | |
} | |
/* Assert or deny control on the bus. | |
This routine is called by the indicated unit to assert or deny the HP-IB | |
control lines on the bus connected to the specified card. Separate sets of | |
signals to assert and deny are provided. | |
If the bus state after modification did not change, the routine returns with | |
no further action. Otherwise, if the card is in diagnostic mode, then | |
notification of the bus change is sent to another card in the card cage that | |
is also in diagnostic mode. | |
If the card is not in diagnostic mode, then the set of control lines that | |
are changing is checked to determine whether notification is necessary. If | |
not, then the change is not broadcast to improve performance. However, if | |
notification is required, then all acceptors on the bus are informed of the | |
change. | |
Implementation notes: | |
1. If a signal is asserted and denied in the same call, the assertion takes | |
precedence. | |
2. Of the sixteen potential control line state changes, only IFC assertion | |
and ATN and NRFD denial must be broadcast. Asserting IFC unaddresses all | |
devices, and denying ATN or NRFD allows a waiting talker to source a data | |
byte to the bus. Devices do not act upon the remaining thirteen state | |
changes, and a considerable performance improvement is obtained by | |
omitting the notification calls. | |
3. All control line state notifications are sent in diagnostic mode, as the | |
responses of the other card are specifically tested by the diagnostic. | |
4. Asserting ATN and EOI will conduct a parallel poll. Devices are not | |
notified of the poll. Instead, the previously stored parallel poll | |
responses will be used. | |
*/ | |
#define ASSERT_SET (BUS_IFC) | |
#define DENY_SET (BUS_ATN | BUS_NRFD) | |
void di_bus_control (CARD_ID card, uint32 unit, uint8 assert, uint8 deny) | |
{ | |
CARD_ID other; | |
uint32 acceptors, responder; | |
t_bool responded; | |
uint8 new_state, new_assertions, new_denials; | |
new_state = di [card].bus_cntl & ~deny | assert; /* set up the new control state */ | |
if (new_state == di [card].bus_cntl) /* if the control state did not change */ | |
return; /* return now */ | |
new_assertions = ~di [card].bus_cntl & assert; /* get the changing assertions */ | |
new_denials = di [card].bus_cntl & deny; /* get the changing denials */ | |
di [card].bus_cntl = new_state; /* establish the new control state */ | |
if (unit == CONTROLLER) | |
tpprintf (dptrs [card], DEB_XFER, "HP-IB card %d asserted %s denied %s bus is %s\n", | |
card, | |
fmt_bitset (new_assertions, bus_format), | |
fmt_bitset (new_denials, bus_format), | |
fmt_bitset (new_state, bus_format)); | |
else | |
tpprintf (dptrs [card], DEB_XFER, "HP-IB address %d asserted %s denied %s bus is %s\n", | |
GET_BUSADR (dptrs [card]->units [unit].flags), | |
fmt_bitset (new_assertions, bus_format), | |
fmt_bitset (new_denials, bus_format), | |
fmt_bitset (new_state, bus_format)); | |
if ((dptrs [card]->flags & DEV_DIAG) /* is the card in diagnostic mode? */ | |
|| (new_assertions & ASSERT_SET) /* or are changed signals in the */ | |
|| (new_denials & DENY_SET)) { /* set that must be broadcast? */ | |
responded = FALSE; /* assume no response was received */ | |
if (dptrs [card]->flags & DEV_DIAG) { /* is this a diagnostic run? */ | |
for (other = first_card; other <= last_card; other++) /* look through the list of cards */ | |
if (other != card && dptrs [other] /* for the other card */ | |
&& (dptrs [other]->flags & DEV_DIAG)) { /* that is configured for diagnostic */ | |
di_bus_respond (other, new_state); /* notify the other card of the new control state */ | |
responded = TRUE; /* and note that there was a responder */ | |
} | |
} | |
else { /* this is a normal run */ | |
update_state (card); /* update the card for the new control state */ | |
acceptors = di [card].acceptors; /* get the map of acceptors */ | |
responded = (acceptors != 0); /* set response if there are any acceptors */ | |
for (responder = 0; acceptors; responder++) { /* loop the through units */ | |
if ((acceptors & 1) && responder != unit) /* is the current unit accepting? */ | |
(*bus_respond [card]) (card, responder, new_state); /* call the responder for this card */ | |
acceptors = acceptors >> 1; /* move to the next acceptor */ | |
} | |
} | |
if (!responded) | |
tpprintf (dptrs [card], DEB_XFER, "HP-IB no responders\n"); | |
} | |
if ((new_state & BUS_PPOLL) == BUS_PPOLL) /* was a parallel poll requested? */ | |
di_bus_poll (card); /* conduct the poll */ | |
return; | |
} | |
/* Enable or disable a unit's parallel poll response. | |
The poll response for a unit connected to a specified card is set or cleared | |
as indicated. If a parallel poll is in progress when a poll response is set, | |
the poll is conducted again to reflect the new response. | |
*/ | |
void di_poll_response (CARD_ID card, uint32 unit, FLIP_FLOP response) | |
{ | |
const uint32 address = GET_BUSADR (dptrs [card]->units [unit].flags); | |
uint32 previous_response = di [card].poll_response; | |
if (response == SET) { /* enable the poll response? */ | |
di [card].poll_response |= PPR (address); /* set the response bit */ | |
if ((di [card].bus_cntl & BUS_PPOLL) == BUS_PPOLL) /* is a parallel poll in progress? */ | |
di_bus_poll (card); /* conduct again with the new response */ | |
} | |
else /* disable the poll response */ | |
di [card].poll_response &= ~PPR (address); /* by clearing the response bit */ | |
if (previous_response != di [card].poll_response) | |
tpprintf (dptrs [card], DEB_XFER, "HP-IB address %d parallel poll response %s\n", | |
address, (response == SET ? "enabled" : "disabled")); | |
return; | |
} | |
/* Disc interface local bus routines */ | |
/* Conduct a parallel poll on the bus. | |
A controller asserting ATN and EOI simultaneously on the bus is conducting a | |
parallel poll. In hardware, each device whose poll response is enabled | |
asserts the data line corresponding to its bus address. The controller | |
terminates the poll by denying ATN and EOI. | |
Setting the CIC (controller in charge) and PPE (parallel poll enable) bits in | |
the Control Word Register direct the disc interface to conduct a poll. | |
Setting PPE without CIC enables the poll response for the interface. | |
In the diagnostic mode, one card is set to conduct the poll, and the other is | |
set to respond to it. In the normal mode, connected devices have set or | |
cleared their respective poll responses before this routine is called. | |
Implementation notes: | |
1. The card hardware fills the upper and lower bytes of the FIFO with the | |
response byte. In simulation, we use the diag_access mode to do the same | |
thing (diagnostic loopback also fills both bytes with the lower byte). | |
*/ | |
static void di_bus_poll (CARD_ID card) | |
{ | |
CARD_ID other; | |
uint8 response; | |
if ((di [card].cntl_register | |
& (CNTL_PPE | CNTL_CIC)) == CNTL_PPE) /* is the card's poll response enabled? */ | |
response = di [card].poll_response /* add the card's response */ | |
| PPR (GET_DIADR (dptrs [card]->flags)); /* to the devices' responses */ | |
else | |
response = di [card].poll_response; /* the card response is disabled, so just use devices */ | |
if (dptrs [card]->flags & DEV_DIAG) /* is this a diagnostic run? */ | |
for (other = first_card; other <= last_card; other++) /* look through the list of cards */ | |
if (other != card && dptrs [other] /* for another card */ | |
&& (dptrs [other]->flags & DEV_DIAG) /* that is configured for the diagnostic */ | |
&& (di [other].cntl_register /* and has PPE asserted */ | |
& (CNTL_PPE | CNTL_CIC)) == CNTL_PPE) | |
response |= /* merge its poll response */ | |
PPR (GET_DIADR (dptrs [other]->flags)); | |
if (response) { /* is a poll response indicated? */ | |
tpprintf (dptrs [card], DEB_XFER, "HP-IB parallel poll DIO %03o\n", | |
response); | |
while (di [card].fifo_count != FIFO_SIZE) /* fill the card FIFO with the responses */ | |
fifo_load (card, (uint16) response, diag_access); /* (hardware feature) */ | |
update_state (card); /* update the card state */ | |
} | |
return; | |
} | |
/* Accept a data byte from the bus. | |
The indicated card accepts a byte that has been sourced to the bus. The byte | |
is loaded into the FIFO, and the card state is updated to reflect the load. | |
Bus acceptors return TRUE to indicate that the byte was accepted. A card | |
always accepts a byte, so the routine always returns TRUE. | |
*/ | |
static t_bool di_bus_accept (CARD_ID card, uint8 data) | |
{ | |
tpprintf (dptrs [card], DEB_XFER, "HP-IB card %d accepted data %03o\n", | |
card, data); | |
fifo_load (card, data, bus_access); /* load the data byte into the FIFO */ | |
update_state (card); /* and update the card state */ | |
return TRUE; /* indicate that the byte was accepted */ | |
} | |
/* Respond to the bus control lines. | |
The indicated card is notified of the new control state on the bus. The | |
routine establishes the new bus state and updates the card state to reflect | |
the change. | |
*/ | |
static void di_bus_respond (CARD_ID card, uint8 new_cntl) | |
{ | |
di [card].bus_cntl = new_cntl; /* update the bus control lines */ | |
update_state (card); /* update the card state */ | |
return; | |
} | |
/* Disc interface local utility routines */ | |
/* Master reset the interface. | |
This is the programmed card master reset, not the simulator reset routine. | |
Master reset initializes a number of flip-flops and data paths on the card. | |
The primary use, other than during a PRESET, is to clear the FIFO in | |
preparation to changing the card from a listener to a talker or vice versa. | |
This ensures that unneeded FIFO data is not transmitted inadvertently to the | |
bus or to the CPU. It is also used when changing the data mode from unpacked | |
to packed to release the byte pointer flip-flops, which are held in the | |
"lower byte" position during unpacked transfers. | |
In hardware, a master reset: | |
- clears the EDT, EOR, IRL, LBO, LBI, and IFC flip-flops | |
- clears the Input Data Register | |
- clears the FIFO | |
- sets or clears the odd/even input and output byte pointer flip-flops, | |
depending on whether the P (packed transfer) bit is set in the Control | |
Word Register | |
*/ | |
static void master_reset (CARD_ID card) | |
{ | |
di [card].edt = CLEAR; /* clear the EDT flip-flop */ | |
di [card].eor = CLEAR; /* clear the EOR flip-flop */ | |
if (di [card].cntl_register & CNTL_PACK) /* if packed mode is set, */ | |
di [card].ibp = di [card].obp = upper; /* MR sets the selectors to the upper byte */ | |
else /* otherwise, unpacked mode overrides */ | |
di [card].ibp = di [card].obp = lower; /* and sets the selectors to the lower byte */ | |
di [card].status_register &= /* clear the status flip-flops */ | |
~(STAT_IRL | STAT_LBO | STAT_LBI | STAT_IFC); | |
di [card].input_data_register = 0; /* clear the input data register */ | |
di [card].fifo_count = 0; /* clear the FIFO */ | |
tpprintf (dptrs [card], DEB_BUF, "FIFO cleared\n"); | |
return; | |
} | |
/* Update the interface state. | |
In hardware, certain external operations cause automatic responses by the | |
disc interface card. For example, when the Input Data Register is unloaded | |
by an LIx instruction, it is automatically reloaded with the next word from | |
the FIFO. Also, the card may be set to interrupt in response to the | |
assertion of certain bus control lines. | |
In simulation, this routine must be called whenever the FIFO, card control, | |
or bus control state changes. It determines whether: | |
1. ...the next word from the FIFO should be unloaded into the IDR. If the | |
card is listening, and the IDR is empty, and the FIFO contains data, then | |
a word is unloaded and stored in the IDR, and the Input Register Loaded | |
status bit is set. | |
2. ...the next word from the FIFO should be unloaded and sourced to the bus. | |
If the card is talking (but not polling), and the listener is ready to | |
accept data, and the last byte has not been sent, and the FIFO contains | |
data, then a word is unloaded and sourced to the bus. This occurs | |
regardless of whether or not there are any listeners. | |
3. ...an interface clear operation has completed. If IFC is asserted, and | |
the current simulation time is later than the IFC expiration time, then | |
IFC is denied, and the timer is reset. | |
4. ...the card should assert NRFD to prevent FIFO overflow. If the card is | |
listening, and the FIFO is full, or the last byte has been received, or a | |
pause has been explicitly requested, then NRFD is asserted. | |
5. ...the SRQ flip-flop should be set or cleared. If the card is listening | |
and the Input Data Register has been loaded, or the card is talking and | |
the FIFO is not full, then SRQ is asserted to request a DCPC transfer. | |
6. ...the flag flip-flop should be set or cleared. If the Input Data | |
Register has been loaded or the Last Byte Out flip-flop is set and the | |
corresponding Control Word Register IRL or LBO bits are set, or the End | |
of Record flip-flop is set and the Input Data Register has been unloaded, | |
or SRQ is asserted on the bus and the corresponding Control Word Register | |
bit is set when the card is not the controller-in-charge, or REN or IFC | |
is asserted on the bus and the corresponding Control Word Register bits | |
are set when the card is not the system controller, then the flag is set | |
to request an interrupt. | |
Implementation notes: | |
1. The fifo_unload routine may set STAT_LBO, so the flag test must be done | |
after unloading. | |
2. The gcc compiler (at least as of version 4.6.2) does not optimize | |
repeated use of array-of-structures accesses. Instead, it recalculates | |
the index each time, even though the index is a constant within the | |
function. To avoid this performance penalty, we use a pointer to the | |
selected DI_STATE structure. Note that VC++ 2008 does perform this | |
optimization. | |
*/ | |
static void update_state (CARD_ID card) | |
{ | |
DIB * const dibptr = (DIB *) dptrs [card]->ctxt; | |
DI_STATE * const di_card = &di [card]; | |
uint8 assert = 0; | |
uint8 deny = 0; | |
uint16 data; | |
FLIP_FLOP previous_state; | |
if (di_card->cntl_register & CNTL_LSTN) { /* is the card a listener? */ | |
if (!(di_card->status_register & STAT_IRL) /* is the IDR empty? */ | |
&& ! FIFO_EMPTY) { /* and data remains in the FIFO? */ | |
data = fifo_unload (card, cpu_access); /* unload the FIFO */ | |
di_card->input_data_register = data; /* into the IDR */ | |
di_card->status_register |= STAT_IRL; /* set the input register loaded status */ | |
} | |
} | |
else if ((di_card->cntl_register /* is the card a talker? */ | |
& (CNTL_TALK | CNTL_PPE)) == CNTL_TALK) /* and not polling? */ | |
while (! FIFO_EMPTY /* is data remaining in FIFO? */ | |
&& !(di_card->bus_cntl & BUS_NRFD) /* and NRFD is denied? */ | |
&& !(di_card->status_register & STAT_LBO)) { /* and the last byte has not been sent? */ | |
data = fifo_unload (card, bus_access); /* unload a FIFO byte */ | |
di_bus_source (card, (uint8) data); /* source it to the bus */ | |
} | |
if (di_card->bus_cntl & BUS_IFC /* is an IFC in progress? */ | |
&& di_card->ifc_timer != 0.0 /* and I am timing? */ | |
&& sim_gtime () > di_card->ifc_timer) { /* and has the timeout elapsed? */ | |
deny = BUS_IFC; /* deny IFC on the bus */ | |
di_card->ifc_timer = 0.0; /* clear the IFC timer */ | |
di_card->status_register &= ~STAT_IFC; /* and clear IFC status */ | |
} | |
if (di_card->cntl_register & CNTL_LSTN) /* is the card a listener? */ | |
if (di_card->cntl_register & CNTL_NRFD /* if explicitly requested */ | |
|| di_card->status_register & STAT_LBI /* or the last byte is in */ | |
|| FIFO_FULL) /* or the FIFO is full */ | |
assert = BUS_NRFD; /* then assert NRFD */ | |
else /* otherwise the card is ready for data */ | |
deny |= BUS_NRFD; /* so deny NRFD */ | |
if (assert != deny) /* was there any change in bus state? */ | |
di_bus_control (card, CONTROLLER, assert, deny); /* update the bus control */ | |
previous_state = di_card->srq; /* save the current SRQ state */ | |
if (di_card->cntl_register & CNTL_LSTN /* if the card is a listener */ | |
&& di_card->status_register & STAT_IRL /* and the input register is loaded, */ | |
|| di_card->cntl_register & CNTL_TALK /* or the card is a talker */ | |
&& ! FIFO_FULL) /* and the FIFO is not full */ | |
di_card->srq = SET; /* then request a DCPC cycle */ | |
else | |
di_card->srq = CLEAR; /* otherwise, DCPC service is not needed */ | |
if (di_card->srq != previous_state) | |
tpprintf (dptrs [card], DEB_CMDS, "SRQ %s\n", | |
di_card->srq == SET ? "set" : "cleared"); | |
if (di_card->status_register & STAT_IRL /* is the input register loaded */ | |
&& di_card->cntl_register & CNTL_IRL /* and notification is wanted? */ | |
|| di_card->status_register & STAT_LBO /* or is the last byte out */ | |
&& di_card->cntl_register & CNTL_LBO /* and notification is wanted? */ | |
|| di_card->eor == SET /* or was the end of record seen */ | |
&& !(di_card->status_register & STAT_IRL) /* and the input register was unloaded? */ | |
|| di_card->bus_cntl & BUS_SRQ /* or is SRQ asserted on the bus */ | |
&& di_card->cntl_register & CNTL_SRQ /* and notification is wanted */ | |
&& di_card->cntl_register & CNTL_CIC /* and the card is not controller? */ | |
|| !SW8_SYSCTL /* or is the card not the system controller */ | |
&& di_card->bus_cntl & BUS_REN /* and REN is asserted on the bus */ | |
&& di_card->cntl_register & CNTL_REN /* and notification is wanted? */ | |
|| !SW8_SYSCTL /* or is the card not the system controller */ | |
&& di_card->status_register & STAT_IFC /* and IFC is asserted on the bus */ | |
&& di_card->cntl_register & CNTL_IFC) { /* and notification is wanted? */ | |
tpprintf (dptrs [card], DEB_CMDS, "Flag set\n"); | |
di_io (dibptr, ioENF, 0); /* set the flag and recalculate interrupts */ | |
} | |
else if (di_card->srq != previous_state) /* if SRQ changed state, */ | |
di_io (dibptr, ioSIR, 0); /* then recalculate interrupts */ | |
return; | |
} | |
/* Load a word or byte into the FIFO. | |
A word or byte is loaded into the next available location in the FIFO. The | |
significance of the data parameter is indicated by the access mode as | |
follows: | |
- For CPU access, the parameter is a 16-bit value. | |
- For bus access, the parameter is an 8-bit value in the lower byte and a | |
zero in the upper byte. | |
- For diagnostic access, the parameter is an 8-bit value in the lower byte | |
that will be duplicated in the upper byte. | |
For bus access, byte loading into the FIFO is controlled by the value of the | |
Input Buffer Pointer (IBP) selector. | |
In addition to data words, the FIFO holds tags that mark the last byte | |
received or to be transmitted and that indicate the state of the ATN and EOI | |
bus lines (if listening) or the states to assert (if talking). The tag is | |
assembled into the upper word, the data is assembled into the lower word, and | |
then the 32-bit value is stored in the next available FIFO location. | |
If data is coming from the CPU, the 16-bit value is loaded into the next FIFO | |
location, and the occupancy count is incremented. | |
If the data is coming from the bus, and the input mode is unpacked, the 8-bit | |
value is loaded into the lower byte of the next FIFO location, and the | |
occupancy count is incremented. In hardware, the upper FIFO is not clocked; | |
in simulation, the upper byte is set to zero. The IBP always points at the | |
lower byte in unpacked mode. | |
If the data is coming from the bus, and the input mode is packed, the 8-bit | |
value is loaded into either the upper or lower byte of the next FIFO | |
location, depending on the value of the IBP, and the IBP is toggled. If the | |
value was stored in the lower byte, the occupancy count is incremented. | |
A special case occurs when the value is to be stored in the upper byte, and | |
the LBR tag is set to indicate that this is the last byte to be received. In | |
this case, the value is stored in both bytes of the next FIFO location, and | |
the occupancy counter is incremented. | |
If data is coming from the diagnostic FIFO loopback, the 8-bit value in the | |
lower byte is copied to the upper byte, the resulting 16-bit value is loaded | |
into the next FIFO location, and the occupancy count is incremented. | |
Implementation notes: | |
1. Four tag bits are loaded into the upper word of each FIFO entry: | |
- Last Byte Received (while receiving, a line feed is received and the | |
LF bit is set in the Control Word Register, or a byte with EOI | |
asserted is received and the EOI bit is set). | |
- End of Data Transfer (while transmitting, DCPC asserts the EDT | |
backplane signal, or an unpacked-mode data word has the LBO bit set, | |
or a packed-mode OTx is issued without an accompanying CLF). | |
- ATN (the state of ATN on the bus if receiving, or the ATN bit in the | |
unpacked data word if transmitting). | |
- EOI (the state of EOI on the bus if receiving, or the EOI bit in the | |
unpacked data word if transmitting). | |
2. The FIFO is implemented as circular queue to take advantage of REG_CIRC | |
EXAMINE semantics. REG->qptr is the index of the first word currently in | |
the FIFO. By specifying REG_CIRC, examining FIFO[0-n] will always | |
display the words in load order, regardless of the actual array index of | |
the start of the list. The number of words currently present in the FIFO | |
is kept in fifo_count (0 = empty, 1-16 = number of words available). | |
If fifo_count < FIFO_SIZE, (REG->qptr + fifo_count) mod FIFO_SIZE is the | |
index of the new word location. Loading stores the word there and then | |
increments fifo_count. | |
3. Because the load and unload routines need access to qptr in the REG | |
structure for the FIFO array, pointers to the REG for each card are | |
stored in the fifo_reg array during device reset. | |
4. The gcc compiler (at least as of version 4.6.2) does not optimize | |
repeated use of array-of-structures accesses. Instead, it recalculates | |
the index each time, even though the index is a constant within the | |
function. To avoid this performance penalty, we use a pointer to the | |
selected DI_STATE structure. Note that VC++ 2008 does perform this | |
optimization. | |
*/ | |
static void fifo_load (CARD_ID card, uint16 data, FIFO_ACCESS access) | |
{ | |
uint32 tag, index; | |
t_bool add_word = TRUE; | |
DI_STATE * const di_card = &di [card]; | |
if (FIFO_FULL) { /* is the FIFO already full? */ | |
tpprintf (dptrs [card], DEB_BUF, "Attempted load to full FIFO, data %0*o\n", | |
(access == bus_access ? 3 : 6), data); | |
return; /* return with the load ignored */ | |
} | |
if (di_card->cntl_register & CNTL_LSTN) { /* is the card receiving? */ | |
tag = (di_card->bus_cntl /* set the tag from the bus signals */ | |
& (BUS_ATN | BUS_EOI)) << BUS_SHIFT; /* shifted to the tag locations */ | |
if ((di_card->cntl_register & CNTL_EOI /* EOI detection is enabled, */ | |
&& di_card->bus_cntl & BUS_EOI) /* and data was tagged with EOI? */ | |
|| (di_card->cntl_register & CNTL_LF /* or LF detection is enabled, */ | |
&& LOWER_BYTE (data) == LF)) { /* and the byte is a line feed? */ | |
tag = tag | TAG_LBR; /* tag as the last byte received */ | |
di_card->status_register |= STAT_LBI; /* set the last byte in status */ | |
} | |
else /* neither termination condition was seen */ | |
di_card->status_register &= ~STAT_LBI; /* so clear the last byte in status */ | |
} | |
else /* the card is transmitting */ | |
tag = (data & (DATA_ATN | DATA_EOI)) << DATA_SHIFT; /* set the tag from the data shifted to the tag location */ | |
if (di_card->edt == SET) /* is this the end of the data transfer? */ | |
tag = tag | TAG_EDT; /* set the EDT tag */ | |
index = (di_card->fifo_reg->qptr /* calculate the index */ | |
+ di_card->fifo_count) % FIFO_SIZE; /* of the next available location */ | |
if (access == bus_access) { /* is this a bus access */ | |
if (di_card->ibp == upper) { /* in packed mode for the upper byte? */ | |
di_card->ibp = lower; /* set the lower byte as next */ | |
if (tag & TAG_LBR) /* is this the last byte? */ | |
di_card->fifo [index] = /* copy to both bytes of the FIFO */ | |
tag | TO_WORD (data, data); /* and store with the tag */ | |
else { /* more bytes are expected */ | |
di_card->fifo [index] = /* so position this byte */ | |
tag | TO_WORD (data, 0); /* and store it with the tag */ | |
add_word = FALSE; /* wait for the second byte before adding */ | |
} | |
} | |
else /* this is the lower byte */ | |
if (di_card->cntl_register & CNTL_PACK) { /* is the card in packed mode? */ | |
di_card->ibp = upper; /* set the upper byte as next */ | |
di_card->fifo [index] = /* merge the data and tag values */ | |
tag | di_card->fifo [index] | TO_WORD (0, data); | |
} | |
else /* the card is in unpacked mode */ | |
di_card->fifo [index] = /* position this byte */ | |
tag | TO_WORD (0, data); /* and store with the tag */ | |
} | |
else if (access == cpu_access) /* is this a cpu access? */ | |
di_card->fifo [index] = tag | data; /* store the tag and full word in the FIFO */ | |
else { /* must be diagnostic access */ | |
data = TO_WORD (data, data); /* copy the lower byte to the upper byte */ | |
di_card->fifo [index] = tag | data; /* and store the tag and full word in the FIFO */ | |
} | |
if (add_word) /* did we add a word to the FIFO? */ | |
di_card->fifo_count = di_card->fifo_count + 1; /* increment the count of words stored */ | |
tpprintf (dptrs [card], DEB_XFER, "Data %0*o tag %s loaded into FIFO (%d)\n", | |
(access == bus_access ? 3 : 6), data, | |
fmt_bitset (tag, tag_format), di_card->fifo_count); | |
return; | |
} | |
/* Unload a word or byte from the FIFO. | |
A word or byte is unloaded from the first location in the FIFO. The | |
significance of the returned value is indicated by the access mode as | |
follows: | |
- For CPU access, a 16-bit value is unloaded and returned. | |
- For bus access, an 8-bit value is unloaded and returned. | |
- For diagnostic access, an 16-bit value is unloaded, and the lower byte | |
is returned. | |
For bus access, byte unloading from the FIFO is controlled by the value of | |
the Output Buffer Pointer (OBP) selector. | |
If the FIFO is not empty, the first entry is obtained and split into tag and | |
data words. The LBR tag value is loaded into the EOR flip-flop if the CPU is | |
accessing. The EDT tag sets Last Byte Out status if the last byte is being | |
unloaded. | |
If the data is going to the CPU, the 16-bit packed data value is returned as | |
is, or the lower byte of the unpacked value is merged with the tags for ATN | |
and EOI and returned. The occupancy count is decremented to unload the FIFO | |
entry. | |
If the data is going to the bus, and the input mode is unpacked, the 8-bit | |
value is returned in the lower byte, and the occupancy count is decremented. | |
In hardware, the upper FIFO is not clocked; in simulation, the upper byte is | |
ignored. The OBP always points at the lower byte in unpacked mode. | |
If the data is going to the bus, and the input mode is packed, the 8-bit | |
value is unloaded from either the upper or lower byte of the data word, | |
depending on the value of the OBP, and returned in the lower byte. The OBP | |
value is toggled. If the value was obtained from the lower byte, the | |
occupancy count is decremented to unload the FIFO. Otherwise, the count is | |
not altered, so that the lower-byte access will be from the same FIFO entry. | |
If data is going to the diagnostic FIFO loopback, the lower byte of the | |
16-bit value is returned; the upper byte of the returned value is zero. | |
Implementation notes: | |
1. Four tag bits are unloaded from the upper word of each FIFO entry: | |
- Last Byte Received (sets the End of Record flip-flop when the last | |
byte received is loaded into the Input Data Register). | |
- End of Data Transfer (sets the LBO bit in the Status Word Register | |
when the last byte is unloaded from the FIFO). | |
- ATN (in unpacked mode, sets the ATN bit in the returned data word | |
if listening, or controls the bus ATN line if talking; in packed mode, | |
the tag is ignored). | |
- EOI (in unpacked mode, sets the EOI bit in the returned data word if | |
listening, or asserts the bus EOI line if talking; in packed mode, the | |
tag is ignored). | |
ATN and EOI tag handling is complex. If the card is listening in the | |
unpacked mode, the ATN tag substitutes for bit 8 of the data word, and | |
the EOI tag substitutes for bit 9. In the packed mode, bits 8 and 9 are | |
as stored in the FIFO (they are upper-byte data bits). | |
If the card is talking in the unpacked mode, the ATN tag asserts or | |
denies ATN on the bus if the card is the CIC, and the EOI tag asserts or | |
denies EOI on the bus. In the packed mode, the ATN bit in the Control | |
Word Register asserts or denies ATN on the bus if the card is the CIC, | |
and the EOI bit asserts EOI on the bus if the last byte of the entry | |
tagged with EDT has been unloaded from the FIFO (which sets LBO status) | |
or denies EOI otherwise. | |
2. In hardware, the EOR flip-flop is clocked with the Input Data Register. | |
Therefore, when the card is listening, EOR is set not when the last byte | |
is unloaded from the FIFO, but rather when that byte is loaded into the | |
IDR. These two actions occur together when the IDR is empty. | |
However, during diagnostic access, data unloaded from the FIFO is | |
reloaded, and the IDR is never clocked. As the T and L bits must be set | |
with DIAG in the Control Word Register to enable the loopback path, the | |
LBR tag will be entered into the FIFO if EOI or LF detection is enabled, | |
but the EOR flip-flop will not be set when that word falls through to be | |
unloaded. | |
In simulation, EOR is set whenever the LBR tag is unloaded from the FIFO | |
during CPU access, as a CPU unload is always followed by an IDR store. | |
3. If fifo_count > 0, REG->qptr is the index of the word to remove. Removal | |
gets the word and then increments qptr (mod FIFO_SIZE) and decrements | |
fifo_count. | |
4. The gcc compiler (at least as of version 4.6.2) does not optimize | |
repeated use of array-of-structures accesses. Instead, it recalculates | |
the index each time, even though the index is a constant within the | |
function. To avoid this performance penalty, we use a pointer to the | |
selected DI_STATE structure. Note that VC++ 2008 does perform this | |
optimization. | |
*/ | |
static uint16 fifo_unload (CARD_ID card, FIFO_ACCESS access) | |
{ | |
uint32 data, tag; | |
t_bool remove_word = TRUE; | |
DI_STATE * const di_card = &di [card]; | |
if (FIFO_EMPTY) { /* is the FIFO already empty? */ | |
tpprintf (dptrs [card], DEB_BUF, "Attempted unload from empty FIFO\n"); | |
return 0; /* return with no data */ | |
} | |
data = di_card->fifo [di_card->fifo_reg->qptr]; /* get the tag and data from the FIFO */ | |
tag = data & TAG_MASK; /* mask the tag to just the tag bits */ | |
data = data & DMASK; /* and the data to just the data bits */ | |
if (tag & TAG_EDT /* is this the end of a data transfer */ | |
&& (di_card->obp == lower /* and the lower byte is next */ | |
|| di_card->cntl_register & CNTL_ODD)) /* or we are sending an odd number of bytes? */ | |
di_card->status_register |= STAT_LBO; /* set the last byte out status */ | |
if (access == cpu_access) { /* is this a cpu access? */ | |
if (!(di_card->cntl_register & CNTL_PACK)) /* in unpacked mode? */ | |
data = data & ~(DATA_ATN | DATA_EOI) /* substitute the ATN/EOI tag values */ | |
| (tag & (TAG_ATN | TAG_EOI)) >> DATA_SHIFT; /* into the data word */ | |
if (tag & TAG_LBR) /* is this the last byte? */ | |
di_card->eor = SET; /* set */ | |
else /* or clear */ | |
di_card->eor = CLEAR; /* the end-of-record flip-flop */ | |
} | |
else if (access == bus_access) /* is this a bus access? */ | |
if (di_card->obp == upper) { /* is this the upper byte? */ | |
di_card->obp = lower; /* set the lower byte as next */ | |
data = UPPER_BYTE (data); /* mask and position the upper byte in the data word */ | |
remove_word = FALSE; /* do not unload the FIFO until the next byte */ | |
} | |
else { /* this is the lower byte */ | |
data = LOWER_BYTE (data); /* mask and position it in the data word */ | |
if (di_card->cntl_register & CNTL_PACK) /* is the card in the packed mode? */ | |
di_card->obp = upper; /* set the upper byte as next */ | |
} | |
else /* must be a diagnostic access */ | |
data = LOWER_BYTE (data); /* access is to the lower byte only */ | |
if (remove_word) { /* remove the word from the FIFO? */ | |
di_card->fifo_reg->qptr = /* update the FIFO queue pointer */ | |
(di_card->fifo_reg->qptr + 1) % FIFO_SIZE; /* and wrap around as needed */ | |
di_card->fifo_count = di_card->fifo_count - 1; /* decrement the FIFO count */ | |
} | |
tpprintf (dptrs [card], DEB_BUF, "Data %0*o tag %s unloaded from FIFO (%d)\n", | |
(access == cpu_access ? 6 : 3), data, | |
fmt_bitset (tag, tag_format), di_card->fifo_count); | |
if (di_card->cntl_register & CNTL_TALK) /* is the card talking? */ | |
if (di_card->cntl_register & CNTL_PACK) /* is it in the packed mode? */ | |
if (di_card->status_register & STAT_LBO /* yes, is the last byte out? */ | |
&& di_card->cntl_register & CNTL_EOI) /* and is EOI control enabled? */ | |
di_card->bus_cntl |= BUS_EOI; /* assert EOI on the bus */ | |
else | |
di_card->bus_cntl &= ~BUS_EOI; /* deny EOI on the bus */ | |
else { /* the card is in the unpacked mode */ | |
if (di_card->cntl_register & CNTL_CIC) /* is the card the controller in charge? */ | |
di_card->bus_cntl = /* assert or deny the ATN bus line */ | |
di_card->bus_cntl & ~BUS_ATN /* from the ATN tag value */ | |
| (uint8) ((tag & TAG_ATN) >> BUS_SHIFT); | |
di_card->bus_cntl = /* assert or deny the EOI bus line */ | |
di_card->bus_cntl & ~BUS_EOI /* from the EOI tag value */ | |
| (uint8) ((tag & TAG_EOI) >> BUS_SHIFT); | |
} | |
return (uint16) data; /* return the data value */ | |
} |