/*************************************************************************
 *                                                                       *
 * Copyright (c) 2009-2022 Howard M. Harte.                              *
 * https://github.com/hharte                                             *
 *                                                                       *
 * 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 NON-            *
 * INFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE   *
 * LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN       *
 * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN     *
 * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE      *
 * SOFTWARE.                                                             *
 *                                                                       *
 * Except as contained in this notice, the names of The Authors shall    *
 * not be used in advertising or otherwise to promote the sale, use or   *
 * other dealings in this Software without prior written authorization   *
 * from the Authors.                                                     *
 *                                                                       *
 * Module Description:                                                   *
 *     TX-0 Central Processor                                            *
 *                                                                       *
 * References:                                                           *
 *     See: www.bitsavers.org/pdf/mit/tx-0/ for documentation.           *
 *     See: www.bitsavers.org/bits/MIT/tx-0/ for software.               *
 *************************************************************************/

/*

The original Lincoln Labs TX-0 had only two bits of opcode and no index
register. The machine was moved to room 26-248 at MIT in July 1958 and
after about a year and a half the opcode field was extended to four bits
and an index register was added. 

(ref. Computer Museum Report Vol 8, Spring 1984)

--------------------------------------------------------------
Original TX-0 Registers and Instruction Set
from "A Functional Description of the TX-0 Computer" Oct, 1958
--------------------------------------------------------------

    The register state for the TX-0 is:

        MBR[0:17]       Memory Buffer Register (18 bits)
        AC[0:17]        Accumulator (18 bits)
        MAR[0:15]       Memory Address Register (16 bits)
        PC[0:15]        Program Counter (16 bits)
        IR[0:1]         Instruction Register (2 bits)
        LR[0:17]        Live Register (18 bits)
        TBR[0:17]       Toggle Switch Buffer Register (18 toggle switches)
        TAC[0:17]       Toggle Switch Accumulator (18 toggle switches)

     0  1  2  3  4  5  6  7  8  9 10 11 12 13 14 15 16 17
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+
   |  op |                       address                 |              
   +--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+--+

   This routine is the instruction decode routine for the TX-0.
   It is called from the simulator control program to execute
   instructions in simulated memory, starting at the simulated PC.
   It runs until 'reason' is set non-zero.

   General notes:

   1. Instruction Set: The TX-0 had (at least) two different instruction sets.
      The original instruction set from 1956 had two bits for the opcode (IR)
      while the later 1961 instruction set used five bits for this purpose.
      This simulator is designed to simulate both of these modes.  The micro
      orders were also changed along with the new instruction set.  The main
      part of the instruction fetch/decode logic is the same for both
      instruction sets.  Memory address range is adjusted depending on the mode.
      IR[2:4] are forced to 0 in the 1956 mode.  The Micro Orders are vastly
      different, so the main loop decodes and executes the 1961 micro-orders,
      while the sim_opr_orig() function decodes and executes the 1956 micro-orders.

   2. Reasons to stop.  The simulator can be stopped by:

        HALT instruction
        breakpoint encountered
        I/O error in I/O simulator

   3. Arithmetic.  The TX-0 is a 1's complement system.  In 1's
      complement arithmetic, a negative number is represented by the
      complement (XOR 0777777) of its absolute value.  Addition of 1's
      complement numbers requires propagating the carry out of the high
      order bit back to the low order bit.

   4. Adding I/O devices.  Three modules must be modified:

        tx0_cpu.c      add dispatch code
        tx0_sys.c      add sim_devices table entry

   5. Bugs, limitations, and known issues:
        o There is a bug in the 1961 instruction set simulation, which causes the
          mouse maze program's searching algorithm to fail.
        o The instruction timing (ie, sim_interval) is not accurate, so implementing a
          timing-critical I/O device like the Magtape would require this to be added first.
        o PCQ and History do not work.
        o Symbolic input does not work.
        o Probably lots of other bugs, as Tic-Tac-Toe and Mouse Maze are the only tapes
          that I've tested to mostly work.  It's difficult to tell what tapes on
          bitsavers.org are designed for the 1956 instruction set vs. the later one.


OP    Description
--    -----------

00    sto x   Replace the contents of register x with the contents of the AC
              Let the AC remain the same.
01    add x   Add the word in register x to the contents of the AC and leave
              the sum in the AC
10    trn x   If the sign digit of the accumulator (AC bit 0) is negative (i.e., a one)
              take the next instruction from register x and continue from there.
              If the sign is positive (i.e., a zero ignore this instruction and proceed
              to the next instruction
11    opr x   Execute one of the operate class commands indicated by the number x

Around 1961, the TX-0 was modified to include additional instructions, and the addressable
memory range was lowered to 8K words.  The IR was increased from two bits to five bits.
In addtion, many of the operate-class micro-orders changed.

      OPERATE CLASS MICRO-ORDERS FOR 1961 INSTRUCTION SET
      ---------------------------------------------------
                              Cycle.tp
  cla  --1 --- --- --- --- ---  0.8 Clear AC
  amb  --- 1-- --- --- --- ---  0.7 Transfer AC contents to MBR
  cyr  --- --- --- 110 --- ---  1.6 Cycle AC contents right one binary position (AC 17 -> AC 0)
  shr  --- --- --- 100 --- ---  1.6 Shift AC contents right one binary position (AC 0 unchanged)
  mbl  --- --- --- 01x --- ---  1.4 Transfer MBR contents to LR
  xmb  --- --- --- 0x1 --- ---  1.2 Transfer XR contents to MBR
  com  --- --- --- --- 1-- ---  1.2 Compliment AC
  pad  --- --- --- --- -1- ---  1.5 Parital add MBR to AC (for each MBR one, complement the correp AC bit)
  cry  --- --- --- --- --1 ---  1.7 A carry digit is a one if in the next least sigmificant digit,
                                    either ac=0 and mbr=1 or ac=1 and carry digit=1. The carry digits
                                    so determined are partial added to the AC by cry. pad and cry used
                                    together give a full one's complement addition of C(MBR) to C(AC)
  anb  --- --- --- --- --- 111  1.2-2 And LR and MBR
  orb  --- --- --- --- --- 101  1.3 Or LR into MBR
  lmb  --- --- --- --- --- 01x  1.4 Tranfer LR contents to MBR
  mbx  --- --- --- --- --- 0x1  1.8 Transfer MBR contents to XR

See TX-0 memo M-5001-19 for a simple formula for cry, used in the code below.
*/
#include "tx0_defs.h"

#define OPR_CLA         0100000 /* 0.8 */
#define OPR_AMB         0040000 /* 0.7 */

#define OPR_SHF_MASK    0000700 /* 1.6 */
#define OPR_CYR         0000600
#define OPR_SHR         0000400

#define OPR_MBL_MASK    0000600 /* 1.4 */
#define OPR_MBL         0000200
#define OPR_XMB_MASK    0000500 /* 1.2 */
#define OPR_XMB         0000100

#define OPR_COM         0000040 /* 1.2 */
#define OPR_PAD         0000020 /* 1.5 */
#define OPR_CRY         0000010 /* 1.7 */

#define OPR_LOG_MASK    0000007 /* Logical operation mask */
#define OPR_ANB         0000007 /* 1.2-2 */
#define OPR_ORB         0000005 /* 1.3 */

#define OPR_LMB_MASK    0000006 /* 1.4 */
#define OPR_LMB         0000002
#define OPR_MBX_MASK    0000005 /* 1.8 */
#define OPR_MBX         0000001

