/* hp2100_cpu6.c: HP 1000 RTE-6/VM OS instructions | |
Copyright (c) 2006-2012, 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 | |
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) | |
Additional references are listed with the associated firmware | |
implementations, as are the HP option model numbers pertaining to the | |
applicable CPUs. | |
*/ | |
#include <setjmp.h> | |
#include "hp2100_defs.h" | |
#include "hp2100_cpu.h" | |
#include "hp2100_cpu1.h" | |
/* external variables */ | |
extern jmp_buf save_env; /* MP abort handler */ | |
/* Offsets to data and addresses within RTE. */ | |
static const uint32 xi = 0001647; /* XI address */ | |
static const uint32 intba = 0001654; /* INTBA address */ | |
static const uint32 intlg = 0001655; /* INTLG address */ | |
static const uint32 eqt1 = 0001660; /* EQT1 address */ | |
static const uint32 eqt11 = 0001672; /* EQT11 address */ | |
static const uint32 pvcn = 0001712; /* PVCN address */ | |
static const uint32 xsusp = 0001730; /* XSUSP address */ | |
static const uint32 dummy = 0001737; /* DUMMY address */ | |
static const uint32 mptfl = 0001770; /* MPTFL address */ | |
static const uint32 eqt12 = 0001771; /* EQT12 address */ | |
static const uint32 eqt15 = 0001774; /* EQT15 address */ | |
static const uint32 vctr = 0002000; /* VCTR address */ | |
static const uint32 CLC_0 = 0004700; /* CLC 0 instruction */ | |
static const uint32 STC_0 = 0000700; /* STC 0 instruction */ | |
static const uint32 CLF_0 = 0001100; /* CLF 0 instruction */ | |
static const uint32 STF_0 = 0000100; /* STF 0 instruction */ | |
static const uint32 SFS_0_C = 0003300; /* SFS 0,C instruction */ | |
enum vctr_offsets { 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 }; /* $LIBR return */ | |
/* RTE-6/VM Operating System Instructions | |
The OS instructions were added to acccelerate 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. | |
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 (0 = normal instruction, 1 = 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, 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 "iogrp" 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 uses 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. Because of the volume of calls to the OS firmware, debug printouts | |
attempt to write only one line per instruction invocation. This means | |
that calling and returned register values are printed separately, with a | |
newline added at the end of execution. However, many instructions can MP | |
or DM abort, either intentionally or due to improper use. That would | |
leave debug lines without the required trailing newlines. | |
There are two ways to address this: either we could replace the CPU's | |
setjmp buffer with one that points to a routine that adds the missing | |
newline, or we can add a semaphore that is tested on entry to see if it | |
is already set, implying a longjmp occurred, and then add the newline if | |
so. The latter would add the newline when the MP trap cell instruction | |
was entered or when the next user-level instruction was executed. | |
However, the merged-line problem would still exist if some other module | |
generated intervening debug printouts. So we do the former. This does | |
mean that this routine must be changed if the MP abort mechanism is | |
changed. | |
7. 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. | |
8. 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. | |
Additional references: | |
- RTE-6/VM OS Microcode Source (92084-18831, revision 8). | |
- RTE-6/VM Technical Specifications (92084-90015, Apr-1983). | |
*/ | |
/* 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 (uint32 iotrap) | |
{ | |
uint16 save_area, priv_fence; | |
t_stat reason = SCPE_OK; | |
save_area = ReadW (xsusp); /* addr of PABEO save area */ | |
WriteW (save_area + 0, PC); /* 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 = iogrp (STC_0 + priv_fence, iotrap); /* STC SC on priv fence */ | |
reason = iogrp (CLC_0 + DMA1, iotrap); /* CLC 6 to inh IRQ on DCPC 1 */ | |
reason = iogrp (CLC_0 + DMA2, iotrap); /* CLC 7 to inh IRQ on DCPC 2 */ | |
reason = iogrp (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 in RTE. Saving the | |
registers would overwrite the user's registers that were saved at RTE entry. | |
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) | |
{ | |
uint16 vectors; | |
uint32 saved_PC, int_sys_off; | |
t_stat reason; | |
saved_PC = PC; /* save current PC */ | |
reason = iogrp (SFS_0_C, iotrap); /* turn interrupt system off */ | |
int_sys_off = (PC == saved_PC); /* set flag if already off */ | |
PC = saved_PC; /* restore PC 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, intaddr); /* save select code */ | |
WriteW (mptfl, 1); /* show MP is off */ | |
if (intaddr != 5) { /* only if not MP interrupt */ | |
reason = iogrp (CLF_0 + intaddr, 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. | |
*/ | |
uint32 cpu_get_intbl (uint32 select_code) | |
{ | |
uint16 interrupt_table; /* interrupt table (starts with SC 06) */ | |
uint16 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 OS instruction dispatcher. | |
Debugging printouts are provided with the OS and OSTBG debug flags. The OS | |
flag enables tracing for all instructions except for the three-instruction | |
sequence executed for the time-base generator interrupt ($TBG, .TICK, and | |
.IRT). The OSTBG flag enables tracing for just the TBG sequence. The flags | |
are separate, as the TBG generates 100 interrupts per second. Use caution | |
when specifying the OSTBG flag, as the debug output file will grow rapidly. | |
Note that the OS flag enables the .IRT instruction trace for all cases except | |
a TBG interrupt. | |
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. | |
Howwever, 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 | |
*/ | |
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, uint32 iotrap) | |
{ | |
t_stat reason = SCPE_OK; | |
OPS op; | |
OP_PAT pattern; | |
uint32 entry, count, cp, sa, da, i, ma; | |
uint16 vectors, save_area, priv_fence, eoreg, eqt, key; | |
char test[6], target[6]; | |
jmp_buf mp_handler; | |
int abortval; | |
t_bool debug_print; | |
static t_bool tbg_tick = FALSE; /* set if processing TBG interrupt */ | |
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 */ | |
} | |
tbg_tick = tbg_tick || (IR == 0105357) && iotrap; /* set TBG interrupting flag */ | |
debug_print = (DEBUG_PRI (cpu_dev, DEB_OS) && !tbg_tick) || | |
(DEBUG_PRI (cpu_dev, DEB_OSTBG) && tbg_tick); | |
if (debug_print) { | |
fprintf (sim_deb, ">>CPU OS: IR = %06o (", IR); /* print preamble and IR */ | |
fprint_sym (sim_deb, (iotrap ? intaddr : err_PC), /* print instruction mnemonic */ | |
(t_value *) &IR, NULL, SWMASK('M')); | |
fputc (')', sim_deb); | |
fprint_ops (pattern, op); /* print operands */ | |
memcpy (mp_handler, save_env, sizeof (jmp_buf)); /* save original MP handler */ | |
abortval = setjmp (save_env); /* set new MP abort handler */ | |
if (abortval != 0) { /* MP abort? */ | |
fputs ("...MP abort\n", sim_deb); /* report it and terminate line */ | |
memcpy (save_env, mp_handler, sizeof (jmp_buf)); /* restore original MP handler */ | |
longjmp (save_env, abortval); /* transfer to MP handler */ | |
} | |
} | |
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) */ | |
PC = 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 = iogrp (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, (PC - 2) & VAMASK); /* set point of suspension */ | |
else /* system map current */ | |
WriteW (save_area, (PC - 2) & VAMASK); /* set point of suspension */ | |
} | |
WriteW (pvcn, (ReadW (pvcn) + 1) & DMASK); /* increment priv nest counter */ | |
} | |
break; | |
case 001: /* $LIBX 105341 (OP_A) */ | |
PC = 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 */ | |
PC = ReadW (vectors + lxnd_offset); /* vector to $LXND for processing */ | |
} | |
break; | |
case 002: /* .TICK 105342 (OP_N) */ | |
if (debug_print) /* debugging? */ | |
fprint_regs (",", REG_A | REG_B, 0); /* print entry registers */ | |
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+0 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? */ | |
PC = (PC + 1) & VAMASK; /* P+1 return for no timeout */ | |
if (debug_print) /* debugging? */ | |
fprint_regs ("; result:", /* print return registers */ | |
REG_A | REG_B | REG_P_REL, | |
err_PC + 1); | |
break; | |
case 003: /* .TNAM 105343 (OP_N) */ | |
if (debug_print) /* debugging? */ | |
fprint_regs (",", REG_A | REG_B, 0); /* print entry registers */ | |
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+0 */ | |
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 */ | |
PC = (PC + 1) & VAMASK; /* P+1 for found return */ | |
break; | |
} | |
AR = (AR + 1) & DMASK; /* bump to next keyword */ | |
key = ReadW (AR); /* get next keyword */ | |
}; | |
if (debug_print) /* debugging? */ | |
fprint_regs ("; result:", /* print return registers */ | |
REG_A | REG_B | REG_E | REG_P_REL, | |
err_PC + 1); | |
break; | |
case 004: /* .STIO 105344 (OP_A) */ | |
count = op[0].word - PC; /* get count of operands */ | |
if (debug_print) /* debugging? */ | |
fprintf (sim_deb, /* print registers on entry */ | |
", A = %06o, count = %d", AR, count); | |
for (i = 0; i < count; i++) { | |
ma = ReadW (PC); /* get operand address */ | |
reason = resolve (ma, &ma, intrq); /* resolve indirect */ | |
if (reason != SCPE_OK) { /* resolution failed? */ | |
PC = err_PC; /* IRQ restarts instruction */ | |
break; | |
} | |
WriteW (ma, ReadW (ma) & ~I_DEVMASK | AR); /* set SC into instruction */ | |
PC = (PC + 1) & VAMASK; /* bump to next */ | |
} | |
break; | |
case 005: /* .FNW 105345 (OP_K) */ | |
if (debug_print) /* debugging? */ | |
fprint_regs (",", REG_A | REG_B | REG_X, 0); /* print entry registers */ | |
while (XR != 0) { /* all comparisons done? */ | |
key = ReadW (BR); /* read a buffer word */ | |
if (key == AR) { /* does it match? */ | |
PC = (PC + 1) & VAMASK; /* P+1 found return */ | |
break; | |
} | |
BR = (BR + op[0].word) & DMASK; /* increment buffer ptr */ | |
XR = (XR - 1) & DMASK; /* decrement remaining count */ | |
} | |
/* P+0 not found return */ | |
if (debug_print) /* debugging? */ | |
fprint_regs ("; result:", /* print return registers */ | |
REG_A | REG_B | REG_X | REG_P_REL, | |
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 = iogrp (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 = iogrp (CLC_0 + priv_fence, iotrap); /* CLC SC on priv fence */ | |
reason = iogrp (STF_0 + priv_fence, iotrap); /* STF SC on priv fence */ | |
if (cpu_get_intbl (DMA1) & SIGN) /* DCPC 1 active? */ | |
reason = iogrp (STC_0 + DMA1, iotrap); /* STC 6 to enable IRQ on DCPC 1 */ | |
if (cpu_get_intbl (DMA2) & SIGN) /* DCPC 2 active? */ | |
reason = iogrp (STC_0 + DMA2, iotrap); /* STC 7 to enable IRQ on DCPC 2 */ | |
} | |
tbg_tick = 0; /* .IRT terminates TBG servicing */ | |
break; | |
case 007: /* .LLS 105347 (OP_KK) */ | |
if (debug_print) /* debugging? */ | |
fprint_regs (",", REG_A | REG_B | REG_E, 0); /* print entry registers */ | |
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? */ | |
PC = (PC + 1) & VAMASK; /* P+1 arg not found */ | |
else if ((AR & SIGN) == 0) /* good link? */ | |
PC = (PC + 2) & VAMASK; /* P+2 arg found */ | |
/* P+0 bad link */ | |
if (debug_print) /* debugging? */ | |
fprint_regs ("; result:", /* print return registers */ | |
REG_A | REG_B | REG_P_REL, | |
err_PC + 3); | |
break; | |
case 010: /* .SIP 105350 (OP_N) */ | |
reason = iogrp (STF_0, iotrap); /* turn interrupt system on */ | |
intrq = calc_int (); /* check for interrupt requests */ | |
reason = iogrp (CLF_0, iotrap); /* turn interrupt system off */ | |
if (intrq) /* was interrupt pending? */ | |
PC = (PC + 1) & VAMASK; /* P+1 return for pending IRQ */ | |
/* P+0 return for no pending IRQ */ | |
if (debug_print) /* debugging? */ | |
fprintf (sim_deb, /* print return registers */ | |
", CIR = %02o, return = P+%d", | |
intrq, PC - (err_PC + 1)); | |
break; | |
case 011: /* .YLD 105351 (OP_C) */ | |
PC = op[0].word; /* pick up point of resumption */ | |
reason = iogrp (STF_0, iotrap); /* turn interrupt system on */ | |
ion_defer = 0; /* kill defer so irq occurs immed */ | |
break; | |
case 012: /* .CPM 105352 (OP_KK) */ | |
if (INT16 (op[0].word) > INT16 (op[1].word)) | |
PC = (PC + 2) & VAMASK; /* P+2 arg1 > arg2 */ | |
else if (INT16 (op[0].word) < INT16 (op[1].word)) | |
PC = (PC + 1) & VAMASK; /* P+1 arg1 < arg2 */ | |
/* P+0 arg1 = arg2 */ | |
if (debug_print) /* debugging? */ | |
fprint_regs (",", REG_P_REL, err_PC + 3); /* print return registers */ | |
break; | |
case 013: /* .ETEQ 105353 (OP_N) */ | |
eqt = ReadW (eqt1); /* get addr of EQT1 */ | |
if (AR != eqt) { /* already set up? */ | |
for (eqt = eqt1; eqt <= eqt11; eqt++) /* init EQT1-EQT11 */ | |
WriteW (eqt & VAMASK, (AR++ & DMASK)); | |
for (eqt = eqt12; eqt <= eqt15; eqt++) /* init EQT12-EQT15 */ | |
WriteW (eqt & VAMASK, (AR++ & DMASK)); /* (not contig with EQT1-11) */ | |
} | |
if (debug_print) /* debugging? */ | |
fprintf (sim_deb, /* print return registers */ | |
", A = %06o, EQT1 = %06o", AR, eqt); | |
break; | |
case 014: /* .ENTN/$DCPC 105354 (OP_N) */ | |
if (iotrap) { /* in trap cell? */ | |
reason = cpu_save_state (iotrap); /* DMA interrupt */ | |
AR = cpu_get_intbl (intaddr) & ~SIGN; /* get intbl value and strip sign */ | |
goto DEVINT; /* vector by intbl value */ | |
} | |
else { /* .ENTN instruction */ | |
ma = (PC - 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 */ | |
if (debug_print) /* debugging? */ | |
fprintf (sim_deb, /* print entry registers */ | |
", op [0] = %06o, pcount = %d", | |
da, 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? */ | |
PC = 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? */ | |
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, PC); /* save point of suspension in $CIC */ | |
PC = ReadW (vectors + perr_offset); /* vector to $PERR for processing */ | |
} | |
else { /* MP/DMS violation */ | |
cpu_save_regs (iotrap); /* save CPU registers */ | |
PC = ReadW (vectors + rqst_offset); /* vector to $RQST for processing */ | |
} | |
if (debug_print) { /* debugging? */ | |
fprint_regs (",", REG_CIR, 0); /* print interrupt source */ | |
/* and cause */ | |
if (mp_viol & SIGN) | |
fputs (", parity error", sim_deb); | |
else if (mp_mevff) | |
fputs (", DM violation", sim_deb); | |
else | |
fputs (", MP violation", sim_deb); | |
} | |
} | |
else { /* self-test instruction */ | |
YR = 0000000; /* RPL switch (not implemented) */ | |
AR = 0000000; /* LDR [B] (not implemented) */ | |
SR = 0102077; /* test passed code */ | |
PC = (PC + 1) & VAMASK; /* P+1 return for firmware OK */ | |
if ((cpu_dev.dctrl & DEB_OS) && /* OS debug flag set, */ | |
(sim_deb == NULL)) /* but debugging disabled? */ | |
XR = 0; /* rev = 0 means RTE won't use ucode */ | |
else | |
XR = 010; /* firmware revision 10B = 8 */ | |
if (debug_print) /* debugging? */ | |
fprint_regs (",", REG_X | REG_P_REL, /* print return registers */ | |
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 (intaddr); /* get interrupt table value */ | |
DEVINT: | |
vectors = ReadW (vctr); /* get address of vectors (in SMAP) */ | |
if (INT16 (AR) < 0) /* negative (program ID)? */ | |
PC = ReadW (vectors + sked_offset); /* vector to $SKED for processing */ | |
else if (AR > 0) /* positive (EQT address)? */ | |
PC = ReadW (vectors + cic2_offset); /* vector to $CIC2 for processing */ | |
else /* zero (illegal interrupt) */ | |
PC = ReadW (vectors + cic4_offset); /* vector to $CIC4 for processing */ | |
if (debug_print) /* debugging? */ | |
fprintf (sim_deb, /* print return registers */ | |
", CIR = %02o, INTBL = %06o", | |
intaddr, AR); | |
} | |
else { /* .ENTC instruction */ | |
ma = (PC - 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) */ | |
PC = ReadW (vectors + clck_offset); /* vector to $CLCK for processing */ | |
if (debug_print) /* debugging? */ | |
fprint_regs (",", REG_CIR, 0); /* print interrupt source */ | |
} | |
else /* .DSPI instruction */ | |
reason = stop_inst; /* not implemented yet */ | |
break; | |
} | |
if (debug_print) { /* debugging? */ | |
fputc ('\n', sim_deb); /* terminate line */ | |
memcpy (save_env, mp_handler, sizeof (jmp_buf)); /* restore original MP handler */ | |
} | |
return reason; | |
} |