/* pdp8_fpp.c: PDP-8 floating point processor (FPP8A)

   Copyright (c) 2007-2022, Robert M Supnik

   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
   ROBERT M SUPNIK 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 Robert M Supnik shall not be
   used in advertising or otherwise to promote the sale, use or other dealings
   in this Software without prior written authorization from Robert M Supnik.

   fpp          FPP8A floating point processor

   11-Mar-22    RMS     Zeroed result exponent in double precision (COVERITY)
   05-Jan-22    RHM     Fix fencepost error in FP multiply for extended
                        precision
   21-Oct-21    RMS     Added device number display
   03-Jan-10    RMS     Initialized variables statically, for VMS compiler
   19-Apr-09    RHM     FPICL does not clear all command and status reg bits
                            modify fpp_reset to conform with FPP
   27-Mar-09    RHM     Fixed handling of Underflow fix (zero FAC on underflow)
                        Implemented FPP division and multiplication algorithms
                        FPP behavior on traps - FEXIT does not update APT
                        Follow FPP settings for OPADD 
                        Correct detection of DP add/sub overflow
                        Detect and handle add/sub overshift
                        Single-step mode made consistent with FPP
                        Write calculation results prior to traps
   24-Mar-09    RMS     Many fixes from Rick Murphy:
                        Fix calculation of ATX shift amount
                        Added missing () to read, write XR macros
                        Fixed indirect address calculation
                        Fixed == written as = in normalization
                        Fixed off-by-one count bug in multiplication
                        Removed extraneous ; in divide
                        Fixed direction of compare in divide
                        Fixed count direction bug in alignment

   Floating point formats:

    00 01 02 03 04 05 06 07 08 09 10 11
   +--+--+--+--+--+--+--+--+--+--+--+--+
   | S|          hi integer            | : double precision
   +--+--+--+--+--+--+--+--+--+--+--+--+
   |             lo integer            |
   +--+--+--+--+--+--+--+--+--+--+--+--+

    00 01 02 03 04 05 06 07 08 09 10 11
   +--+--+--+--+--+--+--+--+--+--+--+--+
   | S|          exponent              | : floating point
   +--+--+--+--+--+--+--+--+--+--+--+--+
   | S|          hi fraction           |
   +--+--+--+--+--+--+--+--+--+--+--+--+
   |             lo fraction           |
   +--+--+--+--+--+--+--+--+--+--+--+--+


    00 01 02 03 04 05 06 07 08 09 10 11
   +--+--+--+--+--+--+--+--+--+--+--+--+
   | S|          exponent              | : extended precision
   +--+--+--+--+--+--+--+--+--+--+--+--+
   | S|          hi fraction           |
   +--+--+--+--+--+--+--+--+--+--+--+--+
   |            next fraction          |
   +--+--+--+--+--+--+--+--+--+--+--+--+
   |            next fraction          |
   +--+--+--+--+--+--+--+--+--+--+--+--+
   |            next fraction          |
   +--+--+--+--+--+--+--+--+--+--+--+--+
   |             lo fraction           |
   +--+--+--+--+--+--+--+--+--+--+--+--+

   Exponents are 2's complement, as are fractions.  Normalized numbers have
   the form:

   0.0...0
   0.<non-zero>
   1.<non-zero>
   1.1...0

   Note that 1.0...0 is normalized but considered illegal, since it cannot
   be represented as a positive number. When a result is normalized, 1.0...0
   is converted to 1.1...0 with exp+1.
*/

#include "pdp8_defs.h"

extern int32 int_req;
extern uint16 M[];
extern int32 stop_inst;
extern UNIT cpu_unit;

#define SEXT12(x)       (((x) & 04000)? (x) | ~07777: (x) & 03777)

/* Index registers are in memory */

#define fpp_read_xr(xr) fpp_read (fpp_xra + (xr))
#define fpp_write_xr(xr,d) fpp_write (fpp_xra + (xr), d)

/* Command register */

#define FPC_DP          04000                           /* integer double */
#define FPC_UNFX        02000                           /* exit on fl undf */
#define FPC_FIXF        01000                           /* lock mem field */
#define FPC_IE          00400                           /* int enable */
#define FPC_V_FAST      4                               /* startup bits */
#define FPC_M_FAST      017
#define FPC_LOCK        00010                           /* lockout */
#define FPC_V_APTF      0
#define FPC_M_APTF      07                              /* apta field */
#define FPC_STA         (FPC_DP|FPC_LOCK)
#define FPC_GETFAST(x)  (((x) >> FPC_V_FAST) & FPC_M_FAST)
#define FPC_GETAPTF(x)  (((x) >> FPC_V_APTF) & FPC_M_APTF)

/* Status register */

#define FPS_DP          (FPC_DP)                        /* integer double */
#define FPS_TRPX        02000                           /* trap exit */
#define FPS_HLTX        01000                           /* halt exit */
#define FPS_DVZX        00400                           /* div zero exit */
#define FPS_IOVX        00200                           /* int ovf exit */
#define FPS_FOVX        00100                           /* flt ovf exit */
#define FPS_UNF         00040                           /* underflow */
#define FPS_XXXM        00020                           /* FADDM/FMULM */
#define FPS_LOCK        (FPC_LOCK)                      /* lockout */
#define FPS_EP          00004                           /* ext prec */
#define FPS_PAUSE       00002                           /* paused */
#define FPS_RUN         00001                           /* running */

/* Floating point number: 3-6 words */

#define FPN_FRSIGN      04000
#define FPN_NFR_FP      2                               /* std precision */
#define FPN_NFR_EP      5                               /* ext precision */
#define FPN_NFR_MDS     6                               /* mul/div precision */
#define EXACT           (uint32)((fpp_sta & FPS_EP)? FPN_NFR_EP: FPN_NFR_FP)
#define EXTEND          ((uint32) FPN_NFR_EP)

typedef struct {
    int32       exp;
    uint32      fr[FPN_NFR_MDS+1];
    } FPN;

uint32 fpp_apta = 0;                                    /* APT pointer */
uint32 fpp_aptsvf = 0;                                  /* APT saved field */
uint32 fpp_opa = 0;                                     /* operand pointer */
uint32 fpp_fpc = 0;                                     /* FP PC */
uint32 fpp_bra = 0;                                     /* base reg pointer */
uint32 fpp_xra = 0;                                     /* indx reg pointer */
uint32 fpp_cmd = 0;                                     /* command */
uint32 fpp_sta = 0;                                     /* status */
uint32 fpp_flag = 0;                                    /* flag */
FPN fpp_ac;                                             /* FAC */
uint32 fpp_ssf = 0;                                     /* single-step flag */
uint32 fpp_last_lockbit = 0;                            /* last lockbit */

static FPN fpp_zero = { 0, { 0, 0, 0, 0, 0 } };
static FPN fpp_one = { 1, { 02000, 0, 0, 0, 0 } };