/*
      IN OUT GROUP
      ------------
  nop  --- -00 000 --- --- ---  NOP
  tac  --- -00 001 --- --- ---  1.1
  tbr  --- -00 010 --- --- ---  1.2
  pen  --- -00 011 --- --- ---  1.1
  sel  --- -00 100 --- --- ---  
  spare--- -00 101 --- --- ---  
  rpf  --- -00 110 --- --- ---  1.2
  spf  --- -00 111 --- --- ---  1.6
  exN  --- -01 nnn --- --- ---  IOS
  cpy  --- -10 000 --- --- ---  IOS
  r1l  --- -10 001 --- --- ---  IOS
  dis  --- -10 010 --- --- ---  IOS
  r3l  --- -10 011 --- --- ---  IOS
  prt  --- -10 100 --- --- ---  IOS
  spare--- -10 101 --- --- ---  
  p6h  --- -10 110 --- --- ---  IOS
  p7h  --- -10 111 --- --- ---  IOS
  hlt  --- -11 000 --- --- ---  1.8
  cll  --- -11 001 --- --- ---  0.6
  clr  --- -11 010 --- --- ---  0.6
*/
#define IOS_MASK    0037000
#define IOS_EX_MASK 0030000
#define IOS_NOP     0000000
#define IOS_TAC     0001000
#define IOS_TBR     0002000
#define IOS_PEN     0003000
#define IOS_SEL     0004000
#define IOS_RPF     0006000
#define IOS_SPF     0007000
#define IOS_CPY     0020000
#define IOS_R1L     0021000
#define IOS_DIS     0022000
#define IOS_R3L     0023000
#define IOS_PRT     0024000
#define IOS_P6H     0026000
#define IOS_P7H     0027000
#define IOS_HLT     0030000
#define IOS_CLL     0031000
#define IOS_CLR     0032000

#define PCQ_SIZE        64                              /* must be 2**n */
#define PCQ_MASK        (PCQ_SIZE - 1)
#define PCQ_ENTRY       pcq[pcq_p = (pcq_p - 1) & PCQ_MASK] = PC
#define UNIT_V_MSIZE    (UNIT_V_UF + 4)                 /* dummy mask */
#define UNIT_V_EXT      (UNIT_V_UF + 2)
#define UNIT_EXT_INST   (1 << UNIT_V_EXT)
#define UNIT_MSIZE      (1 << UNIT_V_MSIZE)

#define MIN(a,b) (((a) < (b)) ? (a) : (b))

#define HIST_PC         0x40000000
#define HIST_V_SHF      18
#define HIST_MIN        64
#define HIST_MAX        65536

#define TRACE_PRINT(level, args)    if(cpu_dev.dctrl & level) {   \
                                         printf args;             \
                                    }
typedef struct {
    uint32              pc;
    uint32              ir;
    uint32              ovac;
    uint32              pfio;
    uint32              ea;
    uint32              opnd;
} InstHistory;

int32 M[MAXMEMSIZE] = { 0 };                            /* memory */
int32 AC = 0;                                           /* AC */
int32 IR = 0;                                           /* IR */
int32 PC = 0;                                           /* PC */
int32 MAR = 0;                                          /* MAR */
int32 XR = 0;                                           /* XR (index register) */
int32 MBR = 0;                                          /* MBR */
int32 LR = 0;                                           /* LR (Live Register) */
int32 OV = 0;                                           /* overflow */
int32 TBR = 0;                                          /* sense switches */
int32 PF = 0;                                           /* program flags */
int32 TAC = 0;                                          /* Toggle Switch Accumulator */
int32 iosta = 0;                                        /* status reg */
int32 ios = 0;                                          /* I/O Stop */
int32 ch = 0;                                           /* Chime Alarm */
int32 LPEN = 0;                                         /* Light Pen / Light Gun flops */
int32 mode_tst = 1;                                     /* Test Mode Flip-flop */
int32 mode_rdin = 1;                                    /* Read-In Mode Flip-flop */

uint16 pcq[PCQ_SIZE] = { 0 };                           /* PC queue */
int32 pcq_p = 0;                                        /* PC queue ptr */
REG *pcq_r = NULL;                                      /* PC queue reg ptr */
int32 hst_p = 0;                                        /* history pointer */
int32 hst_lnt = 0;                                      /* history length */
InstHistory *hst = NULL;                                /* inst history */

int32 fpc_MA;                                           /* shadow ma for FPC access */
int32 fpc_OP;                                           /* shadow op for FPC access */

int32 addr_mask = YMASK;

static t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw);
static t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw);
static t_stat cpu_reset (DEVICE *dptr);
static t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
t_stat cpu_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
int32 cpu_get_mode (void);
static t_stat cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
static t_stat cpu_set_ext (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
static t_stat cpu_set_noext (UNIT *uptr, int32 val, CONST char *cptr, void *desc);
static t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc);
t_stat Read (void);
t_stat Write (void);

extern int32 petr (int32 inst, int32 dev, int32 dat);
extern int32 ptp (int32 inst, int32 dev, int32 dat);
extern int32 tti (int32 inst, int32 dev, int32 dat);
extern int32 tto (int32 inst, int32 dev, int32 dat);
extern int32 lpt (int32 inst, int32 dev, int32 dat);
extern int32 dt  (int32 inst, int32 dev, int32 dat);
extern int32 drm (int32 inst, int32 dev, int32 dat);
#ifdef USE_DISPLAY
extern int32 dpy (int32 ac);
#endif

extern UNIT petr_unit;

/* CPU data structures

   cpu_dev      CPU device descriptor
   cpu_unit     CPU unit
   cpu_reg      CPU register list
   cpu_mod      CPU modifier list
*/

UNIT cpu_unit = { UDATA (NULL, UNIT_FIX | UNIT_BINK | UNIT_EXT_INST | UNIT_MODE_READIN, MAXMEMSIZE) };

REG cpu_reg[] = {
    { ORDATAD (PC, PC, ASIZE, "program counter") },
    { ORDATAD (AC, AC, 18, "accumulator") },
    { ORDATAD (IR, IR, 5, "instruction register (5 bits in Extented Mode,                                  2 bits in standard mode)") },
    { ORDATAD (MAR, MAR, 16, "memory address register") },
    { ORDATAD (XR, XR, 14, "index register (Extended Mode only)") },
    { ORDATAD (MBR, MBR, 18, "memory buffer register") },
    { ORDATAD (LR, LR, 18, "live register") },
    { ORDATAD (TAC, TAC, 18, "toggle switch accumulator") },
    { ORDATAD (TBR, TBR, 18, "toggle switch buffer register") },
    { ORDATA (PF, PF, 18) },
    { BRDATA (PCQ, pcq, 8, ASIZE, PCQ_SIZE), REG_RO+REG_CIRC },
    { ORDATA (PCQP, pcq_p, 6), REG_HRO },
    { FLDATAD (IOS, ios, 0, "in out stop") },       /* In Out Stop */
    { FLDATAD (CH, ch, 0, "chime alarm") },         /* Chime Alarm */
    { ORDATAD (LP, LPEN, 2, "light pen") },       /* Light Pen */
    { FLDATA (R, mode_rdin, 0), REG_HRO },      /* Mode "R" (Read In) Flip-Flop */
    { FLDATA (T, mode_tst, 0), REG_HRO },       /* Mode "T" (Test) Flip-Flop */
    { NULL }
    };

MTAB cpu_mod[] = {
    { UNIT_EXT_INST, 0, "standard CPU", "TX0STD", &cpu_set_noext },
    { UNIT_EXT_INST, UNIT_EXT_INST, "Extended Instruction Set", "TX0EXT", &cpu_set_ext },
    { UNIT_MSIZE, 4096, NULL, "4K", &cpu_set_size },
    { UNIT_MSIZE, 8192, NULL, "8K", &cpu_set_size },
    { UNIT_MSIZE, 65536, NULL, "64K", &cpu_set_size },
    { UNIT_MODE, 0, "NORMAL", "NORMAL", &cpu_set_mode },
    { UNIT_MODE, UNIT_MODE_TEST, "TEST", "TEST", &cpu_set_mode },
    { UNIT_MODE, UNIT_MODE_READIN, "READIN", "READIN", &cpu_set_mode }, 
    { MTAB_XTD|MTAB_VDV|MTAB_NMO|MTAB_SHP, 0, "HISTORY", "HISTORY",
      &cpu_set_hist, &cpu_show_hist },
    { 0 }
    };

