The paradigm of using a "weak" linker reference to find what was previously the vm_init_routine() doesn't work reliably on all compile environments supported by the simulators. This has been reported in #794 and it came up again in #862. This change assures that it will not come up again AND it reliably solves the problem with Visual Studio compilers (and linker) that randomly chooses whether to have the desired effect or not. Of the 82 simulators which are currently part of simh, only these three used the sim_vm_init() interface, so removing it had relatively minor impact.
5244 lines
273 KiB
C
5244 lines
273 KiB
C
/* hp2100_cpu.c: HP 21xx/1000 Central Processing Unit simulator
|
|
|
|
Copyright (c) 1993-2016, Robert M. Supnik
|
|
Copyright (c) 2017-2019, J. David Bryan
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
of this software and associated documentation files (the "Software"), to deal
|
|
in the Software without restriction, including without limitation the rights
|
|
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
copies of the Software, and to permit persons to whom the Software is
|
|
furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
|
|
ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the names of the authors shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from the authors.
|
|
|
|
CPU 2114C/2115A/2116C/2100A/1000-M/E/F Central Processing Unit
|
|
I/O subsystem
|
|
Power Fail Recovery System
|
|
|
|
12-Feb-19 JDB Worked around idle problem (SIMH issue 622)
|
|
06-Feb-19 JDB Corrected trace report for simulation stop
|
|
05-Feb-19 JDB sim_dname now takes a const pointer
|
|
13-Aug-18 JDB Renamed "ion_defer" to "cpu_interrupt_enable" and flipped sense
|
|
24-Jul-18 JDB Removed unneeded "iotrap" parameter from "cpu_iog" routine
|
|
20-Jul-18 JDB Moved memory/MEM/MP and DMA into separate source files
|
|
29-Jun-18 JDB Fixed "clear_option" return type definition
|
|
14-Jun-18 JDB Renamed PRO device to MPPE
|
|
05-Jun-18 JDB Revised I/O model
|
|
21-May-18 JDB Changed "access" to "mem_access" to avoid clashing
|
|
07-May-18 JDB Modified "io_dispatch" to display outbound signals
|
|
01-May-18 JDB Multiple consecutive CLC 0 operations are now omitted
|
|
02-Apr-18 JDB SET CPU 21MX now configures an M-Series model
|
|
22-Feb-18 JDB Reworked "cpu_ibl" into "cpu_copy_loader"
|
|
11-Aug-17 JDB MEM must be disabled when DMS is disabled
|
|
01-Aug-17 JDB Changed SET/SHOW CPU [NO]IDLE to use sim_*_idle routines
|
|
22-Jul-17 JDB Renamed "intaddr" to CIR; added IR
|
|
18-Jul-17 JDB Added CPU stops
|
|
11-Jul-17 JDB Moved "hp_enbdis_pair" to hp2100_sys.c
|
|
Renamed "ibl_copy" to "cpu_ibl"
|
|
10-Jul-17 JDB Renamed the global routine "iogrp" to "cpu_iog"
|
|
07-Jul-17 JDB Changed "iotrap" from uint32 to t_bool
|
|
26-Jun-17 JDB Moved I/O instruction subopcode constants from hp2100_defs.h
|
|
16-May-17 JDB Changed REG_A, REG_B to REG_X
|
|
19-Apr-17 JDB SET CPU IDLE now omits idle loop tracing
|
|
04-Apr-17 JDB Added "cpu_configuration" for symbolic ex/dep validation
|
|
Rejected model change no longer changes options
|
|
21-Mar-17 JDB IOP is now illegal on the 1000 F-Series
|
|
27-Feb-17 JDB Added BBL load for 21xx machines
|
|
ibl_copy no longer returns a status code
|
|
22-Feb-17 JDB Added DMA tracing
|
|
21-Feb-17 JDB Added bus tracing to the I/O dispatcher
|
|
19-Jan-17 JDB Added CPU tracing
|
|
Consolidated the memory read and write routines
|
|
05-Aug-16 JDB Renamed the P register from "PC" to "PR"
|
|
13-May-16 JDB Modified for revised SCP API function parameter types
|
|
31-Dec-14 JDB Corrected devdisp data parameters
|
|
30-Dec-14 JDB Added S-register parameters to ibl_copy
|
|
24-Dec-14 JDB Added casts for explicit downward conversions
|
|
18-Mar-13 JDB Removed redundant extern declarations
|
|
05-Feb-13 JDB HLT instruction handler now relies on sim_vm_fprint_stopped
|
|
09-May-12 JDB Separated assignments from conditional expressions
|
|
13-Jan-12 JDB Minor speedup in "is_mapped"
|
|
Added casts to cpu_mod, dmasio, dmapio, cpu_reset, dma_reset
|
|
07-Apr-11 JDB Fixed I/O return status bug for DMA cycles
|
|
Failed I/O cycles now stop on failing instruction
|
|
28-Mar-11 JDB Tidied up signal handling
|
|
29-Oct-10 JDB Revised DMA for new multi-card paradigm
|
|
Consolidated DMA reset routines
|
|
DMA channels renamed from 0,1 to 1,2 to match documentation
|
|
27-Oct-10 JDB Changed I/O instructions, handlers, and DMA for revised signal model
|
|
Changed I/O dispatch table to use DIB pointers
|
|
19-Oct-10 JDB Removed DMA latency counter
|
|
13-Oct-10 JDB Fixed DMA requests to enable stealing every cycle
|
|
Fixed DMA priority for channel 1 over channel 2
|
|
Corrected comments for "cpu_set_idle"
|
|
30-Sep-08 JDB Breakpoints on interrupt trap cells now work
|
|
05-Sep-08 JDB VIS and IOP are now mutually exclusive on 1000-F
|
|
11-Aug-08 JDB Removed A/B shadow register variables
|
|
07-Aug-08 JDB Moved hp_setdev, hp_showdev to hp2100_sys.c
|
|
Moved non-existent memory checks to WritePW
|
|
05-Aug-08 JDB Fixed mp_dms_jmp to accept lower bound, check write protection
|
|
30-Jul-08 JDB Corrected DMS violation register set conditions
|
|
Refefined ABORT to pass address, moved def to hp2100_cpu.h
|
|
Combined dms and dms_io routines
|
|
29-Jul-08 JDB JSB to 0/1 with W5 out and fence = 0 erroneously causes MP abort
|
|
11-Jul-08 JDB Unified I/O slot dispatch by adding DIBs for CPU, MP, and DMA
|
|
26-Jun-08 JDB Rewrote device I/O to model backplane signals
|
|
EDT no longer passes DMA channel
|
|
30-Apr-08 JDB Enabled SIGNAL instructions, SIG debug flag
|
|
28-Apr-08 JDB Added SET CPU IDLE/NOIDLE, idle detection for DOS/RTE
|
|
24-Apr-08 JDB Fixed single stepping through interrupts
|
|
20-Apr-08 JDB Enabled EMA and VIS, added EMA, VIS, and SIGNAL debug flags
|
|
03-Dec-07 JDB Memory ex/dep and bkpt type default to current map mode
|
|
26-Nov-07 JDB Added SET CPU DEBUG and OS/VMA flags, enabled OS/VMA
|
|
15-Nov-07 JDB Corrected MP W5 (JSB) jumper action, SET/SHOW reversal,
|
|
mp_mevff clear on interrupt with I/O instruction in trap cell
|
|
04-Nov-07 JDB Removed DBI support from 1000-M (was temporary for RTE-6/VM)
|
|
28-Apr-07 RMS Removed clock initialization
|
|
02-Mar-07 JDB EDT passes input flag and DMA channel in dat parameter
|
|
11-Jan-07 JDB Added 12578A DMA byte packing
|
|
28-Dec-06 JDB CLC 0 now sends CRS instead of CLC to devices
|
|
26-Dec-06 JDB Fixed improper IRQ deferral for 21xx CPUs
|
|
Fixed improper interrupt servicing in resolve
|
|
21-Dec-06 JDB Added 21xx loader enable/disable support
|
|
16-Dec-06 JDB Added 2114 and 2115 CPU options.
|
|
Added support for 12607B (2114) and 12578A (2115/6) DMA
|
|
01-Dec-06 JDB Added 1000-F CPU option (requires HAVE_INT64)
|
|
SHOW CPU displays 1000-M/E instead of 21MX-M/E
|
|
16-Oct-06 JDB Moved ReadF to hp2100_cpu1.c
|
|
12-Oct-06 JDB Fixed INDMAX off-by-one error in resolve
|
|
26-Sep-06 JDB Added iotrap parameter to UIG dispatchers for RTE microcode
|
|
12-Sep-06 JDB iogrp returns NOTE_IOG to recalc interrupts
|
|
resolve returns NOTE_INDINT to service held-off interrupt
|
|
16-Aug-06 JDB Added support for future microcode options, future F-Series
|
|
09-Aug-06 JDB Added double integer microcode, 1000-M/E synonyms
|
|
Enhanced CPU option validity checking
|
|
Added DCPC as a synonym for DMA for 21MX simulations
|
|
26-Dec-05 JDB Improved reporting in dev_conflict
|
|
22-Sep-05 RMS Fixed declarations (from Sterling Garwood)
|
|
21-Jan-05 JDB Reorganized CPU option flags
|
|
15-Jan-05 RMS Split out EAU and MAC instructions
|
|
26-Dec-04 RMS DMA reset doesn't clear alternate CTL flop (from Dave Bryan)
|
|
DMA reset shouldn't clear control words (from Dave Bryan)
|
|
Alternate CTL flop not visible as register (from Dave Bryan)
|
|
Fixed CBS, SBS, TBS to perform virtual reads
|
|
Separated A/B from M[0/1] for DMA IO (from Dave Bryan)
|
|
Fixed bug in JPY (from Dave Bryan)
|
|
25-Dec-04 JDB Added SET CPU 21MX-M, 21MX-E (21MX defaults to MX-E)
|
|
TIMER/EXECUTE/DIAG instructions disabled for 21MX-M
|
|
T-register reflects changes in M-register when halted
|
|
25-Sep-04 JDB Moved MP into its own device; added MP option jumpers
|
|
Modified DMA to allow disabling
|
|
Modified SET CPU 2100/2116 to truncate memory > 32K
|
|
Added -F switch to SET CPU to force memory truncation
|
|
Fixed S-register behavior on 2116
|
|
Fixed LIx/MIx behavior for DMA on 2116 and 2100
|
|
Fixed LIx/MIx behavior for empty I/O card slots
|
|
Modified WRU to be REG_HRO
|
|
Added BRK and DEL to save console settings
|
|
Fixed use of "unsigned int16" in cpu_reset
|
|
Modified memory size routine to return SCPE_INCOMP if
|
|
memory size truncation declined
|
|
20-Jul-04 RMS Fixed bug in breakpoint test (reported by Dave Bryan)
|
|
Back up PC on instruction errors (from Dave Bryan)
|
|
14-May-04 RMS Fixed bugs and added features from Dave Bryan
|
|
- SBT increments B after store
|
|
- DMS console map must check dms_enb
|
|
- SFS x,C and SFC x,C work
|
|
- MP violation clears automatically on interrupt
|
|
- SFS/SFC 5 is not gated by protection enabled
|
|
- DMS enable does not disable mem prot checks
|
|
- DMS status inconsistent at simulator halt
|
|
- Examine/deposit are checking wrong addresses
|
|
- Physical addresses are 20b not 15b
|
|
- Revised DMS to use memory rather than internal format
|
|
- Added instruction printout to HALT message
|
|
- Added M and T internal registers
|
|
- Added N, S, and U breakpoints
|
|
Revised IBL facility to conform to microcode
|
|
Added DMA EDT I/O pseudo-opcode
|
|
Separated DMA SRQ (service request) from FLG
|
|
12-Mar-03 RMS Added logical name support
|
|
02-Feb-03 RMS Fixed last cycle bug in DMA output (found by Mike Gemeny)
|
|
22-Nov-02 RMS Added 21MX IOP support
|
|
24-Oct-02 RMS Fixed bugs in IOP and extended instructions
|
|
Fixed bugs in memory protection and DMS
|
|
Added clock calibration
|
|
25-Sep-02 RMS Fixed bug in DMS decode (found by Robert Alan Byer)
|
|
26-Jul-02 RMS Restructured extended instructions, added IOP support
|
|
22-Mar-02 RMS Changed to allocate memory array dynamically
|
|
11-Mar-02 RMS Cleaned up setjmp/auto variable interaction
|
|
17-Feb-02 RMS Added DMS support
|
|
Fixed bugs in extended instructions
|
|
03-Feb-02 RMS Added terminal multiplexor support
|
|
Changed PCQ macro to use unmodified PC
|
|
Fixed flop restore logic (found by Bill McDermith)
|
|
Fixed SZx,SLx,RSS bug (found by Bill McDermith)
|
|
Added floating point support
|
|
16-Jan-02 RMS Added additional device support
|
|
07-Jan-02 RMS Fixed DMA register tables (found by Bill McDermith)
|
|
07-Dec-01 RMS Revised to use breakpoint package
|
|
03-Dec-01 RMS Added extended SET/SHOW support
|
|
10-Aug-01 RMS Removed register in declarations
|
|
26-Nov-00 RMS Fixed bug in dual device number routine
|
|
21-Nov-00 RMS Fixed bug in reset routine
|
|
15-Oct-00 RMS Added dynamic device number support
|
|
|
|
References:
|
|
- 2100A Computer Reference Manual
|
|
(02100-90001, December 1971)
|
|
- Model 2100A Computer Installation and Maintenance Manual
|
|
(02100-90002, August 1972)
|
|
- HP 1000 M/E/F-Series Computers Technical Reference Handbook
|
|
(5955-0282, March 1980)
|
|
- HP 1000 M/E/F-Series Computers Engineering and Reference Documentation
|
|
(92851-90001, March 1981)
|
|
- HP 1000 Computer Real-Time Systems
|
|
(5091-4479, August 1992)
|
|
|
|
|
|
Hewlett-Packard sold the HP 21xx/1000 family of real-time computers from 1966
|
|
through 2000. There are three major divisions within this family: the 21xx
|
|
core-memory machines, the 1000 (originally 21MX) M/E/F-Series semiconductor-
|
|
memory machines, and the 1000 L/A-Series machines. All machines are 16-bit
|
|
accumulator-oriented CISC machines running the same base instruction set. A
|
|
wide range of operating systems run on these machines, from a simple 4K word
|
|
paper-tape-based monitor to a megaword multi-user, multiprogramming disc-
|
|
based system and a multi-user time-shared BASIC system.
|
|
|
|
This implementation is a simulator for the 2114, 2115, 2116, 2100, and 1000
|
|
M/E/F-Series machines. A large variety of CPU options, device interface
|
|
cards, and peripherals are provided. High-speed I/O transfers are performed
|
|
by Direct Memory Access and Dual-Channel Port Controller options. This
|
|
simulator does not model the 1000 L/A-Series machines.
|
|
|
|
All of the machines support a 15-bit logical address space, addressing a
|
|
maximum of 32K words, divided into 1K-word pages. Memory-referencing
|
|
instructions in the base set can directly address the 1024 words of the base
|
|
page (page 0) or the 1024 words of the current page (the page containing the
|
|
instruction). The instructions in the extended set directly address the
|
|
32768 words in the full logical address space. The A and B accumulators may
|
|
be addressed as logical memory addresses 0 and 1, respectively.
|
|
|
|
Peripheral devices are connected to the CPU by interface cards installed in
|
|
the I/O card cages present in the CPU and optional I/O extender chassis.
|
|
Each slot in the card cage is assigned an address, called a select code, that
|
|
may be referenced by I/O instructions in the base set. Select codes range
|
|
from 0 to 77 octal, with the first eight select codes reserved for the
|
|
system, providing connections for 56 possible interfaces.
|
|
|
|
The 211x machines use a hardwired processor providing 70 basic instructions
|
|
and up to 32K of core memory. The base instruction set is divided into the
|
|
Memory Reference Group, the Shift-Rotate Group, the Alter-Skip Group, and the
|
|
I/O Group. SRG instruction words may contain from one to four suboperation
|
|
codes that are executed from left-to-right, and ASG instruction words may
|
|
contain from one to eight suboperations. An optional Extended Arithmetic
|
|
Unit may be added to the 2115 and 2116 that provides hardware multiply and
|
|
divide, double-load and -store, and double-word shift and rotate
|
|
instructions.
|
|
|
|
The 2100 machine uses a microprogrammed processor that provides the 80
|
|
instructions of the base set and the EAU as standard equipment. Optional
|
|
floating-point microcode adds six two-word (single-precision) instructions.
|
|
User microprogramming is also supported. When used as part of an HP 2000
|
|
Time-Shared BASIC system, the CPU designated as the I/O processor may be
|
|
equipped with microcode implementing 18 additional OS accelerator
|
|
instructions.
|
|
|
|
The 1000 M/E-Series machines also use microprogrammed processors and extend
|
|
the 2100 instruction set with two new index registers, X and Y, and a new
|
|
Extended Instruction Group consisting of 32 index-register instructions and
|
|
10 word-and-byte-manipulation instructions. The six 2100 floating-point
|
|
instructions are also standard. The 1000 F-Series adds a hardware
|
|
floating-point processor with 18 new triple- and quad-word instructions. A
|
|
number of optional microcode extensions are available with the M/E/F-Series.
|
|
|
|
1000 CPUs offer the optional Dynamic Mapping System, which provides memory
|
|
mapping on a page-by-page basis. The 5-bit page number of a logical memory
|
|
address selects one of 32 ten-bit map registers containing physical page
|
|
numbers. The ten-bit page number combined with the ten-bit page offset
|
|
yields a 20-bit physical address capable of accessing a location in a
|
|
one-megaword memory. DMS provides separate maps for system and user
|
|
programs, as well as for the two DCPC channels, and includes microcode that
|
|
implements the 38 Dynamic Mapping Instructions used to manipulate the mapping
|
|
system.
|
|
|
|
Optional memory protection is accomplished by dividing the logical address
|
|
space into protected and unprotected parts. When protection is enabled, any
|
|
attempt to write or jump below the fence separating the two parts is
|
|
inhibited, and an interrupt to the operating system occurs, which aborts the
|
|
offending user program. If the DMS option is enabled as well, protection is
|
|
enhanced by specifying read and write permissions on a page-by-page basis.
|
|
|
|
A note on terminology: the 1000 series of computers was originally called the
|
|
21MX at introduction. The 21MX (occasionally, 21MXM) corresponds to the 1000
|
|
M-Series, and the 21MXE (occasionally, 21XE) corresponds to the 1000
|
|
E-Series. The model numbers were changed before the introduction of the 1000
|
|
F-Series, although some internal HP documentation refers to this machine as
|
|
the 21MXF.
|
|
|
|
The terms MEM (Memory Expansion Module), MEU (Memory Expansion Unit), DMI
|
|
(Dynamic Mapping Instructions), and DMS (Dynamic Mapping System) are used
|
|
somewhat interchangeably to refer to the logical-to-physical memory address
|
|
translation option provided on the 1000-Series. DMS consists of the MEM card
|
|
(12731A) and the DMI firmware (13307A). However, MEM and MEU have been used
|
|
interchangeably to refer to the mapping card, as have DMI and DMS to refer to
|
|
the firmware instructions.
|
|
|
|
|
|
These CPU hardware registers are present in all machines:
|
|
|
|
Name Width Description
|
|
---- ----- ----------------------------------------------
|
|
A 16 Accumulator (addressable as memory location 0)
|
|
B 16 Accumulator (addressable as memory location 1)
|
|
P 15 Program counter
|
|
S 16 Switch and display register
|
|
M 15 Memory address register
|
|
T 16 Memory data register
|
|
E 1 Extend flag (arithmetic carry out)
|
|
O 1 Overflow flag (arithmetic overflow)
|
|
|
|
In addition, there are two internal registers that are not visible to the
|
|
programmer but are used by the hardware:
|
|
|
|
Name Width Description
|
|
---- ----- ----------------------------------------------
|
|
IR 16 Instruction register
|
|
CIR 6 Central interrupt register
|
|
|
|
The Instruction Register holds the current instruction, while the Central
|
|
Interrupt Register holds the select code identifying an interrupting device.
|
|
|
|
The 1000 Series adds these CPU hardware registers:
|
|
|
|
Name Width Description
|
|
---- ----- ----------------------------------------------
|
|
X 16 index register
|
|
Y 16 index register
|
|
|
|
The data types supported by the base instruction set are:
|
|
|
|
- 8-bit unsigned byte
|
|
- 16-bit unsigned integer
|
|
- 16-bit two's-complement integer
|
|
- 32-bit two's-complement integer
|
|
- 32-bit two's-complement floating point
|
|
|
|
Multi-word values are stored in memory with the most-significant words in the
|
|
lowest addresses. Bytes are stored in memory with the most-significant byte
|
|
in the upper half of the 16-bit word and the least-significant byte in the
|
|
lower half.
|
|
|
|
The instruction set is fairly irregular -- a legacy of its original
|
|
implementation in hardware in the 2116 and the accretion of microprogrammed
|
|
instructions in the 2100 and 1000 CPUs. Initially, there were five base-set
|
|
instruction groups:
|
|
|
|
1. Memory-Reference Group (MRG)
|
|
2. Shift-Rotate Group (SRG)
|
|
3. Alter-Skip Group (ASG)
|
|
4. I/O Group (IOG)
|
|
5. Macroinstruction Group (MAC)
|
|
|
|
All of the instructions added after the 2116 are in the Macroinstruction
|
|
Group.
|
|
|
|
The 2116 offers two hardware options that extended the instruction set. The
|
|
first is the 12579A Extended Arithmetic Unit. The second is the 2152A
|
|
Floating Point Processor, which is interfaced through, and therefore
|
|
requires, the EAU. The EAU adds 10 instructions including integer multiply
|
|
and divide and double-word loads, stores, shifts, and rotates. The FPP adds
|
|
30 floating-point arithmetic, trigonometric, logarithmic, and exponential
|
|
instructions. The 2116 EAU is compatible with the 2100 and 1000 EAU
|
|
implementations and is provided by the simulator. The 2116 FPP is unique
|
|
to that machine and is not simulated.
|
|
|
|
The base set groups are decoded from bits 15-12 and 10, as follows:
|
|
|
|
15 14-12 10 Group Address Ranges
|
|
-- ----- -- ----- -------------------------------
|
|
x nnn x MRG 010000-077777 and 110000-177777
|
|
0 000 0 SRG 000000-001777 and 004000-005777
|
|
0 000 1 ASG 002000-003777 and 006000-007777
|
|
1 000 1 IOG 102000-103777 and 106000-107777
|
|
1 000 0 MAC 100000-101777 and 104000-105777
|
|
|
|
Where:
|
|
|
|
x = don't care
|
|
n = any combination other than all zeros
|
|
|
|
The MAC group is subdivided into the Extended Arithmetic Group (EAG) and the
|
|
User Instruction Group (UIG), based on bits 11, 9, and 8, as follows:
|
|
|
|
11 9 8 Group Address Range
|
|
-- -- -- ----- -------------
|
|
0 0 0 EAG 100000-100377
|
|
0 0 1 EAG 100400-100777
|
|
0 1 0 EAG 101000-101377
|
|
0 1 1 UIG-1 101400-101777
|
|
1 0 0 EAG 104000-104377
|
|
1 0 1 EAG 104400-104777
|
|
1 1 0 UIG-0 105000-105377
|
|
1 1 1 UIG-1 105400-105777
|
|
|
|
All of the 2116 FPP instructions are in the UIG sets: 3 use 10144x opcodes
|
|
and the rest use 1050xx and 1054xx opcodes. The 2100 decodes only UIG-0
|
|
instructions, whereas the 1000s use both UIG sets. In particular, the
|
|
105740-105777 range is used by the 1000 Extended Instruction Group (EIG),
|
|
which is part of the 1000-Series base set.
|
|
|
|
The 21xx and 1000 M/E/F-Series machines do not trap unimplemented
|
|
instructions. In general, unimplemented EAG instructions cause erroneous
|
|
execution, and unimplemented UIG instructions execute as NOP. However, there
|
|
are machine-to-machine variations, and some unimplemented instructions
|
|
execute as other, defined instructions.
|
|
|
|
The Memory-Reference Group instructions are encoded as follows:
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| I | mem op | P | memory address | MRG
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| I | mem op | R | P | memory address | MRG
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
Where:
|
|
|
|
I = direct/indirect (0/1)
|
|
R = A/B register (0/1)
|
|
P = base/current page (0/1)
|
|
|
|
The "mem ops" are encoded as follows:
|
|
|
|
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, P = P + 2 if M [MA] == 0
|
|
1000 ADA A = A + M [MA]
|
|
1001 ADB B = B + M [MA]
|
|
1010 CPA P = P + 2 if A != M [MA]
|
|
1011 CPB P = P + 2 if B != M [MA]
|
|
1100 LDA A = M [MA]
|
|
1101 LDB B = M [MA]
|
|
1110 STA M [MA] = A
|
|
1111 STB M [MA] = B
|
|
|
|
Bits 15 and 10 encode the type of access, as follows:
|
|
|
|
15 10 Access Type Action
|
|
--- --- --------------------- -----------------------------
|
|
0 0 base page direct MA = IR <9:0>
|
|
0 1 current page direct MA = P <14:10> | IR <9:0>
|
|
1 0 base page indirect MA = M [IR <9:0>]
|
|
1 1 current page indirect MA = M [P <14:10> | IR <9:0>]
|
|
|
|
|
|
The Shift-Rotate Group instructions are encoded as follows:
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 0 | 0 0 0 | R | 0 | E | op 1 | C | E | S | op 2 | SRG
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
Where:
|
|
|
|
R = A/B register (0/1)
|
|
E = disable/enable op
|
|
C = CLE
|
|
S = SL*
|
|
|
|
Bits 8-6 and 2-0 each encode one of eight operations, as follows:
|
|
|
|
Op N Operation
|
|
---- ---------------------------
|
|
000 Arithmetic left shift
|
|
001 Arithmetic right shift
|
|
010 Rotate left
|
|
011 Rotate right
|
|
100 Shift left and clear sign
|
|
101 Rotate right through Extend
|
|
110 Rotate left through Extend
|
|
111 Rotate left four bits
|
|
|
|
|
|
The Alter-Skip Group instructions are encoded as follows:
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 0 | 0 0 0 | R | 1 | r op | e op | E | S | L | I | Z | V | ASG
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
Where:
|
|
|
|
R = A/B register (0/1)
|
|
E = SEZ
|
|
S = SS*
|
|
L = SL*
|
|
I = IN*
|
|
Z = SZ*
|
|
V = RSS
|
|
|
|
Bits 9-8 and 7-6 each encode one of three operations, as follows:
|
|
|
|
9-8 Operation
|
|
--- ------------------------
|
|
01 Clear A/B
|
|
10 Complement A/B
|
|
11 Clear and complement A/B
|
|
|
|
7-6 Operation
|
|
--- ---------------------------
|
|
01 Clear Extend
|
|
10 Complement Extend
|
|
11 Clear and complement Extend
|
|
|
|
|
|
The Input-Output Group instructions are encoded as follows:
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | R | 1 | H | I/O op | select code | IOG
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
Where:
|
|
|
|
R = A/B register (0/1)
|
|
H = hold/clear flag (0/1)
|
|
|
|
There are ten instructions. Six are encoded directly by bits 8-6, with
|
|
bits 11 and 9 assuming the definitions above. The other four are encoded
|
|
with bits 11 and 9 differentiating, as follows:
|
|
|
|
11 9 8-6 Operation
|
|
--- --- --- ---------------------------
|
|
x H 000 Halt
|
|
x 0 001 Set the flag flip-flop
|
|
x 1 001 Clear the flag flip-flop
|
|
x H 010 Skip if the flag is clear
|
|
x H 011 Skip if the flag is set
|
|
R H 100 Merge input data into A/B
|
|
R H 101 Load input data into A/B
|
|
R H 110 Store output data from A/B
|
|
0 H 111 Set the control flip-flop
|
|
1 H 111 Clear the control flip-flop
|
|
|
|
An I/O group instruction controls the device specified by the select code.
|
|
Depending on the opcode, the instruction may set or clear the device flag,
|
|
start or stop I/O, or read or write data.
|
|
|
|
|
|
The Macroinstruction Group instructions are encoded with bits 15-12 and 10 as
|
|
1 000 0. Bits 11 and 9-0 determine the specific EAU or UIG instruction.
|
|
|
|
The Extended Arithmetic Group instructions are encoded as follows:
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | op| 0 | operation | 0 0 0 0 0 0 | EAG
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
Operations:
|
|
|
|
11 9-6 Operation
|
|
--- ---- -----------------------------------------------------
|
|
0 0010 Multiply 16 x 16 = 32-bit product
|
|
0 0100 Divide 32 / 16 = 16-bit quotient and 16-bit remainder
|
|
1 0010 Double load A and B registers from memory
|
|
1 0100 Double store A and B registers to memory
|
|
|
|
All other encodings are undefined.
|
|
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | 0 | 0 | shift/rotate op | shift count | EAG
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
Operations:
|
|
|
|
9-4 Operation
|
|
------ --------------------------------------------------
|
|
100001 Arithmetic shift right A and B registers 1-16 bits
|
|
000001 Arithmetic shift left A and B registers 1-16 bits
|
|
100010 Logical shift right A and B registers 1-16 bits
|
|
000010 Logical shift left A and B registers 1-16 bits
|
|
100100 Rotate right A and B registers 1-16 bits
|
|
000100 Rotate left A and B registers 1-16 bits
|
|
|
|
The shift count encodes the number of bits shifted, with a count of zero
|
|
representing a shift of 16 bits.
|
|
|
|
|
|
The User Instruction Group instructions are encoded as follows:
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | R | 0 1 | module | operation | UIG
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
Where:
|
|
|
|
R = A/B register (0/1)
|
|
|
|
Bits 8-4 encode the microcode module containing the instructions, and bits
|
|
3-0 encode the specific instructions. See the individual UIG instruction
|
|
simulator source files for the specific encodings used.
|
|
|
|
|
|
I/O device interfaces and their connected devices are simulated by
|
|
substituting software states for I/O backplane signals. The set of signals
|
|
generated by I/O instructions and DMA cycles is dispatched to the target
|
|
interface simulator for action, and the set of signals asserted or denied in
|
|
response is returned from the call. Backplane signals are processed
|
|
sequentially. For example, the "STC sc,C" instruction generates the "set
|
|
control" and the "clear flag" signals that are processed in that order.
|
|
|
|
The HP 21xx/1000 interrupt structure is based on the PRH, PRL, IEN, IRQ, and
|
|
IAK signals. PRH indicates that no higher-priority device is interrupting.
|
|
PRL indicates to lower-priority devices that a given device is not
|
|
interrupting. IEN asserts when the interrupt system is enabled. IRQ
|
|
indicates that a given device is requesting an interrupt. IAK indicates that
|
|
the given device's interrupt request is being acknowledged.
|
|
|
|
Typical I/O interfaces have a flag buffer, a flag, and a control flip-flop.
|
|
If an interface's flag buffer, flag, and control flip-flops are all set, the
|
|
interrupt system is enabled, and the interface has the highest priority on
|
|
the interrupt chain, it requests an interrupt by asserting the IRQ signal.
|
|
When the interrupt is acknowledged with the IAK signal, the flag buffer is
|
|
cleared, preventing further interrupt requests from that interface. The
|
|
combination of flag set and control set blocks interrupts from lower priority
|
|
devices.
|
|
|
|
Service requests are used to trigger the DMA service logic. Setting the
|
|
interface flag flip-flop typically asserts SRQ, although SRQ may be
|
|
calculated independently.
|
|
|
|
Most of the I/O signals are generated by execution of IOG instructions or DMA
|
|
cycles; the target interface simulator is called explicitly to respond to the
|
|
signal assertions. However, two hardware signals (ENF and SIR) are periodic,
|
|
and a direct simulation would call each interface simulator with these
|
|
signals after each machine instruction. Instead, the interface simulator is
|
|
called only when these signals would have an effect on the state of the
|
|
interface. Also, two signals (IEN and PRH) are combinatorial, in that
|
|
changing either potentially affects all interfaces in the system. These are
|
|
handled efficiently by saving the states of each interface in bit vectors, as
|
|
described below.
|
|
|
|
PRH and PRL form a hardware priority chain that extends from interface to
|
|
interface on the backplane. For an interface to generate an interrupt, PRH
|
|
must be asserted by the next-higher-priority interface. When an interface
|
|
generates an interrupt, it denies PRL to the next-lower-priority interface.
|
|
When an interface is not interrupting, it passes PRH to PRL unaltered. This
|
|
means that a given interface can interrupt only if all higher-priority
|
|
devices are receiving PRH asserted and are asserting PRL. It also means that
|
|
clearing the interrupt on a given interface, i.e., reasserting PRL, may
|
|
affect all lower-priority interfaces.
|
|
|
|
As an example, assume that the interface at select code 10 ("SC 10") has its
|
|
flag buffer, flag, and control flip-flops set, that IEN and PRH are asserted,
|
|
and that no other interfaces have their flag flip-flops set. SC 10 will
|
|
assert IRQ and deny PRL. SC 11 sees its PRH low (because PRL 10 is connected
|
|
to PRH 11) and so denies PRL, and this action ripples through all of the
|
|
lower-priority interfaces.
|
|
|
|
Then, while the interrupt for SC 10 is being serviced, the interface at
|
|
select code 17 sets its flag buffer, flag, and control flip-flops. SC 17 is
|
|
inhibited from interrupting by PRH denied.
|
|
|
|
When the interrupt service routine clears the interrupt by clearing the flag
|
|
buffer and flag flip-flops, SC 10 reasserts PRL to SC 11, and that signal
|
|
ripples through the interfaces at select codes 12-16, arriving at SC 17 as
|
|
PRH assertion. With PRH asserted, SC 17 generates an interrupt and denies
|
|
PRL to SC 20 and above.
|
|
|
|
A direct simulation of this hardware behavior would require calling all
|
|
lower-priority interface simulators in ascending select code (and therefore
|
|
priority) order with the PRH signal and checking for an asserted IRQ signal
|
|
or a denied PRL signal. This is inefficient.
|
|
|
|
To avoid making a potentially long sequence of calls, each interface
|
|
simulator returns a "conditional IRQ" signal and a "conditional PRL" signal
|
|
in addition to the standard IRQ and PRL signals. The conditional signals
|
|
are those that would result if the higher-priority interface is asserting
|
|
PRH and the interrupt system is on. So, for instance, an interface simulator
|
|
with its flag buffer, flag, and control flip-flops set will assert its
|
|
conditional IRQ signal and deny its conditional PRL signal. If PRH and IEN
|
|
are asserted, then its "real" IRQ and PRL signals are also asserted and
|
|
denied respectively.
|
|
|
|
For fast assertion checking, the conditional IRQ and PRL signal states are
|
|
kept in bit vectors, which are updated after each interface simulator call.
|
|
Each vector is represented as a two-element array of 32-bit unsigned
|
|
integers, forming a 64-bit vector in which bits 0-31 of the first element
|
|
correspond to select codes 00-37 octal, and bits 0-31 of the second element
|
|
correspond to select codes 40-77 octal. The "interrupt_request_set" array
|
|
holds conditional IRQ states, and the "priority_holdoff_set" array holds the
|
|
complement of the conditional PRL states (the complement is used to simplify
|
|
the priority calculation). These vectors permit rapid determination of an
|
|
interrupting interface when a higher-priority interface reasserts PRL or when
|
|
the interrupt system is reenabled.
|
|
|
|
|
|
The simulator provides three stop conditions related to instruction execution
|
|
that may be enabled with a SET CPU STOP=<stop> command:
|
|
|
|
<stop> Action
|
|
------ ------------------------------------------
|
|
UNIMPL stop on an unimplemented instruction
|
|
UNDEF stop on an undefined instruction
|
|
UNSC stop on an access to an unused select code
|
|
IOERR stop on an unreported I/O error
|
|
|
|
If an enabled stop condition is detected, execution ceases with the
|
|
instruction pending, and control returns to the SCP prompt. When simulation
|
|
stops, execution may be resumed in two ways. If the cause of the stop has
|
|
not been remedied and the stop has not been disabled, resuming execution with
|
|
CONTINUE, STEP, GO, or RUN will cause the stop to occur again. Alternately,
|
|
specifying the "-B" switch with any of the preceding commands will resume
|
|
execution while bypassing the stop for the current instruction.
|
|
|
|
The UNIMPL option stops the simulator if execution is attempted of an
|
|
instruction provided by a firmware option that is not currently installed
|
|
(e.g., a DAD instruction when the double-integer firmware is not installed)
|
|
or of an opcode provided by an installed option but not assigned to an
|
|
instruction (e.g., opcode 105335 from the double-integer firmware).
|
|
Bypassing the stop will execute the instruction as a NOP (no-operation).
|
|
|
|
The UNDEF option stops the simulator if execution is attempted of an
|
|
instruction containing a decoded reserved bit pattern other than that defined
|
|
in the Operating and Reference manual for the CPU. For example, opcodes
|
|
101700 and 105700 are not listed as DMS instructions, but they execute as
|
|
XMM instructions, rather than as NOP. The intent of this stop is to catch
|
|
instructions containing reserved fields with values that change the meaning
|
|
of those instructions. Bypassing the stop will decode and execute the
|
|
instruction in the same manner as the selected CPU.
|
|
|
|
The UNSC option stops the simulator if an I/O instruction addresses a select
|
|
code that is not assigned to an enabled device (equivalent to an empty
|
|
hardware I/O backplane slot). Bypassing the stop will read the floating
|
|
S-bus or I/O-bus for LIA/B and MIA/B instructions or do nothing for all other
|
|
instructions.
|
|
|
|
The IOERR option stops the simulator if an I/O error condition exists for a
|
|
device that does not report this status to the CPU. For example, the paper
|
|
tape reader device (PTR) does not report "no tape loaded" status, and the
|
|
processor interconnect device (IPL) does not report "cable disconnected." In
|
|
both cases, I/O to the device will simply hang with no indication of the
|
|
problem. Enabling the IOERR option will stop the simulator with an error
|
|
indication for these devices.
|
|
|
|
In addition, a simulation stop will occur if an indirect addressing chain
|
|
exceeds the maximum length specified by a SET CPU INDIR=<limit> command.
|
|
Memory addresses may be indirect to indicate that the values point to the
|
|
target addresses rather than contain the target addresses. The target of an
|
|
indirect address may itself be indirect, and the CPU follows this chain of
|
|
addresses until it finds a direct address. Indirect addressing is typically
|
|
only one or two levels deep, but if the chain loops back on itself (e.g., if
|
|
an indirect address points at itself), then instruction execution will hang.
|
|
|
|
The limit may be set to any number of levels up to 32,768. This is the
|
|
absolute maximum number of levels that can be created without an infinite
|
|
loop -- each location in memory points to the next one except for the last,
|
|
which contains the target value. In practice, anything over a few levels
|
|
likely represents a programming error. The default setting is 16 levels.
|
|
|
|
|
|
The CPU simulator provides extensive tracing capabilities that may be enabled
|
|
with the SET DEBUG <filename> and SET CPU DEBUG=<trace> commands. The trace
|
|
options that may be specified are:
|
|
|
|
Trace Action
|
|
----- -------------------------------------------
|
|
INSTR trace instructions executed
|
|
DATA trace memory data accesses
|
|
FETCH trace memory instruction fetches
|
|
REG trace registers
|
|
OPND trace instruction operands
|
|
EXEC trace matching instruction execution states
|
|
|
|
A section of an example trace is:
|
|
|
|
>>CPU instr: S 0002 05735 103101 CLO
|
|
>>CPU fetch: S 0002 05736 000036 instruction fetch
|
|
>>CPU reg: - **** 01011 042200 A 177777, B 000000, X 177777, Y 000000, e o i
|
|
>>CPU instr: S 0002 05736 000036 SLA,ELA
|
|
>>CPU fetch: S 0002 05737 102101 instruction fetch
|
|
>>CPU reg: - **** 01011 042200 A 177776, B 000000, X 177777, Y 000000, E o i
|
|
>>CPU instr: S 0002 05737 102101 STO
|
|
>>CPU fetch: S 0002 05740 002400 instruction fetch
|
|
>>CPU reg: - **** 01011 042200 A 177776, B 000000, X 177777, Y 000000, E O i
|
|
>>CPU instr: S 0002 05755 102100 STF 0
|
|
>>CPU fetch: S 0002 05756 102705 instruction fetch
|
|
>>CPU reg: - **** 01011 042200 A 177777, B 177777, X 177777, Y 000000, E O I
|
|
>>CPU instr: S 0002 05756 102705 STC 5
|
|
>>CPU fetch: S 0002 05757 105736 instruction fetch
|
|
>>CPU reg: P **** 01011 042200 A 177777, B 177777, X 177777, Y 000000, E O I
|
|
>>CPU instr: S 0002 05757 105736 UJP 2111
|
|
>>CPU fetch: S 0002 05760 002111 instruction fetch
|
|
>>CPU fetch: U 0001 02111 026111 instruction fetch
|
|
>>CPU reg: P **** 01011 042200 A 177777, B 177777, X 177777, Y 000000, E O I
|
|
>>CPU instr: U 0001 02111 026111 JMP 2111
|
|
>>CPU instr: U 0001 02111 000011 interrupt
|
|
>>CPU fetch: S 0000 00011 115013 instruction fetch
|
|
>>CPU reg: - **** 01011 042200 A 177777, B 177777, X 177777, Y 000000, E O I
|
|
>>CPU reg: - **** ***** ****** MPF 000000, MPV 002111, MES 163011, MEV 030000
|
|
>>CPU instr: S 0000 00011 115013 JSB 1013,I
|
|
>>CPU data: S 0000 01013 005557 data read
|
|
>>CPU data: S 0002 05557 002111 data write
|
|
>>CPU fetch: S 0002 05560 103100 instruction fetch
|
|
>>CPU reg: - **** 01011 042200 A 177777, B 177777, X 177777, Y 000000, E O I
|
|
>>CPU instr: S 0002 05560 103100 CLF 0
|
|
>>CPU fetch: S 0002 05561 105714 instruction fetch
|
|
>>CPU reg: - **** 01011 042200 A 177777, B 177777, X 177777, Y 000000, E O i
|
|
>>CPU exec: ********************
|
|
>>CPU reg: P **** 01567 000000 A 100036, B 000100, X 000100, Y 074000, E o I
|
|
>>CPU instr: U 0220 07063 105240 .PMAP
|
|
>>CPU data: U 0000 01776 000227 unprotected read
|
|
>>CPU data: U 0227 76100 000233 data read
|
|
>>CPU opnd: * **** 07065 105240 return location is P+2 (no error)
|
|
>>CPU fetch: U 0220 07065 127055 instruction fetch
|
|
>>CPU reg: P **** 01567 000000 A 100037, B 000101, X 000100, Y 074000, e o I
|
|
|
|
The INSTR option traces instruction executions and interrupts. Each
|
|
instruction is printed in symbolic form before it is executed.
|
|
|
|
The DATA option traces reads from and writes to memory. Each access is
|
|
classified by its usage type as "data" (using the current or alternate map
|
|
with access protection) or "unprotected" (using a specified map without
|
|
protection).
|
|
|
|
The FETCH option traces instruction fetches from memory. Reads of the
|
|
additional words in a multiword instruction, such as the target address of a
|
|
DLD (double load) instruction, are also classified as fetches.
|
|
|
|
The REG option traces register values. Two sets of registers are printed.
|
|
After executing each instruction, the working registers (A, B, E, O, S, and,
|
|
for 1000 CPUs, X and Y) and the state of the interrupt system (on or off) are
|
|
printed. After executing an instruction that may alter the Memory Protect or
|
|
Memory Expansion Module state, the MP fence and violation registers, the MEM
|
|
status and violation registers, and the current protection state are printed.
|
|
|
|
The OPND option traces operand values. Some instructions that take memory
|
|
and register operands that are difficult to decode from DATA or REG traces
|
|
present the operand values in a higher-level format. The operand data and
|
|
value presented are specific to the instruction; see the instruction executor
|
|
comments for details.
|
|
|
|
The EXEC option traces the execution of instructions that match
|
|
user-specified criteria. When a match occurs, all CPU trace options are
|
|
turned on for the duration of the execution of the matched instruction. The
|
|
prior trace settings are restored when a match fails. This option allows
|
|
detailed tracing of specified instructions while minimizing the log file size
|
|
compared to a full instruction trace.
|
|
|
|
The various trace formats are interpreted as follows:
|
|
|
|
>>CPU instr: U 0045 10341 016200 LDA 11200
|
|
~ ~~~~ ~~~~~ ~~~~~~ ~~~~~~~~~
|
|
| | | | |
|
|
| | | | +-- instruction mnemonic
|
|
| | | +---------- octal data (instruction opcode)
|
|
| | +------------------ octal logical address (P register)
|
|
| +----------------------- octal physical page number
|
|
+--------------------------- memory map (S/U/- system/user/disabled)
|
|
|
|
>>CPU instr: U 0045 10341 000011 interrupt
|
|
~ ~~~~ ~~~~~ ~~~~~~ ~~~~~~~~~
|
|
| | | | |
|
|
| | | | +-- interrupt classification
|
|
| | | +---------- octal device number (CIR register)
|
|
| | +------------------ octal logical address at interrupt (P register)
|
|
| +----------------------- octal physical page number at interrupt
|
|
+--------------------------- memory map (S/U/- system/user/disabled)
|
|
|
|
>>CPU fetch: - 0000 10341 016200 instruction fetch
|
|
>>CPU data: U 0013 01200 123003 data read
|
|
>>CPU data: S 0013 01200 017200 unprotected write
|
|
~ ~~~~ ~~~~~ ~~~~~~ ~~~~~~~~~~~~~~~~~
|
|
| | | | |
|
|
| | | | +-- memory access classification
|
|
| | | +------------ octal data (memory contents)
|
|
| | +-------------------- octal logical address (effective address)
|
|
| +------------------------- octal physical page number
|
|
+----------------------------- memory map (S/U/A/B/- system/user/port A/port B/disabled)
|
|
|
|
>>CPU reg: P **** 01535 040013 A 123003, B 001340, X 000000, Y 000000, e O I
|
|
~ ~~~~ ~~~~~ ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| | | | |
|
|
| | | | +-- A, B, X, Y, E, O, interrupt system registers
|
|
| | | | (lower/upper case = 0/1 or off/on)
|
|
| | | +------------ S register
|
|
| | +-------------------- MEM fence
|
|
| +------------------------- (place holder)
|
|
+----------------------------- protection state (P/- protected/unprotected)
|
|
|
|
>>CPU reg: P **** ***** ****** MPF 00000, MPV 000000, MES 000000, MEV 000000
|
|
~ ~~~~ ~~~~~ ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| | | | |
|
|
| | | | +-- memory protect fence and violation registers
|
|
| | | | memory expansion status and violation registers
|
|
| | | +------------ (place holder)
|
|
| | +-------------------- (place holder)
|
|
| +------------------------- (place holder)
|
|
+----------------------------- protection state (P/- protected/unprotected)
|
|
|
|
|
|
|
|
>>CPU opnd: . **** 36002 101475 return location is P+3 (error EM21)
|
|
>>CPU opnd: . **** 22067 105355 entry is for a dynamic mapping violation
|
|
~~~~~ ~~~~~~ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
|
|
| | |
|
|
| | +-- operand-specific value
|
|
| +------------ operand-specific octal data
|
|
+-------------------- octal logical address (P register)
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. The simulator is fast enough, compared to the run-time of the longest
|
|
instructions, for interruptibility not to matter. However, the HP
|
|
diagnostics explicitly test interruptibility in the EIS and DMS
|
|
instructions and in 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).
|
|
|
|
2. The Power Fail option is not currently implemented.
|
|
|
|
3. Idling with 4.x simulator framework versions after June 14, 2018 (git
|
|
commit ID d3986466) loses TBG ticks. This causes the DOS/RTE/TSB system
|
|
clock to run slowly, losing about one second per minute. A workaround is
|
|
to prevent "sim_interval" from going negative on return from "sim_idle".
|
|
Issue 622 on the SIMH site describes the problem.
|
|
*/
|
|
|
|
|
|
|
|
#include <setjmp.h>
|
|
|
|
#include "hp2100_defs.h"
|
|
#include "hp2100_cpu.h"
|
|
#include "hp2100_cpu_dmm.h"
|
|
|
|
|
|
|
|
/* Lost time workaround */
|
|
|
|
#if (SIM_MAJOR >= 4)
|
|
#define sim_idle(timer,decrement) \
|
|
if (sim_idle (timer, decrement) == TRUE /* [workaround] idle the simulator; if idling occurred */ \
|
|
&& sim_interval < 0) /* [workaround] and the time interval is negative */ \
|
|
sim_interval = 0 /* [workaround] then reset it to zero */
|
|
#endif
|
|
|
|
|
|
|
|
/* CPU program constants */
|
|
|
|
/* Alter-Skip Group instruction register fields */
|
|
|
|
#define IR_CMx 0001000u /* CMA/B */
|
|
#define IR_CLx 0000400u /* CLA/B */
|
|
#define IR_CME 0000200u /* CME */
|
|
#define IR_CLE 0000100u /* CLE */
|
|
#define IR_SEZ 0000040u /* SEZ */
|
|
#define IR_SSx 0000020u /* SSA/B */
|
|
#define IR_SLx 0000010u /* SLA/B */
|
|
#define IR_INx 0000004u /* INA/B */
|
|
#define IR_SZx 0000002u /* SZA/B */
|
|
#define IR_RSS 0000001u /* RSS */
|
|
|
|
#define IR_SSx_SLx_RSS (IR_SSx | IR_SLx | IR_RSS) /* a special case */
|
|
#define IR_ALL_SKIPS (IR_SEZ | IR_SZx | IR_SSx_SLx_RSS) /* another special case */
|
|
|
|
/* Shift-Rotate Group instruction register micro-ops */
|
|
|
|
#define IR_xLS 0000000u /* ALS/BLS */
|
|
#define IR_xRS 0000001u /* ARS/BRS */
|
|
#define IR_RxL 0000002u /* RAL/RBL */
|
|
#define IR_RxR 0000003u /* RAR/RBR */
|
|
#define IR_xLR 0000004u /* ALR/BLR */
|
|
#define IR_ERx 0000005u /* ERA/ERB */
|
|
#define IR_ELx 0000006u /* ELA/ELB */
|
|
#define IR_xLF 0000007u /* ALF/BLF */
|
|
|
|
#define SRG_DIS 0000000u /* micro-op disable */
|
|
#define SRG1_EN 0000010u /* micro-op 1 enable */
|
|
#define SRG2_EN 0000020u /* micro-op 2 enable */
|
|
|
|
/* Instruction register masks */
|
|
|
|
#define IR_MRG (MRG | AB_MASK) /* MRG instructions mask */
|
|
#define IR_MRG_I (IR_MRG | IR_IND) /* MRG indirect instructions mask */
|
|
|
|
#define IR_JSB 0014000u /* JSB instruction */
|
|
#define IR_JSB_I (IR_JSB | IR_IND) /* JSB,I instruction */
|
|
#define IR_JMP 0024000u /* JMP instruction */
|
|
|
|
#define IR_HLT_MASK 0172700u /* I/O group mask for HLT[,C] instruction */
|
|
#define IR_CLC_MASK 0176700u /* I/O group mask for a CLC[,C] instruction */
|
|
|
|
#define IR_HLT 0102000u /* HLT instruction */
|
|
#define IR_CLC 0106700u /* CLC instruction */
|
|
|
|
|
|
/* CPU unit flags and accessors.
|
|
|
|
31 30 29 28 27 26 25 24 23 22 21 20 19 18 17 16
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| r | - | G | V | O | E | D | F | M | I | P | U | CPU model |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
Where:
|
|
|
|
r = reserved
|
|
G = SIGNAL/1000 firmware is present
|
|
V = Vector Instruction Set firmware is present
|
|
O = RTE-6/VM VMA and OS firmware is present
|
|
E = RTE-IV EMA firmware is present
|
|
D = Double Integer firmware is present
|
|
F = Fast FORTRAN Processor firmware is present
|
|
M = Dynamic Mapping System firmware is present
|
|
I = 2000 I/O Processor firmware is present
|
|
P = Floating Point hardware or firmware is present
|
|
U = Extended Arithmetic Unit is present
|
|
*/
|
|
|
|
#define UNIT_MODEL_SHIFT (UNIT_V_UF + 0) /* bits 0- 3: CPU model (OPTION_ID value) */
|
|
#define UNIT_OPTION_SHIFT (UNIT_V_UF + 4) /* bits 4-15: CPU options installed */
|
|
|
|
#define UNIT_MODEL_MASK 0000017u /* model ID mask */
|
|
#define UNIT_OPTION_MASK 0003777u /* option ID mask */
|
|
|
|
#define UNIT_MODEL_FIELD (UNIT_MODEL_MASK << UNIT_MODEL_SHIFT)
|
|
#define UNIT_OPTION_FIELD (UNIT_OPTION_MASK << UNIT_OPTION_SHIFT)
|
|
|
|
#define UNIT_MODEL(f) ((f) >> UNIT_MODEL_SHIFT & UNIT_MODEL_MASK)
|
|
#define UNIT_OPTION(f) ((f) >> UNIT_OPTION_SHIFT & UNIT_OPTION_MASK)
|
|
|
|
#define TO_UNIT_OPTION(id) (1u << UNIT_OPTION_SHIFT + (id) - Option_BASE - 1)
|
|
|
|
/* Unit models */
|
|
|
|
#define UNIT_2116 (Option_2116 << UNIT_MODEL_SHIFT)
|
|
#define UNIT_2115 (Option_2115 << UNIT_MODEL_SHIFT)
|
|
#define UNIT_2114 (Option_2114 << UNIT_MODEL_SHIFT)
|
|
#define UNIT_2100 (Option_2100 << UNIT_MODEL_SHIFT)
|
|
#define UNIT_1000_M (Option_1000_M << UNIT_MODEL_SHIFT)
|
|
#define UNIT_1000_E (Option_1000_E << UNIT_MODEL_SHIFT)
|
|
#define UNIT_1000_F (Option_1000_F << UNIT_MODEL_SHIFT)
|
|
|
|
/* Unit options */
|
|
|
|
#define UNIT_EAU TO_UNIT_OPTION (Option_EAU)
|
|
#define UNIT_FP TO_UNIT_OPTION (Option_FP)
|
|
#define UNIT_IOP TO_UNIT_OPTION (Option_IOP)
|
|
#define UNIT_DMS TO_UNIT_OPTION (Option_DMS)
|
|
#define UNIT_FFP TO_UNIT_OPTION (Option_FFP)
|
|
#define UNIT_DBI TO_UNIT_OPTION (Option_DBI)
|
|
#define UNIT_EMA TO_UNIT_OPTION (Option_EMA)
|
|
#define UNIT_VMAOS TO_UNIT_OPTION (Option_VMAOS)
|
|
#define UNIT_VIS TO_UNIT_OPTION (Option_VIS)
|
|
#define UNIT_SIGNAL TO_UNIT_OPTION (Option_SIGNAL)
|
|
#define UNIT_DS TO_UNIT_OPTION (Option_DS)
|
|
|
|
#define UNIT_EMA_VMA (UNIT_EMA | UNIT_VMAOS)
|
|
|
|
/* Unit conversions to CPU options */
|
|
|
|
#define TO_CPU_MODEL(f) (CPU_OPTION) (1u << UNIT_MODEL (f))
|
|
#define TO_CPU_OPTION(f) (CPU_OPTION) (UNIT_OPTION (f) << CPU_OPTION_SHIFT)
|
|
|
|
/* "Pseudo-option" flags used only for option testing; never set into the UNIT structure */
|
|
|
|
#define UNIT_V_PFAIL (UNIT_V_UF - 1) /* Power fail is installed */
|
|
#define UNIT_V_DMA (UNIT_V_UF - 2) /* DMA is installed */
|
|
#define UNIT_V_MP (UNIT_V_UF - 3) /* Memory protect is installed */
|
|
|
|
#define UNIT_PFAIL (1 << UNIT_V_PFAIL)
|
|
#define UNIT_DMA (1 << UNIT_V_DMA)
|
|
#define UNIT_MP (1 << UNIT_V_MP)
|
|
|
|
|
|
/* CPU global SCP data definitions */
|
|
|
|
REG *sim_PC = NULL; /* the pointer to the P register */
|
|
|
|
|
|
/* CPU global data structures */
|
|
|
|
|
|
/* CPU registers */
|
|
|
|
HP_WORD ABREG [2] = { 0, 0 }; /* A and B registers */
|
|
|
|
HP_WORD PR = 0; /* P register */
|
|
HP_WORD SR = 0; /* S register */
|
|
HP_WORD MR = 0; /* M register */
|
|
HP_WORD TR = 0; /* T register */
|
|
HP_WORD XR = 0; /* X register */
|
|
HP_WORD YR = 0; /* Y register */
|
|
|
|
uint32 E = 0; /* E register */
|
|
uint32 O = 0; /* O register */
|
|
|
|
HP_WORD IR = 0; /* Instruction Register */
|
|
HP_WORD CIR = 0; /* Central Interrupt Register */
|
|
HP_WORD SPR = 0; /* 1000 Stack Pointer Register / 2100 F Register */
|
|
|
|
|
|
/* CPU global state */
|
|
|
|
FLIP_FLOP cpu_interrupt_enable = SET; /* interrupt enable flip-flop */
|
|
uint32 cpu_pending_interrupt = 0; /* pending interrupt select code or zero if none */
|
|
|
|
t_stat cpu_ss_unimpl = SCPE_OK; /* status return for unimplemented instruction execution */
|
|
t_stat cpu_ss_undef = SCPE_OK; /* status return for undefined instruction execution */
|
|
t_stat cpu_ss_unsc = SCPE_OK; /* status return for I/O to an unassigned select code */
|
|
t_stat cpu_ss_ioerr = SCPE_OK; /* status return for an unreported I/O error */
|
|
t_stat cpu_ss_inhibit = SCPE_OK; /* CPU stop inhibition mask */
|
|
UNIT *cpu_ioerr_uptr = NULL; /* pointer to a unit with an unreported I/O error */
|
|
|
|
HP_WORD err_PR = 0; /* error PC */
|
|
uint16 pcq [PCQ_SIZE] = { 0 }; /* PC queue (must be 16-bits wide for REG array entry) */
|
|
uint32 pcq_p = 0; /* PC queue pointer */
|
|
REG *pcq_r = NULL; /* PC queue register pointer */
|
|
|
|
CPU_OPTION_SET cpu_configuration; /* the current CPU option set and model */
|
|
uint32 cpu_speed = 1; /* the CPU speed, expressed as a multiplier of a real machine */
|
|
|
|
|
|
/* CPU local state.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. The "is_1000" variable is used to index into tables where the row
|
|
selected depends on whether or not the CPU is a 1000 M/E/F-series model.
|
|
For logical tests that depend on this, it is faster (by one x86 machine
|
|
instruction) to test the "cpu_configuration" variable for the presence of
|
|
one of the three 1000 model flags.
|
|
*/
|
|
|
|
static jmp_buf abort_environment; /* microcode abort environment */
|
|
|
|
static FLIP_FLOP interrupt_system = CLEAR; /* interrupt system */
|
|
static uint32 interrupt_request = 0; /* the currently interrupting select code or zero if none */
|
|
|
|
static uint32 interrupt_request_set [2] = { 0, 0 }; /* device interrupt request bit vector */
|
|
static uint32 priority_holdoff_set [2] = { 0, 0 }; /* device priority holdoff bit vector */
|
|
|
|
static uint32 exec_mask = 0; /* the current instruction execution trace mask */
|
|
static uint32 exec_match = D16_UMAX; /* the current instruction execution trace matching value */
|
|
static uint32 indirect_limit = 16; /* the indirect chain length limit */
|
|
|
|
static t_bool is_1000 = FALSE; /* TRUE if the CPU is a 1000 M/E/F-Series */
|
|
static t_bool mp_is_present = FALSE; /* TRUE if Memory Protect is present */
|
|
static uint32 last_select_code = 0; /* the last select code sent over the I/O backplane */
|
|
static HP_WORD saved_MR = 0; /* the M-register value between SCP commands */
|
|
|
|
static DEVICE *loader_rom [4] = { NULL }; /* the four boot loader ROM sockets in a 1000 CPU */
|
|
|
|
|
|
/* CPU local data structures */
|
|
|
|
|
|
/* CPU features table.
|
|
|
|
The feature table is used to validate CPU feature changes within the subset
|
|
of features supported by a given CPU. 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.
|
|
*/
|
|
|
|
typedef struct { /* CPU model feature table */
|
|
uint32 typ; /* standard features plus typically configured options */
|
|
uint32 opt; /* complete list of optional features */
|
|
uint32 maxmem; /* maximum configurable memory in 16-bit words */
|
|
} FEATURE_TABLE;
|
|
|
|
static const FEATURE_TABLE cpu_features [] = { /* CPU features indexed by OPTION_ID */
|
|
{ UNIT_DMA | UNIT_MP, /* Option_2116 */
|
|
UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_EAU,
|
|
32 * 1024
|
|
},
|
|
|
|
{ UNIT_DMA, /* Option_2115 */
|
|
UNIT_PFAIL | UNIT_DMA | UNIT_EAU,
|
|
8 * 1024
|
|
},
|
|
|
|
{ UNIT_DMA, /* Option_2114 */
|
|
UNIT_PFAIL | UNIT_DMA,
|
|
16 * 1024
|
|
},
|
|
|
|
{ UNIT_PFAIL | UNIT_MP | UNIT_DMA | UNIT_EAU, /* Option_2100 */
|
|
UNIT_DMA | UNIT_FP | UNIT_IOP | UNIT_FFP,
|
|
32 * 1024
|
|
},
|
|
|
|
{ UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | UNIT_DMS, /* Option_1000_M */
|
|
UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_DMS |
|
|
UNIT_IOP | UNIT_FFP | UNIT_DS,
|
|
1024 * 1024
|
|
},
|
|
|
|
{ UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | UNIT_DMS, /* Option_1000_E */
|
|
UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_DMS |
|
|
UNIT_IOP | UNIT_FFP | UNIT_DBI | UNIT_DS | UNIT_EMA_VMA,
|
|
1024 * 1024
|
|
},
|
|
|
|
{ UNIT_MP | UNIT_DMA | UNIT_EAU | UNIT_FP | /* Option_1000_F */
|
|
UNIT_FFP | UNIT_DBI | UNIT_DMS,
|
|
UNIT_PFAIL | UNIT_DMA | UNIT_MP | UNIT_DMS |
|
|
UNIT_VIS | UNIT_DS | UNIT_SIGNAL | UNIT_EMA_VMA,
|
|
1024 * 1024
|
|
}
|
|
};
|
|
|
|
|
|
/* CPU local SCP support routine declarations */
|
|
|
|
static INTERFACE cpu_interface;
|
|
static INTERFACE ovf_interface;
|
|
static INTERFACE pwr_interface;
|
|
|
|
static t_stat cpu_examine (t_value *eval, t_addr address, UNIT *uptr, int32 switches);
|
|
static t_stat cpu_deposit (t_value value, t_addr address, UNIT *uptr, int32 switches);
|
|
|
|
static t_stat cpu_reset (DEVICE *dptr);
|
|
static t_stat cpu_boot (int32 unitno, DEVICE *dptr);
|
|
|
|
static t_stat set_stops (UNIT *uptr, int32 option, CONST char *cptr, void *desc);
|
|
static t_stat set_size (UNIT *uptr, int32 new_size, CONST char *cptr, void *desc);
|
|
static t_stat set_model (UNIT *uptr, int32 new_model, CONST char *cptr, void *desc);
|
|
static t_stat set_option (UNIT *uptr, int32 option, CONST char *cptr, void *desc);
|
|
static t_stat clear_option (UNIT *uptr, int32 option, CONST char *cptr, void *desc);
|
|
static t_stat set_loader (UNIT *uptr, int32 enable, CONST char *cptr, void *desc);
|
|
static t_stat set_roms (UNIT *uptr, int32 option, CONST char *cptr, void *desc);
|
|
static t_stat set_exec (UNIT *uptr, int32 option, CONST char *cptr, void *desc);
|
|
|
|
static t_stat show_stops (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
|
static t_stat show_model (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
|
static t_stat show_roms (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
|
static t_stat show_cage (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
|
static t_stat show_exec (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
|
static t_stat show_speed (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
|
|
|
|
|
|
/* CPU local utility routine declarations */
|
|
|
|
static t_stat mrg_address (void);
|
|
static HP_WORD srg_uop (HP_WORD value, HP_WORD operation);
|
|
static t_stat machine_instruction (t_bool int_ack, uint32 *idle_save);
|
|
static t_bool reenable_interrupts (void);
|
|
|
|
|
|
/* CPU SCP data structures */
|
|
|
|
|
|
/* Device information blocks */
|
|
|
|
static DIB cpu_dib = { /* CPU (select code 0) */
|
|
&cpu_interface, /* the device's I/O interface function pointer */
|
|
CPU, /* the device's select code (02-77) */
|
|
0, /* the card index */
|
|
NULL, /* the card description */
|
|
NULL /* the ROM description */
|
|
};
|
|
|
|
static DIB ovfl_dib = { /* Overflow (select code 1) */
|
|
&ovf_interface, /* the device's I/O interface function pointer */
|
|
OVF, /* the device's select code (02-77) */
|
|
0, /* the card index */
|
|
NULL, /* the card description */
|
|
NULL /* the ROM description */
|
|
};
|
|
|
|
static DIB pwrf_dib = { /* Power Fail (select code 4) */
|
|
&pwr_interface, /* the device's I/O interface function pointer */
|
|
PWR, /* the device's select code (02-77) */
|
|
0, /* the card index */
|
|
NULL, /* the card description */
|
|
NULL /* the ROM description */
|
|
};
|
|
|
|
|
|
/* Unit list.
|
|
|
|
The CPU unit "capac" field is set to the current main memory capacity in
|
|
16-bit words by the "set_size" utility routine. The CPU does not use a unit
|
|
event service routine.
|
|
*/
|
|
|
|
static UNIT cpu_unit [] = {
|
|
{ UDATA (NULL, UNIT_FIX | UNIT_BINK, 0) }
|
|
};
|
|
|
|
|
|
/* Register list.
|
|
|
|
The CPU register list exposes the machine registers for user inspection and
|
|
modification.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. All registers that reference variables of type HP_WORD must have the
|
|
REG_FIT flag for proper access if HP_WORD is a 16-bit type.
|
|
|
|
2. The REG_X flag indicates that the register may be displayed in symbolic
|
|
form.
|
|
|
|
3. The T register cannot be modified. To change a memory location value,
|
|
the DEPOSIT CPU command must be used.
|
|
*/
|
|
|
|
static REG cpu_reg [] = {
|
|
/* Macro Name Location Radix Width Offset Depth Flags */
|
|
/* ------ ------- ------------------ ----- ----- -------- ----------------- ----------------- */
|
|
{ ORDATA (P, PR, 15) },
|
|
{ ORDATA (A, AR, 16), REG_X },
|
|
{ ORDATA (B, BR, 16), REG_X },
|
|
{ ORDATA (M, MR, 15) },
|
|
{ ORDATA (T, TR, 16), REG_RO | REG_X },
|
|
{ ORDATA (X, XR, 16), REG_X },
|
|
{ ORDATA (Y, YR, 16), REG_X },
|
|
{ ORDATA (S, SR, 16), REG_X },
|
|
{ FLDATA (E, E, 0) },
|
|
{ FLDATA (O, O, 0) },
|
|
{ ORDATA (CIR, CIR, 6) },
|
|
|
|
{ FLDATA (INTSYS, interrupt_system, 0) },
|
|
{ FLDATA (INTEN, cpu_interrupt_enable, 0) },
|
|
|
|
{ ORDATA (IOPSP, SPR, 16) },
|
|
{ BRDATA (PCQ, pcq, 8, 15, PCQ_SIZE), REG_CIRC | REG_RO },
|
|
|
|
{ ORDATA (IR, IR, 16), REG_HRO },
|
|
{ ORDATA (SAVEDMR, saved_MR, 32), REG_HRO },
|
|
{ ORDATA (PCQP, pcq_p, 6), REG_HRO },
|
|
|
|
{ ORDATA (EMASK, exec_mask, 16), REG_HRO },
|
|
{ ORDATA (EMATCH, exec_match, 16), REG_HRO },
|
|
{ DRDATA (ILIMIT, indirect_limit, 16), REG_HRO },
|
|
{ ORDATA (FWANXM, mem_end, 32), REG_HRO },
|
|
{ ORDATA (CONFIG, cpu_configuration, 32), REG_HRO },
|
|
{ BRDATA (ROMS, loader_rom, 8, 32, 4), REG_HRO },
|
|
|
|
{ FLDATA (IS1000, is_1000, 0), REG_HRO },
|
|
{ ORDATA (INTREQ, interrupt_request, 6), REG_HRO },
|
|
{ ORDATA (LASTSC, last_select_code, 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 },
|
|
|
|
{ NULL }
|
|
};
|
|
|
|
|
|
/* Modifier list.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. 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.
|
|
|
|
2. The string descriptors are used by the "show_model" routine to print the
|
|
CPU model numbers prior to appending "loader enabled" or "loader
|
|
disabled" to the report.
|
|
|
|
3. Each CPU option requires three modifiers. The two regular modifiers
|
|
control the setting and printing of the option, while the extended
|
|
modifier controls clearing the option. The latter is necessary because
|
|
the option must be checked before confirming the change, and so the
|
|
option value must be passed to the validation routine.
|
|
*/
|
|
|
|
static MTAB cpu_mod [] = {
|
|
/* Mask Value Match Value Print String Match String Validation Display Descriptor */
|
|
/* ---------------- ----------- ------------ ------------ ------------- ----------- ----------------- */
|
|
{ UNIT_MODEL_FIELD, UNIT_2116, "", "2116", &set_model, &show_model, (void *) "2116" },
|
|
{ UNIT_MODEL_FIELD, UNIT_2115, "", "2115", &set_model, &show_model, (void *) "2115" },
|
|
{ UNIT_MODEL_FIELD, UNIT_2114, "", "2114", &set_model, &show_model, (void *) "2114" },
|
|
{ UNIT_MODEL_FIELD, UNIT_2100, "", "2100", &set_model, &show_model, (void *) "2100" },
|
|
{ UNIT_MODEL_FIELD, UNIT_1000_E, "", "1000-E", &set_model, &show_model, (void *) "1000-E" },
|
|
{ UNIT_MODEL_FIELD, UNIT_1000_M, "", "1000-M", &set_model, &show_model, (void *) "1000-M" },
|
|
|
|
#if defined (HAVE_INT64)
|
|
{ UNIT_MODEL_FIELD, UNIT_1000_F, "", "1000-F", &set_model, &show_model, (void *) "1000-F" },
|
|
#endif
|
|
|
|
{ UNIT_MODEL_FIELD, UNIT_1000_M, NULL, "21MX-M", &set_model, &show_model, (void *) "1000-M" },
|
|
{ UNIT_MODEL_FIELD, UNIT_1000_E, NULL, "21MX-E", &set_model, &show_model, (void *) "1000-E" },
|
|
|
|
{ UNIT_EAU, UNIT_EAU, "EAU", "EAU", &set_option, NULL, NULL },
|
|
{ UNIT_EAU, 0, "no EAU", NULL, NULL, NULL, NULL },
|
|
{ MTAB_XDV, UNIT_EAU, NULL, "NOEAU", &clear_option, NULL, NULL },
|
|
|
|
{ UNIT_FP, UNIT_FP, "FP", "FP", &set_option, NULL, NULL },
|
|
{ UNIT_FP, 0, "no FP", NULL, NULL, NULL, NULL },
|
|
{ MTAB_XDV, UNIT_FP, NULL, "NOFP", &clear_option, NULL, NULL },
|
|
|
|
{ UNIT_IOP, UNIT_IOP, "IOP", "IOP", &set_option, NULL, NULL },
|
|
{ UNIT_IOP, 0, "no IOP", NULL, NULL, NULL, NULL },
|
|
{ MTAB_XDV, UNIT_IOP, NULL, "NOIOP", &clear_option, NULL, NULL },
|
|
|
|
{ UNIT_DMS, UNIT_DMS, "DMS", "DMS", &set_option, NULL, NULL },
|
|
{ UNIT_DMS, 0, "no DMS", NULL, NULL, NULL, NULL },
|
|
{ MTAB_XDV, UNIT_DMS, NULL, "NODMS", &clear_option, NULL, NULL },
|
|
|
|
{ UNIT_FFP, UNIT_FFP, "FFP", "FFP", &set_option, NULL, NULL },
|
|
{ UNIT_FFP, 0, "no FFP", NULL, NULL, NULL, NULL },
|
|
{ MTAB_XDV, UNIT_FFP, NULL, "NOFFP", &clear_option, NULL, NULL },
|
|
|
|
{ UNIT_DBI, UNIT_DBI, "DBI", "DBI", &set_option, NULL, NULL },
|
|
{ UNIT_DBI, 0, "no DBI", NULL, NULL, NULL, NULL },
|
|
{ MTAB_XDV, UNIT_DBI, NULL, "NODBI", &clear_option, NULL, NULL },
|
|
|
|
{ UNIT_EMA_VMA, UNIT_EMA, "EMA", "EMA", &set_option, NULL, NULL },
|
|
{ MTAB_XDV, UNIT_EMA, NULL, "NOEMA", &clear_option, NULL, NULL },
|
|
|
|
{ UNIT_EMA_VMA, UNIT_VMAOS, "VMA", "VMA", &set_option, NULL, NULL },
|
|
{ MTAB_XDV, UNIT_VMAOS, NULL, "NOVMA", &clear_option, NULL, NULL },
|
|
|
|
{ UNIT_EMA_VMA, 0, "no EMA/VMA", NULL, &set_option, NULL, NULL },
|
|
|
|
#if defined (HAVE_INT64)
|
|
{ UNIT_VIS, UNIT_VIS, "VIS", "VIS", &set_option, NULL, NULL },
|
|
{ UNIT_VIS, 0, "no VIS", NULL, NULL, NULL, NULL },
|
|
{ MTAB_XDV, UNIT_VIS, NULL, "NOVIS", &clear_option, NULL, NULL },
|
|
|
|
{ UNIT_SIGNAL, UNIT_SIGNAL, "SIGNAL", "SIGNAL", &set_option, NULL, NULL },
|
|
{ UNIT_SIGNAL, 0, "no SIGNAL", NULL, NULL, NULL, NULL },
|
|
{ MTAB_XDV, UNIT_SIGNAL, NULL, "NOSIGNAL", &clear_option, NULL, NULL },
|
|
#endif
|
|
|
|
/* Future microcode support.
|
|
{ UNIT_DS, UNIT_DS, "DS", "DS", &set_option, NULL, NULL },
|
|
{ UNIT_DS, 0, "no DS", NULL, NULL, NULL, NULL },
|
|
{ MTAB_XDV, UNIT_DS, NULL, "NODS", &clear_option, NULL, NULL },
|
|
*/
|
|
|
|
/* Entry Flags Value Print String Match String Validation Display Descriptor */
|
|
/* ------------------- ----------- ------------ --------------- ------------- -------------- ---------- */
|
|
{ MTAB_XDV, 0, "IDLE", "IDLE", &sim_set_idle, &sim_show_idle, NULL },
|
|
{ MTAB_XDV, 0, NULL, "NOIDLE", &sim_clr_idle, NULL, NULL },
|
|
|
|
{ MTAB_XDV, 1, NULL, "LOADERENABLE", &set_loader, NULL, NULL },
|
|
{ MTAB_XDV, 0, NULL, "LOADERDISABLE", &set_loader, NULL, NULL },
|
|
|
|
{ MTAB_XDV, 4 * 1024, NULL, "4K", &set_size, NULL, NULL },
|
|
{ MTAB_XDV, 8 * 1024, NULL, "8K", &set_size, NULL, NULL },
|
|
{ MTAB_XDV, 12 * 1024, NULL, "12K", &set_size, NULL, NULL },
|
|
{ MTAB_XDV, 16 * 1024, NULL, "16K", &set_size, NULL, NULL },
|
|
{ MTAB_XDV, 24 * 1024, NULL, "24K", &set_size, NULL, NULL },
|
|
{ MTAB_XDV, 32 * 1024, NULL, "32K", &set_size, NULL, NULL },
|
|
{ MTAB_XDV, 64 * 1024, NULL, "64K", &set_size, NULL, NULL },
|
|
{ MTAB_XDV, 128 * 1024, NULL, "128K", &set_size, NULL, NULL },
|
|
{ MTAB_XDV, 256 * 1024, NULL, "256K", &set_size, NULL, NULL },
|
|
{ MTAB_XDV, 512 * 1024, NULL, "512K", &set_size, NULL, NULL },
|
|
{ MTAB_XDV, 1024 * 1024, NULL, "1024K", &set_size, NULL, NULL },
|
|
|
|
{ MTAB_XDV | MTAB_NMO, 0, "ROMS", "ROMS", &set_roms, &show_roms, NULL },
|
|
{ MTAB_XDV | MTAB_NMO, 0, "IOCAGE", NULL, NULL, &show_cage, NULL },
|
|
|
|
{ MTAB_XDV | MTAB_NMO, 1, "STOPS", "STOP", &set_stops, &show_stops, NULL },
|
|
{ MTAB_XDV, 0, NULL, "NOSTOP", &set_stops, NULL, NULL },
|
|
{ MTAB_XDV | MTAB_NMO, 2, "INDIR", "INDIR", &set_stops, &show_stops, NULL },
|
|
|
|
{ MTAB_XDV | MTAB_NMO, 1, "EXEC", "EXEC", &set_exec, &show_exec, NULL },
|
|
{ MTAB_XDV, 0, NULL, "NOEXEC", &set_exec, NULL, NULL },
|
|
|
|
{ MTAB_XDV | MTAB_NMO, 0, "SPEED", NULL, NULL, &show_speed, NULL },
|
|
|
|
{ 0 }
|
|
};
|
|
|
|
|
|
/* Trace list */
|
|
|
|
static DEBTAB cpu_deb [] = {
|
|
{ "INSTR", TRACE_INSTR }, /* trace instruction executions */
|
|
{ "DATA", TRACE_DATA }, /* trace memory data accesses */
|
|
{ "FETCH", TRACE_FETCH }, /* trace memory instruction fetches */
|
|
{ "REG", TRACE_REG }, /* trace register values */
|
|
{ "OPND", TRACE_OPND }, /* trace instruction operands */
|
|
{ "EXEC", TRACE_EXEC }, /* trace matching instruction execution states */
|
|
{ "NOOS", DEBUG_NOOS }, /* RTE-6/VM will not use OS firmware */
|
|
{ NULL, 0 }
|
|
};
|
|
|
|
|
|
/* Simulation stop list.
|
|
|
|
The simulator can be configured to detect certain machine instruction
|
|
conditions and stop execution when one of them occurs. Stops may be enabled
|
|
or disabled individually with these commands:
|
|
|
|
SET CPU STOP=<option>[;<option]
|
|
SET CPU NOSTOP=<option>[;<option]
|
|
|
|
The CPU stop table is used to parse the commands and set the appropriate
|
|
variables to enable or disable the stops.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. To avoid the testing of stop conditions at run time, they are implemented
|
|
by setting individual stop status variables either to the appropriate
|
|
stop code (if enabled) or to SCPE_OK (if disabled). This allows the
|
|
affected routines to return the status value unconditionally and cause
|
|
either a simulator stop or continued execution without a run-time test.
|
|
|
|
2. SCPE_IOERR is not actually returned for unreported I/O errors. Instead,
|
|
it is simply a flag that a stop code specific to the detected error
|
|
should be returned.
|
|
|
|
3. To permit stops to be bypassed for one instruction execution, routines
|
|
use the STOP macro to return the value of the applicable stop variable
|
|
ANDed with the complement of the value of the "cpu_ss_inhibit" variable.
|
|
The latter is set in the instruction prelude to SS_INHIBIT (i.e., all
|
|
ones) if a bypass is requested or to SCPE_OK (i.e., all zeros) if not,
|
|
and is reset to SCPE_OK after each instruction execution. The effect is
|
|
that SCPE_OK is returned instead of a simulator stop if a stop condition
|
|
occurs when a bypass is specified. This action depends on the value of
|
|
SCPE_OK being zero (which is guaranteed).
|
|
*/
|
|
|
|
typedef struct {
|
|
const char *name; /* stop name */
|
|
t_stat *status; /* pointer to the stop status variable */
|
|
t_stat value; /* stop status return value */
|
|
} STOPTAB;
|
|
|
|
static STOPTAB cpu_stop [] = {
|
|
{ "UNIMPL", &cpu_ss_unimpl, STOP_UNIMPL }, /* stop on an unimplemented instruction */
|
|
{ "UNDEF", &cpu_ss_undef, STOP_UNDEF }, /* stop on an undefined instruction */
|
|
{ "UNSC", &cpu_ss_unsc, STOP_UNSC }, /* stop on I/O to an unassigned select code */
|
|
{ "IOERR", &cpu_ss_ioerr, SCPE_IOERR }, /* stop on an unreported I/O error */
|
|
{ NULL, NULL, 0 }
|
|
};
|
|
|
|
|
|
/* Device descriptor */
|
|
|
|
DEVICE cpu_dev = {
|
|
"CPU", /* device name */
|
|
cpu_unit, /* unit array */
|
|
cpu_reg, /* register array */
|
|
cpu_mod, /* modifier array */
|
|
1, /* number of units */
|
|
8, /* address radix */
|
|
PA_WIDTH, /* address width */
|
|
1, /* address increment */
|
|
8, /* data radix */
|
|
16, /* data width */
|
|
&cpu_examine, /* examine routine */
|
|
&cpu_deposit, /* deposit routine */
|
|
&cpu_reset, /* reset routine */
|
|
&cpu_boot, /* boot routine */
|
|
NULL, /* attach routine */
|
|
NULL, /* detach routine */
|
|
&cpu_dib, /* device information block pointer */
|
|
DEV_DEBUG, /* device flags */
|
|
0, /* debug control flags */
|
|
cpu_deb, /* debug flag name table */
|
|
NULL, /* memory size change routine */
|
|
NULL /* logical device name */
|
|
};
|
|
|
|
|
|
|
|
/* I/O subsystem local data structures */
|
|
|
|
|
|
/* Signal names */
|
|
|
|
static const BITSET_NAME inbound_names [] = { /* Inbound signal names, in INBOUND_SIGNAL order */
|
|
"PON", /* 000000000001 */
|
|
"IOI", /* 000000000002 */
|
|
"IOO", /* 000000000004 */
|
|
"SFS", /* 000000000010 */
|
|
"SFC", /* 000000000020 */
|
|
"STC", /* 000000000040 */
|
|
"CLC", /* 000000000100 */
|
|
"STF", /* 000000000200 */
|
|
"CLF", /* 000000000400 */
|
|
"EDT", /* 000000001000 */
|
|
"CRS", /* 000000002000 */
|
|
"POPIO", /* 000000004000 */
|
|
"IAK", /* 000000010000 */
|
|
"ENF", /* 000000020000 */
|
|
"SIR", /* 000000040000 */
|
|
"IEN", /* 000000100000 */
|
|
"PRH" /* 000000200000 */
|
|
};
|
|
|
|
static const BITSET_FORMAT inbound_format = /* names, offset, direction, alternates, bar */
|
|
{ FMT_INIT (inbound_names, 0, lsb_first, no_alt, no_bar) };
|
|
|
|
|
|
static const BITSET_NAME outbound_names [] = { /* Outbound signal names, in OUTBOUND_SIGNAL order */
|
|
"SKF", /* 000000000001 */
|
|
"PRL", /* 000000000002 */
|
|
"FLG", /* 000000000004 */
|
|
"IRQ", /* 000000000010 */
|
|
"SRQ" /* 000000000020 */
|
|
};
|
|
|
|
static const BITSET_FORMAT outbound_format = /* names, offset, direction, alternates, bar */
|
|
{ FMT_INIT (outbound_names, 0, lsb_first, no_alt, no_bar) };
|
|
|
|
|
|
/* I/O signal tables.
|
|
|
|
These tables contain the set of I/O signals that are appropriate for each I/O
|
|
operation. Two tables are defined.
|
|
|
|
The first table defines the backplane signals generated by each I/O Group
|
|
operation. A signal set is sent to the device interface associated with the
|
|
select code specified in an IOG instruction to direct the operation of the
|
|
interface. Backplane signals map closely to IOG instructions. For example,
|
|
the SFS instruction asserts the SFS backplane signal to the interface card.
|
|
|
|
The hardware ENF and SIR signals are periodic. In simulation, they are
|
|
asserted only when the flag buffer or the flag and control flip-flops are
|
|
changed, respectively.
|
|
|
|
The second table defines the signals generated in addition to the explicitly
|
|
asserted signal. Specifically, asserting PON also asserts POPIO and CRS,
|
|
and asserting POPIO also asserts CRS. The ENF and SIR signals are asserted
|
|
when necessary as described above.
|
|
*/
|
|
|
|
static const INBOUND_SET control_set [] = { /* indexed by IO_GROUP_OP */
|
|
ioNONE, /* iog_HLT */
|
|
ioSTF | ioENF | ioSIR, /* iog_STF */
|
|
ioSFC, /* iog_SFC */
|
|
ioSFS, /* iog_SFS */
|
|
ioIOI, /* iog_MIx */
|
|
ioIOI, /* iog_LIx */
|
|
ioIOO, /* iog_OTx */
|
|
ioSTC | ioSIR, /* iog_STC */
|
|
|
|
ioNONE | ioCLF | ioSIR, /* iog_HLT_C */
|
|
ioCLF | ioSIR, /* iog_CLF */
|
|
ioSFC | ioCLF | ioSIR, /* iog_SFC_C */
|
|
ioSFS | ioCLF | ioSIR, /* iog_SFS_C */
|
|
ioIOI | ioCLF | ioSIR, /* iog_MIx_C */
|
|
ioIOI | ioCLF | ioSIR, /* iog_LIx_C */
|
|
ioIOO | ioCLF | ioSIR, /* iog_OTx_C */
|
|
ioSTC | ioCLF | ioSIR, /* iog_STC_C */
|
|
|
|
ioCLC | ioSIR, /* iog_CLC */
|
|
ioCLC | ioCLF | ioSIR /* iog_CLC_C */
|
|
};
|
|
|
|
static const INBOUND_SET assert_set [] = { /* indexed by IO_ASSERTION */
|
|
ioENF | ioSIR, /* ioa_ENF */
|
|
ioSIR, /* ioa_SIR */
|
|
ioPON | ioPOPIO | ioCRS | ioENF | ioSIR, /* ioa_PON */
|
|
ioPOPIO | ioCRS | ioENF | ioSIR, /* ioa_POPIO */
|
|
ioCRS | ioENF | ioSIR, /* ioa_CRS */
|
|
ioIAK | ioSIR /* ioa_IAK */
|
|
};
|
|
|
|
|
|
/* Interrupt enable table.
|
|
|
|
I/O Group instructions that alter the interrupt priority chain must delay
|
|
recognition of interrupts until the following instruction completes. The HP
|
|
1000 microcode does this by executing an IOFF micro-order to clear the
|
|
interrupt enable (INTEN) flip-flop. The table below gives the INTEN state
|
|
for each I/O instruction; CLEAR corresponds to an IOFF micro-order, while SET
|
|
corresponds to ION. The table is also indexed by the "is_1000" selector, as
|
|
the disable rules are different for the 21xx and 1000 machines.
|
|
*/
|
|
|
|
static const t_bool enable_map [2] [18] = { /* interrupt enable table, indexed by is_1000 and IO_GROUP_OP */
|
|
/* HLT STF SFC SFS MIx LIx OTx STC */
|
|
/* HLT_C CLF SFC_C SFS_C MIx_C LIx_C OTx_C STC_C */
|
|
/* CLC CLC_C */
|
|
/* ----- ----- ----- ----- ----- ----- ----- ----- */
|
|
{ CLEAR, CLEAR, SET, SET, SET, SET, SET, CLEAR, /* 21xx */
|
|
CLEAR, CLEAR, SET, SET, SET, SET, SET, CLEAR, /* 21xx */
|
|
CLEAR, CLEAR },
|
|
|
|
{ CLEAR, CLEAR, CLEAR, CLEAR, SET, SET, SET, CLEAR, /* 1000 */
|
|
CLEAR, CLEAR, CLEAR, CLEAR, SET, SET, SET, CLEAR, /* 1000 */
|
|
CLEAR, CLEAR }
|
|
};
|
|
|
|
|
|
/* I/O access table.
|
|
|
|
I/O signals are directed to specific interface cards by specifying their
|
|
locations in the I/O card cage. Each location is assigned a number, called a
|
|
select code, that is specified by I/O Group instructions to indicate the
|
|
interface card to activate.
|
|
|
|
In simulation, the select code corresponding to each interface is stored in
|
|
the corresponding Device Information Block (DIB). To avoid having to scan
|
|
the device list each time an I/O instruction is executed, an I/O access table
|
|
is filled in as part of I/O initialization in the instruction execution
|
|
prelude. The table is indexed by select code (00-77 octal) and contains
|
|
pointers to the device and DIB structures associated with each index.
|
|
|
|
Initialization is performed during each "sim_instr" call, as the select code
|
|
assignments may have been changed by the user at the SCP prompt.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. The entries for select codes 0 and 1 (the CPU and Overflow Register,
|
|
respectively) are initialized here, as they are always present (i.e.,
|
|
cannot be disabled) and cannot change.
|
|
|
|
2. The table contains constant pointers, but "const" cannot be used here, as
|
|
"hp_trace" calls "sim_dname", which takes a variable device pointer even
|
|
though it does not change anything.
|
|
|
|
3. The "references" entries are used only during table initialization to
|
|
ensure that each select code is referenced by only one device.
|
|
*/
|
|
|
|
typedef struct { /* I/O access table entry */
|
|
DEVICE *devptr; /* a pointer to the DEVICE structure */
|
|
DIB *dibptr; /* a pointer to the DIB structure */
|
|
uint32 references; /* a count of the references to this select code */
|
|
} IO_TABLE;
|
|
|
|
static IO_TABLE iot [SC_MAX + 1] = { /* index by select code for I/O instruction dispatch */
|
|
{ &cpu_dev, &cpu_dib, 0 }, /* select code 00 = interrupt system */
|
|
{ &cpu_dev, &ovfl_dib, 0 } /* select code 01 = overflow register */
|
|
};
|
|
|
|
|
|
/* I/O subsystem local utility routine declarations */
|
|
|
|
static t_bool initialize_io (t_bool is_executing);
|
|
|
|
|
|
|
|
/* CPU global SCP support routines */
|
|
|
|
|
|
/* Execute CPU instructions.
|
|
|
|
This is the instruction decode routine for the HP 21xx/1000 simulator. It is
|
|
called from the simulator control program (SCP) to execute instructions in
|
|
simulated memory, starting at the simulated program counter. It runs until
|
|
the status to be returned is set to a value other than SCPE_OK.
|
|
|
|
On entry, P points to the instruction to execute, and the "sim_switches"
|
|
global contains any command-line switches included with the RUN command. On
|
|
exit, P points at the next instruction to execute.
|
|
|
|
Execution is divided into four phases.
|
|
|
|
First, the instruction prelude configures the simulation state to resume
|
|
execution. This involves verifying that there are no device conflicts (e.g.,
|
|
two devices with the same select code) and initializing the I/O state. These
|
|
actions accommodate reconfiguration of the I/O device settings and program
|
|
counter while the simulator was stopped. The prelude also checks for one
|
|
command-line switch: if "-B" is specified, the current set of simulation stop
|
|
conditions is bypassed for the first instruction executed.
|
|
|
|
Second, the memory protect option is initialized. MP aborts utilize the
|
|
"setjmp/longjmp" mechanism to transfer control out of the instruction
|
|
executors without returning through the call stack. This allows an
|
|
instruction to be aborted part-way through execution when continuation is
|
|
impossible due to a memory access violation. An MP abort returns to the main
|
|
instruction loop at the "setjmp" routine.
|
|
|
|
Third, the instruction execution loop decodes instructions and calls the
|
|
individual executors in turn until a condition occurs that prevents further
|
|
execution. Examples of such conditions include execution of a HLT
|
|
instruction, a user stop request (CTRL+E) from the simulation console, a
|
|
recoverable device error (such as an improperly formatted tape image), a
|
|
user-specified breakpoint, and a simulation stop condition (such as execution
|
|
of an unimplemented instruction). The execution loop also polls for I/O
|
|
events and device interrupts, and runs DMA channel cycles. During
|
|
instruction execution, the IR register contains the currently executing
|
|
instruction, and the P register points to the memory location containing the
|
|
next instruction.
|
|
|
|
Fourth, the instruction postlude updates the simulation state in preparation
|
|
for returning to the SCP command prompt. Devices that maintain an internal
|
|
state different from their external state, such as the MEM status and
|
|
violation registers, are updated so that their internal and external states
|
|
are fully consistent. This ensures that the state visible to the user during
|
|
the simulation stop is correct. It also ensures that the program counter
|
|
points correctly at the next instruction to execute upon resumption.
|
|
|
|
|
|
The instruction execution loop starts by checking for event timer expiration.
|
|
If one occurs, the associated device's service routine is called by the
|
|
"sim_process_event" routine. Then a check for DMA service requests is made.
|
|
If a request is active, the "dma_service" routine is called to process it.
|
|
|
|
DMA cycles are requested by an I/O card asserting its SRQ signal. If a DMA
|
|
channel is programmed to respond to that card's select code, the channel's
|
|
service request flag is set in the "dma_request_set". On each pass through
|
|
the instruction execution loop, "dma_request_set" is checked; if it is
|
|
non-zero, a DMA cycle will be initiated. A DMA cycle consists of a memory
|
|
cycle and an I/O cycle. These cycles are synchronized with the control
|
|
processor on the 21xx CPUs. On the 1000s, memory cycles are asynchronous,
|
|
while I/O cycles are synchronous. Memory cycle time is about 40% of the I/O
|
|
cycle time.
|
|
|
|
With properly designed interface cards, DMA is capable of taking consecutive
|
|
I/O cycles. On all machines except the 1000 M-Series, a DMA cycle freezes
|
|
the CPU for the duration of the cycle. On the M-Series, a DMA cycle freezes
|
|
the CPU if it attempts an I/O cycle (including IAK) or a directly-interfering
|
|
memory cycle. An interleaved memory cycle is allowed. Otherwise, the
|
|
control processor is allowed to run. Therefore, during consecutive DMA
|
|
cycles, the M-Series CPU will run until an IOG instruction is attempted,
|
|
whereas the other CPUs will freeze completely. This is simulated by skipping
|
|
instruction execution if "dma_request_set" is still non-zero after servicing
|
|
the current request, i.e., if the device asserted SRQ again as a result of
|
|
the DMA cycle.
|
|
|
|
All DMA cards except the 12607B provide two independent channels. If both
|
|
channels are active simultaneously, channel 1 has priority for I/O cycles
|
|
over channel 2.
|
|
|
|
Most I/O cards assert SRQ no more than 50% of the time. A few buffered
|
|
cards, such as the 12821A and 13175A Disc Interfaces, are capable of
|
|
asserting SRQ continuously while filling or emptying the buffer. If SRQ for
|
|
channel 1 is asserted continuously when both channels are active, then no
|
|
channel 2 cycles will occur until channel 1 completes.
|
|
|
|
After DMA servicing, a check for pending interrupt requests is made.
|
|
|
|
Interrupt recognition in the HP 1000 CPU is controlled by three state
|
|
variables: "interrupt_system", "cpu_interrupt_enable", and
|
|
"interrupt_request". "interrupt_system" corresponds to the INTSYS flip-flop
|
|
in the 1000 CPU, "cpu_interrupt_enable" corresponds to the INTEN flip-flop,
|
|
and "interrupt_request" corresponds to the NRMINT flip-flop. STF 00 and CLF
|
|
00 set and clear INTSYS, turning the interrupt system on and off. Microcode
|
|
instructions ION and IOFF set and clear INTEN, enabling or disabling certain
|
|
interrupts. An IRQ signal from a device, qualified by the corresponding PRH
|
|
and IEN signals, will set NRMINT to request a normal interrupt; an IOFF or
|
|
IAK will clear it.
|
|
|
|
Under simulation, "interrupt_system" is controlled by STF/CLF 00.
|
|
"cpu_interrupt_enable" is set or cleared as appropriate by the individual
|
|
instruction simulators. "interrupt_request" is set to the successfully
|
|
interrupting device's select code, or to zero if there is no qualifying
|
|
interrupt request.
|
|
|
|
The rules controlling interrupt recognition are:
|
|
|
|
1. Power fail (SC 04) may interrupt if "cpu_interrupt_enable" is set; this
|
|
is not conditional on "interrupt_system" being set.
|
|
|
|
2. Memory protect (SC 05) may interrupt if "interrupt_system" is set; this
|
|
is not conditional on "cpu_interrupt_enable" being set.
|
|
|
|
3. Parity error (SC 05) may interrupt always; this is not conditional on
|
|
either "interrupt_system" or "cpu_interrupt_enable" being set.
|
|
|
|
4. All other devices (SC 06 and up) may interrupt only if both
|
|
"interrupt_system" and "cpu_interrupt_enable" are set.
|
|
|
|
Qualification with "interrupt_system" is performed by the I/O dispatcher,
|
|
which asserts IEN to the device interface if the interrupt system is on. All
|
|
interfaces other than Power Fail or Parity Error assert IRQ only if IEN is
|
|
asserted. If IEN is denied, i.e., the interrupt system is off, then only
|
|
Power Fail and Parity Error will assert IRQ and thereby set the
|
|
"interrupt_request" value to their respective select codes. Therefore, we
|
|
need only qualify by "cpu_interrupt_enable" here.
|
|
|
|
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, for a 1000-series CPU, an SFS, SFC, JRS, DJP, DJS, SJP, SJS, UJP, or
|
|
UJS. The executors for these instructions clear the "cpu_interrupt_enable"
|
|
flag, which is then set unilaterally when each instruction is dispatched.
|
|
The flag is also cleared by an interrupt acknowledgement, deferring
|
|
additional interrupts until after the instruction in the trap cell is
|
|
executed.
|
|
|
|
On the HP 1000, an interrupt 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. In simulation, the "reenable_interrupts" routine is called to
|
|
handle this case.
|
|
|
|
|
|
When a status other than SCPE_OK is returned from an instruction executor or
|
|
event service routine, the instruction execution loop exits into the
|
|
instruction postlude. The set of debug trace flags is restored if it had
|
|
been changed by an active execution trace or idle trace suppression. This
|
|
ensures that the simulation stop does not exit with the flags set improperly.
|
|
If the simulation stopped for a programmed halt, the 21xx binary loader area
|
|
is protected in case it had been unprotected to run the loader. The MEU
|
|
status and violation registers and the program counter queue pointer are
|
|
updated to present the proper values to the user interface. The default
|
|
breakpoint type is updated to reflect the current MEU state (disabled, system
|
|
map enabled, or user map enabled). Finally, the P register is reset if the
|
|
current instruction is to be reexecuted on reentry (for example, on an
|
|
unimplemented instruction stop).
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. While the Microsoft VC++ "setjmp" documentation says, "All variables
|
|
(except register variables) accessible to the routine receiving control
|
|
contain the values they had when longjmp was called," the ISO C99
|
|
standard says, "All accessible objects have values...as of the time the
|
|
longjmp function was called, except that the values of objects of
|
|
automatic storage duration that are local to the function containing the
|
|
invocation of the corresponding setjmp macro that do not have
|
|
volatile-qualified type and have been changed between the setjmp
|
|
invocation and longjmp call are indeterminate."
|
|
|
|
Therefore, the "exec_save" and "idle_save" variables are marked static
|
|
to ensure that they are reloaded after a longjmp caused by a memory
|
|
protect abort (they are not marked volatile to save redundant reloads
|
|
within the instruction execution loop). Also, "status" and "exec_test"
|
|
are set before reentering the instruction loop after an abort. This is
|
|
done solely to reassure the compiler that the values are not clobbered,
|
|
even though in both cases the values are reestablished after an abort
|
|
before they are used.
|
|
|
|
2. The C standard requires that the "setjmp" call be made from a frame that
|
|
is still active (i.e., still on the stack) when "longjmp" is called.
|
|
Consequently, we must call "setjmp" from this routine rather than a
|
|
subordinate routine that will have exited (to return to this routine)
|
|
when the "longjmp" call is made.
|
|
|
|
3. The -P switch is removed from the set of command line switches to ensure
|
|
that internal calls to the device reset routines are not interpreted as
|
|
"power-on" resets.
|
|
|
|
4. A CPU freeze is simulated by skipping instruction execution during the
|
|
current loop cycle.
|
|
|
|
5. The 1000 M-Series allows some CPU processing concurrently with
|
|
continuous DMA cycles, whereas all other CPUs freeze. The processor
|
|
freezes if an I/O cycle is attempted, including an interrupt
|
|
acknowledgement. Because some microcode extensions (e.g., Access IOP,
|
|
RTE-6/VM OS) perform I/O cycles, advance detection of I/O cycles is
|
|
difficult. Therefore, we freeze all processing for the M-Series as well.
|
|
|
|
6. EXEC tracing is active when exec_save is non-zero. "exec_save" saves the
|
|
current state of the trace flags when an EXEC trace match occurs. For
|
|
this to happen, at least TRACE_EXEC must be set, so "exec_save" will be
|
|
set non-zero when a match is active.
|
|
|
|
7. The execution trace (TRACE_EXEC) match test is performed in two parts to
|
|
display the register values both before and after the instruction
|
|
execution. Consequently, the enable test is done before the register
|
|
trace, and the disable test is done after.
|
|
|
|
8. A simulation stop bypass is inactivated after the first instruction
|
|
execution by the expedient of setting the stop inhibition mask to the
|
|
execution status result. This must be SCPE_OK (i.e., zero) for execution
|
|
to continue, which removes the stop inhibition. If a non-zero status
|
|
value is returned, then the inhibition mask will be set improperly, but
|
|
that is irrelevant, as execution will stop in this case.
|
|
|
|
9. In hardware, the IAK signal is asserted to all devices in the I/O card
|
|
cage, as well as to the Memory Protect card. Only the interrupting
|
|
device will respond to IAK. In simulation, IAK is dispatched to the
|
|
interrupting device and, if that device is not the MP card, then to the
|
|
MP device as well.
|
|
|
|
10. In hardware, execution of the instruction in the trap cell does not use
|
|
the FTCH micro-order to avoid changing the MP violation register during
|
|
an MP interrupt. In simulation, we use a Fetch classification to label
|
|
the access correctly in a trace listing. This is OK because the Enable
|
|
Violation Register flip-flop has already been reset if this is an MP
|
|
interrupt, so the Fetch will not alter the MP VR.
|
|
|
|
11. The "meu_assert_IAK" routine sets the "meu_indicator" and "meu_page"
|
|
values for the P register before switching to the system map. Therefore,
|
|
on return, they indicate the prior map and page in use when the interrupt
|
|
occurred.
|
|
*/
|
|
|
|
t_stat sim_instr (void)
|
|
{
|
|
static uint32 exec_save; /* the trace flag settings saved by an EXEC match */
|
|
static uint32 idle_save; /* the trace flag settings saved by an idle match */
|
|
MICRO_ABORT abort_reason;
|
|
t_bool exec_test; /* set after setjmp */
|
|
t_bool interrupt_acknowledge; /* set after setjmp */
|
|
t_stat status; /* set after setjmp */
|
|
|
|
|
|
/* Instruction prelude */
|
|
|
|
if (sim_switches & SWMASK ('B')) /* if a simulation stop bypass was requested */
|
|
cpu_ss_inhibit = SS_INHIBIT; /* then inhibit stops for the first instruction only */
|
|
else /* otherwise */
|
|
cpu_ss_inhibit = SCPE_OK; /* clear the inhibition mask */
|
|
|
|
sim_switches &= ~SWMASK ('P'); /* clear the power-on switch to prevent interference */
|
|
|
|
if (initialize_io (TRUE) == FALSE) /* set up the I/O table; if there's a select code conflict */
|
|
return SCPE_STOP; /* then inhibit execution */
|
|
|
|
mp_is_present = mp_initialize (); /* set up memory protect */
|
|
|
|
exec_save = 0; /* clear the EXEC match */
|
|
idle_save = 0; /* and idle match trace flags */
|
|
|
|
cpu_ioerr_uptr = NULL; /* clear the I/O error unit pointer */
|
|
|
|
|
|
/* Microcode abort processor */
|
|
|
|
abort_reason = (MICRO_ABORT) setjmp (abort_environment); /* set the microcode abort handler */
|
|
|
|
switch (abort_reason) { /* dispatch on the abort reason */
|
|
|
|
case Memory_Protect: /* a memory protect abort */
|
|
status = SCPE_OK; /* continues execution with FLG 5 asserted */
|
|
break;
|
|
|
|
|
|
case Interrupt: /* an interrupt in an indirect chain */
|
|
PR = err_PR; /* backs out of the instruction */
|
|
status = SCPE_OK; /* and then continues execution to service the interrupt */
|
|
break;
|
|
|
|
|
|
case Indirect_Loop: /* an indirect chain length exceeding the limit */
|
|
status = STOP_INDIR; /* causes a simulator stop */
|
|
break;
|
|
|
|
|
|
default: /* anything else */
|
|
status = SCPE_OK; /* continues execution */
|
|
break;
|
|
}
|
|
|
|
exec_test = FALSE; /* clear the execution test flag */
|
|
|
|
|
|
/* Instruction execution loop */
|
|
|
|
while (status == SCPE_OK) { /* execute until simulator status prevents continuation */
|
|
|
|
err_PR = PR; /* save P for error recovery */
|
|
|
|
if (sim_interval <= 0) { /* if an event is pending */
|
|
status = sim_process_event (); /* then call the event service */
|
|
|
|
if (status != SCPE_OK) /* if the service failed */
|
|
break; /* then stop execution */
|
|
}
|
|
|
|
if (dma_request_set) { /* if a DMA service request is pending */
|
|
dma_service (); /* then service the active channel(s) */
|
|
|
|
if (dma_request_set) /* if a DMA request is still pending */
|
|
continue; /* then service it before instruction execution */
|
|
}
|
|
|
|
if (interrupt_request /* if an interrupt request is pending */
|
|
&& (cpu_interrupt_enable || reenable_interrupts ())) { /* and is enabled or reenabled */
|
|
if (sim_brk_summ /* then if any breakpoints are defined */
|
|
&& sim_brk_test (interrupt_request, /* and an unconditional breakpoint */
|
|
SWMASK ('E') /* or a breakpoint matching */
|
|
| meu_breakpoint_type (TRUE))) { /* the current MEM map is set */
|
|
status = STOP_BRKPNT; /* then stop simulation */
|
|
break;
|
|
}
|
|
|
|
CIR = (HP_WORD) interrupt_request; /* set the CIR to the select code of the interrupting device */
|
|
interrupt_request = 0; /* and then clear the request */
|
|
|
|
cpu_interrupt_enable = CLEAR; /* inhibit interrupts */
|
|
interrupt_acknowledge = TRUE; /* while in an interrupt acknowledge cycle */
|
|
|
|
if (idle_save != 0) { /* if idle loop tracing is suppressed */
|
|
cpu_dev.dctrl = idle_save; /* then restore the saved trace flag set */
|
|
idle_save = 0; /* and indicate that we are out of the idle loop */
|
|
}
|
|
|
|
meu_assert_IAK (); /* assert IAK to the MEM to switch to the system map */
|
|
|
|
tprintf (cpu_dev, TRACE_INSTR, DMS_FORMAT "interrupt\n",
|
|
meu_indicator, meu_page, PR, CIR);
|
|
|
|
io_assert (iot [CIR].devptr, ioa_IAK); /* acknowledge the interrupt */
|
|
|
|
if (CIR != MPPE) /* if the MP is not interrupting */
|
|
io_assert (iot [MPPE].devptr, ioa_IAK); /* then notify MP of the IAK too */
|
|
|
|
IR = ReadF (CIR); /* fetch the trap cell instruction */
|
|
}
|
|
|
|
else { /* otherwise this is a normal instruction execution */
|
|
interrupt_acknowledge = FALSE; /* so clear the interrupt acknowledgement status */
|
|
|
|
if (sim_brk_summ /* if any breakpoints are defined */
|
|
&& sim_brk_test (PR, SWMASK ('E') /* and an unconditional breakpoint or a */
|
|
| meu_breakpoint_type (FALSE))) { /* breakpoint matching the current map is set */
|
|
status = STOP_BRKPNT; /* then stop simulation */
|
|
break;
|
|
}
|
|
|
|
IR = ReadF (PR); /* fetch the instruction */
|
|
PR = PR + 1 & LA_MASK; /* and point at the next memory location */
|
|
|
|
cpu_interrupt_enable = SET; /* enable interrupts */
|
|
}
|
|
|
|
|
|
if (TRACING (cpu_dev, TRACE_EXEC | TRACE_REG)) { /* if execution or register tracing is enabled */
|
|
if (cpu_dev.dctrl & TRACE_EXEC) /* then if tracing execution */
|
|
exec_test = (IR & exec_mask) == exec_match; /* then the execution test succeeds if */
|
|
/* the next instruction matches the test criteria */
|
|
|
|
if (cpu_dev.dctrl & TRACE_EXEC /* if execution tracing is enabled */
|
|
&& exec_save == 0 /* and is currently inactive */
|
|
&& exec_test) { /* and the matching test succeeds */
|
|
exec_save = cpu_dev.dctrl; /* then save the current trace flag set */
|
|
cpu_dev.dctrl |= TRACE_ALL; /* and turn on full tracing */
|
|
}
|
|
|
|
if (cpu_dev.dctrl & TRACE_REG) /* if register tracing is enabled */
|
|
mem_trace_registers (interrupt_system); /* then output the working and MP/MEM registers */
|
|
|
|
if (cpu_dev.dctrl & TRACE_EXEC /* if execution tracing is enabled */
|
|
&& exec_save != 0 /* and is currently active */
|
|
&& ! exec_test) { /* and the matching test fails */
|
|
cpu_dev.dctrl = exec_save; /* then restore the saved debug flag set */
|
|
exec_save = 0; /* and indicate that tracing is disabled */
|
|
|
|
hp_trace (&cpu_dev, TRACE_EXEC, EXEC_FORMAT "\n"); /* add a separator to the trace log */
|
|
}
|
|
}
|
|
|
|
if (TRACING (cpu_dev, TRACE_INSTR)) { /* if instruction tracing is enabled */
|
|
hp_trace (&cpu_dev, TRACE_INSTR, /* then output the address and opcode */
|
|
DMS_FORMAT,
|
|
meu_indicator, meu_page,
|
|
MR, IR);
|
|
|
|
sim_eval [0] = IR; /* save the (first) instruction word in the eval array */
|
|
|
|
if (fprint_cpu (sim_deb, MR, sim_eval, 0, CPU_Trace) > SCPE_OK) /* print the mnemonic; if that fails */
|
|
fprint_val (sim_deb, sim_eval [0], cpu_dev.dradix, /* then print the numeric */
|
|
cpu_dev.dwidth, PV_RZRO); /* value again */
|
|
|
|
fputc ('\n', sim_deb); /* end the trace with a newline */
|
|
}
|
|
|
|
|
|
sim_interval = sim_interval - 1; /* count the instruction */
|
|
|
|
status = machine_instruction (interrupt_acknowledge, /* execute one machine instruction */
|
|
&idle_save);
|
|
|
|
if (status == NOTE_INDINT) { /* if an interrupt was recognized while resolving indirects */
|
|
PR = err_PR; /* then back out of the instruction */
|
|
status = SCPE_OK; /* to service the interrupt */
|
|
}
|
|
|
|
cpu_ss_inhibit = status; /* clear the simulation stop inhibition mask */
|
|
} /* and continue the instruction execution loop */
|
|
|
|
|
|
/* Instruction postlude */
|
|
|
|
if (interrupt_request /* if an interrupt request is pending */
|
|
&& (cpu_interrupt_enable || reenable_interrupts ())) /* and is enabled or reenabled */
|
|
cpu_pending_interrupt = interrupt_request; /* then report the pending interrupt select code */
|
|
else /* otherwise */
|
|
cpu_pending_interrupt = 0; /* report that no interrupt is pending */
|
|
|
|
if (exec_save != 0) { /* if EXEC tracing is active */
|
|
cpu_dev.dctrl = exec_save; /* then restore the saved trace flag set */
|
|
hp_trace (&cpu_dev, TRACE_EXEC, EXEC_FORMAT "\n"); /* and add a separator to the trace log */
|
|
}
|
|
|
|
else if (idle_save != 0) /* otherwise if idle tracing is suppressed */
|
|
cpu_dev.dctrl = idle_save; /* then restore the saved trace flag set */
|
|
|
|
saved_MR = MR; /* save the current M value to detect a user change */
|
|
|
|
if (status == STOP_HALT) /* if this is a programmed halt */
|
|
set_loader (NULL, FALSE, NULL, NULL); /* then disable the 21xx loader */
|
|
|
|
else if (status <= STOP_RERUN) /* otherwise if this is a simulation stop */
|
|
PR = err_PR; /* then restore P to reexecute the instruction */
|
|
|
|
meu_update_status (); /* update the MEM status register */
|
|
meu_update_violation (); /* and the violation register */
|
|
|
|
pcq_r->qptr = pcq_p; /* update the PC queue pointer */
|
|
|
|
sim_brk_dflt = meu_breakpoint_type (FALSE); /* base the default breakpoint type on the current MEM state */
|
|
|
|
tprintf (cpu_dev, cpu_dev.dctrl,
|
|
DMS_FORMAT "simulation stop: %s\n",
|
|
meu_indicator, meu_page, MR, TR,
|
|
status >= SCPE_BASE ? sim_error_text (status)
|
|
: sim_stop_messages [status]);
|
|
|
|
return status; /* return the status code */
|
|
}
|
|
|
|
|
|
/* VM command post-processor.
|
|
|
|
This routine is called from SCP after every command before returning to the
|
|
command prompt. We use it to update the T (memory data) register whenever
|
|
the M (memory address) register is changed to follow the 21xx/1000 CPU
|
|
hardware action.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. The T register must be changed only when M has changed. Otherwise, if T
|
|
is updated after every command, then T will be set to zero if M points
|
|
into the protected loader area of the 21xx machines, e.g., after a HLT
|
|
instruction in the loader reenables loader protection.
|
|
*/
|
|
|
|
void cpu_post_cmd (t_bool from_scp)
|
|
{
|
|
if (MR != saved_MR) { /* if M has changed since the last update */
|
|
saved_MR = MR; /* then save the new M value */
|
|
TR = mem_fast_read (MR, Current_Map); /* and set T to the contents of the addressed location */
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
|
|
/* CPU global utility routines */
|
|
|
|
|
|
/* Execute an I/O instruction.
|
|
|
|
This routine executes the I/O Group instruction that is passed in the
|
|
"instruction" parameter. The instruction is examined to obtain the I/O
|
|
function desired. A memory protect check is then made to determine if it is
|
|
legal to execute the instruction. If it is, the state of the interrupt
|
|
enable flip-flop is set, the set of I/O backplane signals generated by the
|
|
instruction are picked up from the "control_set" array, and the signals and
|
|
inbound data value are dispatched to the device interface indicated by the
|
|
select code contained in the instruction. The data value and signals
|
|
returned from the interface are used as directed by the I/O operation, and
|
|
the status of the operation is returned.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. The STC and CLC instructions share the same pattern in bits 9-6 that are
|
|
used to decode the rest of the IOG instructions. These instructions are
|
|
differentiated by the A/B selector (bit 11).
|
|
|
|
2. If a memory protect violation occurs, the IOG signal from the CPU to the
|
|
I/O backplane is denied to disable all I/O signals. For a LIA/B or MIA/B
|
|
instruction, this will load or merge the value of the floating I/O bus
|
|
into the A or B registers. This value is zero on all machines. Merging
|
|
zero doesn't change the register value, so the only the LIA/B case must
|
|
be explicitly checked.
|
|
*/
|
|
|
|
t_stat cpu_iog (HP_WORD instruction)
|
|
{
|
|
const uint32 select_code = instruction & SC_MASK; /* device select code */
|
|
const uint32 ab_selector = AB_SELECT (instruction); /* A/B register selector */
|
|
IO_GROUP_OP micro_op;
|
|
HP_WORD inbound_value;
|
|
INBOUND_SET inbound_signals;
|
|
SKPF_DATA outbound;
|
|
|
|
if ((instruction & IR_CLC_MASK) == IR_CLC) /* if the instruction is CLC or CLC,C */
|
|
if (instruction & IR_HCF) /* then if the H/C flag bit is set */
|
|
micro_op = iog_CLC_C; /* then it's a CLC,C operation */
|
|
else /* otherwise */
|
|
micro_op = iog_CLC; /* it's a CLC operation */
|
|
else /* otherwise */
|
|
micro_op = IOG_OP (instruction); /* the operation is decoded directly */
|
|
|
|
if (micro_op == iog_LIx || micro_op == iog_LIx_C) /* if executing an LIA or LIB instruction */
|
|
ABREG [ab_selector] = 0; /* then clear the register value in case MP aborts */
|
|
|
|
mp_check_io (select_code, micro_op); /* check for a memory protect violation */
|
|
|
|
cpu_interrupt_enable = enable_map [is_1000] [micro_op]; /* disable interrupts depending on the instruction */
|
|
|
|
inbound_signals = control_set [micro_op]; /* get the set of signals to assert to the interface */
|
|
|
|
if (micro_op == iog_OTx || micro_op == iog_OTx_C) /* if the instruction is OTA/B or OTA/B,C */
|
|
inbound_value = ABREG [ab_selector]; /* then send the register value to the interface */
|
|
else /* otherwise */
|
|
inbound_value = 0; /* the interface won't use the inbound value */
|
|
|
|
outbound = io_dispatch (select_code, inbound_signals, /* dispatch the I/O action to the interface */
|
|
inbound_value);
|
|
|
|
if (micro_op == iog_LIx || micro_op == iog_LIx_C) /* if the instruction is LIA/B or LIA/B,C */
|
|
ABREG [ab_selector] = outbound.data; /* then store the I/O bus data into the register */
|
|
|
|
else if (micro_op == iog_MIx || micro_op == iog_MIx_C) /* otherwise if the instruction is MIA/B or MIA/B,C */
|
|
ABREG [ab_selector] |= outbound.data; /* then merge the I/O bus data into the register */
|
|
|
|
else if (outbound.skip) { /* otherwise if the interface asserted SKF */
|
|
PR = PR + 1 & R_MASK; /* then bump P to skip then next instruction */
|
|
return SCPE_OK; /* and return success */
|
|
}
|
|
|
|
else if (micro_op == iog_HLT || micro_op == iog_HLT_C) /* otherwise if the instruction is HLT or HLT,C */
|
|
return STOP_HALT; /* then stop the simulator */
|
|
|
|
if (iot [select_code].devptr == NULL) /* if the I/O slot is empty */
|
|
return STOP (cpu_ss_unsc); /* then return stop status if enabled */
|
|
else /* otherwise */
|
|
return SCPE_OK; /* the instruction executed successfully */
|
|
}
|
|
|
|
|
|
/* Resolve an indirect address.
|
|
|
|
This routine resolves a possibly indirect memory address into a direct
|
|
address by following an indirect chain, if any. On entry, the M register
|
|
contains the address to resolve, and the "interruptible" parameter is set to
|
|
TRUE if the instruction is interruptible or FALSE if it is not. On exit, the
|
|
M register contains the direct address, and SCPE_OK is returned. If an
|
|
interrupt is pending and permitted, NOTE_INDINT is returned to abort the
|
|
instruction. If the indirect chain length is greater than the chain limit,
|
|
STOP_INDIR is returned to abort execution. In both abort cases, the M
|
|
register content is undefined.
|
|
|
|
Logical memory addresses are 15 bits wide, providing direct access to a 32K
|
|
logical address space. Addresses may also be indirect, with bit 15 (the MSB)
|
|
serving as the direct/indirect indicator. An indirect address may point at
|
|
either a direct or indirect address. In the latter case, the chain is
|
|
followed until a direct address is obtained.
|
|
|
|
Indirect addressing has implications for interrupt handling. Normally,
|
|
interrupts are checked at each level of indirection, and if one is pending,
|
|
the CPU will abort execution of the instruction and then service the
|
|
interrupt. On return from the interrupt handler, the instruction will be
|
|
restarted.
|
|
|
|
Detection of interrupts is dependent on the Interrupt Enable flip-flop being
|
|
set. Certain instructions, such as JMP indirect, JSB indirect, and most IOG
|
|
instructions, clear the enable flag to hold off interrupts until the current
|
|
and following instructions complete, including complete resolution of the
|
|
indirect chain. If the chain is unresolvable (i.e., it points to itself, as
|
|
in the instruction sequence JMP *+1,I and DEF *,I), then interrupts are held
|
|
off forever.
|
|
|
|
To prevent a user program from freezing a protected OS with an infinite
|
|
indirect chain, and to permit real-time interrupts to be handled while
|
|
resolving a long indirect chain, the Memory Protect accessory counts indirect
|
|
levels during address resolution and will reenable interrupt recognition
|
|
after the third level. Operating systems that run without MP installed are
|
|
subject to freezing as above, but those employing MP will be able to regain
|
|
control from an infinite indirect chain.
|
|
|
|
In simulation, the SET CPU INDIR=<limit> command sets the maximum number of
|
|
levels; the default is 16. If the level is exceeded during address
|
|
resolution, the simulator will stop. The maximum limit is 32768, which is
|
|
the maximum possible address chain without an infinite loop, but an indirect
|
|
chain over a few levels deep almost certainly represents a programming error.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. Virtually all valid indirect references are one level deep, so we
|
|
optimize for this case. Also, we protect against entry with a direct
|
|
address by simply returning the address, but the overhead can be saved by
|
|
calling this routine only for indirect addresses.
|
|
|
|
2. The 12892B Memory Protect accessory jumper W6 ("INT") controls whether
|
|
pending interrupts with the Interrupt Enable flip-flop clear are serviced
|
|
immediately (jumper removed) or after three levels of indirection (jumper
|
|
installed). If the jumper is removed, MP must be enabled (control
|
|
flip-flop set) for the interrupt disable to be overridden.
|
|
|
|
The jumper state need not be checked here, however, because this routine
|
|
can be entered with an interrupt pending, i.e., "interrupt_request" is
|
|
non-zero, only if "cpu_interrupt_enable" and "mp_reenable_interrupts" are
|
|
both false. If either is true, the pending interrupt would have been
|
|
serviced before calling the instruction executor that caleld this routine
|
|
to resolve its address. For "mp_reenable_interrupts" to return false,
|
|
the INT jumper must be installed or the MP control flip-flop must be
|
|
clear.
|
|
|
|
3. When employing the indirect counter, the hardware clears a pending
|
|
interrupt deferral after the third level of indirection and aborts the
|
|
instruction after the fourth.
|
|
|
|
4. The JRS, DJP, DJS, SJP, SJS, UJP, and UJS instructions also hold off
|
|
interrupts for three indirect levels, but they count levels internally
|
|
and do not depend on the presence of the MP accessory to reenable
|
|
interrupt recognition. However, DMS requires MP, so simulation uses the
|
|
MP indirect counter for these instructions as well.
|
|
|
|
5. In hardware, it is possible to execute an instruction with an infinite
|
|
indirect loop (e.g., JMP *+1,I and DEF *,I). If MP is not installed,
|
|
this freezes the CPU with interrupts disabled until HALT is pressed. In
|
|
simulation, the instruction executes until the indirect limit is reached,
|
|
whereupon the simulator stops with "Indirect address loop" status.
|
|
Modelling the hardware CPU freeze would be difficult, as the simulation
|
|
console would have to be polled locally to watch for CTRL+E (the
|
|
simulation equivalent of the CPU front panel HALT button).
|
|
|
|
6. In hardware, all instructions that resolve indirects are interruptible.
|
|
In simulation, some instruction executors are not written to handle an
|
|
instruction abort (e.g., the P register is not backed up properly to
|
|
rerun the instruction); these will pass FALSE for the "interruptible"
|
|
parameter.
|
|
*/
|
|
|
|
t_stat cpu_resolve_indirects (t_bool interruptible)
|
|
{
|
|
uint32 level;
|
|
t_bool pending;
|
|
|
|
if (MR & IR_IND) { /* if the address is indirect */
|
|
MR = ReadW (MR & LA_MASK); /* then follow the chain (first level) */
|
|
|
|
if (MR & IR_IND) { /* if the address is still indirect */
|
|
pending = interruptible && interrupt_request; /* then see if an interrupt request is pending */
|
|
|
|
if (pending && cpu_interrupt_enable) /* if it's pending and enabled */
|
|
return NOTE_INDINT; /* then service the interrupt now */
|
|
else /* otherwise */
|
|
pending = pending && mp_is_present; /* a pending interrupt is recognized only if MP is present */
|
|
|
|
for (level = 2; MR & IR_IND; level++) { /* follow the chain from level 2 until the address is direct */
|
|
if (level > indirect_limit) /* if the limit is exceeded */
|
|
return STOP_INDIR; /* then stop the simulator */
|
|
|
|
else if (pending) /* otherwise if an interrupt is pending */
|
|
if (level == 3) /* then if this is the third level */
|
|
cpu_interrupt_enable = SET; /* then reenable interrupts */
|
|
else if (level == 4) /* otherwise if this is the fourth level */
|
|
return NOTE_INDINT; /* then service the interrupt now */
|
|
|
|
MR = ReadW (MR & LA_MASK); /* follow the address chain */
|
|
}
|
|
}
|
|
}
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
|
|
/* Abort an instruction.
|
|
|
|
This routine performs a microcode abort for the reason passed in the
|
|
"abort_reason" parameter. In hardware, microcode aborts are implemented by
|
|
direct jumps to the FETCH label in the base set (microcode address 0). This
|
|
is typically done when an interrupt is detected while executing a lengthy
|
|
instruction or during resolution of an indirect chain. In simulation, this
|
|
is performed by a "longjmp" back to the start of the instruction execution
|
|
loop. It is the caller's responsibility to restore the machine state, e.g.,
|
|
to back up the P register to rerun the instruction.
|
|
*/
|
|
|
|
void cpu_microcode_abort (MICRO_ABORT abort_reason)
|
|
{
|
|
longjmp (abort_environment, (int) abort_reason); /* jump back to the instruction execution loop */
|
|
}
|
|
|
|
|
|
/* Install a bootstrap loader into memory.
|
|
|
|
This routine copies the bootstrap loader specified by "boot" into the last 64
|
|
words of main memory, limited by a 32K memory size. If "sc" contains the
|
|
select code of an I/O interface (i.e., select code 10 or above), this routine
|
|
will configure the I/O instructions in the loader to the supplied select
|
|
code. On exit, the P register will be set to point at the loader starting
|
|
program address, and the S register will be altered as directed by the
|
|
"sr_clear" and "sr_set" masks if the current CPU is a 1000. The updated P
|
|
register value is returned to the caller.
|
|
|
|
The currently configured CPU family (21xx or 1000) determines which of two
|
|
BOOT_LOADER structures is accessed from the "boot" array. Each structure
|
|
contains the 64-word loader array and three indicies into the loader
|
|
array that specify the start of program execution, the element containing the
|
|
DMA control word, and the element containing the (negative) address of the
|
|
first loader word in memory.
|
|
|
|
21xx-series loaders consist of subsections handling one or two devices. A
|
|
two-part loader is indicated by a starting program index other than 0, i.e.,
|
|
other than the beginning of the loader. An example is the Basic Moving-Head
|
|
Disc Loader (BMDL), which consists of a paper tape loader section starting at
|
|
index 0 and a disc loader section starting at index 50 octal. For these
|
|
loaders, I/O configuration depends on the "start_index" field of the selected
|
|
BOOTSTRAP structure: I/O instructions before the starting index are
|
|
configured to the current paper-tape reader select code, and instructions at
|
|
or after the starting index are configured to the device select code
|
|
specified by "sc". Single-part loaders specify a starting index of 0, and
|
|
all I/O instructions are configured to the "sc" select code.
|
|
|
|
1000-series loaders are always single part and always start at index 0, so
|
|
they are always configured to use the "sc" select code.
|
|
|
|
If a given device does not have both a 21xx-series and a 1000-series loader,
|
|
the "start_index" field of the undefined loader will be set to the "IBL_NA"
|
|
value. If this routine is called to copy an undefined loader, it will reject
|
|
the call by returning a starting address of zero to the caller. In this
|
|
case, neither P nor S are changed.
|
|
|
|
If I/O configuration is requested, each instruction in the loader array is
|
|
examined as it is copied. If the instruction is a non-HLT I/O instruction
|
|
referencing a select code >= 10, the select code will be reset by subtracting
|
|
10 and adding the value of the select code supplied by the "sc" parameter (or
|
|
the paper-tape reader select code, as above). This permits configuration of
|
|
loaders that address two- or three-card interfaces. Passing an "sc" value of
|
|
0 will inhibit configuration, and the loader array will be copied verbatim.
|
|
|
|
As an example, passing an "sc" value of 24 octal will alter these I/O-group
|
|
instructions as follows:
|
|
|
|
Loader Configured
|
|
Instruction Instruction Note
|
|
----------- ----------- ------------------------------
|
|
OTA 10 OTA 24 Normal configuration
|
|
LIA 11 LIA 25 Second card configuration
|
|
STC 6 STC 6 DCPC configuration not changed
|
|
HLT 11 HLT 11 Halt instruction not changed
|
|
|
|
If configuration is performed, two additional operations may be performed.
|
|
First, the routine will alter the word at the index specified by the
|
|
"dma_index" field of the selected BOOTSTRAP structure unconditionally as
|
|
above. This word is assumed to contain a DMA control word; it is configured
|
|
to reference the supplied select code. Second, it will set the word at the
|
|
index specified by the "fwa_index" field to the two's-complement of the
|
|
starting address of the loader in memory. This value may be used by the
|
|
loader to check that it will not be overwritten by loaded data.
|
|
|
|
If either field is set to the IBL_NA value, then the corresponding
|
|
modification is not made. For example, the 21xx Basic Binary Loader (BBL)
|
|
does not use DMA, so its "dma_index" field is set to IBL_NA, and so no DMA
|
|
control word modification is done.
|
|
|
|
The starting address for loader execution is derived from the "start_index"
|
|
field and the starting memory address to which the loader is copied.
|
|
|
|
Finally, if the current CPU is a 1000-series machine, the S register bits
|
|
corresponding to those set in the "sr_clear" value are masked off, and the
|
|
bits corresponding to those in the "sr_set" value are set. In addition, the
|
|
select code from the "sc" value is shifted left and ORed into the value.
|
|
This action presets the S-register to the correct value for the selected
|
|
loader.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. The paper-tape reader's select code is determined on each entry to the
|
|
routine to accommodate select code reassignment by the user.
|
|
*/
|
|
|
|
uint32 cpu_copy_loader (const LOADER_ARRAY boot, uint32 sc, HP_WORD sr_clear, HP_WORD sr_set)
|
|
{
|
|
uint32 index, loader_start, ptr_sc;
|
|
MEMORY_WORD loader [IBL_SIZE];
|
|
MEMORY_WORD word;
|
|
DEVICE *ptr_dptr;
|
|
|
|
if (boot [is_1000].start_index == IBL_NA) /* if the bootstrap is not defined for the current CPU */
|
|
return 0; /* then reject the command */
|
|
|
|
else if (boot [is_1000].start_index > 0 && sc > 0) { /* if this is a two-part loader with I/O reconfiguration */
|
|
ptr_dptr = find_dev ("PTR"); /* then get a pointer to the paper tape reader device */
|
|
|
|
if (ptr_dptr == NULL) /* if the PTR device is not present */
|
|
return 0; /* then something is seriously wrong */
|
|
else /* otherwise */
|
|
ptr_sc = ((DIB *) ptr_dptr->ctxt)->select_code; /* get the select code from the device's DIB */
|
|
}
|
|
|
|
else /* otherwise this is a single-part loader */
|
|
ptr_sc = 0; /* or I/O reconfiguration is not requested */
|
|
|
|
loader_start = mem_size - 1 & ~IBL_MASK & LA_MASK; /* get the base memory address of the loader */
|
|
PR = loader_start + boot [is_1000].start_index & R_MASK; /* and store the starting program address in P */
|
|
|
|
set_loader (NULL, TRUE, NULL, NULL); /* enable the loader (ignore errors if not 21xx) */
|
|
|
|
for (index = 0; index < IBL_SIZE; index++) { /* copy the bootstrap loader to memory */
|
|
word = boot [is_1000].loader [index]; /* get the next word */
|
|
|
|
if (sc == 0) /* if reconfiguration is not requested */
|
|
loader [index] = word; /* then copy the instruction verbatim */
|
|
|
|
else if (IOGOP (word) /* otherwise if this is an I/O instruction */
|
|
&& (word & SC_MASK) >= SC_VAR /* and the referenced select code is >= 10B */
|
|
&& (word & IR_HLT_MASK) != IR_HLT) /* and it's not a halt instruction */
|
|
if (index < boot [is_1000].start_index) /* then if this is a split loader */
|
|
loader [index] = word + (ptr_sc - SC_VAR) & DV_MASK; /* then reconfigure the paper tape reader */
|
|
else /* otherwise */
|
|
loader [index] = word + (sc - SC_VAR) & DV_MASK; /* reconfigure the target device */
|
|
|
|
else if (index == boot [is_1000].dma_index) /* otherwise if this is the DMA configuration word */
|
|
loader [index] = word + (sc - SC_VAR) & DV_MASK; /* then reconfigure the target device */
|
|
|
|
else if (index == boot [is_1000].fwa_index) /* otherwise if this is the starting address word */
|
|
loader [index] = NEG16 (loader_start); /* then set the negative starting address of the bootstrap */
|
|
|
|
else /* otherwise the word is not a special one */
|
|
loader [index] = word; /* so simply copy it */
|
|
}
|
|
|
|
mem_copy_loader (loader, loader_start, To_Memory); /* copy the loader to memory */
|
|
|
|
if (cpu_configuration & CPU_1000) /* if the CPU is a 1000 */
|
|
SR = SR & sr_clear | sr_set | IBL_TO_SC (sc); /* then modify the S register as indicated */
|
|
|
|
return PR; /* return the starting execution address of the loader */
|
|
}
|
|
|
|
|
|
/* Check for an I/O stop condition.
|
|
|
|
Entering the SET CPU STOP=IOERR command at the SCP command prompt stops the
|
|
simulator if an I/O error occurs on a device that does not return error
|
|
status to the CPU. For example, while the paper tape punch returns low- or
|
|
out-of-tape status, the paper tape reader gives no indication that a tape is
|
|
loaded. If the reader is commanded to read when no tape is mounted, the
|
|
interface hangs while waiting for the handshake with the device to complete.
|
|
|
|
In hardware, the CPU can detect this condition only by timing the operation
|
|
and concluding that the tape is missing if the timeout is exceeded. However,
|
|
if an IOERR stop has been set, then the simulator will stop with an error
|
|
message to permit the condition to be fixed. For instance, attempting to
|
|
read from the paper tape reader with no paper tape image file attached will
|
|
print "No tape loaded in the PTR device" and will stop the simulator.
|
|
|
|
Such devices will call this routine and pass a pointer to the unit that
|
|
encountered the error condition. The routine returns TRUE and saves the
|
|
pointer to the failing unit if the IOERR stop is enabled, and FALSE
|
|
otherwise.
|
|
*/
|
|
|
|
t_bool cpu_io_stop (UNIT *uptr)
|
|
{
|
|
if (cpu_ss_ioerr != SCPE_OK) { /* if the I/O error stop is enabled */
|
|
cpu_ioerr_uptr = uptr; /* then save the failing unit */
|
|
return TRUE; /* and return TRUE to indicate that the stop is enabled */
|
|
}
|
|
|
|
else /* otherwise */
|
|
return FALSE; /* return FALSE to indicate that the stop is disabled */
|
|
}
|
|
|
|
|
|
|
|
/* I/O subsystem global utility routines */
|
|
|
|
|
|
/* Device I/O signal dispatcher.
|
|
|
|
This routine calls the I/O interface handler of the device corresponding to
|
|
the supplied "select_code" value, passing the "inbound_signals" and
|
|
"inbound_value" to the interface. The combined skip status and outbound data
|
|
value from the handler is returned to the caller.
|
|
|
|
The 21xx/1000 I/O structure requires that no empty slots exist between
|
|
interface cards. This is due to the hardware priority chaining (PRH/PRL)
|
|
that is passed from card-to-card. If it is necessary to leave unused I/O
|
|
slots, HP 12777A Priority Jumper Cards must be installed in them to maintain
|
|
priority continuity.
|
|
|
|
Under simulation, every unassigned I/O slot behaves as though a 12777A were
|
|
resident. In this configuration, I/O instructions addressed to one of these
|
|
slots read the floating bus for LIA/B and MIA/B instructions or do nothing
|
|
for all other instructions.
|
|
|
|
If the slot is occupied, then the routine first determines the rank (0 or 1)
|
|
and bit (0 to 31) corresponding to the select code of the interface. These
|
|
will be used to access the interrupt, priority, and service request bit
|
|
vectors. Then it augments the supplied inbound signals set with IEN if the
|
|
interrupt system flip-flop is set, and PRH if no higher priority interface is
|
|
denying PRL.
|
|
|
|
In hardware, PRH of each interface is connected to PRL of the next higher
|
|
priority (lower select code) interface, so that a PRH-to-PRL chain is formed.
|
|
An interface receives PRH if every higher-priority interface is asserting
|
|
PRL. When an interface denies PRL, each lower-priority interface has PRH
|
|
denied and so denies PRL to the next interface in the chain.
|
|
|
|
To avoid checking calling each device simulator's higher-priority interface
|
|
routine to ascertain its PRL status, the priority holdoff bit vector is
|
|
checked. This vector has a bit set for each interface currently denying PRL.
|
|
The check is made as in the following example:
|
|
|
|
sc bit : ...0 0 1 0 0 0 0 0 0 0 0 0 (dispatching to SC 11)
|
|
sc bit - 1 : ...0 0 0 1 1 1 1 1 1 1 1 1 (PRH required thru SC 10)
|
|
pri holdoff : ...0 0 1 0 0 1 0 0 0 0 0 0 (PRL denied for SC 06 and 11)
|
|
ANDed value : ...0 0 0 0 0 1 0 0 0 0 0 0 (PRL blocked at SC 06)
|
|
|
|
If the ANDed value is 0, then there are no higher priority devices in this
|
|
rank that are denying PRL. If the rank is 0, then PRH is asserted to the
|
|
current select code. If the rank is 1, then PRH is asserted only if all rank
|
|
0 priority holdoff bits are zero, i.e., if no rank 0 interfaces are denying
|
|
PRL.
|
|
|
|
The device interface handler is obtained from the "dibs" array, and the
|
|
interface is called to process the inbound signals.
|
|
|
|
On return, the outbound signals from the interface are examined. The
|
|
interrupt vector bit is set to the state of the IRQ signal, and the priority
|
|
vector bit is set to the state of the PRL signal.
|
|
|
|
If the IRQ signal is present, then the interface is the highest priority
|
|
device requesting an interrupt (otherwise the interface would not have
|
|
received PRH, which is a term in asserting IRQ), so the select code of the
|
|
interrupting device is set. Otherwise, if the interface asserted its PRL
|
|
condition from a prior denied state, then lower priority devices are checked
|
|
to see if any now have a valid interrupt request.
|
|
|
|
If the SRQ signal is asserted, a DMA request is made; if the device is under
|
|
DMA control, a DMA cycle will be initiated at the start of the next pass
|
|
through the instruction execution loop. Finally, the skip condition is set
|
|
if the SKF signal is asserted, and the skip state and outbound value from the
|
|
interface are returned to the caller.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. For select codes < 10 octal, an IOI signal reads the floating S-bus
|
|
(high on the 1000, low on the 21xx). For select codes >= 10 octal, an
|
|
IOI reads the floating I/O bus (low on all machines).
|
|
|
|
2. The last select code used is saved for use by the CPU I/O handler in
|
|
detecting consecutive CLC 0 executions.
|
|
|
|
3. The IRQ and FLG signals always assert together on HP 21xx/1000 machines.
|
|
They are physically tied together on all interface cards, and the CPUs
|
|
depend on their concurrent assertion for correct operation. In
|
|
simulation, we check only IRQ from the interface, although FLG will be
|
|
asserted as well.
|
|
|
|
4. The IRQ and SRQ signals usually assert together, which means that an
|
|
interrupt is pending before the DMA request is serviced. However, DMA
|
|
cycles always assert CLF, which clears the interrupt request before
|
|
interrupts are checked. In hardware, an SRQ to an active channel asserts
|
|
DMALO (DMA lock out), which inhibits IRQ from setting the interrupt
|
|
request flip-flop until the DMA cycle completes. In simulation, IRQ is
|
|
never asserted because the CLF is processed by the interface simulator
|
|
before IRQ is determined.
|
|
|
|
5. An interface will assert "cnVALID" if the conditional PRL and IRQ were
|
|
determined. If "cnVALID" is not asserted by the interface, then the
|
|
states of the "cnPRL" and "cnIRQ" signals cannot be inferred from their
|
|
presence or absence in the outbound signal set.
|
|
|
|
6. The "cnVALID" pseudo-signal is required because although most interfaces
|
|
determine the PRL and IRQ states in response to an SIR assertion, not all
|
|
do. In particular, the 12936A Privileged Interrupt Fence determines PRL
|
|
in response to an IOO signal.
|
|
*/
|
|
|
|
SKPF_DATA io_dispatch (uint32 select_code, INBOUND_SET inbound_signals, HP_WORD inbound_value)
|
|
{
|
|
SKPF_DATA result;
|
|
SIGNALS_VALUE outbound;
|
|
uint32 sc_rank, sc_bit, previous_holdoff;
|
|
|
|
if (iot [select_code].dibptr == NULL) { /* if the I/O slot is empty */
|
|
result.skip = FALSE; /* then SKF cannot be asserted */
|
|
|
|
if (inbound_signals & ioIOI && select_code < SC_VAR /* if this is an input request for an internal device */
|
|
&& cpu_configuration & CPU_1000) /* of a 1000 CPU */
|
|
result.data = D16_UMAX; /* then the empty slot reads as all ones */
|
|
else /* otherwise */
|
|
result.data = 0; /* the empty slot reads as all zeros */
|
|
}
|
|
|
|
else { /* otherwise the slot is occupied */
|
|
sc_rank = select_code / 32; /* so set the rank */
|
|
sc_bit = 1u << select_code % 32; /* and bit of the interface */
|
|
|
|
if (interrupt_system == SET) { /* if the interrupt system is on */
|
|
inbound_signals |= ioIEN; /* then assert IEN to the interface */
|
|
|
|
if ((sc_bit - 1 & priority_holdoff_set [sc_rank]) == 0 /* if no higher priority device */
|
|
&& (sc_rank == 0 || priority_holdoff_set [0] == 0)) /* is denying PRL */
|
|
inbound_signals |= ioPRH; /* then assert PRH to our interface */
|
|
}
|
|
|
|
tpprintf (iot [select_code].devptr, TRACE_IOBUS, "Received data %06o with signals %s\n",
|
|
inbound_value, fmt_bitset (inbound_signals, inbound_format));
|
|
|
|
outbound = /* call the device interface */
|
|
iot [select_code].dibptr->io_interface (iot [select_code].dibptr, /* with the device select code */
|
|
inbound_signals, /* and inbound signal set */
|
|
inbound_value); /* and data value */
|
|
|
|
tpprintf (iot [select_code].devptr, TRACE_IOBUS, "Returned data %06o with signals %s\n",
|
|
outbound.value, fmt_bitset (outbound.signals, outbound_format));
|
|
|
|
last_select_code = select_code; /* save the select code for CLC 0 detection */
|
|
previous_holdoff = priority_holdoff_set [sc_rank]; /* and the current priority holdoff for this rank */
|
|
|
|
if (outbound.signals & cnVALID) { /* if the IRQ and PRL signals are valid */
|
|
if (outbound.signals & cnIRQ) /* then if the conditional interrupt request signal is present */
|
|
interrupt_request_set [sc_rank] |= sc_bit; /* then set the interrupt request bit */
|
|
else /* otherwise */
|
|
interrupt_request_set [sc_rank] &= ~sc_bit; /* clear the request bit */
|
|
|
|
if (outbound.signals & cnPRL) /* if the conditional priority low signal is present */
|
|
priority_holdoff_set [sc_rank] &= ~sc_bit; /* then clear the priority inhibit bit */
|
|
else /* otherwise */
|
|
priority_holdoff_set [sc_rank] |= sc_bit; /* set the inhibit bit */
|
|
}
|
|
|
|
if (outbound.signals & ioIRQ) /* if the interrupt request signal is present */
|
|
interrupt_request = select_code; /* then indicate that the select code is interrupting */
|
|
|
|
else if (previous_holdoff & ~priority_holdoff_set [sc_rank] & sc_bit) /* otherwise if priority is newly asserted */
|
|
interrupt_request = io_poll_interrupts (interrupt_system); /* then check interrupt requests */
|
|
|
|
if (outbound.signals & ioSRQ) /* if SRQ is asserted */
|
|
dma_assert_SRQ (select_code); /* then check if DMA is controlling this interface */
|
|
|
|
result.skip = (outbound.signals & ioSKF) != 0; /* return TRUE if the skip-on-flag signal is present */
|
|
result.data = outbound.value; /* and return the outbound data from the interface */
|
|
}
|
|
|
|
return result; /* return the result of the I/O operation */
|
|
}
|
|
|
|
|
|
/* Execute an I/O control operation.
|
|
|
|
This routine performs an I/O control operation on the interface specified by
|
|
the "select_code" parameter. I/O control operations are all those that do
|
|
not pass data to or from the interface. The routine returns TRUE if the
|
|
interface asserted the SKF signal as a result of the operation and FALSE if
|
|
it did not.
|
|
|
|
Certain microcode extension instructions perform I/O operations as part of
|
|
their execution. In hardware, this is done by setting bits 11-6 of the
|
|
Instruction Register to a code describing the operation and then issuing the
|
|
IOG micro-order to generate the sppropriate backplane signals. In
|
|
simulation, this relatively lightweight routine performs the same action,
|
|
avoiding much of the overhead of the "cpu_iog" routine.
|
|
|
|
The routine performs a memory protect check and then dispatches the operation
|
|
to the interface indicated by the supplied select code.
|
|
*/
|
|
|
|
t_bool io_control (uint32 select_code, IO_GROUP_OP micro_op)
|
|
{
|
|
SKPF_DATA result;
|
|
|
|
mp_check_io (select_code, micro_op); /* check that the I/O operation is permitted */
|
|
|
|
result = io_dispatch (select_code, /* send the signal set */
|
|
control_set [micro_op], 0); /* to the indicated interface */
|
|
|
|
return result.skip; /* return TRUE if the interface asserted SKF */
|
|
}
|
|
|
|
|
|
/* Assert an I/O backplane signal.
|
|
|
|
This routine is called by a device interface simulator to assert a specific
|
|
I/O backplane signal. The supported signal assertions are ENF, SIR, PON,
|
|
POPIO, CRS, and IAK. In hardware, these signals would be asserted by logic
|
|
gates on the interface; in simulation, they are asserted by calls to this
|
|
routine.
|
|
|
|
The operation is dispatched via the I/O access table by the "io_dispatch"
|
|
routine, so we first ensure that the table entry is set correctly. During
|
|
instruction execution, this is redundant, as the table has been set up during
|
|
the execution prelude. However, this routine is also called by each
|
|
interface simulator's device reset routine in response to a RESET ALL or
|
|
RESET <device> SCP command. At that point, the table may not have been
|
|
intitialized or may contain incorrect entries (if the device has been enabled
|
|
or reassigned since the last time the table was set up).
|
|
*/
|
|
|
|
void io_assert (DEVICE *dptr, IO_ASSERTION assertion)
|
|
{
|
|
DIB *dibptr;
|
|
uint32 select_code;
|
|
|
|
if (dptr != NULL && dptr->ctxt != NULL) { /* if the device points to a valid DIB */
|
|
dibptr = (DIB *) dptr->ctxt; /* then get the DIB pointer */
|
|
select_code = dibptr->select_code; /* and associated select code */
|
|
|
|
iot [select_code].devptr = dptr; /* set the current device and DIB pointer assignments */
|
|
iot [select_code].dibptr = dibptr; /* into the table */
|
|
|
|
io_dispatch (select_code, assert_set [assertion], 0); /* assert the signal set to the indicated interface */
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* Poll for a new interrupt request.
|
|
|
|
This routine is called when an interface asserts a previously denied PRL
|
|
signal, i.e., when PRL goes from low to high on the I/O backplane. It
|
|
determines if any interfaces lower in the priority chain are now ready to
|
|
interrupt. If so, the select code of the highest priority interrupting
|
|
interface is returned; otherwise, 0 is returned.
|
|
|
|
In hardware, PRH and PRL form a priority chain that extends from interface to
|
|
interface on the backplane. For an interface to generate an interrupt, PRH
|
|
must be asserted by the next-higher-priority interface. An interface
|
|
receiving PRH asserts IRQ to request an interrupt, and it denies PRL to
|
|
prevent lower-priority devices from interrupting. IRQ is cleared by an
|
|
interrupt acknowledge (IAK) signal. PRL generally remains low while a
|
|
device's interrupt service routine is executing to prevent preemption.
|
|
|
|
IRQ and PRL indicate one of four possible states for a device:
|
|
|
|
IRQ PRL Device state
|
|
--- --- ----------------------
|
|
0 1 Not interrupting
|
|
1 0 Interrupt requested
|
|
0 0 Interrupt acknowledged
|
|
1 1 (not allowed)
|
|
|
|
PRL must be denied when requesting an interrupt (IRQ asserted). This is a
|
|
hardware requirement of the 21xx/1000 series. The IRQ lines from the
|
|
backplane are not priority encoded. Instead, the PRL chain expresses the
|
|
priority by allowing only one IRQ line to be active at a time. This allows a
|
|
simple pull-down encoding of the CIR inputs.
|
|
|
|
When a given interface denies PRL, the PRH-to-PRL chain through all
|
|
lower-priority interfaces also denies. When that interface reasserts PRL,
|
|
the chain reasserts through intervening interfaces until it reaches one that
|
|
is ready to request an interrupt or through all interfaces if none are ready.
|
|
An interface that is ready to interrupt except for a denied PRH will assert
|
|
IRQ when the higher-priority PRH-to-PRL chain reasserts.
|
|
|
|
A direct simulation of this hardware behavior would require polling all
|
|
lower-priority interfaces in order by calling their interface routines,
|
|
asserting PRH to each, and checking each for an asserted IRQ or a denied PRL
|
|
in response. Two avoid this inefficiency, two bit vectors are kept that
|
|
reflect the states of each interface's IRQ and PRL signals conditional on PRH
|
|
being asserted. The "interrupt_request_set" bit vector reflects each
|
|
interface's readiness to interrupt if its associated PRH signal is asserted,
|
|
and the "priority_holdoff_set" reflects the (inverted) state of each
|
|
interface's PRL signal, i.e., a zero-bit indicates that the interface is
|
|
ready to assert PRL if its associated PRH signal is asserted. Each vector is
|
|
represented as a two-element array of 32-bit unsigned integers, forming a
|
|
64-bit vector in which bits 0-31 of the first element correspond to select
|
|
codes 00-37 octal, and bits 0-31 of the second element correspond to select
|
|
codes 40-77 octal.
|
|
|
|
The routine begins by seeing if an interrupt is pending. If not, it returns
|
|
0. Otherwise, the PRH-to-PRL chain is checked to see if PRL is denied
|
|
anywhere upstream of the highest priority interrupting interface. If it is,
|
|
then no interface can interrupt. If all higher-priority PRL signals are
|
|
asserted, then the interrupt request of that interface is granted.
|
|
|
|
Recognition of interrupts depends on the current state of the interrupt
|
|
system and interrupt enable flip-flops, as follows:
|
|
|
|
INTSYS INTEN Interrupts Recognized
|
|
------ ------ ----------------------------
|
|
CLEAR CLEAR Parity error
|
|
CLEAR SET Parity error, power fail
|
|
SET CLEAR Parity error, memory protect
|
|
SET SET All sources
|
|
|
|
Memory protect and parity error share select code 05, but the interrupt
|
|
sources are differentiated on the MP card -- setting the flag buffer for a
|
|
memory protect violation is qualified by the IEN signal that reflects
|
|
the INTSYS state, whereas no qualification is used for a parity error.
|
|
Therefore, an interrupt on select code 05 with the interrupt system off must
|
|
be a parity error interrupt.
|
|
|
|
The end of the priority chain is marked by the highest-priority
|
|
(lowest-order) bit that is set. We calculate a priority mask by ANDing the
|
|
the PRL bits with its two's complement using the IOPRIORITY macro. Only the
|
|
lowest-order bit will differ. For example:
|
|
|
|
pri holdoff : ...0 0 1 0 0 1 0 0 0 0 0 0 (PRL denied for SC 06 and 11)
|
|
one's compl : ...1 1 0 1 1 0 1 1 1 1 1 1
|
|
two's compl : ...1 1 0 1 1 1 0 0 0 0 0 0
|
|
ANDed value : ...0 0 0 0 0 1 0 0 0 0 0 0 (chain is broken at SC 06)
|
|
|
|
The interrupt requests are then ANDed with the priority masks to determine if
|
|
a request is pending:
|
|
|
|
pri mask : ...0 0 0 0 0 1 0 0 0 0 0 0 (allowed interrupt source)
|
|
int request : ...0 0 1 0 0 1 0 0 0 0 0 0 (interfaces asserting IRQ)
|
|
ANDed value : ...0 0 0 0 0 1 0 0 0 0 0 0 (request to grant)
|
|
|
|
The select code corresponding to the granted request is then returned to the
|
|
caller.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. Recognition of an interrupt in the second half of the request set (i.e.,
|
|
for select codes 40-77 octal) occurs only if the priority set for the
|
|
first half is 0, i.e., if the PRH-to-PRL chain is unbroken through select
|
|
code 37.
|
|
|
|
2. If an interrupt for select codes 00-37 is pending but is inhibited, then
|
|
all higher select code interrupts are inhibited as well, so we only need
|
|
to check requests for the lower half or the upper half, but not both.
|
|
|
|
3. Splitting the interrupt request and priority holdoff sets into two 32-bit
|
|
parts is actually more efficient on 32-bit host CPUs than using a single
|
|
64-bit set, especially when right-shifting to obtain the bit number.
|
|
Efficiency is also aided by the fact that interface select codes are
|
|
typically below 40 octal and therefore are represented in the first bit
|
|
set(s).
|
|
*/
|
|
|
|
uint32 io_poll_interrupts (FLIP_FLOP interrupt_system)
|
|
{
|
|
const uint32 unmaskable = 1u << PWR | 1u << MPPE; /* these interrupts are not inhibited by INTSYS clear */
|
|
uint32 sc, rank, priority_mask, request_granted;
|
|
|
|
if (interrupt_request_set [0]) { /* if a lower pending interrupt exists */
|
|
priority_mask = IOPRIORITY (priority_holdoff_set [0]); /* then calculate the first priority mask */
|
|
request_granted = priority_mask & interrupt_request_set [0]; /* and the request to grant */
|
|
rank = 0; /* for interrupt sources SC 00-37 */
|
|
|
|
if (interrupt_system == CLEAR) /* if the interrupt system is off */
|
|
request_granted &= unmaskable; /* then only Power Fail and Parity Error may interrupt */
|
|
}
|
|
|
|
else if (interrupt_request_set [1] /* otherwise if an upper pending interrupt exists */
|
|
&& priority_holdoff_set [0] == 0) { /* and the priority chain is intact */
|
|
priority_mask = IOPRIORITY (priority_holdoff_set [1]); /* then calculate the second priority mask */
|
|
request_granted = priority_mask & interrupt_request_set [1]; /* and the request to grant */
|
|
rank = 32; /* for interrupt sources SC 40-77 */
|
|
}
|
|
|
|
else /* otherwise no interrupts are pending */
|
|
return 0; /* so return */
|
|
|
|
if (request_granted == 0) /* if no request was granted */
|
|
return 0; /* then return */
|
|
|
|
else { /* otherwise */
|
|
for (sc = rank; !(request_granted & 1); sc++) /* determine the interrupting select code */
|
|
request_granted = request_granted >> 1; /* by counting the bits until the set bit is reached */
|
|
|
|
return sc; /* return the interrupting select code */
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/* CPU local SCP support routines */
|
|
|
|
|
|
/* CPU interface (select code 00).
|
|
|
|
I/O operations directed to select code 0 manipulate the interrupt system.
|
|
STF and CLF turn the interrupt system on and off, and SFS and SFC test the
|
|
state of the interrupt system. When the interrupt system is off, only Power
|
|
Fail and Parity Error interrupts are allowed. The PRL signal follows the
|
|
state of the interrupt system.
|
|
|
|
CLC asserts CRS to all interfaces from select code 6 upward.
|
|
|
|
A PON reset initializes certain CPU registers. The 1000 series does a
|
|
microcoded memory clear and leaves the T and P registers set as a result.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. An IOI signal reads the floating I/O bus (0 on all machines).
|
|
|
|
2. A CLC 0 issues CRS to all devices, not CLC. While most cards react
|
|
identically to CRS and CLC, some do not, e.g., the 12566B when used as an
|
|
I/O diagnostic target.
|
|
|
|
3. RTE uses the undocumented SFS 0,C instruction to both test and turn off
|
|
the interrupt system. This is confirmed in the "RTE-6/VM Technical
|
|
Specifications" manual (HP 92084-90015), section 2.3.1 "Process the
|
|
Interrupt", subsection "A.1 $CIC":
|
|
|
|
"Test to see if the interrupt system is on or off. This is done with
|
|
the SFS 0,C instruction. In either case, turn it off (the ,C does
|
|
it)."
|
|
|
|
...and in section 5.8, "Parity Error Detection":
|
|
|
|
"Because parity error interrupts can occur even when the interrupt
|
|
system is off, the code at $CIC must be able to save the complete
|
|
system status. The major hole in being able to save the complete state
|
|
is in saving the interrupt system state. In order to do this in both
|
|
the 21MX and the 21XE the instruction 103300 was used to both test the
|
|
interrupt system and turn it off."
|
|
|
|
4. Select code 0 cannot interrupt, so the SIR handler does not assert IRQ.
|
|
|
|
5. To guarantee proper initialization, the 12920A terminal multiplexer
|
|
requires that the Control Reset (CRS) I/O signal be asserted for a
|
|
minimum of 100 milliseconds. In practice, this is achieved by executing
|
|
131,072 (128K) CLC 0 instructions in a tight loop. This is not necessary
|
|
in simulation, and in fact is detrimental, as 262,000+ trace lines will
|
|
be written for each device that enables IOBUS tracing. To avoid this,
|
|
consecutive CLC 0 operations after the first are omitted. This is
|
|
detected by checking the select code and signal set of the last I/O
|
|
operation.
|
|
*/
|
|
|
|
static SIGNALS_VALUE cpu_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
|
|
{
|
|
static INBOUND_SET last_signal_set = ioNONE; /* the last set of I/O signals processed */
|
|
INBOUND_SIGNAL signal;
|
|
INBOUND_SET working_set = inbound_signals;
|
|
SIGNALS_VALUE outbound = { ioNONE, 0 };
|
|
uint32 sc;
|
|
|
|
while (working_set) { /* while signals remain */
|
|
signal = IONEXTSIG (working_set); /* isolate the next signal */
|
|
|
|
switch (signal) { /* dispatch the I/O signal */
|
|
|
|
case ioCLF: /* Clear Flag flip-flop */
|
|
interrupt_system = CLEAR; /* turn the interrupt system off */
|
|
|
|
if (interrupt_request > MPPE) /* if any interrupt other than power fail or parity error */
|
|
interrupt_request = 0; /* is pending, then clear it */
|
|
break;
|
|
|
|
|
|
case ioSTF: /* Set Flag flip-flop */
|
|
interrupt_system = SET; /* turn the interrupt system on */
|
|
break;
|
|
|
|
|
|
case ioSFC: /* Skip if Flag is Clear */
|
|
if (interrupt_system == CLEAR) /* if the interrupt system is off */
|
|
outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
|
|
break;
|
|
|
|
|
|
case ioSFS: /* Skip if Flag is Set */
|
|
if (interrupt_system == SET) /* if the interrupt system is on */
|
|
outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
|
|
break;
|
|
|
|
|
|
case ioIOI: /* I/O input */
|
|
outbound.value = 0; /* returns 0 */
|
|
break;
|
|
|
|
|
|
case ioPON: /* Power On Normal */
|
|
AR = 0; /* clear A register */
|
|
BR = 0; /* clear B register */
|
|
SR = 0; /* clear S register */
|
|
TR = 0; /* clear T register */
|
|
E = 1; /* set E register */
|
|
|
|
if (cpu_configuration & CPU_1000) { /* if the CPU is a 1000-series machine */
|
|
mem_zero (0, mem_size); /* then power on clears memory */
|
|
|
|
MR = LA_MAX; /* set the M register to the maximum logical address */
|
|
PR = MR + 1; /* and the P register to one more than that */
|
|
}
|
|
|
|
else { /* otherwise is a 21xx-series machine */
|
|
MR = 0; /* which clears the M register */
|
|
PR = 0; /* and the P register */
|
|
}
|
|
break;
|
|
|
|
|
|
case ioPOPIO: /* Power-On Preset to I/O */
|
|
O = 0; /* clear the overflow register */
|
|
|
|
interrupt_system = CLEAR; /* turn off the interrupt system */
|
|
cpu_interrupt_enable = SET; /* and enable interrupts */
|
|
break;
|
|
|
|
|
|
case ioCLC: /* Clear Control flip-flop */
|
|
if (last_select_code != 0 /* if the last I/O instruction */
|
|
|| (last_signal_set & ioCLC) == 0) /* was not a CLC 0 */
|
|
for (sc = SC_CRS; sc <= SC_MAX; sc++) /* then assert the CRS signal */
|
|
if (iot [sc].devptr != NULL) /* to all occupied I/O slots */
|
|
io_assert (iot [sc].devptr, ioa_CRS); /* from select code 6 and up */
|
|
break;
|
|
|
|
|
|
case ioSIR: /* Set Interrupt Request */
|
|
if (interrupt_system) /* if the interrupt system is on */
|
|
outbound.signals |= ioPRL | cnPRL | cnVALID; /* then assert PRL */
|
|
else /* otherwise */
|
|
outbound.signals |= cnVALID; /* deny PRL */
|
|
break;
|
|
|
|
|
|
case ioIOO: /* not used by this interface */
|
|
case ioSTC: /* not used by this interface */
|
|
case ioEDT: /* not used by this interface */
|
|
case ioCRS: /* not used by this interface */
|
|
case ioIAK: /* not used by this interface */
|
|
case ioENF: /* not used by this interface */
|
|
case ioIEN: /* not used by this interface */
|
|
case ioPRH: /* not used by this interface */
|
|
break;
|
|
}
|
|
|
|
IOCLEARSIG (working_set, signal); /* remove the current signal from the set */
|
|
} /* and continue until all signals are processed */
|
|
|
|
last_signal_set = inbound_signals; /* save the current signal set for the next call */
|
|
|
|
return outbound; /* return the outbound signals and value */
|
|
}
|
|
|
|
|
|
/* Overflow and S-register interface (select code 01).
|
|
|
|
I/O operations directed to select code 1 manipulate the overflow (O) and
|
|
switch (S) registers. On the 2115 and 2116, there is no S-register
|
|
indicator, so it is effectively read-only. On the other machines, a
|
|
front-panel display of the S-register is provided. On all machines,
|
|
front-panel switches are provided to set the contents of the S register.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. Select code 1 cannot interrupt, so there is no SIR handler, and PRH is
|
|
passed through to PRL.
|
|
*/
|
|
|
|
static SIGNALS_VALUE ovf_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
|
|
{
|
|
INBOUND_SIGNAL signal;
|
|
INBOUND_SET working_set = inbound_signals;
|
|
SIGNALS_VALUE outbound = { ioNONE, 0 };
|
|
|
|
while (working_set) { /* while signals remain */
|
|
signal = IONEXTSIG (working_set); /* isolate the next signal */
|
|
|
|
switch (signal) { /* dispatch the I/O signal */
|
|
|
|
case ioCLF: /* Clear Flag flip-flop */
|
|
O = 0; /* clear the overflow register */
|
|
break;
|
|
|
|
|
|
case ioSTF: /* Set Flag flip-flop */
|
|
O = 1; /* set the overflow register */
|
|
break;
|
|
|
|
|
|
case ioSFC: /* Skip if Flag is Clear */
|
|
if (O == 0) /* if the overflow register is clear */
|
|
outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
|
|
break;
|
|
|
|
|
|
case ioSFS: /* Skip if Flag is Set */
|
|
if (O != 0) /* if the overflow register is set */
|
|
outbound.signals |= ioSKF; /* then assert the Skip on Flag signal */
|
|
break;
|
|
|
|
|
|
case ioIOI: /* I/O data input */
|
|
outbound.value = SR; /* read switch register value */
|
|
break;
|
|
|
|
|
|
case ioIOO: /* I/O data output */
|
|
if (!(cpu_configuration & (CPU_2115 | CPU_2116))) /* on all machines except the 2115 and 2116 */
|
|
SR = inbound_value & R_MASK; /* write the value to the S-register display */
|
|
break;
|
|
|
|
|
|
case ioPRH: /* Priority High */
|
|
outbound.signals |= ioPRL; /* assert PRL */
|
|
break;
|
|
|
|
|
|
case ioSTC: /* not used by this interface */
|
|
case ioCLC: /* not used by this interface */
|
|
case ioEDT: /* not used by this interface */
|
|
case ioCRS: /* not used by this interface */
|
|
case ioPOPIO: /* not used by this interface */
|
|
case ioPON: /* not used by this interface */
|
|
case ioIAK: /* not used by this interface */
|
|
case ioENF: /* not used by this interface */
|
|
case ioIEN: /* not used by this interface */
|
|
case ioSIR: /* not used by this interface */
|
|
break;
|
|
}
|
|
|
|
IOCLEARSIG (working_set, signal); /* remove the current signal from the set */
|
|
} /* and continue until all signals are processed */
|
|
|
|
return outbound; /* return the outbound signals and value */
|
|
}
|
|
|
|
|
|
/* Power fail interface (select code 04).
|
|
|
|
Power fail detection is standard on 2100 and 1000 systems and is optional on
|
|
21xx systems. Power fail recovery is standard on the 2100 and optional on
|
|
the others. Power failure or restoration will cause an interrupt on select
|
|
code 4 and will deny PRL, preventing all other devices from interrupting
|
|
while the power fail routine is executing. Asserting either STC or CLC
|
|
clears the interrupt and reasserts PRL. The direction of power change (down
|
|
or up) can be tested by SFC, which will skip if power is failing.
|
|
|
|
Asserting IOI to select code 4 reads the Central Interrupt Register value.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. Currently, power fail is not simulated, and the interface is provided
|
|
solely to read the CIR. As a result, PRL is always asserted.
|
|
*/
|
|
|
|
static SIGNALS_VALUE pwr_interface (const DIB *dibptr, INBOUND_SET inbound_signals, HP_WORD inbound_value)
|
|
{
|
|
INBOUND_SIGNAL signal;
|
|
INBOUND_SET working_set = inbound_signals;
|
|
SIGNALS_VALUE outbound = { ioNONE, 0 };
|
|
|
|
while (working_set) { /* while signals remain */
|
|
signal = IONEXTSIG (working_set); /* isolate the next signal */
|
|
|
|
switch (signal) { /* dispatch the I/O signal */
|
|
|
|
case ioSTC: /* Set Control flip-flop */
|
|
case ioCLC: /* Clear Control flip-flop */
|
|
break; /* reinitializes power fail */
|
|
|
|
|
|
case ioSFC: /* Skip if Flag is Clear */
|
|
break; /* skips if power is failing */
|
|
|
|
|
|
case ioIOI: /* I/O data input */
|
|
outbound.value = CIR; /* return the select code of the interrupting interface */
|
|
break;
|
|
|
|
|
|
case ioPRH: /* Priority High */
|
|
outbound.signals |= ioPRL; /* assert PRL */
|
|
break;
|
|
|
|
|
|
case ioSTF: /* not used by this interface */
|
|
case ioCLF: /* not used by this interface */
|
|
case ioSFS: /* not used by this interface */
|
|
case ioIOO: /* not used by this interface */
|
|
case ioEDT: /* not used by this interface */
|
|
case ioCRS: /* not used by this interface */
|
|
case ioPOPIO: /* not used by this interface */
|
|
case ioPON: /* not used by this interface */
|
|
case ioIAK: /* not used by this interface */
|
|
case ioENF: /* not used by this interface */
|
|
case ioIEN: /* not used by this interface */
|
|
case ioSIR: /* not used by this interface */
|
|
break;
|
|
}
|
|
|
|
IOCLEARSIG (working_set, signal); /* remove the current signal from the set */
|
|
} /* and continue until all signals are processed */
|
|
|
|
return outbound; /* return the outbound signals and value */
|
|
}
|
|
|
|
|
|
/* Examine a CPU memory location.
|
|
|
|
This routine is called by the SCP to examine memory. The routine retrieves
|
|
the memory location indicated by "address" as modified by any "switches" that
|
|
were specified on the command line and returns the value in the first element
|
|
of "eval_array". The "uptr" parameter is not used.
|
|
|
|
On entry, the "meu_map_address" routine is called to translate a logical
|
|
address to a physical address. If "switches" includes SIM_SW_REST or "-N",
|
|
then the address is a physical address, and the routine returns the address
|
|
unaltered.
|
|
|
|
Otherwise, the address is a logical address interpreted in the context of the
|
|
translation map implied by the specified switch and is mapped to a physical
|
|
address. If memory expansion is disabled but a map is specified, then the
|
|
command is rejected. Otherwise if the resulting address is beyond the
|
|
current memory limit, or if mapping is implied or explicit but the address
|
|
specified is outside of the logical address space, "address space exceeded"
|
|
status is returned.
|
|
|
|
Otherwise, the value is obtained from memory or the A/B register and returned
|
|
in the first word of "eval_array."
|
|
*/
|
|
|
|
static t_stat cpu_examine (t_value *eval_array, t_addr address, UNIT *uptr, int32 switches)
|
|
{
|
|
uint32 index;
|
|
|
|
index = meu_map_address ((HP_WORD) address, switches); /* map the supplied address as directed by the switches */
|
|
|
|
if (index == D32_UMAX) /* if the MEM is disabled but a mapping mode was given */
|
|
return SCPE_NOFNC; /* then the command is not allowed */
|
|
|
|
else if (index >= mem_size) /* otherwise if the address is beyond the memory limit */
|
|
return SCPE_NXM; /* then return non-existent memory status */
|
|
|
|
else if (eval_array == NULL) /* otherwise if the value pointer was not supplied */
|
|
return SCPE_IERR; /* then return internal error status */
|
|
|
|
else /* otherwise */
|
|
*eval_array = (t_value) mem_examine (index); /* then return the memory or A/B register value */
|
|
|
|
return SCPE_OK; /* return success status */
|
|
}
|
|
|
|
|
|
/* Deposit to a CPU memory location.
|
|
|
|
This routine is called by the SCP to deposit to memory. The routine stores
|
|
the supplied "value" into memory at the "address" location as modified by any
|
|
"switches" that were specified on the command line.
|
|
|
|
On entry, the "meu_map_address" routine is called to translate a logical
|
|
address to a physical address. If "switches" includes SIM_SW_REST or "-N",
|
|
then the address is a physical address, and the routine returns the address
|
|
unaltered.
|
|
|
|
Otherwise, the address is a logical address interpreted in the context of the
|
|
translation map implied by the specified switch and is mapped to a physical
|
|
address. If memory expansion is disabled but a map is specified, then the
|
|
command is rejected. Otherwise if the resulting address is beyond the
|
|
current memory limit, or if mapping is implied or explicit but the address
|
|
specified is outside of the logical address space, "address space exceeded"
|
|
status is returned.
|
|
|
|
Otherwise, the value is stored into memory or the A/B register.
|
|
*/
|
|
|
|
static t_stat cpu_deposit (t_value value, t_addr address, UNIT *uptr, int32 switches)
|
|
{
|
|
uint32 index;
|
|
|
|
index = meu_map_address ((HP_WORD) address, switches); /* map the supplied address as directed by the switches */
|
|
|
|
if (index == D32_UMAX) /* if the MEM is disabled but a mapping mode was given */
|
|
return SCPE_NOFNC; /* then the command is not allowed */
|
|
|
|
else if (index >= mem_size) /* otherwise if the address is beyond the memory limit */
|
|
return SCPE_NXM; /* then return non-existent memory status */
|
|
|
|
else /* otherwise */
|
|
mem_deposit (index, (HP_WORD) value); /* write the memory or A/B register value */
|
|
|
|
return SCPE_OK; /* return success status */
|
|
}
|
|
|
|
|
|
/* Reset the CPU.
|
|
|
|
This routine is called for a RESET, RESET CPU, RUN, or BOOT CPU command. It
|
|
is the simulation equivalent of an initial power-on condition (corresponding
|
|
to PON, POPIO, and CRS signal assertion in the CPU) or a front-panel PRESET
|
|
button press (corresponding to POPIO and CRS assertion). SCP delivers a
|
|
power-on reset to all devices when the simulator is started.
|
|
|
|
If this is the first call after simulator startup, the initial memory array
|
|
is allocated, the SCP-required program counter pointer is set to point to the
|
|
REG array element corresponding to the P register, and the default CPU and
|
|
memory size configuration is set. In addition, the loader ROM sockets of the
|
|
1000-series CPUs are populated with the initial ROM set, and the Basic Binary
|
|
Loader (BBL) is installed in the protected memory (the upper 64 words of the
|
|
defined memory size) of the 2116.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. Setting the sim_PC value at run time accommodates changes in the register
|
|
order automatically. A fixed setting runs the risk of it not being
|
|
updated if a change in the register order is made.
|
|
|
|
2. The initial set of installed HP 1000 boot loader ROMs is:
|
|
|
|
Socket ROM Boot Device
|
|
------ ------ ------------------------
|
|
0 12992K 2748 Paper Tape Reader
|
|
1 12992A 7900 or 2883 Disc Drive
|
|
2 12992D 7970 Magnetic Tape Drive
|
|
3 12992B 7905/06/20/25 Disc Drive
|
|
*/
|
|
|
|
static t_stat cpu_reset (DEVICE *dptr)
|
|
{
|
|
t_stat status;
|
|
|
|
if (sim_PC == NULL) { /* if this is the first call after simulator start */
|
|
|
|
hp_one_time_init(); /* perform one time initializations (previously defined as sim_vm_init() */
|
|
|
|
status = mem_initialize (PA_MAX); /* then allocate main memory */
|
|
|
|
if (status == SCPE_OK) { /* if memory initialization succeeds */
|
|
for (sim_PC = dptr->registers; /* then find the P register entry */
|
|
sim_PC->loc != &PR && sim_PC->loc != NULL; /* in the register array */
|
|
sim_PC++); /* for the SCP interface */
|
|
|
|
if (sim_PC == NULL) /* if the P register entry is not present */
|
|
return SCPE_NXREG; /* then there is a serious problem! */
|
|
|
|
pcq_r = find_reg ("PCQ", NULL, dptr); /* get the PC queue pointer */
|
|
|
|
if (pcq_r == NULL) /* if the PCQ register is not present */
|
|
return SCPE_IERR; /* then something is seriously wrong */
|
|
else /* otherwise */
|
|
pcq_r->qptr = 0; /* initialize the register's queue pointer */
|
|
|
|
mem_size = 32768; /* set the initial memory size to 32K */
|
|
set_model (NULL, UNIT_2116, NULL, NULL); /* and the initial CPU model */
|
|
|
|
loader_rom [0] = find_dev ("PTR"); /* install the 12992K ROM in socket 0 */
|
|
loader_rom [1] = find_dev ("DQC"); /* and the 12992A ROM in socket 1 */
|
|
loader_rom [2] = find_dev ("MSC"); /* and the 12992D ROM in socket 2 */
|
|
loader_rom [3] = find_dev ("DS"); /* and the 12992B ROM in socket 3 */
|
|
|
|
loader_rom [0]->boot (0, loader_rom [0]); /* install the BBL via the paper tape reader boot routine */
|
|
set_loader (NULL, FALSE, NULL, NULL); /* and then disable the loader, which had been enabled */
|
|
}
|
|
|
|
else /* otherwise memory initialization failed */
|
|
return status; /* so report the error and abort the simulator */
|
|
}
|
|
|
|
if (sim_switches & SWMASK ('P')) /* if this is a power-on reset */
|
|
io_assert (&cpu_dev, ioa_PON); /* then issue the PON signal to the CPU */
|
|
else /* otherwise */
|
|
io_assert (&cpu_dev, ioa_POPIO); /* issue a PRESET */
|
|
|
|
sim_brk_dflt = SWMASK ('N'); /* the default breakpoint type is "nomap" as MEM is disabled */
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
|
|
/* Device boot routine.
|
|
|
|
This routine is called by the BOOT CPU and LOAD CPU commands to copy the
|
|
specified boot loader ROM program into the upper 64 words of the logical
|
|
address space. It is equivalent to pressing the IBL (Initial Binary Loader)
|
|
button on the front panel of a 1000 M/E/F-Series CPU.
|
|
|
|
On entry, the S register must be set to indicate the specific boot loader ROM
|
|
and the associated device select code to be copied, as follows:
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| ROM # | - - | select code | - - - - - - |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
Bits 15-14 select one of four loader ROM sockets on the CPU board that may
|
|
contain ROMs. If the specified socket does, the contents of the ROM are
|
|
copied into the upper 64 words of memory and configured to use the specified
|
|
select code. The unspecified bits of the S register are available for use by
|
|
the bootstrap program.
|
|
|
|
If the select code is less than 10 octal, the loader is not copied, and the
|
|
O (overflow) register is set to 1. A successful copy and configuration
|
|
clears the O register.
|
|
|
|
The 21xx-series CPUs do not provide the IBL function. If this routine is
|
|
invoked while the CPU is configured as one of these machines, the command is
|
|
rejected.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. In hardware, a non-existent ROM (i.e., an empty socket) reads as though
|
|
all words contain 177777 octal. This would result in the loader area of
|
|
memory containing 62 all-ones words, followed by a word set to 177777 +
|
|
SC - 000010, where SC is the configured select code, followed by a word
|
|
set to the negative starting address of the loader. This is not
|
|
simulated; instead, an attempt to boot from an empty socket is rejected
|
|
with "Non-existent device."
|
|
*/
|
|
|
|
static t_stat cpu_boot (int32 unitno, DEVICE *dptr)
|
|
{
|
|
const int32 select_code = IBL_SC (SR); /* the select code from S register bits 11-6 */
|
|
const int32 rom_socket = IBL_ROM (SR); /* the ROM socket number from S register bits 15-14 */
|
|
|
|
if (cpu_configuration & CPU_1000) /* if this is a 1000-series CPU */
|
|
if (select_code < SC_VAR) { /* then if the select code is invalid */
|
|
O = 1; /* then set the overflow register */
|
|
return SCPE_ARG; /* and reject the IBL with "Invalid argument" */
|
|
}
|
|
|
|
else if (loader_rom [rom_socket] == NULL) /* otherwise if the ROM socket is empty */
|
|
return SCPE_NXDEV; /* then reject with "Non-existent device" */
|
|
|
|
else { /* otherwise */
|
|
O = 0; /* clear overflow to indicate a good IBL */
|
|
return loader_rom [rom_socket]->boot (select_code, NULL); /* and copy the ROM into memory */
|
|
}
|
|
|
|
else /* otherwise this is a 21xx machine */
|
|
return SCPE_NOFNC; /* and IBL isn't supported */
|
|
}
|
|
|
|
|
|
/* Set the CPU simulation stop conditions.
|
|
|
|
This validation routine is called to configure the set of CPU stop
|
|
conditions. The "option" parameter is 0 to clear the stops, 1 to set the
|
|
stops, and 2 to set the indirect chain length limit. "cptr" points to the
|
|
first character of the name of the stop to be cleared or set. The unit and
|
|
description pointers are not used.
|
|
|
|
The routine processes commands of the form:
|
|
|
|
SET CPU STOP
|
|
SET CPU STOP=<stopname>[;<stopname>...]
|
|
SET CPU NOSTOP
|
|
SET CPU NOSTOP=<stopname>[;<stopname>...]
|
|
SET CPU INDIR=<limit>
|
|
|
|
The valid <stopname>s are contained in the "cpu_stop" table. If names are
|
|
not specified, all stop conditions are enabled or disabled.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. The maximum indirect limit value is 32K, as an indirect chain cannot
|
|
exceed the logical memory size without being in a loop.
|
|
*/
|
|
|
|
static t_stat set_stops (UNIT *uptr, int32 option, CONST char *cptr, void *desc)
|
|
{
|
|
char gbuf [CBUFSIZE];
|
|
t_stat status;
|
|
uint32 stop;
|
|
|
|
if (cptr == NULL) /* if there are no arguments */
|
|
if (option == 0) /* then if we're clearing the stops */
|
|
for (stop = 0; cpu_stop [stop].name != NULL; stop++) /* then loop through the flags */
|
|
*cpu_stop [stop].status = SCPE_OK; /* and clear each stop status */
|
|
|
|
else if (option == 1) /* otherwise if we're setting the stops */
|
|
for (stop = 0; cpu_stop [stop].name != NULL; stop++) /* then loop through the flags */
|
|
*cpu_stop [stop].status = cpu_stop [stop].value; /* and set each stop status */
|
|
|
|
else /* otherwise */
|
|
return SCPE_MISVAL; /* report the missing indirect limit value */
|
|
|
|
else if (*cptr == '\0') /* otherwise if the argument is empty */
|
|
return SCPE_MISVAL; /* then report the missing value */
|
|
|
|
else if (option == 2) { /* otherwise if we're setting the indirect limit */
|
|
stop = (uint32) get_uint (cptr, 10, LA_MAX + 1, &status); /* then parse the limit value */
|
|
|
|
if (status != SCPE_OK) /* if a parsing error occurred */
|
|
return status; /* then return the error status */
|
|
else /* otherwise */
|
|
indirect_limit = stop; /* set the indirect limit */
|
|
}
|
|
|
|
else /* otherwise at least one stop argument is present */
|
|
while (*cptr) { /* loop through the arguments */
|
|
cptr = get_glyph (cptr, gbuf, ';'); /* get the next argument */
|
|
|
|
for (stop = 0; cpu_stop [stop].name != NULL; stop++) /* loop through the flags */
|
|
if (strcmp (cpu_stop [stop].name, gbuf) == 0) { /* and if the argument matches */
|
|
if (option == 1) /* then if it's a STOP argument */
|
|
*cpu_stop [stop].status = cpu_stop [stop].value; /* then set the stop status */
|
|
else /* otherwise it's a NOSTOP argument */
|
|
*cpu_stop [stop].status = SCPE_OK; /* so clear the stop status */
|
|
|
|
break; /* this argument has been processed */
|
|
}
|
|
|
|
if (cpu_stop [stop].name == NULL) /* if the argument was not found */
|
|
return SCPE_ARG; /* then report it */
|
|
}
|
|
|
|
return SCPE_OK; /* the stops were successfully processed */
|
|
}
|
|
|
|
|
|
/* Change the CPU memory size.
|
|
|
|
This validation routine is called to configure the CPU memory size. The
|
|
"new_size" parameter is set to the size desired and will be one of the
|
|
discrete sizes supported by the simulator. The "uptr" parameter points to
|
|
the CPU unit and is used to obtain the CPU model. The other parameters are
|
|
not used.
|
|
|
|
The routine processes commands of the form:
|
|
|
|
SET [-F] CPU <memsize>
|
|
|
|
If the new memory size is larger than the supported size for the CPU model
|
|
currently selected, the routine returns an error. If the new size is smaller
|
|
than the previous size, and if the area that would be lost contains non-zero
|
|
data, the user is prompted to confirm that memory should be truncated. If
|
|
the user denies the request, the change is rejected. Otherwise, the new size
|
|
is set. The user may omit the confirmation request and force truncation by
|
|
specifying the "-F" switch on the command line.
|
|
|
|
On a 21xx CPU, the last 64 words in memory are reserved for the binary
|
|
loader. Before changing the memory size, the current loader is copied to the
|
|
shadow RAM to preserve any manual changes that were made. Then the new
|
|
memory size is set, with the beginning of the loader area set as the first
|
|
word of non-existent memory.
|
|
|
|
Finally, non-existent memory is zeroed, so that the mem_read routine does not
|
|
need any special handling for addresses beyond the end of defined memory.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. In hardware, reads from non-existent memory return zero, and writes are
|
|
ignored. In simulation, the largest possible memory is instantiated and
|
|
initialized to zero. Therefore, only writes need to be checked against
|
|
memory size.
|
|
|
|
2. On the 21xx machines, doing SET CPU LOADERDISABLE decreases available
|
|
memory size by 64 words.
|
|
*/
|
|
|
|
static t_stat set_size (UNIT *uptr, int32 new_size, CONST char *cptr, void *desc)
|
|
{
|
|
static CONST char confirm [] = "Really truncate memory [N]?";
|
|
const uint32 model = UNIT_MODEL (cpu_unit [0].flags); /* the current CPU model index */
|
|
int32 old_size = (int32) mem_size; /* current memory size */
|
|
|
|
if ((uint32) new_size > cpu_features [model].maxmem) /* if the new memory size is not supported on current model */
|
|
return SCPE_NOFNC; /* then report the error */
|
|
|
|
if (!(sim_switches & SWMASK ('F')) /* if truncation is not explicitly forced */
|
|
&& ! mem_is_empty (new_size) /* and the truncated part is not empty */
|
|
&& get_yn (confirm, FALSE) == FALSE) /* and the user denies confirmation */
|
|
return SCPE_INCOMP; /* then abort the command */
|
|
|
|
if (cpu_configuration & CPU_1000) /* if the CPU is a 1000-series machine */
|
|
cpu_unit [0].capac = mem_size = mem_end = new_size; /* then memory is not reserved for the loader */
|
|
|
|
else { /* otherwise */
|
|
set_loader (uptr, FALSE, NULL, NULL); /* save loader to shadow RAM */
|
|
cpu_unit [0].capac = mem_size = new_size; /* set new memory size */
|
|
mem_end = mem_size - IBL_SIZE; /* reserve memory for loader */
|
|
}
|
|
|
|
if (old_size > new_size) /* if the new size is smaller than the prior size */
|
|
mem_zero (mem_end, old_size - new_size); /* then zero the newly non-existent memory area */
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
|
|
/* Change the CPU model.
|
|
|
|
This validation routine is called to configure the CPU model. The
|
|
"new_model" parameter is set to the unit flag corresponding to the model
|
|
desired. The "uptr" parameter points to the CPU unit. The other parameters
|
|
are not used.
|
|
|
|
Validation starts by setting the new memory size. If the current memory size
|
|
is within the range of memory sizes permitted by the new CPU model, it is
|
|
kept; otherwise, it is reduced to the maximum size permitted. If memory is
|
|
to be truncated, the "set_size" routine verifies either that it is blank
|
|
(i.e., filled with zero values) or that the user confirms that truncation is
|
|
allowable.
|
|
|
|
If the new memory size is accepted, the CPU options are set to the typical
|
|
configuration supported by the new model. Memory Protect and DMA are then
|
|
configured as specified by the CPU feature table. Memory expansion is
|
|
enabled if the DMS instruction set is present. Finally, the "is_1000" flag
|
|
and memory reserved for the binary loader are set as directed by the new
|
|
model.
|
|
|
|
On return, the "cpu_configuration" bit set is updated to indicate the new
|
|
model configuration.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. "cpu_configuration" is used by the symbolic examine and deposit routines
|
|
and instruction tracing to determine whether the firmware implementing a
|
|
given opcode is present. It is a copy of the CPU unit option flags with
|
|
the encoded CPU model decoded into model flag bits. This allows a simple
|
|
(and fast) AND operation with a firmware feature word to determine
|
|
applicability, saving the multiple masks and comparisons that would
|
|
otherwise be required.
|
|
|
|
2. The 'is_1000" variable is used to index into tables where the row
|
|
selected depends on whether or not the CPU is a 1000 M/E/F-series model.
|
|
For logical tests that depend on this, it is faster (by one x86 machine
|
|
instruction) to test the "cpu_configuration" variable for the presence of
|
|
one of the three 1000 model flags.
|
|
*/
|
|
|
|
static t_stat set_model (UNIT *uptr, int32 new_model, CONST char *cptr, void *desc)
|
|
{
|
|
const FEATURE_TABLE new_cpu = cpu_features [UNIT_MODEL (new_model)]; /* get the features describing the new model */
|
|
uint32 new_memsize;
|
|
t_stat result;
|
|
|
|
if (mem_size > new_cpu.maxmem) /* if the current memory size is too large for the new model */
|
|
new_memsize = new_cpu.maxmem; /* then set it to the maximum size supported */
|
|
else /* otherwise */
|
|
new_memsize = mem_size; /* leave it unchanged */
|
|
|
|
result = set_size (uptr, new_memsize, NULL, NULL); /* set the new memory size */
|
|
|
|
if (result == SCPE_OK) { /* if the change succeeded */
|
|
cpu_configuration = TO_CPU_OPTION (new_cpu.typ) /* then set the typical options */
|
|
| CPU_BASE /* and the base instruction set bit */
|
|
| TO_CPU_MODEL (new_model); /* and the new CPU model flag */
|
|
|
|
cpu_unit [0].flags = cpu_unit [0].flags & ~UNIT_OPTION_FIELD /* enable the typical features */
|
|
| new_cpu.typ & UNIT_OPTION_FIELD; /* for the new model */
|
|
|
|
mp_configure ((new_cpu.typ & UNIT_MP) != 0, /* configure MP, specifying whether */
|
|
(new_cpu.opt & UNIT_MP) != 0); /* it is enabled and optional */
|
|
|
|
dma_configure (); /* configure DMA for the new model */
|
|
|
|
if (new_cpu.typ & UNIT_DMS) /* if DMS instructions are present */
|
|
meu_configure (ME_Enabled); /* then enable the MEM device */
|
|
else /* otherwise */
|
|
meu_configure (ME_Disabled); /* disable the MEM and mapping */
|
|
|
|
if (cpu_configuration & CPU_1000) { /* if the CPU is a 1000-series machine */
|
|
is_1000 = TRUE; /* then set the model index */
|
|
mem_end = mem_size; /* memory is not reserved for the loader */
|
|
}
|
|
|
|
else { /* otherwise this is a 2100 or 211x */
|
|
is_1000 = FALSE; /* so set the model index */
|
|
mem_end = mem_size - IBL_SIZE; /* and reserve memory for the loader */
|
|
}
|
|
}
|
|
|
|
return result;
|
|
}
|
|
|
|
|
|
/* Set a CPU option.
|
|
|
|
This validation routine is called to add an option to the current CPU
|
|
configuration. The "option" parameter is set to the unit flag corresponding
|
|
to the option desired. The "uptr" parameter points to the CPU unit and is
|
|
used to obtain the CPU model. The other parameters are not used.
|
|
|
|
The routine processes commands of the form:
|
|
|
|
SET CPU <option>[,<option>...]
|
|
|
|
The option must be valid for the current CPU model, or the command will be
|
|
rejected.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. "cpu_configuration" is used by the symbolic examine and deposit routines
|
|
and instruction tracing to determine whether the firmware implementing a
|
|
given opcode is present. It is a copy of the CPU unit option flags with
|
|
the encoded CPU model decoded into model flag bits. This allows a simple
|
|
(and fast) AND operation with a firmware feature word to determine
|
|
applicability, saving the multiple masks and comparisons that would
|
|
otherwise be required.
|
|
*/
|
|
|
|
static t_stat set_option (UNIT *uptr, int32 option, CONST char *cptr, void *desc)
|
|
{
|
|
const uint32 model = UNIT_MODEL (uptr->flags); /* the current CPU model index */
|
|
|
|
if ((cpu_features [model].opt & option) == 0) /* if the option is not available for the current CPU */
|
|
return SCPE_NOFNC; /* then reject the request */
|
|
|
|
if (option == UNIT_DMS) /* if DMS instructions are being enabled */
|
|
meu_configure (ME_Enabled); /* then enable the MEM device */
|
|
|
|
if (cpu_configuration & CPU_2100) { /* if the current CPU is a 2100 */
|
|
if ((option == UNIT_FP) || (option == UNIT_FFP)) /* then the IOP option */
|
|
uptr->flags &= ~UNIT_IOP; /* and the FP and FFP options */
|
|
else if (option == UNIT_IOP) /* are */
|
|
uptr->flags &= ~(UNIT_FP | UNIT_FFP); /* mutually exclusive */
|
|
|
|
if (option == UNIT_FFP) /* the FFP option */
|
|
uptr->flags |= UNIT_FP; /* requires FP as well */
|
|
}
|
|
|
|
cpu_configuration = cpu_configuration & ~CPU_OPTION_MASK /* update the CPU configuration */
|
|
| TO_CPU_OPTION (uptr->flags) /* with any revised option settings */
|
|
| CPU_BASE; /* and the base set bit */
|
|
|
|
if (option & UNIT_EMA_VMA) /* if EMA or VMA is being set */
|
|
cpu_configuration &= ~UNIT_EMA_VMA; /* then first remove both as they are mutually exclusive */
|
|
|
|
cpu_configuration |= TO_CPU_OPTION (option); /* include the new setting in the configuration */
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
|
|
/* Clear a CPU option.
|
|
|
|
This validation routine is called to remove an option from the current CPU
|
|
configuration. The "option" parameter is set to the unit flag corresponding
|
|
to the option desired. The "uptr" parameter points to the CPU unit and is
|
|
used to obtain the CPU model. The other parameters are not used.
|
|
|
|
The routine processes commands of the form:
|
|
|
|
SET CPU NO<option>[,NO<option>...]
|
|
|
|
The option must be valid for the current CPU model, or the command will be
|
|
rejected.
|
|
*/
|
|
|
|
static t_stat clear_option (UNIT *uptr, int32 option, CONST char *cptr, void *desc)
|
|
{
|
|
const uint32 model = UNIT_MODEL (uptr->flags); /* the current CPU model index */
|
|
|
|
if ((cpu_features [model].opt & option) == 0) /* if the option is not available for the current CPU */
|
|
return SCPE_NOFNC; /* then reject the request */
|
|
|
|
uptr->flags &= ~option; /* disable the option */
|
|
|
|
if (option == UNIT_DMS) /* if DMS instructions are being disabled */
|
|
meu_configure (ME_Disabled); /* then disable the MEM device */
|
|
|
|
if (cpu_configuration & CPU_2100 && option == UNIT_FP) /* if the FP option on a 2100 is being disabled */
|
|
uptr->flags &= ~UNIT_FFP; /* then disable the FFP as well */
|
|
|
|
cpu_configuration = cpu_configuration & ~CPU_OPTION_MASK /* update the CPU configuration */
|
|
| TO_CPU_OPTION (uptr->flags) /* with the revised option settings */
|
|
| CPU_BASE; /* and the base set bit */
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
|
|
/* Enable or disable the 21xx binary loader.
|
|
|
|
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 (mem_size) and
|
|
a current configured memory size (mem_end = "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.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. In hardware, reads from non-existent memory return zero. In simulation,
|
|
the largest possible memory is instantiated and initialized to zero, so
|
|
that reads need not be checked against memory size. To preserve this
|
|
model for the protected loader, we save and then zero the memory area
|
|
when the loader is disabled.
|
|
*/
|
|
|
|
static t_stat set_loader (UNIT *uptr, int32 enable, CONST char *cptr, void *desc)
|
|
{
|
|
static MEMORY_WORD loader [IBL_SIZE]; /* the shadow memory for the currently disabled loader */
|
|
const t_bool currently_enabled = (mem_end == mem_size); /* TRUE if the loader is currently enabled */
|
|
|
|
if (cpu_configuration & CPU_1000) /* if the current CPU is a 1000-series */
|
|
return SCPE_NOFNC; /* then the protected loader does not exist */
|
|
|
|
if (currently_enabled && enable == 0) { /* if the enabled loader is being disabled */
|
|
mem_end = mem_size - IBL_SIZE; /* then decrease available memory */
|
|
mem_copy_loader (loader, mem_end, From_Memory); /* and shadow the loader and zero memory */
|
|
}
|
|
|
|
else if (!currently_enabled && enable == 1) { /* otherwise if the disabled loader is being enabled */
|
|
mem_copy_loader (loader, mem_end, To_Memory); /* then copy the shadow loader to memory */
|
|
mem_end = mem_size; /* and increase available memory */
|
|
}
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
|
|
/* Change the set of installed loader ROMs.
|
|
|
|
This validation routine is called to install loader ROMs in the four
|
|
available sockets of a 1000-series CPU. The routine processes commands of
|
|
the form:
|
|
|
|
SET CPU ROMS=[<dev0>][;[<dev1>][;[<dev2>][;[<dev3>]]]]
|
|
|
|
On entry, "cptr" points at the the first character of the ROM list. The
|
|
option value and the unit and description pointers are not used.
|
|
|
|
All four ROM sockets are set for each command. If no devices are specified,
|
|
then all sockets are emptied. Otherwise, specifying a valid device name
|
|
installs the device loader ROM into the socket corresponding to the position
|
|
of the device name in the list. Sockets may be left empty by omitting the
|
|
corresponding device name or by supplying fewer than four device names.
|
|
|
|
Loader ROMs may only be altered if the current CPU model is a 1000-series
|
|
machine, and a device must be bootable and have a loader ROM assigned, or the
|
|
command will be rejected. A rejected command does not alter any of the ROM
|
|
assignments.
|
|
|
|
Example commands and their effects on the installed ROM sockets follow:
|
|
|
|
Command Action
|
|
--------------------- -------------------------------------------------
|
|
SET CPU ROMS= Remove ROMs from sockets 0-3
|
|
SET CPU ROMS=PTR Install PTR in 0; leave 1-3 empty
|
|
SET CPU ROMS=DS;MS Install DS in 0 and MS in 1; leave 2 and 3 empty
|
|
SET CPU ROMS=;;DPC Install DPC in 2; leave 0, 1, and 3 empty
|
|
SET CPU ROMS=DQC;;;DA Install DQC in 0 and DA in 3; leave 1 and 2 empty
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. Entering "SET CPU ROMS" without an equals sign or list is rejected with a
|
|
"Missing value" error. This is to prevent accidental socket clearing
|
|
when "SHOW CPU ROMS" was intended.
|
|
*/
|
|
|
|
static t_stat set_roms (UNIT *uptr, int32 option, CONST char *cptr, void *desc)
|
|
{
|
|
DEVICE *dptr;
|
|
char gbuf [CBUFSIZE];
|
|
uint32 socket = 0;
|
|
DEVICE *rom [4] = { NULL };
|
|
|
|
if (!(cpu_configuration & CPU_1000)) /* if the CPU is not a 1000-series unit */
|
|
return SCPE_NOFNC; /* then reject the command */
|
|
|
|
else if (cptr == NULL) /* otherwise if the list is not specified */
|
|
return SCPE_MISVAL; /* then report that the list is missing */
|
|
|
|
else if (*cptr == '\0') { /* otherwise if the list is null */
|
|
loader_rom [0] = NULL; /* then empty */
|
|
loader_rom [1] = NULL; /* all of the */
|
|
loader_rom [2] = NULL; /* ROM sockets */
|
|
loader_rom [3] = NULL;
|
|
}
|
|
|
|
else { /* otherwise */
|
|
while (*cptr) { /* loop through the arguments */
|
|
cptr = get_glyph (cptr, gbuf, ';'); /* get the next argument */
|
|
|
|
if (socket == 4) /* if all four sockets have been set */
|
|
return SCPE_2MARG; /* then reject the command */
|
|
|
|
else if (gbuf [0] == '\0') /* otherwise if the device name is omitted */
|
|
rom [socket++] = NULL; /* then empty the corresponding socket */
|
|
|
|
else { /* otherwise we have a device name */
|
|
dptr = find_dev (gbuf); /* so find the associated DEVICE pointer */
|
|
|
|
if (dptr == NULL) /* if the device name is not valid */
|
|
return SCPE_NXDEV; /* then reject the command */
|
|
|
|
else if (dptr->boot == NULL) /* otherwise if it's valid but not bootable */
|
|
return SCPE_NOFNC; /* then reject the command */
|
|
|
|
else /* otherwise */
|
|
rom [socket++] = dptr; /* install the boot loader ROM */
|
|
}
|
|
}
|
|
|
|
loader_rom [0] = rom [0]; /* install the ROM set */
|
|
loader_rom [1] = rom [1]; /* now that we have */
|
|
loader_rom [2] = rom [2]; /* a valid */
|
|
loader_rom [3] = rom [3]; /* device list */
|
|
}
|
|
|
|
return SCPE_OK; /* report that the command succeeded */
|
|
}
|
|
|
|
|
|
/* Change the instruction execution trace criteria.
|
|
|
|
This validation routine is called to configure the criteria that select
|
|
instruction execution tracing. The "option" parameter is 0 to clear and 1 to
|
|
set the criteria, and "cptr" points to the first character of the match value
|
|
to be set. The unit and description pointers are not used.
|
|
|
|
The routine processes commands of the form:
|
|
|
|
SET CPU EXEC=<match>[;<mask>]
|
|
SET CPU NOEXEC
|
|
|
|
If the <mask> value is not supplied, a mask of 177777 octal is used. The
|
|
values are entered in the current CPU data radix, which defaults to octal,
|
|
unless an override switch is present on the command line.
|
|
*/
|
|
|
|
static t_stat set_exec (UNIT *uptr, int32 option, CONST char *cptr, void *desc)
|
|
{
|
|
char gbuf [CBUFSIZE];
|
|
uint32 match, mask, radix;
|
|
t_stat status;
|
|
|
|
if (option == 0) /* if this is a NOEXEC request */
|
|
if (cptr == NULL) { /* then if there are no arguments */
|
|
exec_match = D16_UMAX; /* then set the match and mask values */
|
|
exec_mask = 0; /* to prevent matching */
|
|
return SCPE_OK; /* and return success */
|
|
}
|
|
|
|
else /* otherwise there are extraneous characters */
|
|
return SCPE_2MARG; /* so report that there are too many arguments */
|
|
|
|
else if (cptr == NULL || *cptr == '\0') /* otherwise if the EXEC request supplies no arguments */
|
|
return SCPE_MISVAL; /* then report a missing value */
|
|
|
|
else { /* otherwise at least one argument is present */
|
|
cptr = get_glyph (cptr, gbuf, ';'); /* so get the match argument */
|
|
|
|
if (sim_switches & SWMASK ('O')) /* if an octal override is present */
|
|
radix = 8; /* then parse the value in base 8 */
|
|
else if (sim_switches & SWMASK ('D')) /* otherwise if a decimal override is present */
|
|
radix = 10; /* then parse the value in base 10 */
|
|
else if (sim_switches & SWMASK ('H')) /* otherwise if a hex override is present */
|
|
radix = 16; /* then parse the value in base 16 */
|
|
else /* otherwise */
|
|
radix = cpu_dev.dradix; /* use the current CPU data radix */
|
|
|
|
match = (uint32) get_uint (gbuf, radix, D16_UMAX, &status); /* parse the match value */
|
|
|
|
if (status != SCPE_OK) /* if a parsing error occurred */
|
|
return status; /* then return the error status */
|
|
|
|
else if (*cptr == '\0') { /* otherwise if no more characters are present */
|
|
exec_match = match; /* then set the match value */
|
|
exec_mask = D16_MASK; /* and default the mask value */
|
|
return SCPE_OK; /* and return success */
|
|
}
|
|
|
|
else { /* otherwise another argument is present */
|
|
cptr = get_glyph (cptr, gbuf, ';'); /* so get the mask argument */
|
|
|
|
mask = (uint32) get_uint (gbuf, radix, D16_UMAX, &status); /* parse the mask value */
|
|
|
|
if (status != SCPE_OK) /* if a parsing error occurred */
|
|
return status; /* then return the error status */
|
|
|
|
else if (*cptr == '\0') /* if no more characters are present */
|
|
if (mask == 0) /* then if the mask value is zero */
|
|
return SCPE_ARG; /* then the match will never succeed */
|
|
|
|
else { /* otherwise */
|
|
exec_match = match; /* set the match value */
|
|
exec_mask = mask; /* and the mask value */
|
|
return SCPE_OK; /* and return success */
|
|
}
|
|
|
|
else /* otherwise extraneous characters are present */
|
|
return SCPE_2MARG; /* so report that there are too many arguments */
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
/* Show the CPU simulation stop conditions.
|
|
|
|
This display routine is called to show the set of CPU stop conditions or the
|
|
indirect chain length limit. The "st" parameter is the open output stream,
|
|
and "val" is 1 to show the stops and 2 to show the indirect limit. The other
|
|
parameters are not used.
|
|
|
|
To show stops, the routine searches through the stop table for status
|
|
variables that are set to values other than SCPE_OK. For each one it finds,
|
|
the routine prints the corresponding stop name. If none are found, it
|
|
reports that all stops are disabled.
|
|
|
|
This routine services an extended modifier entry, so it must add the trailing
|
|
newline to the output before returning.
|
|
*/
|
|
|
|
static t_stat show_stops (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
|
{
|
|
uint32 stop;
|
|
t_bool need_spacer = FALSE;
|
|
|
|
if (val == 2) /* if the indirect limit is requested */
|
|
fprintf (st, "Limit=%d\n", indirect_limit); /* then show it */
|
|
|
|
else { /* otherwise show the enabled stops */
|
|
for (stop = 0; cpu_stop [stop].name != NULL; stop++) /* loop through the set of stops in the table */
|
|
if (*cpu_stop [stop].status != SCPE_OK) { /* if the current stop is enabled */
|
|
if (need_spacer) /* then if a spacer is needed */
|
|
fputc (';', st); /* then add it first */
|
|
else /* otherwise this is the first one reported */
|
|
fputs ("Stop=", st); /* so print the report label */
|
|
|
|
fputs (cpu_stop [stop].name, st); /* report the stop name */
|
|
|
|
need_spacer = TRUE; /* a spacer will be needed next time */
|
|
}
|
|
|
|
if (need_spacer) /* if at least one simulation stop was enabled */
|
|
fputc ('\n', st); /* then add the required trailing newline */
|
|
else /* otherwise no enabled stops were found */
|
|
fputs ("Stops disabled\n", st); /* so report that all are disabled */
|
|
}
|
|
|
|
return SCPE_OK; /* report the success of the display */
|
|
}
|
|
|
|
|
|
/* Display the CPU model and optional loader status.
|
|
|
|
Loader status is displayed for 21xx models and suppressed for 1000 models.
|
|
*/
|
|
|
|
static t_stat show_model (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
|
{
|
|
fputs ((const char *) desc, st); /* output the CPU model name */
|
|
|
|
if (!(cpu_configuration & CPU_1000)) /* if the CPU is a 2100 or 21xx */
|
|
if (mem_end < mem_size) /* then if the loader area is non-existent */
|
|
fputs (", loader disabled", st); /* then report that the loader is disabled */
|
|
else /* otherwise */
|
|
fputs (", loader enabled", st); /* report that it is enabled */
|
|
|
|
return SCPE_OK;
|
|
}
|
|
|
|
|
|
/* Show the set of installed loader ROMs.
|
|
|
|
This display routine is called to show the set of installed loader ROMs in
|
|
the four available sockets of a 1000-series CPU. On entry, the "st"
|
|
parameter is the open output stream. The other parameters are not used.
|
|
|
|
The routine prints a table of ROMs in this format:
|
|
|
|
Socket Device ROM Description
|
|
------ ------ -----------------------------------------
|
|
0 PTR 12992K Paper Tape Loader
|
|
1 DA 12992H 7906H/7920H/7925H/9895 Disc Loader
|
|
2 MSC 12992D 7970 Magnetic Tape Loader
|
|
3 (none) (empty socket)
|
|
|
|
If a given socket contains a ROM, the associated device name, HP part number,
|
|
and description of the loader ROM are printed. Loader ROMs may be displayed
|
|
only if the current CPU model is a 1000-series machine; if it is not, the
|
|
command will be rejected.
|
|
|
|
This routine services an extended modifier entry, so it must add the trailing
|
|
newline to the output before returning.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. SCP does not honor the status return from display routines, so we must
|
|
explicitly print the rejection error message if the routine is called for
|
|
a non-1000 CPU.
|
|
|
|
2. sim_dname is called instead of using dptr->name directly to ensure that
|
|
we pick up assigned logical device names.
|
|
*/
|
|
|
|
static t_stat show_roms (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
|
{
|
|
const char *cname, *dname;
|
|
DIB *dibptr;
|
|
uint32 socket;
|
|
|
|
if (!(cpu_configuration & CPU_1000)) { /* if the CPU is not a 1000-series unit */
|
|
fputs (sim_error_text (SCPE_NOFNC), st); /* then print the rejection message */
|
|
fputc ('\n', st); /* and add a line feed */
|
|
|
|
return SCPE_NOFNC; /* reject the command */
|
|
}
|
|
|
|
fputc ('\n', st); /* skip a line */
|
|
fputs ("Socket Device ROM Description\n", st); /* and print */
|
|
fputs ("------ ------ " /* the table header */
|
|
"-----------------------------------------\n", st);
|
|
|
|
for (socket = 0; socket < 4; socket++) /* loop through the sockets */
|
|
if (loader_rom [socket] == NULL) /* if the socket is empty */
|
|
fprintf (st, " %u (none) (empty socket)\n", /* then report it as such */
|
|
socket);
|
|
|
|
else { /* otherwise the socket is occupied */
|
|
dname = sim_dname (loader_rom [socket]); /* so get the device name */
|
|
dibptr = (DIB *) loader_rom [socket]->ctxt; /* and a pointer to that device's DIB */
|
|
cname = dibptr->rom_description; /* to get the ROM description */
|
|
|
|
if (cname == NULL) /* if there is no description */
|
|
cname = ""; /* then use a null string */
|
|
|
|
fprintf (st, " %u %-4s %s\n", /* print the ROM information */
|
|
socket, dname, cname);
|
|
}
|
|
|
|
return SCPE_OK; /* return success status */
|
|
}
|
|
|
|
|
|
/* Show the currently configured I/O card cage.
|
|
|
|
This display routine is called to show the set of interfaces installed in the
|
|
I/O card cage. On entry, the "st" parameter is the open output stream. The
|
|
other parameters are not used.
|
|
|
|
The routine prints the installed I/O cards in this format:
|
|
|
|
SC Device Interface Description
|
|
-- ------ ---------------------------------------------------------------
|
|
10 PTR 12597A-002 Tape Reader Interface
|
|
11 TTY 12531C Buffered Teleprinter Interface
|
|
12 PTP 12597A-005 Tape Punch Interface
|
|
13 TBG 12539C Time Base Generator Interface
|
|
14 (none) 12777A Priority Jumper Card
|
|
15 LPT 12845B Line Printer Interface
|
|
[...]
|
|
|
|
If a given I/O slot contains an interface card, the associated device name,
|
|
HP part number, and description of the interface are printed. If the slot is
|
|
empty, it is displayed as though a 12777A Priority Jumper Card is installed.
|
|
The list terminates with the last occupied I/O slot.
|
|
|
|
If select code conflicts exist, the invalid assignments are printed before
|
|
the interface list, and the corresponding entries in the list are flagged.
|
|
For example:
|
|
|
|
Select code 13 conflict (TBG and LPS)
|
|
|
|
SC Device Interface Description
|
|
-- ------ ------------------------------------------------------------------
|
|
10 PTR 12597A-002 Tape Reader Interface
|
|
11 TTY 12531C Buffered Teleprinter Interface
|
|
12 PTP 12597A-005 Tape Punch Interface
|
|
13 --- (multiple assignments)
|
|
14 (none) 12777A Priority Jumper Card
|
|
15 LPT 12845B Line Printer Interface
|
|
[...]
|
|
|
|
This routine services an extended modifier entry, so it must add the trailing
|
|
newline to the output before returning.
|
|
*/
|
|
|
|
static t_stat show_cage (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
|
{
|
|
const char *cname, *dname;
|
|
uint32 sc, last_sc;
|
|
|
|
fputc ('\n', st); /* skip a line */
|
|
|
|
if (initialize_io (FALSE) == FALSE) /* set up the I/O tables; if a conflict was reported */
|
|
fputc ('\n', st); /* then separate it from the interface list */
|
|
|
|
fputs ("SC Device Interface Description\n", st); /* print */
|
|
fputs ("-- ------ " /* the table header */
|
|
"------------------------------------------------------------------\n", st);
|
|
|
|
for (last_sc = SC_MAX; last_sc > SC_VAR; last_sc--) /* find the last occupied I/O slot */
|
|
if (iot [last_sc].devptr != NULL /* with an assigned device */
|
|
&& !(iot [last_sc].devptr->flags & DEV_DIS)) /* that is currently enabled */
|
|
break;
|
|
|
|
for (sc = SC_VAR; sc <= last_sc; sc++) /* loop through the select codes */
|
|
if (iot [sc].devptr == NULL || iot [sc].devptr->flags & DEV_DIS) /* if the slot is unassigned or disabled */
|
|
fprintf (st, "%02o (none) 12777A Priority Jumper Card\n", sc); /* then report a jumper card */
|
|
|
|
else if (iot [sc].references > 1) /* otherwise if a conflict exists */
|
|
fprintf (st, "%02o --- (multiple assignments)\n", sc); /* then report the multiple assignment */
|
|
|
|
else { /* otherwise the slot is valid */
|
|
dname = sim_dname (iot [sc].devptr); /* so get the device name */
|
|
cname = iot [sc].dibptr->card_description; /* and interface card description */
|
|
|
|
if (cname == NULL) /* if there is no description */
|
|
cname = ""; /* then use a null string */
|
|
|
|
fprintf (st, "%02o %-4s %s\n", sc, dname, cname); /* report the interface in the slot */
|
|
}
|
|
|
|
return SCPE_OK; /* return success status */
|
|
}
|
|
|
|
|
|
/* Show the instruction execution trace criteria.
|
|
|
|
This display routine is called to show the criteria that select instruction
|
|
execution tracing. The "st" parameter is the open output stream. The other
|
|
parameters are not used.
|
|
|
|
This routine services an extended modifier entry, so it must add the trailing
|
|
newline to the output before returning.
|
|
*/
|
|
|
|
static t_stat show_exec (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
|
{
|
|
uint32 radix;
|
|
|
|
if (exec_mask == 0) /* if the instruction is entirely masked */
|
|
fputs ("Execution trace disabled\n", st); /* then report that matching is disabled */
|
|
|
|
else { /* otherwise */
|
|
if (sim_switches & SWMASK ('O')) /* if an octal override is present */
|
|
radix = 8; /* then print the value in base 8 */
|
|
else if (sim_switches & SWMASK ('D')) /* otherwise if a decimal override is present */
|
|
radix = 10; /* then print the value in base 10 */
|
|
else if (sim_switches & SWMASK ('H')) /* otherwise if a hex override is present */
|
|
radix = 16; /* then print the value in base 16 */
|
|
else /* otherwise */
|
|
radix = cpu_dev.dradix; /* use the current CPU data radix */
|
|
|
|
fputs ("Execution trace match = ", st); /* print the label */
|
|
fprint_val (st, exec_match, radix, cpu_dev.dwidth, PV_RZRO); /* and the match value */
|
|
|
|
fputs (", mask = ", st); /* print a separator */
|
|
fprint_val (st, exec_mask, radix, cpu_dev.dwidth, PV_RZRO); /* and the mask value */
|
|
|
|
fputc ('\n', st); /* tie off the line */
|
|
}
|
|
|
|
return SCPE_OK; /* report the success of the display */
|
|
}
|
|
|
|
|
|
/* Show the current CPU simulation speed.
|
|
|
|
This display routine is called to show the current simulation speed. The
|
|
"st" parameter is the open output stream. The other parameters are not used.
|
|
|
|
The CPU speed, expressed as a multiple of the hardware speed, is calculated
|
|
by the console poll service routine. It is only representative when the CPU
|
|
is not idling.
|
|
*/
|
|
|
|
static t_stat show_speed (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
|
|
{
|
|
fprintf (st, "Simulation speed = %ux\n", cpu_speed); /* display the current CPU speed */
|
|
return SCPE_OK; /* and report success */
|
|
}
|
|
|
|
|
|
|
|
/* CPU local utility routine declarations */
|
|
|
|
|
|
/* Execute one machine instruction.
|
|
|
|
This routine executes the CPU instruction present in the IR. The CPU state
|
|
(registers, memory, interrupt status) is modified as necessary, and the
|
|
routine return SCPE_OK if the instruction executed successfully. Any other
|
|
status indicates that execution should cease, and control should return to
|
|
the simulator console. For example, a programmed HALT instruction returns
|
|
STOP_HALT status.
|
|
|
|
This routine implements the main instruction dispatcher. Instructions
|
|
corresponding to the MRG, SRG, and ASG are executed inline. IOG, EAG, and
|
|
UIG instructions are executed by external handlers.
|
|
|
|
The JMP instruction executor handles CPU idling. The 21xx/1000 CPUs have no
|
|
"wait for interrupt" instruction. Idling in HP operating systems consists of
|
|
sitting in "idle loops" that end with JMP instructions. We test for certain
|
|
known patterns when a JMP instruction is executed to decide if the simulator
|
|
should idle.
|
|
|
|
Idling must not occur if an interrupt is pending. As mentioned before, the
|
|
CPU will defer pending interrupts when certain instructions are executed. OS
|
|
interrupt handlers exit via such deferring instructions. If there is a
|
|
pending interrupt when the OS is otherwise idle, the idle loop will execute
|
|
one instruction before reentering the interrupt handler. If we call
|
|
sim_idle() in this case, we will lose interrupts.
|
|
|
|
Consider the situation in RTE. Under simulation, the TTY and TBG events are
|
|
co-scheduled, so their event routines are called sequentially during a single
|
|
"sim_process_event" call. If the TTY has received an input character from
|
|
the console poll, then both devices are ready to interrupt. Assume that the
|
|
TTY has priority. When the TTY interrupts, $CIC in RTE is entered. The TBG
|
|
interrupt is held off through the JSB $CIC,I / JMP $CIC0,I / SFS 0,C
|
|
instruction entry sequence, which defers interrupts until the interrupt
|
|
system is turned off. When $CIC returns via $IRT, one instruction of the
|
|
idle loop is executed, even though the TBG interrupt is still pending,
|
|
because the UJP instruction used to return also defers interrupts.
|
|
|
|
If "sim_idle" is called at this point, the simulator will sleep when it
|
|
should be handling the pending TBG interrupt. When it awakes, TTY expiration
|
|
will be moved forward to the next instruction. The still-pending TBG
|
|
interrupt will then be recognized, and $CIC will be entered. But the TTY and
|
|
TBG will then expire and attempt to interrupt again, although they are
|
|
deferred by the $CIC entry sequence. This causes the second TBG interrupt to
|
|
be missed, as processing of the first one is just now being started.
|
|
|
|
Similarly, at the end of the TBG handling, the TTY interrupt is still
|
|
pending. When $IRT returns to the idle loop, "sim_idle" would be called
|
|
again, so the TTY and then TBG interrupt a third time. Because the second
|
|
TTY interrupt is still pending, $CIC is entered, but the third TTY interrupt
|
|
is lost.
|
|
|
|
We solve this problem by testing for a pending interrupt before calling
|
|
"sim_idle". The system isn't really quiescent if it is just about to handle
|
|
an interrupt.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. Instruction decoding is based on the HP 1000, which does a 256-way branch
|
|
on the upper eight bits of the instruction, as follows:
|
|
|
|
15 14 13 12 11 10 9 8 Instruction Group
|
|
-- -- -- -- -- -- -- -- -------------------------------------------
|
|
x n n n x x x x Memory Reference (n n n not equal to 0 0 0)
|
|
0 0 0 0 x 0 x x Shift-Rotate
|
|
0 0 0 0 x 1 x x Alter-Skip
|
|
1 0 0 0 x 1 x x I/O
|
|
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 Instruction 0 (A/B is set)
|
|
1 0 0 0 x 0 1 1 Extended Instruction 1 (A/B is ignored)
|
|
|
|
2. JSB is tricky. It is possible to generate both an MP and a DM violation
|
|
simultaneously, as the MP and MEM cards validate in parallel. Consider a
|
|
JSB to a location under the MP fence and on a write-protected page. This
|
|
situation must be reported as a DM violation, because it has priority
|
|
(SFS 5 and SFC 5 check only the MEVFF, which sets independently of the MP
|
|
fence violation). Under simulation, this means that DM violations must
|
|
be checked, and the MEVFF must be set, before an MP abort is taken. This
|
|
is done by the "mp_check_jmp" routine.
|
|
|
|
3. Although MR (and TR) will be changed by reads of an indirect chain, the
|
|
idle loop JMP will be direct, and so MR will contain the correct value
|
|
for the "idle loop omitted" trace message.
|
|
|
|
4. The Alter-Skip Group RSS micro-op reverses the skip sense of the SEZ,
|
|
SSA/SSB, SLA/SLB, and SZA/SZB micro-op tests. Normally, the instruction
|
|
skips if any test is true. However, the specific combination of SSA/SSB,
|
|
SLA/SLB, and RSS micro-ops causes a skip if BOTH of the skip cases are
|
|
true, i.e., if both the MSB and LSB of the register value are ones. We
|
|
handle this as a special case, because without RSS, the instruction skips
|
|
if EITHER the MSB or LSB is zero. The other reversed skip cases (SEZ,RSS
|
|
and SZA,RSS/SZB,RSS) are independent.
|
|
*/
|
|
|
|
static t_stat machine_instruction (t_bool int_ack, uint32 *idle_save)
|
|
{
|
|
uint32 ab_selector, result, skip;
|
|
HP_WORD data;
|
|
t_bool rss;
|
|
t_stat status = SCPE_OK;
|
|
|
|
switch (UPPER_BYTE (IR)) { /* dispatch on bits 15-8 of the instruction */
|
|
|
|
/* Memory Reference Group */
|
|
|
|
case 0020: case 0021: case 0022: case 0023: /* AND */
|
|
case 0024: case 0025: case 0026: case 0027:
|
|
case 0220: case 0221: case 0222: case 0223: /* AND,I */
|
|
case 0224: case 0225: case 0226: case 0227:
|
|
status = mrg_address (); /* get the memory referemce address */
|
|
|
|
if (status == SCPE_OK) /* if the address resolved */
|
|
AR = AR & ReadW (MR); /* then AND the accumulator and memory */
|
|
break;
|
|
|
|
|
|
case 0230: case 0231: case 0232: case 0233: /* JSB,I */
|
|
case 0234: case 0235: case 0236: case 0237:
|
|
cpu_interrupt_enable = CLEAR; /* defer interrupts */
|
|
|
|
/* fall through into the JSB case */
|
|
|
|
case 0030: case 0031: case 0032: case 0033: /* JSB */
|
|
case 0034: case 0035: case 0036: case 0037:
|
|
status = mrg_address (); /* get the memory referemce address */
|
|
|
|
if (status == SCPE_OK) { /* if the address resolved */
|
|
mp_check_jsb (MR); /* then validate the jump address */
|
|
|
|
WriteW (MR, PR); /* store P into the target memory address */
|
|
|
|
PCQ_ENTRY; /* save P in the queue */
|
|
PR = MR + 1 & LA_MASK; /* and jump to the word after the target address */
|
|
}
|
|
break;
|
|
|
|
|
|
case 0040: case 0041: case 0042: case 0043: /* XOR */
|
|
case 0044: case 0045: case 0046: case 0047:
|
|
case 0240: case 0241: case 0242: case 0243: /* XOR,I */
|
|
case 0244: case 0245: case 0246: case 0247:
|
|
status = mrg_address (); /* get the memory referemce address */
|
|
|
|
if (status == SCPE_OK) /* if the address resolved */
|
|
AR = AR ^ ReadW (MR); /* then XOR the accumulator and memory */
|
|
break;
|
|
|
|
|
|
case 0250: case 0251: case 0252: case 0253: /* JMP,I */
|
|
case 0254: case 0255: case 0256: case 0257:
|
|
cpu_interrupt_enable = CLEAR; /* defer interrupts */
|
|
|
|
/* fall through into the JMP case */
|
|
|
|
case 0050: case 0051: case 0052: case 0053: /* JMP */
|
|
case 0054: case 0055: case 0056: case 0057:
|
|
status = mrg_address (); /* get the memory referemce address */
|
|
|
|
if (status != SCPE_OK) /* if the address failed to resolve */
|
|
break; /* then abort execution */
|
|
|
|
mp_check_jmp (MR, 0); /* validate the jump address */
|
|
|
|
if (sim_idle_enab && interrupt_request == 0 /* if idle is enabled and no interrupt is pending */
|
|
&& mem_is_idle_loop ()) { /* and execution is in the DOS or RTE idle loop */
|
|
tprintf (cpu_dev, cpu_dev.dctrl,
|
|
DMS_FORMAT "idle loop execution omitted\n",
|
|
meu_indicator, meu_page, MR, IR);
|
|
|
|
if (cpu_dev.dctrl != 0) { /* then if tracing is enabled */
|
|
*idle_save = cpu_dev.dctrl; /* then save the current trace flag set */
|
|
cpu_dev.dctrl = 0; /* and turn off tracing for the idle loop */
|
|
}
|
|
|
|
sim_idle (TMR_POLL, FALSE); /* idle the simulator */
|
|
}
|
|
|
|
PCQ_ENTRY; /* save P in the queue */
|
|
PR = MR; /* and jump to the target address */
|
|
break;
|
|
|
|
|
|
case 0060: case 0061: case 0062: case 0063: /* IOR */
|
|
case 0064: case 0065: case 0066: case 0067:
|
|
case 0260: case 0261: case 0262: case 0263: /* IOR,I */
|
|
case 0264: case 0265: case 0266: case 0267:
|
|
status = mrg_address (); /* get the memory referemce address */
|
|
|
|
if (status == SCPE_OK) /* if the address resolved */
|
|
AR = AR | ReadW (MR); /* then OR the accumulator and memory */
|
|
break;
|
|
|
|
|
|
case 0070: case 0071: case 0072: case 0073: /* ISZ */
|
|
case 0074: case 0075: case 0076: case 0077:
|
|
case 0270: case 0271: case 0272: case 0273: /* ISZ,I */
|
|
case 0274: case 0275: case 0276: case 0277:
|
|
status = mrg_address (); /* get the memory referemce address */
|
|
|
|
if (status == SCPE_OK) { /* if the address resolved */
|
|
data = ReadW (MR) + 1 & D16_MASK; /* then increment the memory word */
|
|
WriteW (MR, data); /* and write it back */
|
|
|
|
if (data == 0) /* if the value rolled over to zero */
|
|
PR = PR + 1 & LA_MASK; /* then increment P */
|
|
}
|
|
break;
|
|
|
|
|
|
case 0100: case 0101: case 0102: case 0103: /* ADA */
|
|
case 0104: case 0105: case 0106: case 0107:
|
|
case 0300: case 0301: case 0302: case 0303: /* ADA,I */
|
|
case 0304: case 0305: case 0306: case 0307:
|
|
status = mrg_address (); /* get the memory referemce address */
|
|
|
|
if (status == SCPE_OK) { /* if the address resolved */
|
|
data = ReadW (MR); /* then get the target word */
|
|
result = AR + data; /* and add the accumulator to memory */
|
|
|
|
if (result > D16_UMAX) /* if the result overflowed */
|
|
E = 1; /* then set the Extend register */
|
|
|
|
if (~(AR ^ data) & (AR ^ result) & D16_SIGN) /* if the sign of the result differs from the signs */
|
|
O = 1; /* of the operands, then set the Overflow register */
|
|
|
|
AR = result & R_MASK; /* store the sum into the accumulator */
|
|
}
|
|
break;
|
|
|
|
|
|
case 0110: case 0111: case 0112: case 0113: /* ADB */
|
|
case 0114: case 0115: case 0116: case 0117:
|
|
case 0310: case 0311: case 0312: case 0313: /* ADB,I */
|
|
case 0314: case 0315: case 0316: case 0317:
|
|
status = mrg_address (); /* get the memory referemce address */
|
|
|
|
if (status == SCPE_OK) { /* if the address resolved */
|
|
data = ReadW (MR); /* then get the target word */
|
|
result = BR + data; /* and add the accumulator to memory */
|
|
|
|
if (result > D16_UMAX) /* if the result overflowed */
|
|
E = 1; /* then set the Extend register */
|
|
|
|
if (~(BR ^ data) & (BR ^ result) & D16_SIGN) /* if the sign of the result differs from the signs */
|
|
O = 1; /* of the operands, then set the Overflow register */
|
|
|
|
BR = result & R_MASK; /* store the sum into the accumulator */
|
|
}
|
|
break;
|
|
|
|
|
|
case 0120: case 0121: case 0122: case 0123: /* CPA */
|
|
case 0124: case 0125: case 0126: case 0127:
|
|
case 0320: case 0321: case 0322: case 0323: /* CPA,I */
|
|
case 0324: case 0325: case 0326: case 0327:
|
|
status = mrg_address (); /* get the memory referemce address */
|
|
|
|
if (status == SCPE_OK) /* if the address resolved */
|
|
if (AR != ReadW (MR)) /* then if the accumulator and memory differ */
|
|
PR = PR + 1 & LA_MASK; /* then increment P */
|
|
break;
|
|
|
|
|
|
case 0130: case 0131: case 0132: case 0133: /* CPB */
|
|
case 0134: case 0135: case 0136: case 0137:
|
|
case 0330: case 0331: case 0332: case 0333: /* CPB,I */
|
|
case 0334: case 0335: case 0336: case 0337:
|
|
status = mrg_address (); /* get the memory referemce address */
|
|
|
|
if (status == SCPE_OK) /* if the address resolved */
|
|
if (BR != ReadW (MR)) /* then if the accumulator and memory differ */
|
|
PR = PR + 1 & LA_MASK; /* then increment P */
|
|
break;
|
|
|
|
|
|
case 0140: case 0141: case 0142: case 0143: /* LDA */
|
|
case 0144: case 0145: case 0146: case 0147:
|
|
case 0340: case 0341: case 0342: case 0343: /* LDA,I */
|
|
case 0344: case 0345: case 0346: case 0347:
|
|
status = mrg_address (); /* get the memory referemce address */
|
|
|
|
if (status == SCPE_OK) /* if the address resolved */
|
|
AR = ReadW (MR); /* then load the accumulator from memory */
|
|
break;
|
|
|
|
|
|
case 0150: case 0151: case 0152: case 0153: /* LDB */
|
|
case 0154: case 0155: case 0156: case 0157:
|
|
case 0350: case 0351: case 0352: case 0353: /* LDB,I */
|
|
case 0354: case 0355: case 0356: case 0357:
|
|
status = mrg_address (); /* get the memory referemce address */
|
|
|
|
if (status == SCPE_OK) /* if the address resolved */
|
|
BR = ReadW (MR); /* then load the accumulator from memory */
|
|
break;
|
|
|
|
|
|
case 0160: case 0161: case 0162: case 0163: /* STA */
|
|
case 0164: case 0165: case 0166: case 0167:
|
|
case 0360: case 0361: case 0362: case 0363: /* STA,I */
|
|
case 0364: case 0365: case 0366: case 0367:
|
|
status = mrg_address (); /* get the memory referemce address */
|
|
|
|
if (status == SCPE_OK) /* if the address resolved */
|
|
WriteW (MR, AR); /* then write the accumulator to memory */
|
|
break;
|
|
|
|
|
|
case 0170: case 0171: case 0172: case 0173: /* STB */
|
|
case 0174: case 0175: case 0176: case 0177:
|
|
case 0370: case 0371: case 0372: case 0373: /* STB,I */
|
|
case 0374: case 0375: case 0376: case 0377:
|
|
status = mrg_address (); /* get the memory referemce address */
|
|
|
|
if (status == SCPE_OK) /* if the address resolved */
|
|
WriteW (MR, BR); /* then write the accumulator to memory */
|
|
break;
|
|
|
|
|
|
/* Alter/Skip Group */
|
|
|
|
case 0004: case 0005: case 0006: case 0007: /* ASG */
|
|
case 0014: case 0015: case 0016: case 0017:
|
|
skip = 0; /* assume that no skip is needed */
|
|
|
|
rss = (IR & IR_RSS) != 0; /* get the Reverse Skip Sense flag */
|
|
|
|
ab_selector = AB_SELECT (IR); /* get the A/B register selector */
|
|
data = ABREG [ab_selector]; /* and the register data */
|
|
|
|
if (IR & IR_CLx) /* if the CLA/CLB micro-op is enabled */
|
|
data = 0; /* then clear the value */
|
|
|
|
if (IR & IR_CMx) /* if the CMA/CMB micro-op is enabled */
|
|
data = data ^ D16_MASK; /* then complement the value */
|
|
|
|
if (IR & IR_SEZ && (E == 0) ^ rss) /* if SEZ[,RSS] is enabled and E is clear [set] */
|
|
skip = 1; /* then skip the next instruction */
|
|
|
|
if (IR & IR_CLE) /* if the CLE micro-op is enabled */
|
|
E = 0; /* then clear E */
|
|
|
|
if (IR & IR_CME) /* if the CME micro-op is enabled */
|
|
E = E ^ LSB; /* then complement E */
|
|
|
|
if ((IR & IR_SSx_SLx_RSS) == IR_SSx_SLx_RSS) { /* if the SSx, SLx, and RSS micro-ops are enabled together */
|
|
if ((data & D16_SIGN_LSB) == D16_SIGN_LSB) /* then if both sign and least-significant bits are set */
|
|
skip = 1; /* then skip the next instruction */
|
|
}
|
|
|
|
else { /* otherwise */
|
|
if (IR & IR_SSx && !(data & D16_SIGN) ^ rss) /* if SSx[,RSS] is enabled and the MSB is clear [set] */
|
|
skip = 1; /* then skip the next instruction */
|
|
|
|
if (IR & IR_SLx && !(data & LSB) ^ rss) /* if SLx[,RSS] is enabled and the LSB is clear [set] */
|
|
skip = 1; /* then skip the next instruction */
|
|
}
|
|
|
|
if (IR & IR_INx) { /* if the INA/INB micro-op is enabled */
|
|
data = data + 1 & D16_MASK; /* then increment the value */
|
|
|
|
if (data == 0) /* if the value wrapped around to zero */
|
|
E = 1; /* then set the Extend register */
|
|
|
|
else if (data == D16_SIGN) /* otherwise if the value overflowed into the sign bit */
|
|
O = 1; /* then set the Overflow register */
|
|
}
|
|
|
|
if (IR & IR_SZx && (data == 0) ^ rss) /* if SZx[,RSS] is enabled and the value is zero [non-zero] */
|
|
skip = 1; /* then skip the next instruction */
|
|
|
|
if ((IR & IR_ALL_SKIPS) == IR_RSS) /* if RSS is present without any other skip micro-ops */
|
|
skip = 1; /* then skip the next instruction unconditionally */
|
|
|
|
ABREG [ab_selector] = data; /* store the result in the selected register */
|
|
PR = PR + skip & LA_MASK; /* and skip the next instruction if indicated */
|
|
break;
|
|
|
|
|
|
/* Shift/Rotate Group */
|
|
|
|
case 0000: case 0001: case 0002: case 0003: /* SRG */
|
|
case 0010: case 0011: case 0012: case 0013:
|
|
ab_selector = AB_SELECT (IR); /* get the A/B register selector */
|
|
data = ABREG [ab_selector]; /* and the register data */
|
|
|
|
data = srg_uop (data, SRG1 (IR)); /* do the first shift */
|
|
|
|
if (IR & SRG_CLE) /* if the CLE micro-op is enabled */
|
|
E = 0; /* then clear E */
|
|
|
|
if (IR & SRG_SLx && (data & LSB) == 0) /* if SLx is enabled and the LSB is clear */
|
|
PR = PR + 1 & LA_MASK; /* then skip the next instruction */
|
|
|
|
ABREG [ab_selector] = srg_uop (data, SRG2 (IR)); /* do the second shift and set the accumulator */
|
|
break;
|
|
|
|
|
|
/* I/O Group */
|
|
|
|
case 0204: case 0205: case 0206: case 0207: /* IOG */
|
|
case 0214: case 0215: case 0216: case 0217:
|
|
status = cpu_iog (IR); /* execute the I/O instruction */
|
|
break;
|
|
|
|
|
|
/* Extended Arithmetic Group */
|
|
|
|
case 0200: /* EAU group 0 */
|
|
case 0201: /* DIV */
|
|
case 0202: /* EAU group 2 */
|
|
case 0210: /* DLD */
|
|
case 0211: /* DST */
|
|
status = cpu_eau (); /* execute the extended arithmetic instruction */
|
|
break;
|
|
|
|
|
|
/* User Instruction Group */
|
|
|
|
case 0212: /* UIG 0 */
|
|
status = cpu_uig_0 (interrupt_request, int_ack); /* execute the user instruction opcode */
|
|
break;
|
|
|
|
|
|
case 0203:
|
|
case 0213: /* UIG 1 */
|
|
status = cpu_uig_1 (interrupt_request); /* execute the user instruction opcode */
|
|
break;
|
|
} /* all cases are handled */
|
|
|
|
|
|
return status; /* return the execution status */
|
|
}
|
|
|
|
|
|
/* Get the effective address from an MRG instruction.
|
|
|
|
Memory references are contained in bits 15, 10, and 9-0 of instructions in
|
|
the Memory Reference Group. Bits 9-0 specify one of 1024 locations within
|
|
either the base page (if bit 10 is 0) or the current page (if bit 10 is 1).
|
|
If bit 15 is 0, the address is direct. If bit 15 is 1, then the address is
|
|
indirect and specifies the location containing the target address. That
|
|
address itself may be direct or indirect as indicated by bit 15, with bits
|
|
14-0 specifying a location within the 32K logical address space.
|
|
|
|
On entry, the instruction register (IR) contains the MRG instruction to
|
|
resolve, and the memory address register (MR) contains the address of the
|
|
instruction. This routine examines the instruction; if the address is
|
|
direct, then the full 15-bit address is returned in the MR register.
|
|
Otherwise, the indirect chain is followed until the address is direct, a
|
|
pending interrupt is recognized, or the length of the indirect address chain
|
|
exceeds the alllowable limit. The resulting direct address is returned in
|
|
the MR register.
|
|
*/
|
|
|
|
static t_stat mrg_address (void)
|
|
{
|
|
if (IR & IR_CP) /* if the instruction specifies the current page */
|
|
MR = IR & (IR_IND | IR_OFFSET) | MR & MR_PAGE; /* then merge the current page and the instruction offset */
|
|
else /* otherwise */
|
|
MR = IR & (IR_IND | IR_OFFSET); /* the offset is on the base page */
|
|
|
|
if (MR & IR_IND) /* if the address is indirect */
|
|
return cpu_resolve_indirects (TRUE); /* then resolve it to a direct address with interruptibility */
|
|
else /* otherwise */
|
|
return SCPE_OK; /* the address in MR is already direct */
|
|
}
|
|
|
|
|
|
/* Execute a Shift-Rotate Group micro-operation.
|
|
|
|
SRG instructions consist of two shift/rotate micro-operations plus a CLE and
|
|
a SLA/SLB micro-op. This routine implements the shift and rotate operation.
|
|
|
|
Each of the two shift-rotate operations has an enable bit that must be set to
|
|
enable the operation. If the bit is not set, the operation is a NOP, with
|
|
the exception that an ELA/ELB or ERA/ERB operation alters the E register (but
|
|
not the A/B register). We accommodate this by including the enable/disable
|
|
bit with the three-bit operation code and decode the disabled operations of
|
|
ELA/ELB and ERA/ERB separately from their enabled operations.
|
|
|
|
On entry, "value" is the value of the selected accumulator (A/B), and
|
|
"operation" is the micro-op and enable bit. The routine returns the updated
|
|
accumulator value and modifies the E register as indicated.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. The enable bit is located adjacent to the three-bit encoded operation for
|
|
the first shift-rotate micro-op, but it is spaced one bit away from the
|
|
encoded operation for the second micro-op. It is faster to decode
|
|
separate values for each location rather than move the second enable bit
|
|
adjacent to its encoded operation. The former imposes no time penalty;
|
|
the jump table for the "switch" statement is simply somewhat larger.
|
|
*/
|
|
|
|
static HP_WORD srg_uop (HP_WORD value, HP_WORD operation)
|
|
{
|
|
uint32 extend;
|
|
|
|
switch (operation) { /* dispatch on the micro operation */
|
|
|
|
case SRG1_EN | IR_xLS:
|
|
case SRG2_EN | IR_xLS: /* ALS/BLS */
|
|
return value & D16_SIGN | value << 1 & D16_SMAX; /* arithmetic left shift */
|
|
|
|
|
|
case SRG1_EN | IR_xRS:
|
|
case SRG2_EN | IR_xRS: /* ARS/BRS */
|
|
return value & D16_SIGN | value >> 1; /* arithmetic right shift */
|
|
|
|
|
|
case SRG1_EN | IR_RxL:
|
|
case SRG2_EN | IR_RxL: /* RAL/RBL */
|
|
return (value << 1 | value >> 15) & D16_MASK; /* rotate left */
|
|
|
|
|
|
case SRG1_EN | IR_RxR:
|
|
case SRG2_EN | IR_RxR: /* RAR/RBR */
|
|
return (value >> 1 | value << 15) & D16_MASK; /* rotate right */
|
|
|
|
|
|
case SRG1_EN | IR_xLR:
|
|
case SRG2_EN | IR_xLR: /* ALR/BLR */
|
|
return value << 1 & D16_SMAX; /* arithmetic left shift, clear sign */
|
|
|
|
|
|
case SRG_DIS | IR_ERx: /* disabled ERA/ERB */
|
|
E = value & LSB; /* rotate the LSB right into E */
|
|
return value; /* and return the original value */
|
|
|
|
|
|
case SRG1_EN | IR_ERx:
|
|
case SRG2_EN | IR_ERx: /* ERA/ERB */
|
|
extend = E; /* save the original E value */
|
|
E = value & LSB; /* rotate the LSB right into E */
|
|
return value >> 1 | (HP_WORD) extend << 15; /* and rotate right with E filling the MSB */
|
|
|
|
|
|
case SRG_DIS | IR_ELx: /* disabled ELA/ELB */
|
|
E = value >> 15 & LSB; /* rotate the MSB left into E */
|
|
return value; /* and return the original value */
|
|
|
|
|
|
case SRG1_EN | IR_ELx:
|
|
case SRG2_EN | IR_ELx: /* ELA/ELB */
|
|
extend = E; /* save the original E value */
|
|
E = value >> 15 & LSB; /* rotate the MSB left into E */
|
|
return (value << 1 | (HP_WORD) extend) & D16_MASK; /* and rotate left with E filling the LSB */
|
|
|
|
|
|
case SRG1_EN | IR_xLF:
|
|
case SRG2_EN | IR_xLF: /* ALF/BLF */
|
|
return (value << 4 | value >> 12) & D16_MASK; /* rotate left four */
|
|
|
|
|
|
default: /* all other (disabled) cases */
|
|
return value; /* return the original value */
|
|
}
|
|
}
|
|
|
|
|
|
/* Reenable interrupt recognition.
|
|
|
|
Certain instructions clear the "cpu_interrupt_enable" flag to defer
|
|
recognition of pending interrupts until the succeeding instruction completes.
|
|
However, the interrupt deferral rules differ for the 21xx vs. the 1000.
|
|
|
|
The 1000 always defers until the completion of the instruction following the
|
|
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 recognized.
|
|
|
|
In either case, if the interrupting device is the memory protect card, or if
|
|
the INT jumper is out on the 12892B MP card, then a pending interrupt is
|
|
always recognized, regardless of the "cpu_interrupt_enable" flag setting.
|
|
|
|
See the "Set Phase Logic Flowchart" for the transition from phase 1A to phase
|
|
1B, and "Section III Theory of Operation," "Control Section Detailed Theory"
|
|
division, "Phase Control Logic" subsection, "Phase 1B" paragraph (3-241) in
|
|
the Model 2100A Computer Installation and Maintenance Manual for details.
|
|
*/
|
|
|
|
static t_bool reenable_interrupts (void)
|
|
{
|
|
HP_WORD next_instruction;
|
|
|
|
if (!(cpu_configuration & CPU_1000)) { /* if the CPU is a 21xx model */
|
|
next_instruction = mem_fast_read (PR, Current_Map); /* then prefetch the next instruction */
|
|
|
|
if (MRGOP (next_instruction) /* if it is an MRG instruction */
|
|
&& (next_instruction & IR_MRG_I) != IR_JSB_I /* but not JSB,I */
|
|
&& (next_instruction & IR_MRG) != IR_JMP) /* and not JMP or JMP,I */
|
|
return TRUE; /* then reenable interrupts */
|
|
}
|
|
|
|
if (interrupt_request == MPPE || mp_reenable_interrupts ()) /* if MP is interrupting or the INT jumper is out */
|
|
return TRUE; /* then reenable interrupts */
|
|
else /* otherwise */
|
|
return FALSE; /* interrupts remain disabled */
|
|
}
|
|
|
|
|
|
|
|
/* I/O subsystem local utility routine declarations */
|
|
|
|
|
|
/* Initialize the I/O system.
|
|
|
|
This routine is called in the instruction prelude to set up the I/O access
|
|
table prior to beginning execution. The table is indexed by select code, and
|
|
each entry records pointers to the device and DIB structures assoicated with
|
|
that select code. This allows fast access to the device trace flags and to
|
|
the device interface routine by the I/O instruction executors, respectively.
|
|
|
|
As part of the device scan, the sizes of the largest device name and active
|
|
trace flag name among the devices enabled for tracing are accumulated for use
|
|
in aligning the trace statements.
|
|
|
|
The "is_executing" parameter indicates whether or not initialization is being
|
|
performed from the instruction execution prelude. If it is, the routine also
|
|
sets the priority holdoff and interrupt request bit vectors by asserting the
|
|
SIR signal to each enabled device interface routine. Sending SIR to all
|
|
devices will set the "interrupt_request" variable if an interrupt is pending,
|
|
so no explicit poll is needed after initialization.
|
|
|
|
After initializing the I/O access table, a check is made for device
|
|
conflicts. These occur if two or more devices are assigned to the same
|
|
select code.
|
|
|
|
Each select code must be unique among the enabled devices. This requirement
|
|
is checked as part of the instruction execution prelude; this allows the user
|
|
to exchange two select codes simply by setting each device to the other's
|
|
select code. If conflicts were enforced instead at the time the codes were
|
|
entered, the first device would have to be set to an unused select code
|
|
before the second could be set to the first device's code.
|
|
|
|
If any conflicts exist, the device table is scanned to find the DIBs whose
|
|
select codes match the conflicting values, and the device names associated
|
|
with the conflicts are printed.
|
|
|
|
This routine returns the success or failure of I/O initialization; failure
|
|
is reported if any select code conflicts exist.
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. If this routine is called from the instruction prelude, the console and
|
|
optional log file have already been put into "raw" output mode.
|
|
Therefore, newlines are not translated to the correct line ends on
|
|
systems that require it. Before reporting a conflict, "sim_ttcmd" is
|
|
called to restore the console and log file translation. This is OK
|
|
because a conflict will abort the run and return to the command line
|
|
anyway.
|
|
|
|
2. sim_dname is called instead of using dptr->name directly to ensure that
|
|
we pick up an assigned logical device name.
|
|
|
|
3. Only the names of active trace (debug) options are accumulated to produce
|
|
the most compact trace log. However, if the CPU device's EXEC option is
|
|
enabled, then all of the CPU option names are accumulated, as EXEC
|
|
enables all trace options for a given instruction or instruction class.
|
|
*/
|
|
|
|
static t_bool initialize_io (t_bool is_executing)
|
|
{
|
|
DEVICE *dptr;
|
|
DIB *dibptr;
|
|
const DEBTAB *tptr;
|
|
uint32 dev, sc, count;
|
|
size_t device_length, flag_length, device_size, flag_size;
|
|
t_bool is_conflict = FALSE;
|
|
|
|
interrupt_request_set [0] = interrupt_request_set [1] = 0; /* clear all interrupt requests */
|
|
priority_holdoff_set [0] = priority_holdoff_set [1] = 0; /* clear all priority inhibits */
|
|
|
|
device_size = 0; /* reset the device and flag name sizes */
|
|
flag_size = 0; /* to those of the devices actively tracing */
|
|
|
|
memset (&iot [2], 0, sizeof iot - 2 * sizeof iot [0]); /* clear the I/O pointer table */
|
|
|
|
for (dev = 0; sim_devices [dev] != NULL; dev++) { /* loop through all of the devices */
|
|
dptr = sim_devices [dev]; /* get a pointer to the device */
|
|
dibptr = (DIB *) dptr->ctxt; /* and to that device's DIB */
|
|
|
|
if (dibptr != NULL && !(dptr->flags & DEV_DIS)) { /* if the DIB exists and the device is enabled */
|
|
sc = dibptr->select_code;
|
|
iot [sc].devptr = dptr; /* then set the device pointer into the device table */
|
|
iot [sc].dibptr = dibptr; /* and set the DIB pointer into the dispatch table */
|
|
|
|
if (sc >= SC_VAR && ++iot [sc].references > 1) /* increment the count of references; if more than one */
|
|
is_conflict = TRUE; /* then a conflict occurs */
|
|
|
|
if (is_executing) /* if the CPU is executing instructions */
|
|
io_assert (dptr, ioa_SIR); /* then set the interrupt request state */
|
|
}
|
|
|
|
if (sim_deb && dptr->dctrl) { /* if tracing is active for this device */
|
|
device_length = strlen (sim_dname (dptr)); /* then get the length of the device name */
|
|
|
|
if (device_length > device_size) /* if it's greater than the current maximum */
|
|
device_size = device_length; /* then reset the size */
|
|
|
|
if (dptr->debflags) /* if the device has a trace flags table */
|
|
for (tptr = dptr->debflags; /* then scan the table */
|
|
tptr->name != NULL; tptr++)
|
|
if (dev == 0 && dptr->dctrl & TRACE_EXEC /* if the CPU device is tracing executions */
|
|
|| tptr->mask & dptr->dctrl) { /* or this trace option is active */
|
|
flag_length = strlen (tptr->name); /* then get the flag name length */
|
|
|
|
if (flag_length > flag_size) /* if it's greater than the current maximum */
|
|
flag_size = flag_length; /* then reset the size */
|
|
}
|
|
}
|
|
}
|
|
|
|
|
|
if (is_conflict) { /* if a conflict exists */
|
|
if (is_executing) /* then if execution has started */
|
|
sim_ttcmd (); /* then restore the console and log I/O mode */
|
|
|
|
for (sc = 0; sc <= SC_MAX; sc++) /* search the conflict table for the next conflict */
|
|
if (iot [sc].references > 1) { /* if a conflict is present for this value */
|
|
count = iot [sc].references; /* then get the number of conflicting devices */
|
|
|
|
cprintf ("Select code %o conflict (", sc); /* report the multiply-assigned select code */
|
|
|
|
for (dev = 0; sim_devices [dev] != NULL; dev++) { /* loop through all of the devices */
|
|
dptr = sim_devices [dev]; /* get a pointer to the device */
|
|
dibptr = (DIB *) dptr->ctxt; /* and to that device's DIB */
|
|
|
|
if (dibptr != NULL && !(dptr->flags & DEV_DIS) /* if the DIB exists and the device is enabled */
|
|
&& dibptr->select_code == sc) { /* to find the conflicting entries */
|
|
if (count < iot [sc].references) /* and report them to the console */
|
|
cputs (" and ");
|
|
|
|
cputs (sim_dname (dptr)); /* report the conflicting device name */
|
|
count = count - 1; /* and drop the count of remaining conflicts */
|
|
|
|
if (count == 0) /* if all devices have been reported */
|
|
break; /* then there's no need to look farther */
|
|
}
|
|
} /* loop until all conflicting devices are reported */
|
|
|
|
cputs (")\n"); /* tie off the line */
|
|
} /* and continue to look for other conflicting select codes */
|
|
|
|
return FALSE; /* report that initialization has failed */
|
|
}
|
|
|
|
else { /* otherwise no conflicts were found */
|
|
iot [PWR].devptr = &cpu_dev; /* for now, powerfail is always present */
|
|
iot [PWR].dibptr = &pwrf_dib; /* and is controlled by the CPU */
|
|
|
|
if (iot [DMA1].devptr) { /* if the first DMA channel is enabled */
|
|
iot [DMALT1] = iot [DMA1]; /* then set up */
|
|
iot [DMALT1].dibptr++; /* the secondary device handler */
|
|
}
|
|
|
|
if (iot [DMA2].devptr) { /* if the second DMA channel is enabled */
|
|
iot [DMALT2] = iot [DMA2]; /* then set up */
|
|
iot [DMALT2].dibptr++; /* the secondary device handler */
|
|
}
|
|
|
|
hp_initialize_trace (device_size, flag_size); /* initialize the trace routine */
|
|
return TRUE; /* and report that initialization has succeeded */
|
|
}
|
|
}
|