int32 fpp55 (int32 IR, int32 AC);
int32 fpp56 (int32 IR, int32 AC);
void fpp_load_apt (uint32 apta);
void fpp_dump_apt (uint32 apta, uint32 sta);
uint32 fpp_1wd_dir (uint32 ir);
uint32 fpp_2wd_dir (uint32 ir);
uint32 fpp_indir (uint32 ir);
uint32 fpp_ad15 (uint32 hi);
uint32 fpp_adxr (uint32 ir, uint32 base_ad);
void fpp_add (FPN *a, FPN *b, uint32 sub);
void fpp_mul (FPN *a, FPN *b);
void fpp_div (FPN *a, FPN *b);
t_bool fpp_imul (FPN *a, FPN *b);
uint32 fpp_fr_add (uint32 *c, uint32 *a, uint32 *b, uint32 cnt);
void fpp_fr_sub (uint32 *c, uint32 *a, uint32 *b, uint32 cnt);
void fpp_fr_mul (uint32 *c, uint32 *a, uint32 *b, t_bool fix);
t_bool fpp_fr_div (uint32 *c, uint32 *a, uint32 *b);
uint32 fpp_fr_neg (uint32 *a, uint32 cnt);
int32 fpp_fr_cmp (uint32 *a, uint32 *b, uint32 cnt);
int32 fpp_fr_test (uint32 *a, uint32 v0, uint32 cnt);
uint32 fpp_fr_abs (uint32 *a, uint32 *b, uint32 cnt);
void fpp_fr_fill (uint32 *a, uint32 v, uint32 cnt);
void fpp_fr_lshn (uint32 *a, uint32 sc, uint32 cnt);
void fpp_fr_lsh12 (uint32 *a, uint32 cnt);
void fpp_fr_lsh1 (uint32 *a, uint32 cnt);
void fpp_fr_rsh1 (uint32 *a, uint32 sign, uint32 cnt);
void fpp_fr_algn (uint32 *a, uint32 sc, uint32 cnt);
t_bool fpp_cond_met (uint32 cond);
t_bool fpp_norm (FPN *a, uint32 cnt);
void fpp_round (FPN *a);
t_bool fpp_test_xp (FPN *a);
void fpp_copy (FPN *a, FPN *b);
void fpp_zcopy (FPN *a, FPN *b);
void fpp_read_op (uint32 ea, FPN *a);
void fpp_write_op (uint32 ea, FPN *a);
uint32 fpp_read (uint32 ea);
void fpp_write (uint32 ea, uint32 val);
uint32 apt_read (uint32 ea);
void apt_write (uint32 ea, uint32 val);
t_stat fpp_svc (UNIT *uptr);
t_stat fpp_reset (DEVICE *dptr);
const char *fpp_description (DEVICE *dptr);

/* FPP data structures

   fpp_dev      FPP device descriptor
   fpp_unit     FPP unit descriptor
   fpp_reg      FPP register list
*/

DIB fpp_dib = { DEV_FPP, 2, { &fpp55, &fpp56 } };

UNIT fpp_unit = { UDATA (&fpp_svc, 0, 0) };

REG fpp_reg[] = {
    { ORDATAD (FPACE, fpp_ac.exp, 12, "floating accumulator") },
    { ORDATAD (FPAC0, fpp_ac.fr[0], 12, "first mantissa") },
    { ORDATAD (FPAC1, fpp_ac.fr[1], 12, "second mantissa") },
    { ORDATAD (FPAC2, fpp_ac.fr[2], 12, "third mantissa") },
    { ORDATAD (FPAC3, fpp_ac.fr[3], 12, "fourth mantissa") },
    { ORDATAD (FPAC4, fpp_ac.fr[4], 12, "fifth mantissa") },
    { ORDATAD (CMD, fpp_cmd, 12, "FPP command register") },
    { ORDATAD (STA, fpp_sta, 12, "status register") },
    { ORDATAD (APTA, fpp_apta, 15, "active parameter table (APT) pointer") },
    { GRDATAD (APTSVF, fpp_aptsvf, 8, 3, 12, "APT field") },
    { ORDATAD (FPC, fpp_fpc, 15, "floating program counter") },
    { ORDATAD (BRA, fpp_bra, 15, "base register") },
    { ORDATAD (XRA, fpp_xra, 15, "pointer to index register 0") },
    { ORDATAD (OPA, fpp_opa, 15, "operand address register") },
    { ORDATAD (SSF, fpp_ssf, 12, "single step flag") },
    { ORDATAD (LASTLOCK, fpp_last_lockbit, 12, "lockout from FPCOM") },
    { FLDATAD (FLAG, fpp_flag, 0, "done flag") },
    { NULL }
    };

MTAB fpp_mod[] = {
    { MTAB_XTD|MTAB_VDV, 0, "DEVNO", NULL, NULL, &show_dev },
    { 0 }
    };

DEVICE fpp_dev = {
    "FPP", &fpp_unit, fpp_reg, fpp_mod,
    1, 10, 31, 1, 8, 8,
    NULL, NULL, &fpp_reset,
    NULL, NULL, NULL,
    &fpp_dib, DEV_DISABLE | DEV_DIS, 0,
    NULL, NULL, NULL, NULL, NULL, NULL,
    &fpp_description
    };

/* IOT routines */

int32 fpp55 (int32 IR, int32 AC)
{
switch (IR & 07) {                                      /* decode IR<9:11> */

    case 1:                                             /* FPINT */
        return (fpp_flag? IOT_SKP | AC: AC);            /* skip on flag */

    case 2:                                             /* FPICL */
        fpp_reset (&fpp_dev);                           /* reset device */
        break;

    case 3:                                             /* FPCOM */
        if (!fpp_flag && !(fpp_sta & FPS_RUN)) {        /* flag clr, !run? */
            fpp_cmd = AC;                               /* load cmd */
            fpp_last_lockbit = fpp_cmd & FPS_LOCK;      /* remember lock state */
            fpp_sta = (fpp_sta & ~FPC_STA) |            /* copy flags */
                (fpp_cmd & FPC_STA);                    /* to status */
            }
        break;

    case 4:                                             /* FPHLT */
        if (fpp_sta & FPS_RUN) {                        /* running? */
            if (fpp_sta & FPS_PAUSE)                    /* paused? */
                fpp_fpc = (fpp_fpc - 1) & ADDRMASK;     /* decr FPC */
            fpp_sta &= ~FPS_PAUSE;                      /* no longer paused */
            sim_cancel (&fpp_unit);                     /* stop execution */
            fpp_dump_apt (fpp_apta, FPS_HLTX);          /* dump APT */
            fpp_ssf = 1;                                /* assume sstep */
            }
        else if (!fpp_flag)
            fpp_ssf = 1;                                /* FPST sing steps */
        if (fpp_sta & FPS_DVZX)                         /* fix diag timing */
            fpp_sta |= FPS_HLTX;
        break;

    case 5:                                             /* FPST */
        if (!fpp_flag && !(fpp_sta & FPS_RUN)) {        /* flag clr, !run? */
            if (fpp_ssf)
                fpp_sta |= fpp_last_lockbit; 
            fpp_sta &= ~FPS_HLTX;                       /* Clear halted */
            fpp_apta = (FPC_GETAPTF (fpp_cmd) << 12) | AC;
            fpp_load_apt (fpp_apta);                    /* load APT */
            fpp_opa = fpp_fpc;
            sim_activate (&fpp_unit, 0);                /* start unit */
            return IOT_SKP | AC;
            }
        if ((fpp_sta & (FPS_RUN|FPS_PAUSE)) == (FPS_RUN|FPS_PAUSE)) {
            fpp_sta &= ~FPS_PAUSE;                      /* continue */
            sim_activate (&fpp_unit, 0);                /* start unit */
            return (IOT_SKP | AC);
            }
        break;

    case 6:                                             /* FPRST */
        return fpp_sta;

    case 7:                                             /* FPIST */
        if (fpp_flag) {                                 /* if flag set */
            uint32 old_sta = fpp_sta;
            fpp_flag = 0;                               /* clr flag, status */
            fpp_sta &= ~(FPS_DP|FPS_EP|FPS_TRPX|FPS_DVZX|FPS_IOVX|FPS_FOVX|FPS_UNF);
            int_req &= ~INT_FPP;                        /* clr int req */
            return IOT_SKP | old_sta;                   /* ret old status */
            }
        break;

    default:
        return (stop_inst << IOT_V_REASON) | AC;
        }                                               /* end switch */

return AC;
}

