/* pdp11_cis.c: PDP-11 CIS optional instruction set simulator

   Copyright (c) 1993-2008, 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.

   This module simulates the PDP-11 commercial instruction set (CIS).

   16-Oct-08    RMS     Fixed overflow bug in ASHx (Word/NibbleLShift)
                        Fixed bug in DIVx (LntDstr calculation)
   30-May-06    RMS     Added interrupt tests to character instructions
                        Added 11/44 stack probe test to MOVCx (only)
   22-May-06    RMS     Fixed bug in decode table (John Dundas)
                        Fixed bug in ASHP (John Dundas)
                        Fixed bug in write decimal string with mmgt enabled
                        Fixed bug in 0-length strings in multiply/divide
   16-Sep-04    RMS     Fixed bug in CMPP/N of negative strings
   17-Oct-02    RMS     Fixed compiler warning (Hans Pufal)
   08-Oct-02    RMS     Fixed macro definitions

   The commercial instruction set consists of three instruction formats:

   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    register operands
   | 0  1  1  1  1  1| 0  0  0  0|      opcode     |    076030:076057
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    076070:076077

   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    inline operands
   | 0  1  1  1  1  1| 0  0  0  1|      opcode     |    076130:076157
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    076170:076177

   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    load descriptors
   | 0  1  1  1  1  1| 0  0  0  0|op| 1  0|  reg   |    076020:076027
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    076060:076067

   The CIS instructions operate on character strings, packed (decimal)
   strings, and numeric (decimal) strings.  Strings are described by
   a two word descriptor:

   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
   |                 length in bytes               |    char string
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    descriptor
   |             starting byte address             |
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
   |  |str type|                    |   length     |    decimal string
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+    descriptor
   |             starting byte address             |
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

   Decimal string types are:

     <14:12>            data type               bytes occupied by n digits
        0               signed zoned                    n
        1               unsigned zone                   n
        2               trailing overpunch              n
        3               leading overpunch               n
        4               trailing separate               n+1
        5               leading separate                n+1
        6               signed packed                   n/2 +1
        7               unsigned packed                 n/2 +1

   Zero length character strings occupy no memory; zero length decimal strings
   require either zero bytes (zoned, overpunch) or one byte (separate, packed).

   CIS instructions can run for a very long time, so they are interruptible
   and restartable.  In the simulator, all instructions run to completion.
   The code is unoptimized.
*/

#include "pdp11_defs.h"

/* Opcode bits */

#define INLINE          0100                            /* inline */
#define PACKED          0020                            /* packed */
#define NUMERIC         0000                            /* numeric */

/* Interrupt test latency */

#define INT_TEST        100

/* Operand type definitions */

#define R0_DESC         1                               /* descr in R0:R1 */
#define R2_DESC         2                               /* descr in R2:R3 */
#define R4_DESC         3                               /* descr in R4:R5 */
#define R4_ARG          4                               /* argument in R4 */
#define IN_DESC         5                               /* inline descriptor */
#define IN_ARG          6                               /* inline argument */
#define MAXOPN          4                               /* max # operands */

/* Decimal data type definitions */

#define XZ              0                               /* signed zoned */
#define UZ              1                               /* unsigned zoned */
#define TO              2                               /* trailing overpunch */
#define LO              3                               /* leading overpunch */
#define TS              4                               /* trailing separate */
#define LS              5                               /* leading separate */
#define XP              6                               /* signed packed */
#define UP              7                               /* unsigned packed */

/* Decimal descriptor definitions */

#define DTYP_M          07                              /* type mask */
#define DTYP_V          12                              /* type position */
#define DLNT_M          037                             /* length mask */
#define DLNT_V          0                               /* length position */
#define GET_DTYP(x)     (((x) >> DTYP_V) & DTYP_M)
#define GET_DLNT(x)     (((x) >> DLNT_V) & DLNT_M)

/* Shift operand definitions */

#define ASHRND_M        017                             /* round digit mask */
#define ASHRND_V        8                               /* round digit pos */
#define ASHLNT_M        0377                            /* shift count mask */
#define ASHLNT_V        0                               /* shift length pos */
#define ASHSGN          0200                            /* shift sign */
#define GET_ASHRND(x)   (((x) >> ASHRND_V) & ASHRND_M)
#define GET_ASHLNT(x)   (((x) >> ASHLNT_V) & ASHLNT_M)

/* Operand array aliases */

#define A1LNT           arg[0]
#define A1ADR           arg[1]
#define A2LNT           arg[2]
#define A2ADR           arg[3]
#define A3LNT           arg[4]
#define A3ADR           arg[5]
#define A1              &arg[0]
#define A2              &arg[2]
#define A3              &arg[4]

/* Condition code macros */

#define GET_BIT(ir,n)   (((ir) >> (n)) & 1)
#define GET_SIGN_L(ir)  GET_BIT((ir), 31)
#define GET_SIGN_W(ir)  GET_BIT((ir), 15)
#define GET_SIGN_B(ir)  GET_BIT((ir), 7)
#define GET_Z(ir)       ((ir) == 0)

/* Decimal string structure */

#define DSTRLNT         4
#define DSTRMAX         (DSTRLNT - 1)
#define MAXDVAL         429496730                       /* 2^32 / 10 */

typedef struct {
    uint32              sign;
    uint32              val[DSTRLNT];
    } DSTR;

static DSTR Dstr0 = { 0, {0, 0, 0, 0} };

extern int32 isenable, dsenable;
extern int32 N, Z, V, C, fpd, ipl;
extern int32 R[8], trap_req;

int32 ReadDstr (int32 *dscr, DSTR *dec, int32 flag);
void WriteDstr (int32 *dscr, DSTR *dec, int32 flag);
int32 AddDstr (DSTR *src1, DSTR *src2, DSTR *dst, int32 cin);
void SubDstr (DSTR *src1, DSTR *src2, DSTR *dst);
int32 CmpDstr (DSTR *src1, DSTR *src2);
int32 TestDstr (DSTR *dsrc);
int32 LntDstr (DSTR *dsrc, int32 nz);
uint32 NibbleLshift (DSTR *dsrc, int32 sc);
uint32 NibbleRshift (DSTR *dsrc, int32 sc, uint32 cin);
int32 WordLshift (DSTR *dsrc, int32 sc);
void WordRshift (DSTR *dsrc, int32 sc);
void CreateTable (DSTR *dsrc, DSTR mtable[10]);
t_bool cis_int_test (int32 cycles, int32 oldpc, t_stat *st);
int32 movx_setup (int32 op, int32 *arg);
void movx_cleanup (int32 op);

extern int32 ReadW (int32 addr);
extern void WriteW (int32 data, int32 addr);
extern int32 ReadB (int32 addr);
extern int32 ReadMB (int32 addr);
extern void WriteB (int32 data, int32 addr);
extern int32 calc_ints (int32 nipl, int32 trq);

/* Table of instruction operands */

