/*
 * Copyright 2018 faddenSoft
 *
 * Licensed under the Apache License, Version 2.0 (the "License");
 * you may not use this file except in compliance with the License.
 * You may obtain a copy of the License at
 *
 *     http://www.apache.org/licenses/LICENSE-2.0
 *
 * Unless required by applicable law or agreed to in writing, software
 * distributed under the License is distributed on an "AS IS" BASIS,
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
 * See the License for the specific language governing permissions and
 * limitations under the License.
 */
using System;
using System.Diagnostics;

namespace Asm65 {
    /// <summary>
    /// Operation code definitions for all 65xx-series CPUs.
    /// 
    /// Instances are immutable.
    /// </summary>
    public class OpDef {
        /// <summary>
        /// Effect an instruction has on code flow.
        /// </summary>
        public enum FlowEffect {
            Unknown = 0,
            Branch,             // JMP, BRA, BRL, ... (jump to new address specified in operand)
            Cont,               // LDA, STA, PHP, NOP, ... (always continue to next instruction)
            NoCont,             // RTS, BRK, ... (jump to new address, not specified in operand)
            CallSubroutine,     // JSR, JSL (jump to new address, and also continue to next)
            ConditionalBranch   // BCC, BEQ, ... (jump to new address, or continue to next)
        }

        /// <summary>
        /// Effect of executing an instruction on memory.
        /// </summary>
        public enum MemoryEffect {
            Unknown = 0,
            None,               // e.g. TAX, PEA addr, LDA #imm
            Read,               // e.g. LDA addr
            Write,              // e.g. STA addr
            ReadModifyWrite     // e.g. LSR addr
        }

        /// <summary>
        /// Addressing mode.  This uses the same distinctions as Eyes &amp; Lichty, which for
        /// most purposes has some redundancy (e.g. StackRTI is only ever used with RTI, so
        /// in most cases it could be considered Implied).
        /// 
        /// The mode, combined with the processor flags on the 65816, determines the number
        /// of bytes required by the instruction.
        /// </summary>
        public enum AddressMode : byte {
            Unknown = 0,        // format           bytes (example)
            Abs,                // OP addr          3
            AbsInd,             // OP (addr)        3 (JMP)
            AbsIndLong,         // OP [addr]        3 (JML)
            AbsIndexX,          // OP addr,X        3
            AbsIndexXInd,       // OP (addr,X)      3 (JMP/JSR)
            AbsIndexXLong,      // OP long,X        4
            AbsIndexY,          // OP addr,Y        3
            AbsLong,            // OP long          4
            Acc,                // OP A             1
            BlockMove,          // OP srcb,dstb     3 (MVN/MVP)
            DP,                 // OP dp            2
            DPInd,              // OP (dp)          2
            DPIndIndexY,        // OP (dp),Y        2
            DPIndIndexYLong,    // OP [dp],Y        2
            DPIndLong,          // OP [dp]          2
            DPIndexX,           // OP dp,X          2
            DPIndexXInd,        // OP (dp,X)        2
            DPIndexY,           // OP dp,Y          2
            DPPCRel,            // OP dp,label      3 (BBR/BBS)
            Imm,                // OP #const8       2
            ImmLongA,           // OP #const8/16    2 or 3, depending on 'm' flag
            ImmLongXY,          // OP #const8/16    2 or 3, depending on 'x' flag
            Implied,            // OP               1
            PCRel,              // OP label         2 (BCC, BNE, ...)
            PCRelLong,          // OP label         3 (BRL)
            StackAbs,           // OP addr          3 (PEA), treat as imm const16
            StackDPInd,         // OP (dp)          2 (PEI)
            StackInt,           // OP               2 (BRK, COP)
            StackPCRelLong,     // OP label         3 (PER)
            StackPull,          // OP               1 (PLA, PLX, ...)
            StackPush,          // OP               1 (PHA, PHX, ...)
            StackRTI,           // OP               1 (RTI)
            StackRTL,           // OP               1 (RTL)
            StackRTS,           // OP               1 (RTS)
            StackRel,           // OP sr,S          2
            StackRelIndIndexY,  // OP (sr,S),Y      2
            WDM                 // OP               2?
        }

        /// <summary>
        /// Cycle count modifiers.  This is meant to be bitwise-ORed with the base value.
        /// 
        /// This defines things generally, with some modifiers noted as being specific to
        /// certain CPUs.  The actual instruction definitions may choose to include or
        /// exclude bits according to the CPU being defined.
        /// </summary>
        [FlagsAttribute]
        public enum CycleMod {
            // Data from chapter 19 of Eyes & Lichty.
            OneIfM0                 = 1 << 8,   // +1 if M=0 (16-bit mem/accumulator)
            TwoIfM0                 = 1 << 9,   // +2 if M=0 (16-bit mem/accumulator)
            OneIfX0                 = 1 << 10,  // +1 if X=0 (16-bit index regs)
            OneIfDpNonzero          = 1 << 11,  // +1 if DP reg != 0
            OneIfIndexPage          = 1 << 12,  // +1 if indexing crosses page boundary
            OneIfD1                 = 1 << 13,  // +1 if D=1 (decimal mode) ONLY on 65C02
            OneIfBranchTaken        = 1 << 14,  // +1 if conditional branch taken
            OneIfBranchPage         = 1 << 15,  // +1 if branch crosses page UNLESS '816 native
            OneIfE0                 = 1 << 16,  // +1 if 65816/865816 native mode (E=0)
            OneIf65C02              = 1 << 17,  // +1 if 65C02
            MinusOneIfNoPage        = 1 << 18,  // -1 if 65C02 and no page boundary crossed
            BlockMove               = 1 << 19,  // +7 per byte moved
        }

        /// <summary>
        /// Width disambiguation requirement.
        /// </summary>
        public enum WidthDisambiguation : byte {
            None = 0,
            ForceDirect,    // mainly needed for forward DP label refs in single-pass assemblers
            ForceAbs,
            ForceLong,
            ForceLongMaybe  // add opcode suffix but not operand prefix

            // May want an additional item: "force long if operand suffix specified".  This
            // would let us generate LDAL for assemblers that like to have that made explicit,
            // while avoiding prepending it to operands that are unambiguously long (e.g.
            // $ff1122).  The counter-argument is that the operand prefix is still useful
            // for humans when looking at labels, e.g. "a:FOO" vs. "f:FOO", because the value
            // of the label may not be apparent.
        }


        /// <summary>
        /// Opcode numeric value, e.g. BRK is $00.
        /// </summary>
        public byte Opcode { get; private set; }

        /// <summary>
        /// Addressing mode.  Determines length of instruction (mostly) and decoding of operand.
        /// </summary>
        public AddressMode AddrMode { get; private set; }

        /// <summary>
        /// True if this is an undocumented opcode.
        /// </summary>
        public bool IsUndocumented { get; private set; }

        /// <summary>
        /// Instruction mnemonic, e.g. LDA for Load Accumulator.
        /// </summary>
        public string Mnemonic { get; private set; }

        /// <summary>
        /// Indication of which flags are affected by an instruction.  Unaffected flags
        /// will have the value TriState16.UNSPECIFIED.
        /// 
        /// This is not equivalent to the code flow status flag updater -- this just notes which
        /// flags are modified directly by the instruction.  This is used for display in the
        /// info window and instruction chart, not analysis.
        /// </summary>
        public StatusFlags FlagsAffected { get; private set; }

        /// <summary>
        /// Effect this instruction has on code flow.
        /// </summary>
        public FlowEffect Effect { get; private set; }

        /// <summary>
        /// Effect this instruction has on memory.
        /// </summary>
        /// <remarks>
        /// We don't consider execution to have a memory effect, so "LDA $1000" is Read but
        /// "JMP $1000" is None.  That's because the instruction itself doesn't access the
        /// memory at $1000, it just changes the program counter to point there.
        /// </remarks>
        public MemoryEffect MemEffect {
            get {
                // If we do this a lot, we should probably just go through and set the
                // mem effect to "none" in all the immediate-mode op definitions.
                if (IsImmediate) {
                    return MemoryEffect.None;
                } else {
                    return BaseMemEffect;
                }
            }
        }
        private MemoryEffect BaseMemEffect { get; set; }

        /// <summary>
        /// Cycles required.  The low 8 bits hold the base cycle count, the remaining bits
        /// are defined by the CycleMod enum.
        /// </summary>
        /// <remaks>
        /// This always returns the full set of possible cycle mods.  In most cases you want
        /// the set that has irrelevant mods masked out.  See CpuDef.GetOpCycleMod().
        /// </remaks>
        private int CycDef { get; set; }
        public int Cycles { get { return CycDef & 0xff; } }
        public CycleMod CycleMods { get { return (CycleMod)(CycDef & ~0xff); } }

        /// <summary>
        /// True if this is a numbered bit operation, i.e. BBR/BBS/RMB/SMB on the W65C02.
        /// </summary>
        public bool IsNumberedBitOp { get; private set; }

        /// <summary>
        /// True if the instruction's address mode is a direct page access.
        /// </summary>
        public bool IsDirectPageInstruction {
            get {
                switch (AddrMode) {
                    case AddressMode.DP:
                    case AddressMode.DPInd:
                    case AddressMode.DPIndexX:
                    case AddressMode.DPIndexXInd:
                    case AddressMode.DPIndexY:
                    case AddressMode.DPIndIndexY:
                    case AddressMode.DPIndIndexYLong:
                    case AddressMode.DPIndLong:
                    case AddressMode.StackDPInd:
                        // not currently handling DPPCRel as DP
                        return true;
                    default:
                        return false;
                }
            }
        }

        /// <summary>
        /// True if the instruction's operand is a stack-relative offset.
        /// </summary>
        public bool IsStackRelInstruction {
            get {
                return AddrMode == AddressMode.StackRel ||
                       AddrMode == AddressMode.StackRelIndIndexY;
            }
        }

        /// <summary>
        /// True for instructions that index off of the operand.  The goal is to identify
        /// instructions like "LDA $1234,Y" that may not access the address specified by
        /// their operand.  So "(dp,X)" returns true, but "(dp),Y" does not.
        /// </summary>
        public bool IsIndexedAccessInstruction {
            get {
                return AddrMode == AddressMode.AbsIndexX ||
                       AddrMode == AddressMode.AbsIndexXInd ||
                       AddrMode == AddressMode.AbsIndexY ||
                       AddrMode == AddressMode.DPIndexX ||
                       AddrMode == AddressMode.DPIndexXInd ||
                       AddrMode == AddressMode.DPIndexY;
            }
        }

        /// <summary>
        /// True for instructions that dereference the operand.
        /// </summary>
        public bool IsPointerAccessInstruction {
            get {
                return AddrMode == AddressMode.DPIndexXInd ||
                       AddrMode == AddressMode.DPInd ||
                       AddrMode == AddressMode.DPIndLong ||
                       AddrMode == AddressMode.DPIndIndexY ||
                       AddrMode == AddressMode.DPIndIndexYLong ||
                       // indirect JMP/JSR
                       AddrMode == AddressMode.AbsInd ||
                       AddrMode == AddressMode.AbsIndLong ||
                       AddrMode == AddressMode.AbsIndexXInd;
            }
        }

        /// <summary>
        /// True if this is an absolute-address instruction whose operand is combined with
        /// the Program Bank Register.
        /// </summary>
        /// <remarks>
        /// As noted in Eyes &amp; Lichty, the Absolute addressing mode uses the Data Bank Register
        /// if locating data, or the Program Bank Register if transferring control.  When
        /// we "LDA symbol" we can only care about the 16-bit value because we can't know what B
        /// will hold at run time.  When we "JMP symbol" we can either (1) complain if it's in
        /// a different bank, (2) complain if a bank is specified at all, or (3) just not care.
        /// All three approaches are in use.
        ///
        /// This call is a way to know that the instruction is merged with the PBR rather than
        /// the DBR.
        /// </remarks>
        public bool IsAbsolutePBR {
            get {
                // Too lazy to add new field.  Set for:
                //  JSR abs
                //  JMP abs
                //  JMP (abs,X)
                //  JSR (abs,X)
                return Opcode == 0x20 || Opcode == 0x4c || Opcode == 0x7c || Opcode == 0xfc;
            }
        }

        /// <summary>
        /// True if the operand's width is uniquely determined by the opcode mnemonic, even
        /// if the operation supports operands with varying widths.
        /// 
        /// Certain ops (ADC AND CMP EOR LDA ORA SBC STA) can be direct page, absolute, or
        /// absolute long.  "LDA 0" could mean LDA $00 (A5), LDA $0000 (AD), or LDA $000000 (AF).
        /// Similar ambiguities exist for some indexed modes.  Assemblers generally use the
        /// smallest form, but allow a longer form to be selected by modifying the opcode
        /// (e.g. LDA: and LDAL) or operand (e.g. LDA >0).
        /// 
        /// Most operations that access memory require disambiguation for direct page vs.
        /// absolute.  Generally speaking, if the operand's high byte is empty, disambiguation
        /// is required.
        /// 
        /// The JMP/JML and JSR/JSL mnemonics are commonly used (and, in some assemblers,
        /// required to be used) to avoid the ambiguity.  For these instructions, we want
        /// to avoid modifying the mnemonic, so we set this flag.
        /// </summary>
        private bool IsOperandWidthUnambiguous { get; set; }

        /// <summary>
        /// Flag update delegate, used for code flow analysis.
        /// </summary>
        /// <param name="flags">Current flags, to be modified by delegate.  For conditional
        ///   branches, this value will be used when the branch is not taken.</param>
        /// <param name="immVal">Immediate mode value, if any.  Value may be 0-255
        ///   for an 8-bit operand, or 0-65535 for a 16-bit operand, or -1 if this is
        ///   not an immediate-mode instruction.</param>
        /// <param name="branchTakenFlags">For conditional branches, this is the updated
        ///   value when the branch is taken.</param>
        private delegate StatusFlags FlagUpdater(StatusFlags flags, int immVal,
            ref StatusFlags condBranchTakenFlags);
        private FlagUpdater StatusFlagUpdater { get; set; }

        /// <summary>
        /// Nullary constructor.  Most things are left at default values.
        /// </summary>
        public OpDef() {
            StatusFlagUpdater = FlagUpdater_NoChange;
        }

        /// <summary>
        /// Copy constructor.
        /// </summary>
        /// <param name="src">Object to copy from</param>
        public OpDef(OpDef src) {
            this.Opcode = src.Opcode;
            this.AddrMode = src.AddrMode;
            this.IsUndocumented = src.IsUndocumented;
            this.IsNumberedBitOp = src.IsNumberedBitOp;
            this.Mnemonic = src.Mnemonic;
            this.FlagsAffected = src.FlagsAffected;
            this.Effect = src.Effect;
            this.BaseMemEffect = src.BaseMemEffect;
            this.CycDef = src.CycDef;
            this.IsOperandWidthUnambiguous = src.IsOperandWidthUnambiguous;
            this.StatusFlagUpdater = src.StatusFlagUpdater;
        }

        private static StatusFlags FlagsAffected_V =
            new StatusFlags() { V = 1 };
        private static StatusFlags FlagsAffected_D =
            new StatusFlags() { D = 1 };
        private static StatusFlags FlagsAffected_I =
            new StatusFlags() { I = 1 };
        private static StatusFlags FlagsAffected_Z =
            new StatusFlags() { Z = 1 };
        private static StatusFlags FlagsAffected_C =
            new StatusFlags() { C = 1 };
        private static StatusFlags FlagsAffected_MXCE =
            new StatusFlags() { M = 1, X = 1, C = 1, E = 1 };
        private static StatusFlags FlagsAffected_DI =
            new StatusFlags() { D = 1, I = 1 };
        private static StatusFlags FlagsAffected_NZ =
            new StatusFlags() { N = 1, Z = 1 };
        private static StatusFlags FlagsAffected_NVZ =
            new StatusFlags() { N = 1, V = 1, Z = 1 };
        private static StatusFlags FlagsAffected_NZC =
            new StatusFlags() { N = 1, Z = 1, C = 1 };
        private static StatusFlags FlagsAffected_NVZC =
            new StatusFlags() { N = 1, V = 1, Z = 1, C = 1 };
        private static StatusFlags FlagsAffected_All =
            new StatusFlags() { N = 1, V = 1, M = 1, X = 1, D = 1, I = 1, Z = 1, C = 1 };


        /// <summary>
        /// True if this operation is any type of branch instruction (conditional branch,
        /// unconditional branch/jump, subroutine call).
        /// </summary>
        public bool IsBranchOrSubCall {
            get {
                return Effect == FlowEffect.Branch ||
                    Effect == FlowEffect.ConditionalBranch ||
                    Effect == FlowEffect.CallSubroutine;
            }
        }