int32 fpp56 (int32 IR, int32 AC)
{
switch (IR & 07) {                                      /* decode IR<9:11> */

    case 7:                                             /* FPEP */
        if ((AC & 04000) && !(fpp_sta & FPS_RUN)) {     /* if AC0, not run, */
            fpp_sta = (fpp_sta | FPS_EP) & ~FPS_DP;     /* set ep */
            AC = 0;
            }
        break;

    default:
        return (stop_inst << IOT_V_REASON) | AC;
        }                                               /* end switch */

return AC;
}

/* Service routine */

t_stat fpp_svc (UNIT *uptr)
{
FPN x;
uint32 ir, op, op2, op3, ad, ea, wd;
uint32 i;
int32 sc;

fpp_ac.exp = SEXT12 (fpp_ac.exp);                       /* sext AC exp */
do {                                                    /* repeat */
    ir = fpp_read (fpp_fpc);                            /* get instr */
    fpp_fpc = (fpp_fpc + 1) & ADDRMASK;                 /* incr FP PC */
    op = (ir >> 7) & 037;                               /* get op+mode */
    op2 = (ir >> 3) & 017;                              /* get subop */
    op3 = ir & 07;                                      /* get field/xr */
    fpp_sta &= ~FPS_XXXM;                               /* not mem op */

    switch (op) {                                       /* case on op+mode */
    case 000:                                           /* operates */

        switch (op2) {                                  /* case on subop */
        case 000:                                       /* no-operands */
            switch (op3) {                              /* case on subsubop */

            case 0:                                     /* FEXIT */
            /* if already trapped, don't update APT, just update status */
                if (fpp_sta & (FPS_DVZX|FPS_IOVX|FPS_FOVX|FPS_UNF)) 
                    fpp_sta |= FPS_HLTX;
                else
                    fpp_dump_apt (fpp_apta, 0);
                break;

            case 1:                                     /* FPAUSE */
                fpp_sta |= FPS_PAUSE;
                break;

            case 2:                                     /* FCLA */
                fpp_copy (&fpp_ac, &fpp_zero);          /* clear FAC */
                break;

            case 3:                                     /* FNEG */
                fpp_fr_neg (fpp_ac.fr, EXACT);          /* do exact length */
                break;

            case 4:                                     /* FNORM */
                if (!(fpp_sta & FPS_DP)) {              /* fp or ep only */
                    fpp_copy (&x, &fpp_ac);             /* copy AC */
                    fpp_norm (&x, EXACT);               /* do exact length */
                    fpp_copy (&fpp_ac, &x);             /* copy back */
                    }
                break;

            case 5:                                     /* STARTF */
                if (fpp_sta & FPS_EP) {                 /* if ep, */
                    fpp_copy (&x, &fpp_ac);             /* copy AC */
                    fpp_round (&x);                     /* round */
                    fpp_copy (&fpp_ac, &x);             /* copy back */
                    }
                fpp_sta &= ~(FPS_DP|FPS_EP);
                break;

            case 6:                                     /* STARTD */
                fpp_sta = (fpp_sta | FPS_DP) & ~FPS_EP;
                break;

            case 7:                                     /* JAC */
                fpp_fpc = ((fpp_ac.fr[0] & 07) << 12) | fpp_ac.fr[1];
                break;
                }
            break;

        case 001:                                       /* ALN */
            if (op3 != 0) {                             /* if xr, */
                wd = fpp_read_xr (op3);                 /* use val */
                fpp_opa = fpp_xra + op3;
                }
            else wd = 027;                              /* else 23 */
            if (!(fpp_sta & FPS_DP)) {                  /* fp or ep? */
                sc = (SEXT12(wd) - fpp_ac.exp) & 07777; /* alignment */
                sc = SEXT12 (sc);
                fpp_ac.exp = SEXT12(wd);                /* new exp */
                }
            else sc = SEXT12 (wd);                      /* dp - simple cnt */
            if (sc < 0)                                 /* left? */
                fpp_fr_lshn (fpp_ac.fr,  -sc, EXACT);
            else fpp_fr_algn (fpp_ac.fr, sc, EXACT);
            if (fpp_fr_test (fpp_ac.fr, 0, EXACT) == 0) /* zero? */
                fpp_ac.exp = 0;                         /* clean exp */
            break;

        case 002:                                       /* ATX */
            if (fpp_sta & FPS_DP)                       /* dp? */
                fpp_write_xr (op3, fpp_ac.fr[1]);       /* xr<-FAC<12:23> */
            else {
                fpp_copy (&x, &fpp_ac);                 /* copy AC */
                sc = 027 - x.exp;                       /* shift amt */
                if (sc < 0)                             /* left? */
                    fpp_fr_lshn (x.fr, -sc, EXACT);
                else fpp_fr_algn (x.fr, sc, EXACT);
                fpp_write_xr (op3, x.fr[1]);            /* xr<-val<12:23> */
                }
            break;

        case 003:                                       /* XTA */
            for (i = FPN_NFR_FP; i < FPN_NFR_EP; i++)
                x.fr[i] = 0;                            /* clear FOP2-4 */
            x.fr[1] = fpp_read_xr (op3);                /* get XR value */
            x.fr[0] = (x.fr[1] & 04000)? 07777: 0;
            x.exp = 027;                                /* standard exp */
            if (!(fpp_sta & FPS_DP)) {                  /* fp or ep? */
                fpp_norm (&x, EXACT);                   /* normalize */
                }
            fpp_copy (&fpp_ac, &x);                     /* result to AC */
            if (fpp_sta & FPS_DP)                       /* dp skips exp */
                fpp_ac.exp = x.exp;                     /*  so force copy */
            fpp_opa = fpp_xra + op3;
            break;

        case 004:                                       /* NOP */
            break;

        case 005:                                       /* STARTE */
            if (!(fpp_sta & FPS_EP)) {
                fpp_sta = (fpp_sta | FPS_EP) & ~FPS_DP;
                for (i = FPN_NFR_FP; i < FPN_NFR_EP; i++)
                    fpp_ac.fr[i] = 0;                   /* clear FAC2-4 */
                }
            break;

        case 010:                                       /* LDX */
            wd = fpp_ad15 (0);                          /* load XR immed */
            fpp_write_xr (op3, wd);
            fpp_opa = fpp_xra + op3;
            break;

        case 011:                                       /* ADDX */
            wd = fpp_ad15 (0);
            wd = wd + fpp_read_xr (op3);                /* add to XR immed */
            fpp_write_xr (op3, wd);                     /* trims to 12b */
            fpp_opa = fpp_xra + op3;
            break;

        default:
            return stop_inst;
            }                                           /* end case subop */
        break;

    case 001:                                           /* FLDA */
        ea = fpp_1wd_dir (ir);
        fpp_read_op (ea, &fpp_ac);
        break;

    case 002:
        ea = fpp_2wd_dir (ir);
        fpp_read_op (ea, &fpp_ac);
        if (fpp_sta & FPS_DP)
            fpp_opa = ea + 1;
        else fpp_opa = ea + 2;
        break;

    case 003:
        ea = fpp_indir (ir);
        fpp_read_op (ea, &fpp_ac);
        break;

    case 004:                                           /* jumps and sets */
        ad = fpp_ad15 (op3);                            /* get 15b address */
        switch (op2) {                                  /* case on subop */

        case 000: case 001: case 002: case 003:         /* cond jump */
        case 004: case 005: case 006: case 007:
            if (fpp_cond_met (op2))                     /* br if cond */
                fpp_fpc = ad;
            break;

        case 010:                                       /* SETX */
            fpp_xra = ad;
            break;

        case 011:                                       /* SETB */
            fpp_bra = ad;
            break;

        case 012:                                       /* JSA */
            fpp_write (ad, 01030 + (fpp_fpc >> 12));    /* save return */
            fpp_write (ad + 1, fpp_fpc);                /* trims to 12b */
            fpp_fpc = (ad + 2) & ADDRMASK;
            fpp_opa = fpp_fpc - 1;
            break;

        case 013:                                       /* JSR */
            fpp_write (fpp_bra + 1, 01030 + (fpp_fpc >> 12));
            fpp_write (fpp_bra + 2, fpp_fpc);           /* trims to 12b */
            fpp_opa = fpp_fpc = ad;
            break;

        default:
            return stop_inst;
            }                                           /* end case subop */
        break;

    case 005:                                           /* FADD */
        ea = fpp_1wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_add (&fpp_ac, &x, 0);
        break;
        
    case 006:
        ea = fpp_2wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_add (&fpp_ac, &x, 0);
        break;

    case 007:
        ea = fpp_indir (ir);
        fpp_read_op (ea, &x);
        fpp_add (&fpp_ac, &x, 0);
        break;

    case 010: {                                         /* JNX */
        uint32 xrn = op2 & 07;
        ad = fpp_ad15 (op3);                            /* get 15b addr */
        wd = fpp_read_xr (xrn);                         /* read xr */
        if (op2 & 010) {                                /* inc? */
            wd = (wd + 1) & 07777;
            fpp_write_xr (xrn, wd);                     /* ++xr */
            }
        if (wd != 0)                                    /* xr != 0? */
            fpp_fpc = ad;                               /* jump */
        break;
        }
    case 011:                                           /* FSUB */
        ea = fpp_1wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_add (&fpp_ac, &x, 1);
        break;
        
    case 012:
        ea = fpp_2wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_add (&fpp_ac, &x, 1);
        break;

    case 013:
        ea = fpp_indir (ir);
        fpp_read_op (ea, &x);
        fpp_add (&fpp_ac, &x, 1);
        break;

    case 014:                                           /* TRAP3 */
    case 020:                                           /* TRAP4 */
        fpp_opa = fpp_ad15 (op3);
        fpp_dump_apt (fpp_apta, FPS_TRPX);
        break;

    case 015:                                           /* FDIV */
        ea = fpp_1wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_div (&fpp_ac, &x);
        break;
        
    case 016:
        ea = fpp_2wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_div (&fpp_ac, &x);
        break;

    case 017:
        ea = fpp_indir (ir);
        fpp_read_op (ea, &x);
        fpp_div (&fpp_ac, &x);
        break;

    case 021:                                           /* FMUL */
        ea = fpp_1wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_mul (&fpp_ac, &x);
        break;
        
    case 022:
        ea = fpp_2wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_mul (&fpp_ac, &x);
        break;

    case 023:
        ea = fpp_indir (ir);
        fpp_read_op (ea, &x);
        fpp_mul (&fpp_ac, &x);
        break;

    case 024:                                           /* LTR */
        fpp_copy (&fpp_ac, (fpp_cond_met (op2 & 07)? &fpp_one: &fpp_zero));
        break;

    case 025:                                           /* FADDM */
        fpp_sta |= FPS_XXXM;
        ea = fpp_1wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_add (&x, &fpp_ac, 0);
        fpp_write_op (ea, &x);                          /* store result */
        break;
        
    case 026:
        fpp_sta |= FPS_XXXM;
        ea = fpp_2wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_add (&x, &fpp_ac, 0);
        fpp_write_op (ea, &x);                          /* store result */
        break;

    case 027:
        fpp_sta |= FPS_XXXM;
        ea = fpp_indir (ir);
        fpp_read_op (ea, &x);
        fpp_add (&x, &fpp_ac, 0);
        fpp_write_op (ea, &x);                          /* store result */
        break;

    case 030:                                           /* IMUL/LEA */
        ea = fpp_2wd_dir (ir);                          /* 2-word direct */
        if (fpp_sta & FPS_DP) {                         /* dp? */
            fpp_read_op (ea, &x);                       /* IMUL */
            fpp_imul (&fpp_ac, &x);
            }
        else {                                          /* LEA */
            fpp_sta = (fpp_sta | FPS_DP) & ~FPS_EP;     /* set dp */
            fpp_ac.fr[0] = (ea >> 12) & 07;
            fpp_ac.fr[1] = ea & 07777;
            }
        break;

    case 031:                                           /* FSTA */
        ea = fpp_1wd_dir (ir);
        fpp_write_op (ea, &fpp_ac);
        break;

    case 032:
        ea = fpp_2wd_dir (ir);
        fpp_write_op (ea, &fpp_ac);
        break;

    case 033:
        ea = fpp_indir (ir);
        fpp_write_op (ea, &fpp_ac);
        break;

    case 034:                                           /* IMULI/LEAI */
        ea = fpp_indir (ir);                            /* 1-word indir */
        if (fpp_sta & FPS_DP) {                         /* dp? */
            fpp_read_op (ea, &x);                       /* IMUL */
            fpp_imul (&fpp_ac, &x);
            }
        else {                                          /* LEA */
            fpp_sta = (fpp_sta | FPS_DP) & ~FPS_EP;     /* set dp */
            fpp_ac.fr[0] = (ea >> 12) & 07;
            fpp_ac.fr[1] = ea & 07777;
            fpp_opa = ea;
            }
        break;

    case 035:                                           /* FMULM */
        fpp_sta |= FPS_XXXM;
        ea = fpp_1wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_mul (&x, &fpp_ac);
        fpp_write_op (ea, &x);                          /* store result */
        break;
        
    case 036:
        fpp_sta |= FPS_XXXM;
        ea = fpp_2wd_dir (ir);
        fpp_read_op (ea, &x);
        fpp_mul (&x, &fpp_ac);
        fpp_write_op (ea, &x);                          /* store result */
        break;

    case 037:
        fpp_sta |= FPS_XXXM;
        ea = fpp_indir (ir);
        fpp_read_op (ea, &x);
        fpp_mul (&x, &fpp_ac);
        fpp_write_op (ea, &x);                          /* store result */
        break;
        }                                               /* end sw op+mode */

    if (fpp_ssf) {
        fpp_dump_apt (fpp_apta, FPS_HLTX);              /* dump APT */
        fpp_ssf = 0;
        }

    if (sim_interval)
        sim_interval = sim_interval - 1;
    } while ((sim_interval > 0) && 
             ((fpp_sta & (FPS_RUN|FPS_PAUSE|FPS_LOCK)) == (FPS_RUN|FPS_LOCK)));
if ((fpp_sta & (FPS_RUN|FPS_PAUSE)) == FPS_RUN)
    sim_activate (uptr, 1);
fpp_ac.exp &= 07777;                                    /* mask AC exp */
return SCPE_OK;
}

