diff --git a/SourceGen/AppSettings.cs b/SourceGen/AppSettings.cs
index 2f34016..d444f81 100644
--- a/SourceGen/AppSettings.cs
+++ b/SourceGen/AppSettings.cs
@@ -77,6 +77,7 @@ namespace SourceGen {
// Symbol-list window options.
public const string SYMWIN_SHOW_USER = "symwin-show-user";
+ public const string SYMWIN_SHOW_NON_UNIQUE = "symwin-show-non-unique";
public const string SYMWIN_SHOW_AUTO = "symwin-show-auto";
public const string SYMWIN_SHOW_PROJECT = "symwin-show-project";
public const string SYMWIN_SHOW_PLATFORM = "symwin-show-platform";
diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs
index 4f6ad03..e2ae299 100644
--- a/SourceGen/MainController.cs
+++ b/SourceGen/MainController.cs
@@ -308,11 +308,12 @@ namespace SourceGen {
// actually used is expected to do something reasonable by default.
settings.SetBool(AppSettings.SYMWIN_SHOW_USER, true);
+ settings.SetBool(AppSettings.SYMWIN_SHOW_NON_UNIQUE, false);
settings.SetBool(AppSettings.SYMWIN_SHOW_PROJECT, true);
settings.SetBool(AppSettings.SYMWIN_SHOW_PLATFORM, false);
settings.SetBool(AppSettings.SYMWIN_SHOW_AUTO, false);
- settings.SetBool(AppSettings.SYMWIN_SHOW_CONST, true);
settings.SetBool(AppSettings.SYMWIN_SHOW_ADDR, true);
+ settings.SetBool(AppSettings.SYMWIN_SHOW_CONST, true);
settings.SetBool(AppSettings.SYMWIN_SORT_ASCENDING, true);
settings.SetInt(AppSettings.SYMWIN_SORT_COL, (int)Symbol.SymbolSortField.Name);
@@ -502,6 +503,8 @@ namespace SourceGen {
// Configure the Symbols window.
mMainWin.SymFilterUserLabels =
settings.GetBool(AppSettings.SYMWIN_SHOW_USER, false);
+ mMainWin.SymFilterNonUniqueLabels =
+ settings.GetBool(AppSettings.SYMWIN_SHOW_NON_UNIQUE, false);
mMainWin.SymFilterAutoLabels =
settings.GetBool(AppSettings.SYMWIN_SHOW_AUTO, false);
mMainWin.SymFilterProjectSymbols =
diff --git a/SourceGen/ProjectFile.cs b/SourceGen/ProjectFile.cs
index 3b79be3..2d339ce 100644
--- a/SourceGen/ProjectFile.cs
+++ b/SourceGen/ProjectFile.cs
@@ -279,7 +279,7 @@ namespace SourceGen {
public SerSymbol() { }
public SerSymbol(Symbol sym) {
- Label = sym.Label;
+ Label = sym.LabelForSerialization; // use bare label here
Value = sym.Value;
Source = sym.SymbolSource.ToString();
Type = sym.SymbolType.ToString();
@@ -308,7 +308,7 @@ namespace SourceGen {
public SerWeakSymbolRef() { }
public SerWeakSymbolRef(WeakSymbolRef weakSym) {
- Label = weakSym.Label;
+ Label = weakSym.Label; // retain non-unique tag in weak refs
Part = weakSym.ValuePart.ToString();
}
}
@@ -642,7 +642,7 @@ namespace SourceGen {
continue;
}
- if (!CreateSymbol(kvp.Value, report, out Symbol newSym)) {
+ if (!CreateSymbol(kvp.Value, intKey, report, out Symbol newSym)) {
continue;
}
if (newSym.SymbolSource != Symbol.Source.User) {
@@ -656,13 +656,15 @@ namespace SourceGen {
// Check for duplicate labels. We only want to compare label strings, so we
// can't test UserLabels.ContainsValue (which might be a linear search anyway).
// Dump the labels into a sorted list.
- if (labelDupCheck.ContainsKey(kvp.Value.Label)) {
+ //
+ // We want to use newSym.Label rather than kvp.Value.Label, because the latter
+ // won't have the non-unique local tag.
+ if (labelDupCheck.ContainsKey(newSym.Label)) {
report.Add(FileLoadItem.Type.Warning,
- string.Format(Res.Strings.ERR_DUPLICATE_LABEL_FMT,
- kvp.Value.Label, intKey));
+ string.Format(Res.Strings.ERR_DUPLICATE_LABEL_FMT, newSym.Label, intKey));
continue;
}
- labelDupCheck.Add(kvp.Value.Label, string.Empty);
+ labelDupCheck.Add(newSym.Label, string.Empty);
proj.UserLabels[intKey] = newSym;
}
@@ -720,11 +722,13 @@ namespace SourceGen {
/// is generated in the FileLoadReport.
///
/// Deserialized data.
+ /// If the symbol is a user label, this is the file offset.
+ /// If not, pass -1. Used for non-unique locals.
/// Error report object.
/// Created symbol.
/// True on success.
- private static bool CreateSymbol(SerSymbol ssym, FileLoadReport report,
- out Symbol outSym) {
+ private static bool CreateSymbol(SerSymbol ssym, int userLabelOffset,
+ FileLoadReport report, out Symbol outSym) {
outSym = null;
Symbol.Source source;
Symbol.Type type;
@@ -736,12 +740,27 @@ namespace SourceGen {
labelAnno = (Symbol.LabelAnnotation)Enum.Parse(
typeof(Symbol.LabelAnnotation), ssym.LabelAnno);
}
+ if (type == Symbol.Type.NonUniqueLocalAddr && source != Symbol.Source.User) {
+ throw new ArgumentException("unexpected source for non-unique local");
+ }
} catch (ArgumentException) {
report.Add(FileLoadItem.Type.Warning, Res.Strings.ERR_BAD_SYMBOL_ST +
": " + ssym.Source + "/" + ssym.Type);
return false;
}
- outSym = new Symbol(ssym.Label, ssym.Value, source, type, labelAnno);
+
+ if (!Asm65.Label.ValidateLabel(ssym.Label)) {
+ report.Add(FileLoadItem.Type.Warning, Res.Strings.ERR_BAD_SYMBOL_LABEL +
+ ": " + ssym.Label);
+ return false;
+ }
+
+ if (type == Symbol.Type.NonUniqueLocalAddr) {
+ outSym = new Symbol(ssym.Label, ssym.Value, labelAnno, userLabelOffset);
+ } else {
+ outSym = new Symbol(ssym.Label, ssym.Value, source, type, labelAnno);
+ }
+
return true;
}
@@ -757,7 +776,7 @@ namespace SourceGen {
FileLoadReport report, out DefSymbol outDefSym) {
outDefSym = null;
- if (!CreateSymbol(serDefSym, report, out Symbol sym)) {
+ if (!CreateSymbol(serDefSym, -1, report, out Symbol sym)) {
return false;
}
if (!CreateFormatDescriptor(serDefSym.DataDescriptor, contentVersion, report,
diff --git a/SourceGen/Res/Strings.xaml b/SourceGen/Res/Strings.xaml
index 911ddf1..cb28111 100644
--- a/SourceGen/Res/Strings.xaml
+++ b/SourceGen/Res/Strings.xaml
@@ -50,6 +50,7 @@ limitations under the License.
Bad local variable {0}
Invalid local variable table at +{0:x6}
Bad range
+ Malformed label in symbol
Unknown Source or Type in symbol
Bad symbol reference part
Type hint not recognized
diff --git a/SourceGen/Res/Strings.xaml.cs b/SourceGen/Res/Strings.xaml.cs
index eb3e5ea..c958c29 100644
--- a/SourceGen/Res/Strings.xaml.cs
+++ b/SourceGen/Res/Strings.xaml.cs
@@ -81,6 +81,8 @@ namespace SourceGen.Res {
(string)Application.Current.FindResource("str_ErrBadLvTableFmt");
public static string ERR_BAD_RANGE =
(string)Application.Current.FindResource("str_ErrBadRange");
+ public static string ERR_BAD_SYMBOL_LABEL =
+ (string)Application.Current.FindResource("str_ErrBadSymbolLabel");
public static string ERR_BAD_SYMBOL_ST =
(string)Application.Current.FindResource("str_ErrBadSymbolSt");
public static string ERR_BAD_SYMREF_PART =
diff --git a/SourceGen/SGTestData/Source/2023-non-unique-labels.S b/SourceGen/SGTestData/Source/2023-non-unique-labels.S
new file mode 100644
index 0000000..8525acc
--- /dev/null
+++ b/SourceGen/SGTestData/Source/2023-non-unique-labels.S
@@ -0,0 +1,88 @@
+; Copyright 2019 faddenSoft. All Rights Reserved.
+; See the LICENSE.txt file for distribution terms (Apache 2.0).
+;
+; Assembler: Merlin 32
+
+ org $1000
+
+; Test conflict with auto-label.
+start lda #$00 ;do not label
+:L1000 lda #$01 ;EDIT: set label to :L1000 (dup of auto)
+ ldx start
+ ldy :L1000
+
+; Test local/global having same name.
+ ldx #$02
+loop1 dex ;EDIT
+ bne loop1
+
+ ldx #$03
+:loop1 dex ;EDIT
+ bne :loop1
+
+; Test nested loops, and ref to a non-unique local on the other side
+; of a global.
+global1 nop ;EDIT
+ ldx #$04
+:loop1 ldy #$05 ;EDIT: local, name "loop"
+
+:loop2 dey ;EDIT: local, name "loop"
+ bne :loop2
+ dex
+ bne :loop1
+ jmp btarg
+
+global2 nop ;EDIT
+
+btarg nop ;EDIT: local, name "loop"
+
+; Test hand-over-hand locals branching forward.
+global3 nop ;EDIT
+ ldx #$06
+ ldy #$07
+ dex
+ beq :fwd1
+ dey
+ beq :fwd2
+:fwd1 nop ;EDIT
+:fwd2 nop ;EDIT
+
+; Test loop with a global in the middle.
+global4 nop ;EDIT
+ ldx #$08
+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"
+
+; Semi-related: test labels that are nothing but underscores.
+global_ nop
+ ldx #$40
+__ dex
+ bne __
+ beq ___
+___ ldx #$41
+:__ dex
+ bne :__
+
+ nop
+
+; Semi-related: test annotations (mostly to confirm that the suffix chars
+; aren't appearing in the assembly output)
+anno lda #$42 ;EDIT: add '?'
+anno1 lda anno
+
+
+ rts
+
+ dw anno1 ;EDIT: use table generator
+
diff --git a/SourceGen/Symbol.cs b/SourceGen/Symbol.cs
index af15c1a..b36a786 100644
--- a/SourceGen/Symbol.cs
+++ b/SourceGen/Symbol.cs
@@ -118,6 +118,19 @@ namespace SourceGen {
public string SourceTypeString { get; private set; }
+ ///
+ /// Label without the non-unique tag.
+ ///
+ public string LabelForSerialization {
+ get {
+ if (SymbolType != Type.NonUniqueLocalAddr) {
+ return Label;
+ } else {
+ return Label.Substring(0, Label.Length - NON_UNIQUE_LEN);
+ }
+ }
+ }
+
///
/// True if the symbol's type is an internal label (auto or user). Will be false
/// for external addresses (including variables) and constants.
@@ -234,26 +247,41 @@ namespace SourceGen {
}
///
- /// Converts a label to displayable form by stripping the uniquification tag (if any)
- /// and appending the optional annotation. This is needed for display of WeakSymbolRefs.
+ /// Converts a label to displayable form by stripping the uniquification tag (if any),
+ /// inserting the non-unique label prefix if appropriate, and appending the optional
+ /// annotation character.
///
+ ///
+ /// There's generally two ways to display a label:
+ /// (1) When displaying labels on-screen, we get a label with the uniquification tag,
+ /// and we want to show the non-unique label prefix ('@' or ':') and annotation.
+ /// (2) When generating assembly source, we get a remapped label with no uniquification
+ /// tag, and we don't want to show the prefix or annotation.
+ /// For case #2, there's no reason to call here. (We're currently doing so because
+ /// remapping isn't happening yet, but that should change soon. When that happens, we
+ /// should be able to eliminate the showNonUnique arg.)
+ ///
/// Base label string. Has the uniquification tag, but no
/// annotation char or non-unique prefix.
/// Annotation; may be None.
+ /// Set true if the returned label should show the
+ /// non-unique label prefix.
+ /// Format object that holds the non-unique label prefix
+ /// string.
/// Formatted label.
public static string ConvertLabelForDisplay(string label, LabelAnnotation anno,
- bool isNonUnique, Asm65.Formatter formatter) {
+ bool showNonUnique, Asm65.Formatter formatter) {
StringBuilder sb = new StringBuilder(label.Length + 2);
- if (isNonUnique) {
+ if (showNonUnique) {
sb.Append(formatter.NonUniqueLabelPrefix);
}
- // NOTE: could make this a length check + label[Length - NON_UNIQUE_LEN]
- int nbrk = label.IndexOf(UNIQUE_TAG_CHAR);
- if (nbrk >= 0) {
- Debug.Assert(nbrk == label.Length - NON_UNIQUE_LEN);
- sb.Append(label.Substring(0, nbrk));
+ 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)
+ sb.Append(label.Substring(0, label.Length - NON_UNIQUE_LEN));
} else {
sb.Append(label);
}
@@ -273,7 +301,8 @@ namespace SourceGen {
///
/// Label to examine.
/// For address symbols, the prefix string for
- /// non-unique labels. May be null if not validating a user label.
+ /// non-unique labels (e.g. '@' or ':'). May be null if not validating a user
+ /// label.
/// True if the entire label is valid.
/// True if the label has a valid length.
/// True if the first character is valid.
diff --git a/SourceGen/WpfGui/MainWindow.xaml b/SourceGen/WpfGui/MainWindow.xaml
index 37eae72..50c8047 100644
--- a/SourceGen/WpfGui/MainWindow.xaml
+++ b/SourceGen/WpfGui/MainWindow.xaml
@@ -702,6 +702,8 @@ limitations under the License.
+
/// Symbols list DataGrid item.
@@ -1659,6 +1670,7 @@ namespace SourceGen.WpfGui {
return;
}
if ((SymFilterUserLabels != true && sli.Sym.SymbolSource == Symbol.Source.User) ||
+ (SymFilterNonUniqueLabels != true && sli.Sym.IsNonUnique) ||
(SymFilterProjectSymbols != true && sli.Sym.SymbolSource == Symbol.Source.Project) ||
(SymFilterPlatformSymbols != true && sli.Sym.SymbolSource == Symbol.Source.Platform) ||
(SymFilterAutoLabels != true && sli.Sym.SymbolSource == Symbol.Source.Auto) ||