mirror of
https://github.com/fadden/6502bench.git
synced 2024-08-20 11:28:58 +00:00
Variables, types, and comments have been updated to reflect the new naming scheme. The project file serialization code is untouched, because the data is output as serialized enumerated values. Adding a string conversion layer didn't seem worthwhile. No changes in behavior. (issue #89)
1232 lines
52 KiB
C#
1232 lines
52 KiB
C#
/*
|
|
* Copyright 2019 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.ComponentModel;
|
|
using System.Diagnostics;
|
|
using System.Runtime.CompilerServices;
|
|
using System.Text;
|
|
using System.Windows;
|
|
|
|
using Asm65;
|
|
|
|
namespace SourceGen.WpfGui {
|
|
/// <summary>
|
|
/// Instruction operand editor.
|
|
/// </summary>
|
|
public partial class EditInstructionOperand : Window, INotifyPropertyChanged {
|
|
/// <summary>
|
|
/// Updated format descriptor. Will be null if the user selected "default".
|
|
/// </summary>
|
|
public FormatDescriptor FormatDescriptorResult { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Updated local variable table. Will be null if no changes were made.
|
|
/// </summary>
|
|
public LocalVariableTable LocalVariableResult { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Offset of the local variable table we updated in LocalVariableResult.
|
|
/// </summary>
|
|
public int LocalVariableTableOffsetResult { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Offset of label that was edited. A non-negative value here indicates that an
|
|
/// edit has been made.
|
|
/// </summary>
|
|
public int SymbolEditOffsetResult { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Edited project property, or null if no changes were made.
|
|
/// </summary>
|
|
public DefSymbol ProjectPropertyResult { get; private set; }
|
|
|
|
/// <summary>
|
|
/// The project property that was modified, or null if none.
|
|
/// </summary>
|
|
public DefSymbol PrevProjectPropertyResult { get; private set; }
|
|
|
|
/// <summary>
|
|
/// Updated label.
|
|
/// </summary>
|
|
public Symbol SymbolEditResult { get; private set; }
|
|
|
|
private readonly string SYMBOL_NOT_USED;
|
|
private readonly string SYMBOL_UNKNOWN;
|
|
private readonly string SYMBOL_INVALID;
|
|
|
|
private readonly string CREATE_LOCAL_VARIABLE;
|
|
private readonly string EDIT_LOCAL_VARIABLE;
|
|
private readonly string LV_MATCH_FOUND_ADDRESS;
|
|
private readonly string LV_MATCH_FOUND_CONSTANT;
|
|
|
|
private readonly string CREATE_LABEL;
|
|
private readonly string EDIT_LABEL;
|
|
private readonly string CREATE_PROJECT_SYMBOL;
|
|
private readonly string EDIT_PROJECT_SYMBOL;
|
|
private readonly string CURRENT_LABEL;
|
|
private readonly string CURRENT_LABEL_ADJUSTED_FMT;
|
|
|
|
/// <summary>
|
|
/// Project reference.
|
|
/// </summary>
|
|
private DisasmProject mProject;
|
|
|
|
/// <summary>
|
|
/// Offset of instruction being edited.
|
|
/// </summary>
|
|
private int mOffset;
|
|
|
|
/// <summary>
|
|
/// Format object.
|
|
/// </summary>
|
|
private Formatter mFormatter;
|
|
|
|
/// <summary>
|
|
/// Operation definition, from file data.
|
|
/// </summary>
|
|
private OpDef mOpDef;
|
|
|
|
/// <summary>
|
|
/// Status flags at the point where the instruction is defined. This tells us whether
|
|
/// an operand is 8-bit or 16-bit.
|
|
/// </summary>
|
|
private StatusFlags mOpStatusFlags;
|
|
|
|
/// <summary>
|
|
/// Operand value, extracted from file data. For a relative branch, this will be
|
|
/// an address instead.
|
|
/// </summary>
|
|
private int mOperandValue;
|
|
|
|
/// <summary>
|
|
/// True when the input is valid. Controls whether the OK button is enabled.
|
|
/// </summary>
|
|
public bool IsValid {
|
|
get { return mIsValid; }
|
|
set { mIsValid = value; OnPropertyChanged(); }
|
|
}
|
|
private bool mIsValid;
|
|
|
|
/// <summary>
|
|
/// Set when our load-time initialization is complete.
|
|
/// </summary>
|
|
private bool mLoadDone;
|
|
|
|
// INotifyPropertyChanged implementation
|
|
public event PropertyChangedEventHandler PropertyChanged;
|
|
private void OnPropertyChanged([CallerMemberName] string propertyName = "") {
|
|
PropertyChanged?.Invoke(this, new PropertyChangedEventArgs(propertyName));
|
|
}
|
|
|
|
|
|
/// <summary>
|
|
/// Constructor.
|
|
/// </summary>
|
|
/// <param name="owner">Parent window.</param>
|
|
/// <param name="project">Project reference.</param>
|
|
/// <param name="offset">File offset of instruction start.</param>
|
|
/// <param name="formatter">Formatter object, for preview window.</param>
|
|
public EditInstructionOperand(Window owner, DisasmProject project, int offset,
|
|
Formatter formatter) {
|
|
InitializeComponent();
|
|
Owner = owner;
|
|
DataContext = this;
|
|
|
|
mProject = project;
|
|
mOffset = offset;
|
|
mFormatter = formatter;
|
|
|
|
SYMBOL_NOT_USED = (string)FindResource("str_SymbolNotUsed");
|
|
SYMBOL_INVALID = (string)FindResource("str_SymbolNotValid");
|
|
SYMBOL_UNKNOWN = (string)FindResource("str_SymbolUnknown");
|
|
|
|
CREATE_LOCAL_VARIABLE = (string)FindResource("str_CreateLocalVariable");
|
|
EDIT_LOCAL_VARIABLE = (string)FindResource("str_EditLocalVariable");
|
|
LV_MATCH_FOUND_ADDRESS = (string)FindResource("str_LvMatchFoundAddress");
|
|
LV_MATCH_FOUND_CONSTANT = (string)FindResource("str_LvMatchFoundConstant");
|
|
|
|
CREATE_LABEL = (string)FindResource("str_CreateLabel");
|
|
EDIT_LABEL = (string)FindResource("str_EditLabel");
|
|
CREATE_PROJECT_SYMBOL = (string)FindResource("str_CreateProjectSymbol");
|
|
EDIT_PROJECT_SYMBOL = (string)FindResource("str_EditProjectSymbol");
|
|
CURRENT_LABEL = (string)FindResource("str_CurrentLabel");
|
|
CURRENT_LABEL_ADJUSTED_FMT = (string)FindResource("str_CurrentLabelAdjustedFmt");
|
|
|
|
Debug.Assert(offset >= 0 && offset < project.FileDataLength);
|
|
mOpDef = project.CpuDef.GetOpDef(project.FileData[offset]);
|
|
Anattrib attr = project.GetAnattrib(offset);
|
|
mOpStatusFlags = attr.StatusFlags;
|
|
Debug.Assert(offset + mOpDef.GetLength(mOpStatusFlags) <= project.FileDataLength);
|
|
|
|
if (attr.OperandAddress >= 0) {
|
|
// Use this as the operand value when available. This lets us present
|
|
// relative branch instructions in the expected form.
|
|
mOperandValue = attr.OperandAddress;
|
|
} else {
|
|
// For BlockMove this will have both parts.
|
|
mOperandValue = mOpDef.GetOperand(project.FileData, offset, attr.StatusFlags);
|
|
}
|
|
}
|
|
|
|
private void Window_Loaded(object sender, RoutedEventArgs e) {
|
|
BasicFormat_Loaded();
|
|
NumericReferences_Loaded();
|
|
LocalVariables_Loaded();
|
|
mLoadDone = true;
|
|
}
|
|
|
|
private void Window_ContentRendered(object sender, EventArgs e) {
|
|
UpdateControls();
|
|
symbolTextBox.SelectAll();
|
|
symbolTextBox.Focus();
|
|
}
|
|
|
|
private void OkButton_Click(object sender, RoutedEventArgs e) {
|
|
FormatDescriptorResult = CreateDescriptorFromControls();
|
|
|
|
// Export the updated local variable table if we made changes.
|
|
if (mEditedLvTable != null) {
|
|
LocalVariableTable lvt = mProject.LvTables[mLvTableOffset];
|
|
if (mEditedLvTable != lvt) {
|
|
LocalVariableResult = mEditedLvTable;
|
|
LocalVariableTableOffsetResult = mLvTableOffset;
|
|
|
|
Debug.WriteLine("NEW TABLE:");
|
|
mEditedLvTable.DebugDump(mLvTableOffset);
|
|
} else {
|
|
Debug.WriteLine("No change to LvTable, not exporting");
|
|
}
|
|
}
|
|
|
|
if (mLabelHasBeenEdited) {
|
|
SymbolEditOffsetResult = mEditedLabelOffset;
|
|
SymbolEditResult = mEditedLabel;
|
|
}
|
|
|
|
ProjectPropertyResult = mEditedProjectSymbol;
|
|
|
|
DialogResult = true;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Looks up the symbol in the symbol table. If not found there, it checks for a
|
|
/// match against the existing or edited project symbol.
|
|
/// </summary>
|
|
private bool LookupSymbol(string label, bool isNonUnique, out Symbol sym) {
|
|
if (isNonUnique) {
|
|
// Only applies to labels, so no need to check mEditedProjectSymbol. We
|
|
// could check mEditedLabel, but there's no reason to add a symbolic reference
|
|
// on top of the numeric reference.
|
|
int targetOffset;
|
|
Anattrib attr = mProject.GetAnattrib(mOffset);
|
|
if (attr.OperandOffset >= 0) {
|
|
targetOffset = attr.OperandOffset;
|
|
} else {
|
|
targetOffset = mOffset;
|
|
}
|
|
sym = mProject.FindBestNonUniqueLabel(label, targetOffset);
|
|
if (sym != null) {
|
|
return true;
|
|
}
|
|
} else {
|
|
if (mProject.SymbolTable.TryGetValue(label, out sym)) {
|
|
return true;
|
|
}
|
|
if (mEditedProjectSymbol != null && label.Equals(mEditedProjectSymbol.Label)) {
|
|
sym = mEditedProjectSymbol;
|
|
return true;
|
|
}
|
|
}
|
|
return false;
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the state of the UI controls as the user interacts with the dialog.
|
|
/// </summary>
|
|
private void UpdateControls() {
|
|
if (!mLoadDone) {
|
|
return;
|
|
}
|
|
|
|
// Parts panel IsEnabled depends directly on formatSymbolButton.IsChecked.
|
|
IsValid = true;
|
|
IsSymbolAuto = false;
|
|
IsSymbolVar = false;
|
|
IsPartPanelEnabled = false;
|
|
SymbolValueDecimal = string.Empty;
|
|
if (FormatSymbol) {
|
|
IsPartPanelEnabled = mOpDef.IsExtendedImmediate;
|
|
|
|
string trimLabel = Symbol.TrimAndValidateLabel(SymbolLabel,
|
|
mFormatter.NonUniqueLabelPrefix, out bool isValid, out bool unused1,
|
|
out bool unused2, out bool hasNonUniquePrefix,
|
|
out Symbol.LabelAnnotation unused3);
|
|
|
|
if (!isValid) {
|
|
SymbolValueHex = SYMBOL_INVALID;
|
|
IsValid = false;
|
|
} else if (LookupSymbol(trimLabel, hasNonUniquePrefix, out Symbol sym)) {
|
|
if (sym.SymbolSource == Symbol.Source.Auto) {
|
|
// We try to block references to auto labels, but it's possible to get
|
|
// around it because FormatDescriptors are weak references (replace auto
|
|
// label with user label, reference non-existent auto label, remove user
|
|
// label). We could try harder, but currently not necessary.
|
|
//
|
|
// Referencing an auto label is unwise because we use weak references
|
|
// by name, and auto labels can appear, disappear, or be renamed.
|
|
IsValid = false;
|
|
IsSymbolAuto = true;
|
|
} else if (sym.SymbolSource == Symbol.Source.Variable) {
|
|
// Local variables can be de-duplicated and uniquified, so referring to
|
|
// them by name doesn't make sense. The numeric operand formatter will
|
|
// disregard attempts to use them in this way.
|
|
IsValid = false;
|
|
IsSymbolVar = true;
|
|
}
|
|
|
|
SymbolValueHex = mFormatter.FormatHexValue(sym.Value, 4);
|
|
SymbolValueDecimal = mFormatter.FormatDecimalValue(sym.Value);
|
|
} else {
|
|
// Valid but unknown symbol. This is fine -- symbols don't have to exist --
|
|
// but it's a little weird for non-unique symbols.
|
|
SymbolValueHex = SYMBOL_UNKNOWN;
|
|
}
|
|
} else {
|
|
SymbolValueHex = SYMBOL_NOT_USED;
|
|
}
|
|
|
|
UpdatePreview();
|
|
UpdateCopyToOperand();
|
|
}
|
|
|
|
/// <summary>
|
|
/// Updates the contents of the preview text box.
|
|
/// </summary>
|
|
private void UpdatePreview() {
|
|
// Generate a descriptor from the controls. This isn't strictly necessary, but it
|
|
// gets all of the data in one small package.
|
|
FormatDescriptor dfd = CreateDescriptorFromControls();
|
|
|
|
if (dfd == null) {
|
|
// Showing the right thing for the default format is surprisingly hard. There
|
|
// are a bunch of complicated steps that are performed in sequence, including
|
|
// the "nearby label" lookups, the elision of hidden symbols, and other
|
|
// obscure bits that may get tweaked from time to time. These things are not
|
|
// easy to factor out because we're slicing the data at a different angle: the
|
|
// initial pass walks the entire file looking for one thing at a point before
|
|
// analysis has completed, while here we're trying to mimic all of the
|
|
// steps for a single offset, after analysis has finished. It's a lot of work
|
|
// to show text that they'll see as soon as they hit "OK".
|
|
PreviewText = string.Empty;
|
|
return;
|
|
}
|
|
|
|
StringBuilder sb = new StringBuilder(16);
|
|
|
|
// Show the opcode. Don't bother trying to figure out width disambiguation here.
|
|
sb.Append(mFormatter.FormatOpcode(mOpDef, OpDef.WidthDisambiguation.None));
|
|
sb.Append(' ');
|
|
|
|
bool showHashPrefix = mOpDef.IsImmediate ||
|
|
mOpDef.AddrMode == OpDef.AddressMode.BlockMove;
|
|
if (showHashPrefix) {
|
|
sb.Append('#');
|
|
}
|
|
|
|
Anattrib attr = mProject.GetAnattrib(mOffset);
|
|
int previewHexDigits = (attr.Length - 1) * 2;
|
|
int operandValue = mOperandValue;
|
|
bool isPcRelative = false;
|
|
bool isBlockMove = false;
|
|
if (attr.OperandAddress >= 0) {
|
|
if (mOpDef.AddrMode == OpDef.AddressMode.PCRel ||
|
|
mOpDef.AddrMode == OpDef.AddressMode.DPPCRel) {
|
|
previewHexDigits = 4; // show branches as $xxxx even when on zero page
|
|
isPcRelative = true;
|
|
} else if (mOpDef.AddrMode == OpDef.AddressMode.PCRelLong ||
|
|
mOpDef.AddrMode == OpDef.AddressMode.StackPCRelLong) {
|
|
isPcRelative = true;
|
|
}
|
|
} else {
|
|
if (mOpDef.AddrMode == OpDef.AddressMode.BlockMove) {
|
|
// MVN and MVP screw things up by having two operands in one instruction.
|
|
// We deal with this by passing in the value from the second byte
|
|
// (source bank) as the value, and applying the chosen format to both bytes.
|
|
isBlockMove = true;
|
|
operandValue = mOperandValue >> 8;
|
|
previewHexDigits = 2;
|
|
}
|
|
}
|
|
|
|
switch (dfd.FormatSubType) {
|
|
case FormatDescriptor.SubType.Hex:
|
|
sb.Append(mFormatter.FormatHexValue(operandValue, previewHexDigits));
|
|
break;
|
|
case FormatDescriptor.SubType.Decimal:
|
|
sb.Append(mFormatter.FormatDecimalValue(operandValue));
|
|
break;
|
|
case FormatDescriptor.SubType.Binary:
|
|
sb.Append(mFormatter.FormatBinaryValue(operandValue, 8));
|
|
break;
|
|
case FormatDescriptor.SubType.Ascii:
|
|
case FormatDescriptor.SubType.HighAscii:
|
|
case FormatDescriptor.SubType.C64Petscii:
|
|
case FormatDescriptor.SubType.C64Screen:
|
|
CharEncoding.Encoding enc = PseudoOp.SubTypeToEnc(dfd.FormatSubType);
|
|
sb.Append(mFormatter.FormatCharacterValue(operandValue, enc));
|
|
break;
|
|
case FormatDescriptor.SubType.Symbol:
|
|
string trimLabel = Symbol.TrimAndValidateLabel(SymbolLabel,
|
|
mFormatter.NonUniqueLabelPrefix, out bool isValid, out bool unused1,
|
|
out bool unused2, out bool hasNonUniquePrefix,
|
|
out Symbol.LabelAnnotation unused3);
|
|
if (string.IsNullOrEmpty(trimLabel)) {
|
|
sb.Append("?");
|
|
} else if (LookupSymbol(trimLabel, hasNonUniquePrefix, out Symbol sym)) {
|
|
// Block move is a little weird. "MVN label1,label2" is supposed to use
|
|
// the bank byte, while "MVN #const1,#const2" uses the entire symbol.
|
|
// The easiest thing to do is require the user to specify the "bank"
|
|
// part for 24-bit symbols, and always generate this as an immediate.
|
|
//
|
|
// MVN/MVP are also the only instructions with two operands, something
|
|
// we don't really handle.
|
|
// TODO(someday): allow a different symbol for each part of the operand.
|
|
|
|
// Hack to make relative branches look right in the preview window.
|
|
// Otherwise they show up like "<LABEL" because they appear to be
|
|
// only 8 bits.
|
|
int operandLen = dfd.Length - 1;
|
|
if (operandLen == 1 && isPcRelative) {
|
|
operandLen = 2;
|
|
}
|
|
|
|
// Set the operand length to 1 for block move so we use the part
|
|
// operators (<, >, ^) rather than bit-shifting.
|
|
if (isBlockMove) {
|
|
operandLen = 1;
|
|
}
|
|
|
|
PseudoOp.FormatNumericOpFlags flags;
|
|
if (isPcRelative) {
|
|
flags = PseudoOp.FormatNumericOpFlags.IsPcRel;
|
|
} else if (showHashPrefix) {
|
|
flags = PseudoOp.FormatNumericOpFlags.HasHashPrefix;
|
|
} else {
|
|
flags = PseudoOp.FormatNumericOpFlags.None;
|
|
}
|
|
string str = PseudoOp.FormatNumericOperand(mFormatter,
|
|
mProject.SymbolTable, null, dfd,
|
|
operandValue, operandLen, flags);
|
|
sb.Append(str);
|
|
|
|
if (sym.SymbolSource == Symbol.Source.Auto) {
|
|
mIsSymbolAuto = true;
|
|
}
|
|
} else {
|
|
sb.Append(dfd.SymbolRef.Label + " (?)");
|
|
//symbolValueLabel.Text = Properties.Resources.MSG_SYMBOL_NOT_FOUND;
|
|
}
|
|
break;
|
|
default:
|
|
Debug.Assert(false);
|
|
sb.Append("BUG");
|
|
break;
|
|
}
|
|
|
|
if (isBlockMove) {
|
|
sb.Append(",#<dest>");
|
|
}
|
|
|
|
PreviewText = sb.ToString();
|
|
}
|
|
|
|
#region Basic Format
|
|
|
|
public bool FormatDefault {
|
|
get { return mFormatDefault; }
|
|
set { mFormatDefault = value; OnPropertyChanged(); UpdateControls(); }
|
|
}
|
|
private bool mFormatDefault;
|
|
|
|
public bool FormatHex {
|
|
get { return mFormatHex; }
|
|
set { mFormatHex = value; OnPropertyChanged(); UpdateControls(); }
|
|
}
|
|
private bool mFormatHex;
|
|
|
|
public bool FormatDecimal {
|
|
get { return mFormatDecimal; }
|
|
set { mFormatDecimal = value; OnPropertyChanged(); UpdateControls(); }
|
|
}
|
|
private bool mFormatDecimal;
|
|
|
|
public bool FormatBinary {
|
|
get { return mFormatBinary; }
|
|
set { mFormatBinary = value; OnPropertyChanged(); UpdateControls(); }
|
|
}
|
|
private bool mFormatBinary;
|
|
|
|
public bool IsFormatAsciiAllowed {
|
|
get { return mIsFormatAsciiAllowed; }
|
|
set { mIsFormatAsciiAllowed = value; OnPropertyChanged(); }
|
|
}
|
|
private bool mIsFormatAsciiAllowed;
|
|
|
|
public bool FormatAscii {
|
|
get { return mFormatAscii; }
|
|
set { mFormatAscii = value; OnPropertyChanged(); UpdateControls(); }
|
|
}
|
|
private bool mFormatAscii;
|
|
|
|
public bool IsFormatPetsciiAllowed {
|
|
get { return mIsFormatPetsciiAllowed; }
|
|
set { mIsFormatPetsciiAllowed = value; OnPropertyChanged(); }
|
|
}
|
|
private bool mIsFormatPetsciiAllowed;
|
|
|
|
public bool FormatPetscii {
|
|
get { return mFormatPetscii; }
|
|
set { mFormatPetscii = value; OnPropertyChanged(); UpdateControls(); }
|
|
}
|
|
private bool mFormatPetscii;
|
|
|
|
public bool IsFormatScreenCodeAllowed {
|
|
get { return mIsFormatScreenCodeAllowed; }
|
|
set { mIsFormatScreenCodeAllowed = value; OnPropertyChanged(); }
|
|
}
|
|
private bool mIsFormatScreenCodeAllowed;
|
|
|
|
public bool FormatScreenCode {
|
|
get { return mFormatScreenCode; }
|
|
set { mFormatScreenCode = value; OnPropertyChanged(); UpdateControls(); }
|
|
}
|
|
private bool mFormatScreenCode;
|
|
|
|
public bool FormatSymbol {
|
|
get { return mFormatSymbol; }
|
|
set { mFormatSymbol = value; OnPropertyChanged(); UpdateControls(); }
|
|
}
|
|
private bool mFormatSymbol;
|
|
|
|
public string SymbolLabel {
|
|
get { return mSymbolLabel; }
|
|
set {
|
|
mSymbolLabel = value;
|
|
OnPropertyChanged();
|
|
// Set the radio button when the user starts typing.
|
|
if (mLoadDone) {
|
|
FormatSymbol = true;
|
|
// this calls UpdateControls; don't do it twice
|
|
} else {
|
|
UpdateControls();
|
|
}
|
|
}
|
|
}
|
|
private string mSymbolLabel;
|
|
|
|
public bool IsSymbolAuto {
|
|
get { return mIsSymbolAuto; }
|
|
set { mIsSymbolAuto = value; OnPropertyChanged(); }
|
|
}
|
|
private bool mIsSymbolAuto;
|
|
|
|
public bool IsSymbolVar{
|
|
get { return mIsSymbolVar; }
|
|
set { mIsSymbolVar = value; OnPropertyChanged(); }
|
|
}
|
|
private bool mIsSymbolVar;
|
|
|
|
public string SymbolValueHex {
|
|
get { return mSymbolValueHex; }
|
|
set { mSymbolValueHex = value; OnPropertyChanged(); }
|
|
}
|
|
private string mSymbolValueHex;
|
|
|
|
public string SymbolValueDecimal {
|
|
get { return mSymbolValueDecimal; }
|
|
set { mSymbolValueDecimal = value; OnPropertyChanged(); }
|
|
}
|
|
private string mSymbolValueDecimal;
|
|
|
|
public bool IsPartPanelEnabled {
|
|
get { return mIsPartPanelEnabled; }
|
|
set { mIsPartPanelEnabled = value; OnPropertyChanged(); }
|
|
}
|
|
private bool mIsPartPanelEnabled;
|
|
|
|
public bool FormatPartLow {
|
|
get { return mFormatPartLow; }
|
|
set { mFormatPartLow = value; OnPropertyChanged(); UpdateControls(); }
|
|
}
|
|
private bool mFormatPartLow;
|
|
|
|
public bool FormatPartHigh {
|
|
get { return mFormatPartHigh; }
|
|
set { mFormatPartHigh = value; OnPropertyChanged(); UpdateControls(); }
|
|
}
|
|
private bool mFormatPartHigh;
|
|
|
|
public bool FormatPartBank {
|
|
get { return mFormatPartBank; }
|
|
set { mFormatPartBank = value; OnPropertyChanged(); UpdateControls(); }
|
|
}
|
|
private bool mFormatPartBank;
|
|
|
|
public string PreviewText {
|
|
get { return mPreviewText; }
|
|
set { mPreviewText = value; OnPropertyChanged(); }
|
|
}
|
|
private string mPreviewText;
|
|
|
|
/// <summary>
|
|
/// Configures the basic formatting options, based on the existing format descriptor.
|
|
/// </summary>
|
|
private void BasicFormat_Loaded() {
|
|
// Can this be represented as a character? We only allow the printable set
|
|
// here, not the extended set (which includes control characters).
|
|
if (mOperandValue == (byte) mOperandValue) {
|
|
IsFormatAsciiAllowed =
|
|
CharEncoding.IsPrintableLowOrHighAscii((byte)mOperandValue);
|
|
IsFormatPetsciiAllowed =
|
|
CharEncoding.IsPrintableC64Petscii((byte)mOperandValue);
|
|
IsFormatScreenCodeAllowed =
|
|
CharEncoding.IsPrintableC64ScreenCode((byte)mOperandValue);
|
|
} else {
|
|
IsFormatAsciiAllowed = IsFormatPetsciiAllowed = IsFormatScreenCodeAllowed =
|
|
false;
|
|
}
|
|
|
|
SymbolLabel = string.Empty;
|
|
FormatPartLow = true; // could default to high for MVN/MVP
|
|
FormatDefault = true; // if nothing better comes along
|
|
|
|
// Is there an operand format at this location? If not, we're done.
|
|
if (!mProject.OperandFormats.TryGetValue(mOffset, out FormatDescriptor dfd)) {
|
|
return;
|
|
}
|
|
|
|
// NOTE: it's entirely possible to have a weird format (e.g. string) if the
|
|
// instruction used to be tagged as code-stop. Handle it gracefully.
|
|
switch (dfd.FormatType) {
|
|
case FormatDescriptor.Type.NumericLE:
|
|
switch (dfd.FormatSubType) {
|
|
case FormatDescriptor.SubType.Hex:
|
|
FormatHex = true;
|
|
break;
|
|
case FormatDescriptor.SubType.Decimal:
|
|
FormatDecimal = true;
|
|
break;
|
|
case FormatDescriptor.SubType.Binary:
|
|
FormatBinary = true;
|
|
break;
|
|
case FormatDescriptor.SubType.Ascii:
|
|
case FormatDescriptor.SubType.HighAscii:
|
|
if (IsFormatAsciiAllowed) {
|
|
FormatAscii = true;
|
|
}
|
|
break;
|
|
case FormatDescriptor.SubType.C64Petscii:
|
|
if (IsFormatPetsciiAllowed) {
|
|
FormatPetscii = true;
|
|
}
|
|
break;
|
|
case FormatDescriptor.SubType.C64Screen:
|
|
if (IsFormatScreenCodeAllowed) {
|
|
FormatScreenCode = true;
|
|
}
|
|
break;
|
|
case FormatDescriptor.SubType.Symbol:
|
|
Debug.Assert(dfd.HasSymbol);
|
|
FormatSymbol = true;
|
|
switch (dfd.SymbolRef.ValuePart) {
|
|
case WeakSymbolRef.Part.Low:
|
|
FormatPartLow = true;
|
|
break;
|
|
case WeakSymbolRef.Part.High:
|
|
FormatPartHigh = true;
|
|
break;
|
|
case WeakSymbolRef.Part.Bank:
|
|
FormatPartBank = true;
|
|
break;
|
|
default:
|
|
Debug.Assert(false);
|
|
break;
|
|
}
|
|
SymbolLabel = Symbol.ConvertLabelForDisplay(dfd.SymbolRef.Label,
|
|
Symbol.LabelAnnotation.None, true, mFormatter);
|
|
break;
|
|
case FormatDescriptor.SubType.None:
|
|
default:
|
|
break;
|
|
}
|
|
break;
|
|
case FormatDescriptor.Type.NumericBE:
|
|
case FormatDescriptor.Type.StringGeneric:
|
|
case FormatDescriptor.Type.StringReverse:
|
|
case FormatDescriptor.Type.StringNullTerm:
|
|
case FormatDescriptor.Type.StringL8:
|
|
case FormatDescriptor.Type.StringL16:
|
|
case FormatDescriptor.Type.StringDci:
|
|
case FormatDescriptor.Type.Dense:
|
|
case FormatDescriptor.Type.Fill:
|
|
default:
|
|
// Unexpected; used to be data?
|
|
break;
|
|
}
|
|
|
|
// In theory, if FormatDefault is still checked, we failed to find a useful match
|
|
// for the format descriptor. In practice, the radio button checkification stuff
|
|
// happens later. If we want to tell the user that there's a bad descriptor present,
|
|
// we'll need to track it locally, or test all known radio buttons for True.
|
|
}
|
|
|
|
/// <summary>
|
|
/// Creates a FormatDescriptor from the current state of the dialog controls.
|
|
/// </summary>
|
|
/// <returns>New FormatDescriptor. Will return null if the default format is
|
|
/// selected, or symbol is selected with an empty label.</returns>
|
|
private FormatDescriptor CreateDescriptorFromControls() {
|
|
int instructionLength = mProject.GetAnattrib(mOffset).Length;
|
|
|
|
if (FormatSymbol) {
|
|
if (string.IsNullOrEmpty(SymbolLabel)) {
|
|
// empty symbol --> default format (intuitive way to delete label reference)
|
|
return null;
|
|
}
|
|
WeakSymbolRef.Part part;
|
|
if (FormatPartLow) {
|
|
part = WeakSymbolRef.Part.Low;
|
|
} else if (FormatPartHigh) {
|
|
part = WeakSymbolRef.Part.High;
|
|
} else if (FormatPartBank) {
|
|
part = WeakSymbolRef.Part.Bank;
|
|
} else {
|
|
Debug.Assert(false);
|
|
part = WeakSymbolRef.Part.Low;
|
|
}
|
|
|
|
// Deal with non-unique labels. If the label refers to an existing
|
|
// symbol, use its label, which will have the tag. If the label doesn't
|
|
// have a match, discard it -- we don't support weak refs to ambiguous
|
|
// non-unique symbols.
|
|
string trimLabel = Symbol.TrimAndValidateLabel(SymbolLabel,
|
|
mFormatter.NonUniqueLabelPrefix, out bool isValid, out bool unused1,
|
|
out bool unused2, out bool hasNonUniquePrefix,
|
|
out Symbol.LabelAnnotation unused3);
|
|
if (isValid && hasNonUniquePrefix) {
|
|
if (LookupSymbol(trimLabel, hasNonUniquePrefix, out Symbol sym)) {
|
|
trimLabel = sym.Label;
|
|
} else {
|
|
Debug.WriteLine("Attempt to create ref to non-existant non-unique sym");
|
|
return null;
|
|
}
|
|
}
|
|
|
|
return FormatDescriptor.Create(instructionLength,
|
|
new WeakSymbolRef(trimLabel, part), false);
|
|
}
|
|
|
|
FormatDescriptor.SubType subType;
|
|
if (FormatDefault) {
|
|
return null;
|
|
} else if (FormatHex) {
|
|
subType = FormatDescriptor.SubType.Hex;
|
|
} else if (FormatDecimal) {
|
|
subType = FormatDescriptor.SubType.Decimal;
|
|
} else if (FormatBinary) {
|
|
subType = FormatDescriptor.SubType.Binary;
|
|
} else if (FormatAscii) {
|
|
if (mOperandValue > 0x7f) {
|
|
subType = FormatDescriptor.SubType.HighAscii;
|
|
} else {
|
|
subType = FormatDescriptor.SubType.Ascii;
|
|
}
|
|
} else if (FormatPetscii) {
|
|
subType = FormatDescriptor.SubType.C64Petscii;
|
|
} else if (FormatScreenCode) {
|
|
subType = FormatDescriptor.SubType.C64Screen;
|
|
} else {
|
|
Debug.Assert(false);
|
|
subType = FormatDescriptor.SubType.None;
|
|
}
|
|
|
|
return FormatDescriptor.Create(instructionLength,
|
|
FormatDescriptor.Type.NumericLE, subType);
|
|
}
|
|
|
|
#endregion Basic Format
|
|
|
|
#region Numeric References
|
|
|
|
public bool ShowNarNotAddress {
|
|
get { return mShowNarNotAddress; }
|
|
set { mShowNarNotAddress = value; OnPropertyChanged(); }
|
|
}
|
|
private bool mShowNarNotAddress;
|
|
|
|
public bool ShowNarEditLabel {
|
|
get { return mShowNarEditLabel; }
|
|
set { mShowNarEditLabel = value; OnPropertyChanged(); }
|
|
}
|
|
private bool mShowNarEditLabel;
|
|
|
|
public bool ShowNarCurrentLabel {
|
|
get { return mShowNarCurrentLabel; }
|
|
set { mShowNarCurrentLabel = value; OnPropertyChanged(); }
|
|
}
|
|
private bool mShowNarCurrentLabel;
|
|
|
|
public string NarLabelOffsetText {
|
|
get { return mNarLabelOffsetText; }
|
|
set { mNarLabelOffsetText = value; OnPropertyChanged(); }
|
|
}
|
|
private string mNarLabelOffsetText;
|
|
|
|
public string NarTargetLabel {
|
|
get { return mNarTargetLabel; }
|
|
set { mNarTargetLabel = value; OnPropertyChanged(); }
|
|
}
|
|
private string mNarTargetLabel;
|
|
|
|
public string CreateEditLabelText {
|
|
get { return mCreateEditLabelText; }
|
|
set { mCreateEditLabelText = value; OnPropertyChanged(); }
|
|
}
|
|
private string mCreateEditLabelText;
|
|
|
|
public bool ShowNarExternalSymbol {
|
|
get { return mShowNarExternalSymbol; }
|
|
set { mShowNarExternalSymbol = value; OnPropertyChanged(); }
|
|
}
|
|
private bool mShowNarExternalSymbol;
|
|
|
|
public bool ShowNarPlatformSymbol {
|
|
get { return mShowNarPlatformSymbol; }
|
|
set { mShowNarPlatformSymbol = value; OnPropertyChanged(); }
|
|
}
|
|
private bool mShowNarPlatformSymbol;
|
|
|
|
public string NarPlatformSymbol {
|
|
get { return mNarPlatformSymbol; }
|
|
set { mNarPlatformSymbol = value; OnPropertyChanged(); }
|
|
}
|
|
private string mNarPlatformSymbol;
|
|
|
|
public bool ShowNarNoProjectMatch {
|
|
get { return mShowNarNoProjectMatch; }
|
|
set { mShowNarNoProjectMatch = value; OnPropertyChanged(); }
|
|
}
|
|
private bool mShowNarNoProjectMatch;
|
|
|
|
public bool ShowNarProjectSymbol {
|
|
get { return mShowNarProjectSymbol; }
|
|
set { mShowNarProjectSymbol = value; OnPropertyChanged(); }
|
|
}
|
|
private bool mShowNarProjectSymbol;
|
|
|
|
public string NarProjectSymbol {
|
|
get { return mNarProjectSymbol; }
|
|
set { mNarProjectSymbol = value; OnPropertyChanged(); }
|
|
}
|
|
private string mNarProjectSymbol;
|
|
|
|
public string CreateEditProjectSymbolText {
|
|
get { return mCreateEditProjectSymbolText; }
|
|
set { mCreateEditProjectSymbolText = value; OnPropertyChanged(); }
|
|
}
|
|
private string mCreateEditProjectSymbolText;
|
|
|
|
public bool IsCopyToOperandEnabled {
|
|
get { return mIsCopyToOperandEnabled; }
|
|
set { mIsCopyToOperandEnabled = value; OnPropertyChanged(); }
|
|
}
|
|
private bool mIsCopyToOperandEnabled;
|
|
|
|
/// <summary>
|
|
/// Edited label value. Will be null if the label hasn't been created, or has been
|
|
/// deleted (by entering a blank string in the label edit box).
|
|
/// </summary>
|
|
private Symbol mEditedLabel;
|
|
|
|
/// <summary>
|
|
/// Set to true if the label has been edited.
|
|
/// </summary>
|
|
private bool mLabelHasBeenEdited;
|
|
|
|
/// <summary>
|
|
/// Address associated with the label (for the Symbol's value).
|
|
/// </summary>
|
|
private int mLabelTargetAddress = -1;
|
|
|
|
/// <summary>
|
|
/// Offset of edited label.
|
|
/// </summary>
|
|
private int mEditedLabelOffset = -1;
|
|
|
|
/// <summary>
|
|
/// Edited project symbol. If a symbol already exists, this will be initialized to the
|
|
/// existing value. Otherwise this will be null.
|
|
/// </summary>
|
|
/// <remarks>
|
|
/// Project symbols can't be deleted from here, so a null reference always means that
|
|
/// there's no symbol and we haven't made an edit.
|
|
/// </remarks>
|
|
private DefSymbol mEditedProjectSymbol;
|
|
|
|
/// <summary>
|
|
/// Configures the UI in the local variables box at load time.
|
|
/// </summary>
|
|
private void NumericReferences_Loaded() {
|
|
SymbolEditOffsetResult = -1;
|
|
|
|
Anattrib attr = mProject.GetAnattrib(mOffset);
|
|
|
|
if (attr.OperandOffset >= 0) {
|
|
// Operand target is inside the file.
|
|
ShowNarEditLabel = true;
|
|
|
|
// Seek back to the start of the instruction or data item if the operand points
|
|
// into the middle of one. This is *not* the same as the "nearby" search,
|
|
// which will traverse multiple items to find a match.
|
|
// TODO: this can create a situation where the code list shows FUBAR-1 but we
|
|
// edit an earlier label, if the earlier label has a multi-byte format that
|
|
// includes the target address. (An example can be found in 2024-ui-edge-cases.)
|
|
mEditedLabelOffset =
|
|
DataAnalysis.GetBaseOperandOffset(mProject, attr.OperandOffset);
|
|
mLabelTargetAddress = mProject.GetAnattrib(mEditedLabelOffset).Address;
|
|
if (mProject.UserLabels.TryGetValue(mEditedLabelOffset, out Symbol sym)) {
|
|
// Has a label.
|
|
ShowNarCurrentLabel = true;
|
|
if (mEditedLabelOffset != attr.OperandOffset) {
|
|
NarLabelOffsetText = string.Format(CURRENT_LABEL_ADJUSTED_FMT,
|
|
mFormatter.FormatAdjustment(attr.OperandOffset - mEditedLabelOffset));
|
|
} else {
|
|
NarLabelOffsetText = CURRENT_LABEL;
|
|
}
|
|
NarTargetLabel = sym.GenerateDisplayLabel(mFormatter);
|
|
mEditedLabel = sym;
|
|
CreateEditLabelText = EDIT_LABEL;
|
|
} else {
|
|
NarLabelOffsetText = CURRENT_LABEL;
|
|
CreateEditLabelText = CREATE_LABEL;
|
|
}
|
|
} else if (attr.OperandAddress >= 0) {
|
|
ShowNarExternalSymbol = true;
|
|
|
|
// There can be multiple symbols with the same value, so we walk through the
|
|
// list and identify the first matching platform and project symbols. We're
|
|
// only interested in address symbols, not constants.
|
|
Symbol firstPlatform = null;
|
|
Symbol firstProject = null;
|
|
foreach (Symbol sym in mProject.SymbolTable) {
|
|
if (sym.Value == attr.OperandAddress && !sym.IsConstant) {
|
|
if (firstPlatform == null && sym.SymbolSource == Symbol.Source.Platform) {
|
|
firstPlatform = sym;
|
|
} else if (firstProject == null &&
|
|
sym.SymbolSource == Symbol.Source.Project) {
|
|
firstProject = sym;
|
|
}
|
|
|
|
if (firstPlatform != null && firstProject != null) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
|
|
if (firstPlatform != null) {
|
|
ShowNarPlatformSymbol = true;
|
|
NarPlatformSymbol = firstPlatform.Label;
|
|
}
|
|
if (firstProject != null) {
|
|
ShowNarProjectSymbol = true;
|
|
NarProjectSymbol = firstProject.Label;
|
|
CreateEditProjectSymbolText = EDIT_PROJECT_SYMBOL;
|
|
|
|
mEditedProjectSymbol = (DefSymbol)firstProject;
|
|
PrevProjectPropertyResult = mEditedProjectSymbol;
|
|
} else {
|
|
ShowNarNoProjectMatch = true;
|
|
CreateEditProjectSymbolText = CREATE_PROJECT_SYMBOL;
|
|
}
|
|
} else {
|
|
// Probably an immediate operand.
|
|
ShowNarNotAddress = true;
|
|
}
|
|
}
|
|
|
|
private void UpdateCopyToOperand() {
|
|
IsCopyToOperandEnabled = false;
|
|
if (mEditedProjectSymbol != null) {
|
|
// We have a pre-existing or recently-edited symbol. See if the current
|
|
// operand configuration already matches.
|
|
if (!FormatSymbol || !mEditedProjectSymbol.Label.Equals(SymbolLabel)) {
|
|
IsCopyToOperandEnabled = true;
|
|
}
|
|
}
|
|
}
|
|
|
|
private void EditLabel_Click(object sender, RoutedEventArgs e) {
|
|
EditLabel dlg = new EditLabel(this, mEditedLabel, mLabelTargetAddress,
|
|
mEditedLabelOffset, mProject.SymbolTable, mFormatter);
|
|
if (dlg.ShowDialog() != true || mEditedLabel == dlg.LabelSym) {
|
|
Debug.WriteLine("No change to label, ignoring edit");
|
|
return;
|
|
}
|
|
|
|
mEditedLabel = dlg.LabelSym;
|
|
mLabelHasBeenEdited = true;
|
|
|
|
// Update UI to match current state.
|
|
if (mEditedLabel == null) {
|
|
ShowNarCurrentLabel = false;
|
|
CreateEditLabelText = CREATE_LABEL;
|
|
} else {
|
|
ShowNarCurrentLabel = true;
|
|
CreateEditLabelText = EDIT_LABEL;
|
|
NarTargetLabel = mEditedLabel.GenerateDisplayLabel(mFormatter);
|
|
}
|
|
|
|
// Sort of nice to just hit return twice after entering a label, so move the focus
|
|
// to the OK button.
|
|
okButton.Focus();
|
|
}
|
|
|
|
private void EditProjectSymbol_Click(object sender, RoutedEventArgs e) {
|
|
DefSymbol origSym = mEditedProjectSymbol;
|
|
if (origSym == null) {
|
|
// Need to start with a symbol so we can set the value field.
|
|
string symName = "SYM";
|
|
if (!string.IsNullOrEmpty(SymbolLabel) &&
|
|
Asm65.Label.ValidateLabel(SymbolLabel)) {
|
|
symName = SymbolLabel;
|
|
}
|
|
origSym = new DefSymbol(symName, mOperandValue, Symbol.Source.Project,
|
|
Symbol.Type.ExternalAddr, FormatDescriptor.SubType.None);
|
|
}
|
|
|
|
EditDefSymbol dlg = new EditDefSymbol(this, mFormatter,
|
|
mProject.ProjectProps.ProjectSyms, origSym, null, false, true);
|
|
if (dlg.ShowDialog() != true) {
|
|
return;
|
|
}
|
|
Debug.Assert(dlg.NewSym != null); // can't delete a symbol from dialog
|
|
|
|
if (mEditedProjectSymbol == dlg.NewSym) {
|
|
Debug.WriteLine("No change to project symbol, ignoring edit");
|
|
return;
|
|
}
|
|
mEditedProjectSymbol = dlg.NewSym;
|
|
ShowNarProjectSymbol = true;
|
|
ShowNarNoProjectMatch = false;
|
|
NarProjectSymbol = mEditedProjectSymbol.Label;
|
|
CreateEditProjectSymbolText = EDIT_PROJECT_SYMBOL;
|
|
|
|
// The preview and symbol value display will use mEditedProjectSymbol if it's the
|
|
// only place the symbol exists, so we want to keep the other controls updated.
|
|
UpdateControls();
|
|
|
|
// Move the focus to the OK button.
|
|
okButton.Focus();
|
|
}
|
|
|
|
private void CopyToOperandButton_Click(object sender, RoutedEventArgs e) {
|
|
FormatSymbol = true;
|
|
SymbolLabel = mEditedProjectSymbol.Label;
|
|
IsCopyToOperandEnabled = false;
|
|
// changes to controls will call UpdateControls() for us
|
|
}
|
|
|
|
#endregion Numeric References
|
|
|
|
#region Local Variables
|
|
|
|
public bool ShowLvNotApplicable {
|
|
get { return mShowLvNotApplicable; }
|
|
set { mShowLvNotApplicable = value; OnPropertyChanged(); }
|
|
}
|
|
private bool mShowLvNotApplicable;
|
|
|
|
public bool ShowLvTableNotFound {
|
|
get { return mShowLvTableNotFound; }
|
|
set { mShowLvTableNotFound = value; OnPropertyChanged(); }
|
|
}
|
|
private bool mShowLvTableNotFound;
|
|
|
|
public bool ShowLvNoMatchFound {
|
|
get { return mShowLvNoMatchFound; }
|
|
set { mShowLvNoMatchFound = value; OnPropertyChanged(); }
|
|
}
|
|
private bool mShowLvNoMatchFound;
|
|
|
|
public bool ShowLvMatchFound {
|
|
get { return mShowLvMatchFound; }
|
|
set { mShowLvMatchFound = value; OnPropertyChanged(); }
|
|
}
|
|
private bool mShowLvMatchFound;
|
|
|
|
public string LvMatchFoundText {
|
|
get { return mLvMatchFoundText; }
|
|
set { mLvMatchFoundText = value; OnPropertyChanged(); }
|
|
}
|
|
private string mLvMatchFoundText;
|
|
|
|
public string LocalVariableLabel {
|
|
get { return mLocalVariableLabel; }
|
|
set { mLocalVariableLabel = value; OnPropertyChanged(); }
|
|
}
|
|
private string mLocalVariableLabel;
|
|
|
|
public bool ShowLvCreateEditButton {
|
|
get { return mShowLvCreateEditButton; }
|
|
set { mShowLvCreateEditButton = value; OnPropertyChanged(); }
|
|
}
|
|
private bool mShowLvCreateEditButton;
|
|
|
|
public string CreateEditLocalVariableText {
|
|
get { return mCreateEditLocalVariableText; }
|
|
set { mCreateEditLocalVariableText = value; OnPropertyChanged(); }
|
|
}
|
|
private string mCreateEditLocalVariableText;
|
|
|
|
/// <summary>
|
|
/// Offset of LocalVariableTable we're going to modify.
|
|
/// </summary>
|
|
private int mLvTableOffset = -1;
|
|
|
|
/// <summary>
|
|
/// Local variable value. If there's already a definition, this will be pre-filled
|
|
/// with the current contents. Otherwise it will be null.
|
|
/// </summary>
|
|
private DefSymbol mEditedLocalVar;
|
|
|
|
/// <summary>
|
|
/// Clone of original table, with local edits.
|
|
/// </summary>
|
|
private LocalVariableTable mEditedLvTable;
|
|
|
|
|
|
/// <summary>
|
|
/// Configures the UI in the local variables box at load time.
|
|
/// </summary>
|
|
private void LocalVariables_Loaded() {
|
|
if (!mOpDef.IsDirectPageInstruction && !mOpDef.IsStackRelInstruction) {
|
|
ShowLvNotApplicable = true;
|
|
return;
|
|
}
|
|
|
|
LvMatchFoundText = mOpDef.IsDirectPageInstruction ?
|
|
LV_MATCH_FOUND_ADDRESS : LV_MATCH_FOUND_CONSTANT;
|
|
|
|
LocalVariableLookup lvLookup =
|
|
new LocalVariableLookup(mProject.LvTables, mProject, null, false, false);
|
|
|
|
// If the operand is already a local variable, use whichever one the
|
|
// analyzer found.
|
|
Anattrib attr = mProject.GetAnattrib(mOffset);
|
|
if (attr.DataDescriptor != null && attr.DataDescriptor.HasSymbol &&
|
|
attr.DataDescriptor.SymbolRef.IsVariable) {
|
|
// Select the table that defines the local variable that's currently
|
|
// associated with this operand.
|
|
mLvTableOffset = lvLookup.GetDefiningTableOffset(mOffset,
|
|
attr.DataDescriptor.SymbolRef);
|
|
Debug.Assert(mLvTableOffset >= 0);
|
|
Debug.WriteLine("Symbol " + attr.DataDescriptor.SymbolRef +
|
|
" from var table at +" + mLvTableOffset.ToString("x6"));
|
|
} else {
|
|
// Operand is not a local variable. Find the closest table.
|
|
mLvTableOffset = lvLookup.GetNearestTableOffset(mOffset);
|
|
Debug.WriteLine("Closest table is at +" + mLvTableOffset.ToString("x6"));
|
|
}
|
|
|
|
if (mLvTableOffset < 0) {
|
|
ShowLvTableNotFound = true;
|
|
} else {
|
|
// Found a table. Do we have a matching symbol?
|
|
ShowLvCreateEditButton = true;
|
|
DefSymbol existingVar = lvLookup.GetSymbol(mOffset, mOperandValue,
|
|
mOpDef.IsDirectPageInstruction ?
|
|
Symbol.Type.ExternalAddr : Symbol.Type.Constant);
|
|
if (existingVar == null) {
|
|
ShowLvNoMatchFound = true;
|
|
CreateEditLocalVariableText = CREATE_LOCAL_VARIABLE;
|
|
} else {
|
|
mEditedLocalVar = lvLookup.GetOriginalForm(existingVar);
|
|
ShowLvMatchFound = true;
|
|
CreateEditLocalVariableText = EDIT_LOCAL_VARIABLE;
|
|
LocalVariableLabel = mEditedLocalVar.Label;
|
|
}
|
|
|
|
// We need to update the symbol table while we work to make the uniqueness
|
|
// check come out right. Otherwise if you edit, rename FOO to BAR,
|
|
// then edit again, you won't be able to rename BAR back to FOO because
|
|
// it's already in the list and it's not self.
|
|
//
|
|
// We don't need the full LVT, just the list of symbols, but we'll want
|
|
// to hand the modified table to the caller when we exit.
|
|
LocalVariableTable lvt = mProject.LvTables[mLvTableOffset];
|
|
mEditedLvTable = new LocalVariableTable(lvt);
|
|
}
|
|
}
|
|
|
|
private void EditLocalVariableButton_Click(object sender, RoutedEventArgs e) {
|
|
Debug.Assert(mOpDef.IsDirectPageInstruction || mOpDef.IsStackRelInstruction);
|
|
Debug.Assert(mLvTableOffset >= 0);
|
|
|
|
DefSymbol initialVar = mEditedLocalVar;
|
|
|
|
if (initialVar == null) {
|
|
Symbol.Type symType;
|
|
if (mOpDef.IsDirectPageInstruction) {
|
|
symType = Symbol.Type.ExternalAddr;
|
|
} else {
|
|
symType = Symbol.Type.Constant;
|
|
}
|
|
|
|
// We need to pre-load the value and type, but we can't create a symbol with
|
|
// an empty name. We don't really need to create something unique since the
|
|
// dialog will handle it.
|
|
initialVar = new DefSymbol("VAR", mOperandValue,
|
|
Symbol.Source.Variable, symType, Symbol.LabelAnnotation.None,
|
|
FormatDescriptor.SubType.None, 1, true, string.Empty,
|
|
DefSymbol.DirectionFlags.ReadWrite, null, string.Empty);
|
|
}
|
|
|
|
EditDefSymbol dlg = new EditDefSymbol(this, mFormatter,
|
|
mEditedLvTable.GetSortedByLabel(), initialVar, mProject.SymbolTable,
|
|
true, true);
|
|
if (dlg.ShowDialog() == true) {
|
|
if (mEditedLocalVar != dlg.NewSym) {
|
|
// Integrate result. Future edits will start with this.
|
|
// We can't delete a symbol, just create or modify.
|
|
Debug.Assert(dlg.NewSym != null);
|
|
mEditedLocalVar = dlg.NewSym;
|
|
mEditedLvTable.AddOrReplace(dlg.NewSym);
|
|
LocalVariableLabel = mEditedLocalVar.Label;
|
|
CreateEditLocalVariableText = EDIT_LOCAL_VARIABLE;
|
|
ShowLvNoMatchFound = false;
|
|
ShowLvMatchFound = true;
|
|
} else {
|
|
Debug.WriteLine("No change to def symbol, ignoring edit");
|
|
}
|
|
|
|
okButton.Focus();
|
|
}
|
|
}
|
|
|
|
#endregion Local Variables
|
|
}
|
|
}
|