static int32 opntab[128][MAXOPN] = {
    {0, 0, 0, 0}, {0, 0, 0, 0},                         /* 000 - 007 */
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {0, 0, 0, 0}, {0, 0, 0, 0},                         /* 010 - 017 */
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {0, 0, 0, 0}, {0, 0, 0, 0},                         /* L2DR */
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {0, 0, 0, 0},                                       /* MOVC */
    {0, 0, 0, 0},                                       /* MOVRC */
    {0, 0, 0, 0},                                       /* MOVTC */
                  {0, 0, 0, 0},                         /* 033 */
    {0, 0, 0, 0}, {0, 0, 0, 0},                         /* 034 - 037 */ 
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {0, 0, 0, 0},                                       /* LOCC */
    {0, 0, 0, 0},                                       /* SKPC */
    {0, 0, 0, 0},                                       /* SCANC */
    {0, 0, 0, 0},                                       /* SPANC */
    {0, 0, 0, 0},                                       /* CMPC */
    {0, 0, 0, 0},                                       /* MATC */
    {0, 0, 0, 0}, {0, 0, 0, 0},                         /* 046 - 047 */
    {R0_DESC, R2_DESC, R4_DESC, 0},                     /* ADDN */
    {R0_DESC, R2_DESC, R4_DESC, 0},                     /* SUBN */
    {R0_DESC, R2_DESC, 0, 0},                           /* CMPN */
    {R0_DESC, 0, 0, 0},                                 /* CVTNL */
    {R0_DESC, R2_DESC, 0, 0},                           /* CVTPN */
    {R0_DESC, R2_DESC, 0, 0},                           /* CVTNP */
    {R0_DESC, R2_DESC, R4_ARG, 0},                      /* ASHN */
    {R0_DESC, 0, 0, 0},                                 /* CVTLN */
    {0, 0, 0, 0}, {0, 0, 0, 0},                         /* L3DR */
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {R0_DESC, R2_DESC, R4_DESC, 0},                     /* ADDP */
    {R0_DESC, R2_DESC, R4_DESC, 0},                     /* SUBP */
    {R0_DESC, R2_DESC, 0, 0},                           /* CMPP */
    {R0_DESC, 0, 0, 0},                                 /* CVTPL */
    {R0_DESC, R2_DESC, R4_DESC, 0},                     /* MULP */
    {R0_DESC, R2_DESC, R4_DESC, 0},                     /* DIVP */
    {R0_DESC, R2_DESC, R4_ARG, 0},                      /* ASHP */
    {R0_DESC, 0, 0, 0},                                 /* CVTLP */
    {0, 0, 0, 0}, {0, 0, 0, 0},                         /* 100 - 107 */
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {0, 0, 0, 0}, {0, 0, 0, 0},                         /* 110 - 117 */
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {0, 0, 0, 0}, {0, 0, 0, 0},                         /* 120 - 127 */
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {IN_DESC, IN_DESC, IN_ARG, 0},                      /* MOVCI */
    {IN_DESC, IN_DESC, IN_ARG, 0},                      /* MOVRCI */
    {IN_DESC, IN_DESC, IN_ARG, IN_ARG},                 /* MOVTCI */
                  {0, 0, 0, 0},                         /* 133 */
    {0, 0, 0, 0}, {0, 0, 0, 0},                         /* 134 - 137 */
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {IN_DESC, IN_ARG, 0, 0},                            /* LOCCI */
    {IN_DESC, IN_ARG, 0, 0},                            /* SKPCI */
    {IN_DESC, IN_DESC, 0, 0},                           /* SCANCI */
    {IN_DESC, IN_DESC, 0, 0},                           /* SPANCI */
    {IN_DESC, IN_DESC, IN_ARG, 0},                      /* CMPCI */
    {IN_DESC, IN_DESC, 0, 0},                           /* MATCI */
    {0, 0, 0, 0}, {0, 0, 0, 0},                         /* 146 - 147 */
    {IN_DESC, IN_DESC, IN_DESC, 0},                     /* ADDNI */
    {IN_DESC, IN_DESC, IN_DESC, 0},                     /* SUBNI */
    {IN_DESC, IN_DESC, 0, 0},                           /* CMPNI */
    {IN_DESC, IN_ARG, 0, 0},                            /* CVTNLI */
    {IN_DESC, IN_DESC, 0, 0},                           /* CVTPNI */
    {IN_DESC, IN_DESC, 0, 0},                           /* CVTNPI */
    {IN_DESC, IN_DESC, IN_ARG, 0},                      /* ASHNI */
    {IN_DESC, IN_DESC, 0, 0},                           /* CVTLNI */
    {0, 0, 0, 0}, {0, 0, 0, 0},                         /* 160 - 167 */
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {0, 0, 0, 0}, {0, 0, 0, 0},
    {IN_DESC, IN_DESC, IN_DESC, 0},                     /* ADDPI */
    {IN_DESC, IN_DESC, IN_DESC, 0},                     /* SUBPI */
    {IN_DESC, IN_DESC, 0, 0},                           /* CMPPI */
    {IN_DESC, IN_ARG, 0, 0},                            /* CVTPLI */
    {IN_DESC, IN_DESC, IN_DESC, 0},                     /* MULPI */
    {IN_DESC, IN_DESC, IN_DESC, 0},                     /* DIVPI */
    {IN_DESC, IN_DESC, IN_ARG, 0},                      /* ASHPI */
    {IN_DESC, IN_DESC, 0, 0}                            /* CVTLPI */
    };


/* ASCII to overpunch table: sign is <7>, digit is <4:0> */

static int32 overbin[128] = {
    0, 0, 0, 0, 0, 0, 0, 0,                             /* 000 - 037 */
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0x80, 0, 0, 0, 0, 0, 0,                          /* 040 - 077 */
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 1, 2, 3, 4, 5, 6, 7,
    8, 9, 0x80, 0, 0, 0, 0, 0,
    0, 1, 2, 3, 4, 5, 6, 7,                             /* 100 - 137 */
    8, 9, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86,
    0x87, 0x88, 0x89, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0x80, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,                             /* 140 - 177 */
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0, 0, 0,
    0, 0, 0, 0, 0, 0x80, 0, 0
    };

/* Overpunch to ASCII table: indexed by sign and digit */

static int32 binover[2][16] = {
    {'{', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'I',
     '0', '0', '0', '0', '0', '0'},
    {'}', 'J', 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R',
     '0', '0', '0', '0', '0', '0'}
    };

/* CIS emulator */

