/* hp2100_io.h: HP 2100 device-to-CPU interface declarations Copyright (c) 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 AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. Except as contained in this notice, the name of the author shall not be used in advertising or otherwise to promote the sale, use or other dealings in this Software without prior written authorization from the author. 04-Sep-18 JDB Added card_description and rom_description to the DIB 10-Jul-18 JDB Created This file contains declarations used by I/O devices to interface with the HP 21xx/1000 CPU. It is required by any module that uses Device Information Blocks (DIBs), i.e., is addressed by an I/O select code. */ /* I/O devices - fixed select code assignments */ #define CPU 000 /* interrupt control */ #define OVF 001 /* overflow */ #define DMALT1 002 /* DMA 1 alternate */ #define DMALT2 003 /* DMA 2 alternate */ #define PWR 004 /* power fail */ #define MPPE 005 /* memory protect/parity error */ #define DMA1 006 /* DMA channel 1 */ #define DMA2 007 /* DMA channel 2 */ /* I/O devices - variable select code assignment defaults */ #define PTR 010 /* 12597A-002 paper tape reader */ #define TTY 011 /* 12531C teleprinter */ #define PTP 012 /* 12597A-005 paper tape punch */ #define TBG 013 /* 12539C time-base generator */ #define LPS 014 /* 12653A line printer */ #define LPT 015 /* 12845A line printer */ #define MTD 020 /* 12559A data */ #define MTC 021 /* 12559A control */ #define DPD 022 /* 12557A data */ #define DPC 023 /* 12557A control */ #define DQD 024 /* 12565A data */ #define DQC 025 /* 12565A control */ #define DRD 026 /* 12610A data */ #define DRC 027 /* 12610A control */ #define MSD 030 /* 13181A data */ #define MSC 031 /* 13181A control */ #define IPLI 032 /* 12566B link in */ #define IPLO 033 /* 12566B link out */ #define DS 034 /* 13037A control */ #define BACI 035 /* 12966A Buffered Async Comm Interface */ #define MPX 036 /* 12792A/B/C 8-channel multiplexer */ #define PIF 037 /* 12620A/12936A Privileged Interrupt Fence */ #define MUXL 040 /* 12920A lower data */ #define MUXU 041 /* 12920A upper data */ #define MUXC 042 /* 12920A control */ #define DI_DA 043 /* 12821A Disc Interface with Amigo disc devices */ #define DI_DC 044 /* 12821A Disc Interface with CS/80 disc and tape devices */ #define MC1 045 /* 12566B Microcircuit Interface */ #define MC2 046 /* 12566B Microcircuit Interface */ #define SC_OPT 002 /* start of optional devices */ #define SC_CRS 006 /* start of devices that receive CRS */ #define SC_VAR 010 /* start of variable assignments */ #define SC_MAX 077 /* the maximum select code */ #define SC_MASK 077u /* the mask for the select code */ #define SC_BASE 8 /* the radix for the select code */ /* I/O backplane signals. The INBOUND_SIGNAL and OUTBOUND_SIGNAL declarations mirror the hardware signals that are received and asserted, respectively, by the interfaces on the I/O backplane. A set of one or more signals forms an INBOUND_SET or OUTBOUND_SET that is sent to or returned from a device interface. Under simulation, the CPU and DMA dispatch one INBOUND_SET to the target device interface per I/O cycle. The interface returns an OUTBOUND_SET and a data value combined into a SIGNALS_VALUE structure to the caller. In addition, some signals must be asserted asynchronously, e.g., in response to an event service call. The "io_assert" function, provided by the CPU module, provides asynchronous assertion for the ENF, SIR, PON, POPIO, CRS, and IAK signals. In hardware, signals are assigned to one or more specific I/O T-periods, and some signals are asserted concurrently. For example, a programmed STC sc,C instruction asserts the STC and CLF signals together in period T4. Under simulation, signals are ORed to form an I/O cycle; in this example, the signal handler would receive an INBOUND_SET value of "ioSTC | ioCLF". Hardware allows parallel action for concurrent signals. Under simulation, a "concurrent" set of signals is processed sequentially by the signal handler in order of ascending numerical value. Although assigned T-periods differ between programmed I/O and DMA I/O cycles, a single processing order is used. The order of execution generally follows the order of T-period assertion, except that SIR is processed after all other signals that may affect the interrupt request chain. Implementation notes: 1. The enumerations describe signals. A set of signals normally would be modeled as an unsigned integer, as a set may contain more than one signal. However, we define a set as the enumeration, as the "gdb" debugger has special provisions for an enumeration of discrete bit values and will display the set in "ORed" form. 2. The null signal ("ioNONE") cannot be defined as an enumeration constant because the C language has a single name space for all enumeration constants, so separate "no inbound signals" and "no outbound signals" identifiers would be required, and because including them would require handlers for them in "switch" statements, which is undesirable. Therefore, we define "ioNONE" as an explicit integer 0, so that it is compatible with both enumerations. 3. The ioCLF signal must be processed after ioSFS/ioSFC to ensure that a true skip test generates ioSKF before the flag is cleared, and after ioIOI/ioIOO/ioSTC/ioCLC to meet the requirement that executing an instruction having the H/C bit set is equivalent to executing the same instruction with the H/C bit clear and then a CLF instruction. 4. The ioENF signal must be processed after ioSTF and ioPOPIO to ensure that the flag buffer flip-flop has been set before it is used to set the flag flip-flop. 5. The ioPRH signal must be processed after ioIEN. The latter sets an internal "interrupts are enabled" flag that must be tested before interrupts are asserted by PRH. 6. An I/O handler will receive ioCRS as a result of a CLC 0 instruction, ioPOPIO and ioCRS as a result of a RESET command, and ioPON, ioPOPIO, and ioCRS as a result of a RESET -P command. 7. An I/O handler will receive ioNONE when a HLT instruction is executed that has the H/C bit clear (i.e., no CLF generated). 8. In hardware, the SIR signal is generated unconditionally every T5 period to time the setting of the IRQ flip-flop. Under simulation, ioSIR indicates that the I/O handler must set the PRL, IRQ, and SRQ signals as required by the interface logic. ioSIR will be included in the I/O cycle if any of the flip-flops affecting these signals are changed and the interface supports interrupts or DMA transfers. 9. In hardware, the ENF signal is unconditionally generated every T2 period to time the setting of the flag flip-flop and to reset the IRQ flip-flop. If the flag buffer flip-flip is set, then flag will be set by ENF. If the flag buffer is clear, ENF will not affect flag. Under simulation, ioENF is sent to set the flag flip-flop after the flag buffer flip-flop is set explicitly. 10. In hardware, the PON signal is asserted continuously while the CPU is operating. Under simulation, ioPON is asserted only at simulator initialization or when processing a RESET -P command. 11. To avoid polling each interface when IEN or PRH asserts from a prior denied condition, interfaces return two conditional signals: cnIRQ and cnPRL. These are identical in meaning to the ioIRQ and ioPRL signals, except their assertions do not depend on IEN or PRH. To identify an interrupting interface rapidly, the CPU keeps these conditional signals in the "interrupt_request_set" and "priority_holdoff_set" arrays. These bit vectors are examined when a higher-priority interface reasserts PRL or when the interrupt system is reenabled. 12. An interface will assert cnVALID if the conditional PRL and IRQ were determined. If cnVALID is not asserted by the interface, then the states of the cnPRL and cnIRQ signals cannot be inferred from their presence or absence in the oubound signal set. The cnVALID pseudo-signal is required because although most interfaces determine the PRL and IRQ states in response to an SIR assertion, not all do. In particular, the 12936A Privileged Interrupt Fence determines PRL in response to an IOO signal. */ typedef enum { /* I/O T-Period Description */ /* ============== ============================= */ ioPON = 000000000001, /* T2 T3 T4 T5 T6 Power On Normal */ ioIOI = 000000000002, /* -- -- T4 T5 -- I/O Data Input (CPU) */ /* T2 T3 -- -- -- I/O Data Input (DMA) */ ioIOO = 000000000004, /* -- T3 T4 -- -- I/O Data Output */ ioSFS = 000000000010, /* -- T3 T4 T5 -- Skip if Flag is Set */ ioSFC = 000000000020, /* -- T3 T4 T5 -- Skip if Flag is Clear */ ioSTC = 000000000040, /* -- -- T4 -- -- Set Control flip-flop (CPU) */ /* -- T3 -- -- -- Set Control flip-flop (DMA) */ ioCLC = 000000000100, /* -- -- T4 -- -- Clear Control flip-flop (CPU) */ /* -- T3 T4 -- -- Clear Control flip-flop (DMA) */ ioSTF = 000000000200, /* -- T3 -- -- -- Set Flag flip-flop */ ioCLF = 000000000400, /* -- -- T4 -- -- Clear Flag flip-flop (CPU) */ /* -- T3 -- -- -- Clear Flag flip-flop (DMA) */ ioEDT = 000000001000, /* -- -- T4 -- -- End Data Transfer */ ioCRS = 000000002000, /* -- -- -- T5 -- Control Reset */ ioPOPIO = 000000004000, /* -- -- -- T5 -- Power-On Preset to I/O */ ioIAK = 000000010000, /* -- -- -- -- T6 Interrupt Acknowledge */ ioENF = 000000020000, /* T2 -- -- -- -- Enable Flag */ ioSIR = 000000040000, /* -- -- -- T5 -- Set Interrupt Request */ ioIEN = 000000100000, /* T2 T3 T4 T5 T6 Interrupt system Enable */ ioPRH = 000000200000 /* T2 T3 T4 T5 T6 Priority High */ } INBOUND_SIGNAL; typedef INBOUND_SIGNAL INBOUND_SET; /* a set of INBOUND_SIGNALs */ typedef enum { /* I/O T-Period Description */ /* ============== ================= */ ioSKF = 000000000001, /* -- T3 T4 T5 -- skip on flag */ ioPRL = 000000000002, /* T2 T3 T4 T5 T6 priority low */ ioFLG = 000000000004, /* -- -- T4 T5 -- flag */ ioIRQ = 000000000010, /* -- -- T4 T5 -- interrupt request */ ioSRQ = 000000000020, /* T2 T3 T4 T5 T6 service request */ cnIRQ = 000000000040, /* conditional interrupt request */ cnPRL = 000000000100, /* conditional priority low */ cnVALID = 000000000200 /* conditional signals are valid */ } OUTBOUND_SIGNAL; typedef OUTBOUND_SIGNAL OUTBOUND_SET; /* a set of OUTBOUND_SIGNALs */ #define ioNONE 0 /* a universal "no signals are asserted" value */ typedef struct { /* the I/O interface return structure */ OUTBOUND_SET signals; /* the outbound signal set */ HP_WORD value; /* the outbound value */ } SIGNALS_VALUE; /* I/O backplane signal assertions */ typedef enum { /* assertions passed to the "io_assert" call */ ioa_ENF, /* Enable Flag */ ioa_SIR, /* Set Interrupt Request */ ioa_PON, /* Power On Normal */ ioa_POPIO, /* Power-On Preset to I/O */ ioa_CRS, /* Control Reset */ ioa_IAK /* Interrupt Acknowledge */ } IO_ASSERTION; /* I/O macros. The following macros are useful in device interface signal handlers and unit service routines. The parameter definition symbols employed are: P = a priority set value S = an INBOUND_SET or OUTBOUND_SET value L = an INBOUND_SIGNAL value A priority set is an unsigned value, where each bit represents an assertion of some nature (e.g., I/O signals, interrupt requests, etc.), and the position of the bit represents its priority, which decreases from LSB to MSB. The IOPRIORITY macro isolates the highest-priority bit from the set. It does this by ANDing the value with its two's complement; only the lowest-order bit will differ. For example (bits are numbered here from the LSB): priority set : ...0 0 1 1 0 1 0 0 0 0 0 0 (bits 6, 8, and 9 are asserted) one's compl : ...1 1 0 0 1 0 1 1 1 1 1 1 two's compl : ...1 1 0 0 1 1 0 0 0 0 0 0 ANDed value : ...0 0 0 0 0 1 0 0 0 0 0 0 (bit 6 is highest priority) If the request set indicates requests by 0 bits, rather than 1 bits, the IOPRIORITY macro must be called with the one's complement of the bits. The IONEXTSIG macro isolates the next inbound signal in sequence to process from the inbound signal set S. The IOCLEARSIG macro removes the processed signal L from the inbound signal set S. Implementation notes: 1. The IOPRIORITY macro implements two's complement explicitly, rather than using a signed negation, to be compatible with machines using a sign-magnitude integer format. "gcc" and "clang" optimize the complement and add to a single NEG instruction on x86 machines. */ #define IOPRIORITY(P) ((P) & ~(P) + 1) #define IONEXTSIG(S) ((INBOUND_SIGNAL) IOPRIORITY (S)) #define IOCLEARSIG(S,L) S = (INBOUND_SIGNAL) ((S) ^ (L)) /* I/O structures. The Device Information Block (DIB) allows devices to be relocated in the machine's I/O space. Each DIB contains a pointer to the device interface routine, a value corresponding to the location of the interface card in the CPU's I/O card cage (which determines the card's select code), a card index that is non-zero if the interface routine services multiple cards, a pointer to a string that describes the card, and an optional pointer to a string that describes the 1000-series boot loader ROM that boots from the device associated with the interface card. The card description is printed by the SHOW CPU IOCAGE command, while the ROM description is printed by the SHOW CPU ROMS command. All devices should have card descriptions, but only those devices having boot loader ROMs will have ROM descriptions. The latter will be NULL if no there is no associated ROM. Implementation notes: 1. The select_code and card_index fields could be smaller than the defined 32-bit sizes, but IA-32 processors execute instructions with 32-bit operands much faster than those with 16- or 8-bit operands. 2. The DIB_REGS macro provides hidden register entries needed to save and restore the state of a DIB. Only the potentially variable fields are referenced. In particular, the "io_interface" field must not be saved, as the address of the device's interface routine may change from version to version of the simulator. 3. The Windows SDK defines an INTERFACE macro. Because "sim_defs.h" includes "windows.h", a name clash occurs, even though we never use any Windows features. So we undefine INTERFACE here to repurpose it for our own use. */ #undef INTERFACE /* remove any previous definition */ typedef struct dib DIB; /* an incomplete definition */ typedef SIGNALS_VALUE INTERFACE /* the I/O device interface function prototype */ (const DIB *dibptr, /* a pointer to the constant device information block */ INBOUND_SET inbound_signals, /* a set of inbound signals */ HP_WORD inbound_value); /* a 16-bit inbound value */ struct dib { /* the Device Information Block */ INTERFACE *io_interface; /* the controller I/O interface function pointer */ uint32 select_code; /* the device's select code (02-77) */ uint32 card_index; /* the card index if multiple interfaces are supported */ const char *card_description; /* the card description (model number and name) */ const char *rom_description; /* the 1000 boot loader ROM description (model number and name) */ }; #define DIB_REGS(dib) \ /* Macro Name Location Width Flags */ \ /* ------ ------- -------------------------- ----- ----------------- */ \ { ORDATA (DIBSC, dib.select_code, 32), PV_LEFT | REG_HRO } /* Initial Binary Loader. HP 1000-series CPUs contain from one to four bootstrap loader ROMs that contain the 64-word initial binary loaders for the associated devices. The loader program to use is selected by setting the S-register as follows: 15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0 +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ | ROM # | - - | select code | - - - - - - | +---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+ ...and then pressing the front panel IBL button to copy the program into main memory for execution. Bits 15-14 select one of four loader ROM sockets on the CPU board that may contain ROMs, and bits 11-6 specify the select code of the associated interface card. If the specified socket does, the contents of the ROM are copied into the upper 64 words of memory and configured to use the specified select code. The unspecified bits of the S register are available for use by the bootstrap program. The 21xx CPUs store their initial binary loaders in the last 64 words of available memory. This memory is protected by a LOADER ENABLE switch on the front panel. When the switch is off (disabled), main memory effectively ends 64 locations earlier, i.e., the loader area is treated as non-existent. Because these are core machines, the loader is retained when system power is off. In simulation, we define a BOOT_LOADER structure containing three indicies and a 64-word array that holds the bootstrap program. The start_index gives the array index of the first program word to execute, the dma_index gives the array index of the DMA control word that specifies the interface select code, and the fwa_index gives the array index of the word containing the negative starting address of the loader in memory; this is used to ensure that the bootstrap does not overlay itself when reading from the device. A LOADER_ARRAY consists of two BOOT_LOADER structures: one for 21xx-series machines, and one for 1000-series machines. If either BOOT_LOADER is not applicable, e.g., because the CPU series does not support booting from the given device, then the associated start_index is set to IBL_NA. If the boot loader exists but does not use DMA and/or does not configure a starting address word, the associated indicies are set to IBL_NA. */ #define IBL_WIDTH 6 /* loader ROM address width */ #define IBL_MASK ((1u << IBL_WIDTH) - 1) /* loader ROM address mask (2 ** 6 - 1) */ #define IBL_MAX ((1u << IBL_WIDTH) - 1) /* loader ROM address maximum (2 ** 6 - 1) */ #define IBL_SIZE (IBL_MAX + 1) /* loader ROM size in words */ #define IBL_START 0 /* ROM array index of the program start */ #define IBL_DMA (IBL_MAX - 1) /* ROM array index of the DMA configuration word */ #define IBL_FWA (IBL_MAX - 0) /* ROM array index of the negative starting address */ #define IBL_NA (IBL_MAX + 1) /* "not-applicable" ROM array index */ #define IBL_S_CLEAR 0000000u /* cpu_copy_loader mask to clear the S register */ #define IBL_S_NOCLEAR 0177777u /* cpu_copy_loader mask to preserve the S register */ #define IBL_S_NOSET 0000000u /* cpu_copy_loader mask to preserve the S register */ #define IBL_ROM_MASK 0140000u /* ROM socket selector mask */ #define IBL_SC_MASK 0007700u /* device select code mask */ #define IBL_USER_MASK ~(IBL_ROM_MASK | IBL_SC_MASK) #define IBL_ROM_SHIFT 14 #define IBL_SC_SHIFT 6 #define IBL_ROM(s) (((s) & IBL_ROM_MASK) >> IBL_ROM_SHIFT) #define IBL_SC(s) (((s) & IBL_SC_MASK) >> IBL_SC_SHIFT) #define IBL_TO_SC(c) ((c) << IBL_SC_SHIFT & IBL_SC_MASK) typedef struct { uint32 start_index; /* the array index of the start of the program */ uint32 dma_index; /* the array index of the DMA configuration word */ uint32 fwa_index; /* the array index of the negative starting address */ MEMORY_WORD loader [IBL_SIZE]; /* the 64-word bootstrap loader program */ } BOOT_LOADER; typedef BOOT_LOADER LOADER_ARRAY [2]; /* array (21xx, 1000) of bootstrap loaders */ /* CPU global utility routine declarations */ extern uint32 cpu_copy_loader (const LOADER_ARRAY boot, uint32 sc, HP_WORD sr_clear, HP_WORD sr_set); extern t_bool cpu_io_stop (UNIT *uptr); /* I/O subsystem global utility routine declarations */ extern void io_assert (DEVICE *dptr, IO_ASSERTION assertion); /* Main memory global utility routine declarations */ extern HP_WORD mem_examine (uint32 address); extern void mem_deposit (uint32 address, HP_WORD value);