/* Address decoding routines */

uint32 fpp_1wd_dir (uint32 ir)
{
uint32 ad; 

ad = fpp_bra + ((ir & 0177) * 3);                       /* base + 3*7b off */
if (fpp_sta & FPS_DP)                                   /* dp? skip exp */
    ad = ad + 1;
ad = ad & ADDRMASK;
if (fpp_sta & FPS_DP)
    fpp_opa = ad + 1;
else fpp_opa = ad + 2;
return ad;
}

uint32 fpp_2wd_dir (uint32 ir)
{
uint32 ad;

ad = fpp_ad15 (ir);                                     /* get 15b addr */
return fpp_adxr (ir, ad);                               /* do indexing */
}

uint32 fpp_indir (uint32 ir)
{
uint32 ad, wd1, wd2;

ad = fpp_bra + ((ir & 07) * 3);                         /* base + 3*3b off */
wd1 = fpp_read (ad + 1);                                /* bp+off points to */
wd2 = fpp_read (ad + 2);
ad = ((wd1 & 07) << 12) | wd2;                          /* indirect ptr */

ad = fpp_adxr (ir, ad);                                 /* do indexing */
if (fpp_sta & FPS_DP)
    fpp_opa = ad + 1;
else fpp_opa = ad + 2;
return ad;
}