/* Debug flags */
#define ERROR_MSG       (1 << 0)
#define TRACE_MSG       (1 << 1)
#define STO_MSG         (1 << 2)
#define ADD_MSG         (1 << 3)
#define TRN_MSG         (1 << 4)
#define ORD_MSG         (1 << 5)
#define IOS_MSG         (1 << 6)
#define READIN_MSG      (1 << 7)
#define VERBOSE_MSG     (1 << 8)
#define COUNTERS_MSG    (1 << 9)

/* Debug Flags */
static DEBTAB cpu_dt[] = {
    { "ERROR",  ERROR_MSG },
    { "TRACE",  TRACE_MSG },
    { "STO",    STO_MSG },
    { "ADD",    ADD_MSG },
    { "TRN",    TRN_MSG },
    { "ORD",    ORD_MSG },
    { "IOS",    IOS_MSG },
    { "READIN", READIN_MSG },
    { "VERBOSE",VERBOSE_MSG },
    { "COUNTERS",COUNTERS_MSG },
    { NULL,     0 }
};

DEVICE cpu_dev = {
    "CPU", &cpu_unit, cpu_reg, cpu_mod,
    1, 8, ASIZE, 1, 8, 18,
    &cpu_ex, &cpu_dep, &cpu_reset,
    NULL, NULL, NULL,
    NULL, DEV_DEBUG, ERROR_MSG,
    cpu_dt, NULL
    };

int32 compute_index (int32 y, int32 XR)
{
    int32 sum;

    y  &= YMASK;    /* force 13-bit (0 sign) */
    XR &= 037777;   /* force 14-bit */

    sum = y + XR;

    if (sum > 037777) { /* Carry from bit 4 into bit 17. */
        sum += 1;
    }

    sum &= YMASK;       /* truncate to 13-bit */

    return (sum);
}

/* CPU Instruction usage counters */
typedef struct {
/* Store group */
    int32 sto, stx, sxa, ado, slr, slx, stz;
/* Add group */
    int32 add, adx, ldx, aux, llr, llx, lda, lax;
/* TRN Group */
    int32 trn, tze, tsx, tix, tra, trx, tlv;
/* OPR Group */
    int32 cla, amb, cyr, shr, mbl, xmb, com, pad, cry, anb, orb, lmb, mbx;
} INST_CTRS;

INST_CTRS inst_ctr;


void tx0_dump_regs(const char *desc)
{
    TRACE_PRINT(TRACE_MSG, ("%s: AC=%06o, MAR=%05o, MBR=%06o, LR=%06o, XR=%05o\n", desc, AC, MAR, MBR, LR, XR));

    /* Check regs sanity */
    if (AC > DMASK) {
        sim_printf("Error: AC > DMASK\n");
    }
    if (MBR > DMASK) {
        sim_printf("Error: MBR > DMASK\n");
    }
    if (LR > DMASK) {
        sim_printf("Error: LR > DMASK\n");
    }
    if (!MEM_ADDR_OK(MAR)) {
        sim_printf("Error: MAR > %06o\n", MEMSIZE);
    }

}

t_stat sim_opr_orig(int32 op);

