diff --git a/Asm65/OpDef.cs b/Asm65/OpDef.cs index 4d2f074..67acfe3 100644 --- a/Asm65/OpDef.cs +++ b/Asm65/OpDef.cs @@ -339,12 +339,13 @@ namespace Asm65 { } /// - /// True if the operand is an "extended immediate" value, which includes PEA in - /// addition to Imm/ImmLongA/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; + return IsImmediate || AddrMode == AddressMode.StackAbs || + AddrMode == AddressMode.BlockMove; } } diff --git a/SourceGen/AsmGen/LabelLocalizer.cs b/SourceGen/AsmGen/LabelLocalizer.cs index bbf33fb..b69bd66 100644 --- a/SourceGen/AsmGen/LabelLocalizer.cs +++ b/SourceGen/AsmGen/LabelLocalizer.cs @@ -250,7 +250,7 @@ namespace SourceGen.AsmGen { XrefSet xrefs = mProject.GetXrefSet(i); if (xrefs != null) { foreach (XrefSet.Xref xref in xrefs) { - if (!xref.IsSymbolic) { + if (!xref.IsByName) { continue; } diff --git a/SourceGen/LocalVariableLookup.cs b/SourceGen/LocalVariableLookup.cs index c596c22..5a9f8f9 100644 --- a/SourceGen/LocalVariableLookup.cs +++ b/SourceGen/LocalVariableLookup.cs @@ -195,6 +195,8 @@ namespace SourceGen { // the current offset. We may need to do this even when variables can be // redefined, because we might have a variable that's a duplicate of a user label // or project symbol. + + // Start by applying the de-duplication map. string label = symRef.Label; if (mDupRemap.TryGetValue(symRef.Label, out string remap)) { label = remap; @@ -213,6 +215,36 @@ namespace SourceGen { return defSym; } + /// + /// Identifies the LocalVariableTable that defined the symbol reference. + /// + /// Offset at which the symbol was referenced. + /// Reference to symbol. + /// Table index, or -1 if not found. + public int GetDefiningTableOffset(int offset, WeakSymbolRef symRef) { + // symRef is the non-uniquified, non-de-duplicated symbol that was generated + // during the analysis pass. This matches the contents of the tables, so we don't + // need to transform it at all. + // + // Walk backward through the list of tables until we find a match. + IList keys = mLvTables.Keys; + for (int i = keys.Count - 1; i >= 0; i--) { + if (keys[i] > offset) { + // table comes after the point of reference + continue; + } + + if (mLvTables.Values[i].GetByLabel(symRef.Label) != null) { + // found it + return keys[i]; + } + } + + // if we didn't find it, it doesn't exist... right? + Debug.Assert(mCurrentTable.GetByLabel(symRef.Label) == null); + return -1; + } + /// /// Gets a LocalVariableTable that is the result of merging all tables up to this point. /// @@ -223,6 +255,25 @@ namespace SourceGen { return mCurrentTable; } + /// + /// Finds the closest table that is defined at or before the specified offset. + /// + /// Target offset. + /// The table's definition offset, or -1 if no tables were defined before this + /// point. + public int GetNearestTableOffset(int offset) { + int nearest = -1; + + // Could do a smarter search, but I'm expecting the set to be small. + foreach (KeyValuePair kvp in mLvTables) { + if (kvp.Key > offset) { + break; + } + nearest = kvp.Key; + } + return nearest; + } + /// /// Generates a list of variables defined at the specified offset, if a table is /// associated with that offset. diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs index eab74b5..6df3ee1 100644 --- a/SourceGen/MainController.cs +++ b/SourceGen/MainController.cs @@ -1508,10 +1508,14 @@ namespace SourceGen { GoToOffset(attr.OperandOffset, false, true); } else if (dfd != null && dfd.HasSymbol) { // Operand has a symbol, do a symbol lookup. - int labelOffset = mProject.FindLabelOffsetByName( - dfd.SymbolRef.Label); - if (labelOffset >= 0) { - GoToOffset(labelOffset, false, true); + if (dfd.SymbolRef.IsVariable) { + GoToVarDefinition(line.FileOffset, dfd.SymbolRef, true); + } else { + int labelOffset = mProject.FindLabelOffsetByName( + dfd.SymbolRef.Label); + if (labelOffset >= 0) { + GoToOffset(labelOffset, false, true); + } } } else if (attr.IsDataStart || attr.IsInlineDataStart) { // If it's an Address or Symbol, we can try to resolve @@ -1926,8 +1930,23 @@ namespace SourceGen { UndoableChange uc = UndoableChange.CreateOperandFormatChange(offset, dfd, dlg.FormatDescriptorResult); cs.Add(uc); + } else { + Debug.WriteLine("No change to operand format"); + } + + // We can't delete an LvTable, so a null value here means no changes made. + if (dlg.LocalVariableResult != null) { + int tableOffset = dlg.LocalVariableTableOffsetResult; + LocalVariableTable lvt = mProject.LvTables[tableOffset]; + Debug.Assert(lvt != null); // cannot create or delete a table + UndoableChange uc = UndoableChange.CreateLocalVariableTableChange(tableOffset, + lvt, dlg.LocalVariableResult); + cs.Add(uc); + } else { + Debug.WriteLine("No change to LvTable"); } #endif + Debug.WriteLine("EditInstructionOperand: " + cs.Count + " changes"); if (cs.Count != 0) { ApplyUndoableChanges(cs); } @@ -1996,7 +2015,7 @@ namespace SourceGen { Debug.Assert(origDefSym.SymbolSource == Symbol.Source.Project); EditDefSymbol dlg = new EditDefSymbol(mMainWin, mOutputFormatter, - mProject.ProjectProps.ProjectSyms, origDefSym, null, false); + mProject.ProjectProps.ProjectSyms, origDefSym, null); if (dlg.ShowDialog() == true) { ProjectProperties newProps = new ProjectProperties(mProject.ProjectProps); newProps.ProjectSyms.Remove(origDefSym.Label); @@ -2345,6 +2364,67 @@ namespace SourceGen { } } + /// + /// Moves the view and selection to the definition of a local variable. + /// + /// Offset at which the variable was referenced. + /// Reference to variable. + public void GoToVarDefinition(int offset, WeakSymbolRef symRef, bool doPush) { + Debug.Assert(offset >= 0); + Debug.Assert(symRef.IsVariable); + + LocalVariableLookup lvLookup = new LocalVariableLookup(mProject.LvTables, mProject, + false); + int varOffset = lvLookup.GetDefiningTableOffset(offset, symRef); + if (varOffset <= 0) { + Debug.WriteLine("Local variable not found; offset=" + offset + " ref=" + symRef); + return; + } + + // We have the offset to which the local variable table is bound. We need to + // walk down until we find the variable definitions, and find the line with the + // matching symbol. + // + // We're comparing to the formatted strings -- safer than trying to find the symbol + // in the table and then guess at how the table arranges itself for display -- so we + // need to compare the formatted form of the label. + string cmpStr = mOutputFormatter.FormatVariableLabel(symRef.Label); + int lineIndex = CodeLineList.FindLineIndexByOffset(varOffset); + while (lineIndex < mProject.FileDataLength) { + LineListGen.Line line = CodeLineList[lineIndex]; + if (line.FileOffset != varOffset) { + // we've gone too far + Debug.WriteLine("ran out of LV table"); + return; + } + + if (line.LineType == LineListGen.Line.Type.LocalVariableTable) { + DisplayList.FormattedParts parts = CodeLineList.GetFormattedParts(lineIndex); + if (cmpStr.Equals(parts.Label)) { + // Eureka + NavStack.Location prevLoc = GetCurrentlySelectedLocation(); + + mMainWin.CodeListView_EnsureVisible(lineIndex); + + // Update the selection. + mMainWin.CodeListView_DeselectAll(); + mMainWin.CodeListView_SelectRange(lineIndex, 1); + + if (doPush) { + // Update the back stack and associated controls. + mNavStack.Push(prevLoc); + } + + return; + } else { + //Debug.WriteLine("Var: '" + cmpStr + "' != '" + parts.Label + "'"); + } + } + + lineIndex++; + } + } + private NavStack.Location GetCurrentlySelectedLocation() { int index = mMainWin.CodeListView_GetFirstSelectedIndex(); if (index < 0) { @@ -2604,6 +2684,7 @@ namespace SourceGen { return false; } + private bool mUpdatingSelectionHighlight; // recursion guard /// /// Updates the selection highlight. When a code item with an operand offset is /// selected, such as a branch, we want to highlight the address and label of the @@ -2612,12 +2693,26 @@ namespace SourceGen { private void UpdateSelectionHighlight() { int targetIndex = FindSelectionHighlight(); - if (mTargetHighlightIndex != targetIndex) { + if (mTargetHighlightIndex != targetIndex && !mUpdatingSelectionHighlight) { + Debug.WriteLine("Target highlight moving from " + mTargetHighlightIndex + + " to " + targetIndex); + + // The highlight is currently implemented by modifying the item in the + // display list. Because those items are immutable, we have to remove the + // old and add a new. The WPF ListView maintains its selection by object + // reference, so replacing an item requires removing the old item from the + // selection set and adding it to the new. + // + // So if a line references itself (like the ZipGS cache conditioner loop does), + // it will be the selected line while we're doing this little dance. When the + // calls below update the selection, this method will be called again. This + // turns into infinite recursion. + mUpdatingSelectionHighlight = true; mMainWin.CodeListView_RemoveSelectionHighlight(mTargetHighlightIndex); mMainWin.CodeListView_AddSelectionHighlight(targetIndex); + mUpdatingSelectionHighlight = false; mTargetHighlightIndex = targetIndex; - Debug.WriteLine("Selection highlight now " + targetIndex); } } @@ -3075,7 +3170,7 @@ namespace SourceGen { MainWindow.ReferencesListItem rli = new MainWindow.ReferencesListItem(xr.Offset, formatter.FormatOffset24(xr.Offset), formatter.FormatAddress(mProject.GetAnattrib(xr.Offset).Address, showBank), - (xr.IsSymbolic ? "Sym " : "Num ") + typeStr + + (xr.IsByName ? "Sym " : "Oth ") + typeStr + formatter.FormatAdjustment(-xr.Adjustment)); mMainWin.ReferencesList.Add(rli); diff --git a/SourceGen/RuntimeData/Help/index.html b/SourceGen/RuntimeData/Help/index.html index 4800098..997ff1f 100644 --- a/SourceGen/RuntimeData/Help/index.html +++ b/SourceGen/RuntimeData/Help/index.html @@ -59,7 +59,7 @@ and 65816 code. The official web site is
  • Navigation
  • Adding and Removing Hints
  • Format Split-Address Table
  • -
  • Toggle Single-Byte Format
  • +
  • Toggle Single-Byte Format
  • Format As Word
  • Toggle Data Scan
  • Copying to Clipboard
  • diff --git a/SourceGen/RuntimeData/Help/mainwin.html b/SourceGen/RuntimeData/Help/mainwin.html index 38cbe47..7572703 100644 --- a/SourceGen/RuntimeData/Help/mainwin.html +++ b/SourceGen/RuntimeData/Help/mainwin.html @@ -92,8 +92,8 @@ assembler directive.

    The list is divided into columns:

    • Offset. The offset within the file where the instruction - or data item starts. Always shown as a six-digit hex value - with a leading '+'.
    • + or data item starts. Throughout the UI, file offsets are shown as + six-digit hex values with a leading '+'.
    • Address. The address where the assembled code will execute. For 8-bit CPUs this is shown as a 4-digit hex number, for 16-bit CPUs the bank is shown as well. Double-click on this field to open the @@ -104,8 +104,8 @@ assembler directive.

      to open the Hex Dump Viewer. This is a floating window, so you can keep it open while you work. - Double-clicking in the bytes column in other rows will update - the window position and selection.
    • + Double-clicking in the bytes column while the window is open will + update the viewer's position and selection.
    • Flags. This shows the state of the status flags as they are before the instruction is executed. Double-click on this field to open the @@ -126,7 +126,8 @@ assembler directive.

      will appear. If you double-click this field for an instruction or data item whose operand refers to an address in the file, the selection will - jump to that location.
    • + jump to that location. If the operand is a local variable, the + selection will jump to the point where the variable was defined.
    • Operand. The instruction or data operand. Data operands may span a large number of bytes. Double-click on this field to open the @@ -151,8 +152,8 @@ to select an item, ctrl-left-click to toggle individual items on and off, and shift-left-click to select a range. You can select all lines with Edit > Select All. Resize columns by left-clicking on the divider in the header and dragging it.

      -

      Multi-line items, such as long comments or character strings, are -selected as a whole when any part is selected.

      +

      Selecting any part of a multi-line item, such as a long comment +or character string, effectively selects the entire item.

      Right-clicking opens a menu. The contents are the same as those in the Actions menu item in the menu bar. The set of options that are @@ -185,15 +186,22 @@ enabled will depend on what you have selected in the main window.

      Sets the name, value, and comment of the project symbol. Enabled when a single equate directive, generated from a project symbol, is selected.
    • +
    • Edit Local Variable Table. + Create, modify, or delete entries in a local variable table.
    • Hinting (Hint As Code Entry Point, Hint As Data Start, Hint As Inline Data, Remove Hints). Enabled when one or more code and data lines are selected. Remove Hints is only enabled when at least one line has hints. The keyboard shortcuts for hints are two-key combinations.
    • + +
    • Format Split-Address Table. Formats + a series of bytes as parts of a table of addresses.
    • Toggle Single-Byte Format. Toggles a range of lines between default format and single-byte format. Enabled when one or more data lines are selected.
    • +
    • Format As Word. Formats two bytes as + a 16-bit little-endian word.
    • Delete Note / Long Comment. Deletes the selected note or long comment. Enabled when a single note or long comment is selected.
    • Show Hex Dump. Opens the hex dump @@ -211,7 +219,7 @@ are added to the undo/redo buffer. This has no fixed size limit, so no matter how much you change, you can always undo back to the point where the project was opened.

      The undo history is not saved as part of the project. Closing a project -clears the buffer.

      +clears it.

      References Window

      @@ -224,21 +232,47 @@ type of reference will be shown.

      The reference type indicates whether the origin is an instruction or data operand, and provides an indication of the nature of the reference:

        -
      • call - subroutine call (e.g. JSR addr, JSL addr)
      • -
      • branch - conditional or unconditional branch (e.g. JMP addr, BCC addr)
      • -
      • read - read from memory (e.g. LDA addr, BIT addr)
      • -
      • write - write to memory (e.g. STA addr)
      • -
      • rmw - read-modify-write (e.g. LSR addr, TSB addr)
      • -
      • ref - address reference only (e.g. LDA #<addr, PEA addr)
      • -
      • data - address referenced by data (e.g. .DD2 addr)
      • +
      • call - subroutine call + (e.g. JSR addr, JSL addr)
      • +
      • branch - conditional or unconditional branch + (e.g. JMP addr, BCC addr)
      • +
      • read - read from memory + (e.g. LDA addr, BIT addr)
      • +
      • write - write to memory + (e.g. STA addr)
      • +
      • rmw - read-modify-write + (e.g. LSR addr, TSB addr)
      • +
      • ref - reference to address by instruction + (e.g. LDA #<addr, PEA addr)
      • +
      • data - reference to address by data + (e.g. .DD2 addr)
      -

      In addition, the source will be identified as a symbolic ("Sym") or -numeric ("Num") reference. Most will be symbolic, because SourceGen -generates symbols automatically. However, references to addresses that -are offset from a symbol will also create a numeric reference entry. -For example, "LDA addr+1" will create a symbolic reference at <addr> -(shown in the table with an adjustment of "+1") and a numeric reference -at <addr+1>.

      +

      This will be prefixed with "Sym" or "Oth" to indicate whether or not +the reference used the label at the current address. To understand +this, consider that addresses can be referenced in different ways. +For example:

      +
      +         LDA     DATA0
      +         LDX     DATA0+1
      +         RTS
      +DATA0    .DD1    $80
      +DATA1    .DD2    $90
      +
      +

      Both DATA0 and DATA1 are accessed, but +both operands used DATA0. When the DATA0 line +is selected in the code list, the references window will show the +LDA and LDX instructions, because both +instructions referenced it. When DATA1 is selected, the +references window will show the LDX, because that +instruction accessed DATA1's location even though it didn't +use the symbol. To make the difference clear, the lines in the references +window will either show "Sym" (to indicate that the symbol at the selected +line was referenced) or "Oth" (to indicate that some other symbol, or no +symbol, was used).

      +

      This is also relevant for project and platform symbols. If the +DATA0/1 labels were project symbols, the entry for DATA1 +would not be included in the list of equates, because the symbol is +never used.

      Double-clicking on a reference moves the code list selection to that reference, and adds the previous selection to the navigation stack.

      @@ -412,7 +446,7 @@ is just significantly more convenient. It also does everything as a single undoable action, so if it comes out looking wrong, just hit "undo".

      -

      Toggle Single-Byte Format

      +

      Toggle Single-Byte Format

      The "Toggle Single-Byte Format" feature provides a quick way to change a range of bytes to single bytes diff --git a/SourceGen/WpfGui/EditDefSymbol.xaml b/SourceGen/WpfGui/EditDefSymbol.xaml index fbf320a..4c8df2e 100644 --- a/SourceGen/WpfGui/EditDefSymbol.xaml +++ b/SourceGen/WpfGui/EditDefSymbol.xaml @@ -51,7 +51,8 @@ limitations under the License. + FontFamily="{StaticResource GeneralMonoFont}" + IsReadOnly="{Binding ReadOnlyValueAndType}"/> @@ -72,7 +73,7 @@ limitations under the License. - + diff --git a/SourceGen/WpfGui/EditDefSymbol.xaml.cs b/SourceGen/WpfGui/EditDefSymbol.xaml.cs index d3fd13a..9d11dce 100644 --- a/SourceGen/WpfGui/EditDefSymbol.xaml.cs +++ b/SourceGen/WpfGui/EditDefSymbol.xaml.cs @@ -79,6 +79,15 @@ namespace SourceGen.WpfGui { } private bool mIsConstant; + public bool ReadOnlyValueAndType { + get { return mReadOnlyValueAndType; } + set { mReadOnlyValueAndType = value; OnPropertyChanged(); } + } + public bool NotReadOnlyValueAndType { + get { return !mReadOnlyValueAndType; } + } + private bool mReadOnlyValueAndType; + ///

      /// Format object to use when formatting addresses and constants. /// @@ -115,11 +124,19 @@ namespace SourceGen.WpfGui { /// - /// Constructor, for editing a project symbol. + /// Constructor, for editing a project or platform symbol. /// public EditDefSymbol(Window owner, Formatter formatter, SortedList defList, DefSymbol defSym, - SymbolTable symbolTable, bool isVariable) { + SymbolTable symbolTable) + : this(owner, formatter, defList, defSym, symbolTable, false, false) { } + + /// + /// Constructor, for editing a local variable. + /// + public EditDefSymbol(Window owner, Formatter formatter, + SortedList defList, DefSymbol defSym, + SymbolTable symbolTable, bool isVariable, bool lockValueAndType) { InitializeComponent(); Owner = owner; DataContext = this; @@ -129,6 +146,7 @@ namespace SourceGen.WpfGui { mOldSym = defSym; mSymbolTable = symbolTable; mIsVariable = isVariable; + mReadOnlyValueAndType = lockValueAndType; Label = Value = VarWidth = Comment = string.Empty; if (isVariable) { diff --git a/SourceGen/WpfGui/EditInstructionOperand.xaml b/SourceGen/WpfGui/EditInstructionOperand.xaml index e16d592..8f4c589 100644 --- a/SourceGen/WpfGui/EditInstructionOperand.xaml +++ b/SourceGen/WpfGui/EditInstructionOperand.xaml @@ -34,6 +34,9 @@ limitations under the License. N/A [invalid symbol name] ? + + Create Local Variable + Edit Local Variable @@ -101,7 +104,7 @@ limitations under the License. Visibility="{Binding IsSymbolAuto, Converter={StaticResource BoolToVis}}"/> + IsEnabled="{Binding IsPartPanelEnabled}"> - - - + + + - + + + @@ -151,13 +159,25 @@ limitations under the License. + - - - - -
    public FormatDescriptor FormatDescriptorResult { get; private set; } + + /// + /// Updated local variable table. Will be null if no changes were made. + /// + public LocalVariableTable LocalVariableResult { get; private set; } + + /// + /// Offset of the local variable table we updated in LocalVariableResult. + /// + public int LocalVariableTableOffsetResult { 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; + /// /// Project reference. /// @@ -111,6 +125,9 @@ namespace SourceGen.WpfGui { 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"); + Debug.Assert(offset >= 0 && offset < project.FileDataLength); mOpDef = project.CpuDef.GetOpDef(project.FileData[offset]); Anattrib attr = project.GetAnattrib(offset); @@ -129,6 +146,7 @@ namespace SourceGen.WpfGui { private void Window_Loaded(object sender, RoutedEventArgs e) { BasicFormat_Loaded(); + NumericRefs_Loaded(); mLoadDone = true; } @@ -140,6 +158,20 @@ namespace SourceGen.WpfGui { 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"); + } + } DialogResult = true; } @@ -154,8 +186,11 @@ namespace SourceGen.WpfGui { // Parts panel IsEnabled depends directly on formatSymbolButton.IsChecked. IsValid = true; IsSymbolAuto = false; + IsPartPanelEnabled = false; SymbolValueDecimal = string.Empty; if (FormatSymbol) { + IsPartPanelEnabled = mOpDef.IsExtendedImmediate; + if (!Asm65.Label.ValidateLabel(SymbolLabel)) { SymbolValueHex = SYMBOL_INVALID; IsValid = false; @@ -417,6 +452,12 @@ namespace SourceGen.WpfGui { } 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(); } @@ -600,6 +641,167 @@ namespace SourceGen.WpfGui { #endregion Basic Format + #region Numeric References + + 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 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; + + /// + /// Offset of LocalVariableTable we're going to modify. + /// + private int mLvTableOffset; + + /// + /// Local variable value. If there's already a definition, this will be pre-filled + /// with the current contents. Otherwise it will be null. + /// + private DefSymbol mEditedLocalVar; + + /// + /// Clone of original table, with local edits. + /// + private LocalVariableTable mEditedLvTable; + + + /// + /// Configures the UI on the bottom half of the dialog. + /// + private void NumericRefs_Loaded() { + if (mOpDef.IsDirectPageInstruction || mOpDef.IsStackRelInstruction) { + LocalVariableLookup lvLookup = + new LocalVariableLookup(mProject.LvTables, mProject, 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; + mEditedLocalVar = lvLookup.GetSymbol(mOffset, mOperandValue, + mOpDef.IsDirectPageInstruction ? + Symbol.Type.ExternalAddr : Symbol.Type.Constant); + if (mEditedLocalVar == null) { + ShowLvNoMatchFound = true; + CreateEditLocalVariableText = CREATE_LOCAL_VARIABLE; + } else { + 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); + } + } else { + ShowLvNotApplicable = true; + } + } + + 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, FormatDescriptor.SubType.None, + string.Empty, string.Empty, 1); + } + + 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; + } else { + Debug.WriteLine("No change to def symbol, ignoring edit"); + } + } + } + + #endregion Numeric References + #if false /// /// Configures the buttons in the "symbol shortcuts" group box. The entire box is diff --git a/SourceGen/WpfGui/EditLocalVariableTable.xaml.cs b/SourceGen/WpfGui/EditLocalVariableTable.xaml.cs index b3e23d9..fe2a5c3 100644 --- a/SourceGen/WpfGui/EditLocalVariableTable.xaml.cs +++ b/SourceGen/WpfGui/EditLocalVariableTable.xaml.cs @@ -211,7 +211,7 @@ namespace SourceGen.WpfGui { private void NewSymbolButton_Click(object sender, RoutedEventArgs e) { EditDefSymbol dlg = new EditDefSymbol(this, mFormatter, mWorkTable.GetSortedByLabel(), - null, mSymbolTable, true); + null, mSymbolTable, true, false); dlg.ShowDialog(); if (dlg.DialogResult == true) { Debug.WriteLine("ADD: " + dlg.NewSym); @@ -235,7 +235,7 @@ namespace SourceGen.WpfGui { private void DoEditSymbol(DefSymbol defSym) { EditDefSymbol dlg = new EditDefSymbol(this, mFormatter, mWorkTable.GetSortedByLabel(), - defSym, mSymbolTable, true); + defSym, mSymbolTable, true, false); dlg.ShowDialog(); if (dlg.DialogResult == true) { // Label might have changed, so remove old before adding new. diff --git a/SourceGen/WpfGui/EditProjectProperties.xaml.cs b/SourceGen/WpfGui/EditProjectProperties.xaml.cs index 048afaf..e453fce 100644 --- a/SourceGen/WpfGui/EditProjectProperties.xaml.cs +++ b/SourceGen/WpfGui/EditProjectProperties.xaml.cs @@ -440,7 +440,7 @@ namespace SourceGen.WpfGui { private void NewSymbolButton_Click(object sender, RoutedEventArgs e) { EditDefSymbol dlg = new EditDefSymbol(this, mFormatter, mWorkProps.ProjectSyms, null, - null, false); + null); dlg.ShowDialog(); if (dlg.DialogResult == true) { Debug.WriteLine("ADD: " + dlg.NewSym); @@ -476,7 +476,7 @@ namespace SourceGen.WpfGui { private void DoEditSymbol(DefSymbol defSym) { EditDefSymbol dlg = new EditDefSymbol(this, mFormatter, mWorkProps.ProjectSyms, - defSym, null, false); + defSym, null); dlg.ShowDialog(); if (dlg.DialogResult == true) { // Label might have changed, so remove old before adding new. diff --git a/SourceGen/XrefSet.cs b/SourceGen/XrefSet.cs index ec04504..4f837a6 100644 --- a/SourceGen/XrefSet.cs +++ b/SourceGen/XrefSet.cs @@ -51,7 +51,13 @@ namespace SourceGen { /// /// True if this reference is by name. /// - public bool IsSymbolic { get; private set; } + /// + /// The time this is of use is when determining the set of project/platform symbols + /// that are actually used. If we have FOO1=$101 and FOO2=$102, and we LDA FOO1 + /// and LDA FOO1+1, we only want to output an equate for FOO1 even though FOO2's + /// address was referenced. + /// + public bool IsByName { get; private set; } /// /// Type of reference. @@ -69,17 +75,17 @@ namespace SourceGen { /// public int Adjustment { get; private set; } - public Xref(int offset, bool isSymbolic, XrefType type, + public Xref(int offset, bool isByName, XrefType type, Asm65.OpDef.MemoryEffect accType, int adjustment) { Offset = offset; - IsSymbolic = isSymbolic; + IsByName = isByName; Type = type; AccType = accType; Adjustment = adjustment; } public override string ToString() { - return "Xref off=+" + Offset.ToString("x6") + " sym=" + IsSymbolic + + return "Xref off=+" + Offset.ToString("x6") + " sym=" + IsByName + " type=" + Type + " accType= " + AccType + " adj=" + Adjustment; } }