t_stat cis11 (int32 IR)
{
int32 c, i, j, t, op, rn, addr;
int32 match, limit, mvlnt, shift;
int32 spc, ldivd, ldivr;
int32 arg[6];                                           /* operands */
int32 old_PC;
uint32 nc, digit, result;
t_stat st;
static DSTR accum, src1, src2, dst;
static DSTR mptable[10];
static DSTR Dstr1 = { 0, {0x10, 0, 0, 0} };

old_PC = (PC - 2) & 0177777;                            /* original PC */
op = IR & 0177;                                         /* IR <6:0> */
for (i = j = 0; (i < MAXOPN) && opntab[op][i]; i++) {   /* parse operands */
    switch (opntab[op][i]) {                            /* case on op type */

        case R0_DESC:
            arg[j++] = R[0];
            arg[j++] = R[1];
            break;

        case R2_DESC:
            arg[j++] = R[2];
            arg[j++] = R[3];
            break;

        case R4_DESC:
            arg[j++] = R[4];
            arg[j++] = R[5];
            break;

        case R4_ARG:
            arg[j++] = R[4];
            break;

        case IN_DESC:
            addr = ReadW (PC | isenable);
            PC = (PC + 2) & 0177777;
            arg[j++] = ReadW (addr | dsenable);
            arg[j++] = ReadW (((addr + 2) & 0177777) | dsenable);
            break;

        case IN_ARG:
            arg[j++] = ReadW (PC | isenable);
            PC = (PC + 2) & 0177777;
            break;

        default:
            return SCPE_IERR;
            }                                           /* end case */
    }                                                   /* end for */
switch (op) {                                           /* case on opcode */

/* MOVC, MOVTC, MOVCI, MOVTCI

   Operands (MOVC, MOVTC):
        R0, R1          =       source string descriptor
        R2, R3          =       dest string descriptor
        R4<7:0>         =       fill character
        R5              =       translation table address (MOVTC only)
   Operands (MOVCI, MOVTCI):
        A1LNT, A1ADR    =       source string descriptor
        A2LNT, A2ADR    =       dest string descriptor
        A3LNT<7:0>      =       fill character
        A3ADR           =       translation table address (MOVTCI only)

   Condition codes:
        NZVC            =       set from src.lnt - dst.lnt

   Registers (MOVC, MOVTC only)
        R0              =       max (0, src.len - dst.len)
        R1:R3           =       0
        R4:R5           =       unchanged

        Notes:   
        - If either the source or destination lengths are zero,
          the move loops exit immediately.
        - If the source length does not exceed the destination
          length, the fill loop exits immediately.
*/

    case 030: case 032: case 0130: case 0132:
        if (!fpd) {                                     /* first time? */
            mvlnt = movx_setup (op, arg);               /* set up reg */
            if (R[1] < R[3]) {                          /* move backwards? */
                R[1] = (R[1] + mvlnt) & 0177777;        /* bias addresses */
                R[3] = (R[3] + mvlnt) & 0177777;
                }
            }

/* At this point,

   R0-R5    =   arguments
   M[SP]    =   move length */

        if (R[0] && R[2]) {                             /* move to do? */
            if (R[1] < R[3]) {                          /* backwards? */
                for (i = 0; R[0] && R[2]; ) {           /* move loop */
                    t = ReadB (((R[1] - 1) & 0177777) | dsenable);
                    if (op & 2)
                        t = ReadB (((R[5] + t) & 0177777) | dsenable);
                    WriteB (t, ((R[3] - 1) & 0177777) | dsenable);
                    R[0]--;
                    R[1] = (R[1] - 1) & 0177777;
                    R[2]--;
                    R[3] = (R[3] - 1) & 0177777;
                    if ((++i >= INT_TEST) && R[0] && R[2]) {
                        if (cis_int_test (i, old_PC, &st))
                            return st;
                        i = 0;
                        }
                    }                                   /* end for lnts */
                mvlnt = ReadW (SP | dsenable);          /* recover mvlnt */
                R[3] = (R[3] + mvlnt) & 0177777;        /* end of dst str */
                }                                       /* end if bkwd */
            else {                                      /* forward */
                for (i = 0; R[0] && R[2]; ) {           /* move loop */
                    t = ReadB ((R[1] & 0177777) | dsenable);
                    if (op & 2)
                        t = ReadB (((R[5] + t) & 0177777) | dsenable);
                    WriteB (t, (R[3] & 0177777) | dsenable);
                    R[0]--;
                    R[1] = (R[1] + 1) & 0177777;
                    R[2]--;
                    R[3] = (R[3] + 1) & 0177777;
                    if ((++i >= INT_TEST) && R[0] && R[2]) {
                        if (cis_int_test (i, old_PC, &st))
                            return st;
                        i = 0;
                        }
                    }                                   /* end for lnts */
                }                                       /* end else fwd */
            }                                           /* end if move */
        for (i = 0; i < R[2]; i++) {
            WriteB (R[4], ((R[3] + i) & 0177777) | dsenable);
            }
        movx_cleanup (op);                              /* cleanup */
        return SCPE_OK;

/* MOVRC, MOVRCI

   Operands (MOVRC):
        R0, R1          =       source string descriptor
        R2, R3          =       dest string descriptor
        R4<7:0>         =       fill character
   Operands (MOVRCI):
        A1LNT, A1ADR    =       source string descriptor
        A2LNT, A2ADR    =       dest string descriptor
        A3LNT<7:0>      =       fill character

   Condition codes:
        NZVC            =       set from src.lnt - dst.lnt

   Registers (MOVRC only)
        R0              =       max (0, src.len - dst.len)
        R1:R3           =       0
        R4:R5           =       unchanged

   Notes: see MOVC, MOVCI
*/

    case 031: case 0131:
        if (!fpd) {                                     /* first time? */
            mvlnt = movx_setup (op, arg);               /* set up reg */
            R[1] = (R[1] + R[0] - mvlnt) & 0177777;     /* eff move start */
            R[3] = (R[3] + R[2] - mvlnt) & 0177777;
            if (R[1] < R[3]) {                          /* move backwards? */
                R[1] = (R[1] + mvlnt) & 0177777;        /* bias addresses */
                R[3] = (R[3] + mvlnt) & 0177777;
                }
            }

/* At this point,

   R0-R5    =   arguments
   M[SP]    =   move length */

        if (R[0] && R[2]) {                             /* move to do? */
            if (R[1] < R[3]) {                          /* backwards? */
                for (i = 0; R[0] && R[2]; ) {           /* move loop */
                    t = ReadB (((R[1] - 1) & 0177777) | dsenable);
                    WriteB (t, ((R[3] - 1) & 0177777) | dsenable);
                    R[0]--;
                    R[1] = (R[1] - 1) & 0177777;
                    R[2]--;
                    R[3] = (R[3] - 1) & 0177777;
                    if ((++i >= INT_TEST) && R[0] && R[2]) {
                        if (cis_int_test (i, old_PC, &st))
                            return st;
                        i = 0;
                        }
                    }                                   /* end for lnts */
                }                                       /* end if bkwd */
            else {                                      /* forward */
                for (i = 0; R[0] && R[2]; ) {           /* move loop */
                    t = ReadB ((R[1] & 0177777) | dsenable);
                    WriteB (t, (R[3] & 0177777) | dsenable);
                    R[0]--;
                    R[1] = (R[1] + 1) & 0177777;
                    R[2]--;
                    R[3] = (R[3] + 1) & 0177777;
                    if ((++i >= INT_TEST) && R[0] && R[2]) {
                        if (cis_int_test (i, old_PC, &st))
                            return st;
                        i = 0;
                        }
                    }                                   /* end for lnts */
                mvlnt = ReadW (SP | dsenable);          /* recover mvlnt */
                R[3] = (R[3] - mvlnt) & 0177777;        /* start of dst str */
                }                                       /* end else fwd */
            }                                           /* end if move */
        for (i = 0; i < R[2]; i++) {
            WriteB (R[4], ((R[3] - R[2] + i) & 0177777) | dsenable);
            }
        movx_cleanup (op);                              /* cleanup */
        return SCPE_OK;

/* Load descriptors - no operands */

    case 020: case 021: case 022: case 023:
    case 024: case 025: case 026: case 027:
    case 060: case 061: case 062: case 063:
    case 064: case 065: case 066: case 067:
        limit = (op & 040)? 6: 4;
        rn = IR & 07;                                   /* get register */
        t = R[rn];
        spc = (rn == 7)? isenable: dsenable;
        for (j = 0; j < limit; j = j + 2) {             /* loop for 2,3 dscr */
            addr = ReadW (((t + j) & 0177777) | spc);
            R[j] = ReadW (addr | dsenable);
            R[j + 1] = ReadW (((addr + 2) & 0177777) | dsenable);
            }
        if (rn >= limit)
            R[rn] = (R[rn] + limit) & 0177777;
        return SCPE_OK;

/* LOCC, SKPC, LOCCI, SKPCI 

   Operands (LOCC, SKPC):
        R0, R1          =       source string descriptor
        R4<7:0>         =       match character
   Operands (LOCCI, SKPCI):
        A1LNT, A1ADR    =       source string descriptor
        A2LNT<7:0>      =       match character

   Condition codes:
        NZ              =       set from R0
        VC              =       0

   Registers:
        R0:R1           =       substring descriptor where operation terminated
*/

    case 0140: case 0141:                               /* inline */
        if (!fpd) {                                     /* FPD clear? */
            WriteW (R[4], ((SP - 2) & 0177777) | dsenable); 
            SP = (SP - 2) & 0177777;                    /* push R4 */
            R[0] = A1LNT;                               /* args to registers */
            R[1] = A1ADR;
            R[4] = A2LNT;
            }                                           /* fall through */
    case 040: case 041:                                 /* register */
        fpd = 1;                                        /* set FPD */
        R[4] = R[4] & 0377;                             /* match character */
        for (i = 0; R[0] != 0;) {                       /* loop */
            c = ReadB (R[1] | dsenable);                /* get char */
            if ((c == R[4]) ^ (op & 1))                 /* = + LOC, != + SKP? */
                break;
            R[0]--;                                     /* decr count, */
            R[1] = (R[1] + 1) & 0177777;                /* incr addr */
            if ((++i >= INT_TEST) && R[0]) {            /* test for intr? */
                if (cis_int_test (i, old_PC, &st))
                    return st;
                i = 0;
                }
            }
        N = GET_SIGN_W (R[0]);
        Z = GET_Z (R[0]);
        V = C = 0;
        fpd = 0;                                        /* instr done */
        if (op & INLINE) {                              /* inline? */
            R[4] = ReadW (SP | dsenable);               /* restore R4 */
            SP = (SP + 2) & 0177777;
            }
        return SCPE_OK;

/* SCANC, SPANC, SCANCI, SPANCI

   Operands (SCANC, SPANC):
        R0, R1          =       source string descriptor
        R4<7:0>         =       mask
        R5              =       table address
   Operands (SCANCI, SPANCI):
        A1LNT, A1ADR    =       source string descriptor
        A2LNT<7:0>      =       match character
        A2ADR           =       table address

   Condition codes:
        NZ              =       set from R0
        VC              =       0

   Registers:
        R0:R1           =       substring descriptor where operation terminated
*/

    case 0142: case 0143:                               /* inline */
        if (!fpd) {                                     /* FPD clear? */
            WriteW (R[4], ((SP - 4) & 0177777) | dsenable);
            WriteW (R[5], ((SP - 2) & 0177777) | dsenable);
            SP = (SP - 4) & 0177777;                    /* push R4, R5 */
            R[0] = A1LNT;                               /* args to registers */
            R[1] = A1ADR;
            R[4] = A2LNT;
            R[5] = A2ADR;
            }                                           /* fall through */
    case 042: case 043:                                 /* register */
        fpd = 1;                                        /* set FPD */
        R[4] = R[4] & 0377;                             /* match character */
        for (i = 0; R[0] != 0;) {                       /* loop */
            t = ReadB (R[1] | dsenable);                /* get char as index */
            c = ReadB (((R[5] + t) & 0177777) | dsenable);
            if (((c & R[4]) != 0) ^ (op & 1))           /* != + SCN, = + SPN? */
                break;
            R[0]--;                                     /* decr count, */
            R[1] = (R[1] + 1) & 0177777;                /* incr addr */
            if ((++i >= INT_TEST) && R[0]) {            /* test for intr? */
                if (cis_int_test (i, old_PC, &st))
                    return st;
                i = 0;
                }
            }
        N = GET_SIGN_W (R[0]);
        Z = GET_Z (R[0]);
        V = C = 0;
        fpd = 0;                                        /* instr done */
        if (op & INLINE) {                              /* inline? */
            R[4] = ReadW (SP | dsenable);               /* restore R4, R5 */
            R[5] = ReadW (((SP + 2) & 0177777) | dsenable);
            SP = (SP + 4) & 0177777;
            }
        return SCPE_OK;

/* CMPC, CMPCI

   Operands (CMPC):
        R0, R1          =       source1 string descriptor
        R2, R3          =       source2 string descriptor
        R4<7:0>         =       fill character
   Operands (CMPCI):
        A1LNT, A1ADR    =       source1 string descriptor
        A2LNT, A2ADR    =       source2 string descriptor
        A3LNT<7:0>      =       fill character

   Condition codes:
        NZVC            =       set from src1 - src2 at mismatch, or
                        =       0100 if equal

   Registers (CMPC only):
        R0:R1           =       unmatched source1 substring descriptor
        R2:R3           =       unmatched source2 substring descriptor
*/

   case 0144:                                           /* inline */
        if (!fpd) {                                     /* FPD clear? */
            WriteW (R[0], ((SP - 10) & 0177777) | dsenable);
            WriteW (R[1], ((SP - 8) & 0177777) | dsenable);
            WriteW (R[2], ((SP - 6) & 0177777) | dsenable);
            WriteW (R[3], ((SP - 4) & 0177777) | dsenable);
            WriteW (R[4], ((SP - 2) & 0177777) | dsenable);
            SP = (SP - 10) & 0177777;                   /* push R0 - R4 */
            R[0] = A1LNT;                               /* args to registers */
            R[1] = A1ADR;
            R[2] = A2LNT;
            R[3] = A2ADR;
            R[4] = A3LNT;
            }                                           /* fall through */
   case 044:                                            /* register */
        fpd = 1;                                        /* set FPD */
        R[4] = R[4] & 0377;                             /* mask fill */
        c = t = 0;
        for (i = 0; (R[0] || R[2]); ) {                 /* until cnts == 0 */
            if (R[0])                                   /* get src1 or fill */
                c = ReadB (R[1] | dsenable);
            else c = R[4];
            if (R[2])                                   /* get src2 or fill */
                t = ReadB (R[3] | dsenable);
            else t = R[4];
            if (c != t)                                 /* if diff, done */
                break;
            if (R[0]) {                                 /* if more src1 */
                R[0]--;                                 /* decr count, */
                R[1] = (R[1] + 1) & 0177777;            /* incr addr */
                }
            if (R[2]) {                                 /* if more src2 */
                R[2]--;                                 /* decr count, */
                R[3] = (R[3] + 1) & 0177777;            /* incr addr */
                }
            if ((++i >= INT_TEST) && (R[0] || R[2])) {  /* test for intr? */
                if (cis_int_test (i, old_PC, &st))
                    return st;
                i = 0;
                }
            }
        j = c - t;                                      /* last chars read */
        N = GET_SIGN_B (j);                             /* set cc's */
        Z = GET_Z (j);
        V = GET_SIGN_B ((c ^ t) & (~t ^ j));
        C = (c < t);
        fpd = 0;                                        /* instr done */
        if (op & INLINE) {                              /* inline? */
            R[0] = ReadW (SP | dsenable);               /* restore R0 - R4 */
            R[1] = ReadW (((SP + 2) & 0177777) | dsenable);
            R[2] = ReadW (((SP + 4) & 0177777) | dsenable);
            R[3] = ReadW (((SP + 6) & 0177777) | dsenable);
            R[4] = ReadW (((SP + 8) & 0177777) | dsenable);
            SP = (SP + 10) & 0177777;
            }
        return SCPE_OK;

/* MATC, MATCI

   Operands (MATC):
        R0, R1          =       source string descriptor
        R2, R3          =       substring descriptor
   Operands (MATCI):
        A1LNT, A1ADR    =       source string descriptor
        A2LNT, A2ADR    =       substring string descriptor

   Condition codes:
        NZ              =       set from R0
        VC              =       0

   Registers:
        R0:R1           =       source substring descriptor for match

   Notes:
        - If the string is zero length, and the substring is not,
          the outer loop exits immediately, and the result is
          "no match"
        - If the substring is zero length, the inner loop always
          exits immediately, and the result is a "match"
        - If the string is zero length, and the substring is as
          well, the outer loop executes, the inner loop exits
          immediately, and the result is a match, but the result
          is the length of the string (zero), or "no match"
*/

    case 0145:                                          /* inline */
        if (!fpd) {                                     /* FPD clear? */
            WriteW (R[2], ((SP - 4) & 0177777) | dsenable);
            WriteW (R[3], ((SP - 2) & 0177777) | dsenable);
            SP = (SP - 4) & 0177777;                    /* push R2, R3 */
            R[0] = A1LNT;                               /* args to registers */
            R[1] = A1ADR;
            R[2] = A2LNT;
            R[3] = A2ADR;
            }                                           /* fall through */
    case 0045:                                          /* register */
        fpd = 1;
        for (match = 0; R[0] >= R[2]; ) {               /* loop thru string */
            for (i = 0, match = 1; match && (i < R[2]); i++) {
                c = ReadB (((R[1] + i) & 0177777) | dsenable);
                t = ReadB (((R[3] + i) & 0177777) | dsenable);
                match = (c == t);                       /* end for substring */
                }
            if (match)                                  /* exit if match */
                break;
            R[0]--;                                     /* on to next char */
            R[1] = (R[1] + 1) & 0177777;
            if (cis_int_test (i, old_PC, &st))
                return st;
            }
        if (!match) {                                   /* if no match */
            R[1] = (R[1] + R[0]) & 0177777;
            R[0] = 0;
            }
        N = GET_SIGN_W (R[0]);
        Z = GET_Z (R[0]);
        V = C = 0;
        fpd = 0;                                        /* instr done */
        if (op & INLINE) {                              /* inline? */
            R[2] = ReadW (SP | dsenable);               /* restore R2, R3 */
            R[3] = ReadW (((SP + 2) & 0177777) | dsenable);
            SP = (SP + 4) & 0177777;
            }
        return SCPE_OK;

/* ADDN, SUBN, ADDP, SUBP, ADDNI, SUBNI, ADDPI, SUBPI

   Operands (ADDN, SUBN, ADDP, SUBP):
        R0, R1          =       source1 string descriptor
        R2, R3          =       source2 string descriptor
        R4, R5          =       destination string descriptor
   Operands (ADDNI, SUBNI, ADDPI, SUBPI):
        A1LNT, A1ADR    =       source1 string descriptor
        A2LNT, A2ADR    =       source2 string descriptor
        A3LNT, A3ADR    =       destination string descriptor

   Condition codes:
        NZV             =       set from result
        C               =       0

   Registers (ADDN, ADDP, SUBN, SUBP only):
        R0:R3           =       0       
*/

    case 050: case 051: case 070: case 071:
    case 0150: case 0151: case 0170: case 0171:
        ReadDstr (A1, &src1, op);                       /* get source1 */
        ReadDstr (A2, &src2, op);                       /* get source2 */
        if (op & 1)                                     /* sub? invert sign */
            src1.sign = src1.sign ^ 1;
        if (src1.sign ^ src2.sign) {                    /* opp signs?  sub */
            if (CmpDstr (&src1, &src2) < 0) {           /* src1 < src2? */
                SubDstr (&src1, &src2, &dst);           /* src2 - src1 */
                dst.sign = src2.sign;                   /* sign = src2 */
                }
            else {
                SubDstr (&src2, &src1, &dst);           /* src1 - src2 */
                dst.sign = src1.sign;                   /* sign = src1 */
                }
            V = 0;                                      /* can't carry */
            }
        else {                                          /* addition */
            V = AddDstr (&src1, &src2, &dst, 0);        /* add magnitudes */
            dst.sign = src1.sign;                       /* set result sign */
            }
        C = 0;
        WriteDstr (A3, &dst, op);                       /* store result */
        if ((op & INLINE) == 0)                         /* if reg, clr reg */
            R[0] = R[1] = R[2] = R[3] = 0;
        return SCPE_OK;

/* MULP, MULPI

   Operands (MULP):
        R0, R1          =       source1 string descriptor
        R2, R3          =       source2 string descriptor
        R4, R5          =       destination string descriptor
   Operands (MULPI):
        A1LNT, A1ADR    =       source1 string descriptor
        A2LNT, A2ADR    =       source2 string descriptor
        A3LNT, A3ADR    =       destination string descriptor

   Condition codes:
        NZV             =       set from result
        C               =       0

   Registers (MULP only):
        R0:R3           =       0       
*/

    case 074: case 0174:
        dst = Dstr0;                                    /* clear result */
        if (ReadDstr (A1, &src1, op) && ReadDstr (A2, &src2, op)) {
            dst.sign = src1.sign ^ src2.sign;           /* sign of result */
            accum = Dstr0;                              /* clear accum */
            NibbleRshift (&src1, 1, 0);                 /* shift out sign */
            CreateTable (&src1, mptable);               /* create *1, *2, ... */
            for (i = 1; i < (DSTRLNT * 8); i++) {       /* 31 iterations */
                digit = (src2.val[i / 8] >> ((i % 8) * 4)) & 0xF;
                if (digit > 0)                          /* add in digit*mpcnd */
                    AddDstr (&mptable[digit], &accum, &accum, 0);
                nc = NibbleRshift (&accum, 1, 0);       /* ac right 4 */
                NibbleRshift (&dst, 1, nc);             /* result right 4 */
                }
            V = TestDstr (&accum) != 0;                 /* if ovflo, set V */
            }
        else V = 0;                                     /* result = 0 */
        C = 0;                                          /* C = 0 */
        WriteDstr (A3, &dst, op);                       /* store result */
        if ((op & INLINE) == 0)                         /* if reg, clr reg */
                R[0] = R[1] = R[2] = R[3] = 0;
        return SCPE_OK;

/* DIVP, DIVPI

   Operands (DIVP):
        R0, R1          =       divisor string descriptor
        R2, R3          =       dividend string descriptor
        R4, R5          =       destination string descriptor
   Operands (DIVPI):
        A1LNT, A1ADR    =       divisor string descriptor
        A2LNT, A2ADR    =       dividend string descriptor
        A3LNT, A3ADR    =       destination string descriptor

   Condition codes:
        NZV             =       set from result
        C               =       set if divide by zero

   Registers (DIVP only):
        R0:R3           =       0       
*/

    case 075: case 0175:
        ldivr = ReadDstr (A1, &src1, op);               /* get divisor */
        if (ldivr == 0) {                               /* divisor = 0? */
            V = C = 1;                                  /* set cc's */
            return SCPE_OK;
            }
        ldivr = LntDstr (&src1, ldivr);                 /* get exact length */
        ldivd = ReadDstr (A2, &src2, op);               /* get dividend */
        ldivd = LntDstr (&src2, ldivd);                 /* get exact length */
        dst = Dstr0;                                    /* clear dest */
        NibbleRshift (&src1, 1, 0);                     /* right justify ops */
        NibbleRshift (&src2, 1, 0);
        if ((t = ldivd - ldivr) >= 0) {                 /* any divide to do? */
            WordLshift (&src1, t / 8);                  /* align divr to divd */
            NibbleLshift (&src1, t % 8);
            CreateTable (&src1, mptable);               /* create *1, *2, ... */
            for (i = 0; i <= t; i++) {                  /* divide loop */
                for (digit = 9; digit > 0; digit--) {   /* find digit */
                    if (CmpDstr (&src2, &mptable[digit]) >= 0) {
                        SubDstr (&mptable[digit], &src2, &src2);
                        dst.val[0] = dst.val[0] | digit;
                        break;
                        }                               /* end if */
                    }                                   /* end for */
                NibbleLshift (&src2, 1);                /* shift dividend */
                NibbleLshift (&dst, 1);                 /* shift quotient */
                }                                       /* end divide loop */
            dst.sign = src1.sign ^ src2.sign;           /* calculate sign */
            }                                           /* end if */
        V = C = 0;
        WriteDstr (A3, &dst, op);                       /* store result */
        if ((op & INLINE) == 0)                         /* if reg, clr reg */
            R[0] = R[1] = R[2] = R[3] = 0;
        return SCPE_OK;

/* CMPN, CMPP, CMPNI, CMPPI

   Operands (CMPN, CMPP):
        R0, R1          =       source1 string descriptor
        R2, R3          =       source2 string descriptor
   Operands (CMPNI, CMPPI):
        A1LNT, A1ADR    =       source1 string descriptor
        A2LNT, A2ADR    =       source2 string descriptor

   Condition codes:
        NZ              =       set from comparison
        VC              =       0

   Registers (CMPN, CMPP only):
        R0:R3           =       0
*/

    case 052: case 072: case 0152: case 0172:
        ReadDstr (A1, &src1, op);                       /* get source1 */
        ReadDstr (A2, &src2, op);                       /* get source2 */
        N = Z = V = C = 0;
        if (src1.sign != src2.sign) N = src1.sign;
        else {
            t = CmpDstr (&src1, &src2);                 /* compare strings */
            if (t < 0)
                N = (src1.sign? 0: 1);
            else if (t > 0)
                N = (src1.sign? 1: 0);
            else Z = 1;
            }
        if ((op & INLINE) == 0)                         /* if reg, clr reg */
            R[0] = R[1] = R[2] = R[3] = 0;
        return SCPE_OK;

/* ASHN, ASHP, ASHNI, ASHPI

   Operands (ASHN, ASHP):
        R0, R1          =       source string descriptor
        R2, R3          =       destination string descriptor
        R4<11:8>        =       rounding digit
        R4<7:0>         =       shift count
   Operands (ASHNI, ASHPI):
        A1LNT, A1ADR    =       source string descriptor
        A2LNT, A2ADR    =       destination string descriptor
        A3LNT<11:8>     =       rounding digit
        A3LNT<7:0>      =       shift count

   Condition codes:
        NZV             =       set from result
        C               =       0

   Registers (ASHN, ASHP only):
        R0:R1, R4       =       0 
*/

    case 056: case 076: case 0156: case 0176:
        ReadDstr (A1, &src1, op);                       /* get source */
        V = C = 0;                                      /* init cc's */
        shift = GET_ASHLNT (A3LNT);                     /* get shift count */
        if (shift & ASHSGN) {                           /* right shift? */
            shift = (ASHLNT_M + 1 - shift);             /* !shift! */
            WordRshift (&src1, shift / 8);              /* do word shifts */    
            NibbleRshift (&src1, shift % 8, 0);         /* do nibble shifts */
            t = GET_ASHRND (A3LNT);                     /* get rounding digit */
            if ((t + (src1.val[0] & 0xF)) > 9)          /* rounding needed? */
                AddDstr (&src1, &Dstr1, &src1, 0);      /* round */
            src1.val[0] = src1.val[0] & ~0xF;           /* clear sign */
            }                                           /* end right shift */
        else if (shift) {                               /* left shift? */
            if (WordLshift (&src1, shift / 8))          /* do word shifts */
                V = 1;
            if (NibbleLshift (&src1, shift % 8))        /* do nibble shifts */
                V = 1;
            }                                           /* end left shift */
        WriteDstr (A2, &src1, op);                      /* store result */
        if ((op & INLINE) == 0)                         /* if reg, clr reg */
            R[0] = R[1] = R[4] = 0;
        return SCPE_OK;

/* CVTPN, CVTPNI

   Operands (CVTPN):
        R0, R1          =       source string descriptor
        R2, R3          =       destination string descriptor
   Operands (CVTPNI):
        A1LNT, A1ADR    =       source string descriptor
        A2LNT, A2ADR    =       destination string descriptor

   Condition codes:
        NZV             =       set from result
        C               =       0

   Registers (CVTPN only):
        R0:R1           =       0
*/

    case 054: case 0154:
        ReadDstr (A1, &src1, PACKED);                   /* get source */
        V = C = 0;                                      /* init cc's */
        WriteDstr (A2, &src1, NUMERIC);                 /* write dest */
        if ((op & INLINE) == 0)                         /* if reg, clr reg */
            R[0] = R[1] = 0;
        return SCPE_OK;

/* CVTNP, CVTNPI

   Operands (CVTNP):
        R0, R1          =       source string descriptor
        R2, R3          =       destination string descriptor
   Operands (CVTNPI):
        A1LNT, A1ADR    =       source string descriptor
        A2LNT, A2ADR    =       destination string descriptor

   Condition codes:
        NZV             =       set from result
        C               =       0

   Registers (CVTNP only):
        R0:R1           =       0
*/

    case 055: case 0155:
        ReadDstr (A1, &src1, NUMERIC);                  /* get source */
        V = C = 0;                                      /* init cc's */
        WriteDstr (A2, &src1, PACKED);                  /* write dest */
        if ((op & INLINE) == 0)                         /* if reg, clr reg */
            R[0] = R[1] = 0;
        return SCPE_OK;

/* CVTNL, CVTPL, CVTNLI, CVTPLI

   Operands (CVTNL, CVTPL):
        R0, R1          =       source string descriptor
   Operands (CVTNLI, CVTPLI):
        A1LNT, A1ADR    =       source string descriptor
        A2LNT           =       destination address

   Condition codes:
        NZV             =       set from result
        C               =       source < 0 and result != 0

   Registers (CVTNL, CVTPL only):
        R0:R1           =       0
        R2:R3           =       result
*/

    case 053: case 073: case 0153: case 0173:
        ReadDstr (A1, &src1, op);                       /* get source */
        V = result = 0;                                 /* clear V, result */
        for (i = (DSTRLNT * 8) - 1; i > 0; i--) {       /* loop thru digits */
            digit = (src1.val[i / 8] >> ((i % 8) * 4)) & 0xF;
            if (digit || result || V) {                 /* skip initial 0's */
                if (result >= MAXDVAL)
                    V = 1;
                result = (result * 10) + digit;
                if (result < digit)
                    V = 1;
                }                                       /* end if */
            }                                           /* end for */
        if (src1.sign)
            result = (~result + 1) & 0xFFFFFFFF;
        N = GET_SIGN_L (result);
        Z = GET_Z (result);
        V = V | (N ^ src1.sign);                        /* overflow if +2**31 */
        C = src1.sign && (Z == 0);                      /* set C based on std */
        if (op & INLINE) {                              /* inline? */
            WriteW (result & 0177777, A2LNT | dsenable);
            WriteW ((result >> 16) & 0177777,
                ((A2LNT + 2) & 0177777) | dsenable);
            }
        else {
            R[0] = R[1] = 0;
            R[2] = (result >> 16) & 0177777;
            R[3] = result & 0177777;
            }
        return SCPE_OK;

/* CVTLN, CVTLP, CVTLNI, CVTLPI

   Operands (CVTLN, CVTLP):
        R0, R1          =       destination string descriptor
        R2:R3           =       source long - EIS format
   Operands (CVTLNI, CVTLPI):
        A1LNT, A1ADR    =       destination string descriptor
        A2LNT, A2ADR    =       source long - VAX format

   Condition codes:
        NZV             =       set from result
        C               =       0

   Registers (CVTLN, CVTLP only)
        R2:R3           =       0       
*/

    case 057: case 077:
        result = (R[2] << 16) | R[3];                   /* op in EIS format */
        R[2] = R[3] = 0;                                /* clear registers */
        goto CVTLx;                                     /* join common code */
    case 0157: case 0177:
        result = (A2ADR << 16) | A2LNT;                 /* op in VAX format */
    CVTLx:
        dst = Dstr0;                                    /* clear result */
        if ((dst.sign = GET_SIGN_L (result)))
            result = (~result + 1) & 0xFFFFFFFF;
        for (i = 1; (i < (DSTRLNT * 8)) && result; i++) {
            digit = result % 10;
            result = result / 10;
            dst.val[i / 8] = dst.val[i / 8] | (digit << ((i % 8) * 4));
            }
        V = C = 0;
        WriteDstr (A1, &dst, op);                       /* write result */
        return SCPE_OK;

    default:
        setTRAP (TRAP_ILL);
        break;
        }                                               /* end case */
return SCPE_OK;
}                                                       /* end cis */