uint32 fpp_ad15 (uint32 hi)
{
uint32 ad;

ad = ((hi & 07) << 12) | fpp_read (fpp_fpc);            /* 15b addr */
fpp_fpc = (fpp_fpc + 1) & ADDRMASK;                     /* incr FPC */
return ad;                                              /* return addr */
}

uint32 fpp_adxr (uint32 ir, uint32 base_ad)
{
uint32 xr, wd;

xr = (ir >> 3) & 07;
wd = fpp_read_xr (xr);                                  /* get xr */
if (ir & 0100) {                                        /* increment? */
    wd = (wd + 1) & 07777;                              /* inc, rewrite */
    fpp_write_xr (xr, wd);
    }
if (xr != 0) {                                          /* indexed? */
    if (fpp_sta & FPS_EP)
        wd = wd * 6;                                    /* scale by len */
    else if (fpp_sta & FPS_DP)
        wd = wd * 2;
    else wd = wd * 3;
    return (base_ad + wd) & ADDRMASK;                   /* return index */
    }
else return base_ad & ADDRMASK;                         /* return addr */
}

/* Computation routines */

/* Fraction/floating add */

void fpp_add (FPN *a, FPN *b, uint32 sub)
{
FPN x, y, z;
uint32 c, ediff;

fpp_zcopy (&x, a);                                      /* copy opnds */
fpp_zcopy (&y, b);
if (sub)                                                /* subtract? */
    fpp_fr_neg (y.fr, EXACT);                           /* neg B, exact */
if (fpp_sta & FPS_DP) {                                 /* dp? */
    uint32 cout = fpp_fr_add (z.fr, x.fr, y.fr, EXTEND);/* z = a + b */
    uint32 zsign = z.fr[0] & FPN_FRSIGN;
    cout = (cout? 04000: 0);                            /* make sign bit */
    z.exp = 0;                                          /* not used in DP */
    /* overflow is indicated when signs are equal and overflow does not
       match the result sign bit */
    fpp_copy (a, &z);                                   /* result is z */
    if (!((x.fr[0] ^ y.fr[0]) & FPN_FRSIGN) && (cout != zsign)) {
        fpp_copy (a, &z);                               /* copy out result */
        fpp_dump_apt (fpp_apta, FPS_IOVX);              /* int ovf? */
        return;
        }
    }
else {                                                  /* fp or ep */
    if (fpp_fr_test (b->fr, 0, EXACT) == 0)             /* B == 0? */
        z = x;                                          /* result is A */
    else if (fpp_fr_test (a->fr, 0, EXACT) == 0)        /* A == 0? */
        z = y;                                          /* result is B */
    else {                                              /* fp or ep */
        if (x.exp < y.exp) {                            /* |a| < |b|? */
            z = x;                                      /* exchange ops */
            x = y;
            y = z;
            }
        ediff = x.exp - y.exp;                          /* exp diff */
        if (ediff <= (uint32) ((fpp_sta & FPS_EP)? 59: 24)) { /* any add? */
            z.exp = x.exp;                              /* result exp */
            if (ediff != 0)                             /* any align? */
                fpp_fr_algn (y.fr, ediff, EXTEND);      /* align, 60b */
            c = fpp_fr_add (z.fr, x.fr, y.fr, EXTEND);  /* add fractions */
            if ((((x.fr[0] ^ y.fr[0]) & FPN_FRSIGN) == 0) && /* same signs? */
                (c ||                                   /* carry out? */
                ((~x.fr[0] & z.fr[0] & FPN_FRSIGN)))) { /* + to - change? */
                fpp_fr_rsh1 (z.fr, c << 11, EXTEND);    /* rsh, insert cout */
                z.exp = z.exp + 1;                      /* incr exp */
                }                                       /* end same signs */
            }                                           /* end in range */
            else z = x;                                 /* ovrshift */
        }                                               /* end ops != 0 */
    if (fpp_norm (&z, EXTEND))                          /* norm, !exact? */
        fpp_round (&z);                                 /* round */
    fpp_copy (a, &z);                                   /* copy out */
    fpp_test_xp (&z);                                   /* ovf, unf? */
    }                                                   /* end else */
return;
}

/* Fraction/floating multiply */

void fpp_mul (FPN *a, FPN *b)
{
FPN x, y, z;

fpp_zcopy (&x, a);                                      /* copy opnds */
fpp_zcopy (&y, b);
if ((fpp_fr_test(y.fr, 0, EXACT-1) == 0) && (y.fr[EXACT-1] < 2)) {
    y.exp = 0;
    y.fr[EXACT-1] = 0;
}
if (fpp_sta & FPS_DP) {                                 /* dp? */
    fpp_fr_mul (z.fr, x.fr, y.fr, TRUE);                /* mult frac */
    z.exp = 0;                                          /* not used in DP */
    }
else {                                                  /* fp or ep */
    fpp_norm (&x, EXACT);
    fpp_norm (&y, EXACT);
    z.exp = x.exp + y.exp;                              /* add exp */
    fpp_fr_mul (z.fr, x.fr, y.fr, TRUE);                /* mult frac */
    if (fpp_norm (&z, EXTEND))                          /* norm, !exact? */
        fpp_round (&z);                                 /* round */
    fpp_copy (a, &z);
    if (z.exp > 2047)
        fpp_dump_apt (fpp_apta, FPS_FOVX);              /* trap */
    return;
    }
fpp_copy (a, &z);                                       /* result is z */
return;
}

