The makefile now works for Linux and most Unix's. Howevr, for Solaris and MacOS, you must first export the OSTYPE environment variable: > export OSTYPE > make Otherwise, you will get build errors. 1. New Features 1.1 3.8-0 1.1.1 SCP and Libraries - BREAK, NOBREAK, and SHOW BREAK with no argument will set, clear, and show (respectively) a breakpoint at the current PC. 1.2 GRI - Added support for the GRI-99 processor. 1.3 HP2100 - Added support for the BACI terminal interface. - Added support for RTE OS/VMA/EMA, SIGNAL, VIS firmware extensions. 1.4 Nova - Added support for 64KW memory (implemented in third-party CPU's). 1.5 PDP-11 - Added support for DC11, RC11, KE11A, KG11A. - Added modem control support for DL11. - Added ASCII character support for all 8b devices. 2. Bugs Fixed Please see the revision history on http://simh.trailing-edge.com or in the source module sim_rev.h.
2781 lines
115 KiB
C
2781 lines
115 KiB
C
/* hp2100_cpu.c: HP 21xx CPU simulator
|
|
|
|
Copyright (c) 1993-2008, 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
|
|
MP 12581A/12892B memory protect
|
|
DMA0,DMA1 12607B/12578A/12895A direct memory access controller
|
|
DCPC0,DCPC1 12897B dual channel port controller
|
|
|
|
30-Apr-08 JDB Enabled SIGNAL instructions, SIG debug flag
|
|
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)
|
|
- 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
|
|
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 |
|
|
+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
|
|
|
|
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 non-zero.
|
|
|
|
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 as five parallel arrays:
|
|
|
|
device commands as bit array dev_cmd[2][31..0]
|
|
device flags as bit array dev_flg[2][31..0]
|
|
device flag buffers as bit array dev_fbf[2][31..0]
|
|
device controls as bit array dev_ctl[2][31..0]
|
|
device service requests as bit array dev_srq[3][31..0]
|
|
|
|
The HP 2100 interrupt structure is based on flag, flag buffer,
|
|
and control. If a device flag is set, the flag buffer is set,
|
|
the control bit is set, and the device is the highest priority
|
|
on the interrupt chain, it requests an interrupt. When the
|
|
interrupt is acknowledged, 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.
|
|
|
|
Command plays no direct role in interrupts. The command flop
|
|
tells whether a device is active. It is set by STC and cleared
|
|
by CLC; it is also cleared when the device flag is set. Simple
|
|
devices don't need to track command separately from control.
|
|
|
|
Service requests are used to trigger the DMA service logic.
|
|
|
|
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.
|
|
*/
|
|
|
|
#include "hp2100_defs.h"
|
|
#include <setjmp.h>
|
|
#include "hp2100_cpu.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 */
|
|
|
|
#define ABORT(val) longjmp (save_env, (val))
|
|
|
|
#define DMAR0 1
|
|
#define DMAR1 2
|
|
|
|
#define ALL_BKPTS (SWMASK('E')|SWMASK('N')|SWMASK('S')|SWMASK('U'))
|
|
#define ALL_MAPMODES (SWMASK('S')|SWMASK('U')|SWMASK('P')|SWMASK('Q'))
|
|
|
|
uint16 *M = NULL; /* memory */
|
|
uint32 saved_AR = 0; /* A register */
|
|
uint32 saved_BR = 0; /* B register */
|
|
uint16 ABREG[2]; /* during execution */
|
|
uint32 PC = 0; /* P register */
|
|
uint32 SR = 0; /* S register */
|
|
uint32 MR = 0; /* M register */
|
|
uint32 saved_MR = 0; /* between executions */
|
|
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 */
|
|
uint32 dev_cmd[2] = { 0 }; /* device command */
|
|
uint32 dev_ctl[2] = { 0 }; /* device control */
|
|
uint32 dev_flg[2] = { 0 }; /* device flags */
|
|
uint32 dev_fbf[2] = { 0 }; /* device flag bufs */
|
|
uint32 dev_srq[2] = { 0 }; /* device svc reqs */
|
|
struct DMA dmac[2] = { { 0 }, { 0 } }; /* DMA channels */
|
|
uint32 ion = 0; /* interrupt enable */
|
|
uint32 ion_defer = 0; /* interrupt defer */
|
|
uint32 intaddr = 0; /* interrupt addr */
|
|
uint32 mp_fence = 0; /* mem prot fence */
|
|
uint32 mp_viol = 0; /* mem prot viol reg */
|
|
uint32 mp_mevff = 0; /* mem exp (dms) viol */
|
|
uint32 mp_evrff = 1; /* update mp_viol */
|
|
uint32 err_PC = 0; /* error PC */
|
|
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 */
|
|
uint32 iop_sp = 0; /* iop stack */
|
|
uint32 ind_max = 16; /* iadr nest limit */
|
|
uint32 stop_inst = 1; /* stop on ill inst */
|
|
uint32 stop_dev = 0; /* stop on ill dev */
|
|
uint32 fwanxm = 0; /* first word addr of nx mem */
|
|
uint16 pcq[PCQ_SIZE] = { 0 }; /* PC queue */
|
|
uint32 pcq_p = 0; /* PC queue ptr */
|
|
REG *pcq_r = NULL; /* PC queue reg ptr */
|
|
jmp_buf save_env; /* abort handler */
|
|
|
|
/* 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 }
|
|
};
|
|
|
|
extern int32 sim_interval;
|
|
extern int32 sim_int_char;
|
|
extern int32 sim_brk_char;
|
|
extern int32 sim_del_char;
|
|
extern uint32 sim_brk_types, sim_brk_dflt, sim_brk_summ; /* breakpoint info */
|
|
extern FILE *sim_log;
|
|
extern DEVICE *sim_devices[];
|
|
extern int32 sim_switches;
|
|
extern char halt_msg[];
|
|
|
|
t_stat Ea (uint32 IR, uint32 *addr, uint32 irq);
|
|
uint16 ReadIO (uint32 addr, uint32 map);
|
|
uint16 ReadPW (uint32 addr);
|
|
uint16 ReadTAB (uint32 addr);
|
|
void WriteIO (uint32 addr, uint32 dat, uint32 map);
|
|
void WritePW (uint32 addr, uint32 dat);
|
|
uint32 dms (uint32 va, uint32 map, uint32 prot);
|
|
uint32 dms_io (uint32 va, uint32 map);
|
|
uint32 shift (uint32 inval, uint32 flag, uint32 oper);
|
|
void dma_cycle (uint32 chan, uint32 map);
|
|
uint32 calc_dma (void);
|
|
uint32 calc_int (void);
|
|
uint32 calc_defer (void);
|
|
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 dma0_reset (DEVICE *dptr);
|
|
t_stat dma1_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_bool dev_conflict (void);
|
|
void hp_post_cmd (t_bool from_scp);
|
|
|
|
extern t_stat cpu_eau (uint32 IR, uint32 intrq);
|
|
extern t_stat cpu_uig_0 (uint32 IR, uint32 intrq, uint32 iotrap);
|
|
extern t_stat cpu_uig_1 (uint32 IR, uint32 intrq, uint32 iotrap);
|
|
extern void (*sim_vm_post) (t_bool from_scp);
|
|
|
|
/* CPU data structures
|
|
|
|
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
|
|
*/
|
|
|
|
UNIT cpu_unit = { UDATA (NULL, UNIT_FIX + UNIT_BINK, 0) };
|
|
|
|
REG cpu_reg[] = {
|
|
{ ORDATA (P, PC, 15) },
|
|
{ ORDATA (A, saved_AR, 16) },
|
|
{ ORDATA (B, saved_BR, 16) },
|
|
{ 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 },
|
|
{ ORDATA (FWANXM, fwanxm, 32), REG_HRO },
|
|
{ BRDATA (PCQ, pcq, 8, 15, PCQ_SIZE), REG_RO+REG_CIRC },
|
|
{ ORDATA (PCQP, pcq_p, 6), 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 },
|
|
{ ORDATA (HCMD, dev_cmd[0], 32), REG_HRO },
|
|
{ ORDATA (LCMD, dev_cmd[1], 32), REG_HRO },
|
|
{ ORDATA (HCTL, dev_ctl[0], 32), REG_HRO },
|
|
{ ORDATA (LCTL, dev_ctl[1], 32), REG_HRO },
|
|
{ ORDATA (HFLG, dev_flg[0], 32), REG_HRO },
|
|
{ ORDATA (LFLG, dev_flg[1], 32), REG_HRO },
|
|
{ ORDATA (HFBF, dev_fbf[0], 32), REG_HRO },
|
|
{ ORDATA (LFBF, dev_fbf[1], 32), REG_HRO },
|
|
{ ORDATA (HSRQ, dev_srq[0], 32), REG_HRO },
|
|
{ ORDATA (LSRQ, dev_srq[1], 32), 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, "2116" },
|
|
{ UNIT_MODEL_MASK, UNIT_2115, "", "2115", &cpu_set_model, &cpu_show_model, "2115" },
|
|
{ UNIT_MODEL_MASK, UNIT_2114, "", "2114", &cpu_set_model, &cpu_show_model, "2114" },
|
|
{ UNIT_MODEL_MASK, UNIT_2100, "", "2100", &cpu_set_model, &cpu_show_model, "2100" },
|
|
{ UNIT_MODEL_MASK, UNIT_1000_E, "", "1000-E", &cpu_set_model, &cpu_show_model, "1000-E" },
|
|
{ UNIT_MODEL_MASK, UNIT_1000_E, NULL, "21MX-E", &cpu_set_model, &cpu_show_model, "1000-E" },
|
|
{ UNIT_MODEL_MASK, UNIT_1000_M, "", "1000-M", &cpu_set_model, &cpu_show_model, "1000-M" },
|
|
{ UNIT_MODEL_MASK, UNIT_1000_M, NULL, "21MX-M", &cpu_set_model, &cpu_show_model, "1000-M" },
|
|
|
|
#if defined (HAVE_INT64)
|
|
{ UNIT_MODEL_MASK, UNIT_1000_F, "", "1000-F", &cpu_set_model, &cpu_show_model, "1000-F" },
|
|
#endif
|
|
|
|
{ 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", &cpu_unit, cpu_reg, cpu_mod,
|
|
1, 8, PA_N_SIZE, 1, 8, 16,
|
|
&cpu_ex, &cpu_dep, &cpu_reset,
|
|
&cpu_boot, NULL, NULL,
|
|
NULL, DEV_DEBUG,
|
|
0, cpu_deb, NULL, NULL
|
|
};
|
|
|
|
/* Memory protect data structures
|
|
|
|
mp_dev MP device descriptor
|
|
mp_unit MP unit descriptor
|
|
mp_reg MP register list
|
|
mp_mod MP modifiers list
|
|
*/
|
|
|
|
UNIT mp_unit = { UDATA (NULL, UNIT_MP_SEL1, 0) }; /* default is JSB in, INT in, SEL1 out */
|
|
|
|
REG mp_reg[] = {
|
|
{ FLDATA (CTL, dev_ctl[PRO/32], INT_V (PRO)) },
|
|
{ FLDATA (FLG, dev_flg[PRO/32], INT_V (PRO)) },
|
|
{ FLDATA (FBF, dev_fbf[PRO/32], INT_V (PRO)) },
|
|
{ ORDATA (FR, mp_fence, 15) },
|
|
{ ORDATA (VR, mp_viol, 16) },
|
|
{ FLDATA (MEV, mp_mevff, 0) },
|
|
{ FLDATA (EVR, mp_evrff, 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", &mp_unit, mp_reg, mp_mod,
|
|
1, 8, 1, 1, 8, 16,
|
|
NULL, NULL, &mp_reset,
|
|
NULL, NULL, NULL,
|
|
NULL, DEV_DISABLE | DEV_DIS
|
|
};
|
|
|
|
/* DMA controller data structures
|
|
|
|
dmax_dev DMAx device descriptor
|
|
dmax_reg DMAx register list
|
|
*/
|
|
|
|
UNIT dma0_unit = { UDATA (NULL, 0, 0) };
|
|
|
|
REG dma0_reg[] = {
|
|
{ FLDATA (CMD, dev_cmd[DMA0/32], INT_V (DMA0)) },
|
|
{ FLDATA (CTL, dev_ctl[DMA0/32], INT_V (DMA0)) },
|
|
{ FLDATA (FLG, dev_flg[DMA0/32], INT_V (DMA0)) },
|
|
{ FLDATA (FBF, dev_fbf[DMA0/32], INT_V (DMA0)) },
|
|
{ FLDATA (CTLALT, dev_ctl[DMALT0/32], INT_V (DMALT0)) },
|
|
{ ORDATA (CW1, dmac[0].cw1, 16) },
|
|
{ ORDATA (CW2, dmac[0].cw2, 16) },
|
|
{ ORDATA (CW3, dmac[0].cw3, 16) },
|
|
{ DRDATA (LATENCY, dmac[0].latency, 8) },
|
|
{ FLDATA (BYTE, dmac[0].packer, 31) },
|
|
{ ORDATA (PACKER, dmac[0].packer, 8) },
|
|
{ NULL }
|
|
};
|
|
|
|
DEVICE dma0_dev = {
|
|
"DMA0", &dma0_unit, dma0_reg, NULL,
|
|
1, 8, 1, 1, 8, 16,
|
|
NULL, NULL, &dma0_reset,
|
|
NULL, NULL, NULL,
|
|
NULL, DEV_DISABLE
|
|
};
|
|
|
|
UNIT dma1_unit = { UDATA (NULL, 0, 0) };
|
|
|
|
REG dma1_reg[] = {
|
|
{ FLDATA (CMD, dev_cmd[DMA1/32], INT_V (DMA1)) },
|
|
{ FLDATA (CTL, dev_ctl[DMA1/32], INT_V (DMA1)) },
|
|
{ FLDATA (FLG, dev_flg[DMA1/32], INT_V (DMA1)) },
|
|
{ FLDATA (FBF, dev_fbf[DMA1/32], INT_V (DMA1)) },
|
|
{ FLDATA (CTLALT, dev_ctl[DMALT1/32], INT_V (DMALT1)) },
|
|
{ ORDATA (CW1, dmac[1].cw1, 16) },
|
|
{ ORDATA (CW2, dmac[1].cw2, 16) },
|
|
{ ORDATA (CW3, dmac[1].cw3, 16) },
|
|
{ DRDATA (LATENCY, dmac[1].latency, 8) },
|
|
{ FLDATA (BYTE, dmac[1].packer, 31) },
|
|
{ ORDATA (PACKER, dmac[1].packer, 8) },
|
|
{ NULL }
|
|
};
|
|
|
|
DEVICE dma1_dev = {
|
|
"DMA1", &dma1_unit, dma1_reg, NULL,
|
|
1, 8, 1, 1, 8, 16,
|
|
NULL, NULL, &dma1_reset,
|
|
NULL, NULL, NULL,
|
|
NULL, DEV_DISABLE
|
|
};
|
|
|
|
/* Interrupt defer table (1000 version) */
|
|
|
|
static int32 defer_tab[] = { 0, 1, 1, 1, 0, 0, 0, 1 };
|
|
|
|
/* Device dispatch table */
|
|
|
|
uint32 devdisp (uint32 devno, uint32 inst, uint32 IR, uint32 outdat);
|
|
int32 cpuio (int32 op, int32 IR, int32 outdat);
|
|
int32 ovfio (int32 op, int32 IR, int32 outdat);
|
|
int32 pwrio (int32 op, int32 IR, int32 outdat);
|
|
int32 proio (int32 op, int32 IR, int32 outdat);
|
|
int32 dmsio (int32 op, int32 IR, int32 outdat);
|
|
int32 dmpio (int32 op, int32 IR, int32 outdat);
|
|
int32 nulio (int32 op, int32 IR, int32 outdat);
|
|
|
|
int32 (*dtab[64])() = {
|
|
&cpuio, &ovfio, &dmsio, &dmsio, &pwrio, &proio, &dmpio, &dmpio,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL,
|
|
NULL, NULL, NULL, NULL, NULL, NULL, NULL, NULL
|
|
};
|
|
|
|
t_stat sim_instr (void)
|
|
{
|
|
uint32 intrq, dmarq; /* set after setjmp */
|
|
uint32 iotrap = 0; /* set after setjmp */
|
|
t_stat reason; /* set after setjmp */
|
|
int32 i, dev; /* temp */
|
|
DEVICE *dptr; /* temp */
|
|
DIB *dibp; /* temp */
|
|
int abortval;
|
|
|
|
/* Restore register state */
|
|
|
|
if (dev_conflict ()) return SCPE_STOP; /* check consistency */
|
|
AR = saved_AR & DMASK; /* restore reg */
|
|
BR = saved_BR & DMASK;
|
|
err_PC = PC = PC & VAMASK; /* load local PC */
|
|
reason = 0;
|
|
|
|
/* Restore I/O state */
|
|
|
|
if (mp_dev.flags & DEV_DIS) dtab[PRO] = NULL;
|
|
else dtab[PRO] = &proio; /* set up MP dispatch */
|
|
if (dma0_dev.flags & DEV_DIS) dtab[DMA0] = dtab[DMALT0] = NULL;
|
|
else {
|
|
dtab[DMA0] = &dmpio; /* set up DMA0 dispatch */
|
|
dtab[DMALT0] = &dmsio;
|
|
}
|
|
if (dma1_dev.flags & DEV_DIS) dtab[DMA1] = dtab[DMALT1] = NULL;
|
|
else {
|
|
dtab[DMA1] = &dmpio; /* set up DMA1 dispatch */
|
|
dtab[DMALT1] = &dmsio;
|
|
}
|
|
|
|
for (i = VARDEV; i <= I_DEVMASK; i++) dtab[i] = NULL; /* clr disp table */
|
|
dev_cmd[0] = dev_cmd[0] & M_FXDEV; /* clear dynamic info */
|
|
dev_ctl[0] = dev_ctl[0] & M_FXDEV;
|
|
dev_flg[0] = dev_flg[0] & M_FXDEV;
|
|
dev_fbf[0] = dev_fbf[0] & M_FXDEV;
|
|
dev_srq[0] = dev_srq[1] = 0; /* init svc requests */
|
|
dev_cmd[1] = dev_ctl[1] = dev_flg[1] = dev_fbf[1] = 0;
|
|
for (i = 0; dptr = sim_devices[i]; i++) { /* loop thru dev */
|
|
dibp = (DIB *) dptr->ctxt; /* get DIB */
|
|
if (dibp && !(dptr->flags & DEV_DIS)) { /* exist, enabled? */
|
|
dev = dibp->devno; /* get dev # */
|
|
if (dibp->cmd) { setCMD (dev); } /* restore cmd */
|
|
if (dibp->ctl) { setCTL (dev); } /* restore ctl */
|
|
if (dibp->flg) { setFLG (dev); } /* restore flg */
|
|
clrFBF (dev); /* also sets fbf */
|
|
if (dibp->fbf) { setFBF (dev); } /* restore fbf */
|
|
if (dibp->srq) { setSRQ (dev); } /* restore srq */
|
|
dtab[dev] = dibp->iot; /* set I/O dispatch */
|
|
}
|
|
}
|
|
|
|
/* Configure interrupt deferral table */
|
|
|
|
if (UNIT_CPU_FAMILY == UNIT_FAMILY_21XX) /* 21xx series? */
|
|
defer_tab[ioSFC] = defer_tab[ioSFS] = 0; /* SFC/S doesn't defer */
|
|
else /* 1000 series */
|
|
defer_tab[ioSFC] = defer_tab[ioSFS] = 1; /* SFC/S does defer */
|
|
|
|
/* 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.
|
|
*/
|
|
|
|
abortval = setjmp (save_env); /* set abort hdlr */
|
|
if (abortval != 0) { /* mem mgt abort? */
|
|
setFLG (PRO); /* req interrupt */
|
|
mp_evrff = 0; /* block mp_viol upd */
|
|
}
|
|
dmarq = calc_dma (); /* recalc DMA masks */
|
|
intrq = calc_int (); /* recalc interrupts */
|
|
|
|
/* Main instruction fetch/decode loop */
|
|
|
|
while (reason == 0) { /* loop until halted */
|
|
uint32 IR, MA, absel, v1, t, skip;
|
|
|
|
if (sim_interval <= 0) { /* check clock queue */
|
|
if (reason = sim_process_event ()) break;
|
|
dmarq = calc_dma (); /* recalc DMA reqs */
|
|
intrq = calc_int (); /* recalc interrupts */
|
|
}
|
|
|
|
if (dmarq) {
|
|
if (dmarq & DMAR0) dma_cycle (0, PAMAP); /* DMA1 cycle? */
|
|
if (dmarq & DMAR1) dma_cycle (1, PBMAP); /* DMA2 cycle? */
|
|
dmarq = calc_dma (); /* recalc DMA reqs */
|
|
intrq = calc_int (); /* recalc interrupts */
|
|
}
|
|
|
|
if (intrq && ion_defer) /* interrupt pending but deferred? */
|
|
ion_defer = calc_defer (); /* confirm deferral */
|
|
|
|
/* (From Dave Bryan)
|
|
Unlike most other I/O devices, the MP flag flip-flop is cleared
|
|
automatically when the interrupt is acknowledged and not by a programmed
|
|
instruction (CLF and STF affect the parity error enable FF instead).
|
|
Section 4.4.3 "Memory Protect and I/O Interrupt Generation" of the "HP 1000
|
|
M/E/F-Series Computers Engineering and Reference Documentation" (HP
|
|
92851-90001) says:
|
|
|
|
"When IAK occurs and IRQ5 is asserted, the FLAGBFF is cleared, FLAGFF
|
|
clocked off at next T2, and IRQ5 will no longer occur."
|
|
*/
|
|
|
|
if (intrq && ((intrq <= PRO) || !ion_defer)) { /* interrupt request? */
|
|
iotrap = 1; /* I/O trap cell instr */
|
|
clrFBF (intrq); /* clear flag buffer */
|
|
if (intrq == PRO) clrFLG (PRO); /* MP flag follows fbuf */
|
|
intaddr = intrq; /* save int addr */
|
|
if (dms_enb) dms_sr = dms_sr | MST_ENBI; /* dms enabled? */
|
|
else dms_sr = dms_sr & ~MST_ENBI;
|
|
if (dms_ump) { /* user map? */
|
|
dms_sr = dms_sr | MST_UMPI;
|
|
dms_ump = SMAP; /* switch to system */
|
|
}
|
|
else dms_sr = dms_sr & ~MST_UMPI;
|
|
IR = ReadW (intrq); /* get dispatch instr */
|
|
ion_defer = 1; /* defer interrupts */
|
|
intrq = 0; /* clear request */
|
|
if (((IR & I_NMRMASK) != I_IO) || /* if not I/O or */
|
|
(I_GETIOOP (IR) == ioHLT)) /* if halt, */
|
|
clrCTL (PRO); /* protection off */
|
|
else /* I/O instr leaves MP on */
|
|
mp_mevff = 0; /* but clears MEV flip-flop */
|
|
}
|
|
|
|
else { /* normal instruction */
|
|
iotrap = 0;
|
|
err_PC = PC; /* save PC for error */
|
|
if (sim_brk_summ && /* any breakpoints? */
|
|
sim_brk_test (PC, SWMASK ('E') | /* unconditional or */
|
|
(dms_enb? (dms_ump? SWMASK ('U'): SWMASK ('S')):
|
|
SWMASK ('N')))) { /* or right type for DMS? */
|
|
reason = STOP_IBKPT; /* stop simulation */
|
|
break;
|
|
}
|
|
if (mp_evrff) mp_viol = PC; /* if ok, upd mp_viol */
|
|
IR = ReadW (PC); /* fetch instr */
|
|
PC = (PC + 1) & VAMASK;
|
|
ion_defer = 0;
|
|
}
|
|
|
|
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:
|
|
if (reason = Ea (IR, &MA, intrq)) break; /* AND */
|
|
AR = AR & ReadW (MA);
|
|
break;
|
|
|
|
/* JSB is a little tricky. It is possible to generate both an MP and a DM
|
|
violation simultaneously. 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 for JSB by the
|
|
WriteW call to store the return PC. However, WriteW only checks for fence
|
|
violations above location 2, as normally JSBs to locations 0 and 1 (i.e., the
|
|
A and B register) are allowed. However, if the W5 (JSB) jumper is out, then
|
|
JSB 0 and JSB 1 are MP violations as well and must be caught. We do this
|
|
with an explicit check before calling WriteW.
|
|
|
|
This would seem to violate the above requirement for DM checks before MP
|
|
checks. However, a DM abort cannot occur on a write to 0/1, even if logical
|
|
page 0 is write-protected, because writes to 0/1 do not attempt to access
|
|
memory; they are intercepted and affect the A/B registers instead (micro-
|
|
order TAB is used in the Store field), so no MEV signal is generated.
|
|
*/
|
|
|
|
case 0230:case 0231:case 0232:case 0233:
|
|
case 0234:case 0235:case 0236:case 0237:
|
|
ion_defer = 1; /* defer if JSB,I */
|
|
|
|
case 0030:case 0031:case 0032:case 0033:
|
|
case 0034:case 0035:case 0036:case 0037:
|
|
if (reason = Ea (IR, &MA, intrq)) break; /* JSB */
|
|
|
|
if ((mp_unit.flags & UNIT_MP_JSB) && /* if W5 (JSB) out */
|
|
CTL (PRO) && (MA <= 1)) /* and MP on and JSB 0 or JSB 1 */
|
|
ABORT (ABORT_PRO); /* MP violation */
|
|
|
|
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:
|
|
if (reason = Ea (IR, &MA, intrq)) break; /* XOR */
|
|
AR = AR ^ ReadW (MA);
|
|
break;
|
|
|
|
case 0250:case 0251:case 0252:case 0253:
|
|
case 0254:case 0255:case 0256:case 0257:
|
|
ion_defer = 1; /* defer if JMP,I */
|
|
|
|
case 0050:case 0051:case 0052:case 0053:
|
|
case 0054:case 0055:case 0056:case 0057:
|
|
if (reason = Ea (IR, &MA, intrq)) break; /* JMP */
|
|
mp_dms_jmp (MA); /* validate jump addr */
|
|
PCQ_ENTRY;
|
|
PC = MA; /* jump */
|
|
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:
|
|
if (reason = Ea (IR, &MA, intrq)) break; /* IOR */
|
|
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:
|
|
if (reason = Ea (IR, &MA, intrq)) break; /* ISZ */
|
|
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:
|
|
if (reason = Ea (IR, &MA, intrq)) break; /* ADA */
|
|
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:
|
|
if (reason = Ea (IR, &MA, intrq)) break; /* ADB */
|
|
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:
|
|
if (reason = Ea (IR, &MA, intrq)) break; /* CPA */
|
|
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:
|
|
if (reason = Ea (IR, &MA, intrq)) break; /* CPB */
|
|
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:
|
|
if (reason = Ea (IR, &MA, intrq)) break; /* LDA */
|
|
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:
|
|
if (reason = Ea (IR, &MA, intrq)) break; /* LDB */
|
|
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:
|
|
if (reason = Ea (IR, &MA, intrq)) break; /* STA */
|
|
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:
|
|
if (reason = Ea (IR, &MA, intrq)) break; /* STB */
|
|
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) t = 0; /* CLx */
|
|
else t = ABREG[absel];
|
|
if (IR & 001000) t = t ^ DMASK; /* CMx */
|
|
if (IR & 000001) { /* RSS? */
|
|
if ((IR & 000040) && (E != 0)) skip = 1; /* SEZ,RSS */
|
|
if (IR & 000100) E = 0; /* CLE */
|
|
if (IR & 000200) E = E ^ 1; /* CME */
|
|
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)) skip = 1; /* SZx,RSS */
|
|
if ((IR & 000072) == 0) skip = 1; /* RSS */
|
|
} /* end if RSS */
|
|
else {
|
|
if ((IR & 000040) && (E == 0)) skip = 1; /* SEZ */
|
|
if (IR & 000100) E = 0; /* CLE */
|
|
if (IR & 000200) E = E ^ 1; /* CME */
|
|
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)) skip = 1; /* SZx */
|
|
} /* end if ~RSS */
|
|
ABREG[absel] = 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) E = 0; /* CLE */
|
|
if ((IR & 000010) && ((t & 1) == 0)) /* SLx */
|
|
PC = (PC + 1) & VAMASK;
|
|
ABREG[absel] = 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 = 0; /* continue */
|
|
}
|
|
|
|
else if (reason == NOTE_INDINT) { /* intr pend during indir? */
|
|
PC = err_PC; /* back out of inst */
|
|
reason = 0; /* continue */
|
|
}
|
|
} /* end while */
|
|
|
|
/* Simulation halted */
|
|
|
|
saved_AR = AR & DMASK;
|
|
saved_BR = BR & DMASK;
|
|
if (iotrap && (reason == STOP_HALT)) MR = intaddr; /* HLT in trap cell? */
|
|
else MR = (PC - 1) & VAMASK; /* no, M = P - 1 */
|
|
TR = ReadTAB (MR); /* last word fetched */
|
|
saved_MR = MR; /* save for T cmd update */
|
|
if ((reason == STOP_RSRV) || (reason == STOP_IODV) || /* instr error? */
|
|
(reason == STOP_IND)) PC = err_PC; /* back up PC */
|
|
dms_upd_sr (); /* update dms_sr */
|
|
if (reason == STOP_HALT) /* programmed halt? */
|
|
cpu_set_ldr (NULL, FALSE, NULL, NULL); /* disable loader (ignore errors) */
|
|
for (i = 0; dptr = sim_devices[i]; i++) { /* loop thru dev */
|
|
dibp = (DIB *) dptr->ctxt; /* get DIB */
|
|
if (dibp) { /* exist? */
|
|
dev = dibp->devno;
|
|
dibp->cmd = CMD (dev);
|
|
dibp->ctl = CTL (dev);
|
|
dibp->flg = FLG (dev);
|
|
dibp->fbf = FBF (dev);
|
|
dibp->srq = SRQ (dev);
|
|
}
|
|
}
|
|
pcq_r->qptr = pcq_p; /* update pc q ptr */
|
|
if (dms_enb) /* default breakpoint type */
|
|
if (dms_ump) sim_brk_dflt = SWMASK ('U'); /* to current map mode */
|
|
else sim_brk_dflt = SWMASK ('S');
|
|
else sim_brk_dflt = SWMASK ('N');
|
|
return reason;
|
|
}
|
|
|
|
/* 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) && CTL(PRO));
|
|
|
|
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 = 0; /* 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) return STOP_IND; /* indirect loop? */
|
|
*addr = MA;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Get effective address from IR */
|
|
|
|
t_stat Ea (uint32 IR, uint32 *addr, uint32 irq)
|
|
{
|
|
uint32 MA;
|
|
|
|
MA = IR & (I_IA | I_DISP); /* ind + disp */
|
|
if (IR & I_CP) MA = ((PC - 1) & I_PAGENO) | MA; /* current page? */
|
|
return resolve (MA, addr, irq); /* resolve indirects */
|
|
}
|
|
|
|
/* Shift micro operation */
|
|
|
|
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) E = t & 1; /* disabled ext rgt rot */
|
|
if (op == 06) E = (t >> 15) & 1; /* disabled ext lft rot */
|
|
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.
|
|
|
|
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)
|
|
{
|
|
uint32 dev, sop, iodata, iostat, ab;
|
|
|
|
ab = (ir & I_AB)? 1: 0; /* get A/B select */
|
|
dev = ir & I_DEVMASK; /* get device */
|
|
sop = I_GETIOOP (ir); /* get subopcode */
|
|
if (!iotrap && CTL (PRO) && /* protected? */
|
|
((sop == ioHLT) || /* halt or !ovf? */
|
|
((dev != OVF) && (mp_unit.flags & UNIT_MP_SEL1)))) { /* sel code OK? */
|
|
if (sop == ioLIX) ABREG[ab] = 0; /* A/B writes anyway */
|
|
ABORT (ABORT_PRO);
|
|
}
|
|
iodata = devdisp (dev, sop, ir, ABREG[ab]); /* process I/O */
|
|
ion_defer = defer_tab[sop]; /* set defer */
|
|
if ((sop == ioMIX) || (sop == ioLIX)) /* store ret data */
|
|
ABREG[ab] = iodata & DMASK;
|
|
if (sop == ioHLT) { /* halt? */
|
|
int32 len = strlen (halt_msg); /* find end msg */
|
|
sprintf (&halt_msg[len - 6], "%06o", ir); /* add the halt */
|
|
return STOP_HALT;
|
|
}
|
|
iostat = iodata >> IOT_V_REASON;
|
|
if (iostat == SCPE_OK) return NOTE_IOG; /* normal status */
|
|
else return iostat; /* abnormal status */
|
|
}
|
|
|
|
/* Device dispatch */
|
|
|
|
uint32 devdisp (uint32 devno, uint32 inst, uint32 IR, uint32 dat)
|
|
{
|
|
if (dtab[devno]) return dtab[devno] (inst, IR, dat);
|
|
else return nulio (inst, IR, dat);
|
|
}
|
|
|
|
/* Calculate DMA requests */
|
|
|
|
uint32 calc_dma (void)
|
|
{
|
|
uint32 r = 0;
|
|
|
|
if (CMD (DMA0) && SRQ (dmac[0].cw1 & I_DEVMASK)) /* check DMA0 cycle */
|
|
r = r | DMAR0;
|
|
if (CMD (DMA1) && SRQ (dmac[1].cw1 & I_DEVMASK)) /* check DMA1 cycle */
|
|
r = r | DMAR1;
|
|
return r;
|
|
}
|
|
|
|
/* Determine whether a pending interrupt deferral should be inhibited.
|
|
|
|
Execution of certain instructions generally causes 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.
|
|
*/
|
|
|
|
uint32 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 0; /* yes, so inhibit deferral */
|
|
else
|
|
return 1; /* no, so allow deferral */
|
|
}
|
|
else
|
|
return 1; /* 1000 always allows deferral */
|
|
}
|
|
|
|
/* Calculate interrupt requests
|
|
|
|
This routine takes into account all the relevant state of the
|
|
interrupt system: ion, dev_flg, dev_fbf, and dev_ctl.
|
|
|
|
1. dev_flg & dev_ctl determines the end of the priority grant.
|
|
The break in the chain will occur at the first device for
|
|
which dev_flg & dev_ctl is true. This is determined by
|
|
AND'ing the set bits with their 2's complement; only the low
|
|
order (highest priority) bit will differ. 1 less than
|
|
that, or'd with the single set bit itself, is the mask of
|
|
possible interrupting devices. If ION is clear, only devices
|
|
4 and 5 are eligible to interrupt.
|
|
2. dev_flg & dev_ctl & dev_fbf determines the outstanding
|
|
interrupt requests. All three bits must be on for a device
|
|
to request an interrupt. This is the masked under the
|
|
result from #1 to determine the highest priority interrupt,
|
|
if any.
|
|
*/
|
|
|
|
uint32 calc_int (void)
|
|
{
|
|
int32 j, lomask, mask[2], req[2];
|
|
|
|
lomask = dev_flg[0] & dev_ctl[0] & ~M_NXDEV; /* start chain calc */
|
|
req[0] = lomask & dev_fbf[0]; /* calc requests */
|
|
lomask = lomask & (-lomask); /* chain & -chain */
|
|
mask[0] = lomask | (lomask - 1); /* enabled devices */
|
|
req[0] = req[0] & mask[0]; /* highest request */
|
|
if (ion) { /* ion? */
|
|
if (lomask == 0) { /* no break in chn? */
|
|
mask[1] = dev_flg[1] & dev_ctl[1]; /* do all devices */
|
|
req[1] = mask[1] & dev_fbf[1];
|
|
mask[1] = mask[1] & (-mask[1]);
|
|
mask[1] = mask[1] | (mask[1] - 1);
|
|
req[1] = req[1] & mask[1];
|
|
}
|
|
else req[1] = 0;
|
|
}
|
|
else {
|
|
req[0] = req[0] & (INT_M (PWR) | INT_M (PRO));
|
|
req[1] = 0;
|
|
}
|
|
if (req[0]) { /* if low request */
|
|
for (j = 0; j < 32; j++) { /* find dev # */
|
|
if (req[0] & INT_M (j)) return j;
|
|
}
|
|
}
|
|
if (req[1]) { /* if hi request */
|
|
for (j = 0; j < 32; j++) { /* find dev # */
|
|
if (req[1] & INT_M (j)) return (32 + j);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/* Memory access routines */
|
|
|
|
uint8 ReadB (uint32 va)
|
|
{
|
|
int32 pa;
|
|
|
|
if (dms_enb) pa = dms (va >> 1, dms_ump, RD);
|
|
else pa = va >> 1;
|
|
if (va & 1) return (ReadPW (pa) & 0377);
|
|
else return ((ReadPW (pa) >> 8) & 0377);
|
|
}
|
|
|
|
uint8 ReadBA (uint32 va)
|
|
{
|
|
uint32 pa;
|
|
|
|
if (dms_enb) pa = dms (va >> 1, dms_ump ^ MAP_LNT, RD);
|
|
else pa = va >> 1;
|
|
if (va & 1) return (ReadPW (pa) & 0377);
|
|
else return ((ReadPW (pa) >> 8) & 0377);
|
|
}
|
|
|
|
uint16 ReadW (uint32 va)
|
|
{
|
|
uint32 pa;
|
|
|
|
if (dms_enb) pa = dms (va, dms_ump, RD);
|
|
else pa = va;
|
|
return ReadPW (pa);
|
|
}
|
|
|
|
uint16 ReadWA (uint32 va)
|
|
{
|
|
uint32 pa;
|
|
|
|
if (dms_enb) pa = dms (va, dms_ump ^ MAP_LNT, RD);
|
|
else pa = va;
|
|
return ReadPW (pa);
|
|
}
|
|
|
|
uint16 ReadIO (uint32 va, uint32 map)
|
|
{
|
|
uint32 pa;
|
|
|
|
if (dms_enb) pa = dms_io (va, map);
|
|
else pa = va;
|
|
return M[pa];
|
|
}
|
|
|
|
uint16 ReadPW (uint32 pa)
|
|
{
|
|
if (pa <= 1) return ABREG[pa];
|
|
return M[pa];
|
|
}
|
|
|
|
uint16 ReadTAB (uint32 addr)
|
|
{
|
|
if (addr == 0) return saved_AR;
|
|
else if (addr == 1) return saved_BR;
|
|
else return ReadIO (addr, dms_ump);
|
|
}
|
|
|
|
/* Memory protection test for writes
|
|
|
|
From Dave Bryan: The problem is that memory writes aren't being checked for
|
|
an MP violation if DMS is enabled, i.e., if DMS is enabled, and the page is
|
|
writable, then whether the target is below the MP fence is not checked. [The
|
|
simulator must] do MP check on all writes after DMS translation and violation
|
|
checks are done (so, to pass, the page must be writable AND the target must
|
|
be above the MP fence).
|
|
*/
|
|
|
|
#define MP_TEST(x) (CTL (PRO) && ((x) > 1) && ((x) < mp_fence))
|
|
|
|
void WriteB (uint32 va, uint32 dat)
|
|
{
|
|
uint32 pa, t;
|
|
|
|
if (dms_enb) pa = dms (va >> 1, dms_ump, WR);
|
|
else pa = va >> 1;
|
|
if (MP_TEST (va >> 1)) ABORT (ABORT_PRO);
|
|
if (MEM_ADDR_OK (pa)) {
|
|
t = ReadPW (pa);
|
|
if (va & 1) t = (t & 0177400) | (dat & 0377);
|
|
else t = (t & 0377) | ((dat & 0377) << 8);
|
|
WritePW (pa, t);
|
|
}
|
|
return;
|
|
}
|
|
|
|
void WriteBA (uint32 va, uint32 dat)
|
|
{
|
|
uint32 pa, t;
|
|
|
|
if (dms_enb) {
|
|
dms_viol (va >> 1, MVI_WPR); /* viol if prot */
|
|
pa = dms (va >> 1, dms_ump ^ MAP_LNT, WR);
|
|
}
|
|
else pa = va >> 1;
|
|
if (MP_TEST (va >> 1)) ABORT (ABORT_PRO);
|
|
if (MEM_ADDR_OK (pa)) {
|
|
t = ReadPW (pa);
|
|
if (va & 1) t = (t & 0177400) | (dat & 0377);
|
|
else t = (t & 0377) | ((dat & 0377) << 8);
|
|
WritePW (pa, t);
|
|
}
|
|
return;
|
|
}
|
|
|
|
void WriteW (uint32 va, uint32 dat)
|
|
{
|
|
uint32 pa;
|
|
|
|
if (dms_enb) pa = dms (va, dms_ump, WR);
|
|
else pa = va;
|
|
if (MP_TEST (va)) ABORT (ABORT_PRO);
|
|
if (MEM_ADDR_OK (pa)) WritePW (pa, dat);
|
|
return;
|
|
}
|
|
|
|
void WriteWA (uint32 va, uint32 dat)
|
|
{
|
|
int32 pa;
|
|
|
|
if (dms_enb) {
|
|
dms_viol (va, MVI_WPR); /* viol if prot */
|
|
pa = dms (va, dms_ump ^ MAP_LNT, WR);
|
|
}
|
|
else pa = va;
|
|
if (MP_TEST (va)) ABORT (ABORT_PRO);
|
|
if (MEM_ADDR_OK (pa)) WritePW (pa, dat);
|
|
return;
|
|
}
|
|
|
|
void WriteIO (uint32 va, uint32 dat, uint32 map)
|
|
{
|
|
uint32 pa;
|
|
|
|
if (dms_enb) pa = dms_io (va, map);
|
|
else pa = va;
|
|
if (MEM_ADDR_OK (pa)) M[pa] = dat & DMASK;
|
|
return;
|
|
}
|
|
|
|
void WritePW (uint32 pa, uint32 dat)
|
|
{
|
|
if (pa <= 1) ABREG[pa] = dat & DMASK;
|
|
else M[pa] = dat & DMASK;
|
|
return;
|
|
}
|
|
|
|
/* DMS relocation for CPU access */
|
|
|
|
uint32 dms (uint32 va, uint32 map, uint32 prot)
|
|
{
|
|
uint32 pgn, mpr;
|
|
|
|
if (va <= 1) return va; /* A, B */
|
|
pgn = VA_GETPAG (va); /* get page num */
|
|
if (pgn == 0) { /* base page? */
|
|
uint32 dms_fence = dms_sr & MST_FENCE; /* get fence value */
|
|
if ((dms_sr & MST_FLT)? /* check unmapped */
|
|
(va >= dms_fence): /* 1B10: >= fence */
|
|
(va < dms_fence)) { /* 0B10: < fence */
|
|
if (prot == WR) dms_viol (va, MVI_BPG); /* if W, viol */
|
|
return va; /* no mapping */
|
|
}
|
|
}
|
|
mpr = dms_map[map + pgn]; /* get map reg */
|
|
if (mpr & prot) dms_viol (va, prot); /* prot violation? */
|
|
return (MAP_GETPAG (mpr) | VA_GETOFF (va));
|
|
}
|
|
|
|
/* DMS relocation for IO access */
|
|
|
|
uint32 dms_io (uint32 va, uint32 map)
|
|
{
|
|
uint32 pgn, mpr;
|
|
|
|
if (va <= 1) return va; /* A, B */
|
|
pgn = VA_GETPAG (va); /* get page num */
|
|
if (pgn == 0) { /* base page? */
|
|
uint32 dms_fence = dms_sr & MST_FENCE; /* get fence value */
|
|
if ((dms_sr & MST_FLT)? /* check unmapped */
|
|
(va >= dms_fence): /* 1B10: >= fence */
|
|
(va < dms_fence)) { /* 0B10: < fence */
|
|
return va; /* no mapping */
|
|
}
|
|
}
|
|
mpr = dms_map[map + pgn]; /* get map reg */
|
|
return (MAP_GETPAG (mpr) | VA_GETOFF (va));
|
|
}
|
|
|
|
/* DMS relocation for console access */
|
|
|
|
uint32 dms_cons (uint32 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 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 map_sel = dms_ump; /* dflt to log addr, cur map */
|
|
if (va >= VASIZE) return MEMSIZE; /* virtual, must be 15b */
|
|
else if (dms_enb) return dms_io (va, map_sel); /* DMS on? go thru map */
|
|
else return va; /* else return virtual */
|
|
}
|
|
|
|
/* Mem protect and DMS validation for jumps */
|
|
|
|
void mp_dms_jmp (uint32 va)
|
|
{
|
|
uint32 pgn = VA_GETPAG (va); /* get page num */
|
|
|
|
if ((pgn == 0) && (va > 1)) { /* base page? */
|
|
uint32 dms_fence = dms_sr & MST_FENCE; /* get fence value */
|
|
if ((dms_sr & MST_FLT)? /* check unmapped */
|
|
(va >= dms_fence): /* 1B10: >= fence */
|
|
(va < dms_fence)) { /* 0B10: < fence */
|
|
dms_viol (va, MVI_BPG); /* if W, viol */
|
|
return; /* PRO not set */
|
|
}
|
|
}
|
|
if (CTL (PRO) && (va < mp_fence)) ABORT (ABORT_PRO); /* base page MPR */
|
|
return;
|
|
}
|
|
|
|
/* DMS read and write maps */
|
|
|
|
uint16 dms_rmap (uint32 mapi)
|
|
{
|
|
mapi = mapi & MAP_MASK;
|
|
return (dms_map[mapi] & ~MAP_MBZ);
|
|
}
|
|
|
|
void dms_wmap (uint32 mapi, uint32 dat)
|
|
{
|
|
mapi = mapi & MAP_MASK;
|
|
dms_map[mapi] = (uint16) (dat & ~MAP_MBZ);
|
|
return;
|
|
}
|
|
|
|
/* DMS violation */
|
|
|
|
void dms_viol (uint32 va, uint32 st)
|
|
{
|
|
dms_vr = st | VA_GETPAG (va) |
|
|
((st & (MVI_RPR | MVI_WPR))? MVI_MEB: 0) | /* set MEB */
|
|
(dms_enb? MVI_MEM: 0) | /* set MEM */
|
|
(dms_ump? MVI_UMP: 0); /* set UMAP */
|
|
if (CTL (PRO)) { /* protected? */
|
|
mp_mevff = 1; /* signal dms */
|
|
ABORT (ABORT_PRO); /* abort */
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* DMS update status */
|
|
|
|
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 (CTL (PRO)) dms_sr = dms_sr | MST_PRO;
|
|
return dms_sr;
|
|
}
|
|
|
|
/* Device 0 (CPU) I/O routine
|
|
|
|
NOTE: LIx/MIx reads floating I/O bus (0 on all machines).
|
|
|
|
NOTE: 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. PRESET also issues CRS (with POPIO).
|
|
|
|
From Dave Bryan: RTE uses the undocumented instruction "SFS 0,C" 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."
|
|
*/
|
|
|
|
int32 cpuio (int32 inst, int32 IR, int32 dat)
|
|
{
|
|
int i;
|
|
|
|
switch (inst) { /* case on opcode */
|
|
|
|
case ioFLG: /* flag */
|
|
ion = (IR & I_HC)? 0: 1; /* interrupts off/on */
|
|
return dat;
|
|
|
|
case ioSFC: /* skip flag clear */
|
|
if (!ion) PC = (PC + 1) & VAMASK;
|
|
break;
|
|
|
|
case ioSFS: /* skip flag set */
|
|
if (ion) PC = (PC + 1) & VAMASK;
|
|
break;
|
|
|
|
case ioLIX: /* load */
|
|
dat = 0; /* returns 0 */
|
|
break;
|
|
|
|
case ioCTL: /* control */
|
|
if (IR & I_CTL) /* CLC 0 sends CRS */
|
|
for (i = 0; i <= I_DEVMASK; i++) /* to all devices */
|
|
devdisp (i, ioCRS, I_CTL + i, 0); /* IR -> "CLC i" for convenience */
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (IR & I_HC) ion = 0; /* HC option */
|
|
return dat;
|
|
}
|
|
|
|
/* Device 1 (overflow/S-register) I/O routine
|
|
|
|
NOTE: The S register is read-only on the 2115/2116. It is read/write on
|
|
the 2114, 2100, and 1000.
|
|
*/
|
|
|
|
int32 ovfio (int32 inst, int32 IR, int32 dat)
|
|
{
|
|
switch (inst) { /* case on opcode */
|
|
|
|
case ioFLG: /* flag */
|
|
O = (IR & I_HC)? 0: 1; /* clear/set overflow */
|
|
return dat;
|
|
|
|
case ioSFC: /* skip flag clear */
|
|
if (!O) PC = (PC + 1) & VAMASK;
|
|
break; /* can clear flag */
|
|
|
|
case ioSFS: /* skip flag set */
|
|
if (O) PC = (PC + 1) & VAMASK;
|
|
break; /* can clear flag */
|
|
|
|
case ioMIX: /* merge */
|
|
dat = dat | SR;
|
|
break;
|
|
|
|
case ioLIX: /* load */
|
|
dat = SR;
|
|
break;
|
|
|
|
case ioOTX: /* output */
|
|
if ((UNIT_CPU_MODEL != UNIT_2116) &&
|
|
(UNIT_CPU_MODEL != UNIT_2115))
|
|
SR = dat;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (IR & I_HC) O = 0; /* HC option */
|
|
return dat;
|
|
}
|
|
|
|
/* Device 4 (power fail) I/O routine */
|
|
|
|
int32 pwrio (int32 inst, int32 IR, int32 dat)
|
|
{
|
|
switch (inst) { /* case on opcode */
|
|
|
|
case ioMIX: /* merge */
|
|
dat = dat | intaddr;
|
|
break;
|
|
|
|
case ioLIX: /* load */
|
|
dat = intaddr;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return dat;
|
|
}
|
|
|
|
/* Device 5 (memory protect) I/O routine
|
|
|
|
From Dave Bryan: Examination of the schematics for the MP card in the
|
|
engineering documentation shows that the SFS and SFC I/O backplane signals
|
|
gate the output of the MEVFF onto the SKF line unconditionally.
|
|
|
|
The MEVFF records memory expansion (a.k.a. dynamic mapping) violations. It
|
|
is set when an DM violation is encountered. It is cleared on POPIO, STC 5,
|
|
and -HLT * IOGSP * INTPT. The latter occurs when an interrupt causes
|
|
execution of a non-halt I/O instruction in the interrupt trap cell.
|
|
*/
|
|
|
|
int32 proio (int32 inst, int32 IR, int32 dat)
|
|
{
|
|
switch (inst) { /* case on opcode */
|
|
|
|
case ioSFC: /* skip flag clear */
|
|
if (!mp_mevff) PC = (PC + 1) & VAMASK; /* skip if mem prot */
|
|
break;
|
|
|
|
case ioSFS: /* skip flag set */
|
|
if (mp_mevff) PC = (PC + 1) & VAMASK; /* skip if DMS */
|
|
break;
|
|
|
|
case ioMIX: /* merge */
|
|
dat = dat | mp_viol;
|
|
break;
|
|
|
|
case ioLIX: /* load */
|
|
dat = mp_viol;
|
|
break;
|
|
|
|
case ioOTX: /* output */
|
|
mp_fence = dat & VAMASK;
|
|
if (cpu_unit.flags & UNIT_2100) iop_sp = mp_fence;
|
|
break;
|
|
|
|
case ioCRS: /* control reset */
|
|
case ioCTL: /* control clear/set */
|
|
if ((IR & I_CTL) == 0) { /* STC */
|
|
setCTL (PRO);
|
|
dms_vr = 0;
|
|
mp_evrff = 1; /* allow mp_viol upd */
|
|
mp_mevff = 0; /* clear DMS flag */
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (IR & I_HC) { clrFLG (PRO); } /* HC option */
|
|
return dat;
|
|
}
|
|
|
|
/* Devices 2,3 (secondary DMA) I/O routine.
|
|
|
|
Implements control word 2 (memory address) and control word 3 (word count).
|
|
|
|
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.
|
|
|
|
Note: because the I/O bus floats to zero on 211x computers, LIA/MIA (word
|
|
count) returns zeros in the unused bit locations, even though the word count
|
|
is a negative value.
|
|
*/
|
|
|
|
int32 dmsio (int32 inst, int32 IR, int32 dat)
|
|
{
|
|
int32 ch;
|
|
|
|
ch = IR & 1; /* get channel num */
|
|
switch (inst) { /* case on opcode */
|
|
|
|
case ioLIX: /* load remaining word count */
|
|
dat = 0;
|
|
|
|
case ioMIX: /* merge */
|
|
if (UNIT_CPU_MODEL == UNIT_2114) /* 2114? */
|
|
dat = dat | (dmac[ch].cw3 & 0017777); /* only 13-bit count */
|
|
else if (UNIT_CPU_TYPE == UNIT_TYPE_211X) /* 2115/2116? */
|
|
dat = dat | (dmac[ch].cw3 & 0037777); /* only 14-bit count */
|
|
else
|
|
dat = dat | dmac[ch].cw3; /* rest use full value */
|
|
break;
|
|
|
|
case ioOTX: /* output */
|
|
if (CTL (DMALT0 + ch)) /* word count selected? */
|
|
dmac[ch].cw3 = dat; /* save count */
|
|
else /* memory address selected */
|
|
if (UNIT_CPU_MODEL == UNIT_2114) /* 2114? */
|
|
dmac[ch].cw2 = dat & 0137777; /* 14-bit address */
|
|
else
|
|
dmac[ch].cw2 = dat; /* full address stored */
|
|
break;
|
|
|
|
case ioCRS: /* control reset */
|
|
case ioCTL: /* control clear/set */
|
|
if (IR & I_CTL) { clrCTL (DMALT0 + ch); } /* CLC */
|
|
else { setCTL (DMALT0 + ch); } /* STC */
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return dat;
|
|
}
|
|
|
|
/* Devices 6,7 (primary DMA) I/O routine
|
|
|
|
Implements control word 1 (device address) and DMA control.
|
|
|
|
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/6), 12895A
|
|
(2100), and 12897B (1000) support the full 10-77 range of select codes.
|
|
|
|
The 12578A supports byte-sized transfers by setting bit 14. Bit 14 is
|
|
ignored by all other DMA cards, which support word transfers only.
|
|
|
|
NOTE: LIx/MIx reads floating S-bus (1 on 21MX, 0 on 211x/2100).
|
|
|
|
NOTE: CRS clears control and command flip-flops, whereas CLC clears only
|
|
control.
|
|
*/
|
|
|
|
int32 dmpio (int32 inst, int32 IR, int32 dat)
|
|
{
|
|
int32 ch;
|
|
|
|
ch = IR & 1; /* get channel number */
|
|
|
|
switch (inst) { /* case on opcode */
|
|
|
|
case ioFLG: /* flag */
|
|
if ((IR & I_HC) == 0) { /* set->abort */
|
|
setFLG (DMA0 + ch); /* set flag */
|
|
clrCMD (DMA0 + ch); /* clr cmd */
|
|
}
|
|
break;
|
|
|
|
case ioSFC: /* skip flag clear */
|
|
if (FLG (DMA0 + ch) == 0) PC = (PC + 1) & VAMASK;
|
|
break;
|
|
|
|
case ioSFS: /* skip flag set */
|
|
if (FLG (DMA0 + ch) != 0) PC = (PC + 1) & VAMASK;
|
|
break;
|
|
|
|
case ioLIX: /* load */
|
|
dat = 0;
|
|
|
|
case ioMIX: /* merge */
|
|
if (UNIT_CPU_TYPE == UNIT_TYPE_1000)
|
|
dat = DMASK;
|
|
break;
|
|
|
|
case ioOTX: /* output */
|
|
if (UNIT_CPU_MODEL == UNIT_2114) /* 12607? */
|
|
dmac[ch].cw1 = (dat & 0137707) | 010; /* mask SC, convert to 10-17 */
|
|
else if (UNIT_CPU_TYPE == UNIT_TYPE_211X) /* 12578? */
|
|
dmac[ch].cw1 = dat; /* store full select code, flags */
|
|
else /* 12895, 12897 */
|
|
dmac[ch].cw1 = dat & ~DMA1_PB; /* clip byte-packing flag */
|
|
break;
|
|
|
|
case ioCRS: /* control reset */
|
|
clrCMD (DMA0 + ch); /* clear command flip-flop */
|
|
|
|
case ioCTL: /* control */
|
|
if (IR & I_CTL) { clrCTL (DMA0 + ch); } /* CLC: cmd unchgd */
|
|
else { /* STC */
|
|
if (UNIT_CPU_TYPE == UNIT_TYPE_211X) /* slow DMA card? */
|
|
dmac[ch].latency = 1; /* needs startup latency */
|
|
else
|
|
dmac[ch].latency = 0; /* DCPC starts immediately */
|
|
|
|
dmac[ch].packer = 0; /* clear packing register */
|
|
setCTL (DMA0 + ch); /* set ctl, cmd */
|
|
setCMD (DMA0 + ch);
|
|
}
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
if (IR & I_HC) { clrFLG (DMA0 + ch); } /* HC option */
|
|
return dat;
|
|
}
|
|
|
|
/* DMA cycle routine
|
|
|
|
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.
|
|
|
|
The last cycle (word count reaches 0) logic is quite tricky.
|
|
Input cases:
|
|
- CLC requested: issue CLC
|
|
Output cases:
|
|
- neither STC nor CLC requested: issue CLF
|
|
- STC requested but not CLC: issue STC,C
|
|
- CLC requested but not STC: issue CLC,C
|
|
- STC and CLC both requested: issue STC,C and CLC,C, in that order
|
|
Either: issue EDT (pass DMA channel number and I/O flag)
|
|
*/
|
|
|
|
void dma_cycle (uint32 ch, uint32 map)
|
|
{
|
|
int32 temp, dev, MA;
|
|
int32 inp = dmac[ch].cw2 & DMA2_OI; /* input flag */
|
|
int32 byt = dmac[ch].cw1 & DMA1_PB; /* pack bytes flag */
|
|
|
|
if (dmac[ch].latency) { /* start-up latency? */
|
|
dmac[ch].latency = dmac[ch].latency - 1; /* decrease it */
|
|
return; /* that's all this cycle */
|
|
}
|
|
|
|
dev = dmac[ch].cw1 & I_DEVMASK; /* get device */
|
|
MA = dmac[ch].cw2 & VAMASK; /* get mem addr */
|
|
|
|
if (inp) { /* input cycle? */
|
|
temp = devdisp (dev, ioLIX, dev, 0); /* do LIA dev */
|
|
|
|
if (byt) { /* byte packing? */
|
|
if (dmac[ch].packer & DMA_OE) { /* second byte? */
|
|
temp = (dmac[ch].packer << 8) | /* merge stored byte */
|
|
(temp & DMASK8);
|
|
WriteIO (MA, temp, map); /* store word data */
|
|
}
|
|
else /* first byte */
|
|
dmac[ch].packer = (temp & DMASK8); /* save it */
|
|
|
|
dmac[ch].packer = dmac[ch].packer ^ DMA_OE; /* flip odd/even bit */
|
|
}
|
|
else /* no byte packing */
|
|
WriteIO (MA, temp, map); /* store word data */
|
|
}
|
|
else { /* output cycle */
|
|
if (byt) { /* byte packing? */
|
|
if (dmac[ch].packer & DMA_OE) /* second byte? */
|
|
temp = dmac[ch].packer & DMASK8; /* retrieve it */
|
|
|
|
else { /* first byte */
|
|
dmac[ch].packer = ReadIO (MA, map); /* read word data */
|
|
temp = (dmac[ch].packer >> 8) & DMASK8; /* get high byte */
|
|
}
|
|
|
|
dmac[ch].packer = dmac[ch].packer ^ DMA_OE; /* flip odd/even bit */
|
|
}
|
|
else /* no byte packing */
|
|
temp = ReadIO (MA, map); /* read word data */
|
|
|
|
devdisp (dev, ioOTX, dev, temp); /* do OTA dev */
|
|
}
|
|
|
|
if ((dmac[ch].packer & DMA_OE) == 0) { /* new byte or no packing? */
|
|
dmac[ch].cw2 = (dmac[ch].cw2 & DMA2_OI) | /* increment address */
|
|
((dmac[ch].cw2 + 1) & VAMASK);
|
|
dmac[ch].cw3 = (dmac[ch].cw3 + 1) & DMASK; /* increment word count */
|
|
}
|
|
|
|
if (dmac[ch].cw3) { /* more to do? */
|
|
if (dmac[ch].cw1 & DMA1_STC) /* if STC flag, */
|
|
devdisp (dev, ioCTL, I_HC + dev, 0); /* do STC,C dev */
|
|
else devdisp (dev, ioFLG, I_HC + dev, 0); /* else CLF dev */
|
|
}
|
|
else {
|
|
if (inp) { /* last cycle, input? */
|
|
if (dmac[ch].cw1 & DMA1_CLC) /* CLC at end? */
|
|
devdisp (dev, ioCTL, I_CTL + dev, 0); /* yes */
|
|
} /* end input */
|
|
else { /* output */
|
|
if ((dmac[ch].cw1 & (DMA1_STC | DMA1_CLC)) == 0)
|
|
devdisp (dev, ioFLG, I_HC + dev, 0); /* clear flag */
|
|
if (dmac[ch].cw1 & DMA1_STC) /* if STC flag, */
|
|
devdisp (dev, ioCTL, I_HC + dev, 0); /* do STC,C dev */
|
|
if (dmac[ch].cw1 & DMA1_CLC) /* CLC at end? */
|
|
devdisp (dev, ioCTL, I_HC + I_CTL + dev, 0); /* yes */
|
|
} /* end output */
|
|
setFLG (DMA0 + ch); /* set DMA flg */
|
|
clrCMD (DMA0 + ch); /* clr DMA cmd */
|
|
devdisp (dev, ioEDT, dev, inp | ch); /* do EDT */
|
|
}
|
|
return;
|
|
}
|
|
|
|
/* Unimplemented device routine
|
|
|
|
NOTE: For SC < 10, LIx/MIx reads floating S-bus (-1 on 21MX, 0 on 211x/2100).
|
|
For SC >= 10, LIx/MIx reads floating I/O bus (0 on all machines).
|
|
*/
|
|
|
|
int32 nulio (int32 inst, int32 IR, int32 dat)
|
|
{
|
|
int32 devd;
|
|
|
|
devd = IR & I_DEVMASK; /* get device no */
|
|
switch (inst) { /* case on opcode */
|
|
|
|
case ioSFC: /* skip flag clear */
|
|
PC = (PC + 1) & VAMASK;
|
|
break;
|
|
|
|
case ioLIX: /* load */
|
|
dat = 0;
|
|
|
|
case ioMIX: /* merge */
|
|
if ((devd < VARDEV) && (UNIT_CPU_TYPE == UNIT_TYPE_1000))
|
|
dat = DMASK;
|
|
break;
|
|
|
|
default:
|
|
break;
|
|
}
|
|
|
|
return (stop_dev << IOT_V_REASON) | dat;
|
|
}
|
|
|
|
/* Reset routines */
|
|
|
|
t_stat cpu_reset (DEVICE *dptr)
|
|
{
|
|
E = 0;
|
|
O = 0;
|
|
ion = ion_defer = 0;
|
|
clrCMD (PWR);
|
|
clrCTL (PWR);
|
|
clrFLG (PWR);
|
|
clrFBF (PWR);
|
|
dev_srq[0] = dev_srq[0] & ~M_FXDEV;
|
|
dms_enb = dms_ump = 0; /* init DMS */
|
|
dms_sr = 0;
|
|
dms_vr = 0;
|
|
pcq_r = find_reg ("PCQ", NULL, dptr);
|
|
sim_brk_types = ALL_BKPTS;
|
|
sim_brk_dflt = SWMASK ('N'); /* type is nomap as DMS is off */
|
|
|
|
if (M == NULL) { /* initial call? */
|
|
M = 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 */
|
|
}
|
|
}
|
|
|
|
if (pcq_r) pcq_r->qptr = 0;
|
|
else return SCPE_IERR;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat mp_reset (DEVICE *dptr)
|
|
{
|
|
clrCTL (PRO);
|
|
clrFLG (PRO);
|
|
clrFBF (PRO);
|
|
mp_fence = 0; /* init mprot */
|
|
mp_viol = 0;
|
|
mp_mevff = 0;
|
|
mp_evrff = 1;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat dma0_reset (DEVICE *tptr)
|
|
{
|
|
if (UNIT_CPU_MODEL != UNIT_2114) /* 2114 has only one channel */
|
|
hp_enbdis_pair (&dma0_dev, &dma1_dev); /* make pair cons */
|
|
clrCMD (DMA0);
|
|
clrCTL (DMA0);
|
|
setFLG (DMA0);
|
|
clrSRQ (DMA0);
|
|
clrCTL (DMALT0);
|
|
dmac[0].latency = dmac[0].packer = 0;
|
|
if (sim_switches & SWMASK ('P')) /* power up? */
|
|
dmac[0].cw1 = dmac[0].cw2 = dmac[0].cw3 = 0;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
t_stat dma1_reset (DEVICE *tptr)
|
|
{
|
|
if (UNIT_CPU_MODEL != UNIT_2114) /* 2114 has only one channel */
|
|
hp_enbdis_pair (&dma1_dev, &dma0_dev); /* make pair cons */
|
|
clrCMD (DMA1);
|
|
clrCTL (DMA1);
|
|
setFLG (DMA1);
|
|
clrSRQ (DMA1);
|
|
clrCTL (DMALT1);
|
|
dmac[1].latency = dmac[1].packer = 0;
|
|
if (sim_switches & SWMASK ('P')) /* power up? */
|
|
dmac[1].cw1 = dmac[1].cw2 = dmac[1].cw3 = 0;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Memory examine */
|
|
|
|
t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
|
|
{
|
|
int32 d;
|
|
|
|
if ((sw & ALL_MAPMODES) && (dms_enb == 0)) /* req map with DMS off? */
|
|
return SCPE_NOFNC; /* command not allowed */
|
|
addr = dms_cons (addr, sw);
|
|
if (addr >= MEMSIZE) return SCPE_NXM;
|
|
if (!(sw & SIM_SW_REST) && (addr == 0)) d = saved_AR;
|
|
else if (!(sw & SIM_SW_REST) && (addr == 1)) d = saved_BR;
|
|
else d = M[addr];
|
|
if (vptr != NULL) *vptr = d & DMASK;
|
|
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);
|
|
if (addr >= MEMSIZE) return SCPE_NXM;
|
|
if (!(sw & SIM_SW_REST) && (addr == 0)) saved_AR = val & DMASK;
|
|
else if (!(sw & SIM_SW_REST) && (addr == 1)) saved_BR = val & DMASK;
|
|
else M[addr] = val & DMASK;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Set device number */
|
|
|
|
t_stat hp_setdev (UNIT *uptr, int32 num, char *cptr, void *desc)
|
|
{
|
|
DEVICE *dptr = (DEVICE *) desc;
|
|
DIB *dibp;
|
|
int32 i, newdev;
|
|
t_stat r;
|
|
|
|
if (cptr == NULL) return SCPE_ARG;
|
|
if ((desc == NULL) || (num > 1)) return SCPE_IERR;
|
|
dibp = (DIB *) dptr->ctxt;
|
|
if (dibp == NULL) return SCPE_IERR;
|
|
newdev = get_uint (cptr, 8, I_DEVMASK - num, &r);
|
|
if (r != SCPE_OK) return r;
|
|
if (newdev < VARDEV) return SCPE_ARG;
|
|
for (i = 0; i <= num; i++, dibp++) dibp->devno = newdev + i;
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* Show device number */
|
|
|
|
t_stat hp_showdev (FILE *st, UNIT *uptr, int32 num, void *desc)
|
|
{
|
|
DEVICE *dptr = (DEVICE *) desc;
|
|
DIB *dibp;
|
|
int32 i;
|
|
|
|
if ((desc == NULL) || (num > 1)) return SCPE_IERR;
|
|
dibp = (DIB *) dptr->ctxt;
|
|
if (dibp == NULL) return SCPE_IERR;
|
|
fprintf (st, "devno=%o", dibp->devno);
|
|
for (i = 1; i <= num; i++) fprintf (st, "/%o", dibp->devno + i);
|
|
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 */
|
|
|
|
t_bool dev_conflict (void)
|
|
{
|
|
DEVICE *dptr;
|
|
DIB *dibp;
|
|
uint32 i, j, k;
|
|
t_bool is_conflict = FALSE;
|
|
uint32 conflicts[I_DEVMASK + 1] = { 0 };
|
|
|
|
for (i = 0; dptr = sim_devices[i]; i++) {
|
|
dibp = (DIB *) dptr->ctxt;
|
|
if (dibp && !(dptr->flags & DEV_DIS))
|
|
if (++conflicts[dibp->devno] > 1)
|
|
is_conflict = TRUE;
|
|
}
|
|
|
|
if (is_conflict) {
|
|
sim_ttcmd();
|
|
for (i = 0; i <= I_DEVMASK; 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; dptr = sim_devices[j]; j++) {
|
|
dibp = (DIB *) dptr->ctxt;
|
|
if (dibp && !(dptr->flags & DEV_DIS) && (i == dibp->devno)) {
|
|
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 = 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++) mc = mc | M[i];
|
|
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 = MEMSIZE - IBL_LNT; /* reserve memory for loader */
|
|
}
|
|
else /* loader unsupported */
|
|
fwanxm = MEMSIZE = new_size; /* set new memory size */
|
|
|
|
for (i = fwanxm; i < old_size; i++) M[i] = 0; /* zero non-existent memory */
|
|
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 0). 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? */
|
|
dma0_dev.flags = dma0_dev.flags & ~DEV_DIS; /* enable DMA channel 0 */
|
|
|
|
if (new_model == UNIT_2114) /* 2114 has only one channel */
|
|
dma1_dev.flags = dma1_dev.flags | DEV_DIS; /* disable channel 1 */
|
|
else /* all others have two channels */
|
|
dma1_dev.flags = dma1_dev.flags & ~DEV_DIS; /* enable it */
|
|
}
|
|
else {
|
|
dma0_dev.flags = dma0_dev.flags | DEV_DIS; /* disable channel 0 */
|
|
dma1_dev.flags = dma1_dev.flags | DEV_DIS; /* disable channel 1 */
|
|
}
|
|
|
|
if (cpu_features[new_index].opt & UNIT_DMA) { /* DMA an option? */
|
|
dma0_dev.flags = dma0_dev.flags | DEV_DISABLE; /* make it alterable */
|
|
|
|
if (new_model == UNIT_2114) /* 2114 has only one channel */
|
|
dma1_dev.flags = dma1_dev.flags & ~DEV_DISABLE; /* make it unalterable */
|
|
else /* all others have two channels */
|
|
dma1_dev.flags = dma1_dev.flags | DEV_DISABLE; /* make it alterable */
|
|
}
|
|
else {
|
|
dma0_dev.flags = dma0_dev.flags & ~DEV_DISABLE; /* make it unalterable */
|
|
dma1_dev.flags = dma1_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 (&dma0_dev); /* delete DCPC names */
|
|
deassign_device (&dma1_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 (&dma0_dev, "DCPC0"); /* change DMA device name */
|
|
assign_device (&dma1_dev, "DCPC1"); /* 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 = 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 = MEMSIZE - IBL_LNT; /* reserve memory for loader */
|
|
else
|
|
fwanxm = 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 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;
|
|
}
|
|
|
|
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 uint16 loader[IBL_LNT];
|
|
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 = 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 = MEMSIZE; /* increase available memory */
|
|
}
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
/* IBL routine (CPU boot) */
|
|
|
|
t_stat cpu_boot (int32 unitno, DEVICE *dptr)
|
|
{
|
|
extern const uint16 ptr_rom[IBL_LNT], dq_rom[IBL_LNT];
|
|
extern const uint16 ms_rom[IBL_LNT], ds_rom[IBL_LNT];
|
|
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);
|
|
break;
|
|
|
|
case 1: /* DP/DQ boot */
|
|
ibl_copy (dq_rom, dev);
|
|
break;
|
|
|
|
case 2: /* MS boot */
|
|
ibl_copy (ms_rom, dev);
|
|
break;
|
|
|
|
case 3: /* DS boot */
|
|
ibl_copy (ds_rom,dev);
|
|
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 2's complement of boot base in last location
|
|
|
|
Notes:
|
|
- SR settings are done by the caller
|
|
- Boot ROM's must be assembled with a device code of 10 (10 and 11 for
|
|
devices requiring two codes)
|
|
*/
|
|
|
|
t_stat ibl_copy (const uint16 pboot[IBL_LNT], int32 dev)
|
|
{
|
|
int32 i;
|
|
uint16 wd;
|
|
|
|
cpu_set_ldr (NULL, TRUE, NULL, NULL); /* enable loader (ignore errors) */
|
|
|
|
if (dev < 010) return SCPE_ARG; /* valid device? */
|
|
PC = ((MEMSIZE - 1) & ~IBL_MASK) & VAMASK; /* start at mem top */
|
|
for (i = 0; i < IBL_LNT; i++) { /* copy bootstrap */
|
|
wd = pboot[i]; /* get word */
|
|
if (((wd & I_NMRMASK) == I_IO) && /* IO instruction? */
|
|
((wd & I_DEVMASK) >= 010) && /* dev >= 10? */
|
|
(I_GETIOOP (wd) != ioHLT)) /* not a HALT? */
|
|
M[PC + i] = (wd + (dev - 010)) & DMASK; /* change dev code */
|
|
else M[PC + i] = wd; /* leave unchanged */
|
|
}
|
|
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 */
|
|
return SCPE_OK;
|
|
}
|