        /// <summary>
        /// True if this operation is a subroutine call.
        /// </summary>
        public bool IsSubroutineCall {
            get {
                return Effect == FlowEffect.CallSubroutine;
            }
        }

        /// <summary>
        /// True if the operand is an immediate value, which should be prefixed with '#'.
        /// </summary>
        public bool IsImmediate {
            get {
                return AddrMode == AddressMode.Imm ||
                    AddrMode == AddressMode.ImmLongA ||
                    AddrMode == AddressMode.ImmLongXY;
            }
        }

        /// <summary>
        /// True if the operand is an "extended immediate" value, which includes PEA and MVN/MVP
        /// in addition to Imm/ImmLongA/ImmLongXY.
        /// </summary>
        public bool IsExtendedImmediate {
            get {
                return IsImmediate || AddrMode == AddressMode.StackAbs ||
                    AddrMode == AddressMode.BlockMove;
            }
        }

        /// <summary>
        /// True if the operand's width could be ambiguous.  Generally speaking, assemblers
        /// will use the shortest form possible, so disambiguation is about using a longer
        /// form than may appear to be required.
        /// </summary>
        public bool IsWidthPotentiallyAmbiguous {
            get {
                if (IsOperandWidthUnambiguous) {
                    // JMP has Abs and AbsLong forms, but those are universally distinguished
                    // with unique mnemonics (JMP vs. JML).  We don't want to generate "JMPL"
                    // or "JMP >long".  Ditto for JSR/JSL.
                    return false;
                }
                switch (AddrMode) {
                    case AddressMode.Abs:               // LDA $0000 vs LDA $00
                    case AddressMode.AbsLong:           // LDA $000000 vs LDA $0000/LDA $00
                    case AddressMode.AbsIndexX:         // LDA $0000,X vs LDA $00,X
                    case AddressMode.AbsIndexXLong:     // LDA $000000,X vs LDA $0000,X/LDA $00,X
                        return true;
                    case AddressMode.AbsIndexY:         // LDX $0000,Y vs LDX $00,Y
                        // AbsIndexY is widely used, but DPIndexY is only available for LDX/STX,
                        // and STX doesn't have AbsIndexY.  So this is only ambiguous for LDX.
                        // We want to compare by opcode instance, rather than the byte code
                        // numeric value, to manage different instruction sets.
                        // (This also applies to the undocumented LAX instruction.)
                        if (this == OpLDX_AbsIndexY || this == OpLAX_AbsIndexY) {
                            return true;
                        }
                        return false;
                    default:
                        return false;
                }
            }
        }

        /// <summary>
        /// Get a value that indicates what sort of disambiguation is required.  Only call
        /// this if IsWidthPotentiallyAmbiguous is true.
        /// </summary>
        /// <param name="instrWidth">Instruction width, including opcode.</param>
        /// <param name="operandValue">Operand value, extracted from byte stream.</param>
        /// <returns>Width disambiguation value.</returns>
        public static WidthDisambiguation GetWidthDisambiguation(int instrWidth,
                int operandValue) {
            Debug.Assert(instrWidth > 2 && instrWidth <= 4);   // zero-page ops are not ambiguous
            if (instrWidth == 3 && operandValue < 0x100) {
                return WidthDisambiguation.ForceAbs;
            } else if (instrWidth == 4) {
                if (operandValue < 0x10000) {
                    return WidthDisambiguation.ForceLong;
                } else {
                    // The width disambiguator may be helpful for humans when reading labels
                    // whose value is not immediately apparent.  "LDA a:FOO" vs. "LDA f:FOO"
                    // could be nice.
                    return WidthDisambiguation.ForceLongMaybe;
                }
            } else {
                return WidthDisambiguation.None;
            }
        }

        /// <summary>
        /// Returns the full length of the instruction.  Some 65816 operations use
        /// a different number of bytes in 16-bit mode, so we need to know what the
        /// M/X status flags are.
        /// </summary>
        /// <param name="flags">Current status flags.</param>
        /// <returns>Length, in bytes.</returns>
        public int GetLength(StatusFlags flags) {
            switch (AddrMode) {
                case AddressMode.Unknown:
                case AddressMode.Acc:
                case AddressMode.Implied:
                case AddressMode.StackPull:
                case AddressMode.StackPush:
                case AddressMode.StackRTI:
                case AddressMode.StackRTL:
                case AddressMode.StackRTS:
                    return 1;
                case AddressMode.DP:
                case AddressMode.DPIndexX:
                case AddressMode.DPIndexY:
                case AddressMode.DPIndexXInd:
                case AddressMode.DPInd:
                case AddressMode.DPIndLong:
                case AddressMode.DPIndIndexY:
                case AddressMode.DPIndIndexYLong:
                case AddressMode.Imm:
                case AddressMode.PCRel:
                case AddressMode.StackDPInd:
                case AddressMode.StackInt:
                case AddressMode.StackRel:
                case AddressMode.StackRelIndIndexY:
                case AddressMode.WDM:
                    return 2;
                case AddressMode.Abs:
                case AddressMode.AbsIndexX:
                case AddressMode.AbsIndexY:
                case AddressMode.AbsIndexXInd:
                case AddressMode.AbsInd:
                case AddressMode.AbsIndLong:
                case AddressMode.BlockMove:
                case AddressMode.DPPCRel:
                case AddressMode.PCRelLong:
                case AddressMode.StackAbs:
                case AddressMode.StackPCRelLong:
                    return 3;

                case AddressMode.AbsLong:
                case AddressMode.AbsIndexXLong:
                    return 4;

                case AddressMode.ImmLongA:
                    bool shortM = flags.IsShortM;
                    return shortM ? 2 : 3;
                case AddressMode.ImmLongXY:
                    bool shortX = flags.IsShortX;
                    return shortX ? 2 : 3;

                default:
                    Debug.Assert(false, "Unknown address mode " + AddrMode);
                    return -1;
            }
        }


        /// <summary>
        /// Return value from branch evaluation functions.
        /// </summary>
        public enum BranchTaken { Indeterminate = 0, Never, Always };

        /// <summary>
        /// Determines whether a branch is always taken, never taken, or is indeterminate
        /// (i.e. it's taken some of the time but not taken others).
        /// </summary>
        /// <param name="flagVal">Processor status flag value.</param>
        /// <param name="branchVal">Bit value corresponding to branch-taken.</param>
        /// <returns>Branch taken indication.</returns>
        private static BranchTaken FlagToBT(int flagVal, int branchVal) {
            if (flagVal == TriState16.INDETERMINATE) {
                return BranchTaken.Indeterminate;
            } else if (flagVal == TriState16.UNSPECIFIED) {
                // should never happen
                Debug.Assert(false);
                return BranchTaken.Indeterminate;
            } else if (flagVal == branchVal) {
                return BranchTaken.Always;
            } else {
                return BranchTaken.Never;
            }
        }

        /// <summary>
        /// Determines if a conditional branch is always taken, based on the status flags.  Only
        /// call here for branch instructions.
        /// </summary>
        /// <param name="op">Conditional branch instruction.</param>
        /// <param name="flags">Processor status flags.</param>
        /// <returns>Whether the branch is sometimes, always, or never taken.</returns>
        public static BranchTaken IsBranchTaken(OpDef op, StatusFlags flags) {
            // Could add a delegate to every OpDef, but that seems silly.
            switch (op.Opcode) {
                case 0x10:      // BPL
                    return FlagToBT(flags.N, 0);
                case 0x30:      // BMI
                    return FlagToBT(flags.N, 1);
                case 0x50:      // BVC
                    return FlagToBT(flags.V, 0);
                case 0x70:      // BVS
                    return FlagToBT(flags.V, 1);
                case 0x90:      // BCC
                    return FlagToBT(flags.C, 0);
                case 0xb0:      // BCS
                    return FlagToBT(flags.C, 1);
                case 0xd0:      // BNE
                    return FlagToBT(flags.Z, 0);
                case 0xf0:      // BEQ
                    return FlagToBT(flags.Z, 1);
                default:
                    if ((op.Opcode & 0x0f) == 0x0f) {
                        // assume W65C02 BBR/BBS
                        return BranchTaken.Indeterminate;
                    }
                    // Not a conditional branch.
                    throw new Exception("Not a conditional branch");
            }
        }

        /// <summary>
        /// Gets the raw operand value.
        /// </summary>
        /// <param name="data">65xx code.</param>
        /// <param name="offset">Offset of opcode.</param>
        /// <param name="flags">Current status flags (need M/X).</param>
        /// <returns>Operand value, or -1 if the instruction doesn't have an operand.</returns>
        public int GetOperand(byte[] data, int offset, StatusFlags flags) {
            switch (GetLength(flags)) {
                case 1:
                    return -1;
                case 2:
                    return data[offset + 1];
                case 3:
                    return (data[offset + 2] << 8) | data[offset + 1];
                case 4:
                    return (data[offset + 3] << 16) | (data[offset + 2] << 8) | data[offset + 1];
                default:
                    return -1;
            }
        }

        /// <summary>
        /// Computes the changes the instruction will have on the status flags.
        /// </summary>
        /// <param name="curFlags">Current status flags. This is used as the initial value
        ///   for the return values, and determines whether 65816 immediate operands are
        ///   treated as 8- or 16-bit.</param>
        /// <param name="data">65xx data stream.</param>
        /// <param name="offset">Offset of opcode.</param>
        /// <param name="newFlags">Effect this instruction has on flags.</param>
        /// <param name="condBranchTakenFlags">Effect this conditional branch instruction
        ///   has on flags when the branch is taken.  If this is not a conditional branch,
        ///   the value can be ignored.</param>
        public void ComputeFlagChanges(StatusFlags curFlags, byte[] data, int offset,
                out StatusFlags newFlags, out StatusFlags condBranchTakenFlags) {
            int immVal = -1;

            switch (AddrMode) {
                case AddressMode.Imm:
                case AddressMode.ImmLongA:
                case AddressMode.ImmLongXY:
                    if (GetLength(curFlags) == 2) {
                        // 8-bit operand
                        immVal = data[offset + 1];
                    } else {
                        // 16-bit operand; make as such by negating it
                        // (if it's zero, 8 vs. 16 doesn't matter)
                        immVal = -((data[offset + 2]) << 16 | data[offset + 1]);
                    }
                    break;
                default:
                    break;
            }

            condBranchTakenFlags = curFlags;
            // Invoke the flag update delegate.
            newFlags = StatusFlagUpdater(curFlags, immVal, ref condBranchTakenFlags);

            // TODO(maybe): there are some constraints we can impose: if Z=1 then
            //  N=0, and if N=1 then Z=0.  I'm not sure this is actually useful though.
        }

        private static StatusFlags FlagUpdater_NoChange(StatusFlags flags, int immVal,
                ref StatusFlags condBranchTakenFlags) {
            return flags;
        }
        private static StatusFlags FlagUpdater_Subroutine(StatusFlags flags, int immVal,
                ref StatusFlags condBranchTakenFlags) {
            // The correct way to do this is to merge the flags from all code
            // that RTS/RTLs back to here, but that's generally hard to do, especially
            // since this will often be used to call into external code.  The easiest
            // thing to do is scramble CZVN and leave IDXM alone.
            //
            // We also use this for BRK, because it either halts execution, in which
            // case the flags don't matter, or it's performing some sort of special action
            // (e.g. Apple /// SOS call) and the flags are altered.
            return FlagUpdater_NVZC(flags, immVal, ref condBranchTakenFlags);
        }
        private static StatusFlags FlagUpdater_Z(StatusFlags flags, int immVal,
                ref StatusFlags condBranchTakenFlags) {
            flags.Z = TriState16.INDETERMINATE;
            return flags;
        }
        private static StatusFlags FlagUpdater_C(StatusFlags flags, int immVal,
                ref StatusFlags condBranchTakenFlags) {
            flags.C = TriState16.INDETERMINATE;
            return flags;
        }
        private static StatusFlags FlagUpdater_NZ(StatusFlags flags, int immVal,
                ref StatusFlags condBranchTakenFlags) {
            flags.Z = flags.N = TriState16.INDETERMINATE;
            return flags;
        }
        private static StatusFlags FlagUpdater_NVZ(StatusFlags flags, int immVal,
                ref StatusFlags condBranchTakenFlags) {
            flags.Z = flags.V = flags.N = TriState16.INDETERMINATE;
            return flags;
        }
        private static StatusFlags FlagUpdater_NZC(StatusFlags flags, int immVal,
                ref StatusFlags condBranchTakenFlags) {
            flags.C = flags.Z = flags.N = TriState16.INDETERMINATE;
            return flags;
        }
        private static StatusFlags FlagUpdater_NVZC(StatusFlags flags, int immVal,
                ref StatusFlags condBranchTakenFlags) {
            flags.C = flags.Z = flags.V = flags.N = TriState16.INDETERMINATE;
            return flags;
        }
        private static StatusFlags FlagUpdater_LoadImm(StatusFlags flags, int immVal,
                ref StatusFlags condBranchTakenFlags) {
            // We can set the N/Z flags based on the value loaded.
            flags.Z = (immVal != 0) ? 0 : 1;
            if (immVal >= 0) {
                // 8-bit operand
                flags.N = (immVal >> 7) & 0x01;
            } else {
                // 16-bit operand
                immVal = -immVal;
                flags.N = (immVal >> 15) & 0x01;
            }
            return flags;
        }
        private static StatusFlags FlagUpdater_ANDImm(StatusFlags flags, int immVal,
                ref StatusFlags condBranchTakenFlags) {
            // If we LDA #$81, LDX #$00, AND #$01, Z flag is clear (for us, Z=unknown).
            // AND #00 --> Z=1, else Z=unknown
            // AND #7f --> N=0, else N=unknown
            if (immVal == 0) {
                flags.Z = 1;    // acc is now zero
            } else {
                flags.Z = TriState16.INDETERMINATE; // may or may not be zero
            }
            bool hiBitClear;
            if (immVal >= 0) {
                // 8-bit operand
                hiBitClear = ((immVal >> 7) & 0x01) == 0;
            } else {
                // 16-bit operand
                immVal = -immVal;
                hiBitClear = ((immVal >> 15) & 0x01) == 0;
            }
            if (hiBitClear) {
                flags.N = 0;
            } else {
                flags.N = TriState16.INDETERMINATE;
            }
            return flags;
        }
        private static StatusFlags FlagUpdater_ORAImm(StatusFlags flags, int immVal,
                ref StatusFlags condBranchTakenFlags) {
            // If we LDA #$80, LDX #$00, ORA #$00, the N flag will be set (for us, N=unknown).
            // ORA #00 --> Z=unknown, else Z=0
            // ORA #80 --> N=1, else N=unknown
            if (immVal != 0) {
                flags.Z = 0;    // acc is now nonzero
            } else {
                flags.Z = TriState16.INDETERMINATE; // may or may not be zero
            }
            bool hiBitSet;
            if (immVal >= 0) {
                // 8-bit operand
                hiBitSet = ((immVal >> 7) & 0x01) != 0;
            } else {
                // 16-bit operand
                immVal = -immVal;
                hiBitSet = ((immVal >> 15) & 0x01) != 0;
            }
            if (hiBitSet) {
                flags.N = 1;
            } else {
                flags.N = TriState16.INDETERMINATE;
            }
            return flags;
        }
        private static StatusFlags FlagUpdater_LSR(StatusFlags flags, int immVal,
                ref StatusFlags condBranchTakenFlags) {
            flags.C = flags.Z = TriState16.INDETERMINATE;
            flags.N = 0;    // always shifts 0 into high bit
            return flags;
        }
        private static StatusFlags FlagUpdater_ROL(StatusFlags flags, int immVal,
                ref StatusFlags condBranchTakenFlags) {
            // In immediate mode we effectively rotate the N flag into C, but we can't know
            // whether the flags were set for the value in the acc, so that doesn't help us.
            // Ditto for the Z flag, which we could otherwise combine with the C flag in
            // certain situations.
            //
            // if carry is set, set Z=0 (N/C indeterminate);
            // otherwise set Z/N=indeterminate
            if (flags.C == 1) {
                flags.Z = 0;
                flags.C = flags.N = TriState16.INDETERMINATE;
            } else {
                flags.C = flags.Z = flags.N = TriState16.INDETERMINATE;
            }
            return flags;
        }
        private static StatusFlags FlagUpdater_ROR(StatusFlags flags, int immVal,
                ref StatusFlags condBranchTakenFlags) {
            // if carry is set, set Z=0 and N=1 (C indeterminate);
            // if carry is clear, set N=0 (Z/C indeterminate);
            // otherwise, Z/N/C=indeterminate
            if (flags.C == 1) {
                flags.Z = 0;
                flags.N = 1;
                flags.C = TriState16.INDETERMINATE;
            } else if (flags.C == 0) {
                flags.N = 0;
                flags.Z = flags.C = TriState16.INDETERMINATE;
            } else {
                flags.C = flags.Z = flags.N = TriState16.INDETERMINATE;
            }
            return flags;
        }
        private static StatusFlags FlagUpdater_PLP(StatusFlags flags, int immVal,
                ref StatusFlags condBranchTakenFlags) {
            // All flags are unknown. The caller may be able to match with a previous
            // PHP to get something less fuzzy.
            flags.C = flags.Z = flags.I = flags.D = flags.X = flags.M = flags.V = flags.N =
                TriState16.INDETERMINATE;
            return flags;
        }
        private static StatusFlags FlagUpdater_REP(StatusFlags flags, int immVal,
                ref StatusFlags condBranchTakenFlags) {
            if ((immVal & (1 << (int)StatusFlags.FlagBits.C)) != 0) {
                flags.C = 0;
            }
            if ((immVal & (1 << (int)StatusFlags.FlagBits.Z)) != 0) {
                flags.Z = 0;
            }
            if ((immVal & (1 << (int)StatusFlags.FlagBits.I)) != 0) {
                flags.I = 0;
            }
            if ((immVal & (1 << (int)StatusFlags.FlagBits.D)) != 0) {
                flags.D = 0;
            }
            if ((immVal & (1 << (int)StatusFlags.FlagBits.X)) != 0) {
                flags.X = 0;
            }
            if ((immVal & (1 << (int)StatusFlags.FlagBits.M)) != 0) {
                flags.M = 0;
            }
            if ((immVal & (1 << (int)StatusFlags.FlagBits.V)) != 0) {
                flags.V = 0;
            }
            if ((immVal & (1 << (int)StatusFlags.FlagBits.N)) != 0) {
                flags.N = 0;
            }
            return flags;
        }
        private static StatusFlags FlagUpdater_SEP(StatusFlags flags, int immVal,
                ref StatusFlags condBranchTakenFlags) {
            if ((immVal & (1 << (int)StatusFlags.FlagBits.C)) != 0) {
                flags.C = 1;
            }
            if ((immVal & (1 << (int)StatusFlags.FlagBits.Z)) != 0) {
                flags.Z = 1;
            }
            if ((immVal & (1 << (int)StatusFlags.FlagBits.I)) != 0) {
                flags.I = 1;
            }
            if ((immVal & (1 << (int)StatusFlags.FlagBits.D)) != 0) {
                flags.D = 1;
            }
            if ((immVal & (1 << (int)StatusFlags.FlagBits.X)) != 0) {
                flags.X = 1;
            }
            if ((immVal & (1 << (int)StatusFlags.FlagBits.M)) != 0) {
                flags.M = 1;
            }
            if ((immVal & (1 << (int)StatusFlags.FlagBits.V)) != 0) {
                flags.V = 1;
            }
            if ((immVal & (1 << (int)StatusFlags.FlagBits.N)) != 0) {
                flags.N = 1;
            }
            return flags;
        }
        private static StatusFlags FlagUpdater_XCE(StatusFlags flags, int immVal,
                ref StatusFlags condBranchTakenFlags) {
            // We want the 'E' flag to always have a definite value.  If we don't know
            // what's in the carry flag, guess it's a return to emulation.
            StatusFlags newFlags = flags;
            if (flags.C == 0) {
                // transition to native
                newFlags.E = 0;
                newFlags.X = newFlags.M = 1;
            } else /*C==1 or C==?*/ {
                // transition to emulation
                // The registers will be treated as short by the CPU, ignoring M/X, but
                // the assembler won't generally know that.  Set the flags to definite
                // values here so we generate the necessary directives.
                newFlags.E = 1;
                newFlags.X = newFlags.M = 1;
            }
            newFlags.C = flags.E;
            return newFlags;
        }