t_stat sim_instr (void)
{
    int32 IR, op, inst_class, y;
    int32 tempLR;   /* LR temporary storage in case both LMB and MBL are set (swap LR<->MBR) */
    t_stat reason;

    /* Clear Instruction counters */
    inst_ctr.sto = inst_ctr.stx = inst_ctr.sxa = inst_ctr.ado = inst_ctr.slr = inst_ctr.slx = inst_ctr.stz = 0;
    inst_ctr.add = inst_ctr.adx = inst_ctr.ldx = inst_ctr.aux = inst_ctr.llr = inst_ctr.llx = inst_ctr.lda = inst_ctr.lax = 0;
    inst_ctr.trn = inst_ctr.tze = inst_ctr.tsx = inst_ctr.tix = inst_ctr.tra = inst_ctr.trx = inst_ctr.tlv = 0;
    inst_ctr.cla = inst_ctr.amb = inst_ctr.cyr = inst_ctr.shr = inst_ctr.mbl = inst_ctr.xmb = inst_ctr.com = inst_ctr.pad = inst_ctr.cry = inst_ctr.anb = inst_ctr.orb = inst_ctr.lmb = inst_ctr.mbx = 0;

    #define INCR_ADDR(x)    ((x+1) & (MEMSIZE-1))

    /* Main instruction fetch/decode loop: check events */

    reason = 0;
    while (reason == 0) {                                   /* loop until halted */

        if (sim_interval <= 0) {                            /* check clock queue */
            reason = sim_process_event ();
            if (reason != SCPE_OK)
                break;
        }

        if (sim_brk_summ && sim_brk_test (PC, SWMASK ('E'))) { /* breakpoint? */
            reason = STOP_IBKPT;                            /* stop simulation */
            break;
        }

        sim_interval = sim_interval - 1;

        if (ios) {
            TRACE_PRINT(ERROR_MSG, ("I/O Stop - Waiting...\n"));
            continue; /* Don't execute test/readin/normal mode */
        }

        /* Handle Instruction Execution in TEST and READIN modes */
        if (mode_tst) { /* Test Mode / Readin mode */
            if (mode_rdin) { /* Readin Mode */

                if ((petr_unit.flags & UNIT_ATT) == 0)
                    return SCPE_UNATT;

                reason = SCPE_OK;   /* Default is to continue reading, and transfer control when done. */
                AC = petr(3,0,0);   /* Read three chars from tape into AC */
                MAR = AC & AMASK;   /* Set memory address */
                IR = AC >> 16;

                if (!MEM_ADDR_OK(MAR)) {
                    TRACE_PRINT(ERROR_MSG, ("READIN: Tape address out of range.\n"));
                    reason = SCPE_FMT;
                    break;
                }

                switch (IR) {
                    case 00:    /* Storage (sto x) */
                    case 03:    /* Storage (opr x) */
                        MBR = petr(3,0,0);  /* Read three characters from tape. */
                        TRACE_PRINT(READIN_MSG, ("READIN: sto @%06o = %06o\n", MAR, MBR));
                        Write();
                        break;
                    case 02:    /* Transfer Control (trn x) Start Execution */
                        PC = MAR;
                        TRACE_PRINT(READIN_MSG, ("READIN: trn %06o (Start Execution)\n", PC));
                        reason = cpu_set_mode(&cpu_unit, 0, NULL, NULL);
                        break;
                    case 01:    /* Transfer (add x) - Halt */
                        PC = MAR;
                        TRACE_PRINT(READIN_MSG, ("READIN: add %06o (Halt)\n", PC));
                        reason = cpu_set_mode(&cpu_unit, 0, NULL, NULL);
                        if (reason == SCPE_OK) reason = SCPE_STOP; /* let SIMH halt. */
                        break;
                    default:
                        reason = SCPE_IERR;
                        break;
                }
                continue;  /* Don't fall into normal-mode processing. */
            } else {  /* Test mode not implemented yet. */
                TRACE_PRINT(ERROR_MSG, ("TEST Mode not implemented.\n"));
                reason = SCPE_STOP;
                break;
            }
        } else if (mode_rdin) {
            TRACE_PRINT(ERROR_MSG, ("Invalid CPU mode.\n"));
            reason = SCPE_IERR;
            break;
        }

        /* Fetch, decode instruction in NORMAL mode */
        MAR = PC;
        if (Read ()) break;                                 /* fetch inst */
     
        IR = (MBR >> 13);                                   /* save in IR */
        inst_class = IR >> 3;
        op = MBR & AMASK;
        y = MBR & YMASK;

        if ((cpu_unit.flags & UNIT_EXT_INST) == 0) {  /* Original instruction set */
            IR &= 030;
            MAR = MBR & AMASK; /* 16-bit address field */
        } else {
            MAR = MBR & YMASK; /* 13-bit address field */
        }

        if (hst_lnt) {                                      /* history enabled? */
            hst_p = (hst_p + 1);                            /* next entry */
            if (hst_p >= hst_lnt) hst_p = 0;
            hst[hst_p].pc = MAR | HIST_PC;                   /* save state */
            hst[hst_p].ir = IR;
            hst[hst_p].ovac = (OV << HIST_V_SHF) | AC;
        }

        PC = INCR_ADDR (PC);                                /* increment PC */

#ifdef USE_FPC
        fpc_OP = op;                                        /* shadow opcode for FPC */
#endif

        tx0_dump_regs("START");
        
        switch (inst_class) {                               /* decode IR<0:1> */

        /* Logical, load, store instructions */
        case 00:                                            /* sto x */
            switch (IR & 07) {
                case 0:     /* sto */
                    MBR = AC;
                    Write();
                    inst_ctr.sto++;
                    break;
                case 1:     /* stx */
                    MBR = AC;
                    MAR = compute_index(y, XR);
                    Write();
                    inst_ctr.stx++;
                    break;
                case 2:     /* sxa */
                    {
                        int32 temp = M[MAR];
                        temp &= 0760000;
                        temp |= (XR & YMASK);
                        MBR = temp;
                        Write();
                    }
                    inst_ctr.sxa++;
                    break;
                case 3:     /* ado */
                    {
                        int32 temp = M[MAR];
                        temp += 1;              /* add 1 */
                        if (temp > DMASK) {     /* Overflow, */
                            temp += 1;          /* propagate carry from bit 0 to bit 17. */
                        }
                        temp &= DMASK;
                        MBR = temp;
                        AC = temp;
                        Write();
                    }
                    inst_ctr.ado++;
                    break;
                case 4:     /* slr */
                    MBR = LR;
                    Write();
                    inst_ctr.slr++;
                    break;
                case 5:     /* slx */
                    MAR = compute_index(y, XR);
                    MBR = LR;
                    Write();
                    inst_ctr.slx++;
                    break;
                case 6:     /* stz */
                    MBR = 0;
                    Write();
                    inst_ctr.stz++;
                    break;
                case 7:     /* no-op */
                    break;
            }
            break;

        case 01:                                            /* add x */
            switch (IR & 07) {
                case 0:     /* add */
                    Read();
                    AC = AC + MBR;
                    if (AC > DMASK) {
                        AC += 1;
                    }
                    AC &= DMASK;
                    inst_ctr.add++;
                    break;
                case 1:     /* adx */
                    MAR = compute_index(y, XR);
                    Read();
                    AC = AC + MBR;
                    if (AC > DMASK) {
                        AC += 1;
                    }
                    AC &= DMASK;
                    inst_ctr.adx++;
                    break;
                case 2:     /* ldx */
                    Read();
                    XR  = MBR & YMASK;         /* load XR[5:17] from C(y[5:17]) */
                    XR |= ((MBR & SIGN) >> 4); /* Load XR[4] from C(y[0]) */
                    inst_ctr.ldx++;
                    break;
                case 3:     /* aux  (Augment Index) */
                    {
                        uint32 newY = (y & 0017777) | ((y & SIGN) >> 4);
                        TRACE_PRINT(ADD_MSG, ("[%06o] AUX: y=%05o, XR=%05o = ", PC-1, newY, XR));
                        XR = XR + newY;
                        TRACE_PRINT(ADD_MSG, ("%05o\n", XR));
                        inst_ctr.aux++;
                        break;
                    }
                case 4:     /* llr (Load Live Register) */
                    Read();
                    LR = MBR;
                    inst_ctr.llr++;
                    break;
                case 5:     /* llx (Load Live Register, Indexed) */
                    MAR = compute_index(y, XR);
                    Read();
                    LR = MBR;
                    inst_ctr.llx++;
                    break;
                case 6:     /* lda (Load Accumulator) */
                    Read();
                    AC = MBR;
                    inst_ctr.lda++;
                    break;
                case 7:     /* lax (Load Accumulator, Indexed) */
                    MAR = compute_index(y, XR);
                    Read();
                    AC = MBR;
                    inst_ctr.lax++;
                    break;
            }
            break;

        case 02:                                            /* trn x */
            switch (IR & 07) {
                case 0:     /* trn (Transfer on Negative AC) */
                    if (AC & SIGN) {
                        TRACE_PRINT(TRN_MSG, ("[%06o] TRN: Transfer taken: PC=%06o\n", PC-1, y));
                        PC = MAR;
                    }
                    inst_ctr.trn++;
                    break;
                case 1:     /* tze (Transfer on +/- Zero) */
                    if ((AC == 0777777) || (AC == 0000000)) {
                        TRACE_PRINT(TRN_MSG, ("[%06o] TZE: Transfer taken: PC=%06o\n", PC-1, y));
                        PC = y;
                    }
                    inst_ctr.tze++;
                    break;
                case 2:     /* tsx (Transfer and set Index) */
                    XR = PC & 0017777;  /* XR[4] = 0; */
                    TRACE_PRINT(TRN_MSG, ("[%06o] TSX: PC=%06o, XR=%05o\n", PC-1, y, XR));
                    PC = y;
                    inst_ctr.tsx++;
                    break;
                case 3:     /* tix (Transfer and Index) */
                    TRACE_PRINT(TRN_MSG, ("[%06o] TIX: XR=%05o\n", PC-1, XR));
                    if ((XR == 037777) || (XR == 000000)) { /* +/- 0, take next instruction */
                        TRACE_PRINT(TRN_MSG, ("+/- 0, transfer not taken.\n"));
                    } else { /* Not +/- 0 */
                        if (XR & 0020000) { /* XR[4] == 1 */
                            TRACE_PRINT(TRN_MSG, ("XR is negative, transfer taken,"));
                            XR ++;
                        } else { /* XR[4] = 0 */
                            TRACE_PRINT(TRN_MSG, ("XR is positive, transfer taken,"));
                            XR --;
                        }
                        PC = y;
                        XR &= 037777;
                        TRACE_PRINT(TRN_MSG, (" PC=%06o, XR=%05o\n", PC, XR));
                        
                    }
                    inst_ctr.tix++;
                    break;
                case 4:     /* tra (Unconditional Transfer) */
                    TRACE_PRINT(TRN_MSG, ("[%06o] TRA: Transfer taken: PC=%06o\n", PC-1, y));
                    PC = y;
                    inst_ctr.tra++;
                    break;
                case 5:     /* trx */
                    {
                        int32 newPC;
                        newPC = compute_index(y, XR);
                        TRACE_PRINT(TRN_MSG, ("[%06o] TRA: Transfer taken: PC=%06o\n", PC-1, newPC));
                        PC = newPC;
                    }
                    inst_ctr.trx++;
                    break;
                case 6:     /* tlv (Transfer on External Level) */
                    TRACE_PRINT(ERROR_MSG, ("[%06o] TODO: Implement TLV\n", PC-1));
                    inst_ctr.tlv++;
                    break;
                case 7:     /* no-op */
                    break;
            }
            break;

        case 03:                                            /* opr x */
            if ((cpu_unit.flags & UNIT_EXT_INST) == 0) {  /* Original instruction set */
                reason = sim_opr_orig(op);
                break;
            }

            /* I can't find this mentioned in the TX-0 Documentation, but for the
             * lro and xro instructions, this must be needed.
             */
            MBR = 0;

/* Cycle 0 */
            if (op & OPR_AMB) { /* 0.7 */
                inst_ctr.amb++;
                MBR = AC;
                TRACE_PRINT(ORD_MSG, ("[%06o]: AMB: MBR=%06o\n", PC-1, MBR));
            }

            if (op & OPR_CLA) { /* 0.8 */
                inst_ctr.cla++;
                AC = 0;
                TRACE_PRINT(ORD_MSG, ("[%06o]: CLA: AC=%06o\n", PC-1, AC));
            }

/* IOS - In / Out Stop */
            /* Check TTI for character.  If so, put in LR and set LR bit 0. */
            if (iosta & IOS_TTI) {
                int32 rbuf;
                rbuf = tti(0,0,0);
                TRACE_PRINT(IOS_MSG, ("TTI: character received=%03o\n", rbuf &077));
                LR &= 0266666; /* Clear bits 0,2,5,8,...,17 */

                LR |= SIGN; /* Set bit 0, character available. */
                LR |= ((rbuf & 001) >> 0) << 15;/* bit 2  */
                LR |= ((rbuf & 002) >> 1) << 12;/* bit 5  */
                LR |= ((rbuf & 004) >> 2) << 9; /* bit 8  */
                LR |= ((rbuf & 010) >> 3) << 6; /* bit 11 */
                LR |= ((rbuf & 020) >> 4) << 3; /* bit 14 */
                LR |= ((rbuf & 040) >> 5) << 0; /* bit 17 */
            }

            switch(op & IOS_MASK) {
                case IOS_NOP:
                    break;
                case IOS_TAC:
                    TRACE_PRINT(IOS_MSG, ("[%06o] TAC %06o\n", PC-1, TAC));
                    AC |= TAC;
                    break;
                case IOS_TBR:
                    TRACE_PRINT(IOS_MSG, ("[%06o] TBR %06o\n", PC-1, TBR));
                    MBR |= TBR;
                    break;
                case IOS_PEN:
                    TRACE_PRINT(IOS_MSG, ("[%06o] Light Pen %01o\n", PC-1, LPEN));
                    AC &= AMASK;
                    AC |= (LPEN & 1) << 17;
                    AC |= (LPEN & 2) << 16;
                    AC &= DMASK;
                    break;
                case IOS_SEL:
                    { /* These are used for Magtape control.
                         Magtape is compatible with IBM 709.  Maybe the SIMH 7090 magtape can be leveraged. */
                        int32 CLRA = (op & 0100000);
                        int32 BINDEC = (op & 020);
                        int32 device = op & 03;
                        int32 tape_ord = (op >> 2) & 03;
                        const char *tape_cmd[] = {"Backspace Tape", "Read/Select Tape", "Rewind Tape", "Write/Select Tape" };

                        TRACE_PRINT(ERROR_MSG, ("[%06o] TODO: SEL (magtape)\n", PC-1));
                        sim_printf("Device %d: CLRA=%d, BINDEC=%d: %s\n", device, CLRA, BINDEC, tape_cmd[tape_ord]);
                    }
                    break;
                case IOS_RPF: /* These are used for Magtape control. */
                    TRACE_PRINT(IOS_MSG, ("[%06o] RPF %06o\n", PC-1, PF));
                    MBR |= PF;
                    break;
                case IOS_SPF: /* These are used for Magtape control. */
                    TRACE_PRINT(IOS_MSG, ("[%06o] SPF %06o\n", PC-1, MBR));
                    PF = MBR;
                    break;
                case IOS_CPY: /* These are used for Magtape control. */
                    TRACE_PRINT(ERROR_MSG, ("[%06o] TODO: CPY\n", PC-1));
                    break;
                case IOS_R1L:
                    AC &= 0333333; /* Clear bits 0,3,6,9,12,15 */
                    AC |= petr(1, 0, 0); /* Read one line from PETR */
                    break;
                case IOS_DIS:
#ifdef USE_DISPLAY
                    LPEN = dpy (AC);  /* Display point on the CRT */
#endif /* USE_DISPLAY */
                    break;
                case IOS_R3L:
                    AC = petr(3, 0, 0); /* Read three lines from PETR */
                    break;
                case IOS_PRT:
                    {
                        uint32 tmpAC = 0;
                        tmpAC |= ((AC & 0000001) >> 0) << 0; /* bit 17 */
                        tmpAC |= ((AC & 0000010) >> 3) << 1; /* bit 14 */
                        tmpAC |= ((AC & 0000100) >> 6) << 2; /* bit 11 */
                        tmpAC |= ((AC & 0001000) >> 9) << 3; /* bit 8 */
                        tmpAC |= ((AC & 0010000) >> 12) << 4; /* bit 5 */
                        tmpAC |= ((AC & 0100000) >> 15) << 5; /* bit 2 */
                        tto (0, 0, tmpAC & 077);    /* Print one character on TTO */
                    }
                    break;
                case IOS_P6H:
                case IOS_P7H:
                    {
                        uint32 tmpAC = 0;
                        tmpAC |= ((AC & 0000001) >> 0) << 0; /* bit 17 */
                        tmpAC |= ((AC & 0000010) >> 3) << 1; /* bit 14 */
                        tmpAC |= ((AC & 0000100) >> 6) << 2; /* bit 11 */
                        tmpAC |= ((AC & 0001000) >> 9) << 3; /* bit 8 */
                        tmpAC |= ((AC & 0010000) >> 12) << 4; /* bit 5 */
                        tmpAC |= ((AC & 0100000) >> 15) << 5; /* bit 2 */
                        tmpAC &= 0077;
                        if ((op & IOS_MASK) == IOS_P7H) {
                            tmpAC |= 0100;  /* Punch 7th hole. */
                            TRACE_PRINT(ERROR_MSG, ("[%06o] Punch 7 holes\n", PC-1));
                        } else {
                            TRACE_PRINT(ERROR_MSG, ("[%06o] Punch 6 holes\n", PC-1));
                        }
                        ptp (0, 0, tmpAC);    /* Punch character on PTP */
                    }
                    break;
                case IOS_HLT:
                    TRACE_PRINT(IOS_MSG, ("[%06o] HALT Instruction\n", PC-1));
                    reason = STOP_HALT;
                    break;
                case IOS_CLL:
                    AC &= 0000777;
                    break;
                case IOS_CLR:
                    AC &= 0777000;
                    break;
                default: /* Could be ex0-ex7, handle them here. */
                    if ((op & IOS_EX_MASK) == 0010000) {
                        TRACE_PRINT(ERROR_MSG, ("[%06o] TODO: EX%o\n", PC-1, (op >> 9) & 07));
                    }
                    break;
            }

/* Cycle 1 */
            if (op & OPR_COM) { /* 1.2 */
                AC = ~AC;
                AC &= DMASK;
                TRACE_PRINT(ORD_MSG, ("[%06o]: COM: AC=%06o\n", PC-1, AC));
                inst_ctr.com++;
            }

            if ((op & OPR_XMB_MASK) == OPR_XMB) { /* 1.2 XR[5:17] -> MBR[5:17], XR[4] -> MBR[0:4] */
                int32 bit14 = (XR >> 13) & 1;
                MBR  = XR & YMASK;      /* XR[5:17] -> MBR[5:17] */
                MBR |= (bit14 << 17);   /* XR[4] -> MBR[0] */
                MBR |= (bit14 << 16);   /* XR[4] -> MBR[1] */
                MBR |= (bit14 << 15);   /* XR[4] -> MBR[2] */
                MBR |= (bit14 << 14);   /* XR[4] -> MBR[3] */
                MBR |= (bit14 << 13);   /* XR[4] -> MBR[4] */

                TRACE_PRINT(ORD_MSG, ("[%06o]: XMB: XR=%05o, MBR=%06o\n", PC-1, XR, MBR));
                inst_ctr.xmb++;
            }

            if ((op & OPR_LOG_MASK) == OPR_ANB) { /* 1.2-2 */
                MBR &= LR;
                TRACE_PRINT(ORD_MSG, ("[%06o]: ANB: MBR=%06o\n", PC-1, MBR));
                inst_ctr.anb++;
            }

            if ((op & OPR_LOG_MASK) == OPR_ORB) { /* 1.3 */
                MBR |= LR;
                TRACE_PRINT(ORD_MSG, ("[%06o]: ORB: MBR=%06o\n", PC-1, MBR));
                inst_ctr.orb++;
            }

            tempLR = LR; /* LR temporary storage in case both LMB and MBL are set (swap LR<->MBR) */
            if ((op & OPR_MBL_MASK) == OPR_MBL) { /* 1.4 */
                LR = MBR;
                TRACE_PRINT(ORD_MSG, ("[%06o]: MBL: LR=%06o, prev LR=%06o\n", PC-1, LR, tempLR));
                inst_ctr.mbl++;
            }

            if ((op & OPR_LMB_MASK) == OPR_LMB) { /* 1.4 */
                MBR = tempLR;
                TRACE_PRINT(ORD_MSG, ("[%06o]: LMB: LR=%06o, MBR=%06o\n", PC-1, LR, MBR));
                inst_ctr.lmb++;
            }

            if (op & OPR_PAD) { /* 1.5 Partial Add (XOR): AC = MBR ^ AC */
                TRACE_PRINT(ORD_MSG, ("[%06o] PAD: AC=%06o, MBR=%06o = ", PC-1, AC, MBR));
                AC = AC ^ MBR;
                AC &= DMASK;
                TRACE_PRINT(ORD_MSG, ("%06o\n", AC));
                inst_ctr.pad++;
            }

            if ((op & OPR_SHF_MASK) == OPR_CYR) { /* 1.6 */
                int32 bit17;
                bit17 = (AC & 1) << 17;
                AC  >>= 1;
                AC |= bit17;
                TRACE_PRINT(ORD_MSG, ("[%06o]: CYR: AC=%06o\n", PC-1, AC));
                inst_ctr.cyr++;
            }

            if ((op & OPR_SHF_MASK) == OPR_SHR) { /* 1.6 Shift AC Right, preserve bit 0. */
                int32 bit0;
                bit0 = AC & 0400000;
                AC = AC >> 1;
                AC |= bit0;
                TRACE_PRINT(ORD_MSG, ("[%06o]: SHR: AC=%06o\n", PC-1, AC));
                inst_ctr.shr++;
            }

            if (op & OPR_CRY) { /* 1.7 */
                TRACE_PRINT(ORD_MSG, ("[%06o] CRY: AC=%06o, MBR=%06o = ", PC-1, AC, MBR));
                AC = AC ^ MBR;
                AC &= DMASK;
                AC = AC + MBR;
                if (AC > DMASK) {
                    AC += 1;
                }
                AC &= DMASK;
                TRACE_PRINT(ORD_MSG, ("%06o\n", AC));
                inst_ctr.cry++;
            }

            if ((op & OPR_MBX_MASK) == OPR_MBX) { /* 1.8    MBR[5:17] -> XR[5:17], MBR[0] -> XR[4] */
                int32 tempXR;
                tempXR  = MBR & YMASK;
                tempXR |= (((MBR >> 17) & 1) << 13);

                XR = tempXR;
                TRACE_PRINT(ORD_MSG, ("[%06o]: MBX: MBR=%06o, XR=%06o\n", PC-1, MBR, XR));
                inst_ctr.mbx++;
            }
        }

        tx0_dump_regs("END");

#ifdef USE_FPC
        fpc_MA = MAR;                                        /* shadow MAR for FPC */
#endif

    } /* end while */
    pcq_r->qptr = pcq_p;                                    /* update pc q ptr */

    TRACE_PRINT(COUNTERS_MSG, ("Instruction Counters\nSTO=%d, STX=%d, SXA=%d, ADO=%d, SLR=%d, SLX=%d, STZ=%d\n",
        inst_ctr.sto, inst_ctr.stx, inst_ctr.sxa, inst_ctr.ado, inst_ctr.slr, inst_ctr.slx, inst_ctr.stz));
    TRACE_PRINT(COUNTERS_MSG, ("ADD=%d, ADX=%d, LDX=%d, AUX=%d, LLR=%d, LLX=%d, LDA=%d, LAX=%d\n",
        inst_ctr.add, inst_ctr.adx, inst_ctr.ldx, inst_ctr.aux, inst_ctr.llr, inst_ctr.llx, inst_ctr.lda, inst_ctr.lax));
    TRACE_PRINT(COUNTERS_MSG, ("TRN=%d, TZE=%d, TSX=%d, TIX=%d, TRA=%d, TRX=%d, TLV=%d\n",
        inst_ctr.trn, inst_ctr.tze, inst_ctr.tsx, inst_ctr.tix, inst_ctr.tra, inst_ctr.trx, inst_ctr.tlv));
    TRACE_PRINT(COUNTERS_MSG, ("CLA=%d, AMB=%d, CYR=%d, SHR=%d, MBL=%d, XMB=%d, COM=%d, PAD=%d, CRY=%d, ANB=%d, ORB=%d, LMB=%d, MBX=%d\n",
        inst_ctr.cla, inst_ctr.amb, inst_ctr.cyr, inst_ctr.shr, inst_ctr.mbl, inst_ctr.xmb, inst_ctr.com, inst_ctr.pad, inst_ctr.cry, inst_ctr.anb, inst_ctr.orb, inst_ctr.lmb, inst_ctr.mbx));

    return reason;
}

