/* hp2100_cpu6.c: HP 1000 RTE-6/VM OS instructions | |
Copyright (c) 2006-2017, 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 OS instructions | |
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, Mar-1980) | |
- HP 1000 M/E/F-Series Computers Engineering and Reference Documentation | |
(92851-90001, Mar-1981) | |
- Macro/1000 Reference Manual (92059-90001, Dec-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 <setjmp.h> | |
#include "hp2100_defs.h" | |
#include "hp2100_cpu.h" | |
#include "hp2100_cpu1.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 */ | |
static const HP_WORD CLC_0 = 0004700u; /* CLC 0 instruction */ | |
static const HP_WORD STC_0 = 0000700u; /* STC 0 instruction */ | |
static const HP_WORD CLF_0 = 0001100u; /* CLF 0 instruction */ | |
static const HP_WORD STF_0 = 0000100u; /* STF 0 instruction */ | |
static const HP_WORD SFS_0_C = 0003300u; /* SFS 0,C instruction */ | |
/* 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. | |
*/ | |
static t_stat cpu_save_regs (t_bool iotrap) | |
{ | |
HP_WORD save_area, priv_fence; | |
t_stat reason = SCPE_OK; | |
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) & 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 (iotrap) { /* do priv setup only if IRQ */ | |
priv_fence = ReadW (dummy); /* get priv fence select code */ | |
if (priv_fence) { /* privileged system? */ | |
reason = cpu_iog (STC_0 + priv_fence, iotrap); /* STC SC on priv fence */ | |
reason = cpu_iog (CLC_0 + DMA1, iotrap); /* CLC 6 to inh IRQ on DCPC 1 */ | |
reason = cpu_iog (CLC_0 + DMA2, iotrap); /* CLC 7 to inh IRQ on DCPC 2 */ | |
reason = cpu_iog (STF_0, iotrap); /* turn interrupt system back on */ | |
} | |
} | |
return reason; | |
} | |
/* Save the machine state at interrupt. | |
This routine is called from each of the trap cell instructions. 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 when we are called. | |
*/ | |
static t_stat cpu_save_state (uint32 iotrap) | |
{ | |
HP_WORD vectors, saved_PR, int_sys_off; | |
t_stat reason; | |
saved_PR = PR; /* save current P register */ | |
reason = cpu_iog (SFS_0_C, iotrap); /* turn interrupt system off */ | |
int_sys_off = (HP_WORD) (PR == saved_PR); /* set flag if already off */ | |
PR = saved_PR; /* restore P in case it bumped */ | |
vectors = ReadW (vctr); /* get address of vectors (in SMAP) */ | |
WriteW (vectors + dms_offset, dms_upd_sr ()); /* 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 != PRO) { /* only if not MP interrupt */ | |
reason = cpu_iog (CLF_0 + CIR, iotrap); /* issue CLF to device */ | |
cpu_save_regs (iotrap); /* save CPU registers */ | |
} | |
return reason; | |
} | |
/* 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 | |
"iotrap" parameter (FALSE = normal instruction, TRUE = trap cell | |
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 "cpu_iog" handler with the appropriate | |
instruction, rather than manipulating the I/O system directly, so that we | |
will remain unaffected by any future changes to the underlying I/O | |
simulation structure. | |
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. | |
*/ | |
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 (uint32 IR, uint32 intrq, t_bool iotrap) | |
{ | |
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_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, intrq); /* 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_control && (ReadW (dummy) != 0))) { /* or priv call + MP on + priv sys? */ | |
if (dms_ump) { /* called from user map? */ | |
dms_viol (err_PC, MVI_PRV); /* privilege violation */ | |
} | |
dms_ump = SMAP; /* 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_control) { /* memory protect on? */ | |
mp_control = CLEAR; /* turn it off */ | |
reason = cpu_iog (CLF_0, iotrap); /* turn interrupt system off */ | |
WriteW (mptfl, 1); /* show MP is off */ | |
save_area = ReadW (xsusp); /* get addr of P save area */ | |
if (dms_ump) /* user map current? */ | |
WriteWA (save_area, (PR - 2) & VAMASK); /* set point of suspension */ | |
else /* system map current */ | |
WriteW (save_area, (PR - 2) & VAMASK); /* set point of suspension */ | |
} | |
WriteW (pvcn, (ReadW (pvcn) + 1) & DMASK); /* 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) & DMASK; /* decrement priv nest counter */ | |
WriteW (pvcn, count); /* write it back */ | |
if (count == 0) { /* end of priv mode? */ | |
dms_ump = SMAP; /* set system map */ | |
reason = cpu_save_regs (iotrap); /* 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) & DMASK; /* 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) & DMASK; /* point at next EQT15 */ | |
BR = (BR - 1) & DMASK; /* decrement count of EQTs */ | |
} while ((BR > 0) && (eqt != 0)); /* loop until timeout or done */ | |
if (BR == 0) /* which termination condition? */ | |
PR = (PR + 1) & VAMASK; /* P+2 return for no timeout */ | |
tprintf (cpu_dev, TRACE_OPND, OPND_FORMAT " return location is P+%u (%stimeout)\n", | |
PR, IR, PR - err_PC, no [PR - err_PC - 1]); | |
break; | |
case 003: /* .TNAM 105343 (OP_N) */ | |
E = 1; /* preset flag for not found */ | |
cp = (BR << 1) & DMASK; /* 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) & DMASK; | |
} | |
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) & DMASK; /* 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) & DMASK; /* but copy 6 to get flags */ | |
} | |
if (strncmp (target, test, 5) == 0) { /* names match? */ | |
AR = (key + 15) & DMASK; /* 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) & VAMASK; /* P+2 for found return */ | |
break; | |
} | |
AR = (AR + 1) & DMASK; /* 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_PC, not [PR - err_PC - 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++) { | |
ma = ReadW (PR); /* get operand address */ | |
reason = resolve (ma, &ma, intrq); /* resolve indirect */ | |
if (reason != SCPE_OK) { /* resolution failed? */ | |
PR = err_PC; /* IRQ restarts instruction */ | |
break; | |
} | |
WriteW (ma, ReadW (ma) & ~I_DEVMASK | AR); /* set SC into instruction */ | |
PR = (PR + 1) & VAMASK; /* 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) & VAMASK; /* P+3 found return */ | |
break; | |
} | |
BR = (BR + op[0].word) & DMASK; /* increment buffer ptr */ | |
XR = (XR - 1) & DMASK; /* 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_PC, not [PR - err_PC - 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) */ | |
reason = cpu_iog (CLF_0, iotrap); /* 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? */ | |
reason = cpu_iog (CLC_0 + priv_fence, iotrap); /* CLC SC on priv fence */ | |
reason = cpu_iog (STF_0 + priv_fence, iotrap); /* STF SC on priv fence */ | |
if (cpu_get_intbl (DMA1) & SIGN) /* DCPC 1 active? */ | |
reason = cpu_iog (STC_0 + DMA1, iotrap); /* STC 6 to enable IRQ on DCPC 1 */ | |
if (cpu_get_intbl (DMA2) & SIGN) /* DCPC 2 active? */ | |
reason = cpu_iog (STC_0 + DMA2, iotrap); /* STC 7 to enable IRQ on DCPC 2 */ | |
} | |
break; | |
case 007: /* .LLS 105347 (OP_KK) */ | |
AR = AR & ~SIGN; /* clear sign bit of A */ | |
while ((AR != 0) && ((AR & SIGN) == 0)) { /* end of list or bad list? */ | |
key = ReadW ((AR + op[1].word) & VAMASK); /* 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) & VAMASK; /* P+4 arg not found */ | |
else if ((AR & SIGN) == 0) /* good link? */ | |
PR = (PR + 2) & VAMASK; /* P+5 arg found */ | |
tprintf (cpu_dev, TRACE_OPND, OPND_FORMAT " return location is P+%u (%s)\n", | |
PR, IR, PR - err_PC, list [PR - err_PC - 3]); | |
break; | |
case 010: /* .SIP 105350 (OP_N) */ | |
reason = cpu_iog (STF_0, iotrap); /* turn interrupt system on */ | |
irq = calc_int (); /* check for interrupt requests */ | |
reason = cpu_iog (CLF_0, iotrap); /* turn interrupt system off */ | |
if (irq) /* was interrupt pending? */ | |
PR = (PR + 1) & VAMASK; /* 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 */ | |
reason = cpu_iog (STF_0, iotrap); /* turn interrupt system on */ | |
ion_defer = FALSE; /* kill defer so irq occurs immed */ | |
break; | |
case 012: /* .CPM 105352 (OP_KK) */ | |
if (INT16 (op[0].word) > INT16 (op[1].word)) | |
PR = (PR + 2) & VAMASK; /* P+5 arg1 > arg2 */ | |
else if (INT16 (op[0].word) < INT16 (op[1].word)) | |
PR = (PR + 1) & VAMASK; /* P+4 arg1 < arg2 */ | |
tprintf (cpu_dev, TRACE_OPND, OPND_FORMAT " return location is P+%u (%s)\n", | |
PR, IR, PR - err_PC, compare [PR - err_PC - 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++ & DMASK); | |
for (eqta = eqt12; eqta <= eqt15; eqta++) /* init EQT12-EQT15 */ | |
WriteW (eqta, AR++ & DMASK); /* (not contig with EQT1-11) */ | |
} | |
AR = AR & DMASK; /* ensure wraparound */ | |
break; | |
case 014: /* .ENTN/$DCPC 105354 (OP_N) */ | |
if (iotrap) { /* in trap cell? */ | |
reason = cpu_save_state (iotrap); /* DMA interrupt */ | |
AR = cpu_get_intbl (CIR) & ~SIGN; /* get intbl value and strip sign */ | |
goto DEVINT; /* vector by intbl value */ | |
} | |
else { /* .ENTN instruction */ | |
ma = (PR - 2) & VAMASK; /* get addr of entry point */ | |
ENTX: /* enter here from .ENTC */ | |
reason = cpu_ops (OP_A, op, intrq); /* 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) & VAMASK); /* 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 */ | |
ma = ReadW (sa); /* get addr of actual */ | |
sa = (sa + 1) & VAMASK; /* increment address */ | |
reason = resolve (ma, &ma, intrq); /* resolve indirect */ | |
if (reason != SCPE_OK) { /* resolution failed? */ | |
PR = err_PC; /* irq restarts instruction */ | |
break; | |
} | |
WriteW (da, ma); /* put addr into formal */ | |
da = (da + 1) & VAMASK; /* increment address */ | |
} | |
if (entry == 016) /* call was .ENTC? */ | |
AR = sa; /* set A to return address */ | |
} | |
break; | |
case 015: /* $OTST/$MPV 105355 (OP_N) */ | |
if (iotrap) { /* in trap cell? */ | |
tprintf (cpu_dev, TRACE_OPND, OPND_FORMAT " entry is for a %s\n", | |
PR, IR, | |
(mp_viol & SIGN | |
? "parity error" | |
: (mp_mevff == SET | |
? "dynamic mapping violation" | |
: "memory protect violation"))); | |
reason = cpu_save_state (iotrap); /* MP/DMS/PE interrupt */ | |
vectors = ReadW (vctr); /* get address of vectors (in SMAP) */ | |
if (mp_viol & SIGN) { /* parity error? */ | |
WriteW (vectors + cic_offset, PR); /* save point of suspension in $CIC */ | |
PR = ReadW (vectors + perr_offset); /* vector to $PERR for processing */ | |
} | |
else { /* MP/DMS violation */ | |
cpu_save_regs (iotrap); /* save CPU registers */ | |
PR = ReadW (vectors + rqst_offset); /* vector to $RQST 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) & VAMASK; /* 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_PC, not [PR - err_PC - 1]); | |
} | |
break; | |
case 016: /* .ENTC/$DEV 105356 (OP_N) */ | |
if (iotrap) { /* in trap cell? */ | |
reason = cpu_save_state (iotrap); /* 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) & VAMASK; /* get addr of entry point */ | |
goto ENTX; /* continue with common processing */ | |
} | |
break; | |
case 017: /* .DSPI/$TBG 105357 (OP_N) */ | |
if (iotrap) { /* in trap cell? */ | |
reason = cpu_save_state (iotrap); /* 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; | |
} |