/* Fraction/floating divide */

void fpp_div (FPN *a, FPN *b)
{
FPN x, y, z;

if (fpp_fr_test (b->fr, 0, EXACT) == 0) {               /* divisor 0? */
    fpp_dump_apt (fpp_apta, FPS_DVZX);                  /* error */
    return;
    }
if (fpp_fr_test (a->fr, 0, EXACT) == 0)                 /* dividend 0? */
    return;                                             /* quotient is 0 */
fpp_zcopy (&x, a);                                      /* copy opnds */
fpp_zcopy (&y, b);
if (fpp_sta & FPS_DP) {                                 /* dp? */
    if (fpp_fr_div (z.fr, x.fr, y.fr)) {                /* fr div, ovflo? */
        fpp_dump_apt (fpp_apta, FPS_IOVX);              /* error */
        return;
        }
    z.exp = 0;                                          /* not used in DP */
    fpp_copy (a, &z);                                   /* result is z */
    }
else {                                                  /* fp or ep */
    fpp_norm (&y, EXACT);                               /* norm divisor */
    if (fpp_fr_test (x.fr, 04000, EXACT) == 0) {        /* divd 1.000...? */
        x.fr[0] = 06000;                                /* fix */
        x.exp = x.exp + 1;
        }
    z.exp = x.exp - y.exp;                              /* calc exp */
    if (fpp_fr_div (z.fr, x.fr, y.fr)) {                /* fr div, ovflo? */
        uint32 cin = (a->fr[0] ^ b->fr[0]) & FPN_FRSIGN;
        fpp_fr_rsh1 (z.fr, cin, EXTEND);                /* rsh, insert sign */
        z.exp = z.exp + 1;                              /* incr exp */
        }
    if (fpp_norm (&z, EXTEND))                          /* norm, !exact? */
        fpp_round (&z);                                 /* round */
    fpp_copy (a, &z);
    if (z.exp > 2048) {                                /* underflow? */
        if (fpp_cmd & FPC_UNFX) {                       /* trap? */
            fpp_dump_apt (fpp_apta, FPS_UNF);
            return;
            }
        }
    }
return;
}

/* Integer multiply - returns true if overflow */

t_bool fpp_imul (FPN *a, FPN *b)
{
uint32 sext;
FPN x, y, z;

fpp_zcopy (&x, a);                                      /* copy args */
fpp_zcopy (&y, b);
fpp_fr_mul (z.fr, x.fr, y.fr, FALSE);                   /* mult fracs */
a->fr[0] = z.fr[1];                                     /* low 24b */
a->fr[1] = z.fr[2];
if ((a->fr[0] == 0) && (a->fr[1] == 0))                 /* fpp zeroes exp */
    a->exp = 0;                                         /* even in dp mode */
sext = (z.fr[2] & FPN_FRSIGN)? 07777: 0;
if (((z.fr[0] | z.fr[1] | sext) != 0) &&                /* hi 25b == 0 */
    ((z.fr[0] & z.fr[1] & sext) != 07777)) {            /* or 777777774? */
    fpp_dump_apt (fpp_apta, FPS_IOVX);
    return TRUE;
    }
return FALSE;
}

/* Auxiliary floating point routines */

t_bool fpp_cond_met (uint32 cond)
{
switch (cond) {

    case 0:
        return (fpp_fr_test (fpp_ac.fr, 0, EXACT) == 0);

    case 1:
        return (fpp_fr_test (fpp_ac.fr, 0, EXACT) >= 0);

    case 2:
        return (fpp_fr_test (fpp_ac.fr, 0, EXACT) <= 0);

    case 3:
        return 1;

    case 4:
        return (fpp_fr_test (fpp_ac.fr, 0, EXACT) != 0);

    case 5:
        return (fpp_fr_test (fpp_ac.fr, 0, EXACT) < 0);

    case 6:
        return (fpp_fr_test (fpp_ac.fr, 0, EXACT) > 0);

    case 7:
        return (fpp_ac.exp > 027);
    }
return 0;
}

/* Normalization - returns TRUE if rounding possible, FALSE if exact */

t_bool fpp_norm (FPN *a, uint32 cnt)
{
if (fpp_fr_test (a->fr, 0, cnt) == 0) {                 /* zero? */
    a->exp = 0;                                         /* clean exp */
    return FALSE;                                       /* don't round */
    }
while (((a->fr[0] == 0) && !(a->fr[1] & 04000)) ||      /* lead 13b same? */
       ((a->fr[0] == 07777) && (a->fr[1] & 04000))) {
    fpp_fr_lsh12 (a->fr, cnt);                          /* move word */
    a->exp = a->exp - 12;
    }
while (((a->fr[0] ^ (a->fr[0] << 1)) & FPN_FRSIGN) == 0) { /* until norm */
    fpp_fr_lsh1 (a->fr, cnt);                           /* shift 1b */
    a->exp = a->exp - 1;
    }
if (fpp_fr_test (a->fr, 04000, EXACT) == 0) {           /* 4000...0000? */
    a->fr[0] = 06000;                                   /* chg to 6000... */
    a->exp = a->exp + 1;                                /* with exp+1 */
    return FALSE;                                       /* don't round */
    }
return TRUE;
}

/* Exact fp number copy */

void fpp_copy (FPN *a, FPN *b)
{
uint32 i;

if (!(fpp_sta & FPS_DP))
    a->exp = b->exp;
for (i = 0; i < EXACT; i++)
    a->fr[i] = b->fr[i];
return;
}

/* Zero extended fp number copy (60b) */

void fpp_zcopy (FPN *a, FPN *b)
{
uint32 i;

a->exp = b->exp;
for (i = 0; i < FPN_NFR_EP; i++) {
    if ((i < FPN_NFR_FP) || (fpp_sta & FPS_EP))
        a->fr[i] = b->fr[i];
    else a->fr[i] = 0;
    }
a->fr[i++] = 0;
a->fr[i] = 0;
return;
}

/* Test exp for overflow or underflow, returns TRUE on trap */

t_bool fpp_test_xp (FPN *a)
{
if (a->exp > 2047) {                                /* overflow? */
    fpp_dump_apt (fpp_apta, FPS_FOVX);              /* trap */
    return TRUE;
    }
if (a->exp < -2048) {                               /* underflow? */
    if (fpp_cmd & FPC_UNFX) {                       /* trap? */
        fpp_dump_apt (fpp_apta, FPS_UNF);
        return TRUE;
        }
    fpp_copy (a, &fpp_zero);                        /* flush to 0 */
    }
return FALSE;
}

/* Round dp/fp value */

void fpp_round (FPN *a)
{
int32 i;
uint32 cin, afr0_sign;

if (fpp_sta & FPS_EP)                               /* ep? */
    return;                                         /* don't round */
afr0_sign = a->fr[0] & FPN_FRSIGN;                  /* save input sign */
cin = afr0_sign? 03777: 04000;
for (i = FPN_NFR_FP; i >= 0; i--) {                 /* 3 words */
   a->fr[i] = a->fr[i] + cin;                       /* add in carry */
   cin = (a->fr[i] >> 12) & 1;
   a->fr[i] = a->fr[i] & 07777;
   }
if (!(fpp_sta & FPS_DP) &&                          /* fp? */
    (afr0_sign ^ (a->fr[0] & FPN_FRSIGN))) {        /* sign change? */
    fpp_fr_rsh1 (a->fr, afr0_sign, EXACT);          /* rsh, insert sign */
    a->exp = a->exp + 1;
    }
return;
}

