diff --git a/SourceGen/DisasmProject.cs b/SourceGen/DisasmProject.cs
index 7c8fbdf..320af6b 100644
--- a/SourceGen/DisasmProject.cs
+++ b/SourceGen/DisasmProject.cs
@@ -2353,6 +2353,43 @@ namespace SourceGen {
return -1;
}
+ ///
+ /// Finds a user label by name, searching only non-unique local address labels.
+ ///
+ /// Label to search for. Must not have the uniquifier tag (if
+ /// it had one, you wouldn't be here).
+ /// If multiple labels are found, we want the one that is
+ /// closest to this offset.
+ /// The symbol found, or null if no match.
+ public Symbol FindBestNonUniqueLabel(string label, int targetOffset) {
+ Symbol bestSym = null;
+ int bestDelta = int.MaxValue;
+
+ // Simple linear search. Right now we're only doing this in a few specific
+ // UI-driven situations (edit operand, goto label), so performance isn't crucial.
+ foreach (KeyValuePair kvp in UserLabels) {
+ Symbol sym = kvp.Value;
+ if (sym.SymbolType != Symbol.Type.NonUniqueLocalAddr) {
+ continue;
+ }
+ if (sym.LabelWithoutTag == label) {
+ // found a match; is it the best one?
+ int delta = Math.Abs(kvp.Key - targetOffset);
+ if (delta < bestDelta) {
+ //Debug.WriteLine("FindBest: " + sym.Label + "/" + delta + " better than " +
+ // (bestSym != null ? bestSym.Label : "-") + "/" + bestDelta);
+ bestSym = sym;
+ bestDelta = delta;
+ } else {
+ //Debug.WriteLine("FindBest: " + sym.Label + "/" + delta + " not better than " +
+ // (bestSym != null ? bestSym.Label : "-") + "/" + bestDelta);
+ }
+ }
+ }
+
+ return bestSym;
+ }
+
///
/// For debugging purposes, get some information about the currently loaded
/// extension scripts.
diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs
index e2ae299..e5285c2 100644
--- a/SourceGen/MainController.cs
+++ b/SourceGen/MainController.cs
@@ -2402,7 +2402,13 @@ namespace SourceGen {
}
public void Goto() {
- GotoBox dlg = new GotoBox(mMainWin, mProject, mOutputFormatter);
+ int index = mMainWin.CodeListView_GetFirstSelectedIndex();
+ if (index < 0) {
+ index = mMainWin.CodeListView_GetTopIndex(); // nothing selected
+ }
+ int offset = CodeLineList[index].FileOffset;
+
+ GotoBox dlg = new GotoBox(mMainWin, mProject, offset, mOutputFormatter);
if (dlg.ShowDialog() == true) {
GoToLocation(new NavStack.Location(dlg.TargetOffset, 0, false),
GoToMode.JumpToCodeData, true);
diff --git a/SourceGen/ProjectFile.cs b/SourceGen/ProjectFile.cs
index 2d339ce..5a81b47 100644
--- a/SourceGen/ProjectFile.cs
+++ b/SourceGen/ProjectFile.cs
@@ -279,7 +279,7 @@ namespace SourceGen {
public SerSymbol() { }
public SerSymbol(Symbol sym) {
- Label = sym.LabelForSerialization; // use bare label here
+ Label = sym.LabelWithoutTag; // use bare label here
Value = sym.Value;
Source = sym.SymbolSource.ToString();
Type = sym.SymbolType.ToString();
diff --git a/SourceGen/PseudoOp.cs b/SourceGen/PseudoOp.cs
index 21c486e..5067268 100644
--- a/SourceGen/PseudoOp.cs
+++ b/SourceGen/PseudoOp.cs
@@ -720,7 +720,7 @@ namespace SourceGen {
// if it's from the assembler.
if ((flags & FormatNumericOpFlags.OmitLabelPrefixSuffix) == 0) {
symLabel = Symbol.ConvertLabelForDisplay(symLabel, sym.LabelAnno,
- sym.IsNonUnique, formatter);
+ true, formatter);
} else {
// TODO(xyzzy): remapper will handle this
symLabel = Symbol.ConvertLabelForDisplay(symLabel, Symbol.LabelAnnotation.None,
@@ -863,7 +863,7 @@ namespace SourceGen {
}
if ((flags & FormatNumericOpFlags.OmitLabelPrefixSuffix) == 0) {
symLabel = Symbol.ConvertLabelForDisplay(symLabel, sym.LabelAnno,
- sym.IsNonUnique, formatter);
+ true, formatter);
} else {
// TODO(xyzzy): remapper will handle this
symLabel = Symbol.ConvertLabelForDisplay(symLabel, Symbol.LabelAnnotation.None,
@@ -964,7 +964,7 @@ namespace SourceGen {
}
if ((flags & FormatNumericOpFlags.OmitLabelPrefixSuffix) == 0) {
symLabel = Symbol.ConvertLabelForDisplay(symLabel, sym.LabelAnno,
- sym.IsNonUnique, formatter);
+ true, formatter);
} else {
// TODO(xyzzy): remapper will handle this
symLabel = Symbol.ConvertLabelForDisplay(symLabel, Symbol.LabelAnnotation.None,
diff --git a/SourceGen/SGTestData/Source/2023-non-unique-labels.S b/SourceGen/SGTestData/Source/2023-non-unique-labels.S
index 8525acc..d47fcca 100644
--- a/SourceGen/SGTestData/Source/2023-non-unique-labels.S
+++ b/SourceGen/SGTestData/Source/2023-non-unique-labels.S
@@ -30,7 +30,7 @@ global1 nop ;EDIT
bne :loop2
dex
bne :loop1
- jmp btarg
+ jmp btarg
global2 nop ;EDIT
@@ -53,16 +53,34 @@ global4 nop ;EDIT
gloop dex ;EDIT: local, name "loop"
global5 nop
bne gloop
-
+
nop
; Test symbolic references.
-
global6 nop
:spin1 jsr :spin2 ;EDIT: local, name "spin1", operand ref to ":spin2"
:spin2 jsr :spin1 ;EDIT: local, name "spin2", operand ref to ":spin1"
nop
:spin3 lda :spin3 ;EDIT: local, name "spin1", operand ref to ":spin1"
+ beq :spin3 ;EDIT: operand ref to ":spin1"
+
+ lda #<:spin1
+ ldx #<:spin2
+ lda #>:spin1
+ ldx #>:spin2
+ bne :skip
+
+ dw :spin1 ;EDIT: local, name "spin1"
+ dw :spin2 ;EDIT: local, name "spin1" (will be offset)
+ dw :spin3 ;EDIT: local, name "spin1"
+
+ dfb <:spin1 ;EDIT: local, name "spin1" (may need to do as
+ dfb <:spin2 ;EDIT: local, name "spin1" unique names and then
+ dfb >:spin1 ;EDIT: local, name "spin1" rename afterward)
+ dfb >:spin2 ;EDIT: local, name "spin1"
+
+:skip nop ;EDIT: local
+
; Semi-related: test labels that are nothing but underscores.
global_ nop
diff --git a/SourceGen/Symbol.cs b/SourceGen/Symbol.cs
index b36a786..ea057c6 100644
--- a/SourceGen/Symbol.cs
+++ b/SourceGen/Symbol.cs
@@ -119,9 +119,9 @@ namespace SourceGen {
///
- /// Label without the non-unique tag.
+ /// Label without the non-unique tag. Used for serialization.
///
- public string LabelForSerialization {
+ public string LabelWithoutTag {
get {
if (SymbolType != Type.NonUniqueLocalAddr) {
return Label;
@@ -217,7 +217,7 @@ namespace SourceGen {
Debug.Assert(uniqueTag >= 0 && uniqueTag < 0x01000000); // fit in 6 hex digits
Debug.Assert(label.IndexOf(UNIQUE_TAG_CHAR) < 0); // already extended?
- Value = value; // passed a bogus value earlier for assert
+ Value = value; // passed a bogus value to base ctor for assert
// Add tag to label to make it unique.
Label = label + UNIQUE_TAG_CHAR + uniqueTag.ToString("x6");
@@ -230,7 +230,7 @@ namespace SourceGen {
/// Formatter object.
/// Label suitable for display.
public string GenerateDisplayLabel(Asm65.Formatter formatter) {
- return ConvertLabelForDisplay(Label, LabelAnno, IsNonUnique, formatter);
+ return ConvertLabelForDisplay(Label, LabelAnno, true, formatter);
}
///
@@ -273,14 +273,13 @@ namespace SourceGen {
bool showNonUnique, Asm65.Formatter formatter) {
StringBuilder sb = new StringBuilder(label.Length + 2);
- if (showNonUnique) {
- sb.Append(formatter.NonUniqueLabelPrefix);
- }
-
if (label.Length > NON_UNIQUE_LEN &&
label[label.Length - NON_UNIQUE_LEN] == UNIQUE_TAG_CHAR) {
// showNonUnique may be false if generating assembly code (but by this
// point the unique tag should be remapped away)
+ if (showNonUnique) {
+ sb.Append(formatter.NonUniqueLabelPrefix);
+ }
sb.Append(label.Substring(0, label.Length - NON_UNIQUE_LEN));
} else {
sb.Append(label);
diff --git a/SourceGen/WpfGui/EditDataOperand.xaml.cs b/SourceGen/WpfGui/EditDataOperand.xaml.cs
index 1487f8f..30ab354 100644
--- a/SourceGen/WpfGui/EditDataOperand.xaml.cs
+++ b/SourceGen/WpfGui/EditDataOperand.xaml.cs
@@ -64,6 +64,11 @@ namespace SourceGen.WpfGui {
///
public FormatDescriptor mFirstFormatDescriptor;
+ ///
+ /// Project reference.
+ ///
+ private DisasmProject mProject;
+
///
/// Raw file data.
///
@@ -129,6 +134,7 @@ namespace SourceGen.WpfGui {
Owner = owner;
DataContext = this;
+ mProject = project;
mFileData = project.FileData;
mSymbolTable = project.SymbolTable;
mAddrMap = project.AddrMap;
@@ -345,11 +351,14 @@ namespace SourceGen.WpfGui {
bool isOk = true;
if (radioSimpleDataSymbolic.IsChecked == true) {
// Just check for correct format. References to non-existent labels are allowed.
- isOk = Asm65.Label.ValidateLabel(symbolEntryTextBox.Text);
+ Symbol.TrimAndValidateLabel(symbolEntryTextBox.Text,
+ mFormatter.NonUniqueLabelPrefix, out isOk, out bool unused1,
+ out bool unused2, out bool unused3, out Symbol.LabelAnnotation unused4);
- // Actually, let's discourage references to auto-labels.
+ // Actually, let's discourage references to auto-labels and variables.
if (isOk && mSymbolTable.TryGetValue(symbolEntryTextBox.Text, out Symbol sym)) {
- isOk = sym.SymbolSource != Symbol.Source.Auto;
+ isOk = sym.SymbolSource != Symbol.Source.Auto &&
+ sym.SymbolSource != Symbol.Source.Variable;
}
}
IsValid = isOk;
@@ -819,7 +828,9 @@ namespace SourceGen.WpfGui {
break;
}
Debug.Assert(dfd.HasSymbol);
- symbolEntryTextBox.Text = dfd.SymbolRef.Label;
+ symbolEntryTextBox.Text = Symbol.ConvertLabelForDisplay(
+ dfd.SymbolRef.Label, Symbol.LabelAnnotation.None,
+ true, mFormatter);
break;
default:
Debug.Assert(false);
@@ -965,7 +976,35 @@ namespace SourceGen.WpfGui {
part = WeakSymbolRef.Part.Low;
}
subType = FormatDescriptor.SubType.Symbol;
- symbolRef = new WeakSymbolRef(symbolEntryTextBox.Text, part);
+
+ string weakLabel = symbolEntryTextBox.Text;
+ // 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(weakLabel,
+ mFormatter.NonUniqueLabelPrefix, out bool isValid, out bool unused1,
+ out bool unused2, out bool hasNonUniquePrefix,
+ out Symbol.LabelAnnotation unused3);
+ if (isValid && hasNonUniquePrefix) {
+ // We want to find the match that's closest to the thing we're
+ // referencing, but that's awkward when there's multiple ranges and
+ // multiple ways of interpreting the data. So as a simple measure
+ // we just grab the lowest offset in the first range.
+ IEnumerator oiter = mSelection.RangeListIterator;
+ oiter.MoveNext();
+ TypedRangeSet.TypedRange rng = oiter.Current;
+ int matchOffset = rng.Low;
+
+ Symbol osym = mProject.FindBestNonUniqueLabel(trimLabel, matchOffset);
+ if (osym != null) {
+ weakLabel = osym.Label;
+ } else {
+ Debug.WriteLine("Attempt to create ref to nonexistant non-unique sym");
+ subType = FormatDescriptor.SubType.Hex;
+ }
+ }
+ symbolRef = new WeakSymbolRef(weakLabel, part);
} else {
Debug.Assert(false);
}
diff --git a/SourceGen/WpfGui/EditInstructionOperand.xaml.cs b/SourceGen/WpfGui/EditInstructionOperand.xaml.cs
index 6e9a914..f950e5b 100644
--- a/SourceGen/WpfGui/EditInstructionOperand.xaml.cs
+++ b/SourceGen/WpfGui/EditInstructionOperand.xaml.cs
@@ -225,13 +225,30 @@ namespace SourceGen.WpfGui {
/// Looks up the symbol in the symbol table. If not found there, it checks for a
/// match against the existing or edited project symbol.
///
- private bool LookupSymbol(string label, out Symbol sym) {
- if (mProject.SymbolTable.TryGetValue(label, out sym)) {
- return true;
- }
- if (mEditedProjectSymbol != null && label.Equals(mEditedProjectSymbol.Label)) {
- sym = mEditedProjectSymbol;
- return true;
+ 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;
}
@@ -253,10 +270,15 @@ namespace SourceGen.WpfGui {
if (FormatSymbol) {
IsPartPanelEnabled = mOpDef.IsExtendedImmediate;
- if (!Asm65.Label.ValidateLabel(SymbolLabel)) {
+ 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(SymbolLabel, out Symbol sym)) {
+ } 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
@@ -278,7 +300,8 @@ namespace SourceGen.WpfGui {
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.
+ // 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 {
@@ -365,7 +388,11 @@ namespace SourceGen.WpfGui {
sb.Append(mFormatter.FormatCharacterValue(operandValue, enc));
break;
case FormatDescriptor.SubType.Symbol:
- if (LookupSymbol(dfd.SymbolRef.Label, out Symbol sym)) {
+ string trimLabel = Symbol.TrimAndValidateLabel(SymbolLabel,
+ mFormatter.NonUniqueLabelPrefix, out bool isValid, out bool unused1,
+ out bool unused2, out bool hasNonUniquePrefix,
+ out Symbol.LabelAnnotation unused3);
+ 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"
@@ -500,8 +527,10 @@ namespace SourceGen.WpfGui {
// Set the radio button when the user starts typing.
if (mLoadDone) {
FormatSymbol = true;
+ // this calls UpdateControls; don't do it twice
+ } else {
+ UpdateControls();
}
- UpdateControls();
}
}
private string mSymbolLabel;
@@ -634,7 +663,8 @@ namespace SourceGen.WpfGui {
Debug.Assert(false);
break;
}
- SymbolLabel = dfd.SymbolRef.Label;
+ SymbolLabel = Symbol.ConvertLabelForDisplay(dfd.SymbolRef.Label,
+ Symbol.LabelAnnotation.None, true, mFormatter);
break;
case FormatDescriptor.SubType.None:
default:
@@ -685,8 +715,28 @@ namespace SourceGen.WpfGui {
Debug.Assert(false);
part = WeakSymbolRef.Part.Low;
}
+
+ string weakLabel = SymbolLabel;
+
+ // 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)) {
+ weakLabel = sym.Label;
+ } else {
+ Debug.WriteLine("Attempt to create ref to non-existant non-unique sym");
+ return null;
+ }
+ }
+
return FormatDescriptor.Create(instructionLength,
- new WeakSymbolRef(SymbolLabel, part), false);
+ new WeakSymbolRef(weakLabel, part), false);
}
FormatDescriptor.SubType subType;
@@ -863,7 +913,7 @@ namespace SourceGen.WpfGui {
} else {
NarLabelOffsetText = CURRENT_LABEL;
}
- NarTargetLabel = sym.Label;
+ NarTargetLabel = sym.GenerateDisplayLabel(mFormatter);
mEditedLabel = sym;
CreateEditLabelText = EDIT_LABEL;
} else {
@@ -943,7 +993,7 @@ namespace SourceGen.WpfGui {
} else {
ShowNarCurrentLabel = true;
CreateEditLabelText = EDIT_LABEL;
- NarTargetLabel = mEditedLabel.Label;
+ NarTargetLabel = mEditedLabel.GenerateDisplayLabel(mFormatter);
}
// Sort of nice to just hit return twice after entering a label, so move the focus
diff --git a/SourceGen/WpfGui/GotoBox.xaml.cs b/SourceGen/WpfGui/GotoBox.xaml.cs
index 8647180..3e95c53 100644
--- a/SourceGen/WpfGui/GotoBox.xaml.cs
+++ b/SourceGen/WpfGui/GotoBox.xaml.cs
@@ -37,6 +37,11 @@ namespace SourceGen.WpfGui {
///
private DisasmProject mProject;
+ ///
+ /// Initial offset, used for finding the best match among non-unique labels.
+ ///
+ private int mInitialOffset;
+
///
/// Reference to formatter. This determines how values are displayed.
///
@@ -88,12 +93,13 @@ namespace SourceGen.WpfGui {
}
- public GotoBox(Window owner, DisasmProject proj, Formatter formatter) {
+ public GotoBox(Window owner, DisasmProject proj, int initialOffset, Formatter formatter) {
InitializeComponent();
Owner = owner;
DataContext = this;
mProject = proj;
+ mInitialOffset = initialOffset;
mFormatter = formatter;
TargetOffset = -1;
}
@@ -138,7 +144,23 @@ namespace SourceGen.WpfGui {
// Try it as a label. If they give the label a hex name (e.g. "A001") they
// can prefix it with '$' to disambiguate the address.
- int labelOffset = mProject.FindLabelOffsetByName(input);
+ int labelOffset = -1;
+ string trimLabel = Symbol.TrimAndValidateLabel(input,
+ mFormatter.NonUniqueLabelPrefix, out bool isValid, out bool unused1,
+ out bool unused2, out bool hasNonUniquePrefix,
+ out Symbol.LabelAnnotation unused3);
+ if (isValid) {
+ // Could be a label. See if there's a match.
+ if (hasNonUniquePrefix) {
+ Symbol sym = mProject.FindBestNonUniqueLabel(trimLabel, mInitialOffset);
+ if (sym != null) {
+ labelOffset = mProject.FindLabelOffsetByName(sym.Label);
+ }
+ } else {
+ labelOffset = mProject.FindLabelOffsetByName(trimLabel);
+ }
+ }
+
if (labelOffset >= 0) {
TargetOffset = labelOffset;
} else if (Address.ParseAddress(input, 1 << 24, out int addr)) {
@@ -158,10 +180,10 @@ namespace SourceGen.WpfGui {
if (TargetOffset >= 0) {
offsetStr = mFormatter.FormatOffset24(TargetOffset);
int addr = mProject.GetAnattrib(TargetOffset).Address;
- addressStr = mFormatter.FormatAddress(addr, addr > 0xffff);
+ addressStr = "$" + mFormatter.FormatAddress(addr, addr > 0xffff);
Symbol sym = mProject.GetAnattrib(TargetOffset).Symbol;
if (sym != null) {
- labelStr = sym.Label;
+ labelStr = sym.GenerateDisplayLabel(mFormatter);
}
}