        // ======================================================================================
        // Base operation definitions, one per op.
        //

        private static OpDef OpUnknown = new OpDef() {
            Mnemonic = OpName.Unknown,
            Effect = FlowEffect.Cont
        };
        private static OpDef OpADC = new OpDef() {
            Mnemonic = OpName.ADC,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Read,
            FlagsAffected = FlagsAffected_NVZC,
            StatusFlagUpdater = FlagUpdater_NVZC
            // We can assert C=0 after ADC #$00, but only if C=0 on entry.  Doesn't seem useful.
        };
        private static OpDef OpAND = new OpDef() {
            Mnemonic = OpName.AND,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Read,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ  // special handling for imm
        };
        private static OpDef OpASL = new OpDef() {
            Mnemonic = OpName.ASL,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.ReadModifyWrite,
            FlagsAffected = FlagsAffected_NZC,
            StatusFlagUpdater = FlagUpdater_NZC
        };
        private static OpDef OpBCC = new OpDef() {
            Mnemonic = OpName.BCC,
            Effect = FlowEffect.ConditionalBranch,
            BaseMemEffect = MemoryEffect.None,
            StatusFlagUpdater = delegate(StatusFlags flags, int immVal,
                    ref StatusFlags condBranchTakenFlags) {
                condBranchTakenFlags.C = 0;
                flags.C = 1;
                return flags;
            }
        };
        private static OpDef OpBCS = new OpDef() {
            Mnemonic = OpName.BCS,
            Effect = FlowEffect.ConditionalBranch,
            BaseMemEffect = MemoryEffect.None,
            StatusFlagUpdater = delegate (StatusFlags flags, int immVal,
                    ref StatusFlags condBranchTakenFlags) {
                condBranchTakenFlags.C = 1;
                flags.C = 0;
                return flags;
            }
        };
        private static OpDef OpBEQ = new OpDef() {
            Mnemonic = OpName.BEQ,
            Effect = FlowEffect.ConditionalBranch,
            BaseMemEffect = MemoryEffect.None,
            StatusFlagUpdater = delegate (StatusFlags flags, int immVal,
                    ref StatusFlags condBranchTakenFlags) {
                condBranchTakenFlags.Z = 1;
                flags.Z = 0;
                return flags;
            }
        };
        private static OpDef OpBIT = new OpDef() {
            Mnemonic = OpName.BIT,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NVZ,  // special handling for imm
            StatusFlagUpdater = FlagUpdater_NVZ // special handling for imm
        };
        private static OpDef OpBMI = new OpDef() {
            Mnemonic = OpName.BMI,
            Effect = FlowEffect.ConditionalBranch,
            BaseMemEffect = MemoryEffect.None,
            StatusFlagUpdater = delegate (StatusFlags flags, int immVal,
                    ref StatusFlags condBranchTakenFlags) {
                // It's potentially useful to update Z here and in BPL.  For example, if the
                // branch succeeds, it's likely that whatever set N=1 also set Z=0.  However,
                // you can set Z independently with TRB/TSB and BIT #imm (and of course PHA/PLP).
                // These things are rare, but its best to stick with what we know must be true.
                condBranchTakenFlags.N = 1;
                flags.N = 0;
                return flags;
            }
        };
        private static OpDef OpBNE = new OpDef() {
            Mnemonic = OpName.BNE,
            Effect = FlowEffect.ConditionalBranch,
            BaseMemEffect = MemoryEffect.None,
            StatusFlagUpdater = delegate (StatusFlags flags, int immVal,
                    ref StatusFlags condBranchTakenFlags) {
                condBranchTakenFlags.Z = 0;
                flags.Z = 1;
                return flags;
            }
        };
        private static OpDef OpBPL = new OpDef() {
            Mnemonic = OpName.BPL,
            Effect = FlowEffect.ConditionalBranch,
            BaseMemEffect = MemoryEffect.None,
            StatusFlagUpdater = delegate (StatusFlags flags, int immVal,
                    ref StatusFlags condBranchTakenFlags) {
                condBranchTakenFlags.N = 0;
                flags.N = 1;
                return flags;
            }
        };
        private static OpDef OpBRA = new OpDef() {
            Mnemonic = OpName.BRA,
            Effect = FlowEffect.Branch,
            BaseMemEffect = MemoryEffect.None
        };
        private static OpDef OpBRK = new OpDef() {
            Mnemonic = OpName.BRK,
            Effect = FlowEffect.NoCont,
            BaseMemEffect = MemoryEffect.None,
            // On 6502 this doesn't clear the 'D' flag.  Easier to explain that in text than
            // create a separate definition.
            //
            // The processor status flags have the 'B' flag set on 6502/65C02/65816 emu mode
            // when pushed onto the stack, but it doesn't affect the actual processor status
            // flag in the code executed next.
            FlagsAffected = FlagsAffected_DI,
            // We don't try to track execution into the software break routine, but we do
            // handle operating system calls that return with the flags altered.  So if BRK
            // does continue, we want to mark NVZC as indeterminate, just like we would for
            // a general subroutine call.
            StatusFlagUpdater = FlagUpdater_Subroutine
        };
        private static OpDef OpBRL = new OpDef() {
            Mnemonic = OpName.BRL,
            Effect = FlowEffect.Branch,
            BaseMemEffect = MemoryEffect.None
        };
        private static OpDef OpBVC = new OpDef() {
            Mnemonic = OpName.BVC,
            Effect = FlowEffect.ConditionalBranch,
            BaseMemEffect = MemoryEffect.None,
            StatusFlagUpdater = delegate (StatusFlags flags, int immVal,
                    ref StatusFlags condBranchTakenFlags) {
                condBranchTakenFlags.V = 0;
                flags.V = 1;
                return flags;
            }
        };
        private static OpDef OpBVS = new OpDef() {
            Mnemonic = OpName.BVS,
            Effect = FlowEffect.ConditionalBranch,
            BaseMemEffect = MemoryEffect.None,
            StatusFlagUpdater = delegate (StatusFlags flags, int immVal,
                    ref StatusFlags condBranchTakenFlags) {
                condBranchTakenFlags.V = 1;
                flags.V = 0;
                return flags;
            }
        };
        private static OpDef OpCLC = new OpDef() {
            Mnemonic = OpName.CLC,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_C,
            StatusFlagUpdater = delegate (StatusFlags flags, int immVal,
                    ref StatusFlags condBranchTakenFlags) {
                flags.C = 0;
                return flags;
            }
        };
        private static OpDef OpCLD = new OpDef() {
            Mnemonic = OpName.CLD,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_D,
            StatusFlagUpdater = delegate (StatusFlags flags, int immVal,
                    ref StatusFlags condBranchTakenFlags) {
                flags.D = 0;
                return flags;
            }
        };
        private static OpDef OpCLI = new OpDef() {
            Mnemonic = OpName.CLI,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_I,
            StatusFlagUpdater = delegate (StatusFlags flags, int immVal,
                    ref StatusFlags condBranchTakenFlags) {
                flags.I = 0;
                return flags;
            }
        };
        private static OpDef OpCLV = new OpDef() {
            Mnemonic = OpName.CLV,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_V,
            StatusFlagUpdater = delegate (StatusFlags flags, int immVal,
                    ref StatusFlags condBranchTakenFlags) {
                flags.V = 0;
                return flags;
            }
        };
        private static OpDef OpCMP = new OpDef() {
            Mnemonic = OpName.CMP,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Read,
            FlagsAffected = FlagsAffected_NZC,
            StatusFlagUpdater = FlagUpdater_NZC
        };
        private static OpDef OpCOP = new OpDef() {
            Mnemonic = OpName.COP,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_DI
        };
        private static OpDef OpCPX = new OpDef() {
            Mnemonic = OpName.CPX,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Read,
            FlagsAffected = FlagsAffected_NZC,
            StatusFlagUpdater = FlagUpdater_NZC
        };
        private static OpDef OpCPY = new OpDef() {
            Mnemonic = OpName.CPY,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Read,
            FlagsAffected = FlagsAffected_NZC,
            StatusFlagUpdater = FlagUpdater_NZC
        };
        private static OpDef OpDEC = new OpDef() {
            Mnemonic = OpName.DEC,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.ReadModifyWrite,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpDEX = new OpDef() {
            Mnemonic = OpName.DEX,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpDEY = new OpDef() {
            Mnemonic = OpName.DEY,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpEOR = new OpDef() {
            Mnemonic = OpName.EOR,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Read,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpINC = new OpDef() {
            Mnemonic = OpName.INC,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.ReadModifyWrite,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpINX = new OpDef() {
            Mnemonic = OpName.INX,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpINY = new OpDef() {
            Mnemonic = OpName.INY,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpJML = new OpDef() {
            Mnemonic = OpName.JML,      // technically JMP with long operand, but JML is convention
            Effect = FlowEffect.Branch,
            BaseMemEffect = MemoryEffect.None,
            IsOperandWidthUnambiguous = true,
        };
        private static OpDef OpJMP = new OpDef() {
            Mnemonic = OpName.JMP,
            Effect = FlowEffect.Branch,
            BaseMemEffect = MemoryEffect.None,
            IsOperandWidthUnambiguous = true
        };
        private static OpDef OpJSL = new OpDef() {
            Mnemonic = OpName.JSL,      // technically JSR with long operand, but JSL is convention
            Effect = FlowEffect.CallSubroutine,
            BaseMemEffect = MemoryEffect.None,
            IsOperandWidthUnambiguous = true,
            StatusFlagUpdater = FlagUpdater_Subroutine
        };
        private static OpDef OpJSR = new OpDef() {
            Mnemonic = OpName.JSR,
            Effect = FlowEffect.CallSubroutine,
            BaseMemEffect = MemoryEffect.None,
            IsOperandWidthUnambiguous = true,
            StatusFlagUpdater = FlagUpdater_Subroutine
        };
        private static OpDef OpLDA = new OpDef() {
            Mnemonic = OpName.LDA,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Read,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ  // special handling for imm
        };
        private static OpDef OpLDX = new OpDef() {
            Mnemonic = OpName.LDX,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Read,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ  // special handling for imm
        };
        private static OpDef OpLDY = new OpDef() {
            Mnemonic = OpName.LDY,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Read,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ  // special handling for imm
        };
        private static OpDef OpLSR = new OpDef() {
            Mnemonic = OpName.LSR,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.ReadModifyWrite,
            FlagsAffected = FlagsAffected_NZC,
            StatusFlagUpdater = FlagUpdater_LSR
        };
        private static OpDef OpMVN = new OpDef() {
            Mnemonic = OpName.MVN,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None       // not quite right, but what is?
        };
        private static OpDef OpMVP = new OpDef() {
            Mnemonic = OpName.MVP,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None
        };
        private static OpDef OpNOP = new OpDef() {
            Mnemonic = OpName.NOP,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None
        };
        private static OpDef OpORA = new OpDef() {
            Mnemonic = OpName.ORA,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Read,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ  // special handling for imm
        };
        private static OpDef OpPEA = new OpDef() {
            Mnemonic = OpName.PEA,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None
        };
        private static OpDef OpPEI = new OpDef() {
            Mnemonic = OpName.PEI,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Read
        };
        private static OpDef OpPER = new OpDef() {
            Mnemonic = OpName.PER,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None
        };
        private static OpDef OpPHA = new OpDef() {
            Mnemonic = OpName.PHA,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None
        };
        private static OpDef OpPHB = new OpDef() {
            Mnemonic = OpName.PHB,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None
        };
        private static OpDef OpPHD = new OpDef() {
            Mnemonic = OpName.PHD,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None
        };
        private static OpDef OpPHK = new OpDef() {
            Mnemonic = OpName.PHK,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None
        };
        private static OpDef OpPHP = new OpDef() {
            Mnemonic = OpName.PHP,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None
        };
        private static OpDef OpPHX = new OpDef() {
            Mnemonic = OpName.PHX,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None
        };
        private static OpDef OpPHY = new OpDef() {
            Mnemonic = OpName.PHY,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None
        };
        private static OpDef OpPLA = new OpDef() {
            Mnemonic = OpName.PLA,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpPLB = new OpDef() {
            Mnemonic = OpName.PLB,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpPLD = new OpDef() {
            Mnemonic = OpName.PLD,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpPLP = new OpDef() {
            Mnemonic = OpName.PLP,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_All,
            StatusFlagUpdater = FlagUpdater_PLP
        };
        private static OpDef OpPLX = new OpDef() {
            Mnemonic = OpName.PLX,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpPLY = new OpDef() {
            Mnemonic = OpName.PLY,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpREP = new OpDef() {
            Mnemonic = OpName.REP,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_All,
            StatusFlagUpdater = FlagUpdater_REP
        };
        private static OpDef OpROL = new OpDef() {
            Mnemonic = OpName.ROL,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.ReadModifyWrite,
            FlagsAffected = FlagsAffected_NZC,
            StatusFlagUpdater = FlagUpdater_ROL
        };
        private static OpDef OpROR = new OpDef() {
            Mnemonic = OpName.ROR,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.ReadModifyWrite,
            FlagsAffected = FlagsAffected_NZC,
            StatusFlagUpdater = FlagUpdater_ROR
        };
        private static OpDef OpRTI = new OpDef() {
            Mnemonic = OpName.RTI,
            Effect = FlowEffect.NoCont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_All
        };
        private static OpDef OpRTL = new OpDef() {
            Mnemonic = OpName.RTL,
            Effect = FlowEffect.NoCont,
            BaseMemEffect = MemoryEffect.None,
        };
        private static OpDef OpRTS = new OpDef() {
            Mnemonic = OpName.RTS,
            Effect = FlowEffect.NoCont,
            BaseMemEffect = MemoryEffect.None,
        };
        private static OpDef OpSBC = new OpDef() {
            Mnemonic = OpName.SBC,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Read,
            FlagsAffected = FlagsAffected_NVZC,
            StatusFlagUpdater = FlagUpdater_NVZC
        };
        private static OpDef OpSEC = new OpDef() {
            Mnemonic = OpName.SEC,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_C,
            StatusFlagUpdater = delegate (StatusFlags flags, int immVal,
                    ref StatusFlags condBranchTakenFlags) {
                flags.C = 1;
                return flags;
            }
        };
        private static OpDef OpSED = new OpDef() {
            Mnemonic = OpName.SED,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_D,
            StatusFlagUpdater = delegate (StatusFlags flags, int immVal,
                    ref StatusFlags condBranchTakenFlags) {
                flags.D = 1;
                return flags;
            }
        };
        private static OpDef OpSEI = new OpDef() {
            Mnemonic = OpName.SEI,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_I,
            StatusFlagUpdater = delegate (StatusFlags flags, int immVal,
                    ref StatusFlags condBranchTakenFlags) {
                flags.I = 1;
                return flags;
            }
        };
        private static OpDef OpSEP = new OpDef() {
            Mnemonic = OpName.SEP,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_All,
            StatusFlagUpdater = FlagUpdater_SEP
        };
        private static OpDef OpSTA = new OpDef() {
            Mnemonic = OpName.STA,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Write,
        };
        private static OpDef OpSTP = new OpDef() {
            Mnemonic = OpName.STP,
            Effect = FlowEffect.NoCont,
            BaseMemEffect = MemoryEffect.None,
        };
        private static OpDef OpSTX = new OpDef() {
            Mnemonic = OpName.STX,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Write,
        };
        private static OpDef OpSTY = new OpDef() {
            Mnemonic = OpName.STY,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Write,
        };
        private static OpDef OpSTZ = new OpDef() {
            Mnemonic = OpName.STZ,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Write,
        };
        private static OpDef OpTAX = new OpDef() {
            Mnemonic = OpName.TAX,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpTAY = new OpDef() {
            Mnemonic = OpName.TAY,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpTCD = new OpDef() {
            Mnemonic = OpName.TCD,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpTCS = new OpDef() {
            Mnemonic = OpName.TCS,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None
        };
        private static OpDef OpTDC = new OpDef() {
            Mnemonic = OpName.TDC,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpTRB = new OpDef() {
            Mnemonic = OpName.TRB,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.ReadModifyWrite,
            FlagsAffected = FlagsAffected_Z,
            StatusFlagUpdater = FlagUpdater_Z
        };
        private static OpDef OpTSB = new OpDef() {
            Mnemonic = OpName.TSB,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.ReadModifyWrite,
            FlagsAffected = FlagsAffected_Z,
            StatusFlagUpdater = FlagUpdater_Z
        };
        private static OpDef OpTSC = new OpDef() {
            Mnemonic = OpName.TSC,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpTSX = new OpDef() {
            Mnemonic = OpName.TSX,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpTXA = new OpDef() {
            Mnemonic = OpName.TXA,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpTXS = new OpDef() {
            Mnemonic = OpName.TXS,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None
        };
        private static OpDef OpTXY = new OpDef() {
            Mnemonic = OpName.TXY,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpTYA = new OpDef() {
            Mnemonic = OpName.TYA,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpTYX = new OpDef() {
            Mnemonic = OpName.TYX,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpWAI = new OpDef() {
            Mnemonic = OpName.WAI,
            Effect = FlowEffect.Cont,   // when I=1 (interrupts disabled), continues on interrupt
            BaseMemEffect = MemoryEffect.None
        };
        private static OpDef OpWDM = new OpDef() {
            Mnemonic = OpName.WDM,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None
        };
        private static OpDef OpXBA = new OpDef() {
            Mnemonic = OpName.XBA,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpXCE = new OpDef() {
            Mnemonic = OpName.XCE,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_MXCE,
            StatusFlagUpdater = FlagUpdater_XCE
        };


        #region 65816 Instructions

        // ======================================================================================
        // Operation + address mode definitions.
        //
        // It's possible for more than one definition to have the same opcode number.  The
        // 6502 only supports 8-bit LDA imm, while the 65816 version can be 8 or 16.  There
        // are minor differences in behavior for some opcodes on different CPUs.
        //
        // Significantly, the "invalid" 6502 ops overlap with official ops defined on later CPUs.
        //

        public static readonly OpDef OpInvalid = new OpDef(OpUnknown) {
            AddrMode = AddressMode.Unknown
        };

        public static readonly OpDef OpBRK_Implied = new OpDef(OpBRK) {     // 1-byte form
            Opcode = 0x00,
            AddrMode = AddressMode.Implied,
            CycDef = 7 | (int)(CycleMod.OneIfE0)
        };
        public static readonly OpDef OpBRK_StackInt = new OpDef(OpBRK) {    // 2-byte form
            Opcode = 0x00,
            AddrMode = AddressMode.StackInt,
            CycDef = 7 | (int)(CycleMod.OneIfE0)
        };
        public static readonly OpDef OpORA_DPIndexXInd = new OpDef(OpORA) {
            Opcode = 0x01,
            AddrMode = AddressMode.DPIndexXInd,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpCOP_StackInt = new OpDef(OpCOP) {
            Opcode = 0x02,
            AddrMode = AddressMode.StackInt,
            CycDef = 7 | (int)(CycleMod.OneIfE0)
        };
        public static readonly OpDef OpORA_StackRel = new OpDef(OpORA) {
            Opcode = 0x03,
            AddrMode = AddressMode.StackRel,
            CycDef = 4 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpTSB_DP = new OpDef(OpTSB) {
            Opcode = 0x04,
            AddrMode = AddressMode.DP,
            CycDef = 5 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0)
        };
        public static readonly OpDef OpORA_DP = new OpDef(OpORA) {
            Opcode = 0x05,
            AddrMode = AddressMode.DP,
            CycDef = 3 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpASL_DP = new OpDef(OpASL) {
            Opcode = 0x06,
            AddrMode = AddressMode.DP,
            CycDef = 5 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0)
        };
        public static readonly OpDef OpORA_DPIndLong = new OpDef(OpORA) {
            Opcode = 0x07,
            AddrMode = AddressMode.DPIndLong,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpPHP_StackPush = new OpDef(OpPHP) {
            Opcode = 0x08,
            AddrMode = AddressMode.StackPush,
            CycDef = 3
        };
        public static readonly OpDef OpORA_ImmLongA = new OpDef(OpORA) {
            Opcode = 0x09,
            AddrMode = AddressMode.ImmLongA,
            StatusFlagUpdater = FlagUpdater_ORAImm,
            CycDef = 2 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpORA_Imm = new OpDef(OpORA) {
            Opcode = 0x09,
            AddrMode = AddressMode.Imm,
            StatusFlagUpdater = FlagUpdater_ORAImm,
            CycDef = 2 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpASL_Acc = new OpDef(OpASL) {
            Opcode = 0x0a,
            AddrMode = AddressMode.Acc,
            CycDef = 2
        };
        public static readonly OpDef OpPHD_StackPush = new OpDef(OpPHD) {
            Opcode = 0x0b,
            AddrMode = AddressMode.StackPush,
            CycDef = 4
        };
        public static readonly OpDef OpTSB_Abs = new OpDef(OpTSB) {
            Opcode = 0x0c,
            AddrMode = AddressMode.Abs,
            CycDef = 6 | (int)(CycleMod.TwoIfM0)
        };
        public static readonly OpDef OpORA_Abs = new OpDef(OpORA) {
            Opcode = 0x0d,
            AddrMode = AddressMode.Abs,
            CycDef = 4 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpASL_Abs = new OpDef(OpASL) {
            Opcode = 0x0e,
            AddrMode = AddressMode.Abs,
            CycDef = 6 | (int)(CycleMod.TwoIfM0)
        };
        public static readonly OpDef OpORA_AbsLong = new OpDef(OpORA) {
            Opcode = 0x0f,
            AddrMode = AddressMode.AbsLong,
            CycDef = 5 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpBPL_PCRel = new OpDef(OpBPL) {
            Opcode = 0x10,
            AddrMode = AddressMode.PCRel,
            CycDef = 2 | (int)(CycleMod.OneIfBranchTaken | CycleMod.OneIfBranchPage)
        };
        public static readonly OpDef OpORA_DPIndIndexY = new OpDef(OpORA) {
            Opcode = 0x11,
            AddrMode = AddressMode.DPIndIndexY,
            CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfIndexPage)
        };
        public static readonly OpDef OpORA_DPInd = new OpDef(OpORA) {
            Opcode = 0x12,
            AddrMode = AddressMode.DPInd,
            CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpORA_StackRelIndIndexY = new OpDef(OpORA) {
            Opcode = 0x13,
            AddrMode = AddressMode.StackRelIndIndexY,
            CycDef = 7 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpTRB_DP = new OpDef(OpTRB) {
            Opcode = 0x14,
            AddrMode = AddressMode.DP,
            CycDef = 5 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0)
        };
        public static readonly OpDef OpORA_DPIndexX = new OpDef(OpORA) {
            Opcode = 0x15,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpASL_DPIndexX = new OpDef(OpASL) {
            Opcode = 0x16,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 6 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0)
        };
        public static readonly OpDef OpORA_DPIndIndexYLong = new OpDef(OpORA) {
            Opcode = 0x17,
            AddrMode = AddressMode.DPIndIndexYLong,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpCLC_Implied = new OpDef(OpCLC) {
            Opcode = 0x18,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpORA_AbsIndexY = new OpDef(OpORA) {
            Opcode = 0x19,
            AddrMode = AddressMode.AbsIndexY,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage)
        };
        public static readonly OpDef OpINC_Acc = new OpDef(OpINC) {
            Opcode = 0x1a,
            AddrMode = AddressMode.Acc,
            CycDef = 2
        };
        public static readonly OpDef OpTCS_Implied = new OpDef(OpTCS) {
            Opcode = 0x1b,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpTRB_Abs = new OpDef(OpTRB) {
            Opcode = 0x1c,
            AddrMode = AddressMode.Abs,
            CycDef = 6 | (int)(CycleMod.TwoIfM0)
        };
        public static readonly OpDef OpORA_AbsIndexX = new OpDef(OpORA) {
            Opcode = 0x1d,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage)
        };
        public static readonly OpDef OpASL_AbsIndexX = new OpDef(OpASL) {
            Opcode = 0x1e,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 7 | (int)(CycleMod.TwoIfM0 | CycleMod.MinusOneIfNoPage)
        };
        public static readonly OpDef OpORA_AbsIndexXLong = new OpDef(OpORA) {
            Opcode = 0x1f,
            AddrMode = AddressMode.AbsIndexXLong,
            CycDef = 5 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpJSR_Abs = new OpDef(OpJSR) {
            Opcode = 0x20,
            AddrMode = AddressMode.Abs,
            CycDef = 6
        };
        public static readonly OpDef OpAND_DPIndexXInd = new OpDef(OpAND) {
            Opcode = 0x21,
            AddrMode = AddressMode.DPIndexXInd,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpJSR_AbsLong = new OpDef(OpJSL) {
            Opcode = 0x22,
            AddrMode = AddressMode.AbsLong,
            CycDef = 8
        };
        public static readonly OpDef OpAND_StackRel = new OpDef(OpAND) {
            Opcode = 0x23,
            AddrMode = AddressMode.StackRel,
            CycDef = 4 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpBIT_DP = new OpDef(OpBIT) {
            Opcode = 0x24,
            AddrMode = AddressMode.DP,
            CycDef = 3 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpAND_DP = new OpDef(OpAND) {
            Opcode = 0x25,
            AddrMode = AddressMode.DP,
            CycDef = 3 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpROL_DP = new OpDef(OpROL) {
            Opcode = 0x26,
            AddrMode = AddressMode.DP,
            CycDef = 5 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0)
        };
        public static readonly OpDef OpAND_DPIndLong = new OpDef(OpAND) {
            Opcode = 0x27,
            AddrMode = AddressMode.DPIndLong,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpPLP_StackPull = new OpDef(OpPLP) {
            Opcode = 0x28,
            AddrMode = AddressMode.StackPull,
            CycDef = 4
        };
        public static readonly OpDef OpAND_ImmLongA = new OpDef(OpAND) {
            Opcode = 0x29,
            AddrMode = AddressMode.ImmLongA,
            StatusFlagUpdater = FlagUpdater_ANDImm,
            CycDef = 2 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpAND_Imm = new OpDef(OpAND) {
            Opcode = 0x29,
            AddrMode = AddressMode.Imm,
            StatusFlagUpdater = FlagUpdater_ANDImm,
            CycDef = 2 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpROL_Acc = new OpDef(OpROL) {
            Opcode = 0x2a,
            AddrMode = AddressMode.Acc,
            CycDef = 2
        };
        public static readonly OpDef OpPLD_StackPull = new OpDef(OpPLD) {
            Opcode = 0x2b,
            AddrMode = AddressMode.StackPull,
            CycDef = 5
        };
        public static readonly OpDef OpBIT_Abs = new OpDef(OpBIT) {
            Opcode = 0x2c,
            AddrMode = AddressMode.Abs,
            CycDef = 4 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpAND_Abs = new OpDef(OpAND) {
            Opcode = 0x2d,
            AddrMode = AddressMode.Abs,
            CycDef = 4 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpROL_Abs = new OpDef(OpROL) {
            Opcode = 0x2e,
            AddrMode = AddressMode.Abs,
            CycDef = 6 | (int)(CycleMod.TwoIfM0)
        };
        public static readonly OpDef OpAND_AbsLong = new OpDef(OpAND) {
            Opcode = 0x2f,
            AddrMode = AddressMode.AbsLong,
            CycDef = 5 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpBMI_PCRel = new OpDef(OpBMI) {
            Opcode = 0x30,
            AddrMode = AddressMode.PCRel,
            CycDef = 2 | (int)(CycleMod.OneIfBranchTaken | CycleMod.OneIfBranchPage)
        };
        public static readonly OpDef OpAND_DPIndIndexY = new OpDef(OpAND) {
            Opcode = 0x31,
            AddrMode = AddressMode.DPIndIndexY,
            CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfIndexPage)
        };
        public static readonly OpDef OpAND_DPInd = new OpDef(OpAND) {
            Opcode = 0x32,
            AddrMode = AddressMode.DPInd,
            CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpAND_StackRelIndIndexY = new OpDef(OpAND) {
            Opcode = 0x33,
            AddrMode = AddressMode.StackRelIndIndexY,
            CycDef = 7 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpBIT_DPIndexX = new OpDef(OpBIT) {
            Opcode = 0x34,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpAND_DPIndexX = new OpDef(OpAND) {
            Opcode = 0x35,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpROL_DPIndexX = new OpDef(OpROL) {
            Opcode = 0x36,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 6 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0)
        };
        public static readonly OpDef OpAND_DPIndIndexYLong = new OpDef(OpAND) {
            Opcode = 0x37,
            AddrMode = AddressMode.DPIndIndexYLong,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpSEC_Implied = new OpDef(OpSEC) {
            Opcode = 0x38,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpAND_AbsIndexY = new OpDef(OpAND) {
            Opcode = 0x39,
            AddrMode = AddressMode.AbsIndexY,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage)
        };
        public static readonly OpDef OpDEC_Acc = new OpDef(OpDEC) {
            Opcode = 0x3a,
            AddrMode = AddressMode.Acc,
            CycDef = 2
        };
        public static readonly OpDef OpTSC_Implied = new OpDef(OpTSC) {
            Opcode = 0x3b,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpBIT_AbsIndexX = new OpDef(OpBIT) {
            Opcode = 0x3c,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage)
        };
        public static readonly OpDef OpAND_AbsIndexX = new OpDef(OpAND) {
            Opcode = 0x3d,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage)
        };
        public static readonly OpDef OpROL_AbsIndexX = new OpDef(OpROL) {
            Opcode = 0x3e,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 7 | (int)(CycleMod.TwoIfM0 | CycleMod.MinusOneIfNoPage)
        };
        public static readonly OpDef OpAND_AbsIndexXLong = new OpDef(OpAND) {
            Opcode = 0x3f,
            AddrMode = AddressMode.AbsIndexXLong,
            CycDef = 5 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpRTI_StackRTI = new OpDef(OpRTI) {
            Opcode = 0x40,
            AddrMode = AddressMode.StackRTI,
            CycDef = 6 | (int)(CycleMod.OneIfE0)
        };
        public static readonly OpDef OpEOR_DPIndexXInd = new OpDef(OpEOR) {
            Opcode = 0x41,
            AddrMode = AddressMode.DPIndexXInd,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpWDM_WDM = new OpDef(OpWDM) {
            Opcode = 0x42,
            AddrMode = AddressMode.WDM,
            CycDef = 2      // arbitrary; actual time is undefined
        };
        public static readonly OpDef OpEOR_StackRel = new OpDef(OpEOR) {
            Opcode = 0x43,
            AddrMode = AddressMode.StackRel,
            CycDef = 4 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpMVP_BlockMove = new OpDef(OpMVP) {
            Opcode = 0x44,
            AddrMode = AddressMode.BlockMove,
            CycDef = 7 | (int)(CycleMod.BlockMove)
        };
        public static readonly OpDef OpEOR_DP = new OpDef(OpEOR) {
            Opcode = 0x45,
            AddrMode = AddressMode.DP,
            CycDef = 3 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpLSR_DP = new OpDef(OpLSR) {
            Opcode = 0x46,
            AddrMode = AddressMode.DP,
            CycDef = 5 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0)
        };
        public static readonly OpDef OpEOR_DPIndLong = new OpDef(OpEOR) {
            Opcode = 0x47,
            AddrMode = AddressMode.DPIndLong,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpPHA_StackPush = new OpDef(OpPHA) {
            Opcode = 0x48,
            AddrMode = AddressMode.StackPush,
            CycDef = 3 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpEOR_ImmLongA = new OpDef(OpEOR) {
            Opcode = 0x49,
            AddrMode = AddressMode.ImmLongA,
            CycDef = 2 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpEOR_Imm = new OpDef(OpEOR) {
            Opcode = 0x49,
            AddrMode = AddressMode.Imm,
            CycDef = 2 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpLSR_Acc = new OpDef(OpLSR) {
            Opcode = 0x4a,
            AddrMode = AddressMode.Acc,
            CycDef = 2
        };
        public static readonly OpDef OpPHK_StackPush = new OpDef(OpPHK) {
            Opcode = 0x4b,
            AddrMode = AddressMode.StackPush,
            CycDef = 3
        };
        public static readonly OpDef OpJMP_Abs = new OpDef(OpJMP) {
            Opcode = 0x4c,
            AddrMode = AddressMode.Abs,
            CycDef = 3
        };
        public static readonly OpDef OpEOR_Abs = new OpDef(OpEOR) {
            Opcode = 0x4d,
            AddrMode = AddressMode.Abs,
            CycDef = 4 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpLSR_Abs = new OpDef(OpLSR) {
            Opcode = 0x4e,
            AddrMode = AddressMode.Abs,
            CycDef = 6 | (int)(CycleMod.TwoIfM0)
        };
        public static readonly OpDef OpEOR_AbsLong = new OpDef(OpEOR) {
            Opcode = 0x4f,
            AddrMode = AddressMode.AbsLong,
            CycDef = 5 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpBVC_PCRel = new OpDef(OpBVC) {
            Opcode = 0x50,
            AddrMode = AddressMode.PCRel,
            CycDef = 2 | (int)(CycleMod.OneIfBranchTaken | CycleMod.OneIfBranchPage)
        };
        public static readonly OpDef OpEOR_DPIndIndexY = new OpDef(OpEOR) {
            Opcode = 0x51,
            AddrMode = AddressMode.DPIndIndexY,
            CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfIndexPage)
        };
        public static readonly OpDef OpEOR_DPInd = new OpDef(OpEOR) {
            Opcode = 0x52,
            AddrMode = AddressMode.DPInd,
            CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpEOR_StackRelIndIndexY = new OpDef(OpEOR) {
            Opcode = 0x53,
            AddrMode = AddressMode.StackRelIndIndexY,
            CycDef = 7 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpMVN_BlockMove = new OpDef(OpMVN) {
            Opcode = 0x54,
            AddrMode = AddressMode.BlockMove,
            CycDef = 7 | (int)(CycleMod.BlockMove)
        };
        public static readonly OpDef OpEOR_DPIndexX = new OpDef(OpEOR) {
            Opcode = 0x55,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpLSR_DPIndexX = new OpDef(OpLSR) {
            Opcode = 0x56,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 6 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0)
        };
        public static readonly OpDef OpEOR_DPIndIndexYLong = new OpDef(OpEOR) {
            Opcode = 0x57,
            AddrMode = AddressMode.DPIndIndexYLong,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpCLI_Implied = new OpDef(OpCLI) {
            Opcode = 0x58,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpEOR_AbsIndexY = new OpDef(OpEOR) {
            Opcode = 0x59,
            AddrMode = AddressMode.AbsIndexY,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage)
        };
        public static readonly OpDef OpPHY_StackPush = new OpDef(OpPHY) {
            Opcode = 0x5a,
            AddrMode = AddressMode.StackPush,
            CycDef = 3 | (int)(CycleMod.OneIfX0)
        };
        public static readonly OpDef OpTCD_Implied = new OpDef(OpTCD) {
            Opcode = 0x5b,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpJMP_AbsLong = new OpDef(OpJML) {
            Opcode = 0x5c,
            AddrMode = AddressMode.AbsLong,
            CycDef = 4
        };
        public static readonly OpDef OpEOR_AbsIndexX = new OpDef(OpEOR) {
            Opcode = 0x5d,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage)
        };
        public static readonly OpDef OpLSR_AbsIndexX = new OpDef(OpLSR) {
            Opcode = 0x5e,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 7 | (int)(CycleMod.TwoIfM0 | CycleMod.MinusOneIfNoPage)
        };
        public static readonly OpDef OpEOR_AbsIndexXLong = new OpDef(OpEOR) {
            Opcode = 0x5f,
            AddrMode = AddressMode.AbsIndexXLong,
            CycDef = 5 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpRTS_StackRTS = new OpDef(OpRTS) {
            Opcode = 0x60,
            AddrMode = AddressMode.StackRTS,
            CycDef = 6
        };
        public static readonly OpDef OpADC_DPIndexXInd = new OpDef(OpADC) {
            Opcode = 0x61,
            AddrMode = AddressMode.DPIndexXInd,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpPER_StackPCRelLong = new OpDef(OpPER) {
            Opcode = 0x62,
            AddrMode = AddressMode.StackPCRelLong,
            CycDef = 6
        };
        public static readonly OpDef OpADC_StackRel = new OpDef(OpADC) {
            Opcode = 0x63,
            AddrMode = AddressMode.StackRel,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpSTZ_DP = new OpDef(OpSTZ) {
            Opcode = 0x64,
            AddrMode = AddressMode.DP,
            CycDef = 3 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpADC_DP = new OpDef(OpADC) {
            Opcode = 0x65,
            AddrMode = AddressMode.DP,
            CycDef = 3 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpROR_DP = new OpDef(OpROR) {
            Opcode = 0x66,
            AddrMode = AddressMode.DP,
            CycDef = 5 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0)
        };
        public static readonly OpDef OpADC_DPIndLong = new OpDef(OpADC) {
            Opcode = 0x67,
            AddrMode = AddressMode.DPIndLong,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpPLA_StackPull = new OpDef(OpPLA) {
            Opcode = 0x68,
            AddrMode = AddressMode.StackPull,
            CycDef = 4 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpADC_ImmLongA = new OpDef(OpADC) {
            Opcode = 0x69,
            AddrMode = AddressMode.ImmLongA,
            CycDef = 2 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpADC_Imm = new OpDef(OpADC) {
            Opcode = 0x69,
            AddrMode = AddressMode.Imm,
            CycDef = 2 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpROR_Acc = new OpDef(OpROR) {
            Opcode = 0x6a,
            AddrMode = AddressMode.Acc,
            CycDef = 2
        };
        public static readonly OpDef OpRTL_StackRTL = new OpDef(OpRTL) {
            Opcode = 0x6b,
            AddrMode = AddressMode.StackRTL,
            CycDef = 6
        };
        public static readonly OpDef OpJMP_AbsInd = new OpDef(OpJMP) {
            Opcode = 0x6c,
            AddrMode = AddressMode.AbsInd,
            CycDef = 5 | (int)(CycleMod.OneIf65C02)
            // takes one extra cycle on CMOS 6502 if low byte is 0xff?? (it also has a bug)
        };
        public static readonly OpDef OpADC_Abs = new OpDef(OpADC) {
            Opcode = 0x6d,
            AddrMode = AddressMode.Abs,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpROR_Abs = new OpDef(OpROR) {
            Opcode = 0x6e,
            AddrMode = AddressMode.Abs,
            CycDef = 6 | (int)(CycleMod.TwoIfM0)
        };
        public static readonly OpDef OpADC_AbsLong = new OpDef(OpADC) {
            Opcode = 0x6f,
            AddrMode = AddressMode.AbsLong,
            CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpBVS_PCRel = new OpDef(OpBVS) {
            Opcode = 0x70,
            AddrMode = AddressMode.PCRel,
            CycDef = 2 | (int)(CycleMod.OneIfBranchTaken | CycleMod.OneIfBranchPage)
        };
        public static readonly OpDef OpADC_DPIndIndexY = new OpDef(OpADC) {
            Opcode = 0x71,
            AddrMode = AddressMode.DPIndIndexY,
            CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero |
                CycleMod.OneIfIndexPage | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpADC_DPInd = new OpDef(OpADC) {
            Opcode = 0x72,
            AddrMode = AddressMode.DPInd,
            CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpADC_StackRelIndIndexY = new OpDef(OpADC) {
            Opcode = 0x73,
            AddrMode = AddressMode.StackRelIndIndexY,
            CycDef = 7 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpSTZ_DPIndexX = new OpDef(OpSTZ) {
            Opcode = 0x74,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpADC_DPIndexX = new OpDef(OpADC) {
            Opcode = 0x75,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpROR_DPIndexX = new OpDef(OpROR) {
            Opcode = 0x76,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 6 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0)
        };
        public static readonly OpDef OpADC_DPIndIndexYLong = new OpDef(OpADC) {
            Opcode = 0x77,
            AddrMode = AddressMode.DPIndIndexYLong,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpSEI_Implied = new OpDef(OpSEI) {
            Opcode = 0x78,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpADC_AbsIndexY = new OpDef(OpADC) {
            Opcode = 0x79,
            AddrMode = AddressMode.AbsIndexY,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpPLY_StackPull = new OpDef(OpPLY) {
            Opcode = 0x7a,
            AddrMode = AddressMode.StackPull,
            CycDef = 4 | (int)(CycleMod.OneIfX0)
        };
        public static readonly OpDef OpTDC_Implied = new OpDef(OpTDC) {
            Opcode = 0x7b,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpJMP_AbsIndexXInd = new OpDef(OpJMP) {
            Opcode = 0x7c,
            AddrMode = AddressMode.AbsIndexXInd,
            CycDef = 6
        };
        public static readonly OpDef OpADC_AbsIndexX = new OpDef(OpADC) {
            Opcode = 0x7d,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpROR_AbsIndexX = new OpDef(OpROR) {
            Opcode = 0x7e,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 7 | (int)(CycleMod.TwoIfM0 | CycleMod.MinusOneIfNoPage)
        };
        public static readonly OpDef OpADC_AbsIndexXLong = new OpDef(OpADC) {
            Opcode = 0x7f,
            AddrMode = AddressMode.AbsIndexXLong,
            CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpBRA_PCRel = new OpDef(OpBRA) {
            Opcode = 0x80,
            AddrMode = AddressMode.PCRel,
            CycDef = 3 | (int)(CycleMod.OneIfBranchPage)
        };
        public static readonly OpDef OpSTA_DPIndexXInd = new OpDef(OpSTA) {
            Opcode = 0x81,
            AddrMode = AddressMode.DPIndexXInd,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpBRL_PCRelLong = new OpDef(OpBRL) {
            Opcode = 0x82,
            AddrMode = AddressMode.PCRelLong,
            CycDef = 4
        };
        public static readonly OpDef OpSTA_StackRel = new OpDef(OpSTA) {
            Opcode = 0x83,
            AddrMode = AddressMode.StackRel,
            CycDef = 4 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpSTY_DP = new OpDef(OpSTY) {
            Opcode = 0x84,
            AddrMode = AddressMode.DP,
            CycDef = 3 | (int)(CycleMod.OneIfDpNonzero | CycleMod.OneIfX0)
        };
        public static readonly OpDef OpSTA_DP = new OpDef(OpSTA) {
            Opcode = 0x85,
            AddrMode = AddressMode.DP,
            CycDef = 3 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpSTX_DP = new OpDef(OpSTX) {
            Opcode = 0x86,
            AddrMode = AddressMode.DP,
            CycDef = 3 | (int)(CycleMod.OneIfDpNonzero | CycleMod.OneIfX0)
        };
        public static readonly OpDef OpSTA_DPIndLong = new OpDef(OpSTA) {
            Opcode = 0x87,
            AddrMode = AddressMode.DPIndLong,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpDEY_Implied = new OpDef(OpDEY) {
            Opcode = 0x88,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpBIT_ImmLongA = new OpDef(OpBIT) {
            Opcode = 0x89,
            AddrMode = AddressMode.ImmLongA,
            FlagsAffected = FlagsAffected_Z,        // special case
            StatusFlagUpdater = FlagUpdater_Z,
            CycDef = 2 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpBIT_Imm = new OpDef(OpBIT) {
            Opcode = 0x89,
            AddrMode = AddressMode.Imm,
            FlagsAffected = FlagsAffected_Z,        // special case
            StatusFlagUpdater = FlagUpdater_Z,
            CycDef = 2 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpTXA_Implied = new OpDef(OpTXA) {
            Opcode = 0x8a,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpPHB_StackPush = new OpDef(OpPHB) {
            Opcode = 0x8b,
            AddrMode = AddressMode.StackPush,
            CycDef = 3
        };
        public static readonly OpDef OpSTY_Abs = new OpDef(OpSTY) {
            Opcode = 0x8c,
            AddrMode = AddressMode.Abs,
            CycDef = 4 | (int)(CycleMod.OneIfX0)
        };
        public static readonly OpDef OpSTA_Abs = new OpDef(OpSTA) {
            Opcode = 0x8d,
            AddrMode = AddressMode.Abs,
            CycDef = 4 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpSTX_Abs = new OpDef(OpSTX) {
            Opcode = 0x8e,
            AddrMode = AddressMode.Abs,
            CycDef = 4 | (int)(CycleMod.OneIfX0)
        };
        public static readonly OpDef OpSTA_AbsLong = new OpDef(OpSTA) {
            Opcode = 0x8f,
            AddrMode = AddressMode.AbsLong,
            CycDef = 5 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpBCC_PCRel = new OpDef(OpBCC) {
            Opcode = 0x90,
            AddrMode = AddressMode.PCRel,
            CycDef = 2 | (int)(CycleMod.OneIfBranchTaken | CycleMod.OneIfBranchPage)
        };
        public static readonly OpDef OpSTA_DPIndIndexY = new OpDef(OpSTA) {
            Opcode = 0x91,
            AddrMode = AddressMode.DPIndIndexY,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpSTA_DPInd = new OpDef(OpSTA) {
            Opcode = 0x92,
            AddrMode = AddressMode.DPInd,
            CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpSTA_StackRelIndIndexY = new OpDef(OpSTA) {
            Opcode = 0x93,
            AddrMode = AddressMode.StackRelIndIndexY,
            CycDef = 7 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpSTY_DPIndexX = new OpDef(OpSTY) {
            Opcode = 0x94,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 4 | (int)(CycleMod.OneIfDpNonzero | CycleMod.OneIfX0)
        };
        public static readonly OpDef OpSTA_DPIndexX = new OpDef(OpSTA) {
            Opcode = 0x95,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpSTX_DPIndexY = new OpDef(OpSTX) {
            Opcode = 0x96,
            AddrMode = AddressMode.DPIndexY,
            CycDef = 4 | (int)(CycleMod.OneIfDpNonzero | CycleMod.OneIfX0)
        };
        public static readonly OpDef OpSTA_DPIndIndexYLong = new OpDef(OpSTA) {
            Opcode = 0x97,
            AddrMode = AddressMode.DPIndIndexYLong,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpTYA_Implied = new OpDef(OpTYA) {
            Opcode = 0x98,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpSTA_AbsIndexY = new OpDef(OpSTA) {
            Opcode = 0x99,
            AddrMode = AddressMode.AbsIndexY,
            CycDef = 5 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpTXS_Implied = new OpDef(OpTXS) {
            Opcode = 0x9a,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpTXY_Implied = new OpDef(OpTXY) {
            Opcode = 0x9b,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpSTZ_Abs = new OpDef(OpSTZ) {
            Opcode = 0x9c,
            AddrMode = AddressMode.Abs,
            CycDef = 4 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpSTA_AbsIndexX = new OpDef(OpSTA) {
            Opcode = 0x9d,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 5 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpSTZ_AbsIndexX = new OpDef(OpSTZ) {
            Opcode = 0x9e,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 5 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpSTA_AbsIndexXLong = new OpDef(OpSTA) {
            Opcode = 0x9f,
            AddrMode = AddressMode.AbsIndexXLong,
            CycDef = 5 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpLDY_ImmLongXY = new OpDef(OpLDY) {
            Opcode = 0xa0,
            AddrMode = AddressMode.ImmLongXY,
            StatusFlagUpdater = FlagUpdater_LoadImm,
            CycDef = 2 | (int)(CycleMod.OneIfX0)
        };
        public static readonly OpDef OpLDY_Imm = new OpDef(OpLDY) {
            Opcode = 0xa0,
            AddrMode = AddressMode.Imm,
            StatusFlagUpdater = FlagUpdater_LoadImm,
            CycDef = 2 | (int)(CycleMod.OneIfX0)
        };
        public static readonly OpDef OpLDA_DPIndexXInd = new OpDef(OpLDA) {
            Opcode = 0xa1,
            AddrMode = AddressMode.DPIndexXInd,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpLDX_ImmLongXY = new OpDef(OpLDX) {
            Opcode = 0xa2,
            AddrMode = AddressMode.ImmLongXY,
            StatusFlagUpdater = FlagUpdater_LoadImm,
            CycDef = 2 | (int)(CycleMod.OneIfX0)
        };
        public static readonly OpDef OpLDX_Imm = new OpDef(OpLDX) {
            Opcode = 0xa2,
            AddrMode = AddressMode.Imm,
            StatusFlagUpdater = FlagUpdater_LoadImm,
            CycDef = 2 | (int)(CycleMod.OneIfX0)
        };
        public static readonly OpDef OpLDA_StackRel = new OpDef(OpLDA) {
            Opcode = 0xa3,
            AddrMode = AddressMode.StackRel,
            CycDef = 4 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpLDY_DP = new OpDef(OpLDY) {
            Opcode = 0xa4,
            AddrMode = AddressMode.DP,
            CycDef = 3 | (int)(CycleMod.OneIfDpNonzero | CycleMod.OneIfX0)
        };
        public static readonly OpDef OpLDA_DP = new OpDef(OpLDA) {
            Opcode = 0xa5,
            AddrMode = AddressMode.DP,
            CycDef = 3 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpLDX_DP = new OpDef(OpLDX) {
            Opcode = 0xa6,
            AddrMode = AddressMode.DP,
            CycDef = 3 | (int)(CycleMod.OneIfDpNonzero | CycleMod.OneIfX0)
        };
        public static readonly OpDef OpLDA_DPIndLong = new OpDef(OpLDA) {
            Opcode = 0xa7,
            AddrMode = AddressMode.DPIndLong,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpTAY_Implied = new OpDef(OpTAY) {
            Opcode = 0xa8,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpLDA_ImmLongA = new OpDef(OpLDA) {    // 16-bit CPU
            Opcode = 0xa9,
            AddrMode = AddressMode.ImmLongA,
            StatusFlagUpdater = FlagUpdater_LoadImm,
            CycDef = 2 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpLDA_Imm = new OpDef(OpLDA) {         // 8-bit CPU
            Opcode = 0xa9,
            AddrMode = AddressMode.Imm,
            StatusFlagUpdater = FlagUpdater_LoadImm,
            CycDef = 2 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpTAX_Implied = new OpDef(OpTAX) {
            Opcode = 0xaa,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpPLB_StackPull = new OpDef(OpPLB) {
            Opcode = 0xab,
            AddrMode = AddressMode.StackPull,
            CycDef = 4
        };
        public static readonly OpDef OpLDY_Abs = new OpDef(OpLDY) {
            Opcode = 0xac,
            AddrMode = AddressMode.Abs,
            CycDef = 4 | (int)(CycleMod.OneIfX0)
        };
        public static readonly OpDef OpLDA_Abs = new OpDef(OpLDA) {
            Opcode = 0xad,
            AddrMode = AddressMode.Abs,
            CycDef = 4 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpLDX_Abs = new OpDef(OpLDX) {
            Opcode = 0xae,
            AddrMode = AddressMode.Abs,
            CycDef = 4 | (int)(CycleMod.OneIfX0)
        };
        public static readonly OpDef OpLDA_AbsLong = new OpDef(OpLDA) {
            Opcode = 0xaf,
            AddrMode = AddressMode.AbsLong,
            CycDef = 5 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpBCS_PCRel = new OpDef(OpBCS) {
            Opcode = 0xb0,
            AddrMode = AddressMode.PCRel,
            CycDef = 2 | (int)(CycleMod.OneIfBranchTaken | CycleMod.OneIfBranchPage)
        };
        public static readonly OpDef OpLDA_DPIndIndexY = new OpDef(OpLDA) {
            Opcode = 0xb1,
            AddrMode = AddressMode.DPIndIndexY,
            CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfIndexPage)
        };
        public static readonly OpDef OpLDA_DPInd = new OpDef(OpLDA) {
            Opcode = 0xb2,
            AddrMode = AddressMode.DPInd,
            CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpLDA_StackRelIndIndexY = new OpDef(OpLDA) {
            Opcode = 0xb3,
            AddrMode = AddressMode.StackRelIndIndexY,
            CycDef = 7 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpLDY_DPIndexX = new OpDef(OpLDY) {
            Opcode = 0xb4,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 4 | (int)(CycleMod.OneIfDpNonzero | CycleMod.OneIfX0)
        };
        public static readonly OpDef OpLDA_DPIndexX = new OpDef(OpLDA) {
            Opcode = 0xb5,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpLDX_DPIndexY = new OpDef(OpLDX) {
            Opcode = 0xb6,
            AddrMode = AddressMode.DPIndexY,
            CycDef = 4 | (int)(CycleMod.OneIfDpNonzero | CycleMod.OneIfX0)
        };
        public static readonly OpDef OpLDA_DPIndIndexYLong = new OpDef(OpLDA) {
            Opcode = 0xb7,
            AddrMode = AddressMode.DPIndIndexYLong,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpCLV_Implied = new OpDef(OpCLV) {
            Opcode = 0xb8,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpLDA_AbsIndexY = new OpDef(OpLDA) {
            Opcode = 0xb9,
            AddrMode = AddressMode.AbsIndexY,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage)
        };
        public static readonly OpDef OpTSX_Implied = new OpDef(OpTSX) {
            Opcode = 0xba,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpTYX_Implied = new OpDef(OpTYX) {
            Opcode = 0xbb,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpLDY_AbsIndexX = new OpDef(OpLDY) {
            Opcode = 0xbc,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 4 | (int)(CycleMod.OneIfIndexPage | CycleMod.OneIfX0)
        };
        public static readonly OpDef OpLDA_AbsIndexX = new OpDef(OpLDA) {
            Opcode = 0xbd,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage)
        };
        public static readonly OpDef OpLDX_AbsIndexY = new OpDef(OpLDX) {
            Opcode = 0xbe,
            AddrMode = AddressMode.AbsIndexY,
            CycDef = 4 | (int)(CycleMod.OneIfIndexPage | CycleMod.OneIfX0)
        };
        public static readonly OpDef OpLDA_AbsIndexXLong = new OpDef(OpLDA) {
            Opcode = 0xbf,
            AddrMode = AddressMode.AbsIndexXLong,
            CycDef = 5 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpCPY_ImmLongXY = new OpDef(OpCPY) {
            Opcode = 0xc0,
            AddrMode = AddressMode.ImmLongXY,
            CycDef = 2 | (int)(CycleMod.OneIfX0)
        };
        public static readonly OpDef OpCPY_Imm = new OpDef(OpCPY) {
            Opcode = 0xc0,
            AddrMode = AddressMode.Imm,
            CycDef = 2 | (int)(CycleMod.OneIfX0)
        };
        public static readonly OpDef OpCMP_DPIndexXInd = new OpDef(OpCMP) {
            Opcode = 0xc1,
            AddrMode = AddressMode.DPIndexXInd,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpREP_Imm = new OpDef(OpREP) {
            Opcode = 0xc2,
            AddrMode = AddressMode.Imm,
            CycDef = 3
        };
        public static readonly OpDef OpCMP_StackRel = new OpDef(OpCMP) {
            Opcode = 0xc3,
            AddrMode = AddressMode.StackRel,
            CycDef = 4 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpCPY_DP = new OpDef(OpCPY) {
            Opcode = 0xc4,
            AddrMode = AddressMode.DP,
            CycDef = 3 | (int)(CycleMod.OneIfDpNonzero | CycleMod.OneIfX0)
        };
        public static readonly OpDef OpCMP_DP = new OpDef(OpCMP) {
            Opcode = 0xc5,
            AddrMode = AddressMode.DP,
            CycDef = 3 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpDEC_DP = new OpDef(OpDEC) {
            Opcode = 0xc6,
            AddrMode = AddressMode.DP,
            CycDef = 5 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0)
        };
        public static readonly OpDef OpCMP_DPIndLong = new OpDef(OpCMP) {
            Opcode = 0xc7,
            AddrMode = AddressMode.DPIndLong,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpINY_Implied = new OpDef(OpINY) {
            Opcode = 0xc8,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpCMP_ImmLongA = new OpDef(OpCMP) {
            Opcode = 0xc9,
            AddrMode = AddressMode.ImmLongA,
            CycDef = 2 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpCMP_Imm = new OpDef(OpCMP) {
            Opcode = 0xc9,
            AddrMode = AddressMode.Imm,
            CycDef = 2 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpDEX_Implied = new OpDef(OpDEX) {
            Opcode = 0xca,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpWAI_Implied = new OpDef(OpWAI) {
            Opcode = 0xcb,
            AddrMode = AddressMode.Implied,
            CycDef = 3      // 3 to shut down, more to restart
        };
        public static readonly OpDef OpCPY_Abs = new OpDef(OpCPY) {
            Opcode = 0xcc,
            AddrMode = AddressMode.Abs,
            CycDef = 4 | (int)(CycleMod.OneIfX0)
        };
        public static readonly OpDef OpCMP_Abs = new OpDef(OpCMP) {
            Opcode = 0xcd,
            AddrMode = AddressMode.Abs,
            CycDef = 4 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpDEC_Abs = new OpDef(OpDEC) {
            Opcode = 0xce,
            AddrMode = AddressMode.Abs,
            CycDef = 6 | (int)(CycleMod.TwoIfM0)
        };
        public static readonly OpDef OpCMP_AbsLong = new OpDef(OpCMP) {
            Opcode = 0xcf,
            AddrMode = AddressMode.AbsLong,
            CycDef = 5 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpBNE_PCRel = new OpDef(OpBNE) {
            Opcode = 0xd0,
            AddrMode = AddressMode.PCRel,
            CycDef = 2 | (int)(CycleMod.OneIfBranchTaken | CycleMod.OneIfBranchPage)
        };
        public static readonly OpDef OpCMP_DPIndIndexY = new OpDef(OpCMP) {
            Opcode = 0xd1,
            AddrMode = AddressMode.DPIndIndexY,
            CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfIndexPage)
        };
        public static readonly OpDef OpCMP_DPInd = new OpDef(OpCMP) {
            Opcode = 0xd2,
            AddrMode = AddressMode.DPInd,
            CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpCMP_StackRelIndIndexY = new OpDef(OpCMP) {
            Opcode = 0xd3,
            AddrMode = AddressMode.StackRelIndIndexY,
            CycDef = 7 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpPEI_StackDPInd = new OpDef(OpPEI) {
            Opcode = 0xd4,
            AddrMode = AddressMode.StackDPInd,
            CycDef = 6 | (int)(CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpCMP_DPIndexX = new OpDef(OpCMP) {
            Opcode = 0xd5,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpDEC_DPIndexX = new OpDef(OpDEC) {
            Opcode = 0xd6,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 6 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0)
        };
        public static readonly OpDef OpCMP_DPIndIndexYLong = new OpDef(OpCMP) {
            Opcode = 0xd7,
            AddrMode = AddressMode.DPIndIndexYLong,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero)
        };
        public static readonly OpDef OpCLD_Implied = new OpDef(OpCLD) {
            Opcode = 0xd8,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpCMP_AbsIndexY = new OpDef(OpCMP) {
            Opcode = 0xd9,
            AddrMode = AddressMode.AbsIndexY,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage)
        };
        public static readonly OpDef OpPHX_StackPush = new OpDef(OpPHX) {
            Opcode = 0xda,
            AddrMode = AddressMode.StackPush,
            CycDef = 3 | (int)(CycleMod.OneIfX0)
        };
        public static readonly OpDef OpSTP_Implied = new OpDef(OpSTP) {
            Opcode = 0xdb,
            AddrMode = AddressMode.Implied,
            CycDef = 3      // 3 to shut down, more to reset out
        };
        public static readonly OpDef OpJMP_AbsIndLong = new OpDef(OpJML) {
            Opcode = 0xdc,
            AddrMode = AddressMode.AbsIndLong,
            CycDef = 6
        };
        public static readonly OpDef OpCMP_AbsIndexX = new OpDef(OpCMP) {
            Opcode = 0xdd,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage)
        };
        public static readonly OpDef OpDEC_AbsIndexX = new OpDef(OpDEC) {
            Opcode = 0xde,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 7 | (int)(CycleMod.TwoIfM0 | CycleMod.MinusOneIfNoPage)
        };
        public static readonly OpDef OpCMP_AbsIndexXLong = new OpDef(OpCMP) {
            Opcode = 0xdf,
            AddrMode = AddressMode.AbsIndexXLong,
            CycDef = 5 | (int)(CycleMod.OneIfM0)
        };
        public static readonly OpDef OpCPX_ImmLongXY = new OpDef(OpCPX) {
            Opcode = 0xe0,
            AddrMode = AddressMode.ImmLongXY,
            CycDef = 2 | (int)(CycleMod.OneIfX0)
        };
        public static readonly OpDef OpCPX_Imm = new OpDef(OpCPX) {
            Opcode = 0xe0,
            AddrMode = AddressMode.Imm,
            CycDef = 2 | (int)(CycleMod.OneIfX0)
        };
        public static readonly OpDef OpSBC_DPIndexXInd = new OpDef(OpSBC) {
            Opcode = 0xe1,
            AddrMode = AddressMode.DPIndexXInd,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpSEP_Imm = new OpDef(OpSEP) {
            Opcode = 0xe2,
            AddrMode = AddressMode.Imm,
            CycDef = 3
        };
        public static readonly OpDef OpSBC_StackRel = new OpDef(OpSBC) {
            Opcode = 0xe3,
            AddrMode = AddressMode.StackRel,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpCPX_DP = new OpDef(OpCPX) {
            Opcode = 0xe4,
            AddrMode = AddressMode.DP,
            CycDef = 3 | (int)(CycleMod.OneIfDpNonzero | CycleMod.OneIfX0)
        };
        public static readonly OpDef OpSBC_DP = new OpDef(OpSBC) {
            Opcode = 0xe5,
            AddrMode = AddressMode.DP,
            CycDef = 3 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpINC_DP = new OpDef(OpINC) {
            Opcode = 0xe6,
            AddrMode = AddressMode.DP,
            CycDef = 5 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0)
        };
        public static readonly OpDef OpSBC_DPIndLong = new OpDef(OpSBC) {
            Opcode = 0xe7,
            AddrMode = AddressMode.DPIndLong,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpINX_Implied = new OpDef(OpINX) {
            Opcode = 0xe8,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpSBC_ImmLongA = new OpDef(OpSBC) {
            Opcode = 0xe9,
            AddrMode = AddressMode.ImmLongA,
            CycDef = 2 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpSBC_Imm = new OpDef(OpSBC) {
            Opcode = 0xe9,
            AddrMode = AddressMode.Imm,
            CycDef = 2 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpNOP_Implied = new OpDef(OpNOP) {
            Opcode = 0xea,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpXBA_Implied = new OpDef(OpXBA) {
            Opcode = 0xeb,
            AddrMode = AddressMode.Implied,
            CycDef = 3
        };
        public static readonly OpDef OpCPX_Abs = new OpDef(OpCPX) {
            Opcode = 0xec,
            AddrMode = AddressMode.Abs,
            CycDef = 4 | (int)(CycleMod.OneIfX0)
        };
        public static readonly OpDef OpSBC_Abs = new OpDef(OpSBC) {
            Opcode = 0xed,
            AddrMode = AddressMode.Abs,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpINC_Abs = new OpDef(OpINC) {
            Opcode = 0xee,
            AddrMode = AddressMode.Abs,
            CycDef = 6 | (int)(CycleMod.TwoIfM0)
        };
        public static readonly OpDef OpSBC_AbsLong = new OpDef(OpSBC) {
            Opcode = 0xef,
            AddrMode = AddressMode.AbsLong,
            CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpBEQ_PCRel = new OpDef(OpBEQ) {
            Opcode = 0xf0,
            AddrMode = AddressMode.PCRel,
            CycDef = 2 | (int)(CycleMod.OneIfBranchTaken | CycleMod.OneIfBranchPage)
        };
        public static readonly OpDef OpSBC_DPIndIndexY = new OpDef(OpSBC) {
            Opcode = 0xf1,
            AddrMode = AddressMode.DPIndIndexY,
            CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero |
                CycleMod.OneIfIndexPage | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpSBC_DPInd = new OpDef(OpSBC) {
            Opcode = 0xf2,
            AddrMode = AddressMode.DPInd,
            CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpSBC_StackRelIndIndexY = new OpDef(OpSBC) {
            Opcode = 0xf3,
            AddrMode = AddressMode.StackRelIndIndexY,
            CycDef = 7 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpPEA_StackAbs = new OpDef(OpPEA) {
            Opcode = 0xf4,
            AddrMode = AddressMode.StackAbs,
            CycDef = 5
        };
        public static readonly OpDef OpSBC_DPIndexX = new OpDef(OpSBC) {
            Opcode = 0xf5,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpINC_DPIndexX = new OpDef(OpINC) {
            Opcode = 0xf6,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 6 | (int)(CycleMod.OneIfDpNonzero | CycleMod.TwoIfM0)
        };
        public static readonly OpDef OpSBC_DPIndIndexYLong = new OpDef(OpSBC) {
            Opcode = 0xf7,
            AddrMode = AddressMode.DPIndIndexYLong,
            CycDef = 6 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfDpNonzero | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpSED_Implied = new OpDef(OpSED) {
            Opcode = 0xf8,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpSBC_AbsIndexY = new OpDef(OpSBC) {
            Opcode = 0xf9,
            AddrMode = AddressMode.AbsIndexY,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpPLX_StackPull = new OpDef(OpPLX) {
            Opcode = 0xfa,
            AddrMode = AddressMode.StackPull,
            CycDef = 4 | (int)(CycleMod.OneIfX0)
        };
        public static readonly OpDef OpXCE_Implied = new OpDef(OpXCE) {
            Opcode = 0xfb,
            AddrMode = AddressMode.Implied,
            CycDef = 2
        };
        public static readonly OpDef OpJSR_AbsIndexXInd = new OpDef(OpJSR) {
            Opcode = 0xfc,
            AddrMode = AddressMode.AbsIndexXInd,
            CycDef = 8
        };
        public static readonly OpDef OpSBC_AbsIndexX = new OpDef(OpSBC) {
            Opcode = 0xfd,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 4 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfIndexPage | CycleMod.OneIfD1)
        };
        public static readonly OpDef OpINC_AbsIndexX = new OpDef(OpINC) {
            Opcode = 0xfe,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 7 | (int)(CycleMod.TwoIfM0 | CycleMod.MinusOneIfNoPage)
        };
        public static readonly OpDef OpSBC_AbsIndexXLong = new OpDef(OpSBC) {
            Opcode = 0xff,
            AddrMode = AddressMode.AbsIndexXLong,
            CycDef = 5 | (int)(CycleMod.OneIfM0 | CycleMod.OneIfD1)
        };

        #endregion 65816 Instructions

        #region Undocumented 6502

        // ======================================================================================
        // Undocumented 6502 instructions.
        //
        // There are 151 defined opcodes.  The rest officially have undefined behavior.  In
        // most cases it's pretty stable, in others the behavior can differ between CPU
        // variants.
        //
        // There is no universally agreed-upon set of mnemonics for these instructions.  The
        // mnemonics "XAS" and "AXS" sometimes mean one thing and sometimes another.  The
        // reference I've chosen to use as primary is "No More Secrets" / "NMOS 6510 Unintended
        // Opcodes":
        //   https://csdb.dk/release/?id=258111
        //
        // Other references:
        //   http://nesdev.com/undocumented_opcodes.txt
        //   http://visual6502.org/wiki/index.php?title=6502_all_256_Opcodes
        //   http://www.ffd2.com/fridge/docs/6502-NMOS.extra.opcodes
        //

        private static OpDef OpANC = new OpDef() {
            IsUndocumented = true,
            Mnemonic = OpName.ANC,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZC,
            StatusFlagUpdater = FlagUpdater_NZC
        };
        private static OpDef OpANE = new OpDef() {
            IsUndocumented = true,
            Mnemonic = OpName.ANE,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpALR = new OpDef() {
            IsUndocumented = true,
            Mnemonic = OpName.ALR,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZC,
            StatusFlagUpdater = FlagUpdater_NZC
        };
        private static OpDef OpARR = new OpDef() {
            IsUndocumented = true,
            Mnemonic = OpName.ARR,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NVZC,
            StatusFlagUpdater = FlagUpdater_NVZC
        };
        private static OpDef OpDCP = new OpDef() {
            IsUndocumented = true,
            Mnemonic = OpName.DCP,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.ReadModifyWrite,
            FlagsAffected = FlagsAffected_NZC,
            StatusFlagUpdater = FlagUpdater_NZC
        };
        private static OpDef OpDOP = new OpDef() {      // double-byte NOP
            IsUndocumented = true,
            Mnemonic = OpName.DOP,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None
        };
        private static OpDef OpJAM = new OpDef() {
            IsUndocumented = true,
            Mnemonic = OpName.JAM,
            Effect = FlowEffect.NoCont,
            BaseMemEffect = MemoryEffect.None
        };
        private static OpDef OpISC = new OpDef() {
            IsUndocumented = true,
            Mnemonic = OpName.ISC,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.ReadModifyWrite,
            FlagsAffected = FlagsAffected_NVZC,
            StatusFlagUpdater = FlagUpdater_NVZC
        };
        private static OpDef OpLAS = new OpDef() {
            IsUndocumented = true,
            Mnemonic = OpName.LAS,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Read,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpLAX = new OpDef() {
            IsUndocumented = true,
            Mnemonic = OpName.LAX,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Read,
            FlagsAffected = FlagsAffected_NZ,
            StatusFlagUpdater = FlagUpdater_NZ
        };
        private static OpDef OpRLA = new OpDef() {
            IsUndocumented = true,
            Mnemonic = OpName.RLA,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.ReadModifyWrite,
            FlagsAffected = FlagsAffected_NZC,
            StatusFlagUpdater = FlagUpdater_NZC
        };
        private static OpDef OpRRA = new OpDef() {
            IsUndocumented = true,
            Mnemonic = OpName.RRA,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.ReadModifyWrite,
            FlagsAffected = FlagsAffected_NVZC,
            StatusFlagUpdater = FlagUpdater_NVZC
        };
        private static OpDef OpSAX = new OpDef() {
            IsUndocumented = true,
            Mnemonic = OpName.SAX,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Write
        };
        private static OpDef OpSBX = new OpDef() {
            IsUndocumented = true,
            Mnemonic = OpName.SBX,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            FlagsAffected = FlagsAffected_NZC,
            StatusFlagUpdater = FlagUpdater_NZC
        };
        private static OpDef OpSHA = new OpDef() {
            IsUndocumented = true,
            Mnemonic = OpName.SHA,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Write
        };
        private static OpDef OpSHX = new OpDef() {
            IsUndocumented = true,
            Mnemonic = OpName.SHX,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Write
        };
        private static OpDef OpSHY = new OpDef() {
            IsUndocumented = true,
            Mnemonic = OpName.SHY,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Write
        };
        private static OpDef OpSLO = new OpDef() {
            IsUndocumented = true,
            Mnemonic = OpName.SLO,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.ReadModifyWrite,
            FlagsAffected = FlagsAffected_NZC,
            StatusFlagUpdater = FlagUpdater_NZC
        };
        private static OpDef OpSRE = new OpDef() {
            IsUndocumented = true,
            Mnemonic = OpName.SRE,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.ReadModifyWrite,
            FlagsAffected = FlagsAffected_NZC,
            StatusFlagUpdater = FlagUpdater_NZC
        };
        private static OpDef OpTAS = new OpDef() {
            IsUndocumented = true,
            Mnemonic = OpName.TAS,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Write
        };
        private static OpDef OpTOP = new OpDef() {      // triple-byte NOP
            IsUndocumented = true,
            Mnemonic = OpName.TOP,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None
        };

        public static readonly OpDef OpSLO_DPIndexXInd = new OpDef(OpSLO) {
            Opcode = 0x03,
            AddrMode = AddressMode.DPIndexXInd,
            CycDef = 8
        };
        public static readonly OpDef OpSLO_DP = new OpDef(OpSLO) {
            Opcode = 0x07,
            AddrMode = AddressMode.DP,
            CycDef = 5
        };
        public static readonly OpDef OpSLO_Absolute = new OpDef(OpSLO) {
            Opcode = 0x0f,
            AddrMode = AddressMode.Abs,
            CycDef = 6
        };
        public static readonly OpDef OpSLO_DPIndIndexY = new OpDef(OpSLO) {
            Opcode = 0x13,
            AddrMode = AddressMode.DPIndIndexY,
            CycDef = 8
        };
        public static readonly OpDef OpSLO_DPIndexX = new OpDef(OpSLO) {
            Opcode = 0x17,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 6
        };
        public static readonly OpDef OpSLO_AbsIndexY = new OpDef(OpSLO) {
            Opcode = 0x1b,
            AddrMode = AddressMode.AbsIndexY,
            CycDef = 7
        };
        public static readonly OpDef OpSLO_AbsIndexX = new OpDef(OpSLO) {
            Opcode = 0x1f,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 7
        };
        public static readonly OpDef OpRLA_DPIndexXInd = new OpDef(OpRLA) {
            Opcode = 0x23,
            AddrMode = AddressMode.DPIndexXInd,
            CycDef = 8
        };
        public static readonly OpDef OpRLA_DP = new OpDef(OpRLA) {
            Opcode = 0x27,
            AddrMode = AddressMode.DP,
            CycDef = 5
        };
        public static readonly OpDef OpRLA_Absolute = new OpDef(OpRLA) {
            Opcode = 0x2f,
            AddrMode = AddressMode.Abs,
            CycDef = 6
        };
        public static readonly OpDef OpRLA_DPIndIndexY = new OpDef(OpRLA) {
            Opcode = 0x33,
            AddrMode = AddressMode.DPIndIndexY,
            CycDef = 8
        };
        public static readonly OpDef OpRLA_DPIndexX = new OpDef(OpRLA) {
            Opcode = 0x37,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 6
        };
        public static readonly OpDef OpRLA_AbsIndexY = new OpDef(OpRLA) {
            Opcode = 0x3b,
            AddrMode = AddressMode.AbsIndexY,
            CycDef = 7
        };
        public static readonly OpDef OpRLA_AbsIndexX = new OpDef(OpRLA) {
            Opcode = 0x3f,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 7
        };
        public static readonly OpDef OpSRE_DPIndexXInd = new OpDef(OpSRE) {
            Opcode = 0x43,
            AddrMode = AddressMode.DPIndexXInd,
            CycDef = 8
        };
        public static readonly OpDef OpSRE_DP = new OpDef(OpSRE) {
            Opcode = 0x47,
            AddrMode = AddressMode.DP,
            CycDef = 5
        };
        public static readonly OpDef OpSRE_Absolute = new OpDef(OpSRE) {
            Opcode = 0x4f,
            AddrMode = AddressMode.Abs,
            CycDef = 6
        };
        public static readonly OpDef OpSRE_DPIndIndexY = new OpDef(OpSRE) {
            Opcode = 0x53,
            AddrMode = AddressMode.DPIndIndexY,
            CycDef = 8
        };
        public static readonly OpDef OpSRE_DPIndexX = new OpDef(OpSRE) {
            Opcode = 0x57,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 6
        };
        public static readonly OpDef OpSRE_AbsIndexY = new OpDef(OpSRE) {
            Opcode = 0x5b,
            AddrMode = AddressMode.AbsIndexY,
            CycDef = 7
        };
        public static readonly OpDef OpSRE_AbsIndexX = new OpDef(OpSRE) {
            Opcode = 0x5f,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 7
        };
        public static readonly OpDef OpRRA_DPIndexXInd = new OpDef(OpRRA) {
            Opcode = 0x63,
            AddrMode = AddressMode.DPIndexXInd,
            CycDef = 8
        };
        public static readonly OpDef OpRRA_DP = new OpDef(OpRRA) {
            Opcode = 0x67,
            AddrMode = AddressMode.DP,
            CycDef = 5
        };
        public static readonly OpDef OpRRA_Absolute = new OpDef(OpRRA) {
            Opcode = 0x6f,
            AddrMode = AddressMode.Abs,
            CycDef = 6
        };
        public static readonly OpDef OpRRA_DPIndIndexY = new OpDef(OpRRA) {
            Opcode = 0x73,
            AddrMode = AddressMode.DPIndIndexY,
            CycDef = 8
        };
        public static readonly OpDef OpRRA_DPIndexX = new OpDef(OpRRA) {
            Opcode = 0x77,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 6
        };
        public static readonly OpDef OpRRA_AbsIndexY = new OpDef(OpRRA) {
            Opcode = 0x7b,
            AddrMode = AddressMode.AbsIndexY,
            CycDef = 7
        };
        public static readonly OpDef OpRRA_AbsIndexX = new OpDef(OpRRA) {
            Opcode = 0x7f,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 7
        };
        public static readonly OpDef OpSAX_DPIndexXInd = new OpDef(OpSAX) {
            Opcode = 0x83,
            AddrMode = AddressMode.DPIndexXInd,
            CycDef = 6
        };
        public static readonly OpDef OpSAX_DP = new OpDef(OpSAX) {
            Opcode = 0x87,
            AddrMode = AddressMode.DP,
            CycDef = 3
        };
        public static readonly OpDef OpSAX_Absolute = new OpDef(OpSAX) {
            Opcode = 0x8f,
            AddrMode = AddressMode.Abs,
            CycDef = 4
        };
        public static readonly OpDef OpSAX_DPIndexY = new OpDef(OpSAX) {
            Opcode = 0x97,
            AddrMode = AddressMode.DPIndexY,
            CycDef = 4
        };
        public static readonly OpDef OpLAX_DPIndexXInd = new OpDef(OpLAX) {
            Opcode = 0xa3,
            AddrMode = AddressMode.DPIndexXInd,
            CycDef = 6
        };
        public static readonly OpDef OpLAX_DP = new OpDef(OpLAX) {
            Opcode = 0xa7,
            AddrMode = AddressMode.DP,
            CycDef = 3
        };
        public static readonly OpDef OpLAX_Absolute = new OpDef(OpLAX) {
            Opcode = 0xaf,
            AddrMode = AddressMode.Abs,
            CycDef = 4
        };
        public static readonly OpDef OpLAX_DPIndIndexY = new OpDef(OpLAX) {
            Opcode = 0xb3,
            AddrMode = AddressMode.DPIndIndexY,
            CycDef = 5 | (int)(CycleMod.OneIfIndexPage)
        };
        public static readonly OpDef OpLAX_DPIndexY = new OpDef(OpLAX) {
            Opcode = 0xb7,
            AddrMode = AddressMode.DPIndexY,
            CycDef = 4
        };
        public static readonly OpDef OpLAX_AbsIndexY = new OpDef(OpLAX) {
            Opcode = 0xbf,
            AddrMode = AddressMode.AbsIndexY,
            CycDef = 4 | (int)(CycleMod.OneIfIndexPage)
        };
        public static readonly OpDef OpDCP_DPIndexXInd = new OpDef(OpDCP) {
            Opcode = 0xc3,
            AddrMode = AddressMode.DPIndexXInd,
            CycDef = 8
        };
        public static readonly OpDef OpDCP_DP = new OpDef(OpDCP) {
            Opcode = 0xc7,
            AddrMode = AddressMode.DP,
            CycDef = 5
        };
        public static readonly OpDef OpDCP_Abs = new OpDef(OpDCP) {
            Opcode = 0xcf,
            AddrMode = AddressMode.Abs,
            CycDef = 6
        };
        public static readonly OpDef OpDCP_DPIndIndexY = new OpDef(OpDCP) {
            Opcode = 0xd3,
            AddrMode = AddressMode.DPIndIndexY,
            CycDef = 8
        };
        public static readonly OpDef OpDCP_DPIndexX = new OpDef(OpDCP) {
            Opcode = 0xd7,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 6
        };
        public static readonly OpDef OpDCP_AbsIndexY = new OpDef(OpDCP) {
            Opcode = 0xdb,
            AddrMode = AddressMode.AbsIndexY,
            CycDef = 7
        };
        public static readonly OpDef OpDCP_AbsIndexX = new OpDef(OpDCP) {
            Opcode = 0xdf,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 7
        };
        public static readonly OpDef OpISC_DPIndexXInd = new OpDef(OpISC) {
            Opcode = 0xe3,
            AddrMode = AddressMode.DPIndexXInd,
            CycDef = 8
        };
        public static readonly OpDef OpISC_DP = new OpDef(OpISC) {
            Opcode = 0xe7,
            AddrMode = AddressMode.DP,
            CycDef = 5
        };
        public static readonly OpDef OpISC_Abs = new OpDef(OpISC) {
            Opcode = 0xef,
            AddrMode = AddressMode.Abs,
            CycDef = 6
        };
        public static readonly OpDef OpISC_DPIndIndexY = new OpDef(OpISC) {
            Opcode = 0xf3,
            AddrMode = AddressMode.DPIndIndexY,
            CycDef = 8
        };
        public static readonly OpDef OpISC_DPIndexX = new OpDef(OpISC) {
            Opcode = 0xf7,
            AddrMode = AddressMode.DPIndexX,
            CycDef = 6
        };
        public static readonly OpDef OpISC_AbsIndexY = new OpDef(OpISC) {
            Opcode = 0xfb,
            AddrMode = AddressMode.AbsIndexY,
            CycDef = 7
        };
        public static readonly OpDef OpISC_AbsIndexX = new OpDef(OpISC) {
            Opcode = 0xff,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 7
        };
        public static readonly OpDef OpALR_Imm = new OpDef(OpALR) {
            Opcode = 0x4b,
            AddrMode = AddressMode.Imm,
            CycDef = 2
        };
        public static readonly OpDef OpARR_Imm = new OpDef(OpARR) {
            Opcode = 0x6b,
            AddrMode = AddressMode.Imm,
            CycDef = 2
        };
        public static readonly OpDef OpANE_Imm = new OpDef(OpANE) {
            Opcode = 0x8b,
            AddrMode = AddressMode.Imm,
            CycDef = 2
        };
        public static readonly OpDef OpLAX_Imm = new OpDef(OpLAX) {
            Opcode = 0xab,
            AddrMode = AddressMode.Imm,
            CycDef = 2
        };
        public static readonly OpDef OpSBX_Imm = new OpDef(OpSBX) {
            Opcode = 0xcb,
            AddrMode = AddressMode.Imm,
            CycDef = 2
        };
        public static readonly OpDef OpDOP_Imm = new OpDef(OpDOP) {
            // multiple opcodes
            AddrMode = AddressMode.Imm,
            CycDef = 2
        };
        public static readonly OpDef OpDOP_DP = new OpDef(OpDOP) {
            // multiple opcodes
            AddrMode = AddressMode.DP,
            CycDef = 3
        };
        public static readonly OpDef OpDOP_DPIndexX = new OpDef(OpDOP) {
            // multiple opcodes
            AddrMode = AddressMode.DPIndexX,
            CycDef = 4
        };
        public static readonly OpDef OpTOP_Abs = new OpDef(OpTOP) {
            Opcode = 0x0c,
            AddrMode = AddressMode.Abs,
            CycDef = 4
        };
        public static readonly OpDef OpTOP_AbsIndeX = new OpDef(OpTOP) {
            // multiple opcodes
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 4 | (int)(CycleMod.OneIfIndexPage)
        };
        public static readonly OpDef OpJAM_Implied = new OpDef(OpJAM) {
            // multiple opcodes
            AddrMode = AddressMode.Implied,
            CycDef = 1
        };
        public static readonly OpDef OpTAS_AbsIndexY  = new OpDef(OpTAS) {
            Opcode = 0x9b,
            AddrMode = AddressMode.AbsIndexY,
            CycDef = 5
        };
        public static readonly OpDef OpSHY_AbsIndexX = new OpDef(OpSHY) {
            Opcode = 0x9c,
            AddrMode = AddressMode.AbsIndexX,
            CycDef = 5
        };
        public static readonly OpDef OpSHX_AbsIndexY = new OpDef(OpSHX) {
            Opcode = 0x9e,
            AddrMode = AddressMode.AbsIndexY,
            CycDef = 5
        };
        public static readonly OpDef OpSHA_DPIndIndexY = new OpDef(OpSHA) {
            Opcode = 0x93,
            AddrMode = AddressMode.DPIndIndexY,
            CycDef = 6
        };
        public static readonly OpDef OpSHA_AbsIndexY = new OpDef(OpSHA) {
            Opcode = 0x9f,
            AddrMode = AddressMode.AbsIndexY,
            CycDef = 5
        };
        public static readonly OpDef OpANC_Imm = new OpDef(OpANC) {
            // multiple opcodes
            AddrMode = AddressMode.Imm,
            CycDef = 2
        };
        public static readonly OpDef OpLAS_AbsIndexY = new OpDef(OpLAS) {
            Opcode = 0xbb,
            AddrMode = AddressMode.AbsIndexY,
            CycDef = 4 | (int)(CycleMod.OneIfIndexPage)
        };

        #endregion Undocumented 6502

        #region Undocumented 65C02

        // ======================================================================================
        // Undocumented 65C02 instructions.
        //
        // The 65C02 declared all undefined opcodes to be NOPs, but some of them can have
        // side effects.
        //
        // References:
        //   http://laughtonelectronics.com/Arcana/KimKlone/Kimklone_opcode_mapping.html
        //

        // Most undocumented instructions are a single-byte, single-cycle NOP.  I don't know
        // if these are "undocumented" in the strictest sense of the word, but we'll treat
        // them that way for disassembly purposes.
        public static readonly OpDef OpNOP_65C02 = new OpDef() {
            IsUndocumented = true,
            Mnemonic = OpName.NOP,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.None,
            AddrMode = AddressMode.Implied,
            CycDef = 1
        };

        // 65C02 undocumented "Load and Discard" instruction.  These cause bus traffic.
        private static OpDef OpLDD = new OpDef() {
            IsUndocumented = true,
            Mnemonic = OpName.LDD,
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.Read,
            AddrMode = AddressMode.Implied
        };
        public static readonly OpDef OpLDD_Absolute = new OpDef(OpLDD) {
            // multiple opcodes
            AddrMode = AddressMode.Abs,
            CycDef = 4
        };
        public static readonly OpDef OpLDD_DP = new OpDef(OpLDD) {
            // multiple opcodes
            AddrMode = AddressMode.DP,
            CycDef = 3
        };
        public static readonly OpDef OpLDD_DPIndexX = new OpDef(OpLDD) {
            // multiple opcodes
            AddrMode = AddressMode.DPIndexX,
            CycDef = 4
        };
        public static readonly OpDef OpLDD_Imm = new OpDef(OpLDD) {
            // multiple opcodes
            AddrMode = AddressMode.Imm,
            CycDef = 2
        };
        public static readonly OpDef OpLDD_Weird = new OpDef(OpLDD) {
            // multiple opcodes
            AddrMode = AddressMode.Abs,  // not really, but has the right number of bytes
            CycDef = 8
        };

        #endregion Undocumented

        #region Rockwell extensions

        // ======================================================================================
        // Rockwell extensions to the 65C02.
        //
        // These are declared separately because they overlap with 65816 instructions.  The
        // 32 opcodes occupy the numbers $x7 and $xf.
        //

        private static OpDef OpBBR = new OpDef() {
            IsNumberedBitOp = true,
            Mnemonic = "???",
            Effect = FlowEffect.ConditionalBranch,
            BaseMemEffect = MemoryEffect.None
        };
        private static OpDef OpBBS = new OpDef() {
            IsNumberedBitOp = true,
            Mnemonic = "???",
            Effect = FlowEffect.ConditionalBranch,
            BaseMemEffect = MemoryEffect.None
        };
        private static OpDef OpRMB = new OpDef() {
            IsNumberedBitOp = true,
            Mnemonic = "???",
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.ReadModifyWrite
        };
        private static OpDef OpSMB = new OpDef() {
            IsNumberedBitOp = true,
            Mnemonic = "???",
            Effect = FlowEffect.Cont,
            BaseMemEffect = MemoryEffect.ReadModifyWrite
        };

        public static readonly OpDef OpBBR0_DPPCRel = new OpDef(OpBBR) {
            Mnemonic = OpName.BBR0,
            Opcode = 0x0f,
            AddrMode = AddressMode.DPPCRel,
            CycDef = 5
        };
        public static readonly OpDef OpBBR1_DPPCRel = new OpDef(OpBBR) {
            Mnemonic = OpName.BBR1,
            Opcode = 0x1f,
            AddrMode = AddressMode.DPPCRel,
            CycDef = 5
        };
        public static readonly OpDef OpBBR2_DPPCRel = new OpDef(OpBBR) {
            Mnemonic = OpName.BBR2,
            Opcode = 0x2f,
            AddrMode = AddressMode.DPPCRel,
            CycDef = 5
        };
        public static readonly OpDef OpBBR3_DPPCRel = new OpDef(OpBBR) {
            Mnemonic = OpName.BBR3,
            Opcode = 0x3f,
            AddrMode = AddressMode.DPPCRel,
            CycDef = 5
        };
        public static readonly OpDef OpBBR4_DPPCRel = new OpDef(OpBBR) {
            Mnemonic = OpName.BBR4,
            Opcode = 0x4f,
            AddrMode = AddressMode.DPPCRel,
            CycDef = 5
        };
        public static readonly OpDef OpBBR5_DPPCRel = new OpDef(OpBBR) {
            Mnemonic = OpName.BBR5,
            Opcode = 0x5f,
            AddrMode = AddressMode.DPPCRel,
            CycDef = 5
        };
        public static readonly OpDef OpBBR6_DPPCRel = new OpDef(OpBBR) {
            Mnemonic = OpName.BBR6,
            Opcode = 0x6f,
            AddrMode = AddressMode.DPPCRel,
            CycDef = 5
        };
        public static readonly OpDef OpBBR7_DPPCRel = new OpDef(OpBBR) {
            Mnemonic = OpName.BBR7,
            Opcode = 0x7f,
            AddrMode = AddressMode.DPPCRel,
            CycDef = 5
        };
        public static readonly OpDef OpBBS0_DPPCRel = new OpDef(OpBBS) {
            Mnemonic = OpName.BBS0,
            Opcode = 0x8f,
            AddrMode = AddressMode.DPPCRel,
            CycDef = 5
        };
        public static readonly OpDef OpBBS1_DPPCRel = new OpDef(OpBBS) {
            Mnemonic = OpName.BBS1,
            Opcode = 0x9f,
            AddrMode = AddressMode.DPPCRel,
            CycDef = 5
        };
        public static readonly OpDef OpBBS2_DPPCRel = new OpDef(OpBBS) {
            Mnemonic = OpName.BBS2,
            Opcode = 0xaf,
            AddrMode = AddressMode.DPPCRel,
            CycDef = 5
        };
        public static readonly OpDef OpBBS3_DPPCRel = new OpDef(OpBBS) {
            Mnemonic = OpName.BBS3,
            Opcode = 0xbf,
            AddrMode = AddressMode.DPPCRel,
            CycDef = 5
        };
        public static readonly OpDef OpBBS4_DPPCRel = new OpDef(OpBBS) {
            Mnemonic = OpName.BBS4,
            Opcode = 0xcf,
            AddrMode = AddressMode.DPPCRel,
            CycDef = 5
        };
        public static readonly OpDef OpBBS5_DPPCRel = new OpDef(OpBBS) {
            Mnemonic = OpName.BBS5,
            Opcode = 0xdf,
            AddrMode = AddressMode.DPPCRel,
            CycDef = 5
        };
        public static readonly OpDef OpBBS6_DPPCRel = new OpDef(OpBBS) {
            Mnemonic = OpName.BBS6,
            Opcode = 0xef,
            AddrMode = AddressMode.DPPCRel,
            CycDef = 5
        };
        public static readonly OpDef OpBBS7_DPPCRel = new OpDef(OpBBS) {
            Mnemonic = OpName.BBS7,
            Opcode = 0xff,
            AddrMode = AddressMode.DPPCRel,
            CycDef = 5
        };
        public static readonly OpDef OpRMB0_DP = new OpDef(OpRMB) {
            Mnemonic = OpName.RMB0,
            Opcode = 0x07,
            AddrMode = AddressMode.DP,
            CycDef = 5
        };
        public static readonly OpDef OpRMB1_DP = new OpDef(OpRMB) {
            Mnemonic = OpName.RMB1,
            Opcode = 0x17,
            AddrMode = AddressMode.DP,
            CycDef = 5
        };
        public static readonly OpDef OpRMB2_DP = new OpDef(OpRMB) {
            Mnemonic = OpName.RMB2,
            Opcode = 0x27,
            AddrMode = AddressMode.DP,
            CycDef = 5
        };
        public static readonly OpDef OpRMB3_DP = new OpDef(OpRMB) {
            Mnemonic = OpName.RMB3,
            Opcode = 0x37,
            AddrMode = AddressMode.DP,
            CycDef = 5
        };
        public static readonly OpDef OpRMB4_DP = new OpDef(OpRMB) {
            Mnemonic = OpName.RMB4,
            Opcode = 0x47,
            AddrMode = AddressMode.DP,
            CycDef = 5
        };
        public static readonly OpDef OpRMB5_DP = new OpDef(OpRMB) {
            Mnemonic = OpName.RMB5,
            Opcode = 0x57,
            AddrMode = AddressMode.DP,
            CycDef = 5
        };
        public static readonly OpDef OpRMB6_DP = new OpDef(OpRMB) {
            Mnemonic = OpName.RMB6,
            Opcode = 0x67,
            AddrMode = AddressMode.DP,
            CycDef = 5
        };
        public static readonly OpDef OpRMB7_DP = new OpDef(OpRMB) {
            Mnemonic = OpName.RMB7,
            Opcode = 0x77,
            AddrMode = AddressMode.DP,
            CycDef = 5
        };
        public static readonly OpDef OpSMB0_DP = new OpDef(OpSMB) {
            Mnemonic = OpName.SMB0,
            Opcode = 0x87,
            AddrMode = AddressMode.DP,
            CycDef = 5
        };
        public static readonly OpDef OpSMB1_DP = new OpDef(OpSMB) {
            Mnemonic = OpName.SMB1,
            Opcode = 0x97,
            AddrMode = AddressMode.DP,
            CycDef = 5
        };
        public static readonly OpDef OpSMB2_DP = new OpDef(OpSMB) {
            Mnemonic = OpName.SMB2,
            Opcode = 0xa7,
            AddrMode = AddressMode.DP,
            CycDef = 5
        };
        public static readonly OpDef OpSMB3_DP = new OpDef(OpSMB) {
            Mnemonic = OpName.SMB3,
            Opcode = 0xb7,
            AddrMode = AddressMode.DP,
            CycDef = 5
        };
        public static readonly OpDef OpSMB4_DP = new OpDef(OpSMB) {
            Mnemonic = OpName.SMB4,
            Opcode = 0xc7,
            AddrMode = AddressMode.DP,
            CycDef = 5
        };
        public static readonly OpDef OpSMB5_DP = new OpDef(OpSMB) {
            Mnemonic = OpName.SMB5,
            Opcode = 0xd7,
            AddrMode = AddressMode.DP,
            CycDef = 5
        };
        public static readonly OpDef OpSMB6_DP = new OpDef(OpSMB) {
            Mnemonic = OpName.SMB6,
            Opcode = 0xe7,
            AddrMode = AddressMode.DP,
            CycDef = 5
        };
        public static readonly OpDef OpSMB7_DP = new OpDef(OpSMB) {
            Mnemonic = OpName.SMB7,
            Opcode = 0xf7,
            AddrMode = AddressMode.DP,
            CycDef = 5
        };

        #endregion Rockwell extensions


        /// <summary>
        /// Generates one of the multiply-defined opcodes from a prototype.  This is
        /// particularly useful for undocumented opcodes that are repeated several times.  In
        /// a couple of cases (SBC, some NOPs), the undocumented opcode is just another
        /// reference to an existing instruction.
        /// </summary>
        /// <param name="opcode">Instruction opcode.</param>
        /// <param name="proto">Instruction prototype.</param>
        /// <returns>Newly-created OpDef.</returns>
        public static OpDef GenerateUndoc(byte opcode, OpDef proto) {
            return new OpDef(proto) { Opcode = opcode, IsUndocumented = true };
        }

        public override string ToString() {
            return Opcode.ToString("x2") + "/" + Mnemonic + " " + AddrMode;
        }
    }
}