/* Get decimal string

   Arguments:
        dscr    =       decimal string descriptor
        src     =       decimal string structure
        flag    =       numeric/packed flag

   The routine returns the length in int32's of the non-zero part of
   the string.

   This routine plays fast and loose with operand checking, as did the
   original 11/23 microcode (half of which I wrote).  In particular,

   - If the flag specifies packed, the type is not checked at all.
     The sign of an unsigned string is assumed to be 0xF (an
     alternative for +).
   - If the flag specifies numeric, packed types will be treated
     as unsigned zoned.
   - For separate, only the '-' sign is checked, not the '+'.

   However, to simplify the code elsewhere, digits are range checked,
   and bad digits are replaced with 0's.
*/

int32 ReadDstr (int32 *dscr, DSTR *src, int32 flag)
{
int32 c, i, end, lnt, type, t = 0;

*src = Dstr0;                                           /* clear result */
type = GET_DTYP (dscr[0]);                              /* get type */
lnt = GET_DLNT (dscr[0]);                               /* get string length */
if (flag & PACKED) {                                    /* packed? */
    end = lnt / 2;                                      /* last byte */
    for (i = 0; i <= end; i++) {                        /* loop thru string */
        c = ReadB (((dscr[1] + end - i) & 0177777) | dsenable);
        if (i == 0)                                     /* save sign */
            t = c & 0xF;
        if ((i == end) && ((lnt & 1) == 0))
            c = c & 0xF;
        if (c >= 0xA0)                                  /* check hi digit */
            c = c & 0xF;
        if ((c & 0xF) >= 0xA)                           /* check lo digit */   
            c = c & 0xF0; 
        src->val[i / 4] = src->val[i / 4] | (c << ((i % 4) * 8));
        }                                               /* end for */
    if ((t == 0xB) || (t == 0xD))                       /* if -, set sign */
        src->sign = 1;
    src->val[0] = src->val[0] & ~0xF;                   /* clear sign */
    }                                                   /* end packed */
else {                                                  /* numeric */
    if (type >= TS) src->sign = (ReadB ((((type == TS)?
         dscr[1] + lnt: dscr[1] - 1) & 0177777) | dsenable) == '-');
    for (i = 1; i <= lnt; i++) {                        /* loop thru string */
        c = ReadB (((dscr[1] + lnt - i) & 0177777) | dsenable);
        if ((i == 1) && (type == XZ) && ((c & 0xF0) == 0x70))
            src->sign = 1;                              /* signed zoned */
        else if (((i == 1) && (type == TO)) ||
            ((i == lnt) && (type == LO))) {
            c = overbin[c & 0177];                      /* get sign and digit */
            src->sign = c >> 7;                         /* set sign */
            }
        c = c & 0xF;                                    /* get digit */
        if (c > 9)                                      /* range check */
            c = 0;
        src->val[i / 8] = src->val[i / 8] | (c << ((i % 8) * 4));
        }                                               /* end for */
    }                                                   /* end numeric */
return TestDstr (src);                                  /* clean -0 */
}
       
