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