/* N-precision integer routines */

/* Fraction add/sub */

uint32 fpp_fr_add (uint32 *c, uint32 *a, uint32 *b, uint32 cnt)

{
uint32 i, cin;

for (i = cnt, cin = 0; i > 0; i--) {
    c[i - 1] = a[i - 1] + b[i - 1] + cin;
    cin = (c[i - 1] >> 12) & 1;
    c[i - 1] = c[i - 1] & 07777;
    }
return cin;
}

void fpp_fr_sub (uint32 *c, uint32 *a, uint32 *b, uint32 cnt)
{
uint32 i, cin;

for (i = cnt, cin = 0; i > 0; i--) {
    c[i - 1] = a[i - 1] - b[i - 1] - cin;
    cin = (c[i - 1] >> 12) & 1;
    c[i - 1] = c[i - 1] & 07777;
    }
return;
}

/* Fraction multiply - always develop 60b, multiply is
   either 24b*24b or 60b*60b
   
   This is a signed multiply.  The shift in for signed multiply is
   technically ALU_N XOR ALU_V.  This can be simplified as follows:

   a-sign   c-sign  result-sign     cout    overflow    N XOR V = shift in

   0        0       0               0       0           0
   0        0       1               0       1           0
   0        1       0               1       0           0
   0        1       1               0       0           1
   1        0       0               1       0           0
   1        0       1               0       0           1
   1        1       0               1       1           1
   1        1       1               1       0           1

   If a-sign == c-sign, shift-in = a-sign
   If a-sign != c-sign, shift-in = result-sign
   */

void fpp_fr_mul (uint32 *c, uint32 *a, uint32 *b, t_bool fix)
{
uint32 i, cnt, lo, wc, fill, b_sign;

b_sign = b[0] & FPN_FRSIGN;                         /* remember b's sign */

fpp_fr_fill (c, 0, FPN_NFR_MDS);                    /* clr answer */
if (fpp_sta & FPS_EP)                               /* ep? */
    lo = FPN_NFR_EP;                                /* low order mpyr word */
else  
    lo = FPN_NFR_FP;                                /* low order mpyr word */

if (fix)
    fpp_fr_algn (a, 12, FPN_NFR_MDS + 1);           /* fill left with sign */
wc = 2;                                             /* 3 words at start */
fill = 0;
cnt = lo * 12;                                      /* total steps */
for (i = 0; i < cnt; i++) { 
    if ((i % 12) == 0) {
        wc++;                                       /* do another word */
        lo--;                                       /* and next mpyr word */
        fpp_fr_algn (c, 24, wc + 1);
        if (wc < FPN_NFR_MDS)                       /* don't assume guard word */
            c[wc] = 0;
        c[0] = c[1] = fill;                         /* propagate sign */
        }
    if (b[lo] & FPN_FRSIGN)                         /* mpyr bit set? */
        fpp_fr_add(c, a, c, wc);
    fill = ((c[0] & FPN_FRSIGN) ? 07777 : 0);       /* remember sign */
    fpp_fr_lsh1 (c, wc);                            /* shift the result */
    fpp_fr_lsh1 (b + lo, 1);                        /* shift mpcd */
    
    }

if (!fix)                                           /* imul shifts result */
    fpp_fr_rsh1 (c, c[0] & FPN_FRSIGN, EXACT + 1);  /* result is 1 wd right */
if (b_sign) {                                       /* if mpyr was negative */
    if (fix)
        fpp_fr_lsh12 (a, FPN_NFR_MDS+1);            /* restore a */
    fpp_fr_sub (c, c, a, EXACT);                    /* adjust result */
    fpp_fr_sub (c, c, a, EXACT);
    }

return;
}

/* Fraction divide */

t_bool fpp_fr_div (uint32 *c, uint32 *a, uint32 *b)
{
uint32 i, old_c, lo, cnt, sign, b_sign, addsub, limit;
/* Number of words processed by each divide step */
static uint32 limits[7] = {6, 6, 5, 4, 3, 3, 2};

fpp_fr_fill (c, 0, FPN_NFR_MDS);                    /* clr answer */
sign = (a[0] ^ b[0]) & FPN_FRSIGN;                  /* sign of result */
b_sign = (b[0] & FPN_FRSIGN);
if (a[0] & FPN_FRSIGN)                              /* |a| */
    fpp_fr_neg (a, EXACT);
if (fpp_sta & FPS_EP)                               /* ep? 6 words */
    lo = FPN_NFR_EP-1;
else lo = FPN_NFR_FP-1;                             /* fp, dp? 3 words */
cnt = (lo + 1) * 12;
addsub = 04000;                                     /* setup first op */
for (i = 0; i < cnt; i++) {                         /* loop */
    limit = limits[i / 12];                         /* how many wds this time */
    fpp_fr_lsh1 (c, FPN_NFR_MDS);                   /* shift quotient */
    if (addsub ^ b_sign)                            /* diff signs, subtr */
        fpp_fr_sub (a, a, b, limit);                /* divd - divr */
    else
        fpp_fr_add (a, a, b, limit);                /* restore */
    if (!(a[0] & FPN_FRSIGN)) {
        c[lo] |= 1;                                 /* set quo bit */
        addsub = 04000;                             /* sign for nxt loop */
        }
    else addsub = 0;
    fpp_fr_lsh1 (a, limit);                         /* shift dividend */
    }
old_c = c[0];                                       /* save ho quo */
if (sign)                                           /* expect neg ans? */
    fpp_fr_neg (c, EXTEND);                         /* -quo */
if (old_c & FPN_FRSIGN)                             /* sign set before */
    return TRUE;                                    /* neg? */
return FALSE;
}

/* Negate - 24b or 60b */

uint32 fpp_fr_neg (uint32 *a, uint32 cnt)
{
uint32 i, cin;

for (i = cnt, cin = 1; i > 0; i--) {
    a[i - 1] = (~a[i - 1] + cin) & 07777;
    cin = (cin != 0 && a[i - 1] == 0);
    }
return cin;
}

/* Test (compare to x'0...0) - 24b or 60b */

int32 fpp_fr_test (uint32 *a, uint32 v0, uint32 cnt)
{
uint32 i;

if (a[0] != v0)
    return (a[0] & FPN_FRSIGN)? -1: +1;
for (i = 1; i < cnt; i++) {
    if (a[i] != 0)
        return (a[0] & FPN_FRSIGN)? -1: +1;
    }
return 0;
}

/* Fraction compare - 24b or 60b */

int32 fpp_fr_cmp (uint32 *a, uint32 *b, uint32 cnt)
{
uint32 i;

if ((a[0] ^ b[0]) & FPN_FRSIGN)
    return (b[0] & FPN_FRSIGN)? +1: -1;
for (i = 0; i < cnt; i++) {
    if (a[i] > b[i])
        return (b[0] & FPN_FRSIGN)? +1: -1;
    if (a[i] < b[i])
        return (b[0] & FPN_FRSIGN)? -1: +1;
    }
return 0;
}