/* Store decimal string

   Arguments:
        dsrc    =       decimal string descriptor
        src     =       decimal string structure
        flag    =       numeric/packed flag

   PSW.NZ are also set to their proper values
   PSW.V will be set on overflow; it must be initialized elsewhere
   (to allow for external overflow calculations)

   The rules for the stored sign and the PSW sign are:

   - Stored sign is negative if input is negative, string type
     is signed, and the result is non-zero or there was overflow
   - PSW sign is negative if input is negative, string type is
     signed, and the result is non-zero

   Thus, the stored sign and the PSW sign will differ in one case:
   a negative zero generated by overflow is stored with a negative
   sign, but PSW.N is clear
*/

void WriteDstr (int32 *dscr, DSTR *dst, int32 flag)
{
int32 c, i, limit, end, type, lnt;
uint32 mask;
static uint32 masktab[8] = {
    0xFFFFFFF0, 0xFFFFFF00, 0xFFFFF000, 0xFFFF0000,
    0xFFF00000, 0xFF000000, 0xF0000000, 0x00000000
    };
static int32 unsignedtab[8] = { 0, 1, 0, 0, 0, 0, 0, 1 };

type = GET_DTYP (dscr[0]);                              /* get type */
lnt = GET_DLNT (dscr[0]);                               /* get string length */
mask = 0;                                               /* can't ovflo */
Z = 1;                                                  /* assume all 0's */
limit = lnt / 8;                                        /* limit for test */
for (i = 0; i < DSTRLNT; i++) {                         /* loop thru value */
    if (i == limit)                                     /* at limit, get mask */
        mask = masktab[lnt % 8];
    else if (i > limit)                                 /* beyond, all ovflo */
        mask = 0xFFFFFFFF;
    if (dst->val[i] & mask)                             /* test for ovflo */
        V = 1;
    if ((dst->val[i] = dst->val[i] & ~mask))            /* test nz */
        Z = 0;
    }
dst->sign = dst->sign & ~unsignedtab[type] & ~(Z & ~V);
N = dst->sign & ~Z;                                     /* N = sign, if ~zero */

if (flag & PACKED) {                                    /* packed? */
    end = lnt / 2;                                      /* end of string */
    if (type == UP)
        dst->val[0] = dst->val[0] | 0xF;
    else dst->val[0] = dst->val[0] | 0xC | dst->sign;
    for (i = 0; i <= end; i++) {                        /* store string */
        c = (dst->val[i / 4] >> ((i % 4) * 8)) & 0xFF;
        WriteB (c, ((dscr[1] + end - i) & 0177777) | dsenable);
        }                                               /* end for */
    }                                                   /* end packed */
else {
    if (type >= TS) WriteB (dst->sign? '-': '+', (((type == TS)?
         dscr[1] + lnt: dscr[1] - 1) & 0177777) | dsenable);
    for (i = 1; i <= lnt; i++) {                        /* store string */
        c = (dst->val[i / 8] >> ((i % 8) * 4)) & 0xF;   /* get digit */
        if ((i == 1) && (type == XZ) && dst->sign)
            c = c | 0x70;                               /* signed zoned */
        else if (((i == 1) && (type == TO)) ||
            ((i == lnt) && (type == LO)))
            c = binover[dst->sign][c];                  /* get sign and digit */
        else c = c | 0x30;                              /* default */
        WriteB (c, ((dscr[1] + lnt - i) & 0177777) |dsenable );    
        }                                               /* end for */
    }                                                   /* end numeric */
return;
}

