From bcac8bc6a08950ae71772b3d63c98c601003ca96 Mon Sep 17 00:00:00 2001
From: Andy McFadden
Date: Mon, 21 Oct 2019 15:15:09 -0700
Subject: [PATCH] Add instruction chart
This adds a window that displays all of the instructions for a
given CPU in a summary grid. Undocumented instructions are
included, but shown in grey italics.
Also, tweaked AppSettings to not mark itself as dirty if a "set"
operation doesn't actually change anything.
---
Asm65/CpuDef.cs | 11 +-
Asm65/OpDescription.cs | 1 +
SourceGen/AppSettings.cs | 30 ++-
SourceGen/DisasmProject.cs | 2 +-
SourceGen/MainController.cs | 25 +++
SourceGen/RuntimeData/Help/index.html | 1 +
SourceGen/RuntimeData/Help/tools.html | 8 +-
SourceGen/SourceGen.csproj | 7 +
SourceGen/Tools/WpfGui/InstructionChart.xaml | 83 ++++++++
.../Tools/WpfGui/InstructionChart.xaml.cs | 184 ++++++++++++++++++
SourceGen/WpfGui/MainWindow.xaml | 5 +
SourceGen/WpfGui/MainWindow.xaml.cs | 5 +
12 files changed, 349 insertions(+), 13 deletions(-)
create mode 100644 SourceGen/Tools/WpfGui/InstructionChart.xaml
create mode 100644 SourceGen/Tools/WpfGui/InstructionChart.xaml.cs
diff --git a/Asm65/CpuDef.cs b/Asm65/CpuDef.cs
index 1d9ae8e..1f26d2d 100644
--- a/Asm65/CpuDef.cs
+++ b/Asm65/CpuDef.cs
@@ -171,6 +171,7 @@ namespace Asm65 {
cpuDef = Cpu65C02;
break;
default:
+ // 6502, 6502B, 6502C, 6507, 6510, 8502, 2A03
cpuDef = Cpu6502;
break;
}
@@ -235,13 +236,19 @@ namespace Asm65 {
///
- /// Returns an entry from the OpDef array for the specified opcode, 0-255. (We could
- /// probably just make this the class indexer.)
+ /// Returns an entry from the OpDef array for the specified opcode, 0-255.
///
/// Instruction opcode number (0-255).
/// Instruction definition.
public OpDef GetOpDef(int op) { return mOpDefs[op]; }
+ ///
+ /// Indexer. Returns the definition of opcode N.
+ ///
+ public OpDef this[int op] {
+ get { return mOpDefs[op]; }
+ }
+
///
/// 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
diff --git a/Asm65/OpDescription.cs b/Asm65/OpDescription.cs
index f7dff42..349157b 100644
--- a/Asm65/OpDescription.cs
+++ b/Asm65/OpDescription.cs
@@ -147,6 +147,7 @@ namespace Asm65 {
{ OpName.INC, "Increment Accumulator" },
{ OpName.INX, "Increment Index X" },
{ OpName.INY, "Increment Index Y" },
+ { OpName.JML, "Jump Long" },
{ OpName.JMP, "Jump" },
{ OpName.JSL, "Jump to Subroutine Long" },
{ OpName.JSR, "Jump to Subroutine" },
diff --git a/SourceGen/AppSettings.cs b/SourceGen/AppSettings.cs
index 4f5b442..1ac0013 100644
--- a/SourceGen/AppSettings.cs
+++ b/SourceGen/AppSettings.cs
@@ -152,7 +152,7 @@ namespace SourceGen {
///
- /// Dirty flag, set to true by every "set" call.
+ /// Dirty flag, set to true by every "set" call that changes a value.
///
public bool Dirty { get; set; }
@@ -228,8 +228,11 @@ namespace SourceGen {
/// Setting name.
/// Setting value.
public void SetInt(string name, int value) {
- mSettings[name] = value.ToString();
- Dirty = true;
+ string newVal = value.ToString();
+ if (!mSettings.TryGetValue(name, out string oldValue) || oldValue != newVal) {
+ mSettings[name] = newVal;
+ Dirty = true;
+ }
}
///
@@ -256,8 +259,11 @@ namespace SourceGen {
/// Setting name.
/// Setting value.
public void SetBool(string name, bool value) {
- mSettings[name] = value.ToString();
- Dirty = true;
+ string newVal = value.ToString();
+ if (!mSettings.TryGetValue(name, out string oldValue) || oldValue != newVal) {
+ mSettings[name] = newVal;
+ Dirty = true;
+ }
}
///
@@ -290,8 +296,11 @@ namespace SourceGen {
/// Enum type.
/// Setting value (integer enum index).
public void SetEnum(string name, Type enumType, int value) {
- mSettings[name] = Enum.GetName(enumType, value);
- Dirty = true;
+ string newVal = Enum.GetName(enumType, value);
+ if (!mSettings.TryGetValue(name, out string oldValue) || oldValue != newVal) {
+ mSettings[name] = newVal;
+ Dirty = true;
+ }
}
///
@@ -317,10 +326,13 @@ namespace SourceGen {
public void SetString(string name, string value) {
if (value == null) {
mSettings.Remove(name);
+ Dirty = true;
} else {
- mSettings[name] = value;
+ if (!mSettings.TryGetValue(name, out string oldValue) || oldValue != value) {
+ mSettings[name] = value;
+ Dirty = true;
+ }
}
- Dirty = true;
}
///
diff --git a/SourceGen/DisasmProject.cs b/SourceGen/DisasmProject.cs
index 6b35fb8..6b0518f 100644
--- a/SourceGen/DisasmProject.cs
+++ b/SourceGen/DisasmProject.cs
@@ -894,7 +894,7 @@ namespace SourceGen {
string msg = "descriptor straddles address change; len=" + kvp.Value.Length;
genLog.LogE("+" + offset.ToString("x6") + ": " + msg);
Messages.Add(new MessageList.MessageEntry(
- MessageList.MessageEntry.SeverityLevel.Error,
+ MessageList.MessageEntry.SeverityLevel.Warning,
offset,
MessageList.MessageEntry.MessageType.InvalidOffsetOrLength,
msg,
diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs
index cd4b624..73c6565 100644
--- a/SourceGen/MainController.cs
+++ b/SourceGen/MainController.cs
@@ -94,6 +94,16 @@ namespace SourceGen {
///
public bool IsAsciiChartOpen { get { return mAsciiChartDialog != null; } }
+ ///
+ /// Instruction chart reference window. Not tied to the project.
+ ///
+ private Tools.WpfGui.InstructionChart mInstructionChartDialog;
+
+ ///
+ /// Returns true if the instruction chart window is currently open.
+ ///
+ public bool IsInstructionChartOpen { get { return mInstructionChartDialog != null; } }
+
///
/// List of recently-opened projects.
///
@@ -1238,6 +1248,7 @@ namespace SourceGen {
// WPF won't exit until all windows are closed, so any unowned windows need
// to be cleaned up here.
mAsciiChartDialog?.Close();
+ mInstructionChartDialog?.Close();
mHexDumpDialog?.Close();
mShowAnalysisTimersDialog?.Close();
mShowAnalyzerOutputDialog?.Close();
@@ -3031,6 +3042,20 @@ namespace SourceGen {
}
}
+ public void ToggleInstructionChart() {
+ if (mInstructionChartDialog == null) {
+ // Create without owner so it doesn't have to be in front of main window.
+ mInstructionChartDialog = new Tools.WpfGui.InstructionChart(null,mOutputFormatter);
+ mInstructionChartDialog.Closing += (sender, e) => {
+ Debug.WriteLine("Instruction chart closed");
+ mInstructionChartDialog = null;
+ };
+ mInstructionChartDialog.Show();
+ } else {
+ mInstructionChartDialog.Close();
+ }
+ }
+
public void ToggleDataScan() {
ProjectProperties oldProps = mProject.ProjectProps;
ProjectProperties newProps = new ProjectProperties(oldProps);
diff --git a/SourceGen/RuntimeData/Help/index.html b/SourceGen/RuntimeData/Help/index.html
index ee277b9..23c4e6c 100644
--- a/SourceGen/RuntimeData/Help/index.html
+++ b/SourceGen/RuntimeData/Help/index.html
@@ -130,6 +130,7 @@ and 65816 code. The official web site is
Advanced Topics
diff --git a/SourceGen/RuntimeData/Help/tools.html b/SourceGen/RuntimeData/Help/tools.html
index 7d5693c..987f2f4 100644
--- a/SourceGen/RuntimeData/Help/tools.html
+++ b/SourceGen/RuntimeData/Help/tools.html
@@ -53,9 +53,15 @@ external files.
This opens a window with the ASCII character set. Each character is
displayed next to its numeric value in decimal and hexadecimal. The
-drop list at the bottom allows you to flip between standard and "high"
+pop-up list at the bottom allows you to flip between standard and "high"
ASCII.
+
+
+This opens a window with a summary of all 256 opcodes. The CPU can
+be chosen from the pop-up list at the bottom. Undocumented opcodes are
+shown in italics.
+