/* Fraction fill */

void fpp_fr_fill (uint32 *a, uint32 v, uint32 cnt)
{
uint32 i;

for (i = 0; i < cnt; i++)
    a[i] = v;
return;
}

/* Left shift n (unsigned) */

void fpp_fr_lshn (uint32 *a, uint32 sc, uint32 cnt)
{
uint32 i;

if (sc >= (cnt * 12)) {                             /* out of range? */
    fpp_fr_fill (a, 0, cnt);
    return;
    }
while (sc >= 12) {                                  /* word shift? */
    fpp_fr_lsh12 (a, cnt);
    sc = sc - 12;
    }
if (sc == 0)                                        /* any more? */
    return;
for (i = 1; i < cnt; i++)                           /* bit shift */
    a[i - 1] = ((a[i - 1] << sc) | (a[i] >> (12 - sc))) & 07777;
a[cnt - 1] = (a[cnt - 1] << sc) & 07777;
return;
}

/* Left shift 12b (unsigned) */

void fpp_fr_lsh12 (uint32 *a, uint32 cnt)
{
uint32 i;

for (i = 1; i < cnt; i++)
    a[i - 1] = a[i];
a[cnt - 1] = 0;
return;
}

/* Left shift 1b (unsigned) */

void fpp_fr_lsh1 (uint32 *a, uint32 cnt)
{
uint32 i;

for (i = 1; i < cnt; i++)
    a[i - 1] = ((a[i - 1] << 1) | (a[i] >> 11)) & 07777;
a[cnt - 1] = (a[cnt - 1] << 1) & 07777;
return;
}

/* Right shift 1b, with shift in */

void fpp_fr_rsh1 (uint32 *a, uint32 sign, uint32 cnt)
{
uint32 i;

for (i = cnt - 1; i > 0; i--)
    a[i] = ((a[i] >> 1) | (a[i - 1] << 11)) & 07777;
a[0] = (a[0] >> 1) | sign;
return;
}

/* Right shift n (signed) */

void fpp_fr_algn (uint32 *a, uint32 sc, uint32 cnt)
{
uint32 i, sign;

sign = (a[0] & FPN_FRSIGN)? 07777: 0;
if (sc >= (cnt * 12)) {                             /* out of range? */
    fpp_fr_fill (a, sign, cnt);
    return;
    }
while (sc >= 12) {
    for (i = cnt - 1; i > 0; i--)
        if (i <= FPN_NFR_MDS)                       /* Don't overwrite if EP */
            a[i] = a[i - 1];
    a[0] = sign;
    sc = sc - 12;
    }
if (sc == 0)
    return;
for (i = cnt - 1; i > 0; i--)
    a[i] = ((a[i] >> sc) | (a[i - 1] << (12 - sc))) & 07777;
a[0] = ((a[0] >> sc) | (sign << (12 - sc))) & 07777;
return;
}

/* Read/write routines */

void fpp_read_op (uint32 ea, FPN *a)
{
uint32 i;

if (!(fpp_sta & FPS_DP)) {
    a->exp = fpp_read (ea++);
    a->exp = SEXT12 (a->exp);
    }
for (i = 0; i < EXACT; i++)
    a->fr[i] = fpp_read (ea + i);
return;
}

void fpp_write_op (uint32 ea, FPN *a)
{
uint32 i;

fpp_opa = ea + 2;
if (!(fpp_sta & FPS_DP))
    fpp_write (ea++, a->exp);
for (i = 0; i < EXACT; i++)
    fpp_write (ea + i, a->fr[i]);
return;
}

uint32 fpp_read (uint32 ea)
{
ea = ea & ADDRMASK;
if (fpp_cmd & FPC_FIXF)
    ea = fpp_aptsvf | (ea & 07777);
return M[ea];
}

void fpp_write (uint32 ea, uint32 val)
{
ea = ea & ADDRMASK;
if (fpp_cmd & FPC_FIXF)
    ea = fpp_aptsvf | (ea & 07777);
if (MEM_ADDR_OK (ea))
    M[ea] = val & 07777;
return;
}

uint32 apt_read (uint32 ea)
{
ea = ea & ADDRMASK;
return M[ea];
}

void apt_write (uint32 ea, uint32 val)
{
ea = ea & ADDRMASK;
if (MEM_ADDR_OK (ea))
    M[ea] = val & 07777;
return;
}

/* Utility routines */

void fpp_load_apt (uint32 ad)
{
uint32 wd0, i;

wd0 = apt_read (ad++);
fpp_fpc = ((wd0 & 07) << 12) | apt_read (ad++);
if (FPC_GETFAST (fpp_cmd) != 017) {
    fpp_xra = ((wd0 & 00070) << 9) | apt_read (ad++);
    fpp_bra = ((wd0 & 00700) << 6) | apt_read (ad++);
    fpp_opa = ((wd0 & 07000) << 3) | apt_read (ad++);
    fpp_ac.exp = apt_read (ad++);
    for (i = 0; i < EXACT; i++)
        fpp_ac.fr[i] = apt_read (ad++);
    }
fpp_aptsvf = (ad - 1) & 070000;
fpp_sta |= FPS_RUN;
return;
}

void fpp_dump_apt (uint32 ad, uint32 sta)
{
uint32 wd0, i;

wd0 = (fpp_fpc >> 12) & 07;
if (FPC_GETFAST (fpp_cmd) != 017)
    wd0 = wd0 |
        ((fpp_opa >> 3) & 07000) |
        ((fpp_bra >> 6) & 00700) |
        ((fpp_xra >> 9) & 00070);
apt_write (ad++, wd0);
apt_write (ad++, fpp_fpc);
if (FPC_GETFAST (fpp_cmd) != 017) {
    apt_write (ad++, fpp_xra);
    apt_write (ad++, fpp_bra);
    apt_write (ad++, fpp_opa);
    apt_write (ad++, fpp_ac.exp);
    for (i = 0; i < EXACT; i++)
        apt_write (ad++, fpp_ac.fr[i]);
    }
fpp_sta = (fpp_sta | sta) & ~FPS_RUN;
fpp_flag = 1;
if (fpp_cmd & FPC_IE)
    int_req |= INT_FPP;
return;
}

/* Reset routine */

t_stat fpp_reset (DEVICE *dptr)
{
sim_cancel (&fpp_unit);
fpp_flag = 0;
fpp_last_lockbit = 0;
int_req &= ~INT_FPP;
if (sim_switches & SWMASK ('P')) {
    fpp_apta = 0;
    fpp_aptsvf = 0;
    fpp_fpc = 0;
    fpp_bra = 0;
    fpp_xra = 0;
    fpp_opa = 0;
    fpp_ac = fpp_zero;
    fpp_ssf = 0;
    fpp_sta = 0;
    fpp_cmd = 0;
    }
else {
    fpp_sta &= ~(FPS_DP|FPS_EP|FPS_TRPX|FPS_DVZX|FPS_IOVX|FPS_FOVX|FPS_UNF);
    fpp_cmd &= (FPC_DP|FPC_UNFX|FPC_IE);
    }

return SCPE_OK;
}

const char *fpp_description (DEVICE *dptr)
{
return "FPP8A floating point processor";
}
