blob: b7a2482a39d68ae7e118ffc524d7dab4fb609c5a [file] [log] [blame] [raw]
/* hp2100_cpu4.c: HP 1000 FPP/SIS
Copyright (c) 2006-2016, 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.
CPU4 Floating Point Processor and Scientific Instruction Set
05-Aug-16 JDB Renamed the P register from "PC" to "PR"
24-Dec-14 JDB Added casts for explicit downward conversions
09-May-12 JDB Separated assignments from conditional expressions
06-Feb-12 JDB Added OPSIZE casts to fp_accum calls in .FPWR/.TPWR
11-Sep-08 JDB Moved microcode function prototypes to hp2100_cpu1.h
05-Sep-08 JDB Removed option-present tests (now in UIG dispatchers)
18-Mar-08 JDB Fixed B register return bug in /CMRT
01-Dec-06 JDB Substitutes FPP for firmware FP if HAVE_INT64
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 "hp2100_defs.h"
#include "hp2100_cpu.h"
#include "hp2100_cpu1.h"
#if defined (HAVE_INT64) /* int64 support available */
#include "hp2100_fp1.h"
/* Floating-Point Processor.
The 1000 F-Series replaces the six 2100/1000-M/E single-precision firmware
floating-point instructions with a hardware floating-point processor (FPP).
The FPP executes single-, extended-, and double-precision floating-point
instructions, as well as double-integer instructions. All of the
floating-point instructions, as well as the single- and double-integer fix
and float instructions, are handled here. Pure double-integer instructions
are dispatched to the double-integer handler for simulation.
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 N/A std
For the F-Series, the instruction codes are mapped to routines as follows:
Instr. 1000-F Description
------ ------ -------------------------------------
105000 FAD Single real add
105001 .XADD Extended real add
105002 .TADD Double real add
105003 [EAD] [5-word add]
105004 [tst] [Floating Point Processor self test]
105005 [xpd] [Expand exponent]
105006 [rst] [Floating Point Processor reset]
105007 [stk] [Process stack of operands]
105010 [chk] [FPP addressing check]
105014 .DAD Double integer add
105020 FSB Single real subtract
105021 .XSUB Extended real subtract
105022 .TSUB Double real subtract
105023 [ESB] [5-word subtract]
105034 .DSB Double integer subtract
105040 FMP Single real multiply
105041 .XMPY Extended real multiply
105042 .TMPY Double real multiply
105043 [EMP] [5-word multiply]
105054 .DMP Double integer multiply
105060 FDV Single real divide
105061 .XDIV Extended real divide
105062 .TDIV Double real divide
105063 [EDV] [5-word divide]
105074 .DDI Double integer divide
105100 FIX Single real to integer fix
105101 .XFXS Extended real to integer fix (.DINT)
105102 .TXFS Double real to integer fix (.TINT)
105103 [EFS] [5-word FIXS]
105104 .FIXD Real to double integer fix
105105 .XFXD Extended real to double integer fix
105106 .TFXD Double real to double integer fix
105107 [EFD] [5-word FIXD]
105114 .DSBR Double integer subtraction (reversed)
105120 FLT Integer to single real float
105121 .XFTS Integer to extended real float (.IDBL)
105122 .TFTS Integer to double real float (.ITBL)
105123 [ELS] [5-word FLTS]
105124 .FLTD Double integer to real float
105125 .XFTD Double integer to extended real float
105126 .TFTD Double integer to double real float
105127 [ELD] [5-word FLTD]
105134 .DDIR Double integer divide (reversed)
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 we handle the
single-precision instructions for the 2100 and M/E-Series here, and the
firmware simulation is omitted. If support is unavailable, then the firmware
function is used instead.
Notes:
1. Single-precision arithmetic instructions (.FAD, etc.) and extended- and
double-precision F-Series FPP arithmetic instructions (.XADD, .TADD,
etc.) return positive infinity on both positive and negative overflow.
The equivalent extended-precision M/E-Series FFP instructions return
negative infinity on negative overflow and positive infinity on positive
overflow.
2. The items in brackets above are undocumented instructions that are used
by the 12740 FPP-SIS-FFP diagnostic only.
3. The five-word arithmetic instructions (e.g., 105003) use an expanded
operand format that dedicates a separate word to the exponent. See the
implementation notes in the hardware floating-point processor simulation
for details.
4. The "self test" instruction (105004) returned to P+1 for early F-Series
units without double-integer support. Units incorporating such support
returned to P+2.
5. The "expand exponent" instruction (105005) is used as a "prefix"
instruction to enable a 10-bit exponent range. It is placed immediately
before a 5-word arithmetic instruction sequence, e.g., immediately
preceding an EAD instruction sequence. The arithmetic instruction
executes normally, except that under/overflow is not indicated unless
the exponent exceeds the 10-bit range, instead of the normal 8-bit
range. If overflow is indicated, the exponent is still set to +128.
Note that as 2-, 3-, and 4-word packed numbers only have room for 8-bit
exponents, the Expand Exponent instruction serves no useful purpose in
conjunction with instructions associated with these precisions. If
used, the resulting values may be in error, as overflow from the 8-bit
exponents will not be indicated.
6. The "FPP reset" instruction (105006) is provided to reset a hung box,
e.g., in cases where an improper number of parameters is supplied. The
hardware resets its internal state machine in response to this
instruction. Under simulation, the instruction has no effect, as the
simulated FPP cannot hang.
7. The "process stack" instruction (105007) executes a series of FPP
instruction sets in sequence. Each set consists of a single FPP
instruction and associated operands that specifies the operation,
followed by a "result" instruction and operand. The result instruction
is optional and is only used to specify the result precision; the
instruction itself is not executed. If the result instruction is NOP,
then the result precision is that of the executed FPP instruction. If
the result operand is null, then the result is kept in the internal FPP
accumulator for later use.
The calling sequence is as follows:
STK Process stack instruction
DEF ERRTN Address of error return
DEF SET1 Address of first instruction set
DEF SET2 Address of second instruction set
.
.
.
ERRTN EQU * Return here if execution in error
OKRTN EQU * Return here if execution OK
Instruction sets are specified as follows (e.g.):
SET1 .TADD Operation instruction (NOP to terminate series)
DEC 4 Number of words in first operand (or 0 if accum)
DEF OP1 Address of first operand
DEC 4 Number of words in second operand (or 0 if accum)
DEF OP2 Address of second operand
.XADD Result precision conversion instruction (or NOP)
DEC 3 Number of words to store (or 0 if no store)
DEF RSLT Address of buffer to hold value
The primary use of the "process stack" instruction is to enable chained
operations employing the FPP accumulator for intermediate results and to
enable expanded exponent usage across multiple instructions.
8. The "addressing check" instruction sets bit 0 of the L register to 1,
copies the X register value to the FPP, and then reads the FPP and
stores the result in the Y register. Setting the L register bit 0 to 1
normally deselects the FPP, so that the value in Y is 177777. However,
the FPP box has a strap that inverts the selection logic, even though
the box will not work with the base-set firmware if this is done. The
"addressing check" instruction is provided to test whether the strap is
in the alternate location. Under simulation, the return value is always
177777, indicating that the strap is correctly set.
Additional references:
- DOS/RTE Relocatable Library Reference Manual (24998-90001, Oct-1981)
- FPP-SIS-FFP Diagnostic Source (12740-18001, Rev. 1926)
*/
static const OP_PAT op_fpp[96] = {
OP_RF, OP_AXX, OP_ATT, OP_AEE, /* FAD .XADD .TADD .EADD */
OP_N, OP_C, OP_N, OP_A, /* [tst] [xpd] [rst] [stk] */
OP_N, OP_N, OP_N, OP_N, /* [chk] --- --- --- */
OP_N, OP_N, OP_N, OP_N, /* .DAD --- --- --- */
OP_RF, OP_AXX, OP_ATT, OP_AEE, /* FSB .XSUB .TSUB .ESUB */
OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */
OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */
OP_N, OP_N, OP_N, OP_N, /* .DSB --- --- --- */
OP_RF, OP_AXX, OP_ATT, OP_AEE, /* FMP .XMPY .TMPY .EMPY */
OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */
OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */
OP_N, OP_N, OP_N, OP_N, /* .DMP --- --- --- */
OP_RF, OP_AXX, OP_ATT, OP_AEE, /* FDV .XDIV .TDIV .EDIV */
OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */
OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */
OP_N, OP_N, OP_N, OP_N, /* .DDI --- --- --- */
OP_R, OP_X, OP_T, OP_E, /* FIX .XFXS .TFXS .EFXS */
OP_R, OP_X, OP_T, OP_E, /* .FIXD .XFXD .TFXD .EFXD */
OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */
OP_N, OP_N, OP_N, OP_N, /* .DSBR --- --- --- */
OP_I, OP_IA, OP_IA, OP_IA, /* FLT .XFTS .TFTS .EFTS */
OP_J, OP_JA, OP_JA, OP_JA, /* .FLTD .XFTD .TFTD .EFTD */
OP_N, OP_N, OP_N, OP_N, /* --- --- --- --- */
OP_N, OP_N, OP_N, OP_N /* .DDIR --- --- --- */
};
t_stat cpu_fpp (uint32 IR, uint32 intrq)
{
OP fpop;
OPS op;
OPSIZE op1_prec, op2_prec, rslt_prec, cvt_prec;
uint16 opcode, rtn_addr, stk_ptr;
uint32 entry;
t_stat reason = SCPE_OK;
if (UNIT_CPU_MODEL == UNIT_1000_F) /* F-Series? */
opcode = (uint16) (IR & 0377); /* yes, use full opcode */
else
opcode = (uint16) (IR & 0160); /* no, use 6 SP FP opcodes */
entry = opcode & 0177; /* map to <6:0> */
if (op_fpp [entry] != OP_N) {
reason = cpu_ops (op_fpp [entry], op, intrq); /* get instruction operands */
if (reason != SCPE_OK) /* evaluation failed? */
return reason; /* return reason for failure */
}
switch (entry) { /* decode IR<6:0> */
case 0000: /* FAD 105000 (OP_RF) */
case 0020: /* FSB 105020 (OP_RF) */
case 0040: /* FMP 105040 (OP_RF) */
case 0060: /* FDV 105060 (OP_RF) */
O = fp_exec (opcode, &fpop, op[0], op[1]); /* execute operation */
AR = fpop.fpk[0]; /* return result to A/B */
BR = fpop.fpk[1];
break;
case 0001: /* .XADD 105001 (OP_AXX) */
case 0002: /* .TADD 105002 (OP_ATT) */
case 0003: /* .EADD 105003 (OP_AEE) */
case 0021: /* .XSUB 105021 (OP_AXX) */
case 0022: /* .TSUB 105022 (OP_ATT) */
case 0023: /* .ESUB 105023 (OP_AEE) */
case 0041: /* .XMPY 105041 (OP_AXX) */
case 0042: /* .TMPY 105042 (OP_ATT) */
case 0043: /* .EMPY 105043 (OP_AEE) */
case 0061: /* .XDIV 105061 (OP_AXX) */
case 0062: /* .TDIV 105062 (OP_ATT) */
case 0063: /* .EDIV 105063 (OP_AEE) */
O = fp_exec (opcode, &fpop, op[1], op[2]); /* execute operation */
fp_prec (opcode, NULL, NULL, &rslt_prec); /* determine result precision */
WriteOp (op[0].word, fpop, rslt_prec); /* write result */
break;
case 0004: /* [tst] 105004 (OP_N) */
XR = 3; /* firmware revision */
SR = 0102077; /* test passed code */
PR = (PR + 1) & VAMASK; /* P+2 return for firmware w/DBI */
break;
case 0005: /* [xpd] 105005 (OP_C) */
return cpu_fpp (op[0].word | 0200, intrq); /* set bit 7, execute instr */
case 0006: /* [rst] 105006 (OP_N) */
break; /* do nothing for FPP reset */
case 0007: /* [stk] 105007 (OP_A) */
O = 0; /* clear overflow */
stk_ptr = (uint16) PR; /* save ptr to next buf */
rtn_addr = op[0].word; /* save return address */
while (TRUE) {
PR = ReadW (stk_ptr) & VAMASK; /* point at next instruction set */
stk_ptr = (stk_ptr + 1) & VAMASK;
reason = cpu_ops (OP_CCACACCA, op, intrq); /* get instruction set */
if (reason) {
PR = err_PC; /* irq restarts */
break;
}
if (op[0].word == 0) { /* opcode = NOP? */
PR = (rtn_addr + 1) & VAMASK; /* bump to good return */
break; /* done */
}
fp_prec ((uint16) (op[0].word & 0377), /* determine operand precisions */
&op1_prec, &op2_prec, &rslt_prec);
if (TO_COUNT(op1_prec) != op[1].word) { /* first operand precisions agree? */
PR = rtn_addr; /* no, so take error return */
break;
}
else if (op1_prec != fp_a) /* operand in accumulator? */
op[1] = ReadOp (op[2].word, op1_prec); /* no, so get operand 1 */
if (TO_COUNT(op2_prec) != op[3].word) { /* second operand precisions agree? */
PR = rtn_addr; /* no, so take error return */
break;
}
else if (op2_prec != fp_a) /* operand in accumulator? */
op[2] = ReadOp (op[4].word, op2_prec); /* no, so get operand 2 */
O = O | /* execute instruction */
fp_exec ((uint16) (op[0].word & 0377), /* and accumulate overflow */
&fpop, op[1], op[2]);
if (op[5].word) { /* precision conversion? */
fp_prec ((uint16) (op[5].word & 0377), /* determine conversion precision */
NULL, NULL, &cvt_prec);
fpop = fp_accum (NULL, cvt_prec); /* convert result */
}
else /* no conversion specified */
cvt_prec = rslt_prec; /* so use original precision */
if (op[6].word) /* store result? */
WriteOp (op[7].word, fpop, cvt_prec); /* yes, so write it */
}
break;
case 0010: /* [chk] 105010 (OP_N) */
YR = 0177777; /* -1 if selection strap OK */
break;
case 0014: /* .DAD 105014 (OP_N) */
return cpu_dbi (0105321, intrq); /* remap to double int handler */
case 0034: /* .DSB 105034 (OP_N) */
return cpu_dbi (0105327, intrq); /* remap to double int handler */
case 0054: /* .DMP 105054 (OP_N) */
return cpu_dbi (0105322, intrq); /* remap to double int handler */
case 0074: /* .DDI 105074 (OP_N) */
return cpu_dbi (0105325, intrq); /* remap to double int handler */
case 0100: /* FIX 105100 (OP_R) */
case 0101: /* .XFXS 105101 (OP_X) */
case 0102: /* .TFXS 105102 (OP_T) */
case 0103: /* .EFXS 105103 (OP_E) */
O = fp_exec (opcode, &fpop, op[0], NOP); /* fix to integer */
AR = fpop.fpk[0]; /* save result */
break;
case 0104: /* .FIXD 105104 (OP_R) */
case 0105: /* .XFXD 105105 (OP_X) */
case 0106: /* .TFXD 105106 (OP_T) */
case 0107: /* .EFXD 105107 (OP_E) */
O = fp_exec (opcode, &fpop, op[0], NOP); /* fix to integer */
AR = (fpop.dword >> 16) & DMASK; /* save result */
BR = fpop.dword & DMASK; /* in A and B */
break;
case 0114: /* .DSBR 105114 (OP_N) */
return cpu_dbi (0105334, intrq); /* remap to double int handler */
case 0120: /* FLT 105120 (OP_I) */
case 0124: /* .FLTD 105124 (OP_J) */
O = fp_exec (opcode, &fpop, op[0], NOP); /* float to single */
AR = fpop.fpk[0]; /* save result */
BR = fpop.fpk[1]; /* into A/B */
break;
case 0121: /* .XFTS 105121 (OP_IA) */
case 0122: /* .TFTS 105122 (OP_IA) */
case 0123: /* .EFTS 105123 (OP_IA) */
case 0125: /* .XFTD 105125 (OP_JA) */
case 0126: /* .TFTD 105126 (OP_JA) */
case 0127: /* .EFTD 105127 (OP_JA) */
O = fp_exec (opcode, &fpop, op[0], NOP); /* float integer */
fp_prec (opcode, NULL, NULL, &rslt_prec); /* determine result precision */
WriteOp (op[1].word, fpop, rslt_prec); /* write result */
break;
case 0134: /* .DDIR 105134 (OP_N) */
return cpu_dbi (0105326, intrq); /* remap to double int handler */
default: /* others undefined */
reason = stop_inst;
}
return reason;
}
/* Scientific Instruction Set.
The SIS adds single-precision trigonometric and logarithmic, and
double-precision polynomial evaluation instructions to the 1000-F instruction
set. The SIS is standard on the 1000-F.
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 N/A std
The routines are mapped to instruction codes as follows:
Instr. 1000-F Description
------ ------ ----------------------------------------------
TAN 105320 Tangent
SQRT 105321 Square root
ALOG 105322 Natural logarithm
ATAN 105323 Arc tangent
COS 105324 Cosine
SIN 105325 Sine
EXP 105326 E to the power X
ALOGT 105327 Common logarithm
TANH 105330 Hyperbolic tangent
DPOLY 105331 Double-precision polynomial evaluation
/CMRT 105332 Double-precision common range reduction
/ATLG 105333 Compute (1-x)/(1+x) for .ATAN and .LOG
.FPWR 105334 Single-precision exponentiation
.TPWR 105335 Double-precision exponentiation
[tst] 105337 [self test]
The SIS simulation follows the F-Series SIS microcode, which, in turn,
follows the algebraic approximations given in the Relocatable Library manual
descriptions of the equivalent software routines.
Notes:
1. The word following the DPOLY instruction contains up to three flag bits
to indicate one of several polynomial forms to evaluate. The comments
in the DPOLY software library routine source interchange the actions of
the bit 14 and bit 0 flags. The DPOLY description in the Technical
Reference Handbook is correct.
2. Several instructions (e.g., DPOLY) are documented as leaving undefined
values in the A, B, X, Y, E, or O registers. Simulation does not
attempt to reproduce the same values as would be obtained with the
hardware.
3. The SIS uses the hardware FPP of the F-Series. FPP malfunctions are
detected by the SIS firmware and are indicated by a memory-protect
violation and setting the overflow flag. Under simulation,
malfunctions cannot occur.
4. We use OP_IIT for the .FPWR operand pattern. The "II" is redundant, but
it aligns the operands with the OP_IAT of .TPWR, so the code may be
shared.
Additional references:
- DOS/RTE Relocatable Library Reference Manual (24998-90001, Oct-1981)
- HP 1000 E-Series and F-Series Computer Microprogramming Reference Manual
(02109-90004, Apr-1980).
*/
/* Common single-precision range reduction for SIN, COS, TAN, and EXP.
This routine is called by the SIN, COS, TAN, and EXP handlers to reduce the
range of the argument. Reduction is performed in extended-precision. We
calculate:
multiple = (nearest even integer to argument * multiplier)
argument = argument * multiplier - multiple
*/
static uint32 reduce (OP *argument, int32 *multiple, OP multiplier)
{
OP product, count;
uint32 overflow;
fp_cvt (argument, fp_f, fp_x); /* convert to extended precision */
fp_exec (0041, &product, *argument, multiplier); /* product = argument * multiplier */
overflow = fp_exec (0111, &count, NOP, NOP); /* count = FIX (acc) */
if ((int16) count.word >= 0) /* nearest even integer */
count.word = count.word + 1;
count.word = count.word & ~1;
*multiple = (int16) count.word;
if (overflow == 0) { /* in range? */
fp_exec (0121, ACCUM, count, NOP); /* acc = FLT (count) */
overflow = fp_exec (0025, ACCUM, product, NOP); /* acc = product - acc */
*argument = fp_accum (NULL, fp_f); /* trim to single-precision */
}
return overflow;
}
/* SIS dispatcher. */
static const OP_PAT op_sis[16] = {
OP_R, OP_R, OP_R, OP_R, /* TAN SQRT ALOG ATAN */
OP_R, OP_R, OP_R, OP_R, /* COS SIN EXP ALOGT */
OP_R, OP_CATAKK, OP_AAT, OP_A, /* TANH DPOLY /CMRT /ATLG */
OP_IIF, OP_IAT, OP_N, OP_N /* .FPWR .TPWR --- [tst] */
};
t_stat cpu_sis (uint32 IR, uint32 intrq)
{
OPS op;
OP arg, coeff, pwr, product, count, result;
int16 f, p;
int32 multiple, power, exponent, rsltexp;
uint32 entry, i;
t_bool flag, sign;
t_stat reason = SCPE_OK;
static const OP tan_c4 = { { 0137763, 0051006 } }; /* DEC -4.0030956 */
static const OP tan_c3 = { { 0130007, 0051026 } }; /* DEC -1279.5424 */
static const OP tan_c2 = { { 0040564, 0012761 } }; /* DEC 0.0019974806 */
static const OP tan_c1 = { { 0045472, 0001375 } }; /* DEC 0.14692695 */
static const OP alog_c3 = { { 0065010, 0063002 } }; /* DEC 1.6567626301 */
static const OP alog_c2 = { { 0125606, 0044404 } }; /* DEC -2.6398577035 */
static const OP alog_c1 = { { 0051260, 0037402 } }; /* DEC 1.2920070987 */
static const OP atan_c4 = { { 0040257, 0154404 } }; /* DEC 2.0214656 */
static const OP atan_c3 = { { 0132062, 0133406 } }; /* DEC -4.7376165 */
static const OP atan_c2 = { { 0047407, 0173775 } }; /* DEC 0.154357652 */
static const OP atan_c1 = { { 0053447, 0014002 } }; /* DEC 1.3617611 */
static const OP sin_c4 = { { 0132233, 0040745 } }; /* DEC -0.000035950439 */
static const OP sin_c3 = { { 0050627, 0122361 } }; /* DEC 0.002490001 */
static const OP sin_c2 = { { 0126521, 0011373 } }; /* DEC -0.0807454325 */
static const OP sin_c1 = { { 0062207, 0166400 } }; /* DEC 0.78539816 */
static const OP cos_c4 = { { 0126072, 0002753 } }; /* DEC -0.00031957 */
static const OP cos_c3 = { { 0040355, 0007767 } }; /* DEC 0.015851077 */
static const OP cos_c2 = { { 0130413, 0011377 } }; /* DEC -0.30842483 */
static const OP cos_c1 = { { 0040000, 0000002 } }; /* DEC 1.0 */
static const OP sqrt_a2 = { { 0045612, 0067400 } }; /* DEC 0.5901621 */
static const OP sqrt_b2 = { { 0065324, 0126377 } }; /* DEC 0.4173076 */
static const OP sqrt_a1 = { { 0065324, 0126400 } }; /* DEC 0.8346152 */
static const OP sqrt_b1 = { { 0045612, 0067400 } }; /* DEC 0.5901621 */
static const OP exp_c2 = { { 0073000, 0070771 } }; /* DEC 0.05761803 */
static const OP exp_c1 = { { 0056125, 0041406 } }; /* DEC 5.7708162 */
static const OP tanh_c3 = { { 0050045, 0022004 } }; /* DEC 2.5045337 */
static const OP tanh_c2 = { { 0041347, 0101404 } }; /* DEC 2.0907609 */
static const OP tanh_c1 = { { 0052226, 0047375 } }; /* DEC 0.16520923 */
static const OP minus_1 = { { 0100000, 0000000 } }; /* DEC -1.0 */
static const OP plus_1 = { { 0040000, 0000002 } }; /* DEC +1.0 */
static const OP plus_half = { { 0040000, 0000000 } }; /* DEC +0.5 */
static const OP ln_2 = { { 0054271, 0006000 } }; /* DEC 0.6931471806 (ln 2.0) */
static const OP log_e = { { 0067455, 0166377 } }; /* DEC 0.43429228 (log e) */
static const OP pi_over_4 = { { 0062207, 0166400 } }; /* Pi / 4.0 */
static const OP pi_over_2 = { { 0062207, 0166402 } }; /* Pi / 2.0 */
static const OP four_over_pi = { { 0050574, 0140667, 0023402 } }; /* 4.0 / Pi */
static const OP two_over_ln2 = { { 0056125, 0016624, 0127404 } }; /* 2.0 / ln(2.0) */
static const OP t_one = { { 0040000, 0000000, 0000000, 0000002 } }; /* DEY 1.0 */
entry = IR & 017; /* mask to entry point */
if (op_sis [entry] != OP_N) {
reason = cpu_ops (op_sis [entry], 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: /* TAN 105320 (OP_R) */
O = reduce (&op[0], &multiple, four_over_pi); /* reduce range */
if (O) { /* out of range? */
op[0].fpk[0] = '0' << 8 | '9'; /* return '09' */
op[0].fpk[1] = 'O' << 8 | 'R'; /* return 'OR' */
break; /* error return is P+1 */
}
fp_exec (0040, &op[1], op[0], op[0]); /* op1 = arg ^ 2 */
fp_exec (0010, ACCUM, NOP, tan_c4); /* acc = acc + C4 */
fp_exec (0064, ACCUM, tan_c3, NOP); /* acc = C3 / acc */
fp_exec (0010, ACCUM, NOP, op[1]); /* acc = acc + op1 */
fp_exec (0050, ACCUM, NOP, tan_c2); /* acc = acc * C2 */
fp_exec (0010, ACCUM, NOP, tan_c1); /* acc = acc + C1 */
fp_exec (0050, &op[0], NOP, op[0]); /* res = acc * arg */
if (multiple & 0002) /* multiple * 2 odd? */
fp_exec (0064, &op[0], minus_1, NOP); /* res = -1.0 / acc */
PR = (PR + 1) & VAMASK; /* normal return is P+2 */
break;
case 001: /* SQRT 105321 (OP_R) */
O = 0; /* clear overflow */
if (op[0].fpk[0] == 0) { /* arg = 0? */
PR = (PR + 1) & VAMASK; /* normal return is P+2 */
break;
}
else if ((int16) op[0].fpk[0] < 0) { /* sqrt of neg? */
op[0].fpk[0] = '0' << 8 | '3'; /* return '03' */
op[0].fpk[1] = 'U' << 8 | 'N'; /* return 'UN' */
O = 1; /* set overflow */
break; /* error return is P+1 */
}
fp_unpack (&op[1], &exponent, op[0], fp_f); /* unpack argument */
if (exponent & 1) { /* exponent odd? */
fp_exec (0040, ACCUM, op[1], sqrt_a1); /* acc = op1 * A1 */
fp_exec (0010, &op[2], NOP, sqrt_b1); /* op2 = acc + B1 */
op[1].fpk[1] = op[1].fpk[1] + 2; /* op1 = op1 * 2.0 */
}
else { /* exponent even */
fp_exec (0040, ACCUM, op[1], sqrt_a2); /* acc = op1 * A2 */
fp_exec (0010, &op[2], NOP, sqrt_b2); /* op2 = acc + B2 */
}
fp_exec (0064, ACCUM, op[1], NOP); /* acc = op1 / acc */
fp_exec (0010, &op[2], NOP, op[2]); /* op2 = acc + op2 */
op[1].fpk[1] = op[1].fpk[1] + 4; /* op1 = op1 * 4.0 */
fp_exec (0064, ACCUM, op[1], NOP); /* acc = op1 / acc */
fp_exec (0010, &op[0], NOP, op[2]); /* res = acc + op2 */
power = (exponent >> 1) - 2;
if (op[0].fpk[0]) { /* calc x * 2**n */
fp_unpack (&op[1], &exponent, op[0], fp_f); /* unpack argument */
exponent = exponent + power; /* multiply by 2**n */
if ((exponent > 0177) || /* exponent overflow? */
(exponent < -0200)) { /* or underflow? */
O = 1; /* rtn unscaled val, set ovf */
break; /* error return is P+1 */
}
else
fp_pack (&op[0], op[1], exponent, fp_f);/* repack result */
}
PR = (PR + 1) & VAMASK; /* normal return is P+2 */
break;
case 002: /* ALOG 105322 (OP_R) */
case 007: /* ALOGT 105327 (OP_R) */
O = 0; /* clear overflow */
if ((int16) op[0].fpk[0] <= 0) { /* log of neg or zero? */
op[0].fpk[0] = '0' << 8 | '2'; /* return '02' */
op[0].fpk[1] = 'U' << 8 | 'N'; /* return 'UN' */
O = 1; /* set overflow */
break; /* error return is P+1 */
}
fp_unpack (&op[1], &exponent, op[0], fp_f); /* unpack argument */
if (op[0].fpk[0] < 0055000) { /* out of range? */
exponent = exponent - 1; /* drop exponent */
op[1].fpk[1] = op[1].fpk[1] | 2; /* set "exponent" to 1 */
}
op[2].fpk[0] = (uint16) exponent;
fp_exec (0120, &op[3], op[2], NOP); /* op3 = FLT(exponent) */
fp_exec (0020, &op[4], op[1], plus_1); /* op4 = op1 - 1.0 */
fp_exec (0000, ACCUM, op[1], plus_1); /* acc = op1 + 1.0 */
fp_exec (0064, &op[5], op[4], NOP); /* op5 = op4 / acc */
fp_exec (0054, ACCUM, NOP, NOP); /* acc = acc * acc */
fp_exec (0030, ACCUM, NOP, alog_c3); /* acc = acc - c3 */
fp_exec (0064, ACCUM, alog_c2, NOP); /* acc = c2 / acc */
fp_exec (0010, ACCUM, NOP, alog_c1); /* acc = acc + c1 */
fp_exec (0050, ACCUM, NOP, op[5]); /* acc = acc * op5 */
fp_exec (0010, ACCUM, NOP, op[3]); /* acc = acc + op3 */
fp_exec (0050, &op[0], NOP, ln_2); /* res = acc * ln2 */
if (entry == 007) /* ALOGT? */
fp_exec (0050, &op[0], NOP, log_e); /* res = acc * log(e) */
PR = (PR + 1) & VAMASK; /* normal return is P+2 */
break;
case 003: /* ATAN 105323 (OP_R) */
O = 0; /* clear overflow */
if (op[0].fpk[0] == 0) /* argument zero? */
break; /* result zero */
flag = (op[0].fpk[1] & 1); /* get exponent sign */
sign = ((int16) op[0].fpk[0] < 0); /* get argument sign */
if (flag == 0) { /* exp pos? (abs >= 0.5)? */
if (sign) /* argument negative? */
fp_pcom (&op[0], fp_f); /* make positive */
if (op[0].fpk[1] & 0374) { /* arg >= 2? */
fp_exec(0060, &op[0], plus_1, op[0]); /* arg = 1.0 / arg */
op[2] = pi_over_2; /* constant = pi / 2.0 */
}
else {
fp_exec (0020, &op[1], plus_1, op[0]); /* op1 = 1.0 - arg */
fp_exec (0000, ACCUM, plus_1, op[0]); /* acc = 1.0 + arg */
fp_exec (0064, &op[0], op[1], NOP); /* arg = op1 / acc */
op[2] = pi_over_4; /* constant = pi / 4.0 */
}
}
fp_exec (0040, &op[1], op[0], op[0]); /* op1 = arg * arg */
fp_exec (0010, ACCUM, NOP, atan_c4); /* acc = acc + C4 */
fp_exec (0064, ACCUM, atan_c3, NOP); /* acc = C3 / acc */
fp_exec (0010, ACCUM, NOP, op[1]); /* acc = acc + op1 */
fp_exec (0050, ACCUM, NOP, atan_c2); /* acc = acc * C2 */
fp_exec (0010, ACCUM, NOP, atan_c1); /* acc = acc + C1 */
fp_exec (0064, &op[0], op[0], NOP); /* res = arg / acc */
if (flag == 0) { /* exp pos? (abs >= 0.5)? */
fp_exec (0030, &op[0], NOP, op[2]); /* res = acc - pi / n */
if (sign == 0) /* argument positive? */
fp_pcom (&op[0], fp_f); /* make negative */
}
break;
case 004: /* COS 105324 (OP_R) */
case 005: /* SIN 105325 (OP_R) */
O = reduce (&op[0], &multiple, four_over_pi); /* reduce range */
if (O) { /* out of range? */
op[0].fpk[0] = '0' << 8 | '5'; /* return '05' */
op[0].fpk[1] = 'O' << 8 | 'R'; /* return 'OR' */
break; /* error return is P+1 */
}
multiple = multiple / 2 + (entry == 004); /* add one for cosine */
flag = (multiple & 1); /* decide on series */
fp_exec (0040, &op[1], op[0], op[0]); /* op1 = arg ^ 2 */
if (flag) {
fp_exec (0050, ACCUM, NOP, cos_c4); /* acc = acc * c4 */
fp_exec (0010, ACCUM, NOP, cos_c3); /* acc = acc + c3 */
fp_exec (0050, ACCUM, NOP, op[1]); /* acc = acc * op1 */
fp_exec (0010, ACCUM, NOP, cos_c2); /* acc = acc + c2 */
fp_exec (0050, ACCUM, NOP, op[1]); /* acc = acc * op1 */
fp_exec (0010, &op[0], NOP, cos_c1); /* res = acc + c1 */
}
else {
fp_exec (0050, ACCUM, NOP, sin_c4); /* acc = acc * c4 */
fp_exec (0010, ACCUM, NOP, sin_c3); /* acc = acc + c3 */
fp_exec (0050, ACCUM, NOP, op[1]); /* acc = acc * op1 */
fp_exec (0010, ACCUM, NOP, sin_c2); /* acc = acc + c2 */
fp_exec (0050, ACCUM, NOP, op[1]); /* acc = acc * op1 */
fp_exec (0010, ACCUM, NOP, sin_c1); /* acc = acc + c1 */
fp_exec (0050, &op[0], NOP, op[0]); /* res = acc * arg */
}
if (multiple & 0002) /* multiple * 2 odd? */
fp_pcom (&op[0], fp_f); /* make negative */
PR = (PR + 1) & VAMASK; /* normal return is P+2 */
break;
case 006: /* EXP 105326 (OP_R) */
sign = ((int16) op[0].fpk[0] < 0); /* get argument sign */
O = reduce (&op[0], &multiple, two_over_ln2); /* reduce range */
multiple = multiple / 2; /* get true multiple */
if ((sign == 0) && (O | (multiple > 128))) { /* pos and ovf or out of range? */
op[0].fpk[0] = '0' << 8 | '7'; /* return '07' */
op[0].fpk[1] = 'O' << 8 | 'F'; /* return 'OF' */
O = 1; /* set overflow */
break; /* error return is P+1 */
}
else if (sign && (multiple < -128)) { /* neg and out of range? */
op[0].fpk[0] = 0; /* result is zero */
op[0].fpk[1] = 0;
O = 0; /* clear for underflow */
PR = (PR + 1) & VAMASK; /* normal return is P+2 */
break;
}
fp_exec (0040, ACCUM, op[0], op[0]); /* acc = arg ^ 2 */
fp_exec (0050, ACCUM, NOP, exp_c2); /* acc = acc * c2 */
fp_exec (0030, ACCUM, NOP, op[0]); /* acc = acc - op0 */
fp_exec (0010, ACCUM, NOP, exp_c1); /* acc = acc + c1 */
fp_exec (0064, ACCUM, op[0], NOP); /* acc = op0 / acc */
fp_exec (0010, &op[0], NOP, plus_half); /* res = acc + 0.5 */
power = multiple + 1;
if (op[0].fpk[0]) { /* calc x * 2**n */
fp_unpack (&op[1], &exponent, op[0], fp_f); /* unpack argument */
exponent = exponent + power; /* multiply by 2**n */
if ((exponent > 0177) || /* exponent overflow? */
(exponent < -0200)) { /* or underflow? */
if (sign == 0) { /* arg positive? */
op[0].fpk[0] = '0' << 8 | '7'; /* return '07' */
op[0].fpk[1] = 'O' << 8 | 'F'; /* return 'OF' */
O = 1; /* set overflow */
}
else {
op[0].fpk[0] = 0; /* result is zero */
op[0].fpk[1] = 0;
O = 0; /* clear for underflow */
}
break; /* error return is P+1 */
}
else {
fp_pack (&op[0], op[1], exponent, fp_f);/* repack value */
O = 0;
}
}
PR = (PR + 1) & VAMASK; /* normal return is P+2 */
break;
case 010: /* TANH 105330 (OP_R) */
O = 0;
sign = ((int16) op[0].fpk[0] < 0); /* get argument sign */
if (op[0].fpk[1] & 1) { /* abs (arg) < 0.5? */
fp_exec (0040, ACCUM, op[0], op[0]); /* acc = arg ^ 2 */
fp_exec (0010, ACCUM, NOP, tanh_c3); /* acc = acc + c3 */
fp_exec (0064, ACCUM, tanh_c2, NOP); /* acc = c2 / acc */
fp_exec (0010, ACCUM, NOP, tanh_c1); /* acc = acc + c1 */
fp_exec (0050, &op[0], NOP, op[0]); /* res = acc * arg */
}
else if (op[0].fpk[1] & 0370) /* abs (arg) >= 8.0? */
if (sign) /* arg negative? */
op[0] = minus_1; /* result = -1.0 */
else /* arg positive */
op[0] = plus_1; /* result = +1.0 */
else { /* 0.5 <= abs (arg) < 8.0 */
BR = BR + 2; /* arg = arg * 2.0 */
cpu_sis (0105326, intrq); /* calc exp (arg) */
PR = (PR - 1) & VAMASK; /* correct P (always good rtn) */
op[0].fpk[0] = AR; /* save value */
op[0].fpk[1] = BR;
fp_exec (0020, &op[1], op[0], plus_1); /* op1 = op0 - 1.0 */
fp_exec (0000, ACCUM, op[0], plus_1); /* acc = op0 + 1.0 */
fp_exec (0064, &op[0], op[1], NOP); /* res = op1 / acc */
}
break;
case 011: /* DPOLY 105331 (OP_CATAKK) */
O = 0; /* clear overflow */
AR = op[0].word; /* get flag word */
if ((int16) AR >= 0) { /* flags present? */
AR = 1; /* no, so set default */
arg = op[2]; /* arg = X */
}
else /* bit 15 set */
fp_exec (0042, &arg, op[2], op[2]); /* arg = X ^ 2 */
coeff = ReadOp (op[3].word, fp_t); /* get first coefficient */
op[3].word = (op[3].word + 4) & VAMASK; /* point at next */
fp_accum (&coeff, fp_t); /* acc = coeff */
for (i = 0; i < op[4].word; i++) { /* compute numerator */
fp_exec (0052, ACCUM, NOP, arg); /* acc = P[m] * arg */
coeff = ReadOp (op[3].word, fp_t); /* get next coefficient */
op[3].word = (op[3].word + 4) & VAMASK; /* point at next */
fp_exec (0012, ACCUM, NOP, coeff); /* acc = acc + P[m-1] */
}
if (AR & 1) /* bit 0 set? */
op[6] = fp_accum (NULL, fp_t); /* save numerator */
else
fp_exec (0046, &op[6], op[2], NOP); /* acc = X * acc */
if (op[5].word) { /* n > 0 ? */
fp_accum (&t_one, fp_t); /* acc = 1.0 */
for (i = 0; i < op[5].word; i++) { /* compute denominator */
fp_exec (0052, ACCUM, NOP, arg); /* acc = P[m] * arg */
coeff = ReadOp (op[3].word, fp_t); /* get next coefficient */
op[3].word = (op[3].word + 4) & VAMASK; /* point at next */
fp_exec (0012, ACCUM, NOP, coeff); /* acc = acc + P[m-1] */
}
if (AR & 0040000) /* bit 14 set? */
fp_exec (0032, ACCUM, NOP, op[6]); /* acc = den - num */
fp_exec (0066, &op[6], op[6], NOP); /* op6 = num / den */
}
WriteOp (op[1].word, op[6], fp_t); /* write result */
if (O) /* overflow? */
op[0].fpk[0] = 0; /* microcode rtns with A = 0 */
break;
case 012: /* /CMRT 105332 (OP_AAT) */
O = 0;
f = (int16) AR; /* save flags */
coeff = ReadOp (op[1].word, fp_t); /* get coefficient (C) */
fp_unpack (NULL, &exponent, op[2], fp_t); /* unpack exponent */
if ((f == -1) || (exponent < 4)) { /* TANH or abs (arg) < 16.0? */
/* result = x * c - n */
fp_exec (0042, &product, op[2], coeff); /* product = arg * C */
O = fp_exec (0112, &count, NOP, NOP); /* count = FIX (acc) */
if ((int16) count.word >= 0) /* nearest even integer */
count.word = count.word + 1;
BR = count.word = count.word & ~1; /* save LSBs of N */
O = O | fp_exec (0122, ACCUM, count, NOP); /* acc = FLT (count) */
if (O) { /* out of range? */
op[0].fpk[0] = 0; /* microcode rtns with A = 0 */
break; /* error return is P+1 */
}
fp_exec (0026, &result, product, NOP); /* acc = product - acc */
fp_unpack (NULL, &rsltexp, result, fp_t); /* unpack exponent */
/* determine if cancellation matters */
if ((f < 0) || (f == 2) || (f == 6) || /* EXP, TANH, or COS? */
(exponent - rsltexp < 5)) { /* bits lost < 5? */
WriteOp (op[0].word, result, fp_t); /* write result */
PR = (PR + 1) & VAMASK; /* P+2 return for good result */
op[0].fpk[1] = BR; /* return LSBs of N in B */
break; /* all done! */
}
}
/* result = (xu * cu - n) + (x - xu) * c + xu * cl */
if (exponent >= (8 + 16 * (f >= 0))) { /* exp >= 8 (EXP,TANH)? */
op[0].fpk[0] = 0; /* or 24 (SIN/COS/TAN)? */
break; /* range error return is P+1 */
}
op[3].fpk[0] = coeff.fpk[0]; /* form upper bits of C (CU) */
op[3].fpk[1] = coeff.fpk[1] & 0177770;
op[3].fpk[2] = 0;
op[3].fpk[3] = coeff.fpk[3] & 0000377;
op[4].fpk[0] = op[2].fpk[0]; /* form upper bits of X (XU) */
op[4].fpk[1] = op[2].fpk[1] & 0177770;
op[4].fpk[2] = 0;
op[4].fpk[3] = op[2].fpk[3] & 0000377;
fp_exec (0042, &op[5], op[3], op[4]); /* op5 = cu * xu */
fp_exec (0116, &op[6], NOP, NOP); /* op6 = fix (acc) (2wd) */
if ((int32) op[6].dword >= 0) /* nearest even integer */
op[6].dword = op[6].dword + 1;
op[6].dword = op[6].dword & ~1;
BR = op[6].dword & DMASK; /* save LSBs of N */
O = fp_exec (0126, ACCUM, op[6], NOP); /* acc = flt (op6) */
if (O) { /* overflow? */
op[0].fpk[0] = 0; /* microcode rtns with A = 0 */
break; /* range error return is P+1 */
}
fp_exec (0026, &op[7], op[5], NOP); /* op7 = cu * xu - n */
fp_exec (0022, ACCUM, op[2], op[4]); /* acc = x - xu */
fp_exec (0052, ACCUM, NOP, coeff); /* acc = (x - xu) * c */
fp_exec (0012, &op[5], NOP, op[7]); /* op5 = acc + (cu * xu - n) */
op[1].word = (op[1].word + 4) & VAMASK; /* point at second coefficient */
coeff = ReadOp (op[1].word, fp_t); /* get coefficient (CL) */
fp_exec (0042, ACCUM, op[4], coeff); /* acc = xu * cl */
fp_exec (0012, &result, NOP, op[5]); /* result = acc + (x - xu) * c + (cu * xu - n) */
WriteOp (op[0].word, result, fp_t); /* write result */
PR = (PR + 1) & VAMASK; /* P+2 return for good result */
op[0].fpk[1] = BR; /* return LSBs of N in B */
break;
case 013: /* /ATLG 105333 (OP_A) */
arg = ReadOp (op[0].word, fp_t); /* get argument */
fp_exec (0022, &op[1], t_one, arg); /* op1 = 1.0 - arg */
fp_exec (0002, ACCUM, t_one, arg); /* acc = 1.0 + arg */
fp_exec (0066, &op[1], op[1], NOP); /* res = op1 / acc */
WriteOp (op[0].word, op[1], fp_t); /* write result */
break;
case 014: /* .FPWR 105334 (OP_IIF) */
p = 0; /* set to single-precision */
goto NPWR;
case 015: /* .TPWR 105335 (OP_IAT) */
p = 2; /* set to double-precision */
NPWR:
if (op[2].fpk[0]) { /* non-zero base? */
fp_exec (0120, &pwr, op[0], NOP); /* float power */
sign = ((int16) pwr.fpk[0] < 0); /* save sign of power */
i = (pwr.fpk[0] << 2) & DMASK; /* clear it */
fp_unpack (NULL, &exponent, pwr, fp_f); /* unpack exponent */
if (sign == 0)
exponent = exponent - 1;
O = 0; /* clear overflow */
fp_accum (&op[2], (OPSIZE) (fp_f + p)); /* acc = arg */
while (exponent-- > 0) {
O = O | fp_exec ((uint16) (0054 | p), /* square acc */
ACCUM, NOP, NOP);
if (i & SIGN)
O = O | fp_exec ((uint16) (0050 | p), /* acc = acc * arg */
ACCUM, NOP, op[2]);
i = i << 1;
}
op[2] = fp_accum (NULL, (OPSIZE) (fp_f + p)); /* get accum */
if (op[2].fpk[0] == 0) /* result zero? */
O = 1; /* underflow */
}
if (entry == 014) /* .FPWR ? */
op[0] = op[2]; /* copy result */
else /* .TPWR */
WriteOp (op[1].word, op[2], fp_t); /* write result */
break;
case 017: /* [tst] 105337 (OP_N) */
XR = 4; /* firmware revision */
SR = 0102077; /* test passed code */
PR = (PR + 1) & VAMASK; /* P+2 return for firmware w/DPOLY */
return reason;
default: /* others undefined */
return stop_inst;
}
AR = op[0].fpk[0]; /* save result */
BR = op[0].fpk[1]; /* into A/B */
return reason;
}
#endif /* end of int64 support */