/*
 * 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>
    /// CPU definition.  Includes a set of 256 opcodes.
    /// </summary>
    public class CpuDef {
        /// <summary>
        /// Human-readable name.
        /// </summary>
        public string Name { get; private set; }

        /// <summary>
        /// Maximum possible address.
        /// </summary>
        public int MaxAddressValue { get; private set; }

        /// <summary>
        /// Does this CPU have a 16-bit address space?
        /// </summary>
        public bool HasAddr16 { get { return MaxAddressValue <= 0xffff; } }

        /// <summary>
        /// Does this CPU support the emulation flag (65802/65816)?
        /// </summary>
        public bool HasEmuFlag { get; private set; }

        /// <summary>
        /// CPU type value.
        /// </summary>
        public CpuType Type { get; private set; }

        /// <summary>
        /// True if undocumented opcodes are included.  (This does not mean that the CPU has
        /// undocumented opcodes, only that they haven't been stripped out if they exist.)
        /// </summary>
        public bool HasUndocumented { get; private set; }

        /// <summary>
        /// Instruction set, 256 entries.
        /// </summary>
        private OpDef[] mOpDefs;

        /// <summary>
        /// Cycle counts, 256 entries.
        /// </summary>
        private int[] mCycleCounts;

        /// <summary>
        /// Cycle count modifiers, 256 entries.
        /// </summary>
        private OpDef.CycleMod[] mCycleMods;

        /// <summary>
        /// List of "interesting" CPUs.  These will be presented in the system definition
        /// file when starting a new project.  The actual set of CPUs that are unique (from
        /// our perspective) is much smaller, as most of these can be represented accurately
        /// (for disassembly purposes) by a handful of archetypes.
        /// </summary>
        public enum CpuType {
            CpuUnknown = 0,
            Cpu6502,    // Apple ][+
            Cpu6502B,   // Atari 800
            Cpu6507,    // Atari 2600
            Cpu6502C,   // Atari 5200
            Cpu6510,    // Commodore 64
            Cpu8502,    // Commodore 128
            Cpu2A03,    // NES

            Cpu65C02,   // Apple //e
            Cpu65SC02,  // Atari Lynx
            CpuW65C02,  // ?

            Cpu65802,   // ?
            Cpu65816,   // Apple IIgs
            Cpu5A22     // SNES
        }

        /// <summary>
        /// Converts a CPU type name string to a CpuType value.  Used for deserialization.
        /// </summary>
        /// <param name="name">Case-sensitive CPU name</param>
        /// <returns>CpuType value, or CpuUnknown if name wasn't recognized.</returns>
        public static CpuType GetCpuTypeFromName(string name) {
            switch (name) {
                case "6502":        return CpuType.Cpu6502;
                case "6502B":       return CpuType.Cpu6502B;
                case "6502C":       return CpuType.Cpu6502C;
                case "6507":        return CpuType.Cpu6507;
                case "6510":        return CpuType.Cpu6510;
                case "8502":        return CpuType.Cpu8502;
                case "2A03":        return CpuType.Cpu2A03;
                case "65C02":       return CpuType.Cpu65C02;
                case "65SC02":      return CpuType.Cpu65SC02;
                case "W65C02":      return CpuType.CpuW65C02;
                case "65802":       return CpuType.Cpu65802;
                case "65816":       return CpuType.Cpu65816;
                case "5A22":        return CpuType.Cpu5A22;
                default:
                    return CpuType.CpuUnknown;
            }
        }

        /// <summary>
        /// Converts a CpuType value to a CPU type name string.  Used for serialization.
        /// </summary>
        /// <param name="type">CPU type.</param>
        /// <returns>CPU name string.</returns>
        public static string GetCpuNameFromType(CpuType type) {
            switch (type) {
                case CpuType.Cpu6502:   return "6502";
                case CpuType.Cpu6502B:  return "6502B";
                case CpuType.Cpu6502C:  return "6502C";
                case CpuType.Cpu6507:   return "6507";
                case CpuType.Cpu6510:   return "6510";
                case CpuType.Cpu8502:   return "8502";
                case CpuType.Cpu2A03:   return "2A03";
                case CpuType.Cpu65C02:  return "65C02";
                case CpuType.Cpu65SC02: return "65SC02";
                case CpuType.CpuW65C02: return "W65C02";
                case CpuType.Cpu65802:  return "65802";
                case CpuType.Cpu65816:  return "65816";
                case CpuType.Cpu5A22:   return "5A22";
                default:
                    return "65??";
            }
    }

        /// <summary>
        /// Generates a CpuDef that best matches the parameters.
        /// </summary>
        /// <param name="type">Specific CPU we want.</param>
        /// <param name="includeUndocumented">Set to true if "undocumented" opcodes should
        ///   be included in the definition.</param>
        /// <returns>Best CpuDef.</returns>
        public static CpuDef GetBestMatch(CpuType type, bool includeUndocumented,
                bool twoByteBrk) {
            // Many 65xx variants boil down to a 6502, 65C02, or 65816, at least as far as
            // a disassembler needs to know.  WDC's W65C02 has the Rockwell extensions plus
            // a couple more, so it works for the extended 65C02 variants.
            //
            // These don't strictly fit, and would need full definitions:
            //
            //  Hudson Soft HuC6280 (PC Engine / TurboGrafx)
            //  Commodore CSG 4510 / CSG 65CE02 (Amiga A2232 serial port; 4510 has one
            //   additional instruction, so use that as archetype)
            //  Jeri's 65DTV02 (used in C64DTV single-chip computer); same as 6502 with
            //   some differences in illegal opcodes
            //  Eloraam 65EL02 (defined in a Minecraft-based emulator)

            CpuDef cpuDef;
            switch (type) {
                case CpuType.Cpu65802:
                case CpuType.Cpu65816:
                case CpuType.Cpu5A22:
                    cpuDef = Cpu65816;
                    break;
                case CpuType.Cpu65C02:
                case CpuType.Cpu65SC02:
                    cpuDef = Cpu65C02;
                    break;
                case CpuType.CpuW65C02:
                    cpuDef = CpuW65C02;
                    break;
                default:
                    // 6502, 6502B, 6502C, 6507, 6510, 8502, 2A03
                    cpuDef = Cpu6502;
                    break;
            }

            cpuDef.GenerateCycleCounts();

            // If we don't want undocumented opcodes, strip them out of the definition.
            // Entries are replaced with OpInvalid.
            if (!includeUndocumented) {
                CpuDef stripped = new CpuDef(cpuDef);
                for (int i = 0; i < 256; i++) {
                    if (cpuDef.mOpDefs[i].IsUndocumented) {
                        stripped.mOpDefs[i] = OpDef.OpInvalid;
                    } else {
                        stripped.mOpDefs[i] = cpuDef.mOpDefs[i];
                    }
                }
                cpuDef = stripped;
            }

            // If we want two-byte BRKs, replace the entry with the StackInt form.  Copy the rest.
            if (twoByteBrk) {
                CpuDef bigBrkDef = new CpuDef(cpuDef);
                bigBrkDef.mOpDefs[0] = OpDef.OpBRK_StackInt;
                for (int i = 1; i < 256; i++) {
                    bigBrkDef.mOpDefs[i] = cpuDef.mOpDefs[i];
                }
                cpuDef = bigBrkDef;
            }

            cpuDef.HasUndocumented = includeUndocumented;

            return cpuDef;
        }

        /// <summary>
        /// Constructor.
        /// </summary>
        /// <param name="name">Human-readable name.</param>
        /// <param name="maxAddressValue">Address space bits (16 or 24).</param>
        private CpuDef(string name, int maxAddressValue, bool hasEmuFlag) {
            Name = name;
            MaxAddressValue = maxAddressValue;
            HasEmuFlag = hasEmuFlag;
            HasUndocumented = true;
        }

        /// <summary>
        /// Copy constructor.  Does not copy the contents of mOpDefs.  Only used internally.
        /// </summary>
        /// <param name="src">Object to copy from.</param>
        private CpuDef(CpuDef src) {
            Name = src.Name;
            MaxAddressValue = src.MaxAddressValue;
            HasEmuFlag = src.HasEmuFlag;
            Type = src.Type;
            mCycleCounts = src.mCycleCounts;
            mCycleMods = src.mCycleMods;

            mOpDefs = new OpDef[256];
        }


        /// <summary>
        /// Returns an entry from the OpDef array for the specified opcode, 0-255.
        /// </summary>
        /// <param name="op">Instruction opcode number (0-255).</param>
        /// <returns>Instruction definition.</returns>
        public OpDef GetOpDef(int op) { return mOpDefs[op]; }

        /// <summary>
        /// Returns the set of cycle mods for the given instruction that are in effect
        /// for the current CPU.
        /// </summary>
        /// <param name="op">Instruction opcode number (0-255).</param>
        /// <returns>Reduced cycle mod set.</returns>
        public OpDef.CycleMod GetOpCycleMod(int op) { return mCycleMods[op]; }

        /// <summary>
        /// Indexer.  Returns the definition of opcode N.
        /// </summary>
        public OpDef this[int op] {
            get { return mOpDefs[op]; }
        }

        /// <summary>
        /// Returns the number of cycles required to execute the instruction.  If the value
        /// is negative, the negated value represents the minimum number of cycles for an
        /// instruction with variable timing.
        /// 
        /// The value returned will factor in any CPU-specific aspects.
        /// </summary>
        /// <param name="opNum">Instruction opcode value.</param>
        /// <returns>Cycle count.</returns>
        public int GetCycles(int opNum, StatusFlags flags, OpDef.BranchTaken branchTaken,
                bool branchCrossesPage) {
            // The irrelevant modifiers have already been stripped out, e.g. OneIfD1 only
            // affects the 65C02.
            OpDef.CycleMod mods = mCycleMods[opNum];
            int cycles = mCycleCounts[opNum];

            // Walk through the various cycle mods.  If we can evaluate them definitively,
            // do so now and remove them from the set.

            // The M/X flags are defined to be in one state or the other, even when the flag
            // value is indeterminate, because we have to be able to size immediate operands
            // appropriately.  So there's no ambiguity here even when there's ambiguity.  We
            // make a similar statement about the E flag.
            if ((mods & OpDef.CycleMod.OneIfM0) != 0) {
                if (!flags.IsShortM) {
                    cycles++;
                }
                mods &= ~OpDef.CycleMod.OneIfM0;
            }
            if ((mods & OpDef.CycleMod.TwoIfM0) != 0) {
                if (!flags.IsShortM) {
                    cycles += 2;
                }
                mods &= ~OpDef.CycleMod.TwoIfM0;
            }
            if ((mods & OpDef.CycleMod.OneIfX0) != 0) {
                if (!flags.IsShortX) {
                    cycles++;
                }
                mods &= ~OpDef.CycleMod.OneIfX0;
            }
            if ((mods & OpDef.CycleMod.OneIfE0) != 0) {
                if (!flags.IsEmulationMode) {
                    cycles++;
                }
                mods &= ~OpDef.CycleMod.OneIfE0;
            }

            // Some of these can be known, some can't.
            if ((mods & OpDef.CycleMod.OneIfD1) != 0) {
                if (flags.D == 1) {
                    cycles++;
                }
                if (flags.D == 0 || flags.D == 1) {
                    mods &= ~OpDef.CycleMod.OneIfD1;
                }
            }
            if ((mods & OpDef.CycleMod.OneIfBranchTaken) != 0) {
                if (branchTaken == OpDef.BranchTaken.Always) {
                    cycles++;
                }
                if (branchTaken != OpDef.BranchTaken.Indeterminate) {
                    mods &= ~OpDef.CycleMod.OneIfBranchTaken;
                }
            }
            if ((mods & OpDef.CycleMod.OneIfBranchPage) != 0) {
                if (branchCrossesPage && flags.IsEmulationMode) {
                    cycles++;   // +1 unless we're in native mode on 65816
                }
                mods &= ~OpDef.CycleMod.OneIfBranchPage;
            }

            // We can't evaluate OneIfDpNonzero, OneIfIndexPage, or MinusOneIfNoPage.
            // OneIf65C02 was handled earlier.
            // TODO(maybe): in some cases we can know that the index doesn't cross a
            //   page boundary by checking the address, e.g. "LDA $2000,X" can't cross.

            if (mods != 0) {
                // Some unresolved mods remain.
                cycles = -cycles;
            }
            return cycles;
        }


        /// <summary>
        /// Consistency test.
        /// </summary>
        /// <returns>True on success.</returns>
        public static bool DebugValidate() {
            InternalValidate(Cpu6502);
            InternalValidate(Cpu65C02);
            InternalValidate(CpuW65C02);
            InternalValidate(Cpu65816);
            Debug.WriteLine("CpuDef: tests successful");
            return true;
        }
        private static void InternalValidate(CpuDef cdef) {
            for (int i = 0; i < 256; i++) {
                OpDef op = cdef.mOpDefs[i];
                if (op.Opcode != i && op.AddrMode != OpDef.AddressMode.Unknown) {
                    throw new Exception("CpuDef for " + cdef.Type + ": entry 0x" +
                        i.ToString("x") + " has value " + op.Opcode.ToString("x"));
                }
                if (op.AddrMode != OpDef.AddressMode.Unknown && op.Cycles == 0) {
                    throw new Exception("Instruction 0x" + i.ToString("x2") + ": " +
                        op + " missing cycles");
                }
            }
        }

        public override string ToString() {
            return Name + " (has16=" + HasAddr16 + ", hasEmu=" + HasEmuFlag + ", hasUndoc=" +
                HasUndocumented + ")";
        }

        /// <summary>
        /// Generates the mCycleCounts and mCycleMods arrays.
        /// </summary>
        private void GenerateCycleCounts() {
            if (mCycleCounts != null) {
                return;
            }
            mCycleCounts = new int[256];
            mCycleMods = new OpDef.CycleMod[256];

            // Figure out which mods apply for this CPU.
            OpDef.CycleMod ignoreMask = 0;
            switch (Type) {
                case CpuType.Cpu6502:
                    ignoreMask = OpDef.CycleMod.OneIfM0 |
                                 OpDef.CycleMod.TwoIfM0 |
                                 OpDef.CycleMod.OneIfX0 |
                                 OpDef.CycleMod.OneIfDpNonzero |
                                 OpDef.CycleMod.OneIfD1 |
                                 OpDef.CycleMod.OneIfE0 |
                                 OpDef.CycleMod.OneIf65C02 |
                                 OpDef.CycleMod.MinusOneIfNoPage |
                                 OpDef.CycleMod.BlockMove;
                    break;
                case CpuType.Cpu65C02:
                case CpuType.CpuW65C02:
                    ignoreMask = OpDef.CycleMod.OneIfM0 |
                                 OpDef.CycleMod.TwoIfM0 |
                                 OpDef.CycleMod.OneIfX0 |
                                 OpDef.CycleMod.OneIfDpNonzero |
                                 OpDef.CycleMod.OneIfE0 |
                                 OpDef.CycleMod.BlockMove;
                    break;
                case CpuType.Cpu65816:
                    ignoreMask = OpDef.CycleMod.OneIfD1 |
                                 OpDef.CycleMod.OneIf65C02 |
                                 OpDef.CycleMod.MinusOneIfNoPage;
                    break;
                default:
                    Debug.Assert(false, "unsupported cpu type " + Type);
                    return;
            }

            // If an instruction has one or more applicable mods, declare it as variable.
            for (int i = 0; i < 256; i++) {
                OpDef op = mOpDefs[i];
                int baseCycles = op.Cycles;
                OpDef.CycleMod mods = op.CycleMods & ~ignoreMask;
                if ((mods & OpDef.CycleMod.OneIf65C02) != 0) {
                    // This isn't variable -- the instruction always takes one cycle longer
                    // on the 65C02.  (Applies to $6C, JMP (addr).)
                    Debug.Assert(Type == CpuType.Cpu65C02 || Type == CpuType.CpuW65C02);
                    baseCycles++;
                    mods &= ~OpDef.CycleMod.OneIf65C02;
                }
                mCycleCounts[i] = baseCycles;
                mCycleMods[i] = mods;
            }
        }


        // Classic MOS 6502, with full set of undocumented opcodes.
        private static CpuDef Cpu6502 { get; } = new CpuDef("MOS 6502", (1 << 16) - 1, false) {
            Type = CpuType.Cpu6502,
            mOpDefs = new OpDef[] {
                OpDef.OpBRK_Implied,            // 0x00
                OpDef.OpORA_DPIndexXInd,
                OpDef.GenerateUndoc(0x02, OpDef.OpJAM_Implied),
                OpDef.OpSLO_DPIndexXInd,
                OpDef.GenerateUndoc(0x04, OpDef.OpDOP_DP),
                OpDef.OpORA_DP,
                OpDef.OpASL_DP,
                OpDef.OpSLO_DP,
                OpDef.OpPHP_StackPush,          // 0x08
                OpDef.OpORA_Imm,
                OpDef.OpASL_Acc,
                OpDef.GenerateUndoc(0x0b, OpDef.OpANC_Imm),
                OpDef.OpTOP_Abs,
                OpDef.OpORA_Abs,
                OpDef.OpASL_Abs,
                OpDef.OpSLO_Absolute,
                OpDef.OpBPL_PCRel,              // 0x10
                OpDef.OpORA_DPIndIndexY,
                OpDef.GenerateUndoc(0x12, OpDef.OpJAM_Implied),
                OpDef.OpSLO_DPIndIndexY,
                OpDef.GenerateUndoc(0x14, OpDef.OpDOP_DPIndexX),
                OpDef.OpORA_DPIndexX,
                OpDef.OpASL_DPIndexX,
                OpDef.OpSLO_DPIndexX,
                OpDef.OpCLC_Implied,            // 0x18
                OpDef.OpORA_AbsIndexY,
                OpDef.GenerateUndoc(0x1a, OpDef.OpNOP_Implied),
                OpDef.OpSLO_AbsIndexY,
                OpDef.GenerateUndoc(0x1c, OpDef.OpTOP_AbsIndeX),
                OpDef.OpORA_AbsIndexX,
                OpDef.OpASL_AbsIndexX,
                OpDef.OpSLO_AbsIndexX,
                OpDef.OpJSR_Abs,                // 0x20
                OpDef.OpAND_DPIndexXInd,
                OpDef.GenerateUndoc(0x22, OpDef.OpJAM_Implied),
                OpDef.OpRLA_DPIndexXInd,
                OpDef.OpBIT_DP,
                OpDef.OpAND_DP,
                OpDef.OpROL_DP,
                OpDef.OpRLA_DP,
                OpDef.OpPLP_StackPull,          // 0x28
                OpDef.OpAND_Imm,
                OpDef.OpROL_Acc,
                OpDef.GenerateUndoc(0x2b, OpDef.OpANC_Imm),
                OpDef.OpBIT_Abs,
                OpDef.OpAND_Abs,
                OpDef.OpROL_Abs,
                OpDef.OpRLA_Absolute,
                OpDef.OpBMI_PCRel,              // 0x30
                OpDef.OpAND_DPIndIndexY,
                OpDef.GenerateUndoc(0x32, OpDef.OpJAM_Implied),
                OpDef.OpRLA_DPIndIndexY,
                OpDef.GenerateUndoc(0x34, OpDef.OpDOP_DPIndexX),
                OpDef.OpAND_DPIndexX,
                OpDef.OpROL_DPIndexX,
                OpDef.OpRLA_DPIndexX,
                OpDef.OpSEC_Implied,            // 0x38
                OpDef.OpAND_AbsIndexY,
                OpDef.GenerateUndoc(0x3a, OpDef.OpNOP_Implied),
                OpDef.OpRLA_AbsIndexY,
                OpDef.GenerateUndoc(0x3c, OpDef.OpTOP_AbsIndeX),
                OpDef.OpAND_AbsIndexX,
                OpDef.OpROL_AbsIndexX,
                OpDef.OpRLA_AbsIndexX,
                OpDef.OpRTI_StackRTI,           // 0x40
                OpDef.OpEOR_DPIndexXInd,
                OpDef.GenerateUndoc(0x42, OpDef.OpJAM_Implied),
                OpDef.OpSRE_DPIndexXInd,
                OpDef.GenerateUndoc(0x44, OpDef.OpDOP_DP),
                OpDef.OpEOR_DP,
                OpDef.OpLSR_DP,
                OpDef.OpSRE_DP,
                OpDef.OpPHA_StackPush,          // 0x48
                OpDef.OpEOR_Imm,
                OpDef.OpLSR_Acc,
                OpDef.OpALR_Imm,
                OpDef.OpJMP_Abs,
                OpDef.OpEOR_Abs,
                OpDef.OpLSR_Abs,
                OpDef.OpSRE_Absolute,
                OpDef.OpBVC_PCRel,              // 0x50
                OpDef.OpEOR_DPIndIndexY,
                OpDef.GenerateUndoc(0x52, OpDef.OpJAM_Implied),
                OpDef.OpSRE_DPIndIndexY,
                OpDef.GenerateUndoc(0x54, OpDef.OpDOP_DPIndexX),
                OpDef.OpEOR_DPIndexX,
                OpDef.OpLSR_DPIndexX,
                OpDef.OpSRE_DPIndexX,
                OpDef.OpCLI_Implied,            // 0x58
                OpDef.OpEOR_AbsIndexY,
                OpDef.GenerateUndoc(0x5a, OpDef.OpNOP_Implied),
                OpDef.OpSRE_AbsIndexY,
                OpDef.GenerateUndoc(0x5c, OpDef.OpTOP_AbsIndeX),
                OpDef.OpEOR_AbsIndexX,
                OpDef.OpLSR_AbsIndexX,
                OpDef.OpSRE_AbsIndexX,
                OpDef.OpRTS_StackRTS,           // 0x60
                OpDef.OpADC_DPIndexXInd,
                OpDef.GenerateUndoc(0x62, OpDef.OpJAM_Implied),
                OpDef.OpRRA_DPIndexXInd,
                OpDef.GenerateUndoc(0x64, OpDef.OpDOP_DP),
                OpDef.OpADC_DP,
                OpDef.OpROR_DP,
                OpDef.OpRRA_DP,
                OpDef.OpPLA_StackPull,          // 0x68
                OpDef.OpADC_Imm,
                OpDef.OpROR_Acc,
                OpDef.OpARR_Imm,
                OpDef.OpJMP_AbsInd,
                OpDef.OpADC_Abs,
                OpDef.OpROR_Abs,
                OpDef.OpRRA_Absolute,
                OpDef.OpBVS_PCRel,              // 0x70
                OpDef.OpADC_DPIndIndexY,
                OpDef.GenerateUndoc(0x72, OpDef.OpJAM_Implied),
                OpDef.OpRRA_DPIndIndexY,
                OpDef.GenerateUndoc(0x74, OpDef.OpDOP_DPIndexX),
                OpDef.OpADC_DPIndexX,
                OpDef.OpROR_DPIndexX,
                OpDef.OpRRA_DPIndexX,
                OpDef.OpSEI_Implied,            // 0x78
                OpDef.OpADC_AbsIndexY,
                OpDef.GenerateUndoc(0x7a, OpDef.OpNOP_Implied),
                OpDef.OpRRA_AbsIndexY,
                OpDef.GenerateUndoc(0x7c, OpDef.OpTOP_AbsIndeX),
                OpDef.OpADC_AbsIndexX,
                OpDef.OpROR_AbsIndexX,
                OpDef.OpRRA_AbsIndexX,
                OpDef.GenerateUndoc(0x80, OpDef.OpDOP_Imm), // 0x80
                OpDef.OpSTA_DPIndexXInd,
                OpDef.GenerateUndoc(0x82, OpDef.OpDOP_Imm),
                OpDef.OpSAX_DPIndexXInd,
                OpDef.OpSTY_DP,
                OpDef.OpSTA_DP,
                OpDef.OpSTX_DP,
                OpDef.OpSAX_DP,
                OpDef.OpDEY_Implied,            // 0x88
                OpDef.GenerateUndoc(0x89, OpDef.OpDOP_Imm),
                OpDef.OpTXA_Implied,
                OpDef.OpANE_Imm,
                OpDef.OpSTY_Abs,
                OpDef.OpSTA_Abs,
                OpDef.OpSTX_Abs,
                OpDef.OpSAX_Absolute,
                OpDef.OpBCC_PCRel,              // 0x90
                OpDef.OpSTA_DPIndIndexY,
                OpDef.GenerateUndoc(0x92, OpDef.OpJAM_Implied),
                OpDef.OpSHA_DPIndIndexY,
                OpDef.OpSTY_DPIndexX,
                OpDef.OpSTA_DPIndexX,
                OpDef.OpSTX_DPIndexY,
                OpDef.OpSAX_DPIndexY,
                OpDef.OpTYA_Implied,            // 0x98
                OpDef.OpSTA_AbsIndexY,
                OpDef.OpTXS_Implied,
                OpDef.OpTAS_AbsIndexY,
                OpDef.OpSHY_AbsIndexX,
                OpDef.OpSTA_AbsIndexX,
                OpDef.OpSHX_AbsIndexY,
                OpDef.OpSHA_AbsIndexY,
                OpDef.OpLDY_Imm,                // 0xa0
                OpDef.OpLDA_DPIndexXInd,
                OpDef.OpLDX_Imm,
                OpDef.OpLAX_DPIndexXInd,
                OpDef.OpLDY_DP,
                OpDef.OpLDA_DP,
                OpDef.OpLDX_DP,
                OpDef.OpLAX_DP,
                OpDef.OpTAY_Implied,            // 0xa8
                OpDef.OpLDA_Imm,
                OpDef.OpTAX_Implied,
                OpDef.OpLAX_Imm,
                OpDef.OpLDY_Abs,
                OpDef.OpLDA_Abs,
                OpDef.OpLDX_Abs,
                OpDef.OpLAX_Absolute,
                OpDef.OpBCS_PCRel,              // 0xb0
                OpDef.OpLDA_DPIndIndexY,
                OpDef.GenerateUndoc(0xb2, OpDef.OpJAM_Implied),
                OpDef.OpLAX_DPIndIndexY,
                OpDef.OpLDY_DPIndexX,
                OpDef.OpLDA_DPIndexX,
                OpDef.OpLDX_DPIndexY,
                OpDef.OpLAX_DPIndexY,
                OpDef.OpCLV_Implied,            // 0xb8
                OpDef.OpLDA_AbsIndexY,
                OpDef.OpTSX_Implied,
                OpDef.OpLAS_AbsIndexY,
                OpDef.OpLDY_AbsIndexX,
                OpDef.OpLDA_AbsIndexX,
                OpDef.OpLDX_AbsIndexY,
                OpDef.OpLAX_AbsIndexY,
                OpDef.OpCPY_Imm,                // 0xc0
                OpDef.OpCMP_DPIndexXInd,
                OpDef.GenerateUndoc(0xc2, OpDef.OpDOP_Imm),
                OpDef.OpDCP_DPIndexXInd,
                OpDef.OpCPY_DP,
                OpDef.OpCMP_DP,
                OpDef.OpDEC_DP,
                OpDef.OpDCP_DP,
                OpDef.OpINY_Implied,            // 0xc8
                OpDef.OpCMP_Imm,
                OpDef.OpDEX_Implied,
                OpDef.OpSBX_Imm,
                OpDef.OpCPY_Abs,
                OpDef.OpCMP_Abs,
                OpDef.OpDEC_Abs,
                OpDef.OpDCP_Abs,
                OpDef.OpBNE_PCRel,              // 0xd0
                OpDef.OpCMP_DPIndIndexY,
                OpDef.GenerateUndoc(0xd2, OpDef.OpJAM_Implied),
                OpDef.OpDCP_DPIndIndexY,
                OpDef.GenerateUndoc(0xd4, OpDef.OpDOP_DPIndexX),
                OpDef.OpCMP_DPIndexX,
                OpDef.OpDEC_DPIndexX,
                OpDef.OpDCP_DPIndexX,
                OpDef.OpCLD_Implied,            // 0xd8
                OpDef.OpCMP_AbsIndexY,
                OpDef.GenerateUndoc(0xda, OpDef.OpNOP_Implied),
                OpDef.OpDCP_AbsIndexY,
                OpDef.GenerateUndoc(0xdc, OpDef.OpTOP_AbsIndeX),
                OpDef.OpCMP_AbsIndexX,
                OpDef.OpDEC_AbsIndexX,
                OpDef.OpDCP_AbsIndexX,
                OpDef.OpCPX_Imm,                // 0xe0
                OpDef.OpSBC_DPIndexXInd,
                OpDef.GenerateUndoc(0xe2, OpDef.OpDOP_Imm),
                OpDef.OpISC_DPIndexXInd,
                OpDef.OpCPX_DP,
                OpDef.OpSBC_DP,
                OpDef.OpINC_DP,
                OpDef.OpISC_DP,
                OpDef.OpINX_Implied,            // 0xe8
                OpDef.OpSBC_Imm,
                OpDef.OpNOP_Implied,
                OpDef.GenerateUndoc(0xeb, OpDef.OpSBC_Imm),
                OpDef.OpCPX_Abs,
                OpDef.OpSBC_Abs,
                OpDef.OpINC_Abs,
                OpDef.OpISC_Abs,
                OpDef.OpBEQ_PCRel,              // 0xf0
                OpDef.OpSBC_DPIndIndexY,
                OpDef.GenerateUndoc(0xf2, OpDef.OpJAM_Implied),
                OpDef.OpISC_DPIndIndexY,
                OpDef.GenerateUndoc(0xf4, OpDef.OpDOP_DPIndexX),
                OpDef.OpSBC_DPIndexX,
                OpDef.OpINC_DPIndexX,
                OpDef.OpISC_DPIndexX,
                OpDef.OpSED_Implied,            // 0xf8
                OpDef.OpSBC_AbsIndexY,
                OpDef.GenerateUndoc(0xfa, OpDef.OpNOP_Implied),
                OpDef.OpISC_AbsIndexY,
                OpDef.GenerateUndoc(0xfc, OpDef.OpTOP_AbsIndeX),
                OpDef.OpSBC_AbsIndexX,
                OpDef.OpINC_AbsIndexX,
                OpDef.OpISC_AbsIndexX,
            }
        };


        // Original 65C02, with new opcodes and a handful of slightly strange NOPs.
        private static CpuDef Cpu65C02 { get; } = new CpuDef("WDC 65C02", (1 << 16) - 1, false) {
            Type = CpuType.Cpu65C02,
            mOpDefs = new OpDef[] {
                OpDef.OpBRK_Implied,            // 0x00
                OpDef.OpORA_DPIndexXInd,
                OpDef.GenerateUndoc(0x02, OpDef.OpLDD_Imm),
                OpDef.GenerateUndoc(0x03, OpDef.OpNOP_65C02),
                OpDef.OpTSB_DP,
                OpDef.OpORA_DP,
                OpDef.OpASL_DP,
                OpDef.GenerateUndoc(0x07, OpDef.OpNOP_65C02),
                OpDef.OpPHP_StackPush,          // 0x08
                OpDef.OpORA_Imm,
                OpDef.OpASL_Acc,
                OpDef.GenerateUndoc(0x0b, OpDef.OpNOP_65C02),
                OpDef.OpTSB_Abs,
                OpDef.OpORA_Abs,
                OpDef.OpASL_Abs,
                OpDef.GenerateUndoc(0x0f, OpDef.OpNOP_65C02),
                OpDef.OpBPL_PCRel,              // 0x10
                OpDef.OpORA_DPIndIndexY,
                OpDef.OpORA_DPInd,
                OpDef.GenerateUndoc(0x13, OpDef.OpNOP_65C02),
                OpDef.OpTRB_DP,
                OpDef.OpORA_DPIndexX,
                OpDef.OpASL_DPIndexX,
                OpDef.GenerateUndoc(0x17, OpDef.OpNOP_65C02),
                OpDef.OpCLC_Implied,            // 0x18
                OpDef.OpORA_AbsIndexY,
                OpDef.OpINC_Acc,
                OpDef.GenerateUndoc(0x1b, OpDef.OpNOP_65C02),
                OpDef.OpTRB_Abs,
                OpDef.OpORA_AbsIndexX,
                OpDef.OpASL_AbsIndexX,
                OpDef.GenerateUndoc(0x1f, OpDef.OpNOP_65C02),
                OpDef.OpJSR_Abs,                // 0x20
                OpDef.OpAND_DPIndexXInd,
                OpDef.GenerateUndoc(0x22, OpDef.OpLDD_Imm),
                OpDef.GenerateUndoc(0x23, OpDef.OpNOP_65C02),
                OpDef.OpBIT_DP,
                OpDef.OpAND_DP,
                OpDef.OpROL_DP,
                OpDef.GenerateUndoc(0x27, OpDef.OpNOP_65C02),
                OpDef.OpPLP_StackPull,          // 0x28
                OpDef.OpAND_Imm,
                OpDef.OpROL_Acc,
                OpDef.GenerateUndoc(0x2b, OpDef.OpNOP_65C02),
                OpDef.OpBIT_Abs,
                OpDef.OpAND_Abs,
                OpDef.OpROL_Abs,
                OpDef.GenerateUndoc(0x2f, OpDef.OpNOP_65C02),
                OpDef.OpBMI_PCRel,              // 0x30
                OpDef.OpAND_DPIndIndexY,
                OpDef.OpAND_DPInd,
                OpDef.GenerateUndoc(0x33, OpDef.OpNOP_65C02),
                OpDef.OpBIT_DPIndexX,
                OpDef.OpAND_DPIndexX,
                OpDef.OpROL_DPIndexX,
                OpDef.GenerateUndoc(0x37, OpDef.OpNOP_65C02),
                OpDef.OpSEC_Implied,            // 0x38
                OpDef.OpAND_AbsIndexY,
                OpDef.OpDEC_Acc,
                OpDef.GenerateUndoc(0x3b, OpDef.OpNOP_65C02),
                OpDef.OpBIT_AbsIndexX,
                OpDef.OpAND_AbsIndexX,
                OpDef.OpROL_AbsIndexX,
                OpDef.GenerateUndoc(0x3f, OpDef.OpNOP_65C02),
                OpDef.OpRTI_StackRTI,           // 0x40
                OpDef.OpEOR_DPIndexXInd,
                OpDef.GenerateUndoc(0x42, OpDef.OpLDD_Imm),
                OpDef.GenerateUndoc(0x43, OpDef.OpNOP_65C02),
                OpDef.GenerateUndoc(0x44, OpDef.OpLDD_DP),
                OpDef.OpEOR_DP,
                OpDef.OpLSR_DP,
                OpDef.GenerateUndoc(0x47, OpDef.OpNOP_65C02),
                OpDef.OpPHA_StackPush,          // 0x48
                OpDef.OpEOR_Imm,
                OpDef.OpLSR_Acc,
                OpDef.GenerateUndoc(0x4b, OpDef.OpNOP_65C02),
                OpDef.OpJMP_Abs,
                OpDef.OpEOR_Abs,
                OpDef.OpLSR_Abs,
                OpDef.GenerateUndoc(0x4f, OpDef.OpNOP_65C02),
                OpDef.OpBVC_PCRel,              // 0x50
                OpDef.OpEOR_DPIndIndexY,
                OpDef.OpEOR_DPInd,
                OpDef.GenerateUndoc(0x53, OpDef.OpNOP_65C02),
                OpDef.GenerateUndoc(0x54, OpDef.OpLDD_DPIndexX),
                OpDef.OpEOR_DPIndexX,
                OpDef.OpLSR_DPIndexX,
                OpDef.GenerateUndoc(0x57, OpDef.OpNOP_65C02),
                OpDef.OpCLI_Implied,            // 0x58
                OpDef.OpEOR_AbsIndexY,
                OpDef.OpPHY_StackPush,
                OpDef.GenerateUndoc(0x5b, OpDef.OpNOP_65C02),
                OpDef.GenerateUndoc(0x5c, OpDef.OpLDD_Weird),
                OpDef.OpEOR_AbsIndexX,
                OpDef.OpLSR_AbsIndexX,
                OpDef.GenerateUndoc(0x5f, OpDef.OpNOP_65C02),
                OpDef.OpRTS_StackRTS,           // 0x60
                OpDef.OpADC_DPIndexXInd,
                OpDef.GenerateUndoc(0x62, OpDef.OpLDD_Imm),
                OpDef.GenerateUndoc(0x63, OpDef.OpNOP_65C02),
                OpDef.OpSTZ_DP,
                OpDef.OpADC_DP,
                OpDef.OpROR_DP,
                OpDef.GenerateUndoc(0x67, OpDef.OpNOP_65C02),
                OpDef.OpPLA_StackPull,          // 0x68
                OpDef.OpADC_Imm,
                OpDef.OpROR_Acc,
                OpDef.GenerateUndoc(0x6b, OpDef.OpNOP_65C02),
                OpDef.OpJMP_AbsInd,
                OpDef.OpADC_Abs,
                OpDef.OpROR_Abs,
                OpDef.GenerateUndoc(0x6f, OpDef.OpNOP_65C02),
                OpDef.OpBVS_PCRel,              // 0x70
                OpDef.OpADC_DPIndIndexY,
                OpDef.OpADC_DPInd,
                OpDef.GenerateUndoc(0x73, OpDef.OpNOP_65C02),
                OpDef.OpSTZ_DPIndexX,
                OpDef.OpADC_DPIndexX,
                OpDef.OpROR_DPIndexX,
                OpDef.GenerateUndoc(0x77, OpDef.OpNOP_65C02),
                OpDef.OpSEI_Implied,            // 0x78
                OpDef.OpADC_AbsIndexY,
                OpDef.OpPLY_StackPull,
                OpDef.GenerateUndoc(0x7b, OpDef.OpNOP_65C02),
                OpDef.OpJMP_AbsIndexXInd,
                OpDef.OpADC_AbsIndexX,
                OpDef.OpROR_AbsIndexX,
                OpDef.GenerateUndoc(0x7f, OpDef.OpNOP_65C02),
                OpDef.OpBRA_PCRel,              // 0x80
                OpDef.OpSTA_DPIndexXInd,
                OpDef.GenerateUndoc(0x82, OpDef.OpLDD_Imm),
                OpDef.GenerateUndoc(0x83, OpDef.OpNOP_65C02),
                OpDef.OpSTY_DP,
                OpDef.OpSTA_DP,
                OpDef.OpSTX_DP,
                OpDef.GenerateUndoc(0x87, OpDef.OpNOP_65C02),
                OpDef.OpDEY_Implied,            // 0x88
                OpDef.OpBIT_Imm,
                OpDef.OpTXA_Implied,
                OpDef.GenerateUndoc(0x8b, OpDef.OpNOP_65C02),
                OpDef.OpSTY_Abs,
                OpDef.OpSTA_Abs,
                OpDef.OpSTX_Abs,
                OpDef.GenerateUndoc(0x8f, OpDef.OpNOP_65C02),
                OpDef.OpBCC_PCRel,              // 0x90
                OpDef.OpSTA_DPIndIndexY,
                OpDef.OpSTA_DPInd,
                OpDef.GenerateUndoc(0x93, OpDef.OpNOP_65C02),
                OpDef.OpSTY_DPIndexX,
                OpDef.OpSTA_DPIndexX,
                OpDef.OpSTX_DPIndexY,
                OpDef.GenerateUndoc(0x97, OpDef.OpNOP_65C02),
                OpDef.OpTYA_Implied,            // 0x98
                OpDef.OpSTA_AbsIndexY,
                OpDef.OpTXS_Implied,
                OpDef.GenerateUndoc(0x9b, OpDef.OpNOP_65C02),
                OpDef.OpSTZ_Abs,
                OpDef.OpSTA_AbsIndexX,
                OpDef.OpSTZ_AbsIndexX,
                OpDef.GenerateUndoc(0x9f, OpDef.OpNOP_65C02),
                OpDef.OpLDY_Imm,                // 0xa0
                OpDef.OpLDA_DPIndexXInd,
                OpDef.OpLDX_Imm,
                OpDef.GenerateUndoc(0xa3, OpDef.OpNOP_65C02),
                OpDef.OpLDY_DP,
                OpDef.OpLDA_DP,
                OpDef.OpLDX_DP,
                OpDef.GenerateUndoc(0xa7, OpDef.OpNOP_65C02),
                OpDef.OpTAY_Implied,            // 0xa8
                OpDef.OpLDA_Imm,
                OpDef.OpTAX_Implied,
                OpDef.GenerateUndoc(0xab, OpDef.OpNOP_65C02),
                OpDef.OpLDY_Abs,
                OpDef.OpLDA_Abs,
                OpDef.OpLDX_Abs,
                OpDef.GenerateUndoc(0xaf, OpDef.OpNOP_65C02),
                OpDef.OpBCS_PCRel,              // 0xb0
                OpDef.OpLDA_DPIndIndexY,
                OpDef.OpLDA_DPInd,
                OpDef.GenerateUndoc(0xb3, OpDef.OpNOP_65C02),
                OpDef.OpLDY_DPIndexX,
                OpDef.OpLDA_DPIndexX,
                OpDef.OpLDX_DPIndexY,
                OpDef.GenerateUndoc(0xb7, OpDef.OpNOP_65C02),
                OpDef.OpCLV_Implied,            // 0xb8
                OpDef.OpLDA_AbsIndexY,
                OpDef.OpTSX_Implied,
                OpDef.GenerateUndoc(0xbb, OpDef.OpNOP_65C02),
                OpDef.OpLDY_AbsIndexX,
                OpDef.OpLDA_AbsIndexX,
                OpDef.OpLDX_AbsIndexY,
                OpDef.GenerateUndoc(0xbf, OpDef.OpNOP_65C02),
                OpDef.OpCPY_Imm,                // 0xc0
                OpDef.OpCMP_DPIndexXInd,
                OpDef.GenerateUndoc(0xc2, OpDef.OpLDD_Imm),
                OpDef.GenerateUndoc(0xc3, OpDef.OpNOP_65C02),
                OpDef.OpCPY_DP,
                OpDef.OpCMP_DP,
                OpDef.OpDEC_DP,
                OpDef.GenerateUndoc(0xc7, OpDef.OpNOP_65C02),
                OpDef.OpINY_Implied,            // 0xc8
                OpDef.OpCMP_Imm,
                OpDef.OpDEX_Implied,
                OpDef.GenerateUndoc(0xcb, OpDef.OpNOP_65C02),
                OpDef.OpCPY_Abs,
                OpDef.OpCMP_Abs,
                OpDef.OpDEC_Abs,
                OpDef.GenerateUndoc(0xcf, OpDef.OpNOP_65C02),
                OpDef.OpBNE_PCRel,              // 0xd0
                OpDef.OpCMP_DPIndIndexY,
                OpDef.OpCMP_DPInd,
                OpDef.GenerateUndoc(0xd3, OpDef.OpNOP_65C02),
                OpDef.GenerateUndoc(0xd4, OpDef.OpLDD_DPIndexX),
                OpDef.OpCMP_DPIndexX,
                OpDef.OpDEC_DPIndexX,
                OpDef.GenerateUndoc(0xd7, OpDef.OpNOP_65C02),
                OpDef.OpCLD_Implied,            // 0xd8
                OpDef.OpCMP_AbsIndexY,
                OpDef.OpPHX_StackPush,
                OpDef.GenerateUndoc(0xdb, OpDef.OpNOP_65C02),
                OpDef.GenerateUndoc(0xdc, OpDef.OpLDD_Absolute),
                OpDef.OpCMP_AbsIndexX,
                OpDef.OpDEC_AbsIndexX,
                OpDef.GenerateUndoc(0xdf, OpDef.OpNOP_65C02),
                OpDef.OpCPX_Imm,                // 0xe0
                OpDef.OpSBC_DPIndexXInd,
                OpDef.GenerateUndoc(0xe2, OpDef.OpLDD_Imm),
                OpDef.GenerateUndoc(0xe3, OpDef.OpNOP_65C02),
                OpDef.OpCPX_DP,
                OpDef.OpSBC_DP,
                OpDef.OpINC_DP,
                OpDef.GenerateUndoc(0xe7, OpDef.OpNOP_65C02),
                OpDef.OpINX_Implied,            // 0xe8
                OpDef.OpSBC_Imm,
                OpDef.OpNOP_Implied,
                OpDef.GenerateUndoc(0xeb, OpDef.OpNOP_65C02),
                OpDef.OpCPX_Abs,
                OpDef.OpSBC_Abs,
                OpDef.OpINC_Abs,
                OpDef.GenerateUndoc(0xef, OpDef.OpNOP_65C02),
                OpDef.OpBEQ_PCRel,              // 0xf0
                OpDef.OpSBC_DPIndIndexY,
                OpDef.OpSBC_DPInd,
                OpDef.GenerateUndoc(0xf3, OpDef.OpNOP_65C02),
                OpDef.GenerateUndoc(0xf4, OpDef.OpLDD_DPIndexX),
                OpDef.OpSBC_DPIndexX,
                OpDef.OpINC_DPIndexX,
                OpDef.GenerateUndoc(0xf7, OpDef.OpNOP_65C02),
                OpDef.OpSED_Implied,            // 0xf8
                OpDef.OpSBC_AbsIndexY,
                OpDef.OpPLX_StackPull,
                OpDef.GenerateUndoc(0xfb, OpDef.OpNOP_65C02),
                OpDef.GenerateUndoc(0xfc, OpDef.OpLDD_Absolute),
                OpDef.OpSBC_AbsIndexX,
                OpDef.OpINC_AbsIndexX,
                OpDef.GenerateUndoc(0xff, OpDef.OpNOP_65C02),
            }
        };


        // WDC's W65C02, with the Rockwell extensions and STP/WAI.  I'm assuming that the
        // behavior of the undocumented instructions remains unchanged, which is probably unwise
        // but I have no information to the contrary.
        private static CpuDef CpuW65C02 { get; } = new CpuDef("WDC W65C02S", (1 << 16) - 1, false) {
            Type = CpuType.CpuW65C02,
            mOpDefs = new OpDef[] {
                OpDef.OpBRK_Implied,            // 0x00
                OpDef.OpORA_DPIndexXInd,
                OpDef.GenerateUndoc(0x02, OpDef.OpLDD_Imm),
                OpDef.GenerateUndoc(0x03, OpDef.OpNOP_65C02),
                OpDef.OpTSB_DP,
                OpDef.OpORA_DP,
                OpDef.OpASL_DP,
                OpDef.OpRMB0_DP,
                OpDef.OpPHP_StackPush,          // 0x08
                OpDef.OpORA_Imm,
                OpDef.OpASL_Acc,
                OpDef.GenerateUndoc(0x0b, OpDef.OpNOP_65C02),
                OpDef.OpTSB_Abs,
                OpDef.OpORA_Abs,
                OpDef.OpASL_Abs,
                OpDef.OpBBR0_DPPCRel,
                OpDef.OpBPL_PCRel,              // 0x10
                OpDef.OpORA_DPIndIndexY,
                OpDef.OpORA_DPInd,
                OpDef.GenerateUndoc(0x13, OpDef.OpNOP_65C02),
                OpDef.OpTRB_DP,
                OpDef.OpORA_DPIndexX,
                OpDef.OpASL_DPIndexX,
                OpDef.OpRMB1_DP,
                OpDef.OpCLC_Implied,            // 0x18
                OpDef.OpORA_AbsIndexY,
                OpDef.OpINC_Acc,
                OpDef.GenerateUndoc(0x1b, OpDef.OpNOP_65C02),
                OpDef.OpTRB_Abs,
                OpDef.OpORA_AbsIndexX,
                OpDef.OpASL_AbsIndexX,
                OpDef.OpBBR1_DPPCRel,
                OpDef.OpJSR_Abs,                // 0x20
                OpDef.OpAND_DPIndexXInd,
                OpDef.GenerateUndoc(0x22, OpDef.OpLDD_Imm),
                OpDef.GenerateUndoc(0x23, OpDef.OpNOP_65C02),
                OpDef.OpBIT_DP,
                OpDef.OpAND_DP,
                OpDef.OpROL_DP,
                OpDef.OpRMB2_DP,
                OpDef.OpPLP_StackPull,          // 0x28
                OpDef.OpAND_Imm,
                OpDef.OpROL_Acc,
                OpDef.GenerateUndoc(0x2b, OpDef.OpNOP_65C02),
                OpDef.OpBIT_Abs,
                OpDef.OpAND_Abs,
                OpDef.OpROL_Abs,
                OpDef.OpBBR2_DPPCRel,
                OpDef.OpBMI_PCRel,              // 0x30
                OpDef.OpAND_DPIndIndexY,
                OpDef.OpAND_DPInd,
                OpDef.GenerateUndoc(0x33, OpDef.OpNOP_65C02),
                OpDef.OpBIT_DPIndexX,
                OpDef.OpAND_DPIndexX,
                OpDef.OpROL_DPIndexX,
                OpDef.OpRMB3_DP,
                OpDef.OpSEC_Implied,            // 0x38
                OpDef.OpAND_AbsIndexY,
                OpDef.OpDEC_Acc,
                OpDef.GenerateUndoc(0x3b, OpDef.OpNOP_65C02),
                OpDef.OpBIT_AbsIndexX,
                OpDef.OpAND_AbsIndexX,
                OpDef.OpROL_AbsIndexX,
                OpDef.OpBBR3_DPPCRel,
                OpDef.OpRTI_StackRTI,           // 0x40
                OpDef.OpEOR_DPIndexXInd,
                OpDef.GenerateUndoc(0x42, OpDef.OpLDD_Imm),
                OpDef.GenerateUndoc(0x43, OpDef.OpNOP_65C02),
                OpDef.GenerateUndoc(0x44, OpDef.OpLDD_DP),
                OpDef.OpEOR_DP,
                OpDef.OpLSR_DP,
                OpDef.OpRMB4_DP,
                OpDef.OpPHA_StackPush,          // 0x48
                OpDef.OpEOR_Imm,
                OpDef.OpLSR_Acc,
                OpDef.GenerateUndoc(0x4b, OpDef.OpNOP_65C02),
                OpDef.OpJMP_Abs,
                OpDef.OpEOR_Abs,
                OpDef.OpLSR_Abs,
                OpDef.OpBBR4_DPPCRel,
                OpDef.OpBVC_PCRel,              // 0x50
                OpDef.OpEOR_DPIndIndexY,
                OpDef.OpEOR_DPInd,
                OpDef.GenerateUndoc(0x53, OpDef.OpNOP_65C02),
                OpDef.GenerateUndoc(0x54, OpDef.OpLDD_DPIndexX),
                OpDef.OpEOR_DPIndexX,
                OpDef.OpLSR_DPIndexX,
                OpDef.OpRMB5_DP,
                OpDef.OpCLI_Implied,            // 0x58
                OpDef.OpEOR_AbsIndexY,
                OpDef.OpPHY_StackPush,
                OpDef.GenerateUndoc(0x5b, OpDef.OpNOP_65C02),
                OpDef.GenerateUndoc(0x5c, OpDef.OpLDD_Weird),
                OpDef.OpEOR_AbsIndexX,
                OpDef.OpLSR_AbsIndexX,
                OpDef.OpBBR5_DPPCRel,
                OpDef.OpRTS_StackRTS,           // 0x60
                OpDef.OpADC_DPIndexXInd,
                OpDef.GenerateUndoc(0x62, OpDef.OpLDD_Imm),
                OpDef.GenerateUndoc(0x63, OpDef.OpNOP_65C02),
                OpDef.OpSTZ_DP,
                OpDef.OpADC_DP,
                OpDef.OpROR_DP,
                OpDef.OpRMB6_DP,
                OpDef.OpPLA_StackPull,          // 0x68
                OpDef.OpADC_Imm,
                OpDef.OpROR_Acc,
                OpDef.GenerateUndoc(0x6b, OpDef.OpNOP_65C02),
                OpDef.OpJMP_AbsInd,
                OpDef.OpADC_Abs,
                OpDef.OpROR_Abs,
                OpDef.OpBBR6_DPPCRel,
                OpDef.OpBVS_PCRel,              // 0x70
                OpDef.OpADC_DPIndIndexY,
                OpDef.OpADC_DPInd,
                OpDef.GenerateUndoc(0x73, OpDef.OpNOP_65C02),
                OpDef.OpSTZ_DPIndexX,
                OpDef.OpADC_DPIndexX,
                OpDef.OpROR_DPIndexX,
                OpDef.OpRMB7_DP,
                OpDef.OpSEI_Implied,            // 0x78
                OpDef.OpADC_AbsIndexY,
                OpDef.OpPLY_StackPull,
                OpDef.GenerateUndoc(0x7b, OpDef.OpNOP_65C02),
                OpDef.OpJMP_AbsIndexXInd,
                OpDef.OpADC_AbsIndexX,
                OpDef.OpROR_AbsIndexX,
                OpDef.OpBBR7_DPPCRel,
                OpDef.OpBRA_PCRel,              // 0x80
                OpDef.OpSTA_DPIndexXInd,
                OpDef.GenerateUndoc(0x82, OpDef.OpLDD_Imm),
                OpDef.GenerateUndoc(0x83, OpDef.OpNOP_65C02),
                OpDef.OpSTY_DP,
                OpDef.OpSTA_DP,
                OpDef.OpSTX_DP,
                OpDef.OpSMB0_DP,
                OpDef.OpDEY_Implied,            // 0x88
                OpDef.OpBIT_Imm,
                OpDef.OpTXA_Implied,
                OpDef.GenerateUndoc(0x8b, OpDef.OpNOP_65C02),
                OpDef.OpSTY_Abs,
                OpDef.OpSTA_Abs,
                OpDef.OpSTX_Abs,
                OpDef.OpBBS0_DPPCRel,
                OpDef.OpBCC_PCRel,              // 0x90
                OpDef.OpSTA_DPIndIndexY,
                OpDef.OpSTA_DPInd,
                OpDef.GenerateUndoc(0x93, OpDef.OpNOP_65C02),
                OpDef.OpSTY_DPIndexX,
                OpDef.OpSTA_DPIndexX,
                OpDef.OpSTX_DPIndexY,
                OpDef.OpSMB1_DP,
                OpDef.OpTYA_Implied,            // 0x98
                OpDef.OpSTA_AbsIndexY,
                OpDef.OpTXS_Implied,
                OpDef.GenerateUndoc(0x9b, OpDef.OpNOP_65C02),
                OpDef.OpSTZ_Abs,
                OpDef.OpSTA_AbsIndexX,
                OpDef.OpSTZ_AbsIndexX,
                OpDef.OpBBS1_DPPCRel,
                OpDef.OpLDY_Imm,                // 0xa0
                OpDef.OpLDA_DPIndexXInd,
                OpDef.OpLDX_Imm,
                OpDef.GenerateUndoc(0xa3, OpDef.OpNOP_65C02),
                OpDef.OpLDY_DP,
                OpDef.OpLDA_DP,
                OpDef.OpLDX_DP,
                OpDef.OpSMB2_DP,
                OpDef.OpTAY_Implied,            // 0xa8
                OpDef.OpLDA_Imm,
                OpDef.OpTAX_Implied,
                OpDef.GenerateUndoc(0xab, OpDef.OpNOP_65C02),
                OpDef.OpLDY_Abs,
                OpDef.OpLDA_Abs,
                OpDef.OpLDX_Abs,
                OpDef.OpBBS2_DPPCRel,
                OpDef.OpBCS_PCRel,              // 0xb0
                OpDef.OpLDA_DPIndIndexY,
                OpDef.OpLDA_DPInd,
                OpDef.GenerateUndoc(0xb3, OpDef.OpNOP_65C02),
                OpDef.OpLDY_DPIndexX,
                OpDef.OpLDA_DPIndexX,
                OpDef.OpLDX_DPIndexY,
                OpDef.OpSMB3_DP,
                OpDef.OpCLV_Implied,            // 0xb8
                OpDef.OpLDA_AbsIndexY,
                OpDef.OpTSX_Implied,
                OpDef.GenerateUndoc(0xbb, OpDef.OpNOP_65C02),
                OpDef.OpLDY_AbsIndexX,
                OpDef.OpLDA_AbsIndexX,
                OpDef.OpLDX_AbsIndexY,
                OpDef.OpBBS3_DPPCRel,
                OpDef.OpCPY_Imm,                // 0xc0
                OpDef.OpCMP_DPIndexXInd,
                OpDef.GenerateUndoc(0xc2, OpDef.OpLDD_Imm),
                OpDef.GenerateUndoc(0xc3, OpDef.OpNOP_65C02),
                OpDef.OpCPY_DP,
                OpDef.OpCMP_DP,
                OpDef.OpDEC_DP,
                OpDef.OpSMB4_DP,
                OpDef.OpINY_Implied,            // 0xc8
                OpDef.OpCMP_Imm,
                OpDef.OpDEX_Implied,
                OpDef.OpWAI_Implied,
                OpDef.OpCPY_Abs,
                OpDef.OpCMP_Abs,
                OpDef.OpDEC_Abs,
                OpDef.OpBBS4_DPPCRel,
                OpDef.OpBNE_PCRel,              // 0xd0
                OpDef.OpCMP_DPIndIndexY,
                OpDef.OpCMP_DPInd,
                OpDef.GenerateUndoc(0xd3, OpDef.OpNOP_65C02),
                OpDef.GenerateUndoc(0xd4, OpDef.OpLDD_DPIndexX),
                OpDef.OpCMP_DPIndexX,
                OpDef.OpDEC_DPIndexX,
                OpDef.OpSMB5_DP,
                OpDef.OpCLD_Implied,            // 0xd8
                OpDef.OpCMP_AbsIndexY,
                OpDef.OpPHX_StackPush,
                OpDef.OpSTP_Implied,
                OpDef.GenerateUndoc(0xdc, OpDef.OpLDD_Absolute),
                OpDef.OpCMP_AbsIndexX,
                OpDef.OpDEC_AbsIndexX,
                OpDef.OpBBS5_DPPCRel,
                OpDef.OpCPX_Imm,                // 0xe0
                OpDef.OpSBC_DPIndexXInd,
                OpDef.GenerateUndoc(0xe2, OpDef.OpLDD_Imm),
                OpDef.GenerateUndoc(0xe3, OpDef.OpNOP_65C02),
                OpDef.OpCPX_DP,
                OpDef.OpSBC_DP,
                OpDef.OpINC_DP,
                OpDef.OpSMB6_DP,
                OpDef.OpINX_Implied,            // 0xe8
                OpDef.OpSBC_Imm,
                OpDef.OpNOP_Implied,
                OpDef.GenerateUndoc(0xeb, OpDef.OpNOP_65C02),
                OpDef.OpCPX_Abs,
                OpDef.OpSBC_Abs,
                OpDef.OpINC_Abs,
                OpDef.OpBBS6_DPPCRel,
                OpDef.OpBEQ_PCRel,              // 0xf0
                OpDef.OpSBC_DPIndIndexY,
                OpDef.OpSBC_DPInd,
                OpDef.GenerateUndoc(0xf3, OpDef.OpNOP_65C02),
                OpDef.GenerateUndoc(0xf4, OpDef.OpLDD_DPIndexX),
                OpDef.OpSBC_DPIndexX,
                OpDef.OpINC_DPIndexX,
                OpDef.OpSMB7_DP,
                OpDef.OpSED_Implied,            // 0xf8
                OpDef.OpSBC_AbsIndexY,
                OpDef.OpPLX_StackPull,
                OpDef.GenerateUndoc(0xfb, OpDef.OpNOP_65C02),
                OpDef.GenerateUndoc(0xfc, OpDef.OpLDD_Absolute),
                OpDef.OpSBC_AbsIndexX,
                OpDef.OpINC_AbsIndexX,
                OpDef.OpBBS7_DPPCRel,
            }
        };


        // WDC 65802 and 65816.  No undocumented opcodes -- all 256 are used.
        private static CpuDef Cpu65816 { get; } = new CpuDef("WDC W65C816S", (1 << 24) - 1, true) {
            Type = CpuType.Cpu65816,
            mOpDefs = new OpDef[] {
                OpDef.OpBRK_Implied,            // 0x00
                OpDef.OpORA_DPIndexXInd,
                OpDef.OpCOP_StackInt,
                OpDef.OpORA_StackRel,
                OpDef.OpTSB_DP,
                OpDef.OpORA_DP,
                OpDef.OpASL_DP,
                OpDef.OpORA_DPIndLong,
                OpDef.OpPHP_StackPush,          // 0x08
                OpDef.OpORA_ImmLongA,
                OpDef.OpASL_Acc,
                OpDef.OpPHD_StackPush,
                OpDef.OpTSB_Abs,
                OpDef.OpORA_Abs,
                OpDef.OpASL_Abs,
                OpDef.OpORA_AbsLong,
                OpDef.OpBPL_PCRel,              // 0x10
                OpDef.OpORA_DPIndIndexY,
                OpDef.OpORA_DPInd,
                OpDef.OpORA_StackRelIndIndexY,
                OpDef.OpTRB_DP,
                OpDef.OpORA_DPIndexX,
                OpDef.OpASL_DPIndexX,
                OpDef.OpORA_DPIndIndexYLong,
                OpDef.OpCLC_Implied,            // 0x18
                OpDef.OpORA_AbsIndexY,
                OpDef.OpINC_Acc,
                OpDef.OpTCS_Implied,
                OpDef.OpTRB_Abs,
                OpDef.OpORA_AbsIndexX,
                OpDef.OpASL_AbsIndexX,
                OpDef.OpORA_AbsIndexXLong,
                OpDef.OpJSR_Abs,                // 0x20
                OpDef.OpAND_DPIndexXInd,
                OpDef.OpJSR_AbsLong,
                OpDef.OpAND_StackRel,
                OpDef.OpBIT_DP,
                OpDef.OpAND_DP,
                OpDef.OpROL_DP,
                OpDef.OpAND_DPIndLong,
                OpDef.OpPLP_StackPull,          // 0x28
                OpDef.OpAND_ImmLongA,
                OpDef.OpROL_Acc,
                OpDef.OpPLD_StackPull,
                OpDef.OpBIT_Abs,
                OpDef.OpAND_Abs,
                OpDef.OpROL_Abs,
                OpDef.OpAND_AbsLong,
                OpDef.OpBMI_PCRel,              // 0x30
                OpDef.OpAND_DPIndIndexY,
                OpDef.OpAND_DPInd,
                OpDef.OpAND_StackRelIndIndexY,
                OpDef.OpBIT_DPIndexX,
                OpDef.OpAND_DPIndexX,
                OpDef.OpROL_DPIndexX,
                OpDef.OpAND_DPIndIndexYLong,
                OpDef.OpSEC_Implied,            // 0x38
                OpDef.OpAND_AbsIndexY,
                OpDef.OpDEC_Acc,
                OpDef.OpTSC_Implied,
                OpDef.OpBIT_AbsIndexX,
                OpDef.OpAND_AbsIndexX,
                OpDef.OpROL_AbsIndexX,
                OpDef.OpAND_AbsIndexXLong,
                OpDef.OpRTI_StackRTI,           // 0x40
                OpDef.OpEOR_DPIndexXInd,
                OpDef.OpWDM_WDM,
                OpDef.OpEOR_StackRel,
                OpDef.OpMVP_BlockMove,
                OpDef.OpEOR_DP,
                OpDef.OpLSR_DP,
                OpDef.OpEOR_DPIndLong,
                OpDef.OpPHA_StackPush,          // 0x48
                OpDef.OpEOR_ImmLongA,
                OpDef.OpLSR_Acc,
                OpDef.OpPHK_StackPush,
                OpDef.OpJMP_Abs,
                OpDef.OpEOR_Abs,
                OpDef.OpLSR_Abs,
                OpDef.OpEOR_AbsLong,
                OpDef.OpBVC_PCRel,              // 0x50
                OpDef.OpEOR_DPIndIndexY,
                OpDef.OpEOR_DPInd,
                OpDef.OpEOR_StackRelIndIndexY,
                OpDef.OpMVN_BlockMove,
                OpDef.OpEOR_DPIndexX,
                OpDef.OpLSR_DPIndexX,
                OpDef.OpEOR_DPIndIndexYLong,
                OpDef.OpCLI_Implied,            // 0x58
                OpDef.OpEOR_AbsIndexY,
                OpDef.OpPHY_StackPush,
                OpDef.OpTCD_Implied,
                OpDef.OpJMP_AbsLong,
                OpDef.OpEOR_AbsIndexX,
                OpDef.OpLSR_AbsIndexX,
                OpDef.OpEOR_AbsIndexXLong,
                OpDef.OpRTS_StackRTS,           // 0x60
                OpDef.OpADC_DPIndexXInd,
                OpDef.OpPER_StackPCRelLong,
                OpDef.OpADC_StackRel,
                OpDef.OpSTZ_DP,
                OpDef.OpADC_DP,
                OpDef.OpROR_DP,
                OpDef.OpADC_DPIndLong,
                OpDef.OpPLA_StackPull,          // 0x68
                OpDef.OpADC_ImmLongA,
                OpDef.OpROR_Acc,
                OpDef.OpRTL_StackRTL,
                OpDef.OpJMP_AbsInd,
                OpDef.OpADC_Abs,
                OpDef.OpROR_Abs,
                OpDef.OpADC_AbsLong,
                OpDef.OpBVS_PCRel,              // 0x70
                OpDef.OpADC_DPIndIndexY,
                OpDef.OpADC_DPInd,
                OpDef.OpADC_StackRelIndIndexY,
                OpDef.OpSTZ_DPIndexX,
                OpDef.OpADC_DPIndexX,
                OpDef.OpROR_DPIndexX,
                OpDef.OpADC_DPIndIndexYLong,
                OpDef.OpSEI_Implied,            // 0x78
                OpDef.OpADC_AbsIndexY,
                OpDef.OpPLY_StackPull,
                OpDef.OpTDC_Implied,
                OpDef.OpJMP_AbsIndexXInd,
                OpDef.OpADC_AbsIndexX,
                OpDef.OpROR_AbsIndexX,
                OpDef.OpADC_AbsIndexXLong,
                OpDef.OpBRA_PCRel,              // 0x80
                OpDef.OpSTA_DPIndexXInd,
                OpDef.OpBRL_PCRelLong,
                OpDef.OpSTA_StackRel,
                OpDef.OpSTY_DP,
                OpDef.OpSTA_DP,
                OpDef.OpSTX_DP,
                OpDef.OpSTA_DPIndLong,
                OpDef.OpDEY_Implied,            // 0x88
                OpDef.OpBIT_ImmLongA,
                OpDef.OpTXA_Implied,
                OpDef.OpPHB_StackPush,
                OpDef.OpSTY_Abs,
                OpDef.OpSTA_Abs,
                OpDef.OpSTX_Abs,
                OpDef.OpSTA_AbsLong,
                OpDef.OpBCC_PCRel,              // 0x90
                OpDef.OpSTA_DPIndIndexY,
                OpDef.OpSTA_DPInd,
                OpDef.OpSTA_StackRelIndIndexY,
                OpDef.OpSTY_DPIndexX,
                OpDef.OpSTA_DPIndexX,
                OpDef.OpSTX_DPIndexY,
                OpDef.OpSTA_DPIndIndexYLong,
                OpDef.OpTYA_Implied,            // 0x98
                OpDef.OpSTA_AbsIndexY,
                OpDef.OpTXS_Implied,
                OpDef.OpTXY_Implied,
                OpDef.OpSTZ_Abs,
                OpDef.OpSTA_AbsIndexX,
                OpDef.OpSTZ_AbsIndexX,
                OpDef.OpSTA_AbsIndexXLong,
                OpDef.OpLDY_ImmLongXY,          // 0xa0
                OpDef.OpLDA_DPIndexXInd,
                OpDef.OpLDX_ImmLongXY,
                OpDef.OpLDA_StackRel,
                OpDef.OpLDY_DP,
                OpDef.OpLDA_DP,
                OpDef.OpLDX_DP,
                OpDef.OpLDA_DPIndLong,
                OpDef.OpTAY_Implied,            // 0xa8
                OpDef.OpLDA_ImmLongA,
                OpDef.OpTAX_Implied,
                OpDef.OpPLB_StackPull,
                OpDef.OpLDY_Abs,
                OpDef.OpLDA_Abs,
                OpDef.OpLDX_Abs,
                OpDef.OpLDA_AbsLong,
                OpDef.OpBCS_PCRel,              // 0xb0
                OpDef.OpLDA_DPIndIndexY,
                OpDef.OpLDA_DPInd,
                OpDef.OpLDA_StackRelIndIndexY,
                OpDef.OpLDY_DPIndexX,
                OpDef.OpLDA_DPIndexX,
                OpDef.OpLDX_DPIndexY,
                OpDef.OpLDA_DPIndIndexYLong,
                OpDef.OpCLV_Implied,            // 0xb8
                OpDef.OpLDA_AbsIndexY,
                OpDef.OpTSX_Implied,
                OpDef.OpTYX_Implied,
                OpDef.OpLDY_AbsIndexX,
                OpDef.OpLDA_AbsIndexX,
                OpDef.OpLDX_AbsIndexY,
                OpDef.OpLDA_AbsIndexXLong,
                OpDef.OpCPY_ImmLongXY,          // 0xc0
                OpDef.OpCMP_DPIndexXInd,
                OpDef.OpREP_Imm,
                OpDef.OpCMP_StackRel,
                OpDef.OpCPY_DP,
                OpDef.OpCMP_DP,
                OpDef.OpDEC_DP,
                OpDef.OpCMP_DPIndLong,
                OpDef.OpINY_Implied,            // 0xc8
                OpDef.OpCMP_ImmLongA,
                OpDef.OpDEX_Implied,
                OpDef.OpWAI_Implied,
                OpDef.OpCPY_Abs,
                OpDef.OpCMP_Abs,
                OpDef.OpDEC_Abs,
                OpDef.OpCMP_AbsLong,
                OpDef.OpBNE_PCRel,              // 0xd0
                OpDef.OpCMP_DPIndIndexY,
                OpDef.OpCMP_DPInd,
                OpDef.OpCMP_StackRelIndIndexY,
                OpDef.OpPEI_StackDPInd,
                OpDef.OpCMP_DPIndexX,
                OpDef.OpDEC_DPIndexX,
                OpDef.OpCMP_DPIndIndexYLong,
                OpDef.OpCLD_Implied,            // 0xd8
                OpDef.OpCMP_AbsIndexY,
                OpDef.OpPHX_StackPush,
                OpDef.OpSTP_Implied,
                OpDef.OpJMP_AbsIndLong,
                OpDef.OpCMP_AbsIndexX,
                OpDef.OpDEC_AbsIndexX,
                OpDef.OpCMP_AbsIndexXLong,
                OpDef.OpCPX_ImmLongXY,          // 0xe0
                OpDef.OpSBC_DPIndexXInd,
                OpDef.OpSEP_Imm,
                OpDef.OpSBC_StackRel,
                OpDef.OpCPX_DP,
                OpDef.OpSBC_DP,
                OpDef.OpINC_DP,
                OpDef.OpSBC_DPIndLong,
                OpDef.OpINX_Implied,            // 0xe8
                OpDef.OpSBC_ImmLongA,
                OpDef.OpNOP_Implied,
                OpDef.OpXBA_Implied,
                OpDef.OpCPX_Abs,
                OpDef.OpSBC_Abs,
                OpDef.OpINC_Abs,
                OpDef.OpSBC_AbsLong,
                OpDef.OpBEQ_PCRel,              // 0xf0
                OpDef.OpSBC_DPIndIndexY,
                OpDef.OpSBC_DPInd,
                OpDef.OpSBC_StackRelIndIndexY,
                OpDef.OpPEA_StackAbs,
                OpDef.OpSBC_DPIndexX,
                OpDef.OpINC_DPIndexX,
                OpDef.OpSBC_DPIndIndexYLong,
                OpDef.OpSED_Implied,            // 0xf8
                OpDef.OpSBC_AbsIndexY,
                OpDef.OpPLX_StackPull,
                OpDef.OpXCE_Implied,
                OpDef.OpJSR_AbsIndexXInd,
                OpDef.OpSBC_AbsIndexX,
                OpDef.OpINC_AbsIndexX,
                OpDef.OpSBC_AbsIndexXLong
            }
        };
    }
}
