blob: 14bbfea4ba86b1456101231fffe614a9cde7757d [file] [log] [blame] [raw]
/* hp2100_cpu.c: HP 21xx/1000 CPU simulator
Copyright (c) 1993-2014, Robert M. Supnik
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
ROBERT M SUPNIK 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 Robert M Supnik shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from Robert M Supnik.
CPU 2114C/2115A/2116C/2100A/1000-M/E/F central processing unit
12731A memory expansion module
MP 12581A/12892B memory protect
DMA1,DMA2 12607B/12578A/12895A direct memory access controller
DCPC1,DCPC2 12897B dual channel port controller
31-Dec-14 JDB Corrected devdisp data parameters
30-Dec-14 JDB Added S-register parameters to ibl_copy
24-Dec-14 JDB Added casts for explicit downward conversions
18-Mar-13 JDB Removed redundant extern declarations
05-Feb-13 JDB HLT instruction handler now relies on sim_vm_fprint_stopped
09-May-12 JDB Separated assignments from conditional expressions
13-Jan-12 JDB Minor speedup in "is_mapped"
Added casts to cpu_mod, dmasio, dmapio, cpu_reset, dma_reset
07-Apr-11 JDB Fixed I/O return status bug for DMA cycles
Failed I/O cycles now stop on failing instruction
28-Mar-11 JDB Tidied up signal handling
29-Oct-10 JDB Revised DMA for new multi-card paradigm
Consolidated DMA reset routines
DMA channels renamed from 0,1 to 1,2 to match documentation
27-Oct-10 JDB Changed I/O instructions, handlers, and DMA for revised signal model
Changed I/O dispatch table to use DIB pointers
19-Oct-10 JDB Removed DMA latency counter
13-Oct-10 JDB Fixed DMA requests to enable stealing every cycle
Fixed DMA priority for channel 1 over channel 2
Corrected comments for "cpu_set_idle"
30-Sep-08 JDB Breakpoints on interrupt trap cells now work
05-Sep-08 JDB VIS and IOP are now mutually exclusive on 1000-F
11-Aug-08 JDB Removed A/B shadow register variables
07-Aug-08 JDB Moved hp_setdev, hp_showdev to hp2100_sys.c
Moved non-existent memory checks to WritePW
05-Aug-08 JDB Fixed mp_dms_jmp to accept lower bound, check write protection
30-Jul-08 JDB Corrected DMS violation register set conditions
Refefined ABORT to pass address, moved def to hp2100_cpu.h
Combined dms and dms_io routines
29-Jul-08 JDB JSB to 0/1 with W5 out and fence = 0 erroneously causes MP abort
11-Jul-08 JDB Unified I/O slot dispatch by adding DIBs for CPU, MP, and DMA
26-Jun-08 JDB Rewrote device I/O to model backplane signals
EDT no longer passes DMA channel
30-Apr-08 JDB Enabled SIGNAL instructions, SIG debug flag
28-Apr-08 JDB Added SET CPU IDLE/NOIDLE, idle detection for DOS/RTE
24-Apr-08 JDB Fixed single stepping through interrupts
20-Apr-08 JDB Enabled EMA and VIS, added EMA, VIS, and SIGNAL debug flags
03-Dec-07 JDB Memory ex/dep and bkpt type default to current map mode
26-Nov-07 JDB Added SET CPU DEBUG and OS/VMA flags, enabled OS/VMA
15-Nov-07 JDB Corrected MP W5 (JSB) jumper action, SET/SHOW reversal,
mp_mevff clear on interrupt with I/O instruction in trap cell
04-Nov-07 JDB Removed DBI support from 1000-M (was temporary for RTE-6/VM)
28-Apr-07 RMS Removed clock initialization
02-Mar-07 JDB EDT passes input flag and DMA channel in dat parameter
11-Jan-07 JDB Added 12578A DMA byte packing
28-Dec-06 JDB CLC 0 now sends CRS instead of CLC to devices
26-Dec-06 JDB Fixed improper IRQ deferral for 21xx CPUs
Fixed improper interrupt servicing in resolve
21-Dec-06 JDB Added 21xx loader enable/disable support
16-Dec-06 JDB Added 2114 and 2115 CPU options.
Added support for 12607B (2114) and 12578A (2115/6) DMA
01-Dec-06 JDB Added 1000-F CPU option (requires HAVE_INT64)
SHOW CPU displays 1000-M/E instead of 21MX-M/E
16-Oct-06 JDB Moved ReadF to hp2100_cpu1.c
12-Oct-06 JDB Fixed INDMAX off-by-one error in resolve
26-Sep-06 JDB Added iotrap parameter to UIG dispatchers for RTE microcode
12-Sep-06 JDB iogrp returns NOTE_IOG to recalc interrupts
resolve returns NOTE_INDINT to service held-off interrupt
16-Aug-06 JDB Added support for future microcode options, future F-Series
09-Aug-06 JDB Added double integer microcode, 1000-M/E synonyms
Enhanced CPU option validity checking
Added DCPC as a synonym for DMA for 21MX simulations
26-Dec-05 JDB Improved reporting in dev_conflict
22-Sep-05 RMS Fixed declarations (from Sterling Garwood)
21-Jan-05 JDB Reorganized CPU option flags
15-Jan-05 RMS Split out EAU and MAC instructions
26-Dec-04 RMS DMA reset doesn't clear alternate CTL flop (from Dave Bryan)
DMA reset shouldn't clear control words (from Dave Bryan)
Alternate CTL flop not visible as register (from Dave Bryan)
Fixed CBS, SBS, TBS to perform virtual reads
Separated A/B from M[0/1] for DMA IO (from Dave Bryan)
Fixed bug in JPY (from Dave Bryan)
25-Dec-04 JDB Added SET CPU 21MX-M, 21MX-E (21MX defaults to MX-E)
TIMER/EXECUTE/DIAG instructions disabled for 21MX-M
T-register reflects changes in M-register when halted
25-Sep-04 JDB Moved MP into its own device; added MP option jumpers
Modified DMA to allow disabling
Modified SET CPU 2100/2116 to truncate memory > 32K
Added -F switch to SET CPU to force memory truncation
Fixed S-register behavior on 2116
Fixed LIx/MIx behavior for DMA on 2116 and 2100
Fixed LIx/MIx behavior for empty I/O card slots
Modified WRU to be REG_HRO
Added BRK and DEL to save console settings
Fixed use of "unsigned int16" in cpu_reset
Modified memory size routine to return SCPE_INCOMP if
memory size truncation declined
20-Jul-04 RMS Fixed bug in breakpoint test (reported by Dave Bryan)
Back up PC on instruction errors (from Dave Bryan)
14-May-04 RMS Fixed bugs and added features from Dave Bryan
- SBT increments B after store
- DMS console map must check dms_enb
- SFS x,C and SFC x,C work
- MP violation clears automatically on interrupt
- SFS/SFC 5 is not gated by protection enabled
- DMS enable does not disable mem prot checks
- DMS status inconsistent at simulator halt
- Examine/deposit are checking wrong addresses
- Physical addresses are 20b not 15b
- Revised DMS to use memory rather than internal format
- Added instruction printout to HALT message
- Added M and T internal registers
- Added N, S, and U breakpoints
Revised IBL facility to conform to microcode
Added DMA EDT I/O pseudo-opcode
Separated DMA SRQ (service request) from FLG
12-Mar-03 RMS Added logical name support
02-Feb-03 RMS Fixed last cycle bug in DMA output (found by Mike Gemeny)
22-Nov-02 RMS Added 21MX IOP support
24-Oct-02 RMS Fixed bugs in IOP and extended instructions
Fixed bugs in memory protection and DMS
Added clock calibration
25-Sep-02 RMS Fixed bug in DMS decode (found by Robert Alan Byer)
26-Jul-02 RMS Restructured extended instructions, added IOP support
22-Mar-02 RMS Changed to allocate memory array dynamically
11-Mar-02 RMS Cleaned up setjmp/auto variable interaction
17-Feb-02 RMS Added DMS support
Fixed bugs in extended instructions
03-Feb-02 RMS Added terminal multiplexor support
Changed PCQ macro to use unmodified PC
Fixed flop restore logic (found by Bill McDermith)
Fixed SZx,SLx,RSS bug (found by Bill McDermith)
Added floating point support
16-Jan-02 RMS Added additional device support
07-Jan-02 RMS Fixed DMA register tables (found by Bill McDermith)
07-Dec-01 RMS Revised to use breakpoint package
03-Dec-01 RMS Added extended SET/SHOW support
10-Aug-01 RMS Removed register in declarations
26-Nov-00 RMS Fixed bug in dual device number routine
21-Nov-00 RMS Fixed bug in reset routine
15-Oct-00 RMS Added dynamic device number support
References:
- 2100A Computer Reference Manual (02100-90001, Dec-1971)
- Model 2100A Computer Installation and Maintenance Manual
(02100-90002, Aug-1972)
- HP 1000 M/E/F-Series Computers Technical Reference Handbook
(5955-0282, Mar-1980)
- HP 1000 M/E/F-Series Computers Engineering and Reference Documentation
(92851-90001, Mar-1981)
- HP 1000 M/E/F-Series Computers I/O Interfacing Guide
(02109-90006, Sep-1980)
- 12607A Direct Memory Access Operating and Service Manual
(12607-90002, Jan-1970)
- 12578A/12578A-01 Direct Memory Access Operating and Service Manual
(12578-9001, Mar-1972)
- 12892B Memory Protect Installation Manual (12892-90007, Jun-1978)
The register state for the HP 2116 CPU is:
AR<15:0> A register - addressable as location 0
BR<15:0> B register - addressable as location 1
PC<14:0> P register - program counter
SR<15:0> S register - switch register
MR<14:0> M register - memory address
TR<15:0> T register - memory data
E extend flag (carry out)
O overflow flag
The 2100 adds memory protection logic:
mp_fence<14:0> memory fence register
mp_viol<15:0> memory protection violation register (F register)
The 21MX adds a pair of index registers and memory expansion logic:
XR<15:0> X register
YR<15:0> Y register
dms_sr<15:0> dynamic memory system status register
dms_vr<15:0> dynamic memory system violation register
The original HP 2116 has four instruction formats: memory reference,
shift, alter/skip, and I/O. The HP 2100 added extended memory reference
and extended arithmetic. The HP21MX added extended byte, bit, and word
instructions as well as extended memory.
The memory reference format is:
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|in| op |cp| offset | memory reference
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
<14:11> mnemonic action
0010 AND A = A & M[MA]
0011 JSB M[MA] = P, P = MA + 1
0100 XOR A = A ^ M[MA]
0101 JMP P = MA
0110 IOR A = A | M[MA]
0111 ISZ M[MA] = M[MA] + 1, skip if M[MA] == 0
1000 ADA A = A + M[MA]
1001 ADB B = B + M[MA]
1010 CPA skip if A != M[MA]
1011 CPB skip if B != M[MA]
1100 LDA A = M[MA]
1101 LDB B = M[MA]
1110 STA M[MA] = A
1111 STB M[MA] = B
<15,10> mode action
0,0 page zero direct MA = IR<9:0>
0,1 current page direct MA = PC<14:0>'IR,9:0>
1,0 page zero indirect MA = M[IR<9:0>]
1,1 current page indirect MA = M[PC<14:10>'IR<9:0>]
Memory reference instructions can access an address space of 32K words.
An instruction can directly reference the first 1024 words of memory
(called page zero), as well as 1024 words of the current page; it can
indirectly access all 32K.
The shift format is:
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| 0 0 0 0|ab| 0|s1| op1 |ce|s2|sl| op2 | shift
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| | \---+---/ | | | \---+---/
| | | | | | |
| | | | | | +---- shift 2 opcode
| | | | | +---------- skip if low bit == 0
| | | | +------------- shift 2 enable
| | | +---------------- clear Extend
| | +---------------------- shift 1 opcode
| +---------------------------- shift 1 enable
+---------------------------------- A/B select
The alter/skip format is:
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| 0 0 0 0|ab| 1|regop| e op|se|ss|sl|in|sz|rs| alter/skip
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| \-+-/ \-+-/ | | | | | |
| | | | | | | | +- reverse skip sense
| | | | | | | +---- skip if register == 0
| | | | | | +------- increment register
| | | | | +---------- skip if low bit == 0
| | | | +------------- skip if sign bit == 0
| | | +---------------- skip if Extend == 0
| | +--------------------- clr/com/set Extend
| +--------------------------- clr/com/set register
+---------------------------------- A/B select
The I/O transfer format is:
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| 1 0 0 0|ab| 1|hc| opcode | device | I/O transfer
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| | \---+---/\-------+-------/
| | | |
| | | +--------- device select
| | +---------------------- opcode
| +---------------------------- hold/clear flag
+---------------------------------- A/B select
The IO transfer instruction controls the specified device.
Depending on the opcode, the instruction may set or clear
the device flag, start or stop I/O, or read or write data.
The 2100 added an extended memory reference instruction;
the 21MX added extended arithmetic, operate, byte, word,
and bit instructions. Note that the HP 21xx is, despite
the right-to-left bit numbering, a big endian system.
Bits <15:8> are byte 0, and bits <7:0> are byte 1.
The extended memory reference format (HP 2100) is:
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| 1| 0 0 0|op| 0| opcode | extended mem ref
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|in| operand address |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
The extended arithmetic format (HP 2100) is:
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| 1| 0 0 0 0 0|dr| 0 0| opcode |shift count| extended arithmetic
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
The extended operate format (HP 21MX) is:
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| 1| 0 0 0|op| 0| 1 1 1 1 1| opcode | extended operate
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
The extended byte and word format (HP 21MX) is:
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| 1| 0 0 0 1 0 1 1 1 1 1 1| opcode | extended byte/word
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|in| operand address |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0 0|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
The extended bit operate format (HP 21MX) is:
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
| 1| 0 0 0 1 0 1 1 1 1 1 1 1| opcode | extended bit operate
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|in| operand address |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|in| operand address |
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
General notes:
1. Reasons to stop. The simulator can be stopped by:
HALT instruction
breakpoint encountered
infinite indirection loop
unimplemented instruction and stop_inst flag set
unknown I/O device and stop_dev flag set
I/O error in I/O simulator
2. Interrupts. I/O devices are modelled by substituting software states for
I/O backplane signals. Signals generated by I/O instructions and DMA
cycles are dispatched to the target device for action. Backplane signals
are processed sequentially, except for the "clear flag" signal, which may
be generated in parallel with another signal. For example, the "STC sc,C"
instruction generates the "set control" and the "clear flag" signals
concurrently.
CPU interrupt signals are modelled as three parallel arrays:
- device request priority as bit vector dev_prl [2] [31..0]
- device interrupt requests as bit vector dev_irq [2] [31..0]
- device service requests as bit vector dev_srq [2] [31..0]
Each array forms a 64-bit vector, with bits 0-31 of the first element
corresponding to select codes 00-37 octal, and bits 0-31 of the second
element corresponding to select codes 40-77 octal.
The HP 2100 interrupt structure is based on the PRH, PRL, IRQ, and IAK
signals. PRH indicates that no higher-priority device is interrupting.
PRL indicates to lower-priority devices that a given device is not
interrupting. IRQ indicates that a given device is requesting an
interrupt. IAK indicates that the given device's interrupt request is
being acknowledged.
PRH and PRL form a hardware priority chain that extends from interface to
interface on the backplane. We model just PRL, as PRH is calculated from
the PRLs of higher-priority devices.
Typical I/O devices have a flag, flag buffer, and control flip-flop. If a
device's flag, flag buffer, and control bits are set, and the device is
the highest priority on the interrupt chain, it requests an interrupt by
asserting IRQ. When the interrupt is acknowledged with IAK, the flag
buffer is cleared, preventing further interrupt requests from that device.
The combination of flag and control set blocks interrupts from lower
priority devices.
Service requests are used to trigger the DMA service logic. Setting the
device flag typically also sets SRQ, although SRQ may be calculated
independently.
3. Non-existent memory. On the HP 2100, reads to non-existent memory
return zero, and writes are ignored. In the simulator, the
largest possible memory is instantiated and initialized to zero.
Thus, only writes need be checked against memory size.
On the 21xx machines, doing SET CPU LOADERDISABLE decreases available
memory size by 64 words.
4. Adding I/O devices. These modules must be modified:
hp2100_defs.h add interrupt request definition
hp2100_sys.c add sim_devices table entry
5. Instruction interruptibility. The simulator is fast enough, compared
to the run-time of the longest instructions, for interruptibility not
to matter. But the HP diagnostics explicitly test interruptibility in
EIS and DMS instructions, and long indirect address chains. Accordingly,
the simulator does "just enough" to pass these tests. In particular, if
an interrupt is pending but deferred at the beginning of an interruptible
instruction, the interrupt is taken at the appropriate point; but there
is no testing for new interrupts during execution (that is, the event
timer is not called).
6. Interrupt deferral. At instruction fetch time, a pending interrupt
request will be deferred if the previous instruction was a JMP indirect,
JSB indirect, STC, CLC, STF, CLF, or was executing from an interrupt trap
cell. In addition, the following instructions will cause deferral on the
1000 series: SFS, SFC, JRS, DJP, DJS, SJP, SJS, UJP, and UJS.
On the HP 1000, the request is always deferred until after the current
instruction completes. On the 21xx, the request is deferred unless the
current instruction is an MRG instruction other than JMP or JMP,I or
JSB,I. Note that for the 21xx, SFS and SFC are not included in the
deferral criteria.
7. Terminology. The 1000 series of computers was originally called the 21MX
at introduction. The 21MX (occasionally, 21MXM) corresponds to the 1000
M-Series, and the 21MXE (occasionally, 21XE) corresponds to the 1000
E-Series. The model numbers were changed before the introduction of the
1000 F-Series, although some internal HP documentation refers to a 21MXF.
The terms MEM (Memory Expansion Module), MEU (Memory Expansion Unit), DMI
(Dynamic Mapping Instructions), and DMS (Dynamic Mapping System) are used
somewhat interchangeably to refer to the logical-to-physical memory
address translation option provided on the 1000-Series. DMS consists of
the MEM card (12731A) and the DMI firmware (13307A). However, MEM and MEU
have been used interchangeably to refer to the mapping card, as have DMI
and DMS to refer to the firmware instructions.
*/
#include "hp2100_defs.h"
#include "hp2100_cpu.h"
#include "hp2100_cpu1.h"
/* Memory protect constants */
#define UNIT_V_MP_JSB (UNIT_V_UF + 0) /* MP jumper W5 */
#define UNIT_V_MP_INT (UNIT_V_UF + 1) /* MP jumper W6 */
#define UNIT_V_MP_SEL1 (UNIT_V_UF + 2) /* MP jumper W7 */
#define UNIT_MP_JSB (1 << UNIT_V_MP_JSB) /* 1 = W5 is out */
#define UNIT_MP_INT (1 << UNIT_V_MP_INT) /* 1 = W6 is out */
#define UNIT_MP_SEL1 (1 << UNIT_V_MP_SEL1) /* 1 = W7 is out */
/* DMA channels */
typedef enum { ch1, ch2 } CHANNEL; /* channel number */
#define DMA_CHAN_COUNT 2 /* number of DMA channels */
#define DMA_OE 020000000000 /* byte packing odd/even flag */
#define DMA1_STC 0100000 /* DMA - issue STC */
#define DMA1_PB 0040000 /* DMA - pack bytes */
#define DMA1_CLC 0020000 /* DMA - issue CLC */
#define DMA2_OI 0100000 /* DMA - output/input */
typedef struct {
FLIP_FLOP control; /* control flip-flop */
FLIP_FLOP flag; /* flag flip-flop */
FLIP_FLOP flagbuf; /* flag buffer flip-flop */
FLIP_FLOP xferen; /* transfer enable flip-flop */
FLIP_FLOP select; /* register select flip-flop */
uint32 cw1; /* device select */
uint32 cw2; /* direction, address */
uint32 cw3; /* word count */
uint32 packer; /* byte-packer holding reg */
} DMA_STATE;
#define DMA_1_REQ (1 << ch1) /* channel 1 request */
#define DMA_2_REQ (1 << ch2) /* channel 2 request */
/* Command line switches */
#define ALL_BKPTS (SWMASK('E')|SWMASK('N')|SWMASK('S')|SWMASK('U'))
#define ALL_MAPMODES (SWMASK('S')|SWMASK('U')|SWMASK('P')|SWMASK('Q'))
/* RTE base-page addresses. */
static const uint32 xeqt = 0001717; /* XEQT address */
static const uint32 tbg = 0001674; /* TBG address */
/* DOS base-page addresses. */
static const uint32 m64 = 0000040; /* constant -64 address */
static const uint32 p64 = 0000067; /* constant +64 address */
/* CPU local data */
static uint32 jsb_plb = 2; /* protected lower bound for JSB */
static uint32 saved_MR = 0; /* between executions */
static uint32 fwanxm = 0; /* first word addr of nx mem */
/* CPU global data */
uint16 *M = NULL; /* memory */
uint16 ABREG[2]; /* A/B registers */
uint32 PC = 0; /* P register */
uint32 SR = 0; /* S register */
uint32 MR = 0; /* M register */
uint32 TR = 0; /* T register */
uint32 XR = 0; /* X register */
uint32 YR = 0; /* Y register */
uint32 E = 0; /* E register */
uint32 O = 0; /* O register */
FLIP_FLOP ion = CLEAR; /* interrupt enable */
t_bool ion_defer = FALSE; /* interrupt defer */
uint32 intaddr = 0; /* interrupt addr */
uint32 stop_inst = 1; /* stop on ill inst */
uint32 stop_dev = 0; /* stop on ill dev */
uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */
uint32 pcq_p = 0; /* PC queue ptr */
REG *pcq_r = NULL; /* PC queue reg ptr */
uint32 dev_prl [2] = { ~(uint32) 0, ~(uint32) 0 }; /* device priority low bit vector */
uint32 dev_irq [2] = { 0, 0 }; /* device interrupt request bit vector */
uint32 dev_srq [2] = { 0, 0 }; /* device service request bit vector */
/* Memory protect global data */
FLIP_FLOP mp_control = CLEAR; /* MP control flip-flop */
FLIP_FLOP mp_flag = CLEAR; /* MP flag flip-flop */
FLIP_FLOP mp_flagbuf = CLEAR; /* MP flag buffer flip-flop */
FLIP_FLOP mp_mevff = CLEAR; /* memory expansion violation flip-flop */
FLIP_FLOP mp_evrff = SET; /* enable violation register flip-flop */
uint32 mp_fence = 0; /* MP fence register */
uint32 mp_viol = 0; /* MP violation register */
uint32 iop_sp = 0; /* iop stack reg */
uint32 ind_max = 16; /* iadr nest limit */
uint32 err_PC = 0; /* error PC */
jmp_buf save_env; /* MP abort handler */
/* DMA global data */
DMA_STATE dma [DMA_CHAN_COUNT]; /* per-channel state */
/* Dynamic mapping system global data */
uint32 dms_enb = 0; /* dms enable */
uint32 dms_ump = 0; /* dms user map */
uint32 dms_sr = 0; /* dms status reg */
uint32 dms_vr = 0; /* dms violation reg */
uint16 dms_map[MAP_NUM * MAP_LNT] = { 0 }; /* dms maps */
/* External data */
extern DIB clk_dib; /* CLK DIB for idle check */
extern const BOOT_ROM ptr_rom, dq_rom, ms_rom, ds_rom; /* boot ROMs for cpu_boot routine */
/* CPU local routines */
static t_stat Ea (uint32 IR, uint32 *addr, uint32 irq);
static uint16 ReadTAB (uint32 va);
static uint32 dms (uint32 va, uint32 map, uint32 prot);
static uint32 shift (uint32 inval, uint32 flag, uint32 oper);
static t_stat dma_cycle (CHANNEL chan, uint32 map);
static uint32 calc_dma (void);
static t_bool dev_conflict (void);
static uint32 devdisp (uint32 select_code, IOCYCLE signal_set, uint16 data);
/* CPU global routines */
t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
t_stat cpu_reset (DEVICE *dptr);
t_stat cpu_boot (int32 unitno, DEVICE *dptr);
t_stat mp_reset (DEVICE *dptr);
t_stat dma_reset (DEVICE *dptr);
t_stat cpu_set_size (UNIT *uptr, int32 new_size, char *cptr, void *desc);
t_stat cpu_set_model (UNIT *uptr, int32 new_model, char *cptr, void *desc);
t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc);
t_stat cpu_set_opt (UNIT *uptr, int32 option, char *cptr, void *desc);
t_stat cpu_clr_opt (UNIT *uptr, int32 option, char *cptr, void *desc);
t_stat cpu_set_ldr (UNIT *uptr, int32 enable, char *cptr, void *desc);
t_stat cpu_set_idle (UNIT *uptr, int32 option, char *cptr, void *desc);
t_stat cpu_show_idle (FILE *st, UNIT *uptr, int32 val, void *desc);
void hp_post_cmd (t_bool from_scp);
IOHANDLER cpuio;
IOHANDLER ovflio;
IOHANDLER pwrfio;
IOHANDLER protio;
IOHANDLER dmapio;
IOHANDLER dmasio;
IOHANDLER nullio;
/* Table of CPU features by model.
Fields:
- typ: standard features plus typically configured options.
- opt: complete list of optional features.
- maxmem: maximum configurable memory in 16-bit words.
Features in the "typical" list are enabled when the CPU model is selected.
If a feature appears in the "typical" list but NOT in the "optional" list,
then it is standard equipment and cannot be disabled. If a feature appears
in the "optional" list, then it may be enabled or disabled as desired by the
user.
*/
struct FEATURE_TABLE { /* CPU model feature table: */
uint32 typ; /* - typical features */
uint32 opt; /* - optional features */
uint32 maxmem; /* - maximum memory */
};
static struct FEATURE_TABLE cpu_features[] = { /* features in UNIT_xxxx order*/
{ UNIT_DMA | UNIT_MP, /* UNIT_2116 */
UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_EAU,
32768 },
{ UNIT_DMA, /* UNIT_2115 */
UNIT_PFAIL | UNIT_DMA | UNIT_EAU,
8192 },
{ UNIT_DMA, /* UNIT_2114 */
UNIT_PFAIL | UNIT_DMA,
16384 },
{ 0, 0, 0 },
{ UNIT_PFAIL | UNIT_MP | UNIT_DMA | UNIT_EAU, /* UNIT_2100 */
UNIT_DMA | UNIT_FP | UNIT_IOP | UNIT_FFP,
32768 },
{ 0, 0, 0 },
{ 0, 0, 0 },
{ 0, 0, 0 },
{ UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | UNIT_DMS, /* UNIT_1000_M */
UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_DMS |
UNIT_IOP | UNIT_FFP | UNIT_DS,
1048576 },
{ UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | UNIT_DMS, /* UNIT_1000_E */
UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_DMS |
UNIT_IOP | UNIT_FFP | UNIT_DBI | UNIT_DS | UNIT_EMA_VMA,
1048576 },
{ UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | /* UNIT_1000_F */
UNIT_FFP | UNIT_DBI | UNIT_DMS,
UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_VIS |
UNIT_IOP | UNIT_DS | UNIT_SIGNAL | UNIT_EMA_VMA,
1048576 }
};
/* Null device information block */
DIB null_dib = { &nullio, 0 };
/* CPU data structures
cpu_dib CPU device information block
cpu_dev CPU device descriptor
cpu_unit CPU unit descriptor
cpu_reg CPU register list
cpu_mod CPU modifiers list
cpu_deb CPU debug flags
*/
DIB cpu_dib = { &cpuio, CPU };
UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, 0) };
REG cpu_reg[] = {
{ ORDATA (P, PC, 15) },
{ ORDATA (A, AR, 16), REG_FIT },
{ ORDATA (B, BR, 16), REG_FIT },
{ ORDATA (M, MR, 15) },
{ ORDATA (T, TR, 16), REG_RO },
{ ORDATA (X, XR, 16) },
{ ORDATA (Y, YR, 16) },
{ ORDATA (S, SR, 16) },
{ FLDATA (E, E, 0) },
{ FLDATA (O, O, 0) },
{ FLDATA (ION, ion, 0) },
{ FLDATA (ION_DEFER, ion_defer, 0) },
{ ORDATA (CIR, intaddr, 6) },
{ FLDATA (DMSENB, dms_enb, 0) },
{ FLDATA (DMSCUR, dms_ump, VA_N_PAG) },
{ ORDATA (DMSSR, dms_sr, 16) },
{ ORDATA (DMSVR, dms_vr, 16) },
{ BRDATA (DMSMAP, dms_map, 8, 16, MAP_NUM * MAP_LNT) },
{ ORDATA (IOPSP, iop_sp, 16) },
{ FLDATA (STOP_INST, stop_inst, 0) },
{ FLDATA (STOP_DEV, stop_dev, 1) },
{ DRDATA (INDMAX, ind_max, 16), REG_NZ + PV_LEFT },
{ BRDATA (PCQ, pcq, 8, 15, PCQ_SIZE), REG_RO+REG_CIRC },
{ ORDATA (PCQP, pcq_p, 6), REG_HRO },
{ ORDATA (JSBPLB, jsb_plb, 32), REG_HRO },
{ ORDATA (SAVEDMR, saved_MR, 32), REG_HRO },
{ ORDATA (FWANXM, fwanxm, 32), REG_HRO },
{ ORDATA (WRU, sim_int_char, 8), REG_HRO },
{ ORDATA (BRK, sim_brk_char, 8), REG_HRO },
{ ORDATA (DEL, sim_del_char, 8), REG_HRO },
{ BRDATA (PRL, dev_prl, 8, 32, 2), REG_HRO },
{ BRDATA (IRQ, dev_irq, 8, 32, 2), REG_HRO },
{ BRDATA (SRQ, dev_srq, 8, 32, 2), REG_HRO },
{ NULL }
};
/* CPU modifier table.
The 21MX monikers are deprecated in favor of the 1000 designations. See the
"HP 1000 Series Naming History" on the back inside cover of the Technical
Reference Handbook. */
MTAB cpu_mod[] = {
{ UNIT_MODEL_MASK, UNIT_2116, "", "2116", &cpu_set_model, &cpu_show_model, (void *) "2116" },
{ UNIT_MODEL_MASK, UNIT_2115, "", "2115", &cpu_set_model, &cpu_show_model, (void *) "2115" },
{ UNIT_MODEL_MASK, UNIT_2114, "", "2114", &cpu_set_model, &cpu_show_model, (void *) "2114" },
{ UNIT_MODEL_MASK, UNIT_2100, "", "2100", &cpu_set_model, &cpu_show_model, (void *) "2100" },
{ UNIT_MODEL_MASK, UNIT_1000_E, "", "1000-E", &cpu_set_model, &cpu_show_model, (void *) "1000-E" },
{ UNIT_MODEL_MASK, UNIT_1000_E, NULL, "21MX-E", &cpu_set_model, &cpu_show_model, (void *) "1000-E" },
{ UNIT_MODEL_MASK, UNIT_1000_M, "", "1000-M", &cpu_set_model, &cpu_show_model, (void *) "1000-M" },
{ UNIT_MODEL_MASK, UNIT_1000_M, NULL, "21MX-M", &cpu_set_model, &cpu_show_model, (void *) "1000-M" },
#if defined (HAVE_INT64)
{ UNIT_MODEL_MASK, UNIT_1000_F, "", "1000-F", &cpu_set_model, &cpu_show_model, (void *) "1000-F" },
#endif
{ MTAB_XTD | MTAB_VDV, 1, "IDLE", "IDLE", &cpu_set_idle, &cpu_show_idle, NULL },
{ MTAB_XTD | MTAB_VDV, 0, NULL, "NOIDLE", &cpu_set_idle, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, 1, NULL, "LOADERENABLE", &cpu_set_ldr, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, 0, NULL, "LOADERDISABLE", &cpu_set_ldr, NULL, NULL },
{ UNIT_EAU, UNIT_EAU, "EAU", "EAU", &cpu_set_opt, NULL, NULL },
{ UNIT_EAU, 0, "no EAU", NULL, NULL, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, UNIT_EAU, NULL, "NOEAU", &cpu_clr_opt, NULL, NULL },
{ UNIT_FP, UNIT_FP, "FP", "FP", &cpu_set_opt, NULL, NULL },
{ UNIT_FP, 0, "no FP", NULL, NULL, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, UNIT_FP, NULL, "NOFP", &cpu_clr_opt, NULL, NULL },
{ UNIT_IOP, UNIT_IOP, "IOP", "IOP", &cpu_set_opt, NULL, NULL },
{ UNIT_IOP, 0, "no IOP", NULL, NULL, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, UNIT_IOP, NULL, "NOIOP", &cpu_clr_opt, NULL, NULL },
{ UNIT_DMS, UNIT_DMS, "DMS", "DMS", &cpu_set_opt, NULL, NULL },
{ UNIT_DMS, 0, "no DMS", NULL, NULL, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, UNIT_DMS, NULL, "NODMS", &cpu_clr_opt, NULL, NULL },
{ UNIT_FFP, UNIT_FFP, "FFP", "FFP", &cpu_set_opt, NULL, NULL },
{ UNIT_FFP, 0, "no FFP", NULL, NULL, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, UNIT_FFP, NULL, "NOFFP", &cpu_clr_opt, NULL, NULL },
{ UNIT_DBI, UNIT_DBI, "DBI", "DBI", &cpu_set_opt, NULL, NULL },
{ UNIT_DBI, 0, "no DBI", NULL, NULL, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, UNIT_DBI, NULL, "NODBI", &cpu_clr_opt, NULL, NULL },
{ UNIT_EMA_VMA, UNIT_EMA, "EMA", "EMA", &cpu_set_opt, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, UNIT_EMA, NULL, "NOEMA", &cpu_clr_opt, NULL, NULL },
{ UNIT_EMA_VMA, UNIT_VMAOS, "VMA", "VMA", &cpu_set_opt, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, UNIT_VMAOS, NULL, "NOVMA", &cpu_clr_opt, NULL, NULL },
{ UNIT_EMA_VMA, 0, "no EMA/VMA", NULL, &cpu_set_opt, NULL, NULL },
#if defined (HAVE_INT64)
{ UNIT_VIS, UNIT_VIS, "VIS", "VIS", &cpu_set_opt, NULL, NULL },
{ UNIT_VIS, 0, "no VIS", NULL, NULL, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, UNIT_VIS, NULL, "NOVIS", &cpu_clr_opt, NULL, NULL },
{ UNIT_SIGNAL, UNIT_SIGNAL,"SIGNAL", "SIGNAL", &cpu_set_opt, NULL, NULL },
{ UNIT_SIGNAL, 0, "no SIGNAL", NULL, NULL, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, UNIT_SIGNAL, NULL, "NOSIGNAL", &cpu_clr_opt, NULL, NULL },
#endif
/* Future microcode support.
{ UNIT_DS, UNIT_DS, "DS", "DS", &cpu_set_opt, NULL, NULL },
{ UNIT_DS, 0, "no DS", NULL, NULL, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, UNIT_DS, NULL, "NODS", &cpu_clr_opt, NULL, NULL },
*/
{ MTAB_XTD | MTAB_VDV, 4096, NULL, "4K", &cpu_set_size, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, 8192, NULL, "8K", &cpu_set_size, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, 12288, NULL, "12K", &cpu_set_size, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, 16384, NULL, "16K", &cpu_set_size, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, 24576, NULL, "24K", &cpu_set_size, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, 32768, NULL, "32K", &cpu_set_size, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, 65536, NULL, "64K", &cpu_set_size, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, 131072, NULL, "128K", &cpu_set_size, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, 262144, NULL, "256K", &cpu_set_size, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, 524288, NULL, "512K", &cpu_set_size, NULL, NULL },
{ MTAB_XTD | MTAB_VDV, 1048576, NULL, "1024K", &cpu_set_size, NULL, NULL },
{ 0 }
};
DEBTAB cpu_deb[] = {
{ "OS", DEB_OS },
{ "OSTBG", DEB_OSTBG },
{ "VMA", DEB_VMA },
{ "EMA", DEB_EMA },
{ "VIS", DEB_VIS },
{ "SIG", DEB_SIG },
{ NULL, 0 }
};
DEVICE cpu_dev = {
"CPU", /* device name */
&cpu_unit, /* unit array */
cpu_reg, /* register array */
cpu_mod, /* modifier array */
1, /* number of units */
8, /* address radix */
PA_N_SIZE, /* address width */
1, /* address increment */
8, /* data radix */
16, /* data width */
&cpu_ex, /* examine routine */
&cpu_dep, /* deposit routine */
&cpu_reset, /* reset routine */
&cpu_boot, /* boot routine */
NULL, /* attach routine */
NULL, /* detach routine */
&cpu_dib, /* device information block */
DEV_DEBUG, /* device flags */
0, /* debug control flags */
cpu_deb, /* debug flag name table */
NULL, /* memory size change routine */
NULL }; /* logical device name */
/* Overflow device information block */
DIB ovfl_dib = { &ovflio, OVF };
/* Powerfail device information block */
DIB pwrf_dib = { &pwrfio, PWR };
/* Memory protect data structures
mp_dib MP device information block
mp_dev MP device descriptor
mp_unit MP unit descriptor
mp_reg MP register list
mp_mod MP modifiers list
*/
DIB mp_dib = { &protio, PRO };
UNIT mp_unit = { UDATA (NULL, UNIT_MP_SEL1, 0) }; /* default is JSB in, INT in, SEL1 out */
REG mp_reg[] = {
{ FLDATA (CTL, mp_control, 0) },
{ FLDATA (FLG, mp_flag, 0) },
{ FLDATA (FBF, mp_flagbuf, 0) },
{ ORDATA (FR, mp_fence, 15) },
{ ORDATA (VR, mp_viol, 16) },
{ FLDATA (EVR, mp_evrff, 0) },
{ FLDATA (MEV, mp_mevff, 0) },
{ NULL }
};
MTAB mp_mod[] = {
{ UNIT_MP_JSB, UNIT_MP_JSB, "JSB (W5) out", "JSBOUT", NULL },
{ UNIT_MP_JSB, 0, "JSB (W5) in", "JSBIN", NULL },
{ UNIT_MP_INT, UNIT_MP_INT, "INT (W6) out", "INTOUT", NULL },
{ UNIT_MP_INT, 0, "INT (W6) in", "INTIN", NULL },
{ UNIT_MP_SEL1, UNIT_MP_SEL1, "SEL1 (W7) out", "SEL1OUT", NULL },
{ UNIT_MP_SEL1, 0, "SEL1 (W7) in", "SEL1IN", NULL },
{ 0 }
};
DEVICE mp_dev = {
"MP", /* device name */
&mp_unit, /* unit array */
mp_reg, /* register array */
mp_mod, /* 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 */
&mp_reset, /* reset routine */
NULL, /* boot routine */
NULL, /* attach routine */
NULL, /* detach routine */
&mp_dib, /* device information block */
DEV_DISABLE | DEV_DIS, /* device flags */
0, /* debug control flags */
NULL, /* debug flag name table */
NULL, /* memory size change routine */
NULL }; /* logical device name */
/* DMA controller data structures
dmax_dib DMAx device information block
dmax_dev DMAx device descriptor
dmax_reg DMAx register list
*/
DIB dmap1_dib = { &dmapio, DMA1, ch1 };
DIB dmas1_dib = { &dmasio, DMALT1, ch1 };
UNIT dma1_unit = { UDATA (NULL, 0, 0) };
REG dma1_reg[] = {
{ FLDATA (XFR, dma [ch1].xferen, 0) },
{ FLDATA (CTL, dma [ch1].control, 0) },
{ FLDATA (FLG, dma [ch1].flag, 0) },
{ FLDATA (FBF, dma [ch1].flagbuf, 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].packer, 31) },
{ ORDATA (PACKER, dma [ch1].packer, 8) },
{ NULL }
};
DEVICE dma1_dev = {
"DMA1", /* device name */
&dma1_unit, /* 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 */
&dmap1_dib, /* device information block */
DEV_DISABLE, /* device flags */
0, /* debug control flags */
NULL, /* debug flag name table */
NULL, /* memory size change routine */
NULL }; /* logical device name */
DIB dmap2_dib = { &dmapio, DMA2, ch2 };
DIB dmas2_dib = { &dmasio, DMALT2, ch2 };
UNIT dma2_unit = { UDATA (NULL, 0, 0) };
REG dma2_reg[] = {
{ FLDATA (XFR, dma [ch2].xferen, 0) },
{ FLDATA (CTL, dma [ch2].control, 0) },
{ FLDATA (FLG, dma [ch2].flag, 0) },
{ FLDATA (FBF, dma [ch2].flagbuf, 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].packer, 31) },
{ ORDATA (PACKER, dma [ch2].packer, 8) },
{ NULL }
};
DEVICE dma2_dev = {
"DMA2", /* device name */
&dma2_unit, /* 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 */
&dmap2_dib, /* device information block */
DEV_DISABLE, /* device flags */
0, /* debug control flags */
NULL, /* debug flag name table */
NULL, /* memory size change routine */
NULL }; /* logical device name */
static DEVICE *dma_dptrs [] = { &dma1_dev, &dma2_dev };
/* Interrupt deferral table (1000 version) */
/* Deferral for I/O subops: soHLT, soFLG, soSFC, soSFS, soMIX, soLIX, soOTX, soCTL */
static t_bool defer_tab [] = { FALSE, TRUE, TRUE, TRUE, FALSE, FALSE, FALSE, TRUE };
/* Device I/O dispatch table */
DIB *dtab [64] = { &cpu_dib, &ovfl_dib }; /* init with immutable devices */
/* Execute CPU instructions.
This routine is the instruction decode routine for the HP 2100. It is called
from the simulator control program to execute instructions in simulated
memory, starting at the simulated PC. It runs until 'reason' is set to a
status other than SCPE_OK.
*/
t_stat sim_instr (void)
{
uint32 intrq, dmarq; /* set after setjmp */
uint32 iotrap = 0; /* set after setjmp */
t_stat reason = SCPE_OK; /* set after setjmp */
int32 i; /* temp */
DEVICE *dptr; /* temp */
DIB *dibptr; /* temp */
int abortval;
/* Restore register state */
if (dev_conflict ()) /* check device assignment consistency */
return SCPE_STOP; /* conflict; stop execution */
err_PC = PC = PC & VAMASK; /* load local PC */
/* Restore I/O state */
dev_prl [0] = dev_prl [1] = ~(uint32) 0; /* set all priority lows */
dev_irq [0] = dev_irq [1] = 0; /* clear all interrupt requests */
dev_srq [0] = dev_srq [1] = 0; /* clear all service requests */
for (i = OPTDEV; i <= MAXDEV; i++) /* default optional devices */
dtab [i] = &null_dib;
dtab [PWR] = &pwrf_dib; /* for now, powerfail is always present */
for (i = 0; sim_devices [i] != NULL; i++) { /* loop thru dev */
dptr = sim_devices [i];
dibptr = (DIB *) dptr->ctxt; /* get DIB */
if (dibptr && !(dptr->flags & DEV_DIS)) { /* handler exists and device is enabled? */
dtab [dibptr->select_code] = dibptr; /* set DIB pointer into dispatch table */
dibptr->io_handler (dibptr, ioSIR, 0); /* set interrupt request state */
}
}
if (dtab [DMA1] != &null_dib) /* first DMA channel enabled? */
dtab [DMALT1] = &dmas1_dib; /* set up secondary device handler */
if (dtab [DMA2] != &null_dib) /* second DMA channel enabled? */
dtab [DMALT2] = &dmas2_dib; /* set up secondary device handler */
/* Configure interrupt deferral table */
if (UNIT_CPU_FAMILY == UNIT_FAMILY_21XX) /* 21xx series? */
defer_tab [soSFC] = defer_tab [soSFS] = FALSE; /* SFC/S doesn't defer */
else /* 1000 series */
defer_tab [soSFC] = defer_tab [soSFS] = TRUE; /* SFC/S does defer */
/* Set MP abort handling.
If an abort occurs in memory protection, the relocation routine executes a
longjmp to this area OUTSIDE the main simulation loop. Memory protection
errors are the only sources of aborts in the HP 2100. All referenced
variables must be globals, and all sim_instr scoped automatics must be set
after the setjmp.
To initiate an MP abort, use the MP_ABORT macro and pass the violation
address. MP_ABORT should only be called if "mp_control" is SET, as aborts do
not occur if MP is turned off.
An MP interrupt (SC 05) is qualified by "ion" but not by "ion_defer". If the
interrupt system is off when an MP violation is detected, the violating
instruction will be aborted, even though no interrupt occurs. In this case,
neither the flag nor flag buffer are set, and EVR is not cleared.
Implementation notes:
1. The protected lower bound address for the JSB instruction depends on the
W5 jumper setting. If W5 is in, then the lower bound is 2, allowing JSBs
to the A and B registers. If W5 is out, then the lower bound is 0, just
as with JMP.
2. The violation address is passed to enable the MEM violation register to
be updated. The "longjmp" routine will not pass a value of 0; it is
converted internally to 1. This is OK, because only the page number
of the address value is used, and locations 0 and 1 are both on page 0.
3. This routine is used both for MP and MEM violations. The MEV flip-flop
will be clear for the former and set for the latter. The MEV violation
register will be updated by "dms_upd_vr" only if the call is NOT for an
MEM violation; if it is, then the VR has already been set and should not
be disturbed.
*/
jsb_plb = (mp_unit.flags & UNIT_MP_JSB) ? 0 : 2; /* set protected lower bound for JSB */
abortval = setjmp (save_env); /* set abort hdlr */
if (abortval) { /* memory protect abort? */
dms_upd_vr (abortval); /* update violation register (if not MEV) */
if (ion) /* interrupt system on? */
protio (dtab [PRO], ioENF, 0); /* set flag */
}
dmarq = calc_dma (); /* initial recalc of DMA masks */
intrq = calc_int (); /* initial recalc of interrupts */
/* Main instruction fetch/decode loop */
while (reason == SCPE_OK) { /* loop until halted */
uint32 IR, MA, absel, v1, t, skip;
err_PC = PC; /* save PC for error recovery */
if (sim_interval <= 0) { /* event timeout? */
reason = sim_process_event (); /* process event service */
if (reason != SCPE_OK) /* service failed? */
break; /* stop execution */
dmarq = calc_dma (); /* recalc DMA reqs */
intrq = calc_int (); /* recalc interrupts */
}
/* DMA cycles are requested by an I/O card asserting its SRQ signal. If a DMA
channel is programmed to respond to that card's select code, a DMA cycle will
be initiated. A DMA cycle consists of a memory cycle and an I/O cycle.
These cycles are synchronized with the control processor on the 21xx CPUs.
On the 1000s, memory cycles are asynchronous, while I/O cycles are
synchronous. Memory cycle time is about 40% of the I/O cycle time.
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.
All DMA cards except the 12607B provide two independent channels. If both
channels are active simultaneously, channel 1 has priority for I/O cycles
over channel 2.
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.
Implementation notes:
1. CPU freeze is simulated by skipping instruction execution during the
current loop cycle.
2. If both channels have SRQ asserted, DMA priority is simulated by skipping
the channel 2 cycle if channel 1's SRQ is still asserted at the end of
its cycle. If it is not, then channel 2 steals the next cycle from the
CPU.
3. The 1000 M-Series allows some CPU processing concurrently with
continuous DMA cycles, whereas all other CPUs freeze. The processor
freezes if an I/O cycle is attempted, including an interrupt
acknowledgement. Because some microcode extensions (e.g., Access IOP,
RTE-6/VM OS) perform I/O cycles, advance detection of I/O cycles is
difficult. Therefore, we freeze all processing for the M-Series as well.
*/
if (dmarq) {
if (dmarq & DMA_1_REQ) { /* DMA channel 1 request? */
reason = dma_cycle (ch1, PAMAP); /* do one DMA cycle using port A map */
if (reason == SCPE_OK) /* cycle OK? */
dmarq = calc_dma (); /* recalc DMA requests */
else
break; /* cycle failed, so stop */
}
if ((dmarq & (DMA_1_REQ | DMA_2_REQ)) == DMA_2_REQ) { /* DMA channel 1 idle and channel 2 request? */
reason = dma_cycle (ch2, PBMAP); /* do one DMA cycle using port B map */
if (reason == SCPE_OK) /* cycle OK? */
dmarq = calc_dma (); /* recalc DMA requests */
else
break; /* cycle failed, so stop */
}
if (dmarq) /* DMA request still pending? */
continue; /* service it before instruction execution */
intrq = calc_int (); /* recalc interrupts */
}
if (intrq && ion_defer) /* interrupt pending but deferred? */
ion_defer = calc_defer (); /* confirm deferral */
/* Check for pending interrupt request.
Interrupt recognition is controlled by three state variables: "ion",
"ion_defer", and "intrq". "ion" corresponds to the INTSYS flip-flop in the
1000 CPU, "ion_defer" corresponds to the INTEN flip-flop, and "intrq"
corresponds to the NRMINT flip-flop. STF 00 and CLF 00 set and clear INTSYS,
turning the interrupt system on and off. Micro-orders ION and IOFF set and
clear INTEN, deferring or allowing certain interrupts. An IRQ signal from a
device, qualified by the corresponding PRL signal, will set NRMINT to request
a normal interrupt; an IOFF or IAK will clear it.
Under simulation, "ion" is controlled by STF/CLF 00. "ion_defer" is set or
cleared as appropriate by the individual instruction simulators. "intrq" is
set to the successfully interrupting device's select code, or to zero if
there is no qualifying interrupt request.
Presuming PRL is set to allow priority to an interrupting device:
1. Power fail (SC 04) may interrupt if "ion_defer" is clear; this is not
conditional on "ion" being set.
2. Memory protect (SC 05) may interrupt if "ion" is set; this is not
conditional on "ion_defer" being clear.
3. Parity error (SC 05) may interrupt always; this is not conditional on
"ion" being set or "ion_defer" being clear.
4. All other devices (SC 06 and up) may interrupt if "ion" is set and
"ion_defer" is clear.
Qualification with "ion" is performed by "calc_int", except for case 2, which
is qualified by the MP abort handler above (because qualification occurs on
the MP card, rather than in the CPU). Therefore, we need only qualify by
"ion_defer" here.
*/
if (intrq && ((intrq == PRO) || !ion_defer)) { /* interrupt request? */
if (sim_brk_summ && /* any breakpoints? */
sim_brk_test (intrq, SWMASK ('E') | /* unconditional or right type for DMS? */
(dms_enb ? SWMASK ('S') : SWMASK ('N')))) {
reason = STOP_IBKPT; /* stop simulation */
break;
}
intaddr = intrq; /* save int addr in CIR */
intrq = 0; /* clear request */
ion_defer = TRUE; /* defer interrupts */
iotrap = 1; /* mark as I/O trap cell instr */
if (dms_enb) /* dms enabled? */
dms_sr = dms_sr | MST_ENBI; /* set in status */
else /* not enabled */
dms_sr = dms_sr & ~MST_ENBI; /* clear in status */
if (dms_ump) { /* user map enabled at interrupt? */
dms_sr = dms_sr | MST_UMPI; /* set in status */
dms_ump = SMAP; /* switch to system map */
}
else /* system map enabled at interrupt */
dms_sr = dms_sr & ~MST_UMPI; /* clear in status */
IR = ReadW (intaddr); /* get trap cell instruction */
devdisp (intaddr, ioIAK, (uint16) IR); /* acknowledge interrupt */
if (intaddr != PRO) /* not MP interrupt? */
protio (dtab [intaddr], ioIAK, IR); /* send IAK for device to MP too */
}
else { /* normal instruction */
iotrap = 0; /* not a trap cell instruction */
if (sim_brk_summ && /* any breakpoints? */
sim_brk_test (PC, SWMASK ('E') | /* unconditional or */
(dms_enb ? /* correct type for DMS state? */
(dms_ump ?
SWMASK ('U') : SWMASK ('S')) :
SWMASK ('N')))) {
reason = STOP_IBKPT; /* stop simulation */
break;
}
if (mp_evrff) /* violation register enabled */
mp_viol = PC; /* update with current PC */
IR = ReadW (PC); /* fetch instr */
PC = (PC + 1) & VAMASK;
ion_defer = FALSE;
}
sim_interval = sim_interval - 1; /* count instruction */
/* Instruction decode. The 21MX does a 256-way decode on IR<15:8>
15 14 13 12 11 10 09 08 instruction
x <-!= 0-> x x x x memory reference
0 0 0 0 x 0 x x shift
0 0 0 0 x 0 x x alter-skip
1 0 0 0 x 1 x x IO
1 0 0 0 0 0 x 0 extended arithmetic
1 0 0 0 0 0 0 1 divide (decoded as 100400)
1 0 0 0 1 0 0 0 double load (decoded as 104000)
1 0 0 0 1 0 0 1 double store (decoded as 104400)
1 0 0 0 1 0 1 0 extended instr group 0 (A/B must be set)
1 0 0 0 x 0 1 1 extended instr group 1 (A/B ignored) */
absel = (IR & I_AB) ? 1 : 0; /* get A/B select */
switch ((IR >> 8) & 0377) { /* decode IR<15:8> */
/* Memory reference instructions */
case 0020:case 0021:case 0022:case 0023:
case 0024:case 0025:case 0026:case 0027:
case 0220:case 0221:case 0222:case 0223:
case 0224:case 0225:case 0226:case 0227:
reason = Ea (IR, &MA, intrq); /* AND */
if (reason != SCPE_OK) /* address failed to resolve? */
break; /* stop execution */
AR = AR & ReadW (MA);
break;
/* JSB is a little tricky. It is possible to generate both an MP and a DM
violation simultaneously, as the MP and MEM cards validate in parallel.
Consider a JSB to a location under the MP fence and on a write-protected
page. This situation must be reported as a DM violation, because it has
priority (SFS 5 and SFC 5 check only the MEVFF, which sets independently of
the MP fence violation). Under simulation, this means that DM violations
must be checked, and the MEVFF must be set, before an MP abort is taken.
This is done by the "mp_dms_jmp" routine.
*/
case 0230:case 0231:case 0232:case 0233:
case 0234:case 0235:case 0236:case 0237:
ion_defer = TRUE; /* defer if JSB,I */
case 0030:case 0031:case 0032:case 0033:
case 0034:case 0035:case 0036:case 0037:
reason = Ea (IR, &MA, intrq); /* JSB */
if (reason != SCPE_OK) /* address failed to resolve? */
break; /* stop execution */
mp_dms_jmp (MA, jsb_plb); /* validate jump address */
WriteW (MA, PC); /* store PC */
PCQ_ENTRY;
PC = (MA + 1) & VAMASK; /* jump */
break;
case 0040:case 0041:case 0042:case 0043:
case 0044:case 0045:case 0046:case 0047:
case 0240:case 0241:case 0242:case 0243:
case 0244:case 0245:case 0246:case 0247:
reason = Ea (IR, &MA, intrq); /* XOR */
if (reason != SCPE_OK) /* address failed to resolve? */
break; /* stop execution */
AR = AR ^ ReadW (MA);
break;
/* CPU idle processing.
The 21xx/1000 CPUs have no "wait for interrupt" instruction. Idling in HP
operating systems consists of sitting in "idle loops" that end with JMP
instructions. We test for certain known patterns when a JMP instruction is
executed to decide if the simulator should idle.
Idling must not occur if an interrupt is pending. As mentioned in the
"General Notes" above, HP CPUs will defer interrupts if certain instructions
are executed. OS interrupt handlers exit via such deferring instructions.
If there is a pending interrupt when the OS is otherwise idle, the idle loop
will execute one instruction before reentering the interrupt handler. If we
call sim_idle() in this case, we will lose interrupts.
Consider the situation in RTE. Under simulation, the TTY and CLK events are
co-scheduled, with the CLK expiring one instruction after the TTY. When the
TTY interrupts, $CIC in RTE is entered. One instruction later, the CLK
expires and posts its interrupt, but it is not immediately handled, because
the JSB $CIC,I / JMP $CIC0,I / SFS 0,C instruction entry sequence continually
defers interrupts until the interrupt system is turned off. When $CIC
returns via $IRT, one instruction of the idle loop is executed, even though
the CLK interrupt is still pending, because the UJP instruction used to
return also defers interrupts.
If sim_idle() is called at this point, the simulator will sleep when it
should be handling the pending CLK interrupt. When it awakes, TTY expiration
will be moved forward to the next instruction. The still-pending CLK
interrupt will then be recognized, and $CIC will be entered. But the TTY and
then the CLK will then expire and attempt to interrupt again, although they
are deferred by the $CIC entry sequence. This causes the second CLK
interrupt to be missed, as processing of the first one is just now being
started.
Similarly, at the end of the CLK handling, the TTY interrupt is still
pending. When $IRT returns to the idle loop, sim_idle() would be called
again, so the TTY and then CLK interrupt a third time. Because the second
TTY interrupt is still pending, $CIC is entered, but the third TTY interrupt
is lost.
We solve this problem by testing for a pending interrupt before calling
sim_idle(). The system isn't really quiescent if it is just about to handle
an interrupt.
*/
case 0250:case 0251:case 0252:case 0253:
case 0254:case 0255:case 0256:case 0257:
ion_defer = TRUE; /* defer if JMP,I */
case 0050:case 0051:case 0052:case 0053:
case 0054:case 0055:case 0056:case 0057:
reason = Ea (IR, &MA, intrq); /* JMP */
if (reason != SCPE_OK) /* address failed to resolve? */
break; /* stop execution */
mp_dms_jmp (MA, 0); /* validate jump addr */
PCQ_ENTRY;
PC = MA; /* jump */
/* Idle conditions by operating system:
RTE-6/VM:
- ISZ <n> / JMP *-1
- mp_fence = 0
- XEQT (address 1717B) = 0
- DMS on with system map enabled
- RTE verification: TBG (address 1674B) = CLK select code
RTE though RTE-IVB:
- JMP *
- mp_fence = 0
- XEQT (address 1717B) = 0
- DMS on with user map enabled (RTE-III through RTE-IVB only)
- RTE verification: TBG (address 1674B) = CLK select code
DOS through DOS-III:
- STF 0 / CCA / CCB / JMP *-3
- DOS verification: A = B = -1, address 40B = -64, address 67B = +64
- Note that in DOS, the TBG is set to 100 milliseconds
*/
if ((sim_idle_enab) && (intrq == 0)) /* idle enabled w/o pending irq? */
if (((PC == err_PC) || /* RTE through RTE-IVB */
((PC == (err_PC - 1)) && /* RTE-6/VM */
((ReadW (PC) & I_MRG) == I_ISZ))) && /* RTE jump target */
(mp_fence == CLEAR) && (M [xeqt] == 0) && /* RTE idle indications */
(M [tbg] == clk_dib.select_code) || /* RTE verification */
(PC == (err_PC - 3)) && /* DOS through DOS-III */
(ReadW (PC) == I_STF) && /* DOS jump target */
(AR == 0177777) && (BR == 0177777) && /* DOS idle indication */
(M [m64] == 0177700) && /* DOS verification */
(M [p64] == 0000100)) /* DOS verification */
sim_idle (TMR_POLL, FALSE); /* idle the simulator */
break;
case 0060:case 0061:case 0062:case 0063:
case 0064:case 0065:case 0066:case 0067:
case 0260:case 0261:case 0262:case 0263:
case 0264:case 0265:case 0266:case 0267:
reason = Ea (IR, &MA, intrq); /* IOR */
if (reason != SCPE_OK) /* address failed to resolve? */
break; /* stop execution */
AR = AR | ReadW (MA);
break;
case 0070:case 0071:case 0072:case 0073:
case 0074:case 0075:case 0076:case 0077:
case 0270:case 0271:case 0272:case 0273:
case 0274:case 0275:case 0276:case 0277:
reason = Ea (IR, &MA, intrq); /* ISZ */
if (reason != SCPE_OK) /* address failed to resolve? */
break; /* stop execution */
t = (ReadW (MA) + 1) & DMASK;
WriteW (MA, t);
if (t == 0)
PC = (PC + 1) & VAMASK;
break;
case 0100:case 0101:case 0102:case 0103:
case 0104:case 0105:case 0106:case 0107:
case 0300:case 0301:case 0302:case 0303:
case 0304:case 0305:case 0306:case 0307:
reason = Ea (IR, &MA, intrq); /* ADA */
if (reason != SCPE_OK) /* address failed to resolve? */
break; /* stop execution */
v1 = ReadW (MA);
t = AR + v1;
if (t > DMASK)
E = 1;
if (((~AR ^ v1) & (AR ^ t)) & SIGN)
O = 1;
AR = t & DMASK;
break;
case 0110:case 0111:case 0112:case 0113:
case 0114:case 0115:case 0116:case 0117:
case 0310:case 0311:case 0312:case 0313:
case 0314:case 0315:case 0316:case 0317:
reason = Ea (IR, &MA, intrq); /* ADB */
if (reason != SCPE_OK) /* address failed to resolve? */
break; /* stop execution */
v1 = ReadW (MA);
t = BR + v1;
if (t > DMASK)
E = 1;
if (((~BR ^ v1) & (BR ^ t)) & SIGN)
O = 1;
BR = t & DMASK;
break;
case 0120:case 0121:case 0122:case 0123:
case 0124:case 0125:case 0126:case 0127:
case 0320:case 0321:case 0322:case 0323:
case 0324:case 0325:case 0326:case 0327:
reason = Ea (IR, &MA, intrq); /* CPA */
if (reason != SCPE_OK) /* address failed to resolve? */
break; /* stop execution */
if (AR != ReadW (MA))
PC = (PC + 1) & VAMASK;
break;
case 0130:case 0131:case 0132:case 0133:
case 0134:case 0135:case 0136:case 0137:
case 0330:case 0331:case 0332:case 0333:
case 0334:case 0335:case 0336:case 0337:
reason = Ea (IR, &MA, intrq); /* CPB */
if (reason != SCPE_OK) /* address failed to resolve? */
break; /* stop execution */
if (BR != ReadW (MA))
PC = (PC + 1) & VAMASK;
break;
case 0140:case 0141:case 0142:case 0143:
case 0144:case 0145:case 0146:case 0147:
case 0340:case 0341:case 0342:case 0343:
case 0344:case 0345:case 0346:case 0347:
reason = Ea (IR, &MA, intrq); /* LDA */
if (reason != SCPE_OK) /* address failed to resolve? */
break; /* stop execution */
AR = ReadW (MA);
break;
case 0150:case 0151:case 0152:case 0153:
case 0154:case 0155:case 0156:case 0157:
case 0350:case 0351:case 0352:case 0353:
case 0354:case 0355:case 0356:case 0357:
reason = Ea (IR, &MA, intrq); /* LDB */
if (reason != SCPE_OK) /* address failed to resolve? */
break; /* stop execution */
BR = ReadW (MA);
break;
case 0160:case 0161:case 0162:case 0163:
case 0164:case 0165:case 0166:case 0167:
case 0360:case 0361:case 0362:case 0363:
case 0364:case 0365:case 0366:case 0367:
reason = Ea (IR, &MA, intrq); /* STA */
if (reason != SCPE_OK) /* address failed to resolve? */
break; /* stop execution */
WriteW (MA, AR);
break;
case 0170:case 0171:case 0172:case 0173:
case 0174:case 0175:case 0176:case 0177:
case 0370:case 0371:case 0372:case 0373:
case 0374:case 0375:case 0376:case 0377:
reason = Ea (IR, &MA, intrq); /* STB */
if (reason != SCPE_OK) /* address failed to resolve? */
break; /* stop execution */
WriteW (MA, BR);
break;
/* Alter/skip instructions */
case 0004:case 0005:case 0006:case 0007:
case 0014:case 0015:case 0016:case 0017:
skip = 0; /* no skip */
if (IR & 000400) /* CLx */
t = 0;
else
t = ABREG[absel];
if (IR & 001000) /* CMx */
t = t ^ DMASK;
if (IR & 000001) { /* RSS? */
if ((IR & 000040) && (E != 0)) /* SEZ,RSS */
skip = 1;
if (IR & 000100) /* CLE */
E = 0;
if (IR & 000200) /* CME */
E = E ^ 1;
if (((IR & 000030) == 000030) && /* SSx,SLx,RSS */
((t & 0100001) == 0100001))
skip = 1;
if (((IR & 000030) == 000020) && /* SSx,RSS */
((t & SIGN) != 0))
skip = 1;
if (((IR & 000030) == 000010) && /* SLx,RSS */
((t & 1) != 0))
skip = 1;
if (IR & 000004) { /* INx */
t = (t + 1) & DMASK;
if (t == 0)
E = 1;
if (t == SIGN)
O = 1;
}
if ((IR & 000002) && (t != 0)) /* SZx,RSS */
skip = 1;
if ((IR & 000072) == 0) /* RSS */
skip = 1;
} /* end if RSS */
else {
if ((IR & 000040) && (E == 0)) /* SEZ */
skip = 1;
if (IR & 000100) /* CLE */
E = 0;
if (IR & 000200) /* CME */
E = E ^ 1;
if ((IR & 000020) && /* SSx */
((t & SIGN) == 0))
skip = 1;
if ((IR & 000010) && /* SLx */
((t & 1) == 0))
skip = 1;
if (IR & 000004) { /* INx */
t = (t + 1) & DMASK;
if (t == 0)
E = 1;
if (t == SIGN)
O = 1;
}
if ((IR & 000002) && (t == 0)) /* SZx */
skip = 1;
} /* end if ~RSS */
ABREG[absel] = (uint16) t; /* store result */
PC = (PC + skip) & VAMASK; /* add in skip */
break; /* end if alter/skip */
/* Shift instructions */
case 0000:case 0001:case 0002:case 0003:
case 0010:case 0011:case 0012:case 0013:
t = shift (ABREG[absel], IR & 01000, IR >> 6); /* do first shift */
if (IR & 000040) /* CLE */
E = 0;
if ((IR & 000010) && ((t & 1) == 0)) /* SLx */
PC = (PC + 1) & VAMASK;
ABREG[absel] = (uint16) shift (t, IR & 00020, IR); /* do second shift */
break; /* end if shift */
/* I/O instructions */
case 0204:case 0205:case 0206:case 0207:
case 0214:case 0215:case 0216:case 0217:
reason = iogrp (IR, iotrap); /* execute instr */
break; /* end if I/O */
/* Extended arithmetic */
case 0200: /* EAU group 0 */
case 0201: /* divide */
case 0202: /* EAU group 2 */
case 0210: /* DLD */
case 0211: /* DST */
reason = cpu_eau (IR, intrq); /* extended arith */
break;
/* Extended instructions */
case 0212: /* UIG 0 extension */
reason = cpu_uig_0 (IR, intrq, iotrap); /* extended opcode */
break;
case 0203: /* UIG 1 extension */
case 0213:
reason = cpu_uig_1 (IR, intrq, iotrap); /* extended opcode */
break;
} /* end case IR */
if (reason == NOTE_IOG) { /* I/O instr exec? */
dmarq = calc_dma (); /* recalc DMA masks */
intrq = calc_int (); /* recalc interrupts */
reason = SCPE_OK; /* continue */
}
else if (reason == NOTE_INDINT) { /* intr pend during indir? */
PC = err_PC; /* back out of inst */
reason = SCPE_OK; /* continue */
}
} /* end while */
/* Simulation halted */
if (iotrap && (reason == STOP_HALT)) /* HLT in trap cell? */
MR = intaddr; /* M = interrupt address */
else /* normal HLT */
MR = (PC - 1) & VAMASK; /* M = P - 1 */
TR = ReadTAB (MR); /* T = last word fetched */
saved_MR = MR; /* save for T cmd update */
if (reason == STOP_HALT) /* programmed halt? */
cpu_set_ldr (NULL, FALSE, NULL, NULL); /* disable loader (after T is read) */
else /* simulation stop */
PC = err_PC; /* back out instruction */
dms_upd_sr (); /* update dms_sr */
dms_upd_vr (MR); /* update dms_vr */
pcq_r->qptr = pcq_p; /* update pc q ptr */
if (dms_enb) /* DMS enabled? */
if (dms_ump) /* set default */
sim_brk_dflt = SWMASK ('U'); /* breakpoint type */
else /* to current */
sim_brk_dflt = SWMASK ('S'); /* map mode */
else /* DMS disabled */
sim_brk_dflt = SWMASK ('N'); /* set breakpoint type to non-DMS */
return reason; /* return status code */
}
/* Resolve indirect addresses.
An indirect chain is followed until a direct address is obtained. Under
simulation, a maximum number of indirect levels are allowed (typically 16),
after which the instruction will be aborted.
If the memory protect feature is present, an indirect counter is used that
allows a pending interrupt to be serviced if more than three levels of
indirection are encountered. If MP jumper W6 ("INT") is out and MP is
enabled, then pending interrupts are serviced immediately. When employing
the indirect counter, the hardware clears a pending interrupt deferral after
the third indirection and aborts the instruction after the fourth.
*/
t_stat resolve (uint32 MA, uint32 *addr, uint32 irq)
{
uint32 i;
t_bool pending = (irq && !(mp_unit.flags & DEV_DIS));
t_bool int_enable = ((mp_unit.flags & UNIT_MP_INT) && mp_control);
for (i = 0; (i < ind_max) && (MA & I_IA); i++) { /* resolve multilevel */
if (pending) { /* interrupt pending and MP enabled? */
if ((i == 2) || int_enable) /* 3rd level indirect or INT out? */
ion_defer = FALSE; /* reenable interrrupts */
if ((i > 2) || int_enable) /* 4th or higher or INT out? */
return NOTE_INDINT; /* break out now */
}
MA = ReadW (MA & VAMASK); /* follow address chain */
}
if (MA & I_IA) /* indirect loop? */
return STOP_IND; /* stop simulation */
*addr = MA;
return SCPE_OK;
}
/* Get effective address from IR */
static t_stat Ea (uint32 IR, uint32 *addr, uint32 irq)
{
uint32 MA;
MA = IR & (I_IA | I_DISP); /* ind + disp */
if (IR & I_CP) /* current page? */
MA = ((PC - 1) & I_PAGENO) | MA; /* merge in page from PC */
return resolve (MA, addr, irq); /* resolve indirects */
}
/* Shift micro operation */
static uint32 shift (uint32 t, uint32 flag, uint32 op)
{
uint32 oldE;
op = op & 07; /* get shift op */
if (flag) { /* enabled? */
switch (op) { /* case on operation */
case 00: /* signed left shift */
return ((t & SIGN) | ((t << 1) & 077777));
case 01: /* signed right shift */
return ((t & SIGN) | (t >> 1));
case 02: /* rotate left */
return (((t << 1) | (t >> 15)) & DMASK);
case 03: /* rotate right */
return (((t >> 1) | (t << 15)) & DMASK);
case 04: /* left shift, 0 sign */
return ((t << 1) & 077777);
case 05: /* ext right rotate */
oldE = E;
E = t & 1;
return ((t >> 1) | (oldE << 15));
case 06: /* ext left rotate */
oldE = E;
E = (t >> 15) & 1;
return (((t << 1) | oldE) & DMASK);
case 07: /* rotate left four */
return (((t << 4) | (t >> 12)) & DMASK);
} /* end case */
} /* end if */
if (op == 05) /* disabled ext rgt rot */
E = t & 1;
if (op == 06) /* disabled ext lft rot */
E = (t >> 15) & 1;
return t; /* input unchanged */
}
/* I/O instruction decode.
If memory protect is enabled, and the instruction is not in a trap cell, then
HLT instructions are illegal and will cause a memory protect violation. If
jumper W7 (SEL1) is in, then all other I/O instructions are legal; if W7 is
out, then only I/O instructions to select code 1 are legal, and I/O to other
select codes will cause a violation.
If the instruction is allowed, then the I/O signal corresponding to the
instruction is determined, the state of the interrupt deferral flag is set.
Then the signal is dispatched to the device simulator indicated by the target
select code. The return value is split into status and data values, with the
latter containing the SKF signal state or data to be returned in the A or B
registers.
Implementation notes:
1. If the H/C (hold/clear flag) bit is set, then the ioCLF signal is added
(not ORed) to the base signal derived from the I/O instruction.
2. ioNONE is dispatched for HLT instructions because although HLT does not
assert any backplane signals, the H/C bit may be set. If it is, then the
result will be to dispatch ioCLF.
3. Device simulators return either ioSKF or ioNONE in response to an SFC or
SFS signal. ioSKF means that the instruction should skip. Because
device simulators return the "data" parameter value by default, we
initialize that parameter to ioNONE to ensure that a simulator that does
not implement SFC or SFS does not skip, which is the correct action for
an interface that does not drive the SKF signal.
4. STF/CLF and STC/CLC share sub-opcode values and must be further decoded
by the state of instruction register bits 9 and 11, respectively.
5. We return NOTE_IOG for normal status instead of SCPE_OK to request that
interrupts be recalculated at the end of the instruction (execution of
the I/O group instructions can change the interrupt priority chain).
*/
t_stat iogrp (uint32 ir, uint32 iotrap)
{
/* Translation for I/O subopcodes: soHLT, soFLG, soSFC, soSFS, soMIX, soLIX, soOTX, soCTL */
static const IOSIGNAL generate_signal [] = { ioNONE, ioSTF, ioSFC, ioSFS, ioIOI, ioIOI, ioIOO, ioSTC };
const uint32 dev = ir & I_DEVMASK; /* device select code */
const uint32 sop = I_GETIOOP (ir); /* I/O subopcode */
const uint32 ab = (ir & I_AB) != 0; /* A/B register select */
const t_bool clf = (ir & I_HC) != 0; /* H/C flag select */
uint16 iodata = (uint16) ioNONE; /* initialize for SKF test */
uint32 ioreturn;
t_stat iostat;
IOCYCLE signal_set;
if (!iotrap && mp_control && /* instr not in trap cell and MP on? */
((sop == soHLT) || /* and is HLT? */
((dev != OVF) && (mp_unit.flags & UNIT_MP_SEL1)))) { /* or is not SC 01 and SEL1 out? */
if (sop == soLIX) /* MP violation; is LIA/B instruction? */
ABREG [ab] = 0; /* A/B writes anyway */
MP_ABORT (err_PC); /* MP abort */
}
signal_set = generate_signal [sop]; /* generate I/O signal from instruction */
ion_defer = defer_tab [sop]; /* defer depending on instruction */
if (sop == soOTX) /* OTA/B instruction? */
iodata = ABREG [ab]; /* pass A/B register value */
else if ((sop == soCTL) && (ir & I_CTL)) /* CLC instruction? */
signal_set = ioCLC; /* change STC to CLC signal */
if ((sop == soFLG) && clf) /* CLF instruction? */
signal_set = ioCLF; /* change STF to CLF signal */
else if (clf) /* CLF with another instruction? */
signal_set = signal_set | ioCLF; /* add CLF signal */
ioreturn = devdisp (dev, signal_set, iodata); /* dispatch I/O signal */
iostat = IOSTATUS (ioreturn); /* extract status */
iodata = IODATA (ioreturn); /* extract return data value */
if (((sop == soSFC) || (sop == soSFS)) && /* testing flag state? */
((IOSIGNAL) iodata == ioSKF)) /* and SKF asserted? */
PC = (PC + 1) & VAMASK; /* bump P to skip next instruction */
else if (sop == soLIX) /* LIA/B instruction? */
ABREG [ab] = iodata; /* load returned data */
else if (sop == soMIX) /* MIA/B instruction? */
ABREG [ab] = ABREG [ab] | iodata; /* merge returned data */
else if (sop == soHLT) { /* HLT instruction? */
return STOP_HALT; /* return halt status */
}
if (iostat == SCPE_OK) /* normal status? */
return NOTE_IOG; /* request interrupt recalc */
else /* abnormal status */
return iostat; /* return it */
}
/* Device I/O signal dispatcher */
static uint32 devdisp (uint32 select_code, IOCYCLE signal_set, uint16 data)
{
return dtab [select_code]->io_handler (dtab [select_code],
signal_set,
IORETURN (SCPE_OK, data));
}
/* Calculate DMA requests */
static uint32 calc_dma (void)
{
uint32 r = 0;
if (dma [ch1].xferen && SRQ (dma [ch1].cw1 & I_DEVMASK)) /* check DMA1 cycle */
r = r | DMA_1_REQ;
if (dma [ch2].xferen && SRQ (dma [ch2].cw1 & I_DEVMASK)) /* check DMA2 cycle */
r = r | DMA_2_REQ;
return r;
}
/* Determine whether a pending interrupt deferral should be inhibited.
Execution of certain instructions generally cause a pending interrupt to be
deferred until the succeeding instruction completes. However, the interrupt
deferral rules differ on the 21xx vs. the 1000.
The 1000 always defers until the completion of the instruction following a
deferring instruction. The 21xx defers unless the following instruction is
an MRG instruction other than JMP or JMP,I or JSB,I. If it is, then the
deferral is inhibited, i.e., the pending interrupt will be serviced.
See the "Set Phase Logic Flowchart," transition from phase 1A to phase 1B,
and the "Theory of Operation," "Control Section Detailed Theory," "Phase
Control Logic," "Phase 1B" paragraph in the Model 2100A Computer Installation
and Maintenance Manual for details.
*/
t_bool calc_defer (void)
{
uint16 IR;
if (UNIT_CPU_FAMILY == UNIT_FAMILY_21XX) { /* 21xx series? */
IR = ReadW (PC); /* prefetch next instr */
if (((IR & I_MRG & ~I_AB) != 0000000) && /* is MRG instruction? */
((IR & I_MRG_I) != I_JSB_I) && /* but not JSB,I? */
((IR & I_MRG) != I_JMP)) /* and not JMP or JMP,I? */
return FALSE; /* yes, so inhibit deferral */
else
return TRUE; /* no, so allow deferral */
}
else
return TRUE; /* 1000 always allows deferral */
}
/* Calculate interrupt requests.
The interrupt request (IRQ) of the highest-priority device for which all
higher-priority PRL bits are set is granted. That is, there must be an
unbroken chain of priority to a device requesting an interrupt for that
request to be granted.
A device sets its IRQ bit to request an interrupt, and it clears its PRL bit
to prevent lower-priority devices from interrupting. IRQ is cleared by an
interrupt acknowledge (IAK) signal. PRL generally remains low while a
device's interrupt service routine is executing to prevent preemption.
IRQ and PRL indicate one of four possible states for a device:
IRQ PRL Device state
--- --- ----------------------
0 1 Not interrupting
1 0 Interrupt requested
0 0 Interrupt acknowledged
1 1 (not allowed)
Note that PRL must be dropped when requesting an interrupt (IRQ set). This
is a hardware requirement of the 1000 series. The IRQ lines from the
backplane are not priority encoded. Instead, the PRL chain expresses the
priority by allowing only one IRQ line to be active at a time. This allows a
simple pull-down encoding of the CIR inputs.
The end of priority chain is marked by the highest-priority (lowest-order)
bit that is clear. The device corresponding to that bit is the only device
that may interrupt (a higher priority device that had IRQ set would also have
had PRL set, which is a state violation). We calculate a priority mask by
ANDing the complement of the PRL bits with an increment of the PRL bits.
Only the lowest-order bit will differ. For example:
dev_prl : ...1 1 0 1 1 0 1 1 1 1 1 1 (PRL denied for SC 06 and 11)
dev_prl + 1 : ...1 1 0 1 1 1 0 0 0 0 0 0
~dev_prl : ...0 0 1 0 0 1 0 0 0 0 0 0
ANDed value : ...0 0 0 0 0 1 0 0 0 0 0 0 (break is at SC 06)
The interrupt requests are then ANDed with the priority mask to determine if
a request is pending:
pri mask : ...0 0 0 0 0 1 0 0 0 0 0 0 (allowed interrupt source)
dev_irq : ...0 0 1 0 0 1 0 0 0 0 0 0 (devices requesting interrupts)
ANDed value : ...0 0 0 0 0 1 0 0 0 0 0 0 (request to grant)
The select code corresponding to the granted request is then returned to the
caller.
If ION is clear, only power fail (SC 04) and parity error (SC 05) are
eligible to interrupt (memory protect shares SC 05, but qualification occurs
in the MP abort handler, so if SC 05 is interrupting when ION is clear, it
must be a parity error interrupt).
*/
uint32 calc_int (void)
{
uint32 sc, pri_mask [2], req_grant [2];
pri_mask [0] = ~dev_prl [0] & (dev_prl [0] + 1); /* calculate lower priority mask */
req_grant [0] = pri_mask [0] & dev_irq [0]; /* calculate lower request to grant */
if (ion) /* interrupt system on? */
if ((req_grant [0] == 0) && (pri_mask [0] == 0)) { /* no requests in lower set and PRL unbroken? */
pri_mask [1] = ~dev_prl [1] & (dev_prl [1] + 1); /* calculate upper priority mask */
req_grant [1] = pri_mask [1] & dev_irq [1]; /* calculate upper request to grant */
}
else /* lower set has request */
req_grant [1] = 0; /* no grants to upper set */
else { /* interrupt system off */
req_grant [0] = req_grant [0] & /* only PF and PE can interrupt */
(BIT_M (PWR) | BIT_M (PRO));
req_grant [1] = 0;
}
if (req_grant [0]) /* device in lower half? */
for (sc = 0; sc <= 31; sc++) /* determine interrupting select code */
if (req_grant [0] & 1) /* grant this request? */
return sc; /* return this select code */
else /* not this one */
req_grant [0] = req_grant [0] >> 1; /* position next request */
else if (req_grant [1]) /* device in upper half */
for (sc = 32; sc <= 63; sc++) /* determine interrupting select code */
if (req_grant [1] & 1) /* grant this request? */
return sc; /* return this select code */
else /* not this one */
req_grant [1] = req_grant [1] >> 1; /* position next request */
return 0; /* no interrupt granted */
}
/* Memory access routines.
These routines access memory for reads and writes. They validate the
accesses for MP and MEM violations, if enabled. The following routines are
provided:
- ReadPW : Read a word using a physical address
- ReadB : Read a byte using the current map
- ReadBA : Read a byte using the alternate map
- ReadW : Read a word using the current map
- ReadWA : Read a word using the alternate map
- ReadIO : Read a word using the specified map without protection
- ReadTAB : Read a word using the current map without protection
- WritePW : Write a word using a physical address
- WriteB : Write a byte using the current map
- WriteBA : Write a byte using the alternate map
- WriteW : Write a word using the current map
- WriteWA : Write a word using the alternate map
- WriteIO : Write a word using the specified map without protection
The memory protect (MP) and memory expansion module (MEM) accessories provide
a protected mode that guards against improper accesses by user programs.
They may be enabled or disabled independently, although protection requires
that both be enabled. MP checks that memory writes do not fall below the
Memory Protect Fence Register (MPFR) value, and MEM checks that read/write
protection rules on the target page are compatible with the access desired.
If either check fails, and MP is enabled, then the request is aborted.
Each mapped routine calls "dms" if DMS is enabled to translate the logical
address supplied to a physical address. "dms" performs a protection check
and aborts without returning if the check fails. The write routines perform
an additional memory-protect check and abort if a violation occurs (so, to
pass, a page must be writable AND the target must be above the MP fence).
Note that MP uses a lower bound of 2 for memory writes, allowing unrestricted
access to the A and B registers (addressed as locations 0 and 1).
*/
#define MP_TEST(va) (mp_control && ((va) >= 2) && ((va) < mp_fence))
/* Read a word using a physical address */
uint16 ReadPW (uint32 pa)
{
if (pa <= 1) /* read locations 0 or 1? */
return ABREG[pa]; /* return A/B register */
else /* location >= 2 */
return M[pa]; /* return physical memory value */
}
/* Read a byte using the current map */
uint8 ReadB (uint32 va)
{
int32 pa;
if (dms_enb) /* MEM enabled? */
pa = dms (va >> 1, dms_ump, RDPROT); /* translate address */
else /* MEM disabled */
pa = va >> 1; /* use logical as physical address */
if (va & 1) /* low byte addressed? */
return (ReadPW (pa) & 0377); /* mask to lower byte */
else /* high byte addressed */
return ((ReadPW (pa) >> 8) & 0377); /* position higher byte and mask */
}
/* Read a byte using the alternate map */
uint8 ReadBA (uint32 va)
{
uint32 pa;
if (dms_enb) /* MEM enabled? */
pa = dms (va >> 1, dms_ump ^ MAP_LNT, RDPROT); /* translate address using alternate map */
else /* MEM disabled */
pa = va >> 1; /* use logical as physical address */
if (va & 1) /* low byte addressed? */
return (ReadPW (pa) & 0377); /* mask to lower byte */
else /* high byte addressed */
return ((ReadPW (pa) >> 8) & 0377); /* position higher byte and mask */
}
/* Read a word using the current map */
uint16 ReadW (uint32 va)
{
uint32 pa;
if (dms_enb) /* MEM enabled? */
pa = dms (va, dms_ump, RDPROT); /* translate address */
else /* MEM disabled */
pa = va; /* use logical as physical address */
return ReadPW (pa); /* return word */
}
/* Read a word using the alternate map */
uint16 ReadWA (uint32 va)
{
uint32 pa;
if (dms_enb) /* MEM enabled? */
pa = dms (va, dms_ump ^ MAP_LNT, RDPROT); /* translate address using alternate map */
else /* MEM disabled */
pa = va; /* use logical as physical address */
return ReadPW (pa); /* return word */
}
/* Read a word using the specified map without protection */
uint16 ReadIO (uint32 va, uint32 map)
{
uint32 pa;
if (dms_enb) /* MEM enabled? */
pa = dms (va, map, NOPROT); /* translate address with no protection */
else /* MEM disabled */
pa = va; /* use logical as physical address */
return M[pa]; /* return word without A/B interception */
}
/* Read a word using the current map without protection */
static uint16 ReadTAB (uint32 va)
{
uint32 pa;
if (dms_enb) /* MEM enabled? */
pa = dms (va, dms_ump, NOPROT); /* translate address with no protection */
else /* MEM disabled */
pa = va; /* use logical as physical address */
return ReadPW (pa); /* return word */
}
/* Write a word using a physical address */
void WritePW (uint32 pa, uint32 dat)
{
if (pa <= 1) /* write locations 0 or 1? */
ABREG[pa] = dat & DMASK; /* store A/B register */
else if (pa < fwanxm) /* 2 <= location <= LWA memory? */
M[pa] = dat & DMASK; /* store physical memory value */
return;
}
/* Write a byte using the current map */
void WriteB (uint32 va, uint32 dat)
{
uint32 pa, t;
if (dms_enb) /* MEM enabled? */
pa = dms (va >> 1, dms_ump, WRPROT); /* translate address */
else /* MEM disabled */
pa = va >> 1; /* use logical as physical address */
if (MP_TEST (va >> 1)) /* MPCK? */
MP_ABORT (va >> 1); /* MP violation */
t = ReadPW (pa); /* get word */
if (va & 1) /* low byte addressed? */
t = (t & 0177400) | (dat & 0377); /* merge in lower byte */
else /* high byte addressed */
t = (t & 0377) | ((dat & 0377) << 8); /* position higher byte and merge */
WritePW (pa, t); /* store word */
return;
}
/* Write a byte using the alternate map */
void WriteBA (uint32 va, uint32 dat)
{
uint32 pa, t;
if (dms_enb) { /* MEM enabled? */
dms_viol (va >> 1, MVI_WPR); /* always a violation if protected */
pa = dms (va >> 1, dms_ump ^ MAP_LNT, WRPROT); /* translate address using alternate map */
}
else /* MEM disabled */
pa = va >> 1; /* use logical as physical address */
if (MP_TEST (va >> 1)) /* MPCK? */
MP_ABORT (va >> 1); /* MP violation */
t = ReadPW (pa); /* get word */
if (va & 1) /* low byte addressed? */
t = (t & 0177400) | (dat & 0377); /* merge in lower byte */
else /* high byte addressed */
t = (t & 0377) | ((dat & 0377) << 8); /* position higher byte and merge */
WritePW (pa, t); /* store word */
return;
}
/* Write a word using the current map */
void WriteW (uint32 va, uint32 dat)
{
uint32 pa;
if (dms_enb) /* MEM enabled? */
pa = dms (va, dms_ump, WRPROT); /* translate address */
else /* MEM disabled */
pa = va; /* use logical as physical address */
if (MP_TEST (va)) /* MPCK? */
MP_ABORT (va); /* MP violation */
WritePW (pa, dat); /* store word */
return;
}
/* Write a word using the alternate map */
void WriteWA (uint32 va, uint32 dat)
{
int32 pa;
if (dms_enb) { /* MEM enabled? */
dms_viol (va, MVI_WPR); /* always a violation if protected */
pa = dms (va, dms_ump ^ MAP_LNT, WRPROT); /* translate address using alternate map */
}
else /* MEM disabled */
pa = va; /* use logical as physical address */
if (MP_TEST (va)) /* MPCK? */
MP_ABORT (va); /* MP violation */
WritePW (pa, dat); /* store word */
return;
}
/* Write a word using the specified map without protection */
void WriteIO (uint32 va, uint32 dat, uint32 map)
{
uint32 pa;
if (dms_enb) /* MEM enabled? */
pa = dms (va, map, NOPROT); /* translate address with no protection */
else /* MEM disabled */
pa = va; /* use logical as physical address */
if (pa < fwanxm)
M[pa] = dat & DMASK; /* store word without A/B interception */
return;
}
/* Mapped access check.
Returns TRUE if the address will be mapped (presuming MEM is enabled).
*/
static t_bool is_mapped (uint32 va)
{
uint32 dms_fence;
if (va >= 02000) /* above the base bage? */
return TRUE; /* always mapped */
else {
dms_fence = dms_sr & MST_FENCE; /* get BP fence value */
return (dms_sr & MST_FLT) ? (va < dms_fence) : /* below BP fence and lower portion mapped? */
(va >= dms_fence); /* or above BP fence and upper portion mapped? */
}
}
/* DMS relocation.
This routine translates logical into physical addresses. It must be called
only when DMS is enabled, as that condition is not checked. The logical
address, desired map, and desired access type are supplied. If the access is
legal, the mapped physical address is returned; if it is not, then a MEM
violation is indicated.
The current map may be specified by passing "dms_ump" as the "map" parameter,
or a specific map may be used. Normally, read and write accesses pass RDPROT
or WRPROT as the "prot" parameter to request access checking. For DMA
accesses, NOPROT must be passed to inhibit access checks.
This routine checks for read, write, and base-page violations and will call
"dms_viol" as appropriate. The latter routine will abort if MP is enabled,
or will return if protection is off.
*/
static uint32 dms (uint32 va, uint32 map, uint32 prot)
{
uint32 pgn, mpr;
if (va <= 1) /* reference to A/B register? */
return va; /* use address */
if (!is_mapped (va)) { /* unmapped? */
if ((va >= 2) && (prot == WRPROT)) /* base page write access? */
dms_viol (va, MVI_BPG); /* signal a base page violation */
return va; /* use unmapped address */
}
pgn = VA_GETPAG (va); /* get page num */
mpr = dms_map[map + pgn]; /* get map reg */
if (mpr & prot) /* desired access disallowed? */
dms_viol (va, prot); /* signal protection violation */
return (MAP_GETPAG (mpr) | VA_GETOFF (va)); /* return mapped address */
}
/* DMS relocation for console access.
Console access allows the desired map to be specified by switches on the
command line. All protection checks are off for console access.
This routine is called to restore a saved configuration, and mapping is not
used for restoration.
*/
static uint32 dms_cons (t_addr va, int32 sw)
{
uint32 map_sel;
if ((dms_enb == 0) || /* DMS off? */
(sw & (SWMASK ('N') | SIM_SW_REST))) /* no mapping rqst or save/rest? */
return (uint32) va; /* use physical address */
else if (sw & SWMASK ('S'))
map_sel = SMAP;
else if (sw & SWMASK ('U'))
map_sel = UMAP;
else if (sw & SWMASK ('P'))
map_sel = PAMAP;
else if (sw & SWMASK ('Q'))
map_sel = PBMAP;
else /* dflt to log addr, cur map */
map_sel = dms_ump;
if (va >= VASIZE) /* virtual, must be 15b */
return (uint32) MEMSIZE;
else if (dms_enb) /* DMS on? go thru map */
return dms ((uint32) va, map_sel, NOPROT);
else /* else return virtual */
return (uint32) va;
}
/* Memory protect and DMS validation for jumps.
Jumps are a special case of write validation. The target address is treated
as a write, even when no physical write takes place, so jumping to a
write-protected page causes a MEM violation. In addition, a MEM violation is
indicated if the jump is to the unmapped portion of the base page. Finally,
jumping to a location under the memory-protect fence causes an MP violation.
Because the MP and MEM hardware works in parallel, all three violations may
exist concurrently. For example, a JMP to the unmapped portion of the base
page that is write protected and under the MP fence will indicate a
base-page, write, and MP violation, whereas a JMP to the mapped portion will
indicate a write and MP violation (BPV is inhibited by the MEBEN signal). If
MEM and MP violations occur concurrently, the MEM violation takes precedence,
as the SFS and SFC instructions test the MEV flip-flop.
The lower bound of protected memory is passed in the "plb" argument. This
must be either 0 or 2. All violations are qualified by the MPCND signal,
which responds to the lower bound. Therefore, if the lower bound is 2, and
if the part below the base-page fence is unmapped, or if the base page is
write-protected, then a MEM violation will occur only if the access is not to
locations 0 or 1. The instruction set firmware uses a lower bound of 0 for
JMP, JLY, and JPY (and for JSB with W5 out), and of 2 for DJP, SJP, UJP, JRS,
and .GOTO (and JSB with W5 in).
Finally, all violations are inhibited if MP is off (mp_control is CLEAR), and
MEM violations are inhibited if the MEM is disabled.
*/
void mp_dms_jmp (uint32 va, uint32 plb)
{
uint32 violation = 0;
uint32 pgn = VA_GETPAG (va); /* get page number */
if (mp_control) { /* MP on? */
if (dms_enb) { /* MEM on? */
if (dms_map [dms_ump + pgn] & WRPROT) /* page write protected? */
violation = MVI_WPR; /* write violation occured */
if (!is_mapped (va) && (va >= plb)) /* base page target? */
violation = violation | MVI_BPG; /* base page violation occured */
if (violation) /* any violation? */
dms_viol (va, violation); /* signal MEM violation */
}
if ((va >= plb) && (va < mp_fence)) /* jump under fence? */
MP_ABORT (va); /* signal MP violation */
}
return;
}
/* DMS read and write map registers */
uint16 dms_rmap (uint32 mapi)
{
mapi = mapi & MAP_MASK;
return (dms_map[mapi] & ~MAP_RSVD);
}
void dms_wmap (uint32 mapi, uint32 dat)
{
mapi = mapi & MAP_MASK;
dms_map[mapi] = (uint16) (dat & ~MAP_RSVD);
return;
}
/* Process a MEM violation.
A MEM violation will report the cause in the violation register. This occurs
even if the MEM is not in the protected mode (i.e., MP is not enabled). If
MP is enabled, an MP abort is taken with the MEV flip-flop set. Otherwise,
we return to the caller.
*/
void dms_viol (uint32 va, uint32 st)
{
dms_vr = st | dms_upd_vr (va); /* set violation cause in register */
if (mp_control) { /* memory protect on? */
mp_mevff = SET; /* record memory expansion violation */
MP_ABORT (va); /* abort */
}
return;
}
/* Update the MEM violation register.
In hardware, the MEM violation register (VR) is clocked on every memory read,
every memory write above the lower bound of protected memory, and every
execution of a privileged DMS instruction. The register is not clocked when
MP is disabled by an MP or MEM error (i.e., when MEVFF sets or CTL5FF
clears), in order to capture the state of the MEM. In other words, the VR
continually tracks the memory map register accessed plus the MEM state
(MEBEN, MAPON, and USR) until a violation occurs, and then it's "frozen."
Under simulation, we do not have to update the VR on every memory access,
because the visible state is only available via a programmed RVA/B
instruction or via the SCP interface. Therefore, it is sufficient if the
register is updated:
- at a MEM violation (when freezing)
- at an MP violation (when freezing)
- during RVA/B execution (if not frozen)
- before returning to SCP after a simulator stop (if not frozen)
*/
uint32 dms_upd_vr (uint32 va)
{
if (mp_control && (mp_mevff == CLEAR)) { /* violation register unfrozen? */
dms_vr = VA_GETPAG (va) | /* set map address */
(dms_enb ? MVI_MEM : 0) | /* and MEM enabled */
(dms_ump ? MVI_UMP : 0); /* and user map enabled */
if (is_mapped (va)) /* is addressed mapped? */
dms_vr = dms_vr | MVI_MEB; /* ME bus is enabled */
}
return dms_vr;
}
/* Update the MEM status register */
uint32 dms_upd_sr (void)
{
dms_sr = dms_sr & ~(MST_ENB | MST_UMP | MST_PRO);
if (dms_enb)
dms_sr = dms_sr | MST_ENB;
if (dms_ump)
dms_sr = dms_sr | MST_UMP;
if (mp_control)
dms_sr = dms_sr | MST_PRO;
return dms_sr;
}
/* CPU (SC 0) I/O signal handler.
I/O instructions for select code 0 manipulate the interrupt system. STF and
CLF turn the interrupt system on and off, and SFS and SFC test the state of
the interrupt system. When the interrupt system is off, only power fail and
parity error interrupts are allowed.
A PON reset initializes certain CPU registers. The 1000 series does a
microcoded memory clear and leaves the T and P registers set as a result.
Front-panel PRESET performs additional initialization. We also handle MEM
preset here.
Implementation notes:
1. An IOI signal reads the floating I/O bus (0 on all machines).
2. A CLC 0 issues CRS to all devices, not CLC. While most cards react
identically to CRS and CLC, some do not, e.g., the 12566B when used as an
I/O diagnostic target.
3. RTE uses the undocumented SFS 0,C instruction to both test and turn off
the interrupt system. This is confirmed in the "RTE-6/VM Technical
Specifications" manual (HP 92084-90015), section 2.3.1 "Process the
Interrupt", subsection "A.1 $CIC":
"Test to see if the interrupt system is on or off. This is done with
the SFS 0,C instruction. In either case, turn it off (the ,C does
it)."
...and in section 5.8, "Parity Error Detection":
"Because parity error interrupts can occur even when the interrupt
system is off, the code at $CIC must be able to save the complete
system status. The major hole in being able to save the complete state
is in saving the interrupt system state. In order to do this in both
the 21MX and the 21XE the instruction 103300 was used to both test the
interrupt system and turn it off."
4. Select code 0 cannot interrupt, so there is no SIR handler.
*/
uint32 cpuio (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data)
{
uint32 sc;
IOSIGNAL signal;
IOCYCLE working_set = signal_set; /* no SIR handler needed */
while (working_set) {
signal = IONEXT (working_set); /* isolate next signal */
switch (signal) { /* dispatch I/O signal */
case ioCLF: /* clear flag flip-flop */
ion = CLEAR; /* turn interrupt system off */
break;
case ioSTF: /* set flag flip-flop */
ion = SET; /* turn interrupt system on */
break;
case ioSFC: /* skip if flag is clear */
setSKF (!ion); /* skip if interrupt system is off */
break;
case ioSFS: /* skip if flag is set */
setSKF (ion); /* skip if interupt system is on */
break;
case ioIOI: /* I/O input */
stat_data = IORETURN (SCPE_OK, 0); /* returns 0 */
break;
case ioPON: /* power on normal */
AR = 0; /* clear A register */
BR = 0; /* clear B register */
SR = 0; /* clear S register */
TR = 0; /* clear T register */
E = 1; /* set E register */
if (UNIT_CPU_FAMILY == UNIT_FAMILY_1000) { /* 1000 series? */
memset (M, 0, (uint32) MEMSIZE * 2); /* zero allocated memory */
MR = 0077777; /* set M register */
PC = 0100000; /* set P register */
}
else { /* 21xx series */
MR = 0; /* clear M register */
PC = 0; /* clear P register */
}
break;
case ioPOPIO: /* power-on preset to I/O */
O = 0; /* clear O register */
ion = CLEAR; /* turn off interrupt system */
ion_defer = FALSE; /* clear interrupt deferral */
dms_enb = 0; /* turn DMS off */
dms_ump = 0; /* init to system map */
dms_sr = 0; /* clear status register and BP fence */
dms_vr = 0; /* clear violation register */
break;
case ioCLC: /* clear control flip-flop */
for (sc = CRSDEV; sc <= MAXDEV; sc++) /* send CRS to devices */
devdisp (sc, ioCRS, 0); /* from select code 6 and up */
break;
default: /* all other signals */
break; /* are ignored */
}
working_set = working_set & ~signal; /* remove current signal from set */
}
return stat_data;
}
/* Overflow/S-register (SC 1) I/O signal handler.
Flag instructions directed to select code 1 manipulate the overflow (O)
register. Input and output instructions access the switch (S) register. On
the 2115 and 2116, there is no S-register indicator, so it is effectively
read-only. On the other machines, a front-panel display of the S-register is
provided. On all machines, front-panel switches are provided to set the
contents of the S register.
Implementation notes:
1. Select code 1 cannot interrupt, so there is no SIR handler.
*/
uint32 ovflio (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data)
{
IOSIGNAL signal;
IOCYCLE working_set = signal_set; /* no SIR handler needed */
while (working_set) {
signal = IONEXT (working_set); /* isolate next signal */
switch (signal) { /* dispatch I/O signal */
case ioCLF: /* clear flag flip-flop */
O = 0; /* clear overflow */
break;
case ioSTF: /* set flag flip-flop */
O = 1; /* set overflow */
break;
case ioSFC: /* skip if flag is clear */
setSKF (!O); /* skip if overflow is clear */
break;
case ioSFS: /* skip if flag is set */
setSKF (O); /* skip if overflow is set */
break;
case ioIOI: /* I/O input */
stat_data = IORETURN (SCPE_OK, SR); /* read switch register value */
break;
case ioIOO: /* I/O output */
if ((UNIT_CPU_MODEL != UNIT_2116) && /* no S register display on */
(UNIT_CPU_MODEL != UNIT_2115)) /* 2116 and 2115 machines */
SR = IODATA (stat_data); /* write S register value */
break;
default: /* all other signals */
break; /* are ignored */
}
working_set = working_set & ~signal; /* remove current signal from set */
}
return stat_data;
}
/* Power fail (SC 4) I/O signal handler.
Power fail detection is standard on 2100 and 1000 systems and is optional on
21xx systems. Power fail recovery is standard on the 2100 and optional on
the others. Power failure or restoration will cause an interrupt on select
code 4. The direction of power change (down or up) can be tested by SFC.
We do not implement power fail under simulation. However, the central
interrupt register (CIR) is always read by an IOI directed to select code 4.
*/
uint32 pwrfio (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data)
{
IOSIGNAL signal;
IOCYCLE working_set = IOADDSIR (signal_set); /* add ioSIR if needed */
while (working_set) {
signal = IONEXT (working_set); /* isolate next signal */
switch (signal) { /* dispatch I/O signal */
case ioSTC: /* set control flip-flop */
break; /* reinitializes power fail */
case ioCLC: /* clear control flip-flop */
break; /* reinitializes power fail */
case ioSFC: /* skip if flag is clear */
break; /* skips if power fail occurred */
case ioIOI: /* I/O input */
stat_data = IORETURN (SCPE_OK, intaddr); /* input CIR value */
break;
default: /* all other signals */
break; /* are ignored */
}
working_set = working_set & ~signal; /* remove current signal from set */
}
return stat_data;
}
/* Memory protect/parity error (SC 5) I/O signal handler.
The memory protect card has a number of non-standard features:
- CLF and STF affect the parity error enable flip-flop, not the flag
- SFC and SFS test the memory expansion violation flip-flop, not the flag
- POPIO clears control, flag, and flag buffer instead of setting the flags
- CLC does not clear control (the only way to turn off MP is to cause a
violation)
- PRL and IRQ are a function of the flag only, not flag and control
- IAK is used unqualified by IRQ
The IAK backplane signal is asserted when any interrupt is acknowledged by
the CPU. Normally, an interface qualifies IAK with its own IRQ to ensure
that it responds only to an acknowledgement of its own request. The MP card
does this to reset its flag buffer and flag flip-flops, and to reset the
parity error indication. However, it also responds to an unqualified IAK
(i.e., for any interface) as follows:
- clears the MPV flip-flop
- clears the indirect counter
- clears the control flip-flop
- sets the INTPT flip-flop
The INTPT flip-flop indicates an occurrence of an interrupt. If the trap
cell of the interrupting device contains an I/O instruction that is not a
HLT, action equivalent to STC 05 is taken, i.e.:
- sets the control flip-flop
- set the EVR flip-flop
- clears the MEV flip-flop
- clears the PARERR flip-flop
In other words, an interrupt for any device will disable MP unless the trap
cell contains an I/O instruction other than a HLT.
Implementation notes:
1. Because the card uses IAK unqualified, this routine is called whenever
any interrupt occurs. If the MP card itself is not interrupting, the
select code passed will not be SC 05. In either case, the trap cell
instruction is passed in the data portion of the "stat_data" parameter.
2. The MEV flip-flop records memory expansion (a.k.a. dynamic mapping)
violations. It is set when an DM violation is encountered and can be
tested via SFC/SFS.
3. MP cannot be turned off in hardware, except by causing a violation.
Microcode typically does this by executing an IOG micro-order with select
code /= 1, followed by an IAK to clear the interrupt and a FTCH to clear
the INTPT flip-flop. Under simulation, mp_control may be set to CLEAR to
produce the same effect.
4. Parity error logic is not implemented.
*/
uint32 protio (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data)
{
uint16 data;
IOSIGNAL signal;
IOCYCLE working_set = IOADDSIR (signal_set); /* add ioSIR if needed */
while (working_set) {
signal = IONEXT (working_set); /* isolate next signal */
switch (signal) { /* dispatch I/O signal */
case ioCLF: /* clear flag flip-flop */
break; /* turns off PE interrupt */
case ioSTF: /* set flag flip-flop */
break; /* turns on PE interrupt */
case ioENF: /* enable flag */
mp_flag = mp_flagbuf = SET; /* set flag buffer and flag flip-flops */
mp_evrff = CLEAR; /* inhibit violation register updates */
break;
case ioSFC: /* skip if flag is clear */
setSKF (!mp_mevff); /* skip if MP interrupt */
break;
case ioSFS: /* skip if flag is set */
setSKF (mp_mevff); /* skip if DMS interrupt */
break;
case ioIOI: /* I/O input */
stat_data = IORETURN (SCPE_OK, mp_viol); /* read MP violation register */
break;
case ioIOO: /* I/O output */
mp_fence = IODATA (stat_data) & VAMASK; /* write to MP fence register */
if (cpu_unit.flags & UNIT_2100) /* 2100 IOP uses MP fence */
iop_sp = mp_fence; /* as a stack pointer */
break;
case ioPOPIO: /* power-on preset to I/O */
mp_control = CLEAR; /* clear control flip-flop */
mp_flag = mp_flagbuf = CLEAR; /* clear flag and flag buffer flip-flops */
mp_mevff = CLEAR; /* clear memory expansion violation flip-flop */
mp_evrff = SET; /* set enable violation register flip-flop */
break;
case ioSTC: /* set control flip-flop */
mp_control = SET; /* turn on MP */
mp_mevff = CLEAR; /* clear memory expansion violation flip-flop */
mp_evrff = SET; /* set enable violation register flip-flop */
break;
case ioSIR: /* set interrupt request */
setPRL (PRO, !mp_flag); /* set PRL signal */
setIRQ (PRO, mp_flag); /* set IRQ signal */
break;
case ioIAK: /* interrupt acknowledge */
if (dibptr->select_code == PRO) /* MP interrupt acknowledgement? */
mp_flag = mp_flagbuf = CLEAR; /* clear flag and flag buffer */
data = IODATA (stat_data); /* get trap cell instruction */
if (((data & I_NMRMASK) != I_IO) || /* trap cell instruction not I/O */
(I_GETIOOP (data) == soHLT)) /* or is halt? */
mp_control = CLEAR; /* turn protection off */
else { /* non-HLT I/O instruction leaves MP on */
mp_mevff = CLEAR; /* but clears MEV flip-flop */
mp_evrff = SET; /* and reenables violation register flip-flop */
}
break;
default: /* all other signals */
break; /* are ignored */
}
working_set = working_set & ~signal; /* remove current signal from set */
}
return stat_data;
}
/* DMA/DCPC secondary (SC 2/3) I/O signal handler.
DMA consists of one (12607B) or two (12578A/12895A/12897B) channels. Each
channel uses two select codes: 2 and 6 for channel 1, and 3 and 7 for channel
2. The lower select codes are used to configure the memory address register
(control word 2) and the word count register (control word 3). The upper
select codes are used to configure the service select register (control word
1) and to activate and terminate the transfer.
There are 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 is a negative value.
2. Select codes 2 and 3 cannot interrupt, so there is no SIR handler.
*/
uint32 dmasio (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data)
{
const CHANNEL ch = (CHANNEL) dibptr->card_index; /* DMA channel number */
uint16 data;
IOSIGNAL signal;
IOCYCLE working_set = signal_set; /* no SIR handler needed */
while (working_set) {
signal = IONEXT (working_set); /* isolate next signal */
switch (signal) { /* dispatch I/O signal */
case ioIOI: /* I/O data input */
if (UNIT_CPU_MODEL == UNIT_2114) /* 2114? */
data = dma [ch].cw3 & 0017777; /* only 13-bit count */
else if (UNIT_CPU_TYPE == UNIT_TYPE_211X) /* 2115/2116? */
data = dma [ch].cw3 & 0037777; /* only 14-bit count */
else /* other models */
data = (uint16) dma [ch].cw3; /* rest use full value */
stat_data = IORETURN (SCPE_OK, data); /* merge status and remaining word count */
break;
case ioIOO: /* I/O data output */
if (dma [ch].select) /* word count selected? */
dma [ch].cw3 = IODATA (stat_data); /* save count */
else /* memory address selected */
if (UNIT_CPU_MODEL == UNIT_2114) /* 2114? */
dma [ch].cw2 = IODATA (stat_data) & 0137777; /* only 14-bit address */
else /* other models */
dma [ch].cw2 = IODATA (stat_data); /* full address stored */
break;
case ioCLC: /* clear control flip-flop */
dma [ch].select = CLEAR; /* set for word count access */
break;
case ioSTC: /* set control flip-flop */
dma [ch].select = SET; /* set for memory address access */
break;
default: /* all other signals */
break; /* are ignored */
}
working_set = working_set & ~signal; /* remove current signal from set */
}
return stat_data;
}
/* DMA/DCPC primary (SC 6/7) I/O signal handler.
The primary DMA control interface and the service select register are
manipulated through select codes 6 and 7. Each channel has transfer enable,
control, flag, and flag buffer flip-flops. Transfer enable must be set via
STC to start DMA. Control is used only to enable the DMA completion
interrupt; it is set by STC and cleared by CLC. Flag and flag buffer are set
at transfer completion to signal an interrupt. STF may be issued to abort a
transfer in progress.
Again, 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).
Implementation notes:
1. An IOI reads the floating S-bus (high on the 1000, low on the 21xx).
2. The CRS signal on the DMA card resets the secondary (SC 2/3) select
flip-flops. Under simulation, ioCRS is dispatched to select codes 6 and
up, so we reset the flip-flop in our handler.
3. The 12578A supports byte-sized transfers by setting bit 14. Bit 14 is
ignored by all other DMA cards, which support word transfers only.
Under simulation, we use a byte-packing/unpacking register to hold one
byte while the other is read or written during the DMA cycle.
*/
uint32 dmapio (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data)
{
const CHANNEL ch = (CHANNEL) dibptr->card_index; /* DMA channel number */
uint16 data;
IOSIGNAL signal;
IOCYCLE working_set = IOADDSIR (signal_set); /* add ioSIR if needed */
while (working_set) {
signal = IONEXT (working_set); /* isolate next signal */
switch (signal) { /* dispatch I/O signal */
case ioCLF: /* clear flag flip-flop */
dma [ch].flag = dma [ch].flagbuf = CLEAR; /* clear flag and flag buffer */
break;
case ioSTF: /* set flag flip-flop */
case ioENF: /* enable flag */
dma [ch].flag = dma [ch].flagbuf = SET; /* set flag and flag buffer */
dma [ch].xferen = CLEAR; /* clear transfer enable to abort transfer */
break;
case ioSFC: /* skip if flag is clear */
setstdSKF (dma [ch]); /* skip if transfer in progress */
break;
case ioSFS: /* skip if flag is set */
setstdSKF (dma [ch]); /* skip if transfer is complete */
break;
case ioIOI: /* I/O data input */
if (UNIT_CPU_TYPE == UNIT_TYPE_1000) /* 1000? */
stat_data = IORETURN (SCPE_OK, DMASK); /* return all ones */
else /* other models */
stat_data = IORETURN (SCPE_OK, 0); /* return all zeros */
break;
case ioIOO: /* I/O data output */
data = IODATA (stat_data); /* clear supplied status */
if (UNIT_CPU_MODEL == UNIT_2114) /* 12607? */
dma [ch].cw1 = (data & 0137707) | 010; /* mask SC, convert to 10-17 */
else if (UNIT_CPU_TYPE == UNIT_TYPE_211X) /* 12578? */
dma [ch].cw1 = data; /* store full select code, flags */
else /* 12895, 12897 */
dma [ch].cw1 = data & ~DMA1_PB; /* clip byte-packing flag */
break;
case ioPOPIO: /* power-on preset to I/O */
dma [ch].flag = dma [ch].flagbuf = SET; /* set flag and flag buffer */
break;
case ioCRS: /* control reset */
dma [ch].xferen = CLEAR; /* clear transfer enable */
dma [ch].select = CLEAR; /* set secondary for word count access */
/* fall into CLC handler */
case ioCLC: /* clear control flip-flop */
dma [ch].control = CLEAR; /* clear control */
break;
case ioSTC: /* set control flip-flop */
dma [ch].packer = 0; /* clear packing register */
dma [ch].xferen = dma [ch].control = SET; /* set transfer enable and control */
break;
case ioSIR: /* set interrupt request */
setstdPRL (dma [ch]);
setstdIRQ (dma [ch]);
break;
case ioIAK: /* interrupt acknowledge */
dma [ch].flagbuf = CLEAR; /* clear flag buffer */
break;
default: /* all other signals */
break; /* are ignored */
}
working_set = working_set & ~signal; /* remove current signal from set */
}
return stat_data;
}
/* Unassigned select code I/O signal handler.
The 21xx/1000 I/O structure requires that no empty slots exist between
interface cards. This is due to the hardware priority chaining (PRH/PRL).
If it is necessary to leave unused I/O slots, HP 12777A Priority Jumper Cards
must be installed in them to maintain priority continuity.
Under simulation, every unassigned I/O slot behaves as though a 12777A were
resident.
Implementation notes:
1. For select codes < 10 octal, an IOI reads the floating S-bus (high on
the 1000, low on the 21xx). For select codes >= 10 octal, an IOI reads
the floating I/O bus (low on all machines).
2. If "stop_dev" is TRUE, then the simulator will stop when an unassigned
device is accessed.
*/
uint32 nullio (DIB *dibptr, IOCYCLE signal_set, uint32 stat_data)
{
uint16 data = 0;
IOSIGNAL signal;
IOCYCLE working_set = signal_set; /* no SIR handler needed */
while (working_set) {
signal = IONEXT (working_set); /* isolate next signal */
switch (signal) { /* dispatch I/O signal */
case ioIOI: /* I/O data input */
if ((dibptr->select_code < VARDEV) && /* internal device */
(UNIT_CPU_TYPE == UNIT_TYPE_1000)) /* and 1000? */
data = DMASK; /* return all ones */
else /* external or other model */
data = 0; /* return all zeros */
break;
default: /* all other signals */
break; /* are ignored */
}
working_set = working_set & ~signal; /* remove current signal from set */
}
return IORETURN (stop_dev, data); /* flag missing device */
}
/* DMA cycle routine.
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 signal handler. 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). I/O signal handlers will
process these signals sequentially, in the order listed above, before
returning.
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 t_stat dma_cycle (CHANNEL ch, uint32 map)
{
const uint32 dev = dma [ch].cw1 & I_DEVMASK; /* device select code */
const uint32 stc = dma [ch].cw1 & DMA1_STC; /* STC enable flag */
const uint32 bytes = dma [ch].cw1 & DMA1_PB; /* pack bytes flag */
const uint32 clc = dma [ch].cw1 & DMA1_CLC; /* CLC enable flag */
const uint32 MA = dma [ch].cw2 & VAMASK; /* memory address */
const uint32 input = dma [ch].cw2 & DMA2_OI; /* input flag */
const uint32 even = dma [ch].packer & DMA_OE; /* odd/even packed byte flag */
uint16 data;
t_stat status;
uint32 ioresult;
IOCYCLE signals;
if (bytes && !even || dma [ch].cw3 != DMASK) { /* normal cycle? */
if (input) /* input cycle? */
signals = ioIOI | ioCLF; /* assert IOI and CLF */
else /* output cycle */
signals = ioIOO | ioCLF; /* assert IOO and CLF */
if (stc) /* STC wanted? */
signals = signals | ioSTC; /* assert STC */
}
else { /* last cycle */
if (input) /* input cycle? */
signals = ioIOI | ioEDT; /* assert IOI and EDT */
else { /* output cycle */
signals = ioIOO | ioCLF | ioEDT; /* assert IOO and CLF and EDT */
if (stc) /* STC wanted? */
signals = signals | ioSTC; /* assert STC */
}
if (clc) /* CLC wanted? */
signals = signals | ioCLC; /* assert CLC */
}
if (input) { /* input cycle? */
ioresult = devdisp (dev, signals, 0); /* do I/O input */
status = IOSTATUS (ioresult); /* get cycle status */
if (status == SCPE_OK) { /* good I/O cycle? */
data = IODATA (ioresult); /* extract return data value */
if (bytes) { /* byte packing? */
if (even) { /* second byte? */
data = (uint16) (dma [ch].packer << 8) /* merge stored byte */
| (data & DMASK8);
WriteIO (MA, data, map); /* store word data */
}
else /* first byte */
dma [ch].packer = (data & DMASK8); /* save it */
dma [ch].packer = dma [ch].packer ^ DMA_OE; /* flip odd/even bit */
}
else /* no byte packing */
WriteIO (MA, data, map); /* store word data */
}
}
else { /* output cycle */
if (bytes) { /* byte packing? */
if (even) /* second byte? */
data = dma [ch].packer & DMASK8; /* retrieve it */
else { /* first byte */
dma [ch].packer = ReadIO (MA, map); /* read word data */
data = (dma [ch].packer >> 8) & DMASK8; /* get high byte */
}
dma [ch].packer = dma [ch].packer ^ DMA_OE; /* flip odd/even bit */
}
else /* no byte packing */
data = ReadIO (MA, map); /* read word data */
ioresult = devdisp (dev, signals, data); /* do I/O output */
status = IOSTATUS (ioresult); /* get cycle status */
}
if ((even || !bytes) && (status == SCPE_OK)) { /* new byte or no packing and good xfer? */
dma [ch].cw2 = input | (dma [ch].cw2 + 1) & VAMASK; /* increment address */
dma [ch].cw3 = (dma [ch].cw3 + 1) & DMASK; /* increment word count */
if (dma [ch].cw3 == 0) /* end of transfer? */
dmapio (dtab [DMA1 + ch], ioENF, 0); /* set DMA channel flag */
}
return status; /* return I/O status */
}
/* Reset routines.
The reset routines are called to simulate either an initial power on
condition or a front-panel PRESET button press. For initial power on
(corresponds to PON, POPIO, and CRS signal assertion in the CPU), the "P"
command switch will be set. For PRESET (corresponds to POPIO and CRS
assertion), the switch will be clear.
SCP delivers a power-on reset to all devices when the simulator is started.
A RUN, BOOT, RESET, or RESET ALL command delivers a PRESET to all devices. A
RESET <dev> delivers a PRESET to a specific device.
*/
/* CPU reset.
If this is the first call after simulator startup, allocate the initial
memory array, set the default CPU model, and install the default BBL.
*/
t_stat cpu_reset (DEVICE *dptr)
{
if (M == NULL) { /* initial call after startup? */
pcq_r = find_reg ("PCQ", NULL, dptr); /* get PC queue pointer */
if (pcq_r) /* defined? */
pcq_r->qptr = 0; /* initialize queue */
else /* not defined */
return SCPE_IERR; /* internal error */
M = (uint16 *) calloc (PASIZE, sizeof (uint16)); /* alloc mem */
if (M == NULL) /* alloc fail? */
return SCPE_MEM;
else { /* do one-time init */
MEMSIZE = 32768; /* set initial memory size */
cpu_set_model (NULL, UNIT_2116, NULL, NULL); /* set initial CPU model */
SR = 001000; /* select PTR boot ROM at SC 10 */
cpu_boot (0, NULL); /* install loader for 2116 */
cpu_set_ldr (NULL, FALSE, NULL, NULL); /* disable loader (was enabled) */
SR = 0; /* clear S */
sim_vm_post = &hp_post_cmd; /* set cmd post proc */
sim_vm_fprint_stopped = &hp_fprint_stopped; /* set sim stop printer */
sim_brk_types = ALL_BKPTS; /* register allowed breakpoint types */
}
}
if (sim_switches & SWMASK ('P')) /* PON reset? */
IOPOWERON (&cpu_dib);
else /* PRESET */
IOPRESET (&cpu_dib);
sim_brk_dflt = SWMASK ('N'); /* type is nomap as DMS is off */
return SCPE_OK;
}
/* Memory protect reset */
t_stat mp_reset (DEVICE *dptr)
{
IOPRESET (&mp_dib); /* PRESET device (does not use PON) */
mp_fence = 0; /* clear fence register */
mp_viol = 0; /* clear violation register */
return SCPE_OK;
}
/* DMA reset */
t_stat dma_reset (DEVICE *dptr)
{
DIB *dibptr = (DIB *) dptr->ctxt; /* DIB pointer */
const CHANNEL ch = (CHANNEL) dibptr->card_index; /* DMA channel number */
if (UNIT_CPU_MODEL != UNIT_2114) /* 2114 has only one channel */
hp_enbdis_pair (dma_dptrs [ch], /* make specified channel */
dma_dptrs [ch ^ 1]); /* consistent with other channel */
if (sim_switches & SWMASK ('P')) { /* power-on reset? */
dma [ch].cw1 = 0; /* clear control word registers */
dma [ch].cw2 = 0;
dma [ch].cw3 = 0;
}
IOPRESET (dibptr); /* PRESET device (does not use PON) */
dma [ch].packer = 0; /* clear byte packer */
return SCPE_OK;
}
/* Memory examine */
t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
{
uint16 d;
if ((sw & ALL_MAPMODES) && (dms_enb == 0)) /* req map with DMS off? */
return SCPE_NOFNC; /* command not allowed */
addr = dms_cons (addr, sw); /* translate address as indicated */
if (addr >= MEMSIZE) /* beyond memory limits? */
return SCPE_NXM; /* non-existent memory */
if ((sw & SIM_SW_REST) || (addr >= 2)) /* restoring or memory access? */
d = M[addr]; /* return memory value */
else /* not restoring and A/B access */
d = ABREG[addr]; /* return A/B register value */
if (vptr != NULL)
*vptr = d & DMASK; /* store return value */
return SCPE_OK;
}
/* Memory deposit */
t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
{
if ((sw & ALL_MAPMODES) && (dms_enb == 0)) /* req map with DMS off? */
return SCPE_NOFNC; /* command not allowed */
addr = dms_cons (addr, sw); /* translate address as indicated */
if (addr >= MEMSIZE) /* beyond memory limits? */
return SCPE_NXM; /* non-existent memory */
if ((sw & SIM_SW_REST) || (addr >= 2)) /* restoring or memory access? */
M[addr] = val & DMASK; /* store memory value */
else /* not restoring and A/B access */
ABREG[addr] = val & DMASK; /* store A/B register value */
return SCPE_OK;
}
/* Make a pair of devices consistent */
void hp_enbdis_pair (DEVICE *ccp, DEVICE *dcp)
{
if (ccp->flags & DEV_DIS)
dcp->flags = dcp->flags | DEV_DIS;
else
dcp->flags = dcp->flags & ~DEV_DIS;
return;
}
/* VM command post-processor
Update T register to contents of memory addressed by M register
if M register has changed.
*/
void hp_post_cmd (t_bool from_scp)
{
if (MR != saved_MR) { /* M changed since last update? */
saved_MR = MR;
TR = ReadTAB (MR); /* sync T with new M */
}
return;
}
/* Test for device conflict */
static t_bool dev_conflict (void)
{
DEVICE *dptr;
DIB *dibptr;
uint32 i, j, k;
t_bool is_conflict = FALSE;
uint32 conflicts [MAXDEV + 1] = { 0 };
for (i = 0; sim_devices [i] != NULL; i++) {
dptr = sim_devices [i];
dibptr = (DIB *) dptr->ctxt;
if (dibptr && !(dptr->flags & DEV_DIS))
if (++conflicts [dibptr->select_code] > 1)
is_conflict = TRUE;
}
if (is_conflict) {
sim_ttcmd();
for (i = 0; i <= MAXDEV; i++) {
if (conflicts [i] > 1) {
k = conflicts [i];
printf ("Select code %o conflict:", i);
if (sim_log)
fprintf (sim_log, "Select code %o conflict:", i);
for (j = 0; sim_devices [j] != NULL; j++) {
dptr = sim_devices [j];
dibptr = (DIB *) dptr->ctxt;
if (dibptr && !(dptr->flags & DEV_DIS) && i == dibptr->select_code) {
if (k < conflicts [i]) {
printf (" and");
if (sim_log)
fputs (" and", sim_log);
}
printf (" %s", sim_dname (dptr));
if (sim_log)
fprintf (sim_log, " %s", sim_dname (dptr));
k = k - 1;
if (k == 0) {
putchar ('\n');
if (sim_log)
fputc ('\n', sim_log);
break;
}
}
}
}
}
}
return is_conflict;
}
/* Change CPU memory size.
On a 21xx, move the current loader to the top of the new memory size. Then
clear "non-existent memory" so that reads return zero, per spec.
Validation:
- New size <= maximum size for current CPU.
- New size a positive multiple of 4K (progamming error if not).
- If new size < old size, truncation accepted.
*/
t_stat cpu_set_size (UNIT *uptr, int32 new_size, char *cptr, void *desc)
{
int32 mc = 0;
uint32 i;
uint32 model = CPU_MODEL_INDEX; /* current CPU model index */
uint32 old_size = (uint32) MEMSIZE; /* current memory size */
if ((uint32) new_size > cpu_features[model].maxmem)
return SCPE_NOFNC; /* mem size unsupported */
if ((new_size <= 0) || (new_size > PASIZE) || ((new_size & 07777) != 0))
return SCPE_NXM; /* invalid size (prog err) */
if (!(sim_switches & SWMASK ('F'))) { /* force truncation? */
for (i = new_size; i < MEMSIZE; i++) /* check truncated memory */
mc = mc | M[i]; /* for content */
if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE)))
return SCPE_INCOMP;
}
if (UNIT_CPU_FAMILY == UNIT_FAMILY_21XX) { /* 21xx CPU? */
cpu_set_ldr (uptr, FALSE, NULL, NULL); /* save loader to shadow RAM */
MEMSIZE = new_size; /* set new memory size */
fwanxm = (uint32) MEMSIZE - IBL_LNT; /* reserve memory for loader */
}
else /* loader unsupported */
MEMSIZE = fwanxm = new_size; /* set new memory size */
for (i = fwanxm; i < old_size; i++) /* zero non-existent memory */
M[i] = 0;
return SCPE_OK;
}
/* Change CPU models.
For convenience, MP and DMA are typically enabled if available; they may be
disabled subsequently if desired. Note that the 2114 supports only one DMA
channel (channel 1). All other models support two channels.
Validation:
- Sets standard equipment and convenience features.
- Changes DMA device name to DCPC if 1000 is selected.
- Enforces maximum memory allowed (doesn't change otherwise).
- Disables loader on 21xx machines.
*/
t_stat cpu_set_model (UNIT *uptr, int32 new_model, char *cptr, void *desc)
{
uint32 old_family = UNIT_CPU_FAMILY; /* current CPU type */
uint32 new_family = new_model & UNIT_FAMILY_MASK; /* new CPU family */
uint32 new_index = new_model >> UNIT_V_CPU; /* new CPU model index */
uint32 new_memsize;
t_stat result;
cpu_unit.flags = cpu_unit.flags & ~UNIT_OPTS | /* set typical features */
cpu_features[new_index].typ & UNIT_OPTS; /* mask pseudo-opts */
if (cpu_features[new_index].typ & UNIT_MP) /* MP in typ config? */
mp_dev.flags = mp_dev.flags & ~DEV_DIS; /* enable it */
else
mp_dev.flags = mp_dev.flags | DEV_DIS; /* disable it */
if (cpu_features[new_index].opt & UNIT_MP) /* MP an option? */
mp_dev.flags = mp_dev.flags | DEV_DISABLE; /* make it alterable */
else
mp_dev.flags = mp_dev.flags & ~DEV_DISABLE; /* make it unalterable */
if (cpu_features[new_index].typ & UNIT_DMA) { /* DMA in typ config? */
dma1_dev.flags = dma1_dev.flags & ~DEV_DIS; /* enable DMA channel 1 */
if (new_model == UNIT_2114) /* 2114 has only one channel */
dma2_dev.flags = dma2_dev.flags | DEV_DIS; /* disable channel 2 */
else /* all others have two channels */
dma2_dev.flags = dma2_dev.flags & ~DEV_DIS; /* enable it */
}
else {
dma1_dev.flags = dma1_dev.flags | DEV_DIS; /* disable channel 1 */
dma2_dev.flags = dma2_dev.flags | DEV_DIS; /* disable channel 2 */
}
if (cpu_features[new_index].opt & UNIT_DMA) { /* DMA an option? */
dma1_dev.flags = dma1_dev.flags | DEV_DISABLE; /* make it alterable */
if (new_model == UNIT_2114) /* 2114 has only one channel */
dma2_dev.flags = dma2_dev.flags & ~DEV_DISABLE; /* make it unalterable */
else /* all others have two channels */
dma2_dev.flags = dma2_dev.flags | DEV_DISABLE; /* make it alterable */
}
else {
dma1_dev.flags = dma1_dev.flags & ~DEV_DISABLE; /* make it unalterable */
dma2_dev.flags = dma2_dev.flags & ~DEV_DISABLE; /* make it unalterable */
}
if ((old_family == UNIT_FAMILY_1000) && /* if current family is 1000 */
(new_family == UNIT_FAMILY_21XX)) { /* and new family is 21xx */
deassign_device (&dma1_dev); /* delete DCPC names */
deassign_device (&dma2_dev);
}
else if ((old_family == UNIT_FAMILY_21XX) && /* if current family is 21xx */
(new_family == UNIT_FAMILY_1000)) { /* and new family is 1000 */
assign_device (&dma1_dev, "DCPC1"); /* change DMA device name */
assign_device (&dma2_dev, "DCPC2"); /* to DCPC for familiarity */
}
if ((MEMSIZE == 0) || /* current mem size not set? */
(MEMSIZE > cpu_features[new_index].maxmem)) /* current mem size too large? */
new_memsize = cpu_features[new_index].maxmem; /* set it to max supported */
else
new_memsize = (uint32) MEMSIZE; /* or leave it unchanged */
result = cpu_set_size (uptr, new_memsize, NULL, NULL); /* set memory size */
if (result == SCPE_OK) /* memory change OK? */
if (new_family == UNIT_FAMILY_21XX) /* 21xx CPU? */
fwanxm = (uint32) MEMSIZE - IBL_LNT; /* reserve memory for loader */
else
fwanxm = (uint32) MEMSIZE; /* loader reserved only for 21xx */
return result;
}
/* Display the CPU model and optional loader status.
Loader status is displayed for 21xx models and suppressed for 1000 models.
*/
t_stat cpu_show_model (FILE *st, UNIT *uptr, int32 val, void *desc)
{
fputs ((char *) desc, st); /* write model name */
if (UNIT_CPU_FAMILY == UNIT_FAMILY_21XX) /* valid only for 21xx */
if (fwanxm < MEMSIZE) /* loader area non-existent? */
fputs (", loader disabled", st); /* yes, so access disabled */
else
fputs (", loader enabled", st); /* no, so access enabled */
return SCPE_OK;
}
/* Set a CPU option.
Validation:
- Checks that the current CPU model supports the option selected.
- If CPU is 1000-F, ensures that VIS and IOP are mutually exclusive.
- If CPU is 2100, ensures that FP/FFP and IOP are mutually exclusive.
- If CPU is 2100, ensures that FP is enabled if FFP enabled
(FP is required for FFP installation).
*/
t_stat cpu_set_opt (UNIT *uptr, int32 option, char *cptr, void *desc)
{
uint32 model = CPU_MODEL_INDEX; /* current CPU model index */
if ((cpu_features[model].opt & option) == 0) /* option supported? */
return SCPE_NOFNC; /* no */
if (UNIT_CPU_TYPE == UNIT_TYPE_2100) {
if ((option == UNIT_FP) || (option == UNIT_FFP)) /* 2100 IOP and FP/FFP options */
uptr->flags = uptr->flags & ~UNIT_IOP; /* are mutually exclusive */
else if (option == UNIT_IOP)
uptr->flags = uptr->flags & ~(UNIT_FP | UNIT_FFP);
if (option == UNIT_FFP) /* 2100 FFP option requires FP */
uptr->flags = uptr->flags | UNIT_FP;
}
else if (UNIT_CPU_MODEL == UNIT_1000_F)
if (option == UNIT_VIS) /* 1000-F IOP and VIS options */
uptr->flags = uptr->flags & ~UNIT_IOP; /* are mutually exclusive */
else if (option == UNIT_IOP)
uptr->flags = uptr->flags & ~UNIT_VIS;
return SCPE_OK;
}
/* Clear a CPU option.
Validation:
- Checks that the current CPU model supports the option selected.
- Clears flag from unit structure (we are processing MTAB_XTD entries).
- If CPU is 2100, ensures that FFP is disabled if FP disabled
(FP is required for FFP installation).
*/
t_bool cpu_clr_opt (UNIT *uptr, int32 option, char *cptr, void *desc)
{
uint32 model = CPU_MODEL_INDEX; /* current CPU model index */
if ((cpu_features[model].opt & option) == 0) /* option supported? */
return SCPE_NOFNC; /* no */
uptr->flags = uptr->flags & ~option; /* disable option */
if ((UNIT_CPU_TYPE == UNIT_TYPE_2100) && /* disabling 2100 FP? */
(option == UNIT_FP))
uptr->flags = uptr->flags & ~UNIT_FFP; /* yes, so disable FFP too */
return SCPE_OK;
}
/* 21xx loader enable/disable function.
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.
1000 CPUs do not have a protected loader feature. Instead, loaders are
stored in PROMs and are copied into main memory for execution by the IBL
switch.
Under simulation, we keep both a total configured memory size (MEMSIZE) and a
current configured memory size (fwanxm = "first word address of non-existent
memory"). When the two are equal, the loader is enabled. When the current
size is less than the total size, the loader is disabled.
Disabling the loader copies the last 64 words to a shadow array, zeros the
corresponding memory, and decreases the last word of addressable memory by
64. Enabling the loader reverses this process.
Disabling may be done manually by user command or automatically when a halt
instruction is executed. Enabling occurs only by user command. This differs
slightly from actual machine operation, which additionally disables the
loader when a manual halt is performed. We do not do this to allow
breakpoints within and single-stepping through the loaders.
*/
t_stat cpu_set_ldr (UNIT *uptr, int32 enable, char *cptr, void *desc)
{
static BOOT_ROM loader;
int32 i;
t_bool is_enabled = (fwanxm == MEMSIZE);
if ((UNIT_CPU_FAMILY != UNIT_FAMILY_21XX) || /* valid only for 21xx */
(MEMSIZE == 0)) /* and for initialized memory */
return SCPE_NOFNC;
if (is_enabled && (enable == 0)) { /* disable loader? */
fwanxm = (uint32) MEMSIZE - IBL_LNT; /* decrease available memory */
for (i = 0; i < IBL_LNT; i++) { /* copy loader */
loader[i] = M[fwanxm + i]; /* from memory */
M[fwanxm + i] = 0; /* and zero location */
}
}
else if ((!is_enabled) && (enable == 1)) { /* enable loader? */
for (i = 0; i < IBL_LNT; i++) /* copy loader */
M[fwanxm + i] = loader[i]; /* to memory */
fwanxm = (uint32) MEMSIZE; /* increase available memory */
}
return SCPE_OK;
}
/* Idle enable/disable */
t_stat cpu_set_idle (UNIT *uptr, int32 option, char *cptr, void *desc)
{
if (option)
return sim_set_idle (uptr, 10, NULL, NULL);
else
return sim_clr_idle (uptr, 0, NULL, NULL);
}
/* Idle display */
t_stat cpu_show_idle (FILE *st, UNIT *uptr, int32 val, void *desc)
{
return sim_show_idle (st, uptr, val, desc);
}
/* IBL routine (CPU boot) */
t_stat cpu_boot (int32 unitno, DEVICE *dptr)
{
int32 dev = (SR >> IBL_V_DEV) & I_DEVMASK;
int32 sel = (SR >> IBL_V_SEL) & IBL_M_SEL;
if (dev < 010)
return SCPE_NOFNC;
switch (sel) {
case 0: /* PTR boot */
ibl_copy (ptr_rom, dev, IBL_S_NOCLR, IBL_S_NOSET);
break;
case 1: /* DP/DQ boot */
ibl_copy (dq_rom, dev, IBL_S_NOCLR, IBL_S_NOSET);
break;
case 2: /* MS boot */
ibl_copy (ms_rom, dev, IBL_S_NOCLR, IBL_S_NOSET);
break;
case 3: /* DS boot */
ibl_copy (ds_rom, dev, IBL_S_NOCLR, IBL_S_NOSET);
break;
}
return SCPE_OK;
}
/* IBL boot ROM copy
- Use memory size to set the initial PC and base of the boot area
- Copy boot ROM to memory, updating I/O instructions
- Place 2s complement of boot base in last location
- Modify S register as indicated
Notes:
- Boot ROMs must be assembled with a device code of 10 (10 and 11 for
devices requiring two codes)
*/
t_stat ibl_copy (const BOOT_ROM rom, int32 dev, uint32 sr_clear, uint32 sr_set)
{
int32 i;
uint16 wd;
cpu_set_ldr (NULL, TRUE, NULL, NULL); /* enable loader (ignore errors) */
if (dev < 010) /* valid device? */
return SCPE_ARG;
PC = ((MEMSIZE - 1) & ~IBL_MASK) & VAMASK; /* start at mem top */
for (i = 0; i < IBL_LNT; i++) { /* copy bootstrap */
wd = rom[i]; /* get word */
if (((wd & I_NMRMASK) == I_IO) && /* IO instruction? */
((wd & I_DEVMASK) >= 010) && /* dev >= 10? */
(I_GETIOOP (wd) != soHLT)) /* not a HALT? */
M[PC + i] = (wd + (dev - 010)) & DMASK; /* change dev code */
else /* leave unchanged */
M[PC + i] = wd;
}
M[PC + IBL_DPC] = (M[PC + IBL_DPC] + (dev - 010)) & DMASK; /* patch DMA ctrl */
M[PC + IBL_END] = (~PC + 1) & DMASK; /* fill in start of boot */
SR = (SR & sr_clear) | sr_set; /* modify the S register as indicated */
return SCPE_OK;
}