1063 lines
53 KiB
C
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;
|
|
}
|