/* * 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 { /// /// Operation code definitions for all 65xx-series CPUs. /// /// Instances are immutable. /// public class OpDef { /// /// Effect an instruction has on code flow. /// 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) } /// /// Effect of executing an instruction on memory. /// 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 } /// /// Addressing mode. This uses the same distinctions as Eyes & 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. /// 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) 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? } /// /// 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. /// [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 } /// /// Width disambiguation requirement. /// public enum WidthDisambiguation : byte { None = 0, ForceDirect, // only 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. } /// /// Opcode numeric value, e.g. BRK is $00. /// public byte Opcode { get; private set; } /// /// Addressing mode. Determines length of instruction (mostly) and decoding of operand. /// public AddressMode AddrMode { get; private set; } /// /// True if this is an undocumented opcode. /// public bool IsUndocumented { get; private set; } /// /// Instruction mnemonic, e.g. LDA for Load Accumulator. /// public string Mnemonic { get; private set; } /// /// 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. /// public StatusFlags FlagsAffected { get; private set; } /// /// Effect this instruction has on code flow. /// public FlowEffect Effect { get; private set; } /// /// Effect this instruction has on memory. /// /// /// 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. /// 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; } /// /// Cycles required. The low 8 bits hold the base cycle count, the remaining bits /// are defined by the CycleMod enum. /// /// /// 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(). /// private int CycDef { get; set; } public int Cycles { get { return CycDef & 0xff; } } public CycleMod CycleMods { get { return (CycleMod)(CycDef & ~0xff); } } /// /// True if this is a numbered bit operation, i.e. BBR/BBS/RMB/SMB on the W65C02. /// public bool IsNumberedBitOp { get; private set; } /// /// True if the instruction's address mode is a direct page access. /// 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; } } } /// /// True if the instruction's operand is a stack-relative offset. /// public bool IsStackRelInstruction { get { return AddrMode == AddressMode.StackRel || AddrMode == AddressMode.StackRelIndIndexY; } } /// /// 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. /// public bool IsIndexedAccessInstruction { get { return AddrMode == AddressMode.AbsIndexX || AddrMode == AddressMode.AbsIndexXInd || AddrMode == AddressMode.AbsIndexY || AddrMode == AddressMode.DPIndexX || AddrMode == AddressMode.DPIndexXInd || AddrMode == AddressMode.DPIndexY; } } /// /// True for instructions that dereference the operand. /// 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; } } /// /// True if this is an absolute-address instruction whose operand is combined with /// the Program Bank Register. /// /// /// As noted in Eyes & 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. /// 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; } } /// /// 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. /// private bool IsOperandWidthUnambiguous { get; set; } /// /// Flag update delegate, used for code flow analysis. /// /// Current flags, to be modified by delegate. For conditional /// branches, this value will be used when the branch is not taken. /// 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. /// For conditional branches, this is the updated /// value when the branch is taken. private delegate StatusFlags FlagUpdater(StatusFlags flags, int immVal, ref StatusFlags condBranchTakenFlags); private FlagUpdater StatusFlagUpdater { get; set; } /// /// Nullary constructor. Most things are left at default values. /// public OpDef() { StatusFlagUpdater = FlagUpdater_NoChange; } /// /// Copy constructor. /// /// Object to copy from 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 }; /// /// True if this operation is any type of branch instruction (conditional branch, /// unconditional branch/jump, subroutine call). /// public bool IsBranchOrSubCall { get { return Effect == FlowEffect.Branch || Effect == FlowEffect.ConditionalBranch || Effect == FlowEffect.CallSubroutine; } } /// /// True if this operation is a subroutine call. /// public bool IsSubroutineCall { get { return Effect == FlowEffect.CallSubroutine; } } /// /// True if the operand is an immediate value, which should be prefixed with '#'. /// public bool IsImmediate { get { return AddrMode == AddressMode.Imm || AddrMode == AddressMode.ImmLongA || AddrMode == AddressMode.ImmLongXY; } } /// /// True if the operand is an "extended immediate" value, which includes PEA and MVN/MVP /// in addition to Imm/ImmLongA/ImmLongXY. /// public bool IsExtendedImmediate { get { return IsImmediate || AddrMode == AddressMode.StackAbs || AddrMode == AddressMode.BlockMove; } } /// /// 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. /// 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; } } } /// /// Get a value that indicates what sort of disambiguation is required. Only call /// this if IsWidthPotentiallyAmbiguous is true. /// /// Instruction width, including opcode. /// Operand value, extracted from byte stream. /// Width disambiguation value. 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; } } /// /// 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. /// /// Current status flags. /// Length, in bytes. 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; } } /// /// Return value from branch evaluation functions. /// public enum BranchTaken { Indeterminate = 0, Never, Always }; /// /// 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). /// /// Processor status flag value. /// Bit value corresponding to branch-taken. /// Branch taken indication. 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; } } /// /// Determines if a conditional branch is always taken, based on the status flags. Only /// call here for branch instructions. /// /// Conditional branch instruction. /// Processor status flags. /// Whether the branch is sometimes, always, or never taken. 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"); } } /// /// Gets the raw operand value. /// /// 65xx code. /// Offset of opcode. /// Current status flags (need M/X). /// Operand value, or -1 if the instruction doesn't have an operand. 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; } } /// /// Computes the changes the instruction will have on the status flags. /// /// 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. /// 65xx data stream. /// Offset of opcode. /// Effect this instruction has on flags. /// 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. 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 "NMOS 6510 Unintended Opcodes": // https://csdb.dk/release/?id=161035 // // 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 /// /// 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. /// /// Instruction opcode. /// Instruction prototype. /// Newly-created OpDef. 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; } } }