simh-testsetgenerator/HP2100/hp2100_cpu1.c
2020-02-16 22:25:15 -08:00

759 lines
37 KiB
C

/* hp2100_cpu1.c: HP 2100/1000 EAU/FP/IOP microcode simulator
Copyright (c) 2005-2016, Robert M. Supnik
Copyright (c) 2017-2019, J. David Bryan
Permission is hereby granted, free of charge, to any person obtaining a copy
of this software and associated documentation files (the "Software"), to deal
in the Software without restriction, including without limitation the rights
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
copies of the Software, and to permit persons to whom the Software is
furnished to do so, subject to the following conditions:
The above copyright notice and this permission notice shall be included in
all copies or substantial portions of the Software.
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
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 names of the authors shall not be
used in advertising or otherwise to promote the sale, use or other dealings
in this Software without prior written authorization from the authors.
CPU1 Extended Arithmetic Unit, Floating Point, and I/O Processor
instructions
23-Jan-19 JDB Moved fmt_ab to hp2100_cpu5.c
02-Oct-18 JDB Replaced DMASK with D16_MASK or R_MASK as appropriate
02-Aug-18 JDB Moved UIG dispatcher to hp2100_cpu0.c
Moved FP and IOP dispatchers from hp2100_cpu2.c
25-Jul-18 JDB Use cpu_configuration instead of cpu_unit.flags for tests
07-Sep-17 JDB Removed unnecessary "uint16" casts
01-Aug-17 JDB Changed TIMER and RRR 16 to test for undefined stops
07-Jul-17 JDB Changed "iotrap" from uint32 to t_bool
26-Jun-17 JDB Replaced SEXT with SEXT16
22-Apr-17 JDB Improved the EAU shift/rotate instructions
21-Mar-17 JDB Fixed UIG 1 comment regarding 2000 IOP and F-Series
31-Jan-17 JDB Added fmt_ab to print A/B-register error codes
30-Jan-17 JDB Removed fprint_ops, fprint_regs (now redundant)
05-Aug-16 JDB Renamed the P register from "PC" to "PR"
24-Dec-14 JDB Added casts for explicit downward conversions
05-Apr-14 JDB Corrected typo in comments for cpu_ops
09-May-12 JDB Separated assignments from conditional expressions
11-Sep-08 JDB Moved microcode function prototypes to hp2100_cpu1.h
05-Sep-08 JDB Moved option-present tests to UIG dispatchers
Call "user microcode" dispatcher for unclaimed UIG instructions
20-Apr-08 JDB Fixed VIS and SIGNAL to depend on the FPP and HAVE_INT64
28-Nov-07 JDB Added fprint_ops, fprint_regs for debug printouts
17-Nov-07 JDB Enabled DIAG as NOP on 1000 F-Series
04-Jan-07 JDB Added special DBI dispatcher for non-INT64 diagnostic
29-Dec-06 JDB Allows RRR as NOP if 2114 (diag config test)
01-Dec-06 JDB Substitutes FPP for firmware FP if HAVE_INT64
16-Oct-06 JDB Generalized operands for F-Series FP types
26-Sep-06 JDB Split hp2100_cpu1.c into multiple modules to simplify extensions
Added iotrap parameter to UIG dispatchers for RTE microcode
22-Feb-05 JDB Removed EXECUTE instruction (is NOP in actual microcode)
21-Jan-05 JDB Reorganized CPU option and operand processing flags
Split code along microcode modules
15-Jan-05 RMS Cloned from hp2100_cpu.c
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)
Additional references are listed with the associated firmware
implementations, as are the HP option model numbers pertaining to the
applicable CPUs.
This source file contains the Extended Arithmetic Unit simulator, the
single-precision floating-pointer simulator, and the HP 2000 I/O Processor
instructions simulator.
*/
#include "hp2100_defs.h"
#include "hp2100_cpu.h"
#include "hp2100_cpu_dmm.h"
#if !defined (HAVE_INT64) /* int64 support unavailable */
#include "hp2100_cpu_fp.h"
#endif
/* EAU.
The Extended Arithmetic Unit (EAU) adds ten instructions with double-word
operands, including multiply, divide, shifts, and rotates. Option
implementation by CPU was as follows:
2114 2115 2116 2100 1000-M 1000-E 1000-F
------ ------ ------ ------ ------ ------ ------
N/A 12579A 12579A std std std std
The instruction codes are mapped to routines as follows:
Instr. Bits
Code 15-8 7-4 2116 2100 1000-M 1000-E 1000-F Note
------ ---- --- ------ ------ ------ ------ ------ ---------------------
100000 200 00 [diag] [diag] [self test]
100020 200 01 ASL ASL ASL ASL ASL Bits 3-0 encode shift
100040 200 02 LSL LSL LSL LSL LSL Bits 3-0 encode shift
100060 200 03 TIMER TIMER [deterministic delay]
100100 200 04 RRL RRL RRL RRL RRL Bits 3-0 encode shift
100200 200 10 MPY MPY MPY MPY MPY
100400 201 xx DIV DIV DIV DIV DIV
101020 202 01 ASR ASR ASR ASR ASR Bits 3-0 encode shift
101040 202 02 LSR LSR LSR LSR LSR Bits 3-0 encode shift
101100 202 04 RRR RRR RRR RRR RRR Bits 3-0 encode shift
104200 210 xx DLD DLD DLD DLD DLD
104400 211 xx DST DST DST DST DST
The remaining codes for bits 7-4 are undefined and will cause a simulator
stop if enabled. On a real 1000-M, all undefined instructions in the 200
group decode as MPY, and all in the 202 group decode as NOP. On a real
1000-E, instruction patterns 200/05 through 200/07 and 202/03 decode as NOP;
all others cause erroneous execution.
EAU instruction decoding on the 1000 M-series is convoluted. The JEAU
microorder maps IR bits 11, 9-7 and 5-4 to bits 2-0 of the microcode jump
address. The map is detailed on page IC-84 of the ERD.
The 1000 E/F-series add two undocumented instructions to the 200 group: TIMER
and DIAG. These are described in the ERD on page IA 5-5, paragraph 5-7. The
M-series executes these as MPY and RRL, respectively. A third instruction,
EXECUTE (100120), is also described but was never implemented, and the
E/F-series microcode execute a NOP for this instruction code.
If the EAU is not installed in a 2115 or 2116, EAU instructions execute as
NOPs or cause unimplemented instruction stops if enabled.
Implementation notes:
1. Under simulation, TIMER and DIAG cause undefined instruction stops if the
CPU is not an E/F-Series. Note that TIMER is intentionally executed by
several HP programs to differentiate between M- and E/F-series machines.
2. DIAG is not implemented under simulation. On the E/F, it performs a
destructive test of all installed memory. Because of this, it is only
functional if the machine is halted, i.e., if the instruction is
executed with the INSTR STEP button. If it is executed in a program,
the result is NOP.
3. The RRR 16 instruction is intentionally executed by the diagnostic
configurator on the 2114, which does not have an EAU, to differentiate
between 2114 and 2100/1000 CPUs.
4. The shift count is calculated unconditionally, as six of the ten
instructions will be using the value.
5. An arithmetic left shift must be handled as a special case because the
shifted operand bits "skip over" the sign bit. That is, the bits are
lost from the next-most-significant bit while preserving the MSB. For
all other shifts, including the arithmetic right shift, the operand may
be shifted and then merged with the appropriate fill bits.
6. The C standard specifies that the results of bitwise shifts with negative
signed operands are undefined (for left shifts) or implementation-defined
(for right shifts). Therefore, we must use unsigned operands and handle
arithmetic shifts explicitly.
*/
t_stat cpu_eau (void)
{
t_stat reason = SCPE_OK;
OPS op;
uint32 rs, qs, v1, v2, operand, fill, mask, shift;
int32 sop1, sop2;
if (!(cpu_configuration & CPU_EAU)) /* if the EAU is not installed */
return STOP (cpu_ss_unimpl); /* then the instructions execute as NOPs */
if (IR & 017) /* if the shift count is 1-15 */
shift = IR & 017; /* then use it verbatim */
else /* otherwise the count iz zero */
shift = 16; /* so use a shift count of 16 */
switch ((IR >> 8) & 0377) { /* decode IR<15:8> */
case 0200: /* EAU group 0 */
switch ((IR >> 4) & 017) { /* decode IR<7:4> */
case 000: /* DIAG 100000 */
if (!(cpu_configuration & CPU_1000_E_F)) /* if the CPU is not an E- or F-series */
return STOP (cpu_ss_undef); /* then the instruction is undefined */
break; /* and executes as NOP */
case 001: /* ASL 100020-100037 */
operand = TO_DWORD (BR, AR); /* form the double-word operand */
mask = D32_UMAX << 31 - shift; /* form a mask for the bits that will be lost */
if (operand & D32_SIGN) /* if the operand is negative */
O = (~operand & mask & D32_MASK) != 0; /* then set overflow if any of the lost bits are zeros */
else /* otherwise it's positive */
O = (operand & mask & D32_MASK) != 0; /* so set overflow if any of the lost bits are ones */
operand = operand << shift & D32_SMAX /* shift the operand left */
| operand & D32_SIGN; /* while keeping the original sign bit */
BR = UPPER_WORD (operand); /* split the operand */
AR = LOWER_WORD (operand); /* into its constituent parts */
break;
case 002: /* LSL 100040-100057 */
operand = TO_DWORD (BR, AR) << shift; /* shift the double-word operand left */
BR = UPPER_WORD (operand); /* split the operand */
AR = LOWER_WORD (operand); /* into its constituent parts */
break;
case 004: /* RRL 100100-100117 */
operand = TO_DWORD (BR, AR); /* form the double-word operand */
fill = operand; /* and fill with operand bits */
operand = operand << shift /* rotate the operand left */
| fill >> 32 - shift; /* while filling in on the right */
BR = UPPER_WORD (operand); /* split the operand */
AR = LOWER_WORD (operand); /* into its constituent parts */
break;
case 003: /* TIMER 100060 */
if (cpu_configuration & CPU_1000_E_F) { /* if the CPU is an E- or F-series */
BR = BR + 1 & R_MASK; /* then increment B */
if (BR != 0) /* if B did not roll over */
PR = err_PR; /* then repeat the instruction */
break;
}
else { /* otherwise it's a 21xx or 1000 M-Series */
reason = STOP (cpu_ss_undef); /* and the instruction is undefined */
if (reason != SCPE_OK /* if a stop is indicated */
|| cpu_configuration & CPU_21XX) /* or the CPU is a 21xx */
break; /* then the instruction executes as NOP */
}
/* fall through into the MPY case if 1000 M-Series */
case 010: /* MPY 100200 (OP_K) */
reason = cpu_ops (OP_K, op); /* get operand */
if (reason == SCPE_OK) { /* successful eval? */
sop1 = SEXT16 (AR); /* sext AR */
sop2 = SEXT16 (op[0].word); /* sext mem */
sop1 = sop1 * sop2; /* signed mpy */
BR = UPPER_WORD (sop1); /* to BR'AR */
AR = LOWER_WORD (sop1);
O = 0; /* no overflow */
}
break;
default: /* others undefined */
return STOP (cpu_ss_unimpl);
}
break;
case 0201: /* DIV 100400 (OP_K) */
reason = cpu_ops (OP_K, op); /* get operand */
if (reason != SCPE_OK) /* eval failed? */
break;
rs = qs = BR & D16_SIGN; /* save divd sign */
if (rs) { /* neg? */
AR = (~AR + 1) & R_MASK; /* make B'A pos */
BR = (~BR + (AR == 0)) & R_MASK; /* make divd pos */
}
v2 = op[0].word; /* divr = mem */
if (v2 & D16_SIGN) { /* neg? */
v2 = (~v2 + 1) & D16_MASK; /* make divr pos */
qs = qs ^ D16_SIGN; /* sign of quotient */
}
if (BR >= v2) /* if the divisor is too small */
O = 1; /* then set overflow */
else { /* maybe... */
O = 0; /* assume ok */
v1 = (BR << 16) | AR; /* 32b divd */
AR = (v1 / v2) & R_MASK; /* quotient */
BR = (v1 % v2) & R_MASK; /* remainder */
if (AR) { /* quotient > 0? */
if (qs) /* apply quo sign */
AR = NEG16 (AR);
if ((AR ^ qs) & D16_SIGN) /* still wrong? ovflo */
O = 1;
}
if (rs)
BR = NEG16 (BR); /* apply rem sign */
}
break;
case 0202: /* EAU group 2 */
switch ((IR >> 4) & 017) { /* decode IR<7:4> */
case 001: /* ASR 101020-101037 */
O = 0; /* clear ovflo */
operand = TO_DWORD (BR, AR); /* form the double-word operand */
fill = (operand & D32_SIGN ? ~0 : 0); /* and fill with copies of the sign bit */
operand = operand >> shift /* shift the operand right */
| fill << 32 - shift; /* while filling in with sign bits */
BR = UPPER_WORD (operand); /* split the operand */
AR = LOWER_WORD (operand); /* into its constituent parts */
break;
case 002: /* LSR 101040-101057 */
operand = TO_DWORD (BR, AR) >> shift; /* shift the double-word operand right */
BR = UPPER_WORD (operand); /* split the operand */
AR = LOWER_WORD (operand); /* into its constituent parts */
break;
case 004: /* RRR 101100-101117 */
operand = TO_DWORD (BR, AR); /* form the double-word operand */
fill = operand; /* and fill with operand bits */
operand = operand >> shift /* rotate the operand right */
| fill << 32 - shift; /* while filling in on the left */
BR = UPPER_WORD (operand); /* split the operand */
AR = LOWER_WORD (operand); /* into its constituent parts */
break;
default: /* others undefined */
return STOP (cpu_ss_undef);
}
break;
case 0210: /* DLD 104200 (OP_D) */
reason = cpu_ops (OP_D, op); /* get operand */
if (reason == SCPE_OK) { /* successful eval? */
AR = UPPER_WORD (op[0].dword); /* load AR */
BR = LOWER_WORD (op[0].dword); /* load BR */
}
break;
case 0211: /* DST 104400 (OP_A) */
reason = cpu_ops (OP_A, op); /* get operand */
if (reason == SCPE_OK) { /* successful eval? */
WriteW (op[0].word, AR); /* store AR */
WriteW ((op[0].word + 1) & LA_MASK, BR); /* store BR */
}
break;
default: /* should never get here */
return SCPE_IERR; /* bad call from cpu_instr */
}
return reason;
}
#if !defined (HAVE_INT64) /* int64 support unavailable */
/* Single-Precision Floating Point Instructions.
The 2100 and 1000 CPUs share the single-precision (two word) floating-point
instruction codes. Floating-point firmware was an option on the 2100 and was
standard on the 1000-M and E. The 1000-F had a standard hardware Floating
Point Processor that executed these six instructions and added extended- and
double-precision floating- point instructions, as well as double-integer
instructions (the FPP is simulated separately).
Option implementation by CPU was as follows:
2114 2115 2116 2100 1000-M 1000-E 1000-F
------ ------ ------ ------ ------ ------ ------
N/A N/A N/A 12901A std std N/A
The instruction codes for the 2100 and 1000-M/E systems are mapped to
routines as follows:
Instr. 2100/1000-M/E Description
------ ------------- -----------------------------------
105000 FAD Single real add
105020 FSB Single real subtract
105040 FMP Single real multiply
105060 FDV Single real divide
105100 FIX Single integer to single real fix
105120 FLT Single real to single integer float
Bits 3-0 are not decoded by these instructions, so FAD (e.g.) would be
executed by any instruction in the range 105000-105017.
Implementation note: rather than have two simulators that each executes the
single-precision FP instruction set, we compile conditionally, based on the
availability of 64-bit integer support in the host compiler. 64-bit integers
are required for the FPP, so if they are available, then the FPP is used to
handle the six single-precision instructions for the 2100 and M/E-Series, and
this function is omitted. If support is unavailable, this function is used
instead.
Implementation note: the operands to FAD, etc. are floating-point values, so
OP_F would normally be used. However, the firmware FP support routines want
floating-point operands as 32-bit integer values, so OP_D is used to achieve
this.
*/
static const OP_PAT op_fp[8] = {
OP_D, OP_D, OP_D, OP_D, /* FAD FSB FMP FDV */
OP_N, OP_N, OP_N, OP_N /* FIX FLT --- --- */
};
t_stat cpu_fp (void)
{
t_stat reason = SCPE_OK;
OPS op;
uint32 entry;
entry = (IR >> 4) & 017; /* mask to entry point */
if (op_fp [entry] != OP_N) {
reason = cpu_ops (op_fp [entry], op); /* get instruction operands */
if (reason != SCPE_OK) /* evaluation failed? */
return reason; /* return reason for failure */
}
switch (entry) { /* decode IR<7:4> */
case 000: /* FAD 105000 (OP_D) */
O = f_as (op[0].dword, 0); /* add, upd ovflo */
break;
case 001: /* FSB 105020 (OP_D) */
O = f_as (op[0].dword, 1); /* sub, upd ovflo */
break;
case 002: /* FMP 105040 (OP_D) */
O = f_mul (op[0].dword); /* mul, upd ovflo */
break;
case 003: /* FDV 105060 (OP_D) */
O = f_div (op[0].dword); /* div, upd ovflo */
break;
case 004: /* FIX 105100 (OP_N) */
O = f_fix (); /* fix, upd ovflo */
break;
case 005: /* FLT 105120 (OP_N) */
O = f_flt (); /* float, upd ovflo */
break;
default: /* should be impossible */
return SCPE_IERR;
}
return reason;
}
#endif /* int64 support unavailable */
/* HP 2000 I/O Processor.
The IOP accelerates certain operations of the HP 2000 Time-Share BASIC system
I/O processor. Most 2000 systems were delivered with 2100 CPUs, although IOP
microcode was developed for the 1000-M and 1000-E. As the I/O processors
were specific to the 2000 system, general compatibility with other CPU
microcode options was unnecessary, and indeed no other options were possible
for the 2100.
Option implementation by CPU was as follows:
2114 2115 2116 2100 1000-M 1000-E 1000-F
------ ------ ------ ------ ------ ------ ------
N/A N/A N/A 13206A 13207A 22702A N/A
The routines are mapped to instruction codes as follows:
Instr. 2100 1000-M/E Description
------ ---------- ---------- --------------------------------------------
SAI 105060-117 101400-037 Store A indexed by B (+/- offset in IR<4:0>)
LAI 105020-057 105400-037 Load A indexed by B (+/- offset in IR<4:0>)
CRC 105150 105460 Generate CRC
REST 105340 105461 Restore registers from stack
READF 105220 105462 Read F register (stack pointer)
INS -- 105463 Initialize F register (stack pointer)
ENQ 105240 105464 Enqueue
PENQ 105257 105465 Priority enqueue
DEQ 105260 105466 Dequeue
TRSLT 105160 105467 Translate character
ILIST 105000 105470 Indirect address list (similar to $SETP)
PRFEI 105222 105471 Power fail exit with I/O
PRFEX 105223 105472 Power fail exit
PRFIO 105221 105473 Power fail I/O
SAVE 105362 105474 Save registers to stack
MBYTE 105120 105765 Move bytes (MBT)
MWORD 105200 105777 Move words (MVW)
SBYTE 105300 105764 Store byte (SBT)
LBYTE 105320 105763 Load byte (LBT)
The INS instruction was not required in the 2100 implementation because the
stack pointer was actually the memory protect fence register and so could be
loaded directly with an OTA/B 05. Also, the 1000 implementation did not
offer the MBYTE, MWORD, SBYTE, and LBYTE instructions because the equivalent
instructions from the standard Extended Instruction Group were used instead.
Note that the 2100 MBYTE and MWORD instructions operate slightly differently
from the 1000 MBT and MVW instructions. Specifically, the move count is
signed on the 2100 and unsigned on the 1000. A negative count on the 2100
results in a NOP.
The simulator remaps the 2100 instructions to the 1000 codes. The four EIG
equivalents are dispatched to the EIG simulator. The rest are handled here.
Additional reference:
- HP 2000 Computer System Sources and Listings Documentation
(22687-90020, undated), section 3, pages 2-74 through 2-91
Implementation notes:
1. The SAVE and RESTR instructions use the (otherwise unused) SP register on
the 1000 as the stack pointer. On the 2100, there is no SP register, so
the instructions use the memory protect fence register as the stack
pointer. We update the 2100 fence because it could affect CPU operation
if MP is turned on (although, in practice, the 2100 IOP does not use
memory protect and so never enables it).
*/
static const OP_PAT op_iop[16] = {
OP_V, OP_N, OP_N, OP_N, /* CRC RESTR READF INS */
OP_N, OP_N, OP_N, OP_V, /* ENQ PENQ DEQ TRSLT */
OP_AC, OP_CVA, OP_A, OP_CV, /* ILIST PRFEI PRFEX PRFIO */
OP_N, OP_N, OP_N, OP_N /* SAVE --- --- --- */
};
t_stat cpu_iop (uint32 intrq)
{
t_stat reason = SCPE_OK;
OPS op;
uint8 byte;
uint32 entry, i;
HP_WORD hp, tp, t, wc, MA;
if (cpu_configuration & CPU_2100) { /* 2100 IOP? */
if ((IR >= 0105020) && (IR <= 0105057)) /* remap LAI */
IR = 0105400 | (IR - 0105020);
else if ((IR >= 0105060) && (IR <= 0105117)) /* remap SAI */
IR = 0101400 | (IR - 0105060);
else {
switch (IR) { /* remap others */
case 0105000: IR = 0105470; break; /* ILIST */
case 0105120: return cpu_eig (0105765, intrq); /* MBYTE (maps to MBT) */
case 0105150: IR = 0105460; break; /* CRC */
case 0105160: IR = 0105467; break; /* TRSLT */
case 0105200: return cpu_eig (0105777, intrq); /* MWORD (maps to MVW) */
case 0105220: IR = 0105462; break; /* READF */
case 0105221: IR = 0105473; break; /* PRFIO */
case 0105222: IR = 0105471; break; /* PRFEI */
case 0105223: IR = 0105472; break; /* PRFEX */
case 0105240: IR = 0105464; break; /* ENQ */
case 0105257: IR = 0105465; break; /* PENQ */
case 0105260: IR = 0105466; break; /* DEQ */
case 0105300: return cpu_eig (0105764, intrq); /* SBYTE (maps to SBT) */
case 0105320: return cpu_eig (0105763, intrq); /* LBYTE (maps to LBT) */
case 0105340: IR = 0105461; break; /* REST */
case 0105362: IR = 0105474; break; /* SAVE */
default: /* all others invalid */
return STOP (cpu_ss_unimpl);
}
}
}
entry = IR & 077; /* mask to entry point */
if (entry <= 037) { /* LAI/SAI 10x400-437 */
MA = ((entry - 020) + BR) & LA_MASK; /* +/- offset */
if (IR & AB_MASK) /* if this is an LAI instruction */
AR = ReadW (MA); /* then load the A register */
else /* otherwise */
WriteW (MA, AR); /* store the A register */
return reason;
}
else if (entry <= 057) /* IR = 10x440-457? */
return STOP (cpu_ss_unimpl); /* not part of IOP */
entry = entry - 060; /* offset 10x460-477 */
if (op_iop [entry] != OP_N) {
reason = cpu_ops (op_iop [entry], op); /* get instruction operands */
if (reason != SCPE_OK) /* evaluation failed? */
return reason; /* return reason for failure */
}
switch (entry) { /* decode IR<5:0> */
case 000: /* CRC 105460 (OP_V) */
t = ReadW (op[0].word) ^ (AR & 0377); /* xor prev CRC and char */
for (i = 0; i < 8; i++) { /* apply polynomial */
t = (t >> 1) | ((t & 1) << 15); /* rotate right */
if (t & D16_SIGN) t = t ^ 020001; /* old t<0>? xor */
}
WriteW (op[0].word, t); /* rewrite CRC */
break;
case 001: /* RESTR 105461 (OP_N) */
SPR = (SPR - 1) & LA_MASK; /* decr stack ptr */
t = ReadW (SPR); /* get E and O */
O = ((t >> 1) ^ 1) & 1; /* restore O */
E = t & 1; /* restore E */
SPR = (SPR - 1) & LA_MASK; /* decr sp */
BR = ReadW (SPR); /* restore B */
SPR = (SPR - 1) & LA_MASK; /* decr sp */
AR = ReadW (SPR); /* restore A */
if (cpu_configuration & CPU_2100) /* 2100 keeps sp in MP FR */
mp_fence = SPR; /* (in case MP is turned on) */
break;
case 002: /* READF 105462 (OP_N) */
AR = SPR; /* copy stk ptr */
break;
case 003: /* INS 105463 (OP_N) */
SPR = AR; /* init stk ptr */
break;
case 004: /* ENQ 105464 (OP_N) */
hp = ReadW (AR & LA_MASK); /* addr of head */
tp = ReadW ((AR + 1) & LA_MASK); /* addr of tail */
WriteW ((BR - 1) & LA_MASK, 0); /* entry link */
WriteW ((tp - 1) & LA_MASK, BR); /* tail link */
WriteW ((AR + 1) & LA_MASK, BR); /* queue tail */
if (hp != 0) PR = (PR + 1) & LA_MASK; /* q not empty? skip */
break;
case 005: /* PENQ 105465 (OP_N) */
hp = ReadW (AR & LA_MASK); /* addr of head */
WriteW ((BR - 1) & LA_MASK, hp); /* becomes entry link */
WriteW (AR & LA_MASK, BR); /* queue head */
if (hp == 0) /* q empty? */
WriteW ((AR + 1) & LA_MASK, BR); /* queue tail */
else PR = (PR + 1) & LA_MASK; /* skip */
break;
case 006: /* DEQ 105466 (OP_N) */
BR = ReadW (AR & LA_MASK); /* addr of head */
if (BR) { /* queue not empty? */
hp = ReadW ((BR - 1) & LA_MASK); /* read hd entry link */
WriteW (AR & LA_MASK, hp); /* becomes queue head */
if (hp == 0) /* q now empty? */
WriteW ((AR + 1) & LA_MASK, (AR + 1) & R_MASK);
PR = (PR + 1) & LA_MASK; /* skip */
}
break;
case 007: /* TRSLT 105467 (OP_V) */
wc = ReadW (op[0].word); /* get count */
if (wc & D16_SIGN) break; /* cnt < 0? */
while (wc != 0) { /* loop */
MA = (AR + AR + ReadB (BR)) & LA_MASK;
byte = ReadB (MA); /* xlate */
WriteB (BR, byte); /* store char */
BR = (BR + 1) & R_MASK; /* incr ptr */
wc = (wc - 1) & D16_MASK; /* decr cnt */
if (wc && intrq) { /* more and intr? */
WriteW (op[0].word, wc); /* save count */
PR = err_PR; /* stop for now */
break;
}
}
break;
case 010: /* ILIST 105470 (OP_AC) */
do { /* for count */
WriteW (op[0].word, AR); /* write AR to mem */
AR = (AR + 1) & R_MASK; /* incr AR */
op[0].word = (op[0].word + 1) & LA_MASK; /* incr MA */
op[1].word = (op[1].word - 1) & D16_MASK; /* decr count */
}
while (op[1].word != 0);
break;
case 011: /* PRFEI 105471 (OP_CVA) */
WriteW (op[1].word, 1); /* set flag */
reason = cpu_iog (op[0].word); /* execute I/O instr */
op[0].word = op[2].word; /* set rtn and fall through */
case 012: /* PRFEX 105472 (OP_A) */
PCQ_ENTRY;
PR = ReadW (op[0].word) & LA_MASK; /* jump indirect */
WriteW (op[0].word, 0); /* clear exit */
break;
case 013: /* PRFIO 105473 (OP_CV) */
WriteW (op[1].word, 1); /* set flag */
reason = cpu_iog (op[0].word); /* execute instr */
break;
case 014: /* SAVE 105474 (OP_N) */
WriteW (SPR, AR); /* save A */
SPR = (SPR + 1) & LA_MASK; /* incr stack ptr */
WriteW (SPR, BR); /* save B */
SPR = (SPR + 1) & LA_MASK; /* incr stack ptr */
t = (HP_WORD) ((O ^ 1) << 1 | E); /* merge E and O */
WriteW (SPR, t); /* save E and O */
SPR = (SPR + 1) & LA_MASK; /* incr stack ptr */
if (cpu_configuration & CPU_2100) /* 2100 keeps sp in MP FR */
mp_fence = SPR; /* (in case MP is turned on) */
break;
default: /* instruction unimplemented */
return STOP (cpu_ss_unimpl);
}
return reason;
}