/* Add decimal string magnitudes

   Arguments:
        s1      =       source1 decimal string
        s2      =       source2 decimal string
        ds      =       destination decimal string
        cy      =       carry in
   Output       =       1 if carry, 0 if no carry

   This algorithm courtesy Anton Chernoff, circa 1992 or even earlier.

   We trace the history of a pair of adjacent digits to see how the
   carry is fixed; each parenthesized item is a 4b digit.

   Assume we are adding:

        (a)(b)  I
   +    (x)(y)  J

   First compute I^J:

        (a^x)(b^y)      TMP

   Note that the low bit of each digit is the same as the low bit of
   the sum of the digits, ignoring the carry, since the low bit of the
   sum is the xor of the bits.

   Now compute I+J+66 to get decimal addition with carry forced left
   one digit:

        (a+x+6+carry mod 16)(b+y+6 mod 16)      SUM

   Note that if there was a carry from b+y+6, then the low bit of the
   left digit is different from the expected low bit from the xor.
   If we xor this SUM into TMP, then the low bit of each digit is 1
   if there was a carry, and 0 if not.  We need to subtract 6 from each
   digit that did not have a carry, so take ~(SUM ^ TMP) & 0x11, shift
   it right 4 to the digits that are affected, and subtract 6*adjustment
   (actually, shift it right 3 and subtract 3*adjustment).
*/

