simh-testsetgenerator/HP2100/hp2100_dma.c
2020-02-16 22:25:15 -08:00

1063 lines
53 KiB
C

/* hp2100_dma.c: HP 21xx/1000 Direct Memory Access/Dual-Channel Port Controller simulator
Copyright (c) 1993-2016, Robert M. Supnik
Copyright (c) 2017-2018, 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
AUTHORS 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 names of the authors 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 authors.
DMA1,DMA2 12607B/12578A/12895A Direct Memory Access
DCPC1,DCPC2 12897B Dual Channel Port Controller
20-Jul-18 JDB Split out from hp2100_cpu.c
References:
- HP 1000 M/E/F-Series Computers Engineering and Reference Documentation
(92851-90001, March 1981)
- 12607A Direct Memory Access Operating and Service Manual
(12607-90002, January 1970)
- 12578A/12578A-01 Direct Memory Access Operating and Service Manual
(12578-9001, March 1972)
This module simulates the 12578A/12607B/12895A Direct Memory Access and
12897B Dual-Channel Port Controller devices (hereafter, "DMA"). These
controllers permit the CPU to transfer data directly between an I/O device
and memory on a cycle-stealing basis. Depending on the CPU, the device
interface, and main memory speed, DMA is capable of transferring data blocks
from 1 to 32,768 words in length at rates between 500,000 and 1,000,000 words
per second. The 2114 supports a single DMA channel. All other CPUs support
two DMA channels.
DMA is configured for transfers by setting control words via two select
codes: 2 and 6 for channel 1, and 3 and 7 for channel 2. During simultaneous
transfers, channel 1 has priority over channel 2. Otherwise, the channels
are identical. Channel programming involves setting three control words, as
follows.
SC 06/07 Control Word 1 format (OTA and OTB):
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| S | B | C | - - - - - - - | device select code |
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Where:
S = assert STC during each cycle
B = enable byte packing and unpacking (12578A only)
C = assert CLC at the end of the block transfer
The 12607B (2114) stores only bits 2-0 of the device select code, which are
decoded to assert SCL0-SCL6 on the I/O backplane. Along with unconditional
assertion of SCM1, the 12607B can control interfaces assigned to select codes
10-16, corresponding to the seven I/O slots available in the 2114 chassis.
SC 02/03 Control Words 2 and 3 format (OTA and OTB):
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| D | starting memory address | word 2
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| negative word count | word 3
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Where:
D = transfer direction is out of/into memory (0/1)
Control word 2 is stored if the control flip-flop of select code 2 is clear,
i.e., if the OTA/B is preceded by CLC; control word 3 is stored if the
flip-flop is set by a preceding STC.
The 12607B supports 14-bit addresses and 13-bit word counts. The 12578A
supports 15-bit addresses and 14-bit word counts. The 12895A and 12897B
support 15-bit addresses and 16-bit word counts.
DMA is started by setting the control flip-flop on select code 6. DMA
completion is indicated when the flag flip-flop sets on select code 8, which
causes an interrupt if enabled. Clearing the control flip-flop does not stop
DMA; instead, it inhibits the DMA completion interrupt. DMA is aborted by
setting the flag on select code 6. DMA activity may be checked by testing
the flag on select code 6.
The remaining word count may be obtained at any time by reading from select
code 2, as follows.
SC 02/03 Word Count 2 and 3 format (LIA, LIB, MIA, and MIB):
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 0 | negative remaining word count | 12607
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| 0 0 | negative remaining word count | 12578
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| negative remaining word count | 12895/12897
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
Because the I/O bus floats to zero on 211x computers, an IOI (read word
count) returns zeros in the unused bit locations, even though the word count
itself is a two's-complement negative value.
Implementation notes:
1. The DMA simulation transfers one word per DMA cycle, with cycles
interleaved with machine instruction execution. The alternative
implementation of transferring the entire data block between one
instruction and the next and then delaying DMA completion for the
appropriate block-transfer time will not work. The HP diagnostics check
for word-at-a-time transfers by watching the word count. Other HP
software (e.g., the RTE disc driver for the 7905/06/20/25 units) can
abort a transfer in progress and then use the remaining word count as an
indication of the error location.
*/
#include "hp2100_defs.h"
#include "hp2100_cpu.h"
#include "hp2100_cpu_dmm.h"
/* DMA program constants */
#define DMA_CHAN_COUNT 2 /* number of DMA channels */
typedef enum { /* channel number */
ch1, /* channel 1 */
ch2 /* channel 2 */
} CHANNEL;
#define DMA_1_REQ (1u << ch1) /* channel 1 request */
#define DMA_2_REQ (1u << ch2) /* channel 2 request */
#define TO_REQ(c) (1u << (c))
/* DMA control words.
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| S | B | C | - - - - - - - | device select code | word 1
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| D | starting memory address | word 2
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
| negative word count | word 3
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
*/
#define CN_STC 0100000u /* (S) assert STC during each cycle */
#define CN_PACK 0040000u /* (B) enable byte packing and unpacking (12578A only) */
#define CN_CLC 0020000u /* (C) assert CLC at the end of the block transfer */
#define CN_SC 0000077u /* device select code mask (all but 12607) */
#define CN_SC_12607 0000007u /* device select code mask (12607 only) */
#define CN_12607_MASK (CN_STC | CN_CLC | CN_SC_12607)
#define CN_XFRIN 0100000u /* (D) transfer direction is out of/into memory (0/1) */
#define CN_ADDRESS 0077777u /* memory address mask (all but 12607, 15 bits) */
#define CN_ADDRESS_12607 0037777u /* memory address mask (12607, 14 bits) */
#define CN_COUNT_12607 0017777u /* word count mask (12607, 13 bits) */
#define CN_COUNT_12578 0037777u /* word count mask (12578, 14 bits) */
static const BITSET_NAME dma_cw1_names [] = { /* DMA control word 1 names */
"STC", /* bit 15 */
"byte packing", /* bit 14 */
"CLC" /* bit 13 */
};
static const BITSET_FORMAT dma_cw1_format = /* names, offset, direction, alternates, bar */
{ FMT_INIT (dma_cw1_names, 13, msb_first, no_alt, append_bar) };
/* DMA global state declarations */
uint32 dma_request_set = 0; /* the channels that are requesting service */
/* DMA local state declarations */
typedef struct {
FLIP_FLOP control; /* control flip-flop */
FLIP_FLOP flag; /* flag flip-flop */
FLIP_FLOP flag_buffer; /* flag buffer flip-flop */
FLIP_FLOP select; /* register select flip-flop */
uint32 xfer_sc; /* transfer enable flip-flop */
HP_WORD cw1; /* device select */
HP_WORD cw2; /* direction, address */
HP_WORD cw3; /* word count */
uint8 packer; /* byte-packer holding register */
t_bool occupied; /* TRUE if packing register is occupied */
} DMA_STATE;
static DMA_STATE dma [DMA_CHAN_COUNT]; /* per-channel state */
/* DMA I/O interface routine declarations */
static INTERFACE dma_interface;
static INTERFACE dmc_interface;
/* DMA local SCP support routine declarations */
static t_stat dma_reset (DEVICE *dptr);
/* DMA local utility routine declarations */
static void dma_cycle (CHANNEL chan, ACCESS_CLASS class);
/* DMA SCP data declarations */
/* Unit list */
static UNIT dma_unit [DMA_CHAN_COUNT] = {
/* Event Routine Unit Flags Capacity Delay */
/* ------------- ---------- -------- ----- */
{ UDATA (NULL, 0, 0) }, /* channel 1 dummy unit */
{ UDATA (NULL, 0, 0) } /* channel 2 dummy unit */
};
/* Device information blocks.
Each DMA device uses two DIBs, corresponding to the two select codes assigned
to each channel. During I/O initialization, the DEVICE pointers for select
codes 2 and 6 are both set to the "dma1_dev" device, while the DIB pointers
are set to "dma1_dib [1]" and "dma1_dib [0]", respectively (and similarly for
select codes 3 and 7).
Implementation notes:
1. The DIBs for each channel must be contained in a two-element array, as
the DIB pointer for the lower select code is obtained by incrementing the
pointer stored in the DEVICE structure that points to the DIB for the
upper select code.
*/
static DIB dma1_dib [] = { /* DMA channel 1 (select code 6) */
{ &dma_interface, /* the device's I/O interface function pointer */
DMA1, /* the device's select code (02-77) */
ch1, /* the card index */
NULL, /* the card description */
NULL }, /* the ROM description */
/* DMA channel 1 (select code 2) */
{ &dmc_interface, /* the device's I/O interface function pointer */
DMALT1, /* the device's select code (02-77) */
ch1, /* the card index */
NULL, /* the card description */
NULL } /* the ROM description */
};
static DIB dma2_dib [] = { /* DMA channel 2 (select code 7) */
{ &dma_interface, /* the device's I/O interface function pointer */
DMA2, /* the device's select code (02-77) */
ch2, /* the card index */
NULL, /* the card description */
NULL }, /* the ROM description */
/* DMA channel 2 (select code 3) */
{ &dmc_interface, /* the device's I/O interface function pointer */
DMALT2, /* the device's select code (02-77) */
ch2, /* the card index */
NULL, /* the card description */
NULL } /* the ROM description */
};
/* Register lists */
static REG dma1_reg [] = {
/* Macro Name Location Width Flags */
/* ------ ------- --------------------- ----- ----- */
{ ORDATA (XFR, dma [ch1].xfer_sc, 6) },
{ FLDATA (CTL, dma [ch1].control, 0) },
{ FLDATA (FLG, dma [ch1].flag, 0) },
{ FLDATA (FBF, dma [ch1].flag_buffer, 0) },
{ FLDATA (CTL2, dma [ch1].select, 0) },
{ ORDATA (CW1, dma [ch1].cw1, 16) },
{ ORDATA (CW2, dma [ch1].cw2, 16) },
{ ORDATA (CW3, dma [ch1].cw3, 16) },
{ FLDATA (BYTE, dma [ch1].occupied, 0) },
{ ORDATA (PACKER, dma [ch1].packer, 8), REG_A },
{ NULL }
};
static REG dma2_reg [] = {
/* Macro Name Location Width Flags */
/* ------ ------- --------------------- ----- ----- */
{ ORDATA (XFR, dma [ch2].xfer_sc, 6) },
{ FLDATA (CTL, dma [ch2].control, 0) },
{ FLDATA (FLG, dma [ch2].flag, 0) },
{ FLDATA (FBF, dma [ch2].flag_buffer, 0) },
{ FLDATA (CTL2, dma [ch2].select, 0) },
{ ORDATA (CW1, dma [ch2].cw1, 16) },
{ ORDATA (CW2, dma [ch2].cw2, 16) },
{ ORDATA (CW3, dma [ch2].cw3, 16) },
{ FLDATA (BYTE, dma [ch2].occupied, 0) },
{ ORDATA (PACKER, dma [ch2].packer, 8), REG_A },
{ NULL }
};
/* Trace list */
static DEBTAB dma_deb [] = {
{ "CMD", TRACE_CMD }, /* trace interface or controller commands */
{ "CSRW", TRACE_CSRW }, /* trace interface control, status, read, and write actions */
{ "SR", TRACE_SR }, /* trace service requests received */
{ "DATA", TRACE_DATA }, /* trace memory data accesses */
{ "IOBUS", TRACE_IOBUS }, /* trace I/O bus signals and data words received and returned */
{ NULL, 0 }
};
/* Device descriptors */
DEVICE dma1_dev = {
"DMA1", /* device name */
&dma_unit [ch1], /* unit array */
dma1_reg, /* register array */
NULL, /* modifier array */
1, /* number of units */
8, /* address radix */
1, /* address width */
1, /* address increment */
8, /* data radix */
16, /* data width */
NULL, /* examine routine */
NULL, /* deposit routine */
&dma_reset, /* reset routine */
NULL, /* boot routine */
NULL, /* attach routine */
NULL, /* detach routine */
dma1_dib, /* device information block array */
DEV_DISABLE | DEV_DEBUG, /* device flags */
0, /* debug control flags */
dma_deb, /* debug flag name table */
NULL, /* memory size change routine */
NULL /* logical device name */
};
DEVICE dma2_dev = {
"DMA2", /* device name */
&dma_unit [ch2], /* unit array */
dma2_reg, /* register array */
NULL, /* modifier array */
1, /* number of units */
8, /* address radix */
1, /* address width */
1, /* address increment */
8, /* data radix */
16, /* data width */
NULL, /* examine routine */
NULL, /* deposit routine */
&dma_reset, /* reset routine */
NULL, /* boot routine */
NULL, /* attach routine */
NULL, /* detach routine */
dma2_dib, /* device information block array */
DEV_DISABLE | DEV_DEBUG, /* device flags */
0, /* debug control flags */
dma_deb, /* debug flag name table */
NULL, /* memory size change routine */
NULL /* logical device name */
};
static DEVICE *dma_dptrs [] = {
&dma1_dev,
&dma2_dev
};
/* DMA I/O interface routines */
/* DMA interface (select codes 06 and 07).
I/O operations directed to select code 6 for channel 1 or select code 7 for
channel 2 configure Control Word 1 and start and stop DMA transfers. Each
channel has a transfer enable, a control, a flag, and a flag buffer
flip-flop. Transfer enable must be set via STC to start DMA. The control
flip-flop is used only to enable the DMA completion interrupt; it is set by
STC and cleared by CLC. The flag and flag buffer flip-flops are set at
transfer completion to signal an interrupt. STF may be issued to abort a
transfer in progress, and SFS and SFC test whether a transfer is active.
There are hardware differences between the various DMA cards. The 12607B
(2114) stores only bits 2-0 of the select code and interprets them as select
codes 10-16 (SRQ17 is not decoded). The 12578A (2115/16), 12895A (2100), and
12897B (1000) support the full range of select codes (10-77 octal). The
12578A supports byte-sized transfers by setting bit 14. Bit 14 is ignored by
all other DMA cards, which support word transfers only.
Implementation notes:
1. An IOI reads the floating S-bus (high on the 1000, low on the 21xx).
2. Asserting CRS resets the Control Word 2/3 select flip-flops. Although
the select flip-flops are controlled by the lower select code
interfaces, CRS is asserted only to select codes 6 and up, so we reset
the flip-flops here.
3. The 12578A simulation uses a byte-packing/unpacking register to hold one
byte while the other is read or written during the DMA cycle.
4. The transfer enable flip-flop is simulated by the "xfer_sc" state
variable, which holds the select code of the interface controlled by the
DMA channel (i.e., set by Control Word 1), or the value 100000 octal if
the channel is inactive. These values correspond to the transfer enable
flip-flop being set or cleared, respectively. This implementation
permits a fast activity check when an interface asserts SRQ, which
virtually all interfaces do regardless of whether or not they are under
DMA control. The alternative is to test that the transfer enable
flip-flop is set and then that Control Word 1 masked just to the select
code matches that of the interface asserting SRQ.
5. The transfer enable flip-flop will not set if the flag buffer flip-flop
is set; the latter asserts an asynchronous clear to the former. In
hardware, the STC and CLF signals assert concurrently, so transfer
enable will set when the flag buffer is cleared asynchronously. In
simulation, these signals are processed sequentially, so we must test
for concurrent CLF assertion in the STC handler.
6. When starting a DMA transfer, we must assert SIR to the target interface
to see if SRQ is already asserted and therefore to set the appropriate
channel bit in "dma_request_set". This is required because the
interface may assert SRQ before DMA is started, which will NOT set the
channel request bit if the transfer enable flip-flop is clear.
*/
static SIGNALS_VALUE dma_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
{
const CHANNEL ch = (CHANNEL) dibptr->card_index; /* the DMA channel number */
INBOUND_SIGNAL signal;
INBOUND_SET working_set = inbound_signals;
SIGNALS_VALUE outbound = { ioNONE, 0 };
t_bool irq_enabled = FALSE;
while (working_set) { /* while signals remain */
signal = IONEXTSIG (working_set); /* isolate the next signal */
switch (signal) { /* dispatch the I/O signal */
case ioCLF: /* Clear Flag flip-flop */
dma [ch].flag_buffer = CLEAR; /* reset the flag buffer */
dma [ch].flag = CLEAR; /* and flag flip-flops */
break;
case ioSTF: /* Set Flag flip-flop */
dma [ch].flag_buffer = SET; /* set the flag buffer flip-flop */
break;
case ioENF: /* Enable Flag */
if (dma [ch].flag_buffer == SET) { /* if the flag buffer flip-flop is set */
dma [ch].flag = SET; /* then set the flag flip-flop */
if (dma [ch].xfer_sc <= SC_MAX) { /* if the channel is active */
dma [ch].xfer_sc = D16_SIGN; /* then clear transfer enable to stop the transfer */
tpprintf (dma_dptrs [ch], TRACE_CMD, "Channel transfer %s\n",
(dma [ch].cw3 == 0 ? "completed" : "aborted"));
}
dma_request_set &= ~TO_REQ (ch); /* clear any pending channel service request */
}
break;
case ioSFC: /* Skip if Flag is Clear */
if (dma [ch].flag == CLEAR) /* if a transfer is in progress */
outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
break;
case ioSFS: /* Skip if Flag is Set */
if (dma [ch].flag == SET) /* if transfer is complete */
outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
break;
case ioIOI: /* I/O Data Input */
if (cpu_configuration & CPU_1000) /* if the CPU is a 1000-series machine */
outbound.value = D16_UMAX; /* then return all ones */
else /* otherise */
outbound.value = 0; /* return all zeros for the other models */
break;
case ioIOO: /* I/O Data Output */
if (cpu_configuration & CPU_2114) /* if this is a 12607 */
dma [ch].cw1 = inbound_value & CN_12607_MASK | 010; /* then convert to SC 0-6 to 10-16 */
else if (cpu_configuration & (CPU_2115 | CPU_2116)) /* otherwise if this is a 12578 */
dma [ch].cw1 = inbound_value; /* then store the control word verbatim */
else /* otherwise */
dma [ch].cw1 = inbound_value & ~CN_PACK; /* remove the byte-packing flag */
tpprintf (dma_dptrs [ch], TRACE_CSRW, "Control word 1 is %sselect code %02o\n",
fmt_bitset (inbound_value, dma_cw1_format), inbound_value & CN_SC);
break;
case ioPOPIO: /* Power-On Preset to I/O */
dma [ch].flag_buffer = SET; /* set the flag buffer flip-flop */
break;
case ioCRS: /* Control Reset */
dma [ch].control = CLEAR; /* clear the control flip-flop */
dma [ch].select = CLEAR; /* and the control word select flip-flop */
dma [ch].xfer_sc = D16_SIGN; /* clear transfer enable to abort any in-progress transfer */
break;
case ioCLC: /* Clear Control flip-flop */
dma [ch].control = CLEAR; /* clear the control flip-flop */
if (dma [ch].xfer_sc <= SC_MAX) /* if the channel is active */
tpprintf (dma_dptrs [ch], TRACE_CMD, "Channel completion interrupt is inhibited\n");
break;
case ioSTC: /* Set Control flip-flop */
dma [ch].control = SET; /* set the control flip-flop */
dma [ch].packer = 0; /* clear the packing register */
dma [ch].occupied = FALSE; /* and the occupied flag */
if (dma [ch].flag_buffer == CLEAR /* if the flag buffer is clear */
|| inbound_signals & ioCLF) { /* or will be cleared in this cycle */
dma [ch].xfer_sc = dma [ch].cw1 & CN_SC; /* then set the transfer enable flip-flop */
if (dma [ch].cw2 & CN_XFRIN) /* if this is an input transfer */
tpprintf (dma_dptrs [ch], TRACE_CMD,
"Channel transfer of %u words from select code %02o to address %05o started\n",
NEG16 (dma [ch].cw3), dma [ch].cw1 & CN_SC, dma [ch].cw2 & LA_MASK);
else /* otherwise it's an output transfer */
tpprintf (dma_dptrs [ch], TRACE_CMD,
"Channel transfer of %u words from address %05o to select code %02o started\n",
NEG16 (dma [ch].cw3), dma [ch].cw2 & LA_MASK, dma [ch].cw1 & CN_SC);
io_dispatch (dma [ch].xfer_sc, ioSIR, 0); /* update the target interface's SRQ state */
}
break;
case ioSIR: /* Set Interrupt Request */
if (dma [ch].control & dma [ch].flag) /* if the control and flag flip-flops are set */
outbound.signals |= cnVALID; /* then deny PRL */
else /* otherwise */
outbound.signals |= cnPRL | cnVALID; /* conditionally assert PRL */
if (dma [ch].control & dma [ch].flag /* if the control and flag */
& dma [ch].flag_buffer) /* and flag buffer flip-flops are set */
outbound.signals |= cnIRQ | cnVALID; /* then conditionally assert IRQ */
break;
case ioIAK: /* Interrupt Acknowledge */
dma [ch].flag_buffer = CLEAR; /* clear the flag buffer flip-flop */
break;
case ioIEN: /* Interrupt Enable */
irq_enabled = TRUE; /* permit IRQ to be asserted */
break;
case ioPRH: /* Priority High */
if (irq_enabled && outbound.signals & cnIRQ) /* if IRQ is enabled and conditionally asserted */
outbound.signals |= ioIRQ | ioFLG; /* then assert IRQ and FLG */
if (!irq_enabled || outbound.signals & cnPRL) /* if IRQ is disabled or PRL is conditionally asserted */
outbound.signals |= ioPRL; /* then assert it unconditionally */
break;
case ioEDT: /* not used by this interface */
case ioPON: /* not used by this interface */
break;
}
IOCLEARSIG (working_set, signal); /* remove the current signal from the set */
} /* and continue until all signals are processed */
return outbound; /* return the outbound signals and value */
}
/* DMA configuration interface (select codes 02 and 03).
I/O operations directed to select code 2 for channel 1 or select code 3 for
channel 2 configure Control Words 2 and 3. CLC and STC manipulate the
register select flip-flop, which determines whether IOO writes to the
transfer address (CW2) or word count (CW3) registers, respectively. IOI
reads the current content of the word count register. There are no control,
flag, or flag buffer flip-flops for these select codes, and CLF, STF, SFC,
and SFS are ignored.
There are hardware differences in the implementations of the memory address
and word count registers among the various cards. The 12607B (2114) supports
14-bit addresses and 13-bit word counts. The 12578A (2115/6) supports 15-bit
addresses and 14-bit word counts. The 12895A (2100) and 12897B (1000)
support 15-bit addresses and 16-bit word counts.
Implementation notes:
1. Because the I/O bus floats to zero on 211x computers, an IOI (read word
count) returns zeros in the unused bit locations, even though the word
count itself is a negative value.
2. Select codes 2 and 3 cannot interrupt, so there is no SIR handler.
*/
static SIGNALS_VALUE dmc_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
{
const CHANNEL ch = (CHANNEL) dibptr->card_index; /* the DMA channel number */
INBOUND_SIGNAL signal;
INBOUND_SET working_set = inbound_signals;
SIGNALS_VALUE outbound = { ioNONE, 0 };
while (working_set) { /* while signals remain */
signal = IONEXTSIG (working_set); /* isolate the next signal */
switch (signal) { /* dispatch the I/O signal */
case ioCLC: /* Clear Control flip-flop */
dma [ch].select = CLEAR; /* select the word count register */
break;
case ioSTC: /* Set Control flip-flop */
dma [ch].select = SET; /* select the memory address register */
break;
case ioIOI: /* I/O Data Input */
if (cpu_configuration & CPU_2114) /* if this is a 12607 */
outbound.value = dma [ch].cw3 & CN_COUNT_12607; /* then return only 13 bits of the count */
else if (cpu_configuration & (CPU_2115 | CPU_2116)) /* otherwise if this is a 12578 */
outbound.value = dma [ch].cw3 & CN_COUNT_12578; /* then return only 14 bits of the count */
else /* otherwise */
outbound.value = dma [ch].cw3; /* return the full value of the count */
tpprintf (dma_dptrs [ch], TRACE_CSRW, "Remaining word count is %u\n",
NEG16 (dma [ch].cw3));
break;
case ioIOO: /* I/O Data Output */
if (dma [ch].select) { /* if the word count register is selected */
dma [ch].cw3 = inbound_value; /* then save the (negative) count */
tpprintf (dma_dptrs [ch], TRACE_CSRW, "Control word 3 is word count %u\n",
NEG16 (dma [ch].cw3));
}
else { /* otherwise the address register is selected */
if (cpu_configuration & CPU_2114) /* if this is a 12607 */
dma [ch].cw2 = inbound_value /* then store only 14 bits of the address */
& (CN_XFRIN | CN_ADDRESS_12607); /* while preserving the transfer direction */
else /* otherwise */
dma [ch].cw2 = inbound_value; /* store the full address */
tpprintf (dma_dptrs [ch], TRACE_CSRW, "Control word 2 is %s address %05o\n",
(dma [ch].cw2 & CN_XFRIN ? "input to" : "output from"),
dma [ch].cw2 & LA_MASK);
}
break;
case ioPRH: /* Priority High */
outbound.signals |= ioPRL; /* assert PRL */
break;
case ioSTF: /* not used by this interface */
case ioCLF: /* not used by this interface */
case ioSFS: /* not used by this interface */
case ioSFC: /* not used by this interface */
case ioEDT: /* not used by this interface */
case ioCRS: /* not used by this interface */
case ioPOPIO: /* not used by this interface */
case ioPON: /* not used by this interface */
case ioIAK: /* not used by this interface */
case ioENF: /* not used by this interface */
case ioIEN: /* not used by this interface */
case ioSIR: /* not used by this interface */
break;
}
IOCLEARSIG (working_set, signal); /* remove the current signal from the set */
} /* and continue until all signals are processed */
return outbound; /* return the outbound signals and value */
}
/* DMA global utility routines */
/* Configure DMA for one or two channels.
This routine configures DMA for the specific card being simulated, based on
the CPU model currently selected. The 12607B, which is used with the 2114
CPU, has a single DMA channel. All other CPUs use cards that have two
channels.
On entry, the routine adds or removes the "device can be disabled" and
"device is currently enabled" flags from the DEVICE structure for DMA channel
2, depending on whether or not the current CPU model is a 2114. This ensures
that the user is restricted to configurations that were actually supported on
the current CPU.
In addition, if the CPU is a 1000, it assigns the logical names "DCPC1" and
"DCPC2" to the two DMA channels. This allows 1000-series users to refer to
the channels using the HP-preferred device names (i.e., "Dual-Channel Port
Controller").
Implementation notes:
1. It is OK to deassign the logical name from a device even if one has not
been assigned yet, as the "deassign_device" routine protects against
this. Therefore, it is not necessary to check if the name exists first.
However, assigning a logical name does not check first, so we must ensure
that it has not been assigned before setting the new name.
2. As this routine is called during a CPU model change, we unconditionally
enable DMA channel 1 (and channel 2, if not a 2114), so that setting the
CPU model starts with a known device configuration.
*/
void dma_configure (void)
{
dma1_dev.flags &= ~DEV_DIS; /* enable DMA channel 1 */
if (cpu_configuration & CPU_2114) /* if the current CPU is a 2114 */
dma2_dev.flags = dma2_dev.flags & ~DEV_DISABLE /* then make channel 2 unalterable */
| DEV_DIS; /* and disable it */
else /* otherwise */
dma2_dev.flags = dma2_dev.flags & ~DEV_DIS /* enable channel 2 */
| DEV_DISABLE; /* and make it alterable */
if (cpu_configuration & CPU_1000) { /* if the current CPU family is 1000 */
if (dma1_dev.lname == NULL) { /* then if the logical names have not been set */
assign_device (&dma1_dev, "DCPC1"); /* then change the device names */
assign_device (&dma2_dev, "DCPC2"); /* from DMA to DCPC for familiarity */
}
}
else { /* otherwise the current model is 21xx */
deassign_device (&dma1_dev); /* so delete the DCPC names */
deassign_device (&dma2_dev); /* to restore the original DMA names */
}
return;
}
/* Assert a DMA service request.
This routine is called to assert the SRQ signal for a specified interface to
the DMA device. Interfaces typically assert SRQ when their flag flip-flops
are set. SRQ is asserted regardless of whether or not DMA is active for the
interface. In simulation, this routine is called when any interface returns
SRQ and takes action only if DMA is actively controlling the interface.
Otherwise, it returns with no action taken.
On entry, "select_code" contains the select code of the interface asserting
SRQ. If either DMA channel is currently controlling the interface, the
corresponding channel service request is set; otherwise, the routine simply
returns. On the next pass through the instruction execution loop, the
request will be serviced by initiating a DMA cycle.
*/
void dma_assert_SRQ (uint32 select_code)
{
if (select_code == dma [ch1].xfer_sc) { /* if DMA channel 1 controls this device */
dma_request_set |= DMA_1_REQ; /* then request service for channel 1 */
tprintf (dma1_dev, TRACE_SR, "Select code %02o asserted SRQ\n",
dma [ch1].cw1 & CN_SC);
}
if (select_code == dma [ch2].xfer_sc) { /* if DMA channel 2 controls this device */
dma_request_set |= DMA_2_REQ; /* then request service for channel 2 */
tprintf (dma2_dev, TRACE_SR, "Select code %02o asserted SRQ\n",
dma [ch2].cw1 & CN_SC);
}
return;
}
/* Service DMA requests.
This routine is called to initiate DMA cycles on one or both channels. It is
called as part of the instruction execution loop whenever a DMA request is
pending.
In hardware, the two DMA channels contend independently for memory and I/O
cycles, with channel 1 having priority over channel 2 if they both request
cycles concurrently (i.e., if both controlled devices assert SRQ
concurrently). In simulation, we process a channel 1 request and then, if
channel 1 is NOT requesting but channel 2 is, we process the channel 2
request. If, after servicing, channel 1 immediately requests another DMA
cycle, any pending channel 2 request is held off until channel 1 is serviced
again. This allows channel 1 to steal all available memory cycles as long as
SRQ is continuously asserted.
With properly designed interface cards, DMA is capable of taking consecutive
I/O cycles. On all machines except the 1000 M-Series, a DMA cycle freezes
the CPU for the duration of the cycle. On the M-Series, a DMA cycle freezes
the CPU if it attempts an I/O cycle (including IAK) or a directly-interfering
memory cycle. An interleaved memory cycle is allowed. Otherwise, the
control processor is allowed to run. Therefore, during consecutive DMA
cycles, the M-Series CPU will run until an IOG instruction is attempted,
whereas the other CPUs will freeze completely. This is simulated by skipping
instruction execution if "dma_request_set" is still non-zero after servicing
the current request, i.e., if the device asserted SRQ again as a result of
the DMA cycle.
Most I/O cards assert SRQ no more than 50% of the time. A few buffered
cards, such as the 12821A and 13175A Disc Interfaces, are capable of
asserting SRQ continuously while filling or emptying the buffer. If SRQ for
channel 1 is asserted continuously when both channels are active, then no
channel 2 cycles will occur until channel 1 completes.
*/
void dma_service (void)
{
if (dma_request_set & DMA_1_REQ) /* if the request is for channel 1 */
dma_cycle (ch1, DMA_Channel_1); /* then do one DMA cycle using the port A map */
if ((dma_request_set & (DMA_1_REQ | DMA_2_REQ)) == DMA_2_REQ) /* if channel 1 is idle and channel 2 is requesting */
dma_cycle (ch2, DMA_Channel_2); /* then do one DMA cycle using the port B map */
return;
}
/* DMA local SCP support routines */
/* Reset DMA.
This routine is called for a RESET, RESET DMAn, RUN, or BOOT command. It is
the simulation equivalent of an initial power-on condition (corresponding to
PON, POPIO, and CRS signal assertion) or a front-panel PRESET button press
(corresponding to POPIO and CRS assertion). SCP delivers a power-on reset to
all devices when the simulator is started.
*/
static t_stat dma_reset (DEVICE *dptr)
{
const DIB *dibptr = (DIB *) dptr->ctxt; /* the DIB pointer */
const CHANNEL ch = (CHANNEL) dibptr->card_index; /* the DMA channel number */
if (! (cpu_configuration & CPU_2114)) /* if this is not a 12607 */
hp_enbdis_pair (dma_dptrs [ch], dma_dptrs [ch ^ 1]); /* then make the two channels consistent */
if (sim_switches & SWMASK ('P')) { /* if this is a power-on reset */
dma [ch].cw1 = 0; /* then clear */
dma [ch].cw2 = 0; /* the control */
dma [ch].cw3 = 0; /* word registers */
}
io_assert (dptr, ioa_POPIO); /* PRESET the device */
dma [ch].packer = 0; /* clear the packing register */
dma [ch].occupied = FALSE; /* and the occupied flag */
return SCPE_OK;
}
/* DMA local utility routines */
/* Execute a DMA cycle.
This routine performs one DMA input or output cycle using the indicated DMA
channel number and DMS map. When the transfer word count reaches zero, the
flag is set on the corresponding DMA channel to indicate completion.
The 12578A card supports byte-packing. If bit 14 in Control Word 1 is set,
each transfer will involve one read/write from memory and two output/input
operations in order to transfer sequential bytes to/from the device.
DMA I/O cycles differ from programmed I/O cycles in that multiple I/O control
backplane signals may be asserted simultaneously. With programmed I/O, only
CLF may be asserted with other signals, specifically with STC, CLC, SFS, SFC,
IOI, or IOO. With DMA, as many as five signals may be asserted concurrently.
DMA I/O timing looks like this:
------------ Input ------------ ----------- Output ------------
Sig Normal Cycle Last Cycle Normal Cycle Last Cycle
=== ============== ============== ============== ==============
IOI T2-T3 T2-T3
IOO T3-T4 T3-T4
STC * T3 T3 T3
CLC * T3-T4 T3-T4
CLF T3 T3 T3
EDT T4 T4
* if enabled by control word 1
Under simulation, this routine dispatches one set of I/O signals per DMA
cycle to the target device's I/O interface. The signals correspond to the
table above, except that all signals for a given cycle are concurrent (e.g.,
the last input cycle has IOI, EDT, and optionally CLC asserted, even though
IOI and EDT are not coincident in hardware). The I/O interfaces will process
these signals sequentially, in the order listed above, before returning.
On entry, "ch" indicates the DMA channel that has received a service request
from the controlled interface, and "class" indicates the memory access
classification to use when reading from or writing to memory. The routine
first determines the set of I/O signals to assert to the interface, as
described above. Then an I/O cycle and a memory cycle are performed to
transfer data (the memory cycle is skipped if byte packing is enabled and
the byte to transfer either resides in or is to be saved in the byte packing
register). Finally, unless the first byte of a byte transfer was just
made, the address is incremented and the word count is decremented; if the
word count is zero, the DMA channel flag is set to terminate the transfer.
Implementation notes:
1. The address increment and word count decrement is done only after the I/O
cycle has completed successfully. This allows a failed transfer to be
retried after correcting the I/O error.
*/
static void dma_cycle (CHANNEL ch, ACCESS_CLASS class)
{
const uint32 selcode = dma [ch].cw1 & CN_SC; /* the device select code */
const uint32 packing = dma [ch].cw1 & CN_PACK; /* the packing bytes flag */
const uint32 input = dma [ch].cw2 & CN_XFRIN; /* the input flag */
const HP_WORD MA = dma [ch].cw2 & CN_ADDRESS; /* the memory address */
HP_WORD data;
SKPF_DATA result;
INBOUND_SET signals;
dma_request_set &= ~TO_REQ (ch); /* clear the channel service request */
if (dma [ch].cw3 != D16_UMAX /* if this is a normal (not last) cycle */
|| packing && !dma [ch].occupied) { /* or it's the first of two byte packing cycles */
if (input) /* then if this is an input cycle */
signals = ioIOI | ioCLF | ioSIR; /* then assert IOI and CLF */
else /* otherwise */
signals = ioIOO | ioCLF | ioSIR; /* assert IOO and CLF */
if (dma [ch].cw1 & CN_STC) /* if STC is wanted */
signals |= ioSTC; /* then assert STC */
}
else { /* otherwise this is the last cycle */
if (input) /* so if it's an input cycle */
signals = ioIOI | ioEDT; /* then assert IOI and EDT */
else { /* otherwise */
signals = ioIOO | ioCLF | ioEDT | ioSIR; /* assert IOO and CLF and EDT */
if (dma [ch].cw1 & CN_STC) /* if STC is wanted */
signals |= ioSTC; /* then assert STC */
}
if (dma [ch].cw1 & CN_CLC) /* if CLC is wanted on the last cycle */
signals |= ioCLC | ioSIR; /* then assert CLC */
}
if (input) { /* if this is an input cycle */
result = io_dispatch (selcode, signals, 0); /* then read a byte or word from the interface */
data = result.data; /* extract the returned data value */
if (packing) { /* if byte packing is enabled */
if (dma [ch].occupied) { /* then if this is the second byte */
data = TO_WORD (dma [ch].packer, data); /* then merge the stored byte */
mem_write (dma_dptrs [ch], class, MA, data); /* and write the data word to memory */
}
else /* otherwise it is the first byte */
dma [ch].packer = LOWER_BYTE (data); /* so save it for later packing */
dma [ch].occupied = ! dma [ch].occupied; /* flip the packing register occupation state */
}
else /* otherwise we are doing word transfers */
mem_write (dma_dptrs [ch], class, MA, data); /* so write the data word to memory */
}
else { /* otherwise this is an output cycle */
if (packing) { /* so if byte packing is enabled */
if (dma [ch].occupied) /* then if this is the second byte */
data = dma [ch].packer; /* then retrieve it */
else { /* otherwise this is the first byte */
data = mem_read (dma_dptrs [ch], class, MA); /* so read the data word from memory */
dma [ch].packer = LOWER_BYTE (data); /* save the second byte in the packing register */
data = UPPER_BYTE (data); /* and send the first byte to the interface */
}
dma [ch].occupied = ! dma [ch].occupied; /* flip the packing register occupation state */
}
else /* otherwise we are doing word transfers */
data = mem_read (dma_dptrs [ch], class, MA); /* so read the data word from memory */
result = io_dispatch (selcode, signals, data); /* output the byte or word to the interface */
}
if (! (packing && dma [ch].occupied)) { /* if this is not the first byte of a byte transfer */
dma [ch].cw2 = input | dma [ch].cw2 + 1 & CN_ADDRESS; /* then increment the address part of CW2 */
dma [ch].cw3 = dma [ch].cw3 + 1 & D16_MASK; /* and the (negative) word count */
if (dma [ch].cw3 == 0) { /* if the transfer is complete */
dma [ch].flag_buffer = SET; /* then set the DMA channel flag buffer */
io_assert (dma_dptrs [ch], ioa_ENF); /* and the flag */
}
}
return;
}