/// <summary> /// Cycle modifier description. /// </summary> /// <param name="modBit">A single-bit item from the CycleMod enum.</param> /// <returns>Description string, or question marks if not found.</returns> public string GetCycleModDescription(OpDef.CycleMod modBit) { if (mCycleModDescriptions.TryGetValue(modBit, out string desc)) { return(desc); } else { return("???"); } }
/// <summary> /// Generates the mCycleCounts and mCycleMods arrays. /// </summary> private void GenerateCycleCounts() { if (mCycleCounts != null) { return; } mCycleCounts = new int[256]; mCycleMods = new OpDef.CycleMod[256]; // Figure out which mods apply for this CPU. OpDef.CycleMod ignoreMask = 0; switch (Type) { case CpuType.Cpu6502: ignoreMask = OpDef.CycleMod.OneIfM0 | OpDef.CycleMod.TwoIfM0 | OpDef.CycleMod.OneIfX0 | OpDef.CycleMod.OneIfDpNonzero | OpDef.CycleMod.OneIfD1 | OpDef.CycleMod.OneIfE0 | OpDef.CycleMod.OneIf65C02 | OpDef.CycleMod.MinusOneIfNoPage | OpDef.CycleMod.BlockMove; break; case CpuType.Cpu65C02: ignoreMask = OpDef.CycleMod.OneIfM0 | OpDef.CycleMod.TwoIfM0 | OpDef.CycleMod.OneIfX0 | OpDef.CycleMod.OneIfDpNonzero | OpDef.CycleMod.OneIfE0 | OpDef.CycleMod.BlockMove; break; case CpuType.Cpu65816: ignoreMask = OpDef.CycleMod.OneIfD1 | OpDef.CycleMod.OneIf65C02 | OpDef.CycleMod.MinusOneIfNoPage; break; default: Debug.Assert(false, "unsupported cpu type " + Type); return; } // If an instruction has one or more applicable mods, declare it as variable. for (int i = 0; i < 256; i++) { OpDef op = mOpDefs[i]; int baseCycles = op.Cycles; OpDef.CycleMod mods = op.CycleMods & ~ignoreMask; if ((mods & OpDef.CycleMod.OneIf65C02) != 0) { // This isn't variable -- the instruction always takes one cycle longer // on the 65C02. (Applies to $6C, JMP (addr).) Debug.Assert(Type == CpuType.Cpu65C02); baseCycles++; mods &= ~OpDef.CycleMod.OneIf65C02; } mCycleCounts[i] = baseCycles; mCycleMods[i] = mods; } }
private void UpdateControls() { CpuItem item = (CpuItem)cpuSelectionComboBox.SelectedItem; if (item == null) { // initializing return; } // Push current choice to settings. AppSettings.Global.SetEnum(AppSettings.INSTCH_MODE, typeof(CpuDef.CpuType), (int)item.Type); AppSettings.Global.SetBool(AppSettings.INSTCH_SHOW_UNDOC, mShowUndocumented); // Populate the items source. InstructionItems.Clear(); CpuDef cpuDef = CpuDef.GetBestMatch(item.Type, true, false); for (int opc = 0; opc < 256; opc++) { OpDef op = cpuDef[opc]; if (!mShowUndocumented && op.IsUndocumented) { continue; } int opLen = op.GetLength(StatusFlags.AllIndeterminate); string sampleValue = "$12"; if (op.AddrMode == OpDef.AddressMode.BlockMove) { sampleValue = "#$12,#$34"; } else if (opLen == 3) { sampleValue = "$1234"; } else if (opLen == 4) { sampleValue = "$123456"; } string instrSample = mFormatter.FormatMnemonic(op.Mnemonic, OpDef.WidthDisambiguation.None) + " " + mFormatter.FormatOperand(op, sampleValue, OpDef.WidthDisambiguation.None); StringBuilder flags = new StringBuilder(8); const string FLAGS = "NVMXDIZC"; Asm65.StatusFlags affectedFlags = op.FlagsAffected; for (int fl = 0; fl < 8; fl++) { if (affectedFlags.GetBit((StatusFlags.FlagBits)(7 - fl)) >= 0) { flags.Append(FLAGS[fl]); } else { flags.Append("-"); } } string cycles = op.Cycles.ToString(); OpDef.CycleMod mods = cpuDef.GetOpCycleMod(opc); if (mods != 0) { cycles += '+'; } InstructionItems.Add(new InstructionItem(mFormatter.FormatHexValue(opc, 2), instrSample, flags.ToString(), cycles, mOpDesc.GetShortDescription(op.Mnemonic), mOpDesc.GetAddressModeDescription(op.AddrMode), op.IsUndocumented)); } }
/// <summary> /// Returns the number of cycles required to execute the instruction. If the value /// is negative, the negated value represents the minimum number of cycles for an /// instruction with variable timing. /// /// The value returned will factor in any CPU-specific aspects. /// </summary> /// <param name="opNum">Instruction opcode value.</param> /// <returns>Cycle count.</returns> public int GetCycles(int opNum, StatusFlags flags, OpDef.BranchTaken branchTaken, bool branchCrossesPage) { // The irrelevant modifiers have already been stripped out. OpDef.CycleMod mods = mCycleMods[opNum]; int cycles = mCycleCounts[opNum]; // Walk through the various cycle mods. If we can evaluate them definitively, // do so now and remove them from the set. // The M/X flags are defined to be in one state or the other, even when the flag // value is indeterminate, because we have to be able to size immediate operands // appropriately. So there's no ambiguity here even when there's ambiguity. We // make a similar statement about the E flag. if ((mods & OpDef.CycleMod.OneIfM0) != 0) { if (!flags.ShortM) { cycles++; } mods &= ~OpDef.CycleMod.OneIfM0; } if ((mods & OpDef.CycleMod.TwoIfM0) != 0) { if (!flags.ShortM) { cycles += 2; } mods &= ~OpDef.CycleMod.TwoIfM0; } if ((mods & OpDef.CycleMod.OneIfX0) != 0) { if (!flags.ShortX) { cycles++; } mods &= ~OpDef.CycleMod.OneIfX0; } if ((mods & OpDef.CycleMod.OneIfE0) != 0) { if (flags.E == 0) { cycles++; } mods &= ~OpDef.CycleMod.OneIfE0; } // Some of these can be known, some can't. if ((mods & OpDef.CycleMod.OneIfD1) != 0) { if (flags.D == 1) { cycles++; } if (flags.D == 0 || flags.D == 1) { mods &= ~OpDef.CycleMod.OneIfD1; } } if ((mods & OpDef.CycleMod.OneIfBranchTaken) != 0) { if (branchTaken == OpDef.BranchTaken.Always) { cycles++; } if (branchTaken != OpDef.BranchTaken.Indeterminate) { mods &= ~OpDef.CycleMod.OneIfBranchTaken; } } if ((mods & OpDef.CycleMod.OneIfBranchPage) != 0) { if (branchCrossesPage && flags.E != 0) { cycles++; // +1 unless we're in native mode on 65816 } mods &= ~OpDef.CycleMod.OneIfBranchPage; } // We can't evaluate OneIfDpNonzero, OneIfIndexPage, or MinusOneIfNoPage. // OneIf65C02 was handled earlier. // TODO(maybe): in some cases we can know that the index doesn't cross a // page boundary by checking the address, e.g. "LDA $2000,X" can't cross. if (mods != 0) { // Some unresolved mods remain. cycles = -cycles; } return(cycles); }