int32 AddDstr (DSTR *s1, DSTR *s2, DSTR *ds, int32 cy)
{
int32 i;
uint32 sm1, sm2, tm1, tm2, tm3, tm4;

for (i = 0; i < DSTRLNT; i++) {                         /* loop low to high */
    tm1 = s1->val[i] ^ (s2->val[i] + cy);               /* xor operands */
    sm1 = s1->val[i] + (s2->val[i] + cy);               /* sum operands */
    sm2 = sm1 + 0x66666666;                             /* force carry out */
    cy = ((sm1 < s1->val[i]) || (sm2 < sm1));           /* check for overflow */
    tm2 = tm1 ^ sm2;                                    /* get carry flags */
    tm3 = (tm2 >> 3) | (cy << 29);                      /* compute adjustment */
    tm4 = 0x22222222 & ~tm3;                            /* clear where carry */
    ds->val[i] = sm2 - (3 * tm4);                       /* final result */
    }
return cy;
}

/* Subtract decimal string magnitudes

   Arguments:
        s1      =       source1 decimal string
        s2      =       source2 decimal string
        ds      =       destination decimal string
   Outputs:             s2 - s1 in ds

   Note: the routine assumes that s1 <= s2

*/

void SubDstr (DSTR *s1, DSTR *s2, DSTR *ds)
{
int32 i;
DSTR complX;

for (i = 0; i < DSTRLNT; i++)
    complX.val[i] = 0x99999999 - s1->val[i];
AddDstr (&complX, s2, ds, 1);                           /* s1 + ~s2 + 1 */
return;
}

