1076 lines
55 KiB
C
1076 lines
55 KiB
C
/* hp2100_cpu6.c: HP 1000 RTE-6/VM OS microcode simulator
|
|
|
|
Copyright (c) 2006-2018, J. David Bryan
|
|
|
|
Permission is hereby granted, free of charge, to any person obtaining a
|
|
copy of this software and associated documentation files (the "Software"),
|
|
to deal in the Software without restriction, including without limitation
|
|
the rights to use, copy, modify, merge, publish, distribute, sublicense,
|
|
and/or sell copies of the Software, and to permit persons to whom the
|
|
Software is furnished to do so, subject to the following conditions:
|
|
|
|
The above copyright notice and this permission notice shall be included in
|
|
all copies or substantial portions of the Software.
|
|
|
|
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL
|
|
THE AUTHOR BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
|
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
|
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
|
|
Except as contained in this notice, the name of the author shall not be
|
|
used in advertising or otherwise to promote the sale, use or other dealings
|
|
in this Software without prior written authorization from the author.
|
|
|
|
CPU6 RTE-6/VM Operating System instructions
|
|
|
|
02-Oct-18 JDB Replaced DMASK with D16_MASK or R_MASK as appropriate
|
|
30-Jul-18 JDB Replaced direct set of map mode with call to "meu_set_state"
|
|
14-Jun-18 JDB Renamed PRO to MP
|
|
22-Jul-17 JDB Renamed "intaddr" to CIR
|
|
10-Jul-17 JDB Renamed the global routine "iogrp" to "cpu_iog"
|
|
07-Jul-17 JDB Changed "iotrap" from uint32 to t_bool
|
|
27-Mar-17 JDB Expanded comments to describe instruction encoding
|
|
17-Jan-17 JDB Revised to use tprintf and TRACE_OPND for debugging
|
|
05-Aug-16 JDB Renamed the P register from "PC" to "PR"
|
|
17-May-16 JDB Set local variable instead of call parameter for .SIP test
|
|
24-Dec-14 JDB Added casts for explicit downward conversions
|
|
18-Mar-13 JDB Use MP abort handler declaration in hp2100_cpu.h
|
|
09-May-12 JDB Separated assignments from conditional expressions
|
|
29-Oct-10 JDB DMA channels renamed from 0,1 to 1,2 to match documentation
|
|
18-Sep-08 JDB Corrected .SIP debug formatting
|
|
11-Sep-08 JDB Moved microcode function prototypes to hp2100_cpu1.h
|
|
05-Sep-08 JDB Removed option-present tests (now in UIG dispatchers)
|
|
26-Jun-08 JDB Rewrote device I/O to model backplane signals
|
|
27-Nov-07 JDB Implemented OS instructions
|
|
26-Sep-06 JDB Created
|
|
|
|
Primary references:
|
|
- 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)
|
|
- Macro/1000 Reference Manual
|
|
(92059-90001, December 1992)
|
|
|
|
|
|
The RTE-6/VM Operating System Instructions were added to accelerate certain
|
|
time-consuming operations of the RTE-6/VM operating system, HP product number
|
|
92084A. Microcode was available for the E- and F-Series; the M-Series used
|
|
software equivalents.
|
|
|
|
The opcodes reside in the range 105340-105357. The encodings are:
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | 1 0 1 | 0 1 1 | 1 0 0 | 0 0 0 | $LIBR
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| address of the Temporary Data Block or zero |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
The $LIBR instruction saves the data contained in the Temporary Data Block of
|
|
a reentrant subroutine or turns the interrupt system off for a privileged
|
|
subroutine. The parameter points to the TDB or is zero, respectively.
|
|
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | 1 0 1 | 0 1 1 | 1 0 0 | 0 0 1 | $LIBX
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| address of the subroutine entry point |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
The $LIBX instruction restores the data contained in the Temporary Data Block
|
|
of a reentrant subroutine or turns the interrupt system on for a privileged
|
|
subroutine and then returns to the routine's caller. The parameter points to
|
|
the subroutine entry point, which contains the return address.
|
|
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | 1 0 1 | 0 1 1 | 1 0 0 | 0 1 0 | .TICK
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
: return location if an EQT timed out : P+1
|
|
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+
|
|
: return location if an EQT did not time out : P+2
|
|
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+
|
|
|
|
The .TICK instruction increments the timeout counters of a set of EQTs. On
|
|
entry, the A register contains the address of word 15 of the first EQT, and
|
|
the B register contains the count of EQTs to process. If an EQT timed out,
|
|
the instruction returns at P+1 with the A register pointing at word 15 of the
|
|
EQT, and the B register containing the remaining number of EQTs to process.
|
|
If no EQT timed out, the instruction returns at P+2 with the A register
|
|
pointing at word 15 of the EQT following the last one checked, and the B
|
|
register containing zero.
|
|
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | 1 0 1 | 0 1 1 | 1 0 0 | 0 1 1 | .TNAM
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
: return location if name is not found : P+1
|
|
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+
|
|
: return location if name is found : P+2
|
|
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+
|
|
|
|
The .TNAM instruction finds the ID segment address corresponding to a given
|
|
program name. On entry, the A register contains the address of the keyword
|
|
block, and the B register contains a pointer to the program name. If the ID
|
|
segment is found, return is to P+2 with the A register containing the address
|
|
of word 15 of the ID segment, the B register containing the address of the ID
|
|
segment, and the E register set to 1 for a short ID segment and 0 for a long
|
|
ID segment. If the ID segment is not found, return is to P+1 with the
|
|
registers unspecified.
|
|
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | 1 0 1 | 0 1 1 | 1 0 0 | 1 0 0 | .STIO
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| return address |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| first I/O instruction address to configure |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
...
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| last I/O instruction address to configure |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
The .STIO instruction configures a list of I/O instructions. On entry, each
|
|
parameter following the return address points at an instruction to configure
|
|
with the select code specified in the A register. The instruction returns
|
|
with all registers unchanged.
|
|
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | 1 0 1 | 0 1 1 | 1 0 0 | 1 0 1 | .FNW
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| increment address |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
: return location if word is not found : P+2
|
|
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+
|
|
: return location if word is found : P+3
|
|
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+
|
|
|
|
The .FNW instruction finds a word in a buffer. On entry, the A register
|
|
contains the value of the word to find, the B register contains the address
|
|
of the buffer, the X register contains the number of words to compare, and
|
|
the first parameter points to the increment between words. If the word was
|
|
found, return is to P+3 with the match address in the B register and the
|
|
count of remaining comparisons in the X register. If the word was not found,
|
|
return is to P+2 with the address of the next comparison location in the B
|
|
register and zero in the X register. The A register is unchanged in either
|
|
case.
|
|
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | 1 0 1 | 0 1 1 | 1 0 0 | 1 1 0 | .IRT
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| P-register restore address |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
The .IRT instruction is used to return from an interrupt. It restores the A,
|
|
B, E, O, X, and Y registers from the XSUSP and XI save areas and restores the
|
|
P register from XSUSP,I into the location specified by the parameter, which
|
|
will be the jump target of a following UJP or SJP instruction.
|
|
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | 1 0 1 | 0 1 1 | 1 0 0 | 1 1 1 | .LLS
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| address of the search value |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| address of the offset to the search key |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
: return location if the linked list is in error : P+3
|
|
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+
|
|
: return location if the search value is not found : P+4
|
|
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+
|
|
: return location if the search value is found : P+5
|
|
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+
|
|
|
|
The .LLS instruction conducts a linked-list search. On entry, the A register
|
|
contains a pointer to the head of the list, the B register is zero, and the E
|
|
register is 0 to find a value matching the search value or 1 to find a value
|
|
greater than the search value. The first parameter points at the value to
|
|
find, and the second parameter points at the offset within each list entry
|
|
from the link pointer to the value to compare. The instruction returns to
|
|
P+3 if the list structure is erroneous (a link has its sign bit set), to P+4
|
|
if the search value is not present in the list, or to P+5 if the search value
|
|
was found. In the latter two cases, the A register points to the link word
|
|
of the current entry, and the B register points to the link word of the
|
|
previous entry.
|
|
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | 1 0 1 | 0 1 1 | 1 0 1 | 0 0 0 | .SIP
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
: return location if no interrupt is pending : P+1
|
|
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+
|
|
: return location if interrupt is pending : P+2
|
|
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+
|
|
|
|
The .SIP instruction skips if an interrupt is pending. It is used to avoid
|
|
exiting and then immediately reentering RTE when an interrupt is pending at
|
|
exit. A pending interrupt must be serviced by executing the .YLD
|
|
instruction.
|
|
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | 1 0 1 | 0 1 1 | 1 0 1 | 0 0 1 | .YLD
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| point of resumption after interrupt |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
The .YLD instruction yields control to the trap cell instruction for the
|
|
pending interrupt. Before transferring control, the P-register set to the
|
|
value contained in the second instruction word.
|
|
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | 1 0 1 | 0 1 1 | 1 0 1 | 0 1 0 | .CPM
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| first argument address |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| second argument address |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
: return location if argument 1 = argument 2 : P+3
|
|
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+
|
|
: return location if argument 1 < argument 2 : P+4
|
|
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+
|
|
: return location if argument 1 > argument 2 : P+5
|
|
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+
|
|
|
|
The .CPM instruction compares the two words pointed to by the parameters and
|
|
returns to P+3 if the words are equal, to P+4 if word 1 is less than word 2,
|
|
or to P+5 if word 1 is greater than word 2. The registers are unchanged by
|
|
this instruction.
|
|
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | 1 0 1 | 0 1 1 | 1 0 1 | 0 1 1 | .ETEQ
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
The .ETEQ instruction sets up the base-page EQT addresses to point at the EQT
|
|
whose address is contained in the A register.
|
|
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | 1 0 1 | 0 1 1 | 1 0 1 | 1 0 0 | .ENTN
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| parameter block address |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
The .ENTN instruction transfers the direct addresses of parameters from the
|
|
calling sequence of a utility subroutine to the parameter block specified.
|
|
The sequence differs from the .ENTR sequence in that there is no DEF *+n
|
|
parameter immediately following the call, so the number of parameters
|
|
specified must match the size of the parameter block.
|
|
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | 1 0 1 | 0 1 1 | 1 0 1 | 1 0 1 | $OTST
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
: return location if the firmware is not installed : P+1
|
|
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+
|
|
: return location if the firmware is installed : P+2
|
|
+- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -+
|
|
|
|
The $OTST instruction is used to determine programmatically if the OS
|
|
firmware has been installed. It sets the X-register to the firmware revision
|
|
code, sets S to 102077 (HLT 77B), and returns to P+2. In hardware, it also
|
|
sets Y to the RPL switch settings and sets A to the contents of the loader
|
|
ROM location specified by the B-register. In simulation, these last two
|
|
actions are not implemented.
|
|
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | 1 0 1 | 0 1 1 | 1 0 1 | 1 1 0 | .ENTC
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| parameter block address |
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
The .ENTC instruction transfers the direct addresses of parameters from the
|
|
calling sequence of a privileged or reentrant subroutine to the parameter
|
|
block specified. The sequence differs from the .ENTP sequence in that there
|
|
is no DEF *+n parameter immediately following the call, so the number of
|
|
parameters specified must match the size of the parameter block.
|
|
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | 1 0 1 | 0 1 1 | 1 0 1 | 1 1 1 | .DSPI
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
The .DSPI instruction copies the lower six bits of the A register into the
|
|
display indicator register that controls the A, B, M, T, P, and S LEDs on the
|
|
front panel of the CPU. This is not simulated.
|
|
|
|
|
|
Opcodes 105354-105357 are "dual use" instructions that take different
|
|
actions, depending on whether they are executed from a trap cell during an
|
|
interrupt. When executed from a trap cell, they have these encodings:
|
|
|
|
15 14 13 12 11 10 9 8 7 6 5 4 3 2 1 0
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | 1 0 1 | 0 1 1 | 1 0 1 | 1 0 0 | $DCPC
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
The $DCPC instruction is placed in base-page locations 6 and 7 to handle DCPC
|
|
completion interrupts.
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | 1 0 1 | 0 1 1 | 1 0 1 | 1 0 1 | $MPV
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
The $MPV instruction is placed in base-page location 5 to handle memory
|
|
protect, parity error, and MEM violation interrupts.
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | 1 0 1 | 0 1 1 | 1 0 1 | 1 1 0 | $DEV
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
The $DEV instruction is placed in base-page locations corresponding to the
|
|
select codes of devices whose interrupts are handled by $CIC.
|
|
|
|
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
| 1 | 0 0 0 | 1 0 1 | 0 1 1 | 1 0 1 | 1 1 1 | $TBG
|
|
+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+---+
|
|
|
|
The $TBG instruction is placed in base-page location corresponding to the
|
|
select code of the time-base generator interface to handle TBG tick
|
|
interrupts.
|
|
*/
|
|
|
|
|
|
|
|
#include "hp2100_defs.h"
|
|
#include "hp2100_cpu.h"
|
|
#include "hp2100_cpu_dmm.h"
|
|
|
|
|
|
|
|
/* Offsets to data and addresses within RTE */
|
|
|
|
static const HP_WORD xi = 0001647u; /* XI address */
|
|
static const HP_WORD intba = 0001654u; /* INTBA address */
|
|
static const HP_WORD intlg = 0001655u; /* INTLG address */
|
|
static const HP_WORD eqt1 = 0001660u; /* EQT1 address */
|
|
static const HP_WORD eqt11 = 0001672u; /* EQT11 address */
|
|
static const HP_WORD pvcn = 0001712u; /* PVCN address */
|
|
static const HP_WORD xsusp = 0001730u; /* XSUSP address */
|
|
static const HP_WORD dummy = 0001737u; /* DUMMY address */
|
|
static const HP_WORD mptfl = 0001770u; /* MPTFL address */
|
|
static const HP_WORD eqt12 = 0001771u; /* EQT12 address */
|
|
static const HP_WORD eqt15 = 0001774u; /* EQT15 address */
|
|
static const HP_WORD vctr = 0002000u; /* VCTR address */
|
|
|
|
|
|
/* RTE communication vector.
|
|
|
|
The instructions depend on a status area and a set of (direct) addresses for
|
|
communication with RTE. Location 2000 in the system map contains a pointer
|
|
to the block, which is arranged as follows:
|
|
|
|
Location Label Contents Use
|
|
-------- ----- --------- -------------------------------------------------
|
|
L+00 $DMS BSS 1 DMS status at interrupt
|
|
L+01 $INT BSS 1 Interrupt system status
|
|
L+02 INTCD BSS 1 Interrupting select code
|
|
L+03 DEF $CLCK Address of the TBG handler
|
|
L+04 DEF $CIC4 Address of the illegal interrupt handler
|
|
L+05 DEF $CIC2 Address of the normal drivers handler
|
|
L+06 DEF $SKED Address of the interrupt program scheduler
|
|
L+07 DEF $RQST Address of the EXEC request processor
|
|
L+10 DEF $CIC Address of the P-Register location for $PERR
|
|
L+11 DEF $PERR Address of the parity error processor
|
|
L+12 DEF $MPER Address of the error routine for $LIBR
|
|
L+13 DEF $LXND Address of the privileged mode cleanup for $LIBX
|
|
*/
|
|
|
|
typedef enum {
|
|
dms_offset = 0, /* DMS status */
|
|
int_offset, /* interrupt system status */
|
|
sc_offset, /* select code */
|
|
clck_offset, /* TBG IRQ handler */
|
|
cic4_offset, /* illegal IRQ handler */
|
|
cic2_offset, /* device IRQ handler */
|
|
sked_offset, /* prog sched IRQ handler */
|
|
rqst_offset, /* EXEC request handler */
|
|
cic_offset, /* IRQ location */
|
|
perr_offset, /* parity error IRQ handler */
|
|
mper_offset, /* memory protect IRQ handler */
|
|
lxnd_offset /* $LIBX return */
|
|
} VECTOR_OFFSETS;
|
|
|
|
|
|
|
|
/* Save the CPU registers.
|
|
|
|
The CPU registers are saved in the current ID segment in preparation for
|
|
interrupt handling. Although the RTE base page has separate pointers for the
|
|
P, A, B, and E/O registers, they are always contiguous, and the microcode
|
|
simply increments the P-register pointer (XSUSP) to store the remaining
|
|
values.
|
|
|
|
This routine is called from the trap cell interrupt handlers and from the
|
|
$LIBX processor. In the latter case, the privileged system interrupt
|
|
handling is not required, so it is bypassed. In either case, the current map
|
|
will be the system map when we are called, and memory protect will be off.
|
|
*/
|
|
|
|
static void save_regs (t_bool int_ack)
|
|
{
|
|
HP_WORD save_area, priv_fence;
|
|
|
|
save_area = ReadW (xsusp); /* addr of PABEO save area */
|
|
|
|
WriteW (save_area + 0, PR); /* save P */
|
|
WriteW (save_area + 1, AR); /* save A */
|
|
WriteW (save_area + 2, BR); /* save B */
|
|
WriteW (save_area + 3, (E << 15) & D16_SIGN | O & 1); /* save E and O */
|
|
|
|
save_area = ReadW (xi); /* addr of XY save area */
|
|
WriteWA (save_area + 0, XR); /* save X (in user map) */
|
|
WriteWA (save_area + 1, YR); /* save Y (in user map) */
|
|
|
|
if (int_ack) { /* do priv setup only if IAK */
|
|
priv_fence = ReadW (dummy); /* get priv fence select code */
|
|
|
|
if (priv_fence) { /* privileged system? */
|
|
io_control (priv_fence, iog_STC); /* STC SC on priv fence */
|
|
io_control (DMA1, iog_CLC); /* CLC 6 to inh IRQ on DCPC 1 */
|
|
io_control (DMA2, iog_CLC); /* CLC 7 to inh IRQ on DCPC 2 */
|
|
io_control (CPU, iog_STF); /* turn interrupt system back on */
|
|
}
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* Save the machine state at interrupt.
|
|
|
|
This routine is called from each of the trap cell instructions during an
|
|
interrupt acknowledgement. Its purpose is to save the complete state of the
|
|
machine in preparation for interrupt handling.
|
|
|
|
For the MP/DMS/PE interrupt, the interrupting device must not be cleared and
|
|
the CPU registers must not be saved until it is established that the
|
|
interrupt is not caused by a parity error. Parity errors cannot be
|
|
inhibited, so the interrupt may have occurred while running in RTE with the
|
|
interrupt system off. Saving the registers would overwrite the user's
|
|
registers that were saved at RTE entry. The state of the interrupt system is
|
|
determined by executing a SFS 0,C instruction, which increments P if the
|
|
interrupt system was on, and then turns it off by clearing the flag.
|
|
|
|
Note that the trap cell instructions are dual-use and invoke this routine
|
|
only when they are executed during interrupts. Therefore, the current map
|
|
will always be the system map.
|
|
*/
|
|
|
|
static void save_state (void)
|
|
{
|
|
HP_WORD vectors, int_sys_off;
|
|
|
|
mp_disable (); /* turn MP off */
|
|
|
|
int_sys_off = (HP_WORD) ! io_control (CPU, iog_SFS_C); /* set flag if interrupt system is already off */
|
|
|
|
vectors = ReadW (vctr); /* get address of vectors (in SMAP) */
|
|
|
|
WriteW (vectors + dms_offset, meu_update_status ()); /* save DMS status (SSM) */
|
|
WriteW (vectors + int_offset, int_sys_off); /* save int status */
|
|
WriteW (vectors + sc_offset, CIR); /* save select code */
|
|
|
|
WriteW (mptfl, 1); /* show MP is off */
|
|
|
|
if (CIR != MPPE) { /* only if not MP interrupt */
|
|
io_control (CIR, iog_CLF); /* issue CLF to device */
|
|
save_regs (TRUE); /* save CPU registers */
|
|
}
|
|
|
|
return;
|
|
}
|
|
|
|
|
|
/* Get the interrupt table entry corresponding to a select code.
|
|
|
|
Return the word in the RTE interrupt table that corresponds to the
|
|
interrupting select code. Return 0 if the select code is beyond the end of
|
|
the table.
|
|
*/
|
|
|
|
static HP_WORD cpu_get_intbl (HP_WORD select_code)
|
|
{
|
|
HP_WORD interrupt_table; /* interrupt table (starts with SC 06) */
|
|
HP_WORD table_length; /* length of interrupt table */
|
|
|
|
interrupt_table = ReadW (intba); /* get int table address */
|
|
table_length = ReadW (intlg); /* get int table length */
|
|
|
|
if (select_code - 6 > table_length) /* SC beyond end of table? */
|
|
return 0; /* return 0 for illegal interrupt */
|
|
else
|
|
return ReadW (interrupt_table + select_code - 6); /* else return table entry */
|
|
}
|
|
|
|
|
|
/* RTE-6/VM Operating System Instructions.
|
|
|
|
The OS instructions were added to accelerate certain time-consuming
|
|
operations of the RTE-6/VM operating system, HP product number 92084A.
|
|
Microcode was available for the E- and F-Series; the M-Series used software
|
|
equivalents.
|
|
|
|
Option implementation by CPU was as follows:
|
|
|
|
2114 2115 2116 2100 1000-M 1000-E 1000-F
|
|
------ ------ ------ ------ ------ ------ ------
|
|
N/A N/A N/A N/A N/A 92084A 92084A
|
|
|
|
The routines are mapped to instruction codes as follows:
|
|
|
|
Instr. 1000-E/F Description
|
|
------ -------- ----------------------------------------------
|
|
$LIBR 105340 Enter privileged/reentrant library routine
|
|
$LIBX 105341 Exit privileged/reentrant library routine
|
|
.TICK 105342 TBG tick interrupt handler
|
|
.TNAM 105343 Find ID segment that matches name
|
|
.STIO 105344 Configure I/O instructions
|
|
.FNW 105345 Find word with user increment
|
|
.IRT 105346 Interrupt return processing
|
|
.LLS 105347 Linked list search
|
|
|
|
.SIP 105350 Skip if interrupt pending
|
|
.YLD 105351 .SIP completion return point
|
|
.CPM 105352 Compare words LT/EQ/GT
|
|
.ETEQ 105353 Set up EQT pointers in base page
|
|
.ENTN 105354 Transfer parameter addresses (utility)
|
|
$OTST * 105355 OS firmware self test
|
|
.ENTC 105356 Transfer parameter addresses (priv/reent)
|
|
.DSPI 105357 Set display indicator
|
|
|
|
Opcodes 105354-105357 are "dual use" instructions that take different
|
|
actions, depending on whether they are executed from a trap cell during an
|
|
interrupt. When executed from a trap cell, they have these actions:
|
|
|
|
Instr. 1000-E/F Description
|
|
------ -------- ----------------------------------------------
|
|
$DCPC * 105354 DCPC channel interrupt processing
|
|
$MPV * 105355 MP/DMS/PE interrupt processing
|
|
$DEV * 105356 Standard device interrupt processing
|
|
$TBG * 105357 TBG interrupt processing
|
|
|
|
* These mnemonics are recognized by symbolic examine/deposit but are not
|
|
official HP mnemonics.
|
|
|
|
The default (user microcode) dispatcher will allow the firmware self-test
|
|
instruction (105355) to execute as NOP. This is because RTE-6/VM will always
|
|
test for the presence of OS and VMA firmware on E/F-Series machines. If the
|
|
firmware is not present, then these instructions will return to P+1, and RTE
|
|
will then HLT 21. This means that RTE-6/VM will not run on an E/F-Series
|
|
machine without the OS and VMA firmware.
|
|
|
|
However, RTE allows the firmware instructions to be disabled for debugging
|
|
purposes. If the firmware is present and returns to P+2 but sets the X
|
|
register to 0, then RTE will use software equivalents. We enable this
|
|
condition when the OS firmware is enabled (SET CPU VMA), the OS debug flag is
|
|
set (SET CPU DEBUG=OS), but debug output has been disabled (SET CONSOLE
|
|
NODEBUG). That is:
|
|
|
|
OS Debug
|
|
Firmware Debug Output Tracing Self-Test Instruction
|
|
======== ===== ====== ======= =====================
|
|
disabled x x off NOP
|
|
enabled clear x off X = revision code
|
|
enabled set off off X = 0
|
|
enabled set on on X = revision code
|
|
|
|
Additional references:
|
|
|
|
- RTE-6/VM OS Microcode Source (92084-18831, revision 8).
|
|
- RTE-6/VM Technical Specifications (92084-90015, Apr-1983).
|
|
|
|
|
|
Implementation notes:
|
|
|
|
1. The microcode differentiates between interrupt processing and normal
|
|
execution of the "dual use" instructions by testing the CPU flag.
|
|
Interrupt vectoring sets the flag; a normal instruction fetch clears it.
|
|
Under simulation, interrupt vectoring is indicated by the value of the
|
|
"int_ack" parameter (FALSE = normal instruction, TRUE = interrupt
|
|
acknowledge instruction).
|
|
|
|
2. The operand patterns for .ENTN and .ENTC normally would be coded as
|
|
"OP_A", as each takes a single address as a parameter. However, because
|
|
they might also be executed from a trap cell (as $DCPC and $DEV), we
|
|
cannot assume that P+1 is an address, or we might cause a DM abort when
|
|
trying to resolve indirects. Therefore, "OP_A" handling is done within
|
|
each routine, once the type of use is determined.
|
|
|
|
3. The microcode for .ENTC, .ENTN, .FNW, .LLS, .TICK, and .TNAM explicitly
|
|
checks for interrupts during instruction execution. In addition, the
|
|
.STIO, .CPM, and .LLS instructions implicitly check for interrupts during
|
|
parameter indirect resolution. Because the simulator calculates
|
|
interrupt requests only between instructions, this behavior is not
|
|
simulated.
|
|
|
|
4. The microcode executes certain I/O instructions (e.g., CLF 0) by building
|
|
the instruction in the IR and executing an IOG micro-order. We simulate
|
|
this behavior by calling the "io_control" routine with the appropriate
|
|
I/O signal assertion.
|
|
|
|
5. The $OTST and .DSPI microcode provides features (reading the RPL switches
|
|
and boot loader ROM data, loading the display register) that are not
|
|
simulated. The remaining functions of the $OTST instruction are
|
|
provided. The .DSPI instruction is a NOP or unimplemented instruction
|
|
stop.
|
|
|
|
6. The $LIBX instruction is executed to complete either a privileged or
|
|
reentrant execution. In the former case, the privileged nest counter
|
|
($PVCN) is decremented. In the latter, $PVCN decrement is attempted but
|
|
the write will trap with an MP violation, as reentrant routines execute
|
|
with the interrupt system on. RTE will then complete the release of
|
|
memory allocated for the original $LIBR call.
|
|
|
|
7. The documentation for the .SIP and .YLD instructions is misleading in
|
|
several places. Comments in the RTE $SIP source file say that .SIP
|
|
doesn't return if a "known" interrupt is pending. Actually, .SIP always
|
|
returns, either to P+1 for no pending interrupt, or to P+2 if one is
|
|
pending. There is no check for "known" interrupt handlers. The
|
|
microcode source comments say that the interrupting select code is
|
|
returned in the B register. Actually, the B register is unchanged. The
|
|
RTE Tech Specs say that .SIP "services any pending system interrupts."
|
|
Actually, .SIP only checks for interrupts; no servicing is performed.
|
|
|
|
For .YLD, the microcode comments say that two parameters are passed: the
|
|
new P value, and the interrupting select code. Actually, only the new P
|
|
value is passed.
|
|
|
|
The .SIP and .YLD simulations follow the actual microcode rather than the
|
|
documentation.
|
|
|
|
8. The "%.0u" print specification in the trace call absorbs the zero "CIR"
|
|
value parameter without printing when an interrupt is not pending.
|
|
|
|
9. The $LIBR microcode checks for memory protect on by reading the MEM
|
|
status register and checking the protected mode bit (bit 11). In
|
|
simulation, we check for MP on with the "mp_is_on" routine, as the status
|
|
register is not global.
|
|
*/
|
|
|
|
static const OP_PAT op_os [16] = {
|
|
OP_A, OP_A, OP_N, OP_N, /* $LIBR $LIBX .TICK .TNAM */
|
|
OP_A, OP_K, OP_A, OP_KK, /* .STIO .FNW .IRT .LLS */
|
|
OP_N, OP_C, OP_KK, OP_N, /* .SIP .YLD .CPM .ETEQ */
|
|
OP_N, OP_N, OP_N, OP_N /* .ENTN $OTST .ENTC .DSPI */
|
|
};
|
|
|
|
t_stat cpu_rte_os (t_bool int_ack)
|
|
{
|
|
static const char * const no [2] = { "", "no " };
|
|
static const char * const not [2] = { "not ", "" };
|
|
static const char * const list [3] = { "list error", "not found", "found" };
|
|
static const char * const compare [3] = { "equal", "less than", "greater than" };
|
|
|
|
OPS op;
|
|
OP_PAT pattern;
|
|
uint32 i, irq;
|
|
HP_WORD entry, count, cp, sa, da, ma, eqta;
|
|
HP_WORD vectors, save_area, priv_fence, eoreg, eqt, key;
|
|
char test [6], target [6];
|
|
t_bool mpv;
|
|
t_stat reason = SCPE_OK;
|
|
|
|
entry = IR & 017; /* mask to entry point */
|
|
pattern = op_os [entry]; /* get operand pattern */
|
|
|
|
if (pattern != OP_N) {
|
|
reason = cpu_ops (pattern, op); /* get instruction operands */
|
|
|
|
if (reason != SCPE_OK) /* evaluation failed? */
|
|
return reason; /* return reason for failure */
|
|
}
|
|
|
|
switch (entry) { /* decode IR<3:0> */
|
|
|
|
case 000: /* $LIBR 105340 (OP_A) */
|
|
if (op[0].word != 0 /* reentrant call? */
|
|
|| mp_is_on () && ReadW (dummy) != 0) { /* or priv call + MP on + priv sys? */
|
|
meu_privileged (If_User_Map); /* priv viol if called from user map */
|
|
|
|
meu_set_state (ME_Enabled, System_Map); /* set system map */
|
|
|
|
vectors = ReadW (vctr); /* get address of vectors (in SMAP) */
|
|
PR = ReadW (vectors + mper_offset); /* vector to $MPER for processing */
|
|
}
|
|
|
|
else { /* privileged call */
|
|
if (mp_is_on ()) { /* memory protect on? */
|
|
mp_disable (); /* turn it off */
|
|
io_control (CPU, iog_CLF); /* turn interrupt system off */
|
|
WriteW (mptfl, 1); /* show MP is off */
|
|
|
|
save_area = ReadW (xsusp); /* get addr of P save area */
|
|
WriteS (save_area, PR - 2 & LA_MASK); /* set point of suspension using the system map */
|
|
}
|
|
|
|
WriteW (pvcn, ReadW (pvcn) + 1 & D16_MASK); /* increment priv nest counter */
|
|
}
|
|
break;
|
|
|
|
|
|
case 001: /* $LIBX 105341 (OP_A) */
|
|
PR = ReadW (op[0].word); /* set P to return point */
|
|
count = ReadW (pvcn) - 1 & D16_MASK; /* decrement priv nest counter */
|
|
WriteW (pvcn, count); /* write it back */
|
|
|
|
if (count == 0) { /* end of priv mode? */
|
|
meu_set_state (ME_Enabled, System_Map); /* set system map */
|
|
save_regs (FALSE); /* save registers */
|
|
vectors = ReadW (vctr); /* get address of vectors */
|
|
PR = ReadW (vectors + lxnd_offset); /* vector to $LXND for processing */
|
|
}
|
|
break;
|
|
|
|
|
|
case 002: /* .TICK 105342 (OP_N) */
|
|
do {
|
|
eqt = ReadW (AR) + 1 & D16_MASK; /* bump timeout from EQT15 */
|
|
|
|
if (eqt != 1) { /* was timeout active? */
|
|
WriteW (AR, eqt); /* yes, write it back */
|
|
|
|
if (eqt == 0) /* did timeout expire? */
|
|
break; /* P+1 return for timeout */
|
|
}
|
|
|
|
AR = AR + 15 & R_MASK; /* point at next EQT15 */
|
|
BR = BR - 1 & R_MASK; /* decrement count of EQTs */
|
|
} while ((BR > 0) && (eqt != 0)); /* loop until timeout or done */
|
|
|
|
if (BR == 0) /* which termination condition? */
|
|
PR = (PR + 1) & LA_MASK; /* P+2 return for no timeout */
|
|
|
|
tprintf (cpu_dev, TRACE_OPND, OPND_FORMAT " return location is P+%u (%stimeout)\n",
|
|
PR, IR, PR - err_PR, no [PR - err_PR - 1]);
|
|
break;
|
|
|
|
|
|
case 003: /* .TNAM 105343 (OP_N) */
|
|
E = 1; /* preset flag for not found */
|
|
cp = BR << 1 & D16_MASK; /* form char addr (B is direct) */
|
|
|
|
for (i = 0; i < 5; i++) { /* copy target name */
|
|
target[i] = (char) ReadB (cp); /* name is only 5 chars */
|
|
cp = cp + 1 & D16_MASK;
|
|
}
|
|
|
|
if ((target[0] == '\0') && (target[1] == '\0')) /* if name is null, */
|
|
break; /* return immed to P+1 */
|
|
|
|
key = ReadW (AR); /* get first keyword addr */
|
|
|
|
while (key != 0) { /* end of keywords? */
|
|
cp = key + 12 << 1 & D16_MASK; /* form char addr of name */
|
|
|
|
for (i = 0; i < 6; i++) { /* copy test name */
|
|
test[i] = (char) ReadB (cp); /* name is only 5 chars */
|
|
cp = cp + 1 & D16_MASK; /* but copy 6 to get flags */
|
|
}
|
|
|
|
if (strncmp (target, test, 5) == 0) { /* names match? */
|
|
AR = key + 15 & R_MASK; /* A = addr of IDSEG [15] */
|
|
BR = key; /* B = addr of IDSEG [0] */
|
|
E = (uint32) ((test[5] >> 4) & 1); /* E = short ID segment bit */
|
|
PR = (PR + 1) & LA_MASK; /* P+2 for found return */
|
|
break;
|
|
}
|
|
|
|
AR = AR + 1 & R_MASK; /* bump to next keyword */
|
|
key = ReadW (AR); /* get next keyword */
|
|
};
|
|
|
|
tprintf (cpu_dev, TRACE_OPND, OPND_FORMAT " return location is P+%u (%sfound)\n",
|
|
PR, IR, PR - err_PR, not [PR - err_PR - 1]);
|
|
break;
|
|
|
|
|
|
case 004: /* .STIO 105344 (OP_A) */
|
|
count = op[0].word - PR; /* get count of operands */
|
|
|
|
tprintf (cpu_dev, TRACE_OPND, OPND_FORMAT " instruction count is %u\n",
|
|
PR, IR, count);
|
|
|
|
for (i = 0; i < count; i++) {
|
|
MR = ReadW (PR); /* get operand address */
|
|
|
|
reason = cpu_resolve_indirects (TRUE); /* resolve indirects */
|
|
|
|
if (reason != SCPE_OK) { /* resolution failed? */
|
|
PR = err_PR; /* IRQ restarts instruction */
|
|
break;
|
|
}
|
|
|
|
WriteW (MR, ReadW (MR) & ~SC_MASK | AR); /* set SC into instruction */
|
|
PR = (PR + 1) & LA_MASK; /* bump to next */
|
|
}
|
|
break;
|
|
|
|
|
|
case 005: /* .FNW 105345 (OP_K) */
|
|
while (XR != 0) { /* all comparisons done? */
|
|
key = ReadW (BR); /* read a buffer word */
|
|
|
|
if (key == AR) { /* does it match? */
|
|
PR = (PR + 1) & LA_MASK; /* P+3 found return */
|
|
break;
|
|
}
|
|
|
|
BR = BR + op[0].word & R_MASK; /* increment buffer ptr */
|
|
XR = XR - 1 & R_MASK; /* decrement remaining count */
|
|
}
|
|
/* P+2 not found return */
|
|
|
|
tprintf (cpu_dev, TRACE_OPND, OPND_FORMAT " return location is P+%u (%sfound)\n",
|
|
PR, IR, PR - err_PR, not [PR - err_PR - 2]);
|
|
break;
|
|
|
|
|
|
case 006: /* .IRT 105346 (OP_A) */
|
|
save_area = ReadW (xsusp); /* addr of PABEO save area */
|
|
|
|
WriteW (op[0].word, ReadW (save_area + 0)); /* restore P to DEF RTN */
|
|
|
|
AR = ReadW (save_area + 1); /* restore A */
|
|
BR = ReadW (save_area + 2); /* restore B */
|
|
|
|
eoreg = ReadW (save_area + 3); /* get combined E and O */
|
|
E = (eoreg >> 15) & 1; /* restore E */
|
|
O = eoreg & 1; /* restore O */
|
|
|
|
save_area = ReadW (xi); /* addr of XY save area */
|
|
XR = ReadWA (save_area + 0); /* restore X (from user map) */
|
|
YR = ReadWA (save_area + 1); /* restore Y (from user map) */
|
|
|
|
io_control (CPU, iog_CLF); /* turn interrupt system off */
|
|
WriteW (mptfl, 0); /* show MP is on */
|
|
|
|
priv_fence = ReadW (dummy); /* get priv fence select code */
|
|
|
|
if (priv_fence) { /* privileged system? */
|
|
io_control (priv_fence, iog_CLC); /* CLC SC on priv fence */
|
|
io_control (priv_fence, iog_STF); /* STF SC on priv fence */
|
|
|
|
if (cpu_get_intbl (DMA1) & D16_SIGN) /* DCPC 1 active? */
|
|
io_control (DMA1, iog_STC); /* STC 6 to enable IRQ on DCPC 1 */
|
|
|
|
if (cpu_get_intbl (DMA2) & D16_SIGN) /* DCPC 2 active? */
|
|
io_control (DMA2, iog_STC); /* STC 7 to enable IRQ on DCPC 2 */
|
|
}
|
|
break;
|
|
|
|
|
|
case 007: /* .LLS 105347 (OP_KK) */
|
|
AR = AR & ~D16_SIGN; /* clear sign bit of A */
|
|
|
|
while ((AR != 0) && ((AR & D16_SIGN) == 0)) { /* end of list or bad list? */
|
|
key = ReadW ((AR + op[1].word) & LA_MASK); /* get key value */
|
|
|
|
if ((E == 0) && (key == op[0].word) || /* for E = 0, key = arg? */
|
|
(E != 0) && (key > op[0].word)) /* for E = 1, key > arg? */
|
|
break; /* search is done */
|
|
|
|
BR = AR; /* B = last link */
|
|
AR = ReadW (AR); /* A = next link */
|
|
}
|
|
|
|
if (AR == 0) /* exhausted list? */
|
|
PR = (PR + 1) & LA_MASK; /* P+4 arg not found */
|
|
|
|
else if ((AR & D16_SIGN) == 0) /* good link? */
|
|
PR = (PR + 2) & LA_MASK; /* P+5 arg found */
|
|
|
|
tprintf (cpu_dev, TRACE_OPND, OPND_FORMAT " return location is P+%u (%s)\n",
|
|
PR, IR, PR - err_PR, list [PR - err_PR - 3]);
|
|
break;
|
|
|
|
|
|
case 010: /* .SIP 105350 (OP_N) */
|
|
irq = io_poll_interrupts (SET); /* check for interrupt requests */
|
|
|
|
if (irq) /* was interrupt pending? */
|
|
PR = PR + 1 & R_MASK; /* P+2 return for pending IRQ */
|
|
|
|
tprintf (cpu_dev, TRACE_OPND,
|
|
(irq ? OPND_FORMAT " return location is P+2 (pending), CIR %02o\n"
|
|
: OPND_FORMAT " return location is P+1 (not pending)%.0u\n"),
|
|
PR, IR, irq);
|
|
break;
|
|
|
|
|
|
case 011: /* .YLD 105351 (OP_C) */
|
|
PR = op[0].word; /* pick up point of resumption */
|
|
io_control (CPU, iog_STF); /* turn interrupt system on */
|
|
cpu_interrupt_enable = SET; /* enable so irq occurs immed */
|
|
break;
|
|
|
|
|
|
case 012: /* .CPM 105352 (OP_KK) */
|
|
if (INT16 (op[0].word) > INT16 (op[1].word))
|
|
PR = (PR + 2) & LA_MASK; /* P+5 arg1 > arg2 */
|
|
|
|
else if (INT16 (op[0].word) < INT16 (op[1].word))
|
|
PR = (PR + 1) & LA_MASK; /* P+4 arg1 < arg2 */
|
|
|
|
tprintf (cpu_dev, TRACE_OPND, OPND_FORMAT " return location is P+%u (%s)\n",
|
|
PR, IR, PR - err_PR, compare [PR - err_PR - 3]);
|
|
break;
|
|
|
|
|
|
case 013: /* .ETEQ 105353 (OP_N) */
|
|
eqt = ReadW (eqt1); /* get addr of EQT1 */
|
|
|
|
if (AR != eqt) { /* already set up? */
|
|
for (eqta = eqt1; eqta <= eqt11; eqta++) /* init EQT1-EQT11 */
|
|
WriteW (eqta, AR++ & R_MASK);
|
|
for (eqta = eqt12; eqta <= eqt15; eqta++) /* init EQT12-EQT15 */
|
|
WriteW (eqta, AR++ & R_MASK); /* (not contig with EQT1-11) */
|
|
}
|
|
|
|
AR = AR & R_MASK; /* ensure wraparound */
|
|
break;
|
|
|
|
|
|
case 014: /* .ENTN/$DCPC 105354 (OP_N) */
|
|
if (int_ack) { /* in trap cell? */
|
|
save_state (); /* DMA interrupt */
|
|
AR = cpu_get_intbl (CIR) & ~D16_SIGN; /* get intbl value and strip sign */
|
|
goto DEVINT; /* vector by intbl value */
|
|
}
|
|
|
|
else { /* .ENTN instruction */
|
|
ma = (PR - 2) & LA_MASK; /* get addr of entry point */
|
|
|
|
ENTX: /* enter here from .ENTC */
|
|
reason = cpu_ops (OP_A, op); /* get instruction operand */
|
|
da = op[0].word; /* get addr of 1st formal */
|
|
count = ma - da; /* get count of formals */
|
|
sa = ReadW (ma); /* get addr of 1st actual */
|
|
WriteW (ma, (sa + count) & LA_MASK); /* adjust return point to skip actuals */
|
|
|
|
tprintf (cpu_dev, TRACE_OPND, OPND_FORMAT " parameter count is %u\n",
|
|
PR, IR, count);
|
|
|
|
for (i = 0; i < count; i++) { /* parameter loop */
|
|
MR = ReadW (sa); /* get addr of actual */
|
|
sa = (sa + 1) & LA_MASK; /* increment address */
|
|
|
|
reason = cpu_resolve_indirects (TRUE); /* resolve indirects */
|
|
|
|
if (reason != SCPE_OK) { /* resolution failed? */
|
|
PR = err_PR; /* irq restarts instruction */
|
|
break;
|
|
}
|
|
|
|
WriteW (da, MR); /* put addr into formal */
|
|
da = (da + 1) & LA_MASK; /* increment address */
|
|
}
|
|
|
|
if (entry == 016) /* call was .ENTC? */
|
|
AR = sa; /* set A to return address */
|
|
}
|
|
break;
|
|
|
|
|
|
case 015: /* $OTST/$MPV 105355 (OP_N) */
|
|
if (int_ack) { /* in trap cell? */
|
|
mpv = mp_trace_violation (); /* get MP/PE flag */
|
|
|
|
save_state (); /* MP/DMS/PE interrupt */
|
|
vectors = ReadW (vctr); /* get address of vectors (in SMAP) */
|
|
|
|
if (mpv) { /* MP/DMS violation */
|
|
save_regs (TRUE); /* save registers */
|
|
PR = ReadW (vectors + rqst_offset); /* vector to $RQST for processing */
|
|
}
|
|
|
|
else { /* parity error */
|
|
WriteW (vectors + cic_offset, PR); /* save point of suspension in $CIC */
|
|
PR = ReadW (vectors + perr_offset); /* vector to $PERR for processing */
|
|
}
|
|
}
|
|
|
|
else { /* self-test instruction */
|
|
YR = 0000000; /* RPL switch (not implemented) */
|
|
AR = 0000000; /* LDR [B] (not implemented) */
|
|
SR = 0102077; /* test passed code */
|
|
PR = (PR + 1) & LA_MASK; /* P+2 return for firmware OK */
|
|
|
|
if (cpu_dev.dctrl & DEBUG_NOOS) /* if the OS debug flag is set */
|
|
XR = 0; /* then return rev 0 so that RTE won't use microcode */
|
|
else /* otherwise */
|
|
XR = 010; /* return firmware revision code 10B */
|
|
|
|
tprintf (cpu_dev, TRACE_OPND, OPND_FORMAT " return location is P+%u (firmware %sinstalled)\n",
|
|
PR, IR, PR - err_PR, not [PR - err_PR - 1]);
|
|
}
|
|
break;
|
|
|
|
|
|
case 016: /* .ENTC/$DEV 105356 (OP_N) */
|
|
if (int_ack) { /* in trap cell? */
|
|
save_state (); /* device interrupt */
|
|
AR = cpu_get_intbl (CIR); /* get interrupt table value */
|
|
|
|
DEVINT:
|
|
vectors = ReadW (vctr); /* get address of vectors (in SMAP) */
|
|
|
|
if (INT16 (AR) < 0) /* negative (program ID)? */
|
|
PR = ReadW (vectors + sked_offset); /* vector to $SKED for processing */
|
|
|
|
else if (AR > 0) /* positive (EQT address)? */
|
|
PR = ReadW (vectors + cic2_offset); /* vector to $CIC2 for processing */
|
|
|
|
else /* zero (illegal interrupt) */
|
|
PR = ReadW (vectors + cic4_offset); /* vector to $CIC4 for processing */
|
|
}
|
|
|
|
else { /* .ENTC instruction */
|
|
ma = (PR - 4) & LA_MASK; /* get addr of entry point */
|
|
goto ENTX; /* continue with common processing */
|
|
}
|
|
break;
|
|
|
|
|
|
case 017: /* .DSPI/$TBG 105357 (OP_N) */
|
|
if (int_ack) { /* in trap cell? */
|
|
save_state (); /* TBG interrupt */
|
|
vectors = ReadW (vctr); /* get address of vectors (in SMAP) */
|
|
PR = ReadW (vectors + clck_offset); /* vector to $CLCK for processing */
|
|
}
|
|
|
|
else /* .DSPI instruction */
|
|
reason = STOP (cpu_ss_unimpl); /* not implemented yet */
|
|
|
|
break;
|
|
}
|
|
|
|
return reason;
|
|
}
|