759 lines
37 KiB
C
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;
|
|
}
|