/* Compare decimal string magnitudes

   Arguments:
        s1      =       source1 decimal string
        s2      =       source2 decimal string
   Output       =       1 if >, 0 if =, -1 if <
*/

int32 CmpDstr (DSTR *s1, DSTR *s2)
{
int32 i;

for (i = DSTRMAX; i >=0; i--) {
    if (s1->val[i] > s2->val[i])
        return 1;
    if (s1->val[i] < s2->val[i])
        return -1;
    }
return 0;
}

/* Test decimal string for zero

   Arguments:
        dsrc    =       decimal string structure

   Returns the non-zero length of the string, in int32 units
   If the string is zero, the sign is cleared
*/

int32 TestDstr (DSTR *dsrc)
{
int32 i;

for (i = DSTRMAX; i >= 0; i--) {
    if (dsrc->val[i])
        return (i + 1);
    }
dsrc->sign = 0;
return 0;
}

/* Get exact length of decimal string

   Arguments:
        dsrc    =       decimal string structure
        nz      =       result from TestDstr
*/

int32 LntDstr (DSTR *dsrc, int32 nz)
{
int32 i;

if (nz == 0)
    return 0;
for (i = 7; i >= 0; i--) {
    if ((dsrc->val[nz - 1] >> (i * 4)) & 0xF)
        break;
    }
return ((nz - 1) * 8) + i;
}

/* Create table of multiples

   Arguments:
        dsrc    =       base decimal string structure
        mtable[10] =    array of decimal string structures

   Note that dsrc has a high order zero nibble; this
   guarantees that the largest multiple won't overflow.
   Also note that mtable[0] is not filled in.
*/

void CreateTable (DSTR *dsrc, DSTR mtable[10])
{
int32 i;

mtable[1] = *dsrc;
for (i = 2; i < 10; i++)
    AddDstr (&mtable[1], &mtable[i-1], &mtable[i], 0);
return;
}

/* Word shift right

   Arguments:
        dsrc    =       decimal string structure
        sc      =       shift count
*/

void WordRshift (DSTR *dsrc, int32 sc)
{
int32 i;

if (sc) {
    for (i = 0; i < DSTRLNT; i++) {
        if ((i + sc) < DSTRLNT)
            dsrc->val[i] = dsrc->val[i + sc];
        else dsrc->val[i] = 0;
        }
    }
return;
}

/* Word shift left

   Arguments:
        dsrc    =       decimal string structure
        sc      =       shift count
*/

int32 WordLshift (DSTR *dsrc, int32 sc)
{
int32 i, c;

c = 0;
if (sc) {
    for (i = DSTRMAX; i >= 0; i--) {
        if (i >= sc)
            dsrc->val[i] = dsrc->val[i - sc];
        else {
            c |= dsrc->val[i];
            dsrc->val[i] = 0;
            }
        }
    }
return c;
}               

/* Nibble shift decimal string right

   Arguments:
        dsrc    =       decimal string structure
        sc      =       shift count
        cin     =       carry in
*/

uint32 NibbleRshift (DSTR *dsrc, int32 sc, uint32 cin)
{
int32 i, s, nc;

if ((s = sc * 4)) {
    for (i = DSTRMAX; i >= 0; i--) {
        nc = (dsrc->val[i] << (32 - s)) & 0xFFFFFFFF;
        dsrc->val[i] = ((dsrc->val[i] >> s) |
            cin) & 0xFFFFFFFF;
        cin = nc;
        }
    return cin;
    }
return 0;
}

/* Nibble shift decimal string left

   Arguments:
        dsrc    =       decimal string structure
        sc      =       shift count
*/

uint32 NibbleLshift (DSTR *dsrc, int32 sc)
{
int32 i, s;
uint32 nc, cin;

cin = 0;
if ((s = sc * 4)) {
    for (i = 0; i < DSTRLNT; i++) {
        nc = dsrc->val[i] >> (32 - s);
        dsrc->val[i] = ((dsrc->val[i] << s) | cin) & 0xFFFFFFFF;
        cin = nc;
        }
    return cin;
    }
return 0;
}

/* Common setup routine for MOVC class instructions */

int32 movx_setup (int32 op, int32 *arg)
{
int32 mvlnt, t;

if (CPUT (CPUT_44)) {                                   /* 11/44? */
    ReadMB (((SP - 0200) & 0177777) | dsenable);        /* probe both blocks */
    ReadMB (((SP - 0100) & 0177777) | dsenable);        /* in 64W stack area */
    }
if (op & INLINE) {                                      /* inline */
    mvlnt = (A1LNT < A2LNT)? A1LNT: A2LNT;
    WriteW (mvlnt, ((SP - 14) & 0177777) | dsenable);   /* push move length */
    WriteW (R[0], ((SP - 12) & 0177777) | dsenable);    /* push R0 - R5 */
    WriteW (R[1], ((SP - 10) & 0177777) | dsenable);
    WriteW (R[2], ((SP - 8) & 0177777) | dsenable);
    WriteW (R[3], ((SP - 6) & 0177777) | dsenable);
    WriteW (R[4], ((SP - 4) & 0177777) | dsenable);
    WriteW (R[5], ((SP - 2) & 0177777) | dsenable);
    SP = (SP - 14) & 0177777;
    R[0] = A1LNT;                                       /* args to registers */
    R[1] = A1ADR;
    R[2] = A2LNT;
    R[3] = A2ADR;
    R[4] = A3LNT;
    R[5] = A3ADR & 0177777;
    }
else {                                                  /* register */
    mvlnt = (R[0] < R[2])? R[0]: R[2];
    WriteW (mvlnt, ((SP - 2) & 0177777) | dsenable);    /* push move length */
    SP = (SP - 2) & 0177777;
    }
fpd = 1;
t = R[0] - R[2];                                        /* src.lnt - dst.lnt */
N = GET_SIGN_W (t);                                     /* set cc's from diff */
Z = GET_Z (t);
V = GET_SIGN_W ((R[0] ^ R[2]) & (~R[2] ^ t));
C = (R[0] < R[2]);
return mvlnt;
}

/* Common cleanup routine for MOVC class instructions */

void movx_cleanup (int32 op)
{
SP = (SP + 2) & 0177777;                                /* discard mvlnt */
if (op & INLINE) {                                      /* inline? */
    R[0] = ReadW (SP | dsenable);                       /* restore R0 - R5 */
    R[1] = ReadW (((SP + 2) & 0177777) | dsenable);
    R[2] = ReadW (((SP + 4) & 0177777) | dsenable);
    R[3] = ReadW (((SP + 6) & 0177777) | dsenable);
    R[4] = ReadW (((SP + 8) & 0177777) | dsenable);
    R[5] = ReadW (((SP + 10) & 0177777) | dsenable);
    SP = (SP + 12) & 0177777;
    }
else R[1] = R[2] = R[3] = 0;                            /* reg, clear R1 - R3 */
fpd = 0;                                                /* instr done */
return;
}
    
/* Test for CIS mid-instruction interrupt */

t_bool cis_int_test (int32 cycles, int32 oldpc, t_stat *st)
{
while (cycles >= 0) {                                   /* until delay done */
    if (sim_interval > cycles) {                        /* event > delay */
        sim_interval = sim_interval - cycles;
        break;
        }
    else {                                              /* event <= delay */
        cycles = cycles - sim_interval;                 /* decr delay */
        sim_interval = 0;                               /* process event */
        *st = sim_process_event ();
        trap_req = calc_ints (ipl, trap_req);           /* recalc int req */
        if ((*st != SCPE_OK) ||                         /* bad status or */
            trap_req & TRAP_INT) {                      /* interrupt? */
            PC = oldpc;                                 /* back out */
            return TRUE;
            }                                           /* end if stop */
        }                                               /* end else event */
    }                                                   /* end while delay */
return FALSE;
}
