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
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.
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.
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.
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:
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".
-