/* Read and write memory */
t_stat Read (void)
{
    MAR &= (MEMSIZE - 1);
    MBR = M[MAR];
    MBR &= DMASK;
    return SCPE_OK;
}

t_stat Write (void)
{
    MAR &= (MEMSIZE - 1);
    MBR &= DMASK;
    M[MAR] = MBR;
    return SCPE_OK;
}

/* Reset routine */

static t_stat cpu_reset (DEVICE *dptr)
{
    ios = 0;
    PF = 0;
    MAR = 0;
    MBR = 0;
    pcq_r = find_reg ("PCQ", NULL, dptr);

    if (pcq_r) {
        pcq_r->qptr = 0;
    } else {
        return SCPE_IERR;
    }

    sim_brk_types = sim_brk_dflt = SWMASK ('E');
    
    return SCPE_OK;
}

/* Memory examine */

static t_stat cpu_ex (t_value *vptr, t_addr addr, UNIT *uptr, int32 sw)
{
    if (addr >= MEMSIZE) return SCPE_NXM;
    if (vptr != NULL) *vptr = M[addr] & DMASK;

    return SCPE_OK;
}

/* Memory deposit */

static t_stat cpu_dep (t_value val, t_addr addr, UNIT *uptr, int32 sw)
{
    if (addr >= MEMSIZE) return SCPE_NXM;

    M[addr] = val & DMASK;

    return SCPE_OK;
}

/* Change memory size */

static t_stat cpu_set_size (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
    int32 mc = 0;
    uint32 i;

    if ((val <= 0) || (val > (int32)MAXMEMSIZE) || ((val & 07777) != 0))
        return SCPE_ARG;
    for (i = val; i < MEMSIZE; i++) mc = mc | M[i];
    if ((mc != 0) && (!get_yn ("Really truncate memory [N]?", FALSE)))
        return SCPE_OK;
    MEMSIZE = val;
    for (i = MEMSIZE; i < MAXMEMSIZE; i++) M[i] = 0;
    return SCPE_OK;
}

/* Change CPU Mode (Normal, Test, Readin) */

t_stat cpu_set_mode (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
    if (val == UNIT_MODE_TEST) {
        mode_tst = 1;
        mode_rdin = 0;
    } else if (val == UNIT_MODE_READIN) {
        mode_tst = 1;
        mode_rdin = 1;
    } else {    /* Normal Mode */
        mode_tst = 0;
        mode_rdin = 0;
    }

    return SCPE_OK;
}


/* Set TX-0 with Extended Instruction Set */

static t_stat cpu_set_ext (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
    sim_printf("Set CPU Extended Mode\n");
    return SCPE_OK;
}

static t_stat cpu_set_noext (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
    sim_printf("Set CPU Non-Extended Mode\n");
    return SCPE_OK;
}

int32 cpu_get_mode (void)
{
    return (cpu_unit.flags & UNIT_EXT_INST);
}



/* Set history */

static t_stat cpu_set_hist (UNIT *uptr, int32 val, CONST char *cptr, void *desc)
{
int32 i, lnt;
t_stat r;

if (cptr == NULL) {
    for (i = 0; i < hst_lnt; i++) hst[i].pc = 0;
    hst_p = 0;
    return SCPE_OK;
    }
lnt = (int32) get_uint (cptr, 10, HIST_MAX, &r);
if ((r != SCPE_OK) || (lnt && (lnt < HIST_MIN))) return SCPE_ARG;
hst_p = 0;
if (hst_lnt) {
    free (hst);
    hst_lnt = 0;
    hst = NULL;
    }
if (lnt) {
    hst = (InstHistory *) calloc (lnt, sizeof (InstHistory));
    if (hst == NULL) return SCPE_MEM;
    hst_lnt = lnt;
    }
return SCPE_OK;
}

/* Show history */

static t_stat cpu_show_hist (FILE *st, UNIT *uptr, int32 val, CONST void *desc)
{
int32 ov, pf, op, k, di, lnt;
const char *cptr = (const char *) desc;
t_stat r;
InstHistory *h;

if (hst_lnt == 0) return SCPE_NOFNC;                    /* enabled? */
if (cptr) {
    lnt = (int32) get_uint (cptr, 10, hst_lnt, &r);
    if ((r != SCPE_OK) || (lnt == 0)) return SCPE_ARG;
    }
else lnt = hst_lnt;
di = hst_p - lnt;                                       /* work forward */
if (di < 0) di = di + hst_lnt;
fprintf (st, "PC      OV AC     IO      PF EA      IR\n\n");
for (k = 0; k < lnt; k++) {                             /* print specified */
    h = &hst[(++di) % hst_lnt];                         /* entry pointer */
    if (h->pc & HIST_PC) {                              /* instruction? */
        ov = (h->ovac >> HIST_V_SHF) & 1;               /* overflow */
        pf = 0;
        op = ((h->ir >> 13) & 037);                     /* get opcode */
        fprintf (st, "%06o  %o  %06o %06o %03o ",
            h->pc & AMASK, ov, h->ovac & DMASK, h->pfio & DMASK, pf);
        if ((op < 032) && (op != 007))                  /* mem ref instr */
            fprintf (st, "%06o  ", h->ea);
        else fprintf (st, "        ");
        sim_eval[0] = h->ir;
        if ((fprint_sym (st, h->pc & AMASK, sim_eval, &cpu_unit, SWMASK ('M'))) > 0)
            fprintf (st, "(undefined) %06o", h->ir);
        else if (op < 030)                              /* mem ref instr */
            fprintf (st, " [%06o]", h->opnd);
        fputc ('\n', st);                               /* end line */
        }                                               /* end else instruction */
    }                                                   /* end for */
return SCPE_OK;
}

#ifdef USE_DISPLAY
#include "display/display.h"      /* prototypes */

/* set "test switches"; from display code */
void
cpu_set_switches(unsigned long v1, unsigned long v2)
{
    /* just what we want; smaller CPUs might want to shift down? */
    TAC = v1 ^ v2;
}

void
cpu_get_switches(unsigned long *p1, unsigned long *p2)
{
    *p1 = TAC;
    *p2 = 0;
}
#endif

t_stat sim_load(FILE *fileref, CONST char *cptr, CONST char *fnam, int flag) {
    uint32 word;
    t_addr j, lo, hi, sz, sz_words;
    CONST char *result;

    if (flag) { /* Dump to file. */
        result = get_range(NULL, cptr, &lo, &hi, 8, 0xFFFF, 0);
        if (result == NULL) return SCPE_ARG;

        for (j = lo; j <= hi; j++) {
            if (sim_fwrite(&j, 4, 1, fileref) == 0) return SCPE_IOERR;
            if (sim_fwrite(&M[j], 4, 1, fileref) == 0) return SCPE_IOERR;
        }
    } else {
        lo = strtotv(cptr, &result, 8) & 0xFFFF;
        sz = sim_fsize(fileref);
        sz_words = MIN (sz, sizeof (M)) / sizeof (word);
        for (j = lo; j < sz_words; j++) {
            if (1 != sim_fread(&word, 4, 1, fileref))
                break;
            M[j] = word;
        }
    }

    sim_printf("%d words %s [%06o - %06o].\n", j - lo, flag ? "dumped" : "loaded", lo, j-1);

    return SCPE_OK;
}

/*
Original Operate-class instruction micro orders for the 1956 TX-0 Instruction Set

      Operate Fields
      --------------
      --1 --- --- --- --- ---   CLL     0.8
      --- 1-- --- --- --- ---   CLR     0.8
      --- -10 --- --- --- ---   IOS     0.8
      --- -11 --- --- --- ---   HLT     1.8
      --- --- 111 --- --- ---   P7H     0.8
      --- --- 110 --- --- ---   P6H     0.8
      --- --- 100 --- --- ---   PNT     0.8
      --- --- 001 --- --- ---   R1C     0.8
      --- --- 011 --- --- ---   R3C     0.8
      --- --- 010 --- --- ---   DIS     0.8
      --- --- --- 10- --- ---   SHR     1.4
      --- --- --- 11- --- ---   CYR     1.4
      --- --- --- 01- --- ---   MLR     1.3
      --- --- --- --1 --- 0--   PEN     1.1
      --- --- --- --0 --- 1--   TAC     1.1
      --- --- --- --- 1-- ---   COM     1.2
      --- --- --- --- -1- ---   PAD     1.4
      --- --- --- --- --1 ---   CRY     1.7
      --- --- --- --- --- -01   AMB     1.2 AC -> MBR
      --- --- --- --- --- -11   TBR     1.2 TBR -> MBR
      --- --- --- --- --- -10   LMB     1.3 LR -> MBR
*/
#define OOPR_CLL            0100000
#define OOPR_CLR            0040000
#define OOPR_IOS            0020000
#define OOPR_HLT            0030000
#define OOPR_IOS_MASK   0007000
#define OOPR_P7H            0007000
#define OOPR_P6H            0006000
#define OOPR_PNT            0004000
#define OOPR_R3C            0003000
#define OOPR_DIS            0002000
#define OOPR_R1C            0001000

#define OOPR_SHF_MASK   0000600
#define OOPR_SHR            0000400
#define OOPR_CYR            0000600
#define OOPR_MLR            0000200

#define OOPR_PEN_MASK   0000104
#define OOPR_PEN        0000100

#define OOPR_TAC_MASK   0000104
#define OOPR_TAC        0000004

#define OOPR_COM        0000040
#define OOPR_PAD        0000020
#define OOPR_CRY        0000010

#define OOPR_AMB_MASK   0000007
#define OOPR_AMB        0000001
#define OOPR_TBR        0000003
#define OOPR_LMB        0000002

t_stat sim_opr_orig(int32 op)
{
    t_stat reason = SCPE_OK;

    if (op & OOPR_CLL) {    /* cll  0.8 Clear the left nine digital positions of the AC */
        AC &= 0000777;
        TRACE_PRINT(ORD_MSG, ("[%06o]: CLL\n", PC-1));
    }
    if (op & OOPR_CLR) {    /* clr  0.8 Clear the right nine digital positions of the AC */
        AC &= 0777000;
        TRACE_PRINT(ORD_MSG, ("[%06o]: CLR\n", PC-1));
    }

/* IOS - In / Out Stop */
    /* Check TTI for character.  If so, put in LR and set LR bit 0. */
    if (iosta & IOS_TTI) {
        int32 rbuf;
        rbuf = tti(0,0,0);
        TRACE_PRINT(IOS_MSG, ("TTI: character received='%c'\n", rbuf &077));
        sim_printf("TTI: character received='%c'\n", rbuf &077);
        LR &= 0266666; /* Clear bits 0,2,5,8,...,17 */

        LR |= SIGN; /* Set bit 0, character available. */
        LR |= ((rbuf & 001) >> 0) << 15;/* bit 2  */
        LR |= ((rbuf & 002) >> 1) << 12;/* bit 5  */
        LR |= ((rbuf & 004) >> 2) << 9; /* bit 8  */
        LR |= ((rbuf & 010) >> 3) << 6; /* bit 11 */
        LR |= ((rbuf & 020) >> 4) << 3; /* bit 14 */
        LR |= ((rbuf & 040) >> 5) << 0; /* bit 17 */
    }



    if ((op & OOPR_HLT) == OOPR_IOS) {   /* I/O  0.8 IOS */
        TRACE_PRINT(IOS_MSG, ("[%06o] I/O Operation\n", PC-1));

        switch (op & OOPR_IOS_MASK) {
        case OOPR_P7H:
        case OOPR_P6H:
            {
                uint32 tmpAC = 0;
                tmpAC |= ((AC & 0000001) >> 0) << 0; /* bit 17 */
                tmpAC |= ((AC & 0000010) >> 3) << 1; /* bit 14 */
                tmpAC |= ((AC & 0000100) >> 6) << 2; /* bit 11 */
                tmpAC |= ((AC & 0001000) >> 9) << 3; /* bit 8 */
                tmpAC |= ((AC & 0010000) >> 12) << 4; /* bit 5 */
                tmpAC |= ((AC & 0100000) >> 15) << 5; /* bit 2 */
                tmpAC &= 0077;
                if ((op & OOPR_IOS_MASK) == OOPR_P7H) {
                    tmpAC |= 0100;  /* Punch 7th hole. */
                    TRACE_PRINT(ERROR_MSG, ("[%06o] Punch 7 holes\n", PC-1));
                } else {
                    TRACE_PRINT(ERROR_MSG, ("[%06o] Punch 6 holes\n", PC-1));
                }
                ptp (0, 0, tmpAC);    /* Punch one character on TTO */
            }
            break;
        case OOPR_PNT:
            {
                uint32 tmpAC = 0;
                tmpAC |= ((AC & 0000001) >> 0) << 0; /* bit 17 */
                tmpAC |= ((AC & 0000010) >> 3) << 1; /* bit 14 */
                tmpAC |= ((AC & 0000100) >> 6) << 2; /* bit 11 */
                tmpAC |= ((AC & 0001000) >> 9) << 3; /* bit 8 */
                tmpAC |= ((AC & 0010000) >> 12) << 4; /* bit 5 */
                tmpAC |= ((AC & 0100000) >> 15) << 5; /* bit 2 */
                tto (0, 0, tmpAC & 077);    /* Print one character on TTO */
            }
            break;
        case OOPR_R3C:
            AC = petr(3, 0, 0);
            break;
        case OOPR_R1C:
            AC &= 0333333; /* Clear bits 0,3,6,9,12,15 */
            AC |= petr(1, 0, 0);
            break;
        case OOPR_DIS:
#ifdef USE_DISPLAY
            LPEN = dpy (AC);  /* Display point on the CRT */
#endif /* USE_DISPLAY */
            break;
        }
    }

/* 1.1 TAC and PEN */
    if ((op & OOPR_PEN_MASK) == OOPR_PEN) {  /* pen  1.1 Read the light pen flip flops 1 and 2 into AC0 and AC1 */
        TRACE_PRINT(IOS_MSG, ("[%06o] Light Pen %01o\n", PC-1, LPEN));
        AC &= AMASK;
        AC |= (LPEN & 1) << 17;
        AC |= (LPEN & 2) << 16;
        AC &= DMASK;
    }

    if ((op & OOPR_TAC_MASK) == OOPR_TAC) {  /* tac  1.1 Insert a one in each digital position of the AC whereever there is a one in the corresponding digital position of the TAC */
        TRACE_PRINT(IOS_MSG, ("[%06o] TAC %06o\n", PC-1, TAC));
        AC |= TAC;
    }

    /* 1.2: COM, AMB, TBR */
    if (op & OOPR_COM) {    /* com  1.2 Complement every digit in the accumulator */
        AC = ~AC;
        inst_ctr.com++;
    }

    switch (op & OOPR_AMB_MASK) {
    case OOPR_AMB:
        inst_ctr.amb++;
        MBR = AC;
        break;
    case OOPR_TBR:
        TRACE_PRINT(IOS_MSG, ("[%06o] TBR %06o\n", PC-1, TBR));
        MBR |= TBR;
        break;
    case OOPR_LMB:
        MBR = LR;
        inst_ctr.lmb++;
        break;
    }

    /* 1.3, 1.4: can these happen together? */
    switch (op & OOPR_SHF_MASK) {
    case OOPR_MLR:
        LR = MBR;
        inst_ctr.mbl++;
        break;
    case OOPR_SHR: /* Shift AC Right, preserve bit 0. */
        {
            int32 bit0;
            bit0 = AC & 0400000;
            AC = AC >> 1;
            AC |= bit0;
            inst_ctr.shr++;
            break;
        }
    case OOPR_CYR:  /* cyr  1.4 Cycle the AC right one digital position (AC17 -> AC0) */
        {
            int32 bit17;
            bit17 = (AC & 1) << 17;
            AC  >>= 1;
            AC |= bit17;
            inst_ctr.cyr++;
        }
        break;
    }

    if (op & OOPR_PAD) {    /* 1.5 Partial Add (XOR): AC = MBR ^ AC */
        TRACE_PRINT(ORD_MSG, ("[%06o] PAD: AC=%06o, MBR=%06o = ", PC-1, AC, MBR));
        AC = AC ^ MBR;
        AC &= DMASK;
        TRACE_PRINT(ORD_MSG, ("%06o\n", AC));
        inst_ctr.pad++;
    }

    if (op & OOPR_CRY) {    /* 1.7 */
        TRACE_PRINT(ORD_MSG, ("[%06o] CRY: AC=%06o, MBR=%06o = ", PC-1, AC, MBR));
        AC = AC ^ MBR;
        AC &= DMASK;
        AC = AC + MBR;
        if (AC > DMASK) {
            AC += 1;
        }
        AC &= DMASK;
        TRACE_PRINT(ORD_MSG, ("%06o\n", AC));
        inst_ctr.cry++;
    }

    if ((op & OOPR_HLT) == OOPR_HLT) {   /* hlt  1.8 Halt the computer */
        TRACE_PRINT(IOS_MSG, ("[%06o] HALT Instruction\n", PC-1));
        reason = STOP_HALT;
    }

    return reason;
}
