1927 lines
93 KiB
C
1927 lines
93 KiB
C
/* hp2100_di.c: HP 12821A HP-IB Disc Interface simulator
|
|
|
|
Copyright (c) 2010-2012, 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
|
|
|
|
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,
|
|
Feb-1985)
|
|
- IEEE Standard Digital Interface for Programmable Instrumentation
|
|
(IEEE-488A-1980, Sep-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 and 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 a 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; the 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 files for the tape and disc devices contain the device
|
|
simulations and the device portions of the HP-IB simulations.
|
|
|
|
This simulation 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 install an
|
|
Amigo disc and an Amigo tape on 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 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, 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 */
|
|
#define STAT_ATNBUS 0000400 /* attention */
|
|
#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)
|
|
|
|
|
|
/* 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 global VM routines */
|
|
|
|
IOHANDLER di_io;
|
|
t_stat di_reset (DEVICE *dptr);
|
|
|
|
/* Disc interface global SCP routines */
|
|
|
|
t_stat di_set_address (UNIT *uptr, int32 value, char *cptr, void *desc);
|
|
t_stat di_show_address (FILE *st, UNIT *uptr, int32 value, void *desc);
|
|
t_stat di_set_cable (UNIT *uptr, int32 value, char *cptr, void *desc);
|
|
t_stat di_show_cable (FILE *st, UNIT *uptr, int32 value, void *desc);
|
|
|
|
/* Disc interface global bus routines */
|
|
|
|
t_bool di_bus_source (CARD_ID card, uint8 data);
|
|
void di_bus_control (CARD_ID card, uint32 unit, uint8 assert, uint8 deny);
|
|
void di_poll_response (CARD_ID card, uint32 unit, FLIP_FLOP response);
|
|
|
|
/* 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);
|
|
static void fprint_bus (FILE *file, char *format, uint8 cntl);
|
|
|
|
|
|
|
|
/* Dummy DC device.
|
|
|
|
This 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 [] = {
|
|
{ "CPU", DEB_CPU },
|
|
{ "CMDS", DEB_CMDS },
|
|
{ "BUF", DEB_BUF },
|
|
{ "XFER", DEB_XFER },
|
|
{ "RWSC", DEB_RWSC },
|
|
{ "SERV", DEB_SERV },
|
|
{ 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 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 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, the FIFO is
|
|
loaded with 177777 due to the floating bus. If L is asserted and T is
|
|
not, 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 AND/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 card state */
|
|
|
|
IOSIGNAL signal;
|
|
IOCYCLE working_set = IOADDSIR (signal_set); /* add ioSIR if needed */
|
|
|
|
while (working_set) {
|
|
signal = IONEXT (working_set); /* isolate next signal */
|
|
|
|
switch (signal) { /* dispatch I/O signal */
|
|
|
|
case ioCLF: /* clear flag flip-flop */
|
|
di_card->flag = CLEAR; /* clear flag */
|
|
di_card->flagbuf = CLEAR; /* and flag buffer */
|
|
|
|
if (DEBUG_PRJ (dptrs [card], DEB_CMDS))
|
|
fprintf (sim_deb, ">>%s cmds: [CLF] Flag cleared\n",
|
|
dptrs [card]->name);
|
|
|
|
if (update_required) /* if card state has changed */
|
|
update_state (card); /* then update the state */
|
|
break;
|
|
|
|
|
|
case ioSTF: /* set flag flip-flop */
|
|
if (DEBUG_PRJ (dptrs [card], DEB_CMDS))
|
|
fprintf (sim_deb, ">>%s cmds: [STF] Flag set\n",
|
|
dptrs [card]->name);
|
|
|
|
/* fall into ENF handler */
|
|
|
|
case ioENF: /* enable flag */
|
|
di_card->flag = SET; /* set 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) { /* control set = data mode? */
|
|
data = di_card->input_data_register; /* read input data register */
|
|
di_card->status_register &= ~STAT_IRL; /* clear input register loaded status */
|
|
|
|
if (FIFO_EMPTY && di_card->eor == CLEAR) { /* FIFO empty and end of record not seen? */
|
|
if (di_card->srq == SET && DEBUG_PRJ (dptrs [card], DEB_CMDS))
|
|
fprintf (sim_deb, ">>%s cmds: SRQ cleared\n",
|
|
dptrs [card]->name);
|
|
|
|
di_card->srq = CLEAR; /* clear SRQ */
|
|
update_required = FALSE; /* card state does not change */
|
|
}
|
|
}
|
|
|
|
else { /* control clear = 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 set, card is system controller */
|
|
di_card->status_register |= STAT_SYSCTL;
|
|
|
|
if (di_card->ibp == lower) /* if lower byte input is next */
|
|
di_card->status_register |= STAT_ODD; /* then last transfer was odd */
|
|
|
|
di_card->status_register |= /* set bus status bits */
|
|
(di_card->bus_cntl /* from 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 */
|
|
}
|
|
|
|
if (DEBUG_PRJ (dptrs [card], DEB_CPU))
|
|
fprintf (sim_deb, ">>%s cpu: [LIx%s] %s = %06o\n",
|
|
dptrs [card]->name, hold_or_clear,
|
|
input_state [di_card->control], data);
|
|
|
|
if (update_required && !(signal_set & ioCLF)) /* if update and CLF not present, */
|
|
update_state (card); /* update state, else ioCLF will update */
|
|
|
|
stat_data = IORETURN (SCPE_OK, data); /* merge in return status */
|
|
break;
|
|
|
|
|
|
case ioIOO: /* I/O data output */
|
|
data = IODATA (stat_data); /* get the data value */
|
|
|
|
if (DEBUG_PRJ (dptrs [card], DEB_CPU))
|
|
fprintf (sim_deb, ">>%s cpu: [OTx%s] %s = %06o\n",
|
|
dptrs [card]->name, hold_or_clear,
|
|
output_state [di_card->control], data);
|
|
|
|
if (di_card->control == SET) { /* control set = data mode */
|
|
if (signal_set & ioEDT) /* if end of DCPC transfer */
|
|
di_card->edt = SET; /* set EDT flip-flop */
|
|
|
|
else if (di_card->cntl_register & CNTL_PACK) { /* packed transfer? */
|
|
if (!(signal_set & ioCLF)) /* and CLF not given? */
|
|
di_card->edt = SET; /* set EDT flip-flop */
|
|
}
|
|
|
|
else /* unpacked transfer */
|
|
if (data & DATA_LBO) /* and last byte out? */
|
|
di_card->edt = SET; /* set EDT flip-flop */
|
|
|
|
if (di_card->cntl_register & CNTL_DIAG) { /* DIAG loopback? */
|
|
data = fifo_unload (card, diag_access); /* unload data from FIFO */
|
|
fifo_load (card, data, diag_access); /* and load back in */
|
|
}
|
|
|
|
else { /* normal mode */
|
|
fifo_load (card, data, cpu_access); /* load data word into FIFO */
|
|
|
|
if (FIFO_FULL && (di_card->bus_cntl & BUS_NRFD)) { /* FIFO full and listener not ready? */
|
|
if (di_card->srq == SET && DEBUG_PRJ (dptrs [card], DEB_CMDS))
|
|
fprintf (sim_deb, ">>%s cmds: SRQ cleared\n",
|
|
dptrs [card]->name);
|
|
|
|
di_card->srq = CLEAR; /* clear SRQ */
|
|
update_required = FALSE; /* card state does not change */
|
|
}
|
|
}
|
|
}
|
|
|
|
else { /* control clear = write control word */
|
|
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 card is CIC */
|
|
assert = BUS_PPOLL; /* then conduct 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 card is CIC */
|
|
== (CNTL_PACK | CNTL_CIC | CNTL_ATN)) /* then ATN control output */
|
|
assert = BUS_ATN; /* is coupled to the bus */
|
|
|
|
else /* if none of the above */
|
|
deny = BUS_ATN; /* ATN is not driven */
|
|
}
|
|
|
|
else /* not talking */
|
|
deny = BUS_ATN | BUS_EOI; /* so ATN and EOI are disabled */
|
|
|
|
|
|
if (data & CNTL_NRFD) /* card not ready set explicitly? */
|
|
assert |= BUS_NRFD; /* assert NRFD on bus */
|
|
|
|
else if (di_card->cntl_register & CNTL_NRFD) /* NRFD was set but is not now? */
|
|
deny |= BUS_NRFD; /* deny NRFD on bus */
|
|
|
|
if (FIFO_FULL) /* is FIFO full? */
|
|
if (data & CNTL_LSTN) /* is card now listening? */
|
|
assert |= BUS_NRFD; /* listener and full FIFO asserts NRFD */
|
|
|
|
else if (di_card->cntl_register & CNTL_LSTN) /* was listener but is not now? */
|
|
deny |= BUS_NRFD; /* deny NRFD on 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) { /* IFC set? */
|
|
assert |= BUS_IFC; /* assert IFC on 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 IFC timer by calculating */
|
|
sim_gtime () + IFC_TIMEOUT; /* IFC stop time (100 microseconds) */
|
|
}
|
|
}
|
|
|
|
if ((data & (CNTL_SRQ | CNTL_CIC)) == CNTL_SRQ) /* service request and not controller? */
|
|
assert |= BUS_SRQ; /* assert SRQ on bus */
|
|
else /* else */
|
|
deny |= BUS_SRQ; /* deny SRQ on bus */
|
|
|
|
di_card->cntl_register = data; /* save control word */
|
|
di_bus_control (card, CONTROLLER, assert, deny); /* update bus control state */
|
|
}
|
|
|
|
if (update_required && !(signal_set & ioCLF)) /* if update and CLF not present, */
|
|
update_state (card); /* update state, else ioCLF will update */
|
|
break;
|
|
|
|
|
|
case ioPOPIO: /* power-on preset to I/O */
|
|
di_card->flag = SET; /* set flag */
|
|
di_card->flagbuf = SET; /* and flag buffer */
|
|
|
|
if (DEBUG_PRJ (dptrs [card], DEB_CMDS))
|
|
fprintf (sim_deb, ">>%s cmds: [POPIO] Flag set\n",
|
|
dptrs [card]->name);
|
|
break;
|
|
|
|
|
|
case ioCRS: /* control reset */
|
|
if (DEBUG_PRJ (dptrs [card], DEB_CMDS))
|
|
fprintf (sim_deb, ">>%s cmds: [CRS] Master reset\n",
|
|
dptrs [card]->name);
|
|
|
|
di_card->status_register &= /* clear listen and talk status */
|
|
~(STAT_LSTN | STAT_TALK);
|
|
|
|
deny = BUS_SRQ | BUS_REN | BUS_ATN | BUS_EOI; /* clear lines driven by control register */
|
|
|
|
if (di_card->cntl_register & (CNTL_NRFD | CNTL_LSTN)) /* if asserting NRFD or listening */
|
|
deny |= BUS_NRFD; /* deny because we're clearing */
|
|
|
|
di_card->cntl_register = 0; /* clear control word register */
|
|
di_card->control = CLEAR; /* clear control */
|
|
di_card->srq = CLEAR; /* clear srq */
|
|
|
|
master_reset (card); /* master reset */
|
|
|
|
di_bus_control (card, CONTROLLER, 0, deny); /* update bus control state */
|
|
update_state (card); /* update card state */
|
|
break;
|
|
|
|
|
|
case ioCLC: /* clear control flip-flop */
|
|
di_card->control = CLEAR; /* clear control */
|
|
|
|
if (DEBUG_PRJ (dptrs [card], DEB_CMDS)) {
|
|
fprintf (sim_deb, ">>%s cmds: [CLC%s] Control cleared (configure mode)",
|
|
dptrs [card]->name, hold_or_clear);
|
|
|
|
if (signal_set & ioCLF) /* if ioCLF given, */
|
|
fputs (", master reset\n", sim_deb); /* then report master reset */
|
|
else
|
|
fputc ('\n', sim_deb);
|
|
}
|
|
|
|
if (signal_set & ioCLF) /* if ioCLF given, */
|
|
master_reset (card); /* then do master reset */
|
|
break; /* (ioCLF will call update_state for us) */
|
|
|
|
|
|
case ioSTC: /* set control flip-flop */
|
|
di_card->control = SET; /* set control */
|
|
|
|
if (DEBUG_PRJ (dptrs [card], DEB_CMDS))
|
|
fprintf (sim_deb, ">>%s cmds: [STC%s] Control set (data mode)\n",
|
|
dptrs [card]->name, hold_or_clear);
|
|
break;
|
|
|
|
|
|
case ioEDT: /* end data transfer */
|
|
if (DEBUG_PRJ (dptrs [card], DEB_CPU))
|
|
fprintf (sim_deb, ">>%s cpu: [EDT] DCPC transfer ended\n",
|
|
dptrs [card]->name);
|
|
break;
|
|
|
|
|
|
case ioSIR: /* set interrupt request */
|
|
setstdPRL (di [card]); /* set standard PRL signal */
|
|
setstdIRQ (di [card]); /* set standard IRQ signal */
|
|
|
|
setSRQ (dibptr->select_code, /* set SRQ signal from control and SRQ */
|
|
di_card->srq == SET && di_card->control == SET);
|
|
break;
|
|
|
|
|
|
case ioIAK: /* interrupt acknowledge */
|
|
di_card->flagbuf = CLEAR; /* clear flag buffer */
|
|
break;
|
|
|
|
|
|
default: /* all other signals */
|
|
break; /* are ignored */
|
|
}
|
|
|
|
working_set = working_set & ~signal; /* remove current signal from 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
|
|
asserts 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 the 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; /* DIB pointer */
|
|
const CARD_ID card = (CARD_ID) (dibptr->card_index); /* card number */
|
|
|
|
if (sim_switches & SWMASK ('P')) { /* power-on reset? */
|
|
di [card].fifo_reg = find_reg ("FIFO", NULL, dptr); /* find the FIFO register entry */
|
|
|
|
if (di [card].fifo_reg == NULL) /* not there? */
|
|
return SCPE_IERR; /* is a programming error! */
|
|
else /* found it */
|
|
di [card].fifo_reg->qptr = 0; /* 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 map of listeners */
|
|
di [card].talker = 0; /* clear map of talker */
|
|
di [card].poll_response = 0; /* clear map of parallel poll responses */
|
|
|
|
di [card].ifc_timer = 0.0; /* clear 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 method 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 does 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, 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 address not given */
|
|
return SCPE_ARG; /* report a missing argument */
|
|
|
|
new_address = get_uint (cptr, 10, 7, &status); /* parse the address value */
|
|
|
|
if (status == SCPE_OK) { /* parse OK? */
|
|
if (value) /* setting the card address? */
|
|
dptr->flags = dptr->flags & ~DEV_BUSADR /* store new address in the device flags */
|
|
| SET_DIADR (new_address);
|
|
|
|
else { /* setting a unit address */
|
|
for (index = 0; index < dptr->numunits; index++) /* look through units */
|
|
if (new_address != old_address /* to ensure 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 /* address is valid; change */
|
|
| SET_BUSADR (new_address); /* the 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, void *desc)
|
|
{
|
|
DEVICE *dptr = (DEVICE *) desc;
|
|
|
|
if (value) /* card address? */
|
|
fprintf (st, "address=%d", GET_DIADR (dptr->flags)); /* get from device flags */
|
|
else /* unit address */
|
|
fprintf (st, "bus=%d", GET_BUSADR (uptr->flags)); /* get from 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, char *cptr, void *desc)
|
|
{
|
|
if (value) { /* diagnostic cable selected? */
|
|
((DEVICE *) desc)->flags |= DEV_DIAG; /* set diagnostic flag */
|
|
dc_dev.flags &= ~DEV_DIS; /* enable dummy device */
|
|
dc_dev.flags |= DEV_DIAG; /* and set its flag as well */
|
|
}
|
|
else { /* peripheral cable selected */
|
|
((DEVICE *) desc)->flags &= ~DEV_DIAG; /* clear diagnostic flag */
|
|
dc_dev.flags |= DEV_DIS; /* disable 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, void *desc)
|
|
{
|
|
if (((DEVICE *) desc)->flags & DEV_DIAG) /* is cable connected for diagnostics? */
|
|
fputs ("diagnostic cable", st); /* report it */
|
|
else /* cable is connected for normal 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 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 set as required in the control word
|
|
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 set in the control word, the byte is a
|
|
command; otherwise, it is data. If EOI is set 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
|
|
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.
|
|
|
|
An indication is returned showing 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;
|
|
|
|
if (DEBUG_PRJ (dptrs [card], DEB_XFER)) {
|
|
fprintf (sim_deb, ">>%s xfer: HP-IB DIO %03o available ", dptrs [card]->name, data);
|
|
fprint_bus (sim_deb, "[%s]\n", di [card].bus_cntl);
|
|
}
|
|
|
|
if (dptrs [card]->flags & DEV_DIAG) /* diagnostic run? */
|
|
for (other = first_card; other <= last_card; other++) { /* look through list of cards */
|
|
if (other != card && dptrs [other] /* for the other card */
|
|
&& (dptrs [other]->flags & DEV_DIAG) /* that is configured for diagnostic */
|
|
&& (di [other].cntl_register & CNTL_LSTN)) /* and is listening */
|
|
accepted = di_bus_accept (other, data); /* call interface acceptor for other card */
|
|
}
|
|
|
|
else if ((di [card].bus_cntl & BUS_PPOLL) != BUS_PPOLL) { /* normal run; not a fake poll? */
|
|
if (di [card].cntl_register & CNTL_LSTN) /* is card a listener? */
|
|
accepted = di_bus_accept (card, data); /* call interface acceptor for this card */
|
|
|
|
acceptors = di [card].acceptors; /* get 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 units */
|
|
if (acceptors & 1) /* is current unit accepting? */
|
|
accepted |= (*bus_accept [card]) (unit, data); /* call acceptor for this card */
|
|
|
|
acceptors = acceptors >> 1; /* move to next acceptor */
|
|
}
|
|
}
|
|
|
|
if (DEBUG_PRJ (dptrs [card], DEB_XFER) && !accepted)
|
|
fprintf (sim_deb, ">>%s xfer: HP-IB no acceptors\n",
|
|
dptrs [card]->name);
|
|
|
|
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 new control state */
|
|
|
|
if (new_state == di [card].bus_cntl) /* if control state did not change */
|
|
return; /* return now */
|
|
|
|
new_assertions = ~di [card].bus_cntl & assert; /* get changing assertions */
|
|
new_denials = di [card].bus_cntl & deny; /* get changing denials */
|
|
|
|
di [card].bus_cntl = new_state; /* establish the new control state */
|
|
|
|
if (DEBUG_PRJ (dptrs [card], DEB_XFER)) {
|
|
if (unit == CONTROLLER)
|
|
fprintf (sim_deb, ">>%s xfer: HP-IB card %d", dptrs [card]->name, card);
|
|
else
|
|
fprintf (sim_deb, ">>%s xfer: HP-IB address %d",
|
|
dptrs [card]->name, GET_BUSADR (dptrs [card]->units [unit].flags));
|
|
|
|
if (new_assertions)
|
|
fprint_bus (sim_deb, " asserted [%s]", new_assertions);
|
|
|
|
if (new_denials)
|
|
fprint_bus (sim_deb, " denied [%s]", new_denials);
|
|
|
|
fprint_bus (sim_deb, ", bus is [%s]\n", new_state);
|
|
}
|
|
|
|
if ((dptrs [card]->flags & DEV_DIAG) /* diagnostic mode? */
|
|
|| (new_assertions & ASSERT_SET) /* or changed signals in the */
|
|
|| (new_denials & DENY_SET)) { /* set that must be broadcast? */
|
|
responded = FALSE; /* assume no response */
|
|
|
|
if (dptrs [card]->flags & DEV_DIAG) { /* diagnostic run? */
|
|
for (other = first_card; other <= last_card; other++) /* look through 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 other card of new control state */
|
|
responded = TRUE; /* and note that there was a responder */
|
|
}
|
|
}
|
|
|
|
else { /* normal run */
|
|
update_state (card); /* update card for new control state */
|
|
|
|
acceptors = di [card].acceptors; /* get map of acceptors */
|
|
responded = (acceptors != 0); /* set if there are any acceptors */
|
|
|
|
for (responder = 0; acceptors; responder++) { /* loop through units */
|
|
if ((acceptors & 1) && responder != unit) /* is current unit accepting? */
|
|
(*bus_respond [card]) (card, responder, new_state); /* call responder for this card */
|
|
|
|
acceptors = acceptors >> 1; /* move to next acceptor */
|
|
}
|
|
}
|
|
|
|
if (DEBUG_PRJ (dptrs [card], DEB_XFER) & !responded)
|
|
fprintf (sim_deb, ">>%s xfer: HP-IB no responders\n",
|
|
dptrs [card]->name);
|
|
}
|
|
|
|
if ((new_state & BUS_PPOLL) == BUS_PPOLL) /* 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 PPR? */
|
|
di [card].poll_response |= PPR (address); /* set response bit */
|
|
|
|
if ((di [card].bus_cntl & BUS_PPOLL) == BUS_PPOLL) /* is parallel poll in progress? */
|
|
di_bus_poll (card); /* conduct with new response */
|
|
}
|
|
else /* disable PPR */
|
|
di [card].poll_response &= ~PPR (address); /* clear response bit */
|
|
|
|
if (DEBUG_PRJ (dptrs [card], DEB_XFER)
|
|
&& previous_response != di [card].poll_response)
|
|
fprintf (sim_deb, ">>%s xfer: HP-IB address %d parallel poll response %s\n",
|
|
dptrs [card]->name, 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 one of the data lines 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 directs 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) /* card poll response enabled? */
|
|
response = di [card].poll_response /* add card's response */
|
|
| PPR (GET_DIADR (dptrs [card]->flags)); /* to the devices' responses */
|
|
else
|
|
response = di [card].poll_response; /* card disabled, so just use devices */
|
|
|
|
if (dptrs [card]->flags & DEV_DIAG) /* diagnostic run? */
|
|
for (other = first_card; other <= last_card; other++) /* look through list of cards */
|
|
if (other != card && dptrs [other] /* for another card */
|
|
&& (dptrs [other]->flags & DEV_DIAG) /* that is configured for 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) { /* poll response indicated? */
|
|
if (DEBUG_PRJ (dptrs [card], DEB_XFER))
|
|
fprintf (sim_deb, ">>%s xfer: HP-IB parallel poll DIO %03o\n",
|
|
dptrs [card]->name, response);
|
|
|
|
while (di [card].fifo_count != FIFO_SIZE) /* fill card FIFO with responses */
|
|
fifo_load (card, (uint16) response, diag_access); /* (hardware feature) */
|
|
|
|
update_state (card); /* update 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 if 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)
|
|
{
|
|
if (DEBUG_PRJ (dptrs [card], DEB_XFER))
|
|
fprintf (sim_deb, ">>%s xfer: HP-IB card %d accepted data %03o \n",
|
|
dptrs [card]->name, card, data);
|
|
|
|
fifo_load (card, data, bus_access); /* load data byte into the FIFO */
|
|
update_state (card); /* and update the card state */
|
|
return TRUE; /* indicate 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 bus control */
|
|
update_state (card); /* update 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 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, 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 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 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 */
|
|
|
|
if (DEBUG_PRJ (dptrs [card], DEB_BUF))
|
|
fprintf (sim_deb, ">>%s buf: FIFO cleared\n",
|
|
dptrs [card]->name);
|
|
|
|
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 card a listener? */
|
|
if (!(di_card->status_register & STAT_IRL) /* is IDR empty? */
|
|
&& ! FIFO_EMPTY) { /* and more in FIFO? */
|
|
data = fifo_unload (card, cpu_access); /* unload FIFO */
|
|
di_card->input_data_register = data; /* into IDR */
|
|
di_card->status_register |= STAT_IRL; /* set input register loaded status */
|
|
}
|
|
}
|
|
|
|
else if ((di_card->cntl_register /* is 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 denied? */
|
|
&& !(di_card->status_register & STAT_LBO)) { /* and last byte not sent? */
|
|
data = fifo_unload (card, bus_access); /* unload FIFO byte */
|
|
di_bus_source (card, (uint8) data); /* source it to bus */
|
|
}
|
|
|
|
|
|
if (di_card->bus_cntl & BUS_IFC /* IFC in progress? */
|
|
&& di_card->ifc_timer != 0.0 /* and I am timing? */
|
|
&& sim_gtime () > di_card->ifc_timer) { /* and timeout has elapsed? */
|
|
deny = BUS_IFC; /* deny IFC on bus */
|
|
di_card->ifc_timer = 0.0; /* clear IFC timer */
|
|
di_card->status_register &= ~STAT_IFC; /* clear IFC status */
|
|
}
|
|
|
|
|
|
if (di_card->cntl_register & CNTL_LSTN) /* is card a listener? */
|
|
if (di_card->cntl_register & CNTL_NRFD /* if explicitly requested */
|
|
|| di_card->status_register & STAT_LBI /* or last byte is in */
|
|
|| FIFO_FULL) /* or FIFO is full */
|
|
assert = BUS_NRFD; /* then assert NRFD */
|
|
else /* otherwise card is ready for data */
|
|
deny |= BUS_NRFD; /* so deny NRFD */
|
|
|
|
if (assert != deny) /* any change in bus state? */
|
|
di_bus_control (card, CONTROLLER, assert, deny); /* update bus control */
|
|
|
|
|
|
previous_state = di_card->srq; /* save current SRQ state */
|
|
|
|
if (di_card->cntl_register & CNTL_LSTN /* if card is a listener */
|
|
&& di_card->status_register & STAT_IRL /* and input register is loaded, */
|
|
|| di_card->cntl_register & CNTL_TALK /* or card is a talker */
|
|
&& ! FIFO_FULL) /* and FIFO is not full */
|
|
di_card->srq = SET; /* then request a DCPC cycle */
|
|
else
|
|
di_card->srq = CLEAR; /* else DCPC service is not needed */
|
|
|
|
|
|
if (DEBUG_PRJ (dptrs [card], DEB_CMDS)
|
|
&& di_card->srq != previous_state)
|
|
fprintf (sim_deb, ">>%s cmds: SRQ %s\n",
|
|
dptrs [card]->name, di_card->srq == SET ? "set" : "cleared");
|
|
|
|
|
|
if (di_card->status_register & STAT_IRL /* input register loaded */
|
|
&& di_card->cntl_register & CNTL_IRL /* and notification wanted? */
|
|
|| di_card->status_register & STAT_LBO /* or last byte out */
|
|
&& di_card->cntl_register & CNTL_LBO /* and notification wanted? */
|
|
|| di_card->eor == SET /* or end of record seen */
|
|
&& !(di_card->status_register & STAT_IRL) /* and input register unloaded? */
|
|
|| di_card->bus_cntl & BUS_SRQ /* or SRQ is asserted on the bus */
|
|
&& di_card->cntl_register & CNTL_SRQ /* and notification wanted */
|
|
&& di_card->cntl_register & CNTL_CIC /* and card is not controller? */
|
|
|| !SW8_SYSCTL /* or card is not system controller */
|
|
&& di_card->bus_cntl & BUS_REN /* and REN is asserted on the bus */
|
|
&& di_card->cntl_register & CNTL_REN /* and notification wanted? */
|
|
|| !SW8_SYSCTL /* or card is not system controller */
|
|
&& di_card->status_register & STAT_IFC /* and IFC is asserted on the bus */
|
|
&& di_card->cntl_register & CNTL_IFC) { /* and notification wanted? */
|
|
|
|
if (DEBUG_PRJ (dptrs [card], DEB_CMDS))
|
|
fprintf (sim_deb, ">>%s cmds: Flag set\n",
|
|
dptrs [card]->name);
|
|
|
|
di_io (dibptr, ioENF, 0); /* set 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? */
|
|
if (DEBUG_PRJ (dptrs [card], DEB_BUF))
|
|
fprintf (sim_deb, ">>%s buf: Attempted load to full FIFO, data %0*o\n",
|
|
dptrs [card]->name, (access == bus_access ? 3 : 6), data);
|
|
|
|
return; /* return with load ignored */
|
|
}
|
|
|
|
if (di_card->cntl_register & CNTL_LSTN) { /* is card receiving? */
|
|
tag = (di_card->bus_cntl /* set tag from bus signals */
|
|
& (BUS_ATN | BUS_EOI)) << BUS_SHIFT; /* shifted to tag locations */
|
|
|
|
if ((di_card->cntl_register & CNTL_EOI /* EOI detection enabled, */
|
|
&& di_card->bus_cntl & BUS_EOI) /* and data was tagged with EOI? */
|
|
|| (di_card->cntl_register & CNTL_LF /* or LF detection enabled, */
|
|
&& GET_LOWER (data) == LF)) { /* and byte is a line feed? */
|
|
tag = tag | TAG_LBR; /* tag as last byte received */
|
|
di_card->status_register |= STAT_LBI; /* set last byte in status */
|
|
}
|
|
else /* neither termination condition seen */
|
|
di_card->status_register &= ~STAT_LBI; /* so clear last byte in status */
|
|
}
|
|
|
|
else /* card is transmitting */
|
|
tag = (data & (DATA_ATN | DATA_EOI)) << DATA_SHIFT; /* set tag from data shifted to tag location */
|
|
|
|
if (di_card->edt == SET) /* end of data transfer? */
|
|
tag = tag | TAG_EDT; /* set EDT tag */
|
|
|
|
|
|
index = (di_card->fifo_reg->qptr /* calculate index */
|
|
+ di_card->fifo_count) % FIFO_SIZE; /* of next available location */
|
|
|
|
if (access == bus_access) { /* bus access? */
|
|
if (di_card->ibp == upper) { /* packed and this is the upper byte? */
|
|
di_card->ibp = lower; /* set lower byte as next */
|
|
|
|
if (tag & TAG_LBR) /* is this the last byte? */
|
|
di_card->fifo [index] = /* copy to both bytes of FIFO */
|
|
tag | SET_BOTH (data); /* and store with tag */
|
|
else { /* more bytes expected */
|
|
di_card->fifo [index] = /* so position this byte */
|
|
tag | SET_UPPER (data); /* and store with tag */
|
|
add_word = FALSE; /* wait for second byte before adding */
|
|
}
|
|
}
|
|
|
|
else /* this is the lower byte */
|
|
if (di_card->cntl_register & CNTL_PACK) { /* packed mode? */
|
|
di_card->ibp = upper; /* set upper byte as next */
|
|
|
|
di_card->fifo [index] = /* merge in data and tag */
|
|
tag | di_card->fifo [index] | SET_LOWER (data);
|
|
}
|
|
else /* unpacked mode */
|
|
di_card->fifo [index] = /* position this byte */
|
|
tag | SET_LOWER (data); /* and store with tag */
|
|
}
|
|
|
|
else if (access == cpu_access) /* cpu access? */
|
|
di_card->fifo [index] = tag | data; /* store tag and full word in FIFO */
|
|
|
|
else { /* diag access */
|
|
data = SET_BOTH (GET_LOWER (data)); /* copy lower byte to upper byte */
|
|
di_card->fifo [index] = tag | data; /* and store tag and full word in FIFO */
|
|
}
|
|
|
|
if (add_word) /* did we add a word to the FIFO? */
|
|
di_card->fifo_count = di_card->fifo_count + 1; /* increment count of words stored */
|
|
|
|
if (DEBUG_PRJ (dptrs [card], DEB_BUF)) {
|
|
fprintf (sim_deb, ">>%s buf: Data %0*o tag ",
|
|
dptrs [card]->name, (access == bus_access ? 3 : 6), data);
|
|
fprint_val (sim_deb, tag >> BUS_SHIFT, 2, 4, PV_RZRO);
|
|
fprintf (sim_deb, " loaded into FIFO (%d)\n", 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 a bit 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? */
|
|
if (DEBUG_PRJ (dptrs [card], DEB_BUF))
|
|
fprintf (sim_deb, ">>%s buf: Attempted unload from empty FIFO\n",
|
|
dptrs [card]->name);
|
|
|
|
return 0; /* return with no data */
|
|
}
|
|
|
|
data = di_card->fifo [di_card->fifo_reg->qptr]; /* get 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) { /* cpu access? */
|
|
if (!(di_card->cntl_register & CNTL_PACK)) /* unpacked data format? */
|
|
data = data & ~(DATA_ATN | DATA_EOI) /* substitute 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) /* bus access? */
|
|
if (di_card->obp == upper) { /* is this the upper byte? */
|
|
di_card->obp = lower; /* set the lower byte as next */
|
|
data = GET_UPPER (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 = GET_LOWER (data); /* mask and position it in the data word */
|
|
|
|
if (di_card->cntl_register & CNTL_PACK) /* in packed mode? */
|
|
di_card->obp = upper; /* set the upper byte as next */
|
|
}
|
|
|
|
else /* diagnostic access */
|
|
data = GET_LOWER (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 */
|
|
}
|
|
|
|
|
|
if (DEBUG_PRJ (dptrs [card], DEB_BUF)) {
|
|
fprintf (sim_deb, ">>%s buf: Data %0*o tag ",
|
|
dptrs [card]->name, (access == cpu_access ? 6 : 3), data);
|
|
fprint_val (sim_deb, tag >> BUS_SHIFT, 2, 4, PV_RZRO);
|
|
fprintf (sim_deb, " unloaded from FIFO (%d)\n", di_card->fifo_count);
|
|
}
|
|
|
|
|
|
if (di_card->cntl_register & CNTL_TALK) /* is the card talking? */
|
|
if (di_card->cntl_register & CNTL_PACK) /* in packed mode? */
|
|
if (di_card->status_register & STAT_LBO /* yes, is the last byte out? */
|
|
&& di_card->cntl_register & CNTL_EOI) /* and EOI control enabled? */
|
|
di_card->bus_cntl |= BUS_EOI; /* assert EOI */
|
|
else
|
|
di_card->bus_cntl &= ~BUS_EOI; /* deny EOI */
|
|
|
|
else { /* in unpacked mode */
|
|
if (di_card->cntl_register & CNTL_CIC) /* is 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 */
|
|
| (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 */
|
|
| (tag & TAG_EOI) >> BUS_SHIFT;
|
|
}
|
|
|
|
return (uint16) data; /* return the data value */
|
|
}
|
|
|
|
|
|
/* Print the bus state for debugging.
|
|
|
|
The states of the supplied bus control lines are decoded and printed in
|
|
mnemonic form to the specified file using the indicated format string. An
|
|
asserted bus signal is indicated by its name; a denied signal is omitted.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. The strings in the cntl_names array must appear in BUS_xxx order. The
|
|
first element corresponds to bus bit 0, etc.
|
|
*/
|
|
|
|
static void fprint_bus (FILE *file, char *format, uint8 cntl)
|
|
{
|
|
static const char *cntl_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 */
|
|
};
|
|
|
|
uint32 signal;
|
|
char mnemonics [40];
|
|
|
|
if (cntl == 0) /* any control signal asserted? */
|
|
strcpy (mnemonics, "---"); /* no; use dashes in lieu of an empty string */
|
|
|
|
else { /* one or more signals are asserted */
|
|
mnemonics [0] = '\0';
|
|
|
|
for (signal = 0; signal <= 7; signal++) /* loop though the set of signals */
|
|
if (cntl & (1 << signal)) { /* is this signal asserted? */
|
|
if (strlen (mnemonics) > 0) /* yes; is it the first one asserted? */
|
|
strcat (mnemonics, " "); /* no, so append a space to separate */
|
|
strcat (mnemonics, cntl_names [signal]); /* append the name of the asserted signal */
|
|
}
|
|
}
|
|
|
|
fprintf (file, format, mnemonics); /* print the bus state */
|
|
return;
|
|
}
|