From 5dd7576529d77e10aa5aec529b916400d70dbbab Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Tue, 12 Nov 2019 17:24:41 -0800 Subject: [PATCH] Label rework, part 2 Continue development of non-unique labels. The actual labels are still unique, because we append a uniquifier tag, which gets added and removed behind the scenes. We're currently using the six-digit hex file offset because this is only used for internal address symbols. The label editor and most of the formatters have been updated. We can't yet assemble code that includes non-unique labels, but older stuff hasn't been broken. This removes the "disable label localization" property, since that's fundamentally incompatible with what we're doing, and adds a non- unique label prefix setting so you can put '@' or ':' in front of your should-be-local labels. Also, fixed a field name typo. --- Asm65/Formatter.cs | 29 ++- SourceGen/AppSettings.cs | 1 + SourceGen/AsmGen/AsmAcme.cs | 9 +- SourceGen/AsmGen/AsmCc65.cs | 7 +- SourceGen/AsmGen/AsmMerlin32.cs | 9 +- SourceGen/AsmGen/AsmTass64.cs | 7 +- SourceGen/AsmGen/GenCommon.cs | 8 +- SourceGen/DisasmProject.cs | 4 + SourceGen/DisplayList.cs | 3 + SourceGen/LineListGen.cs | 11 +- SourceGen/MainController.cs | 14 +- SourceGen/PseudoOp.cs | 45 ++-- SourceGen/Sandbox/ScriptManager.cs | 3 + SourceGen/Symbol.cs | 145 ++++++++---- SourceGen/WpfGui/EditAppSettings.xaml | 11 +- SourceGen/WpfGui/EditAppSettings.xaml.cs | 41 +++- SourceGen/WpfGui/EditDefSymbol.xaml.cs | 14 +- .../WpfGui/EditInstructionOperand.xaml.cs | 2 +- SourceGen/WpfGui/EditLabel.xaml | 26 ++- SourceGen/WpfGui/EditLabel.xaml.cs | 209 ++++++++++++------ .../WpfGui/EditLocalVariableTable.xaml.cs | 2 +- .../WpfGui/EditProjectProperties.xaml.cs | 2 +- 22 files changed, 427 insertions(+), 175 deletions(-) diff --git a/Asm65/Formatter.cs b/Asm65/Formatter.cs index ac46ec7..3453eff 100644 --- a/Asm65/Formatter.cs +++ b/Asm65/Formatter.cs @@ -37,9 +37,9 @@ namespace Asm65 { /// because it guarantees that a given Formatter object will produce the same number of /// lines of output. /// - /// NOTE: if the CpuDef changes, the cached values in the Formatter will become invalid. - /// Discard the Formatter and create a new one. (This could be fixed by keying off of - /// the OpDef instead of OpDef.Opcode, but that's less convenient.) + /// NOTE: if the CpuDef changes, the cached values in the Formatter will become invalid + /// (e.g. mOpcodeStrings). Discard the Formatter and create a new one. (This could be + /// fixed by keying off of the OpDef instead of OpDef.Opcode, but that's less convenient.) /// public class Formatter { /// @@ -54,12 +54,13 @@ namespace Asm65 { public bool mUpperOperandA; // display acc operand in upper case? public bool mUpperOperandS; // display stack operand in upper case? public bool mUpperOperandXY; // display index register operand in upper case? - public bool mBankSelectBackQuote; // use '`' rather than '^' for bank select? + public bool mAddSpaceLongComment; // insert space after delimiter for long comments? // functional changes to assembly output public bool mSuppressHexNotation; // omit '$' before hex digits public bool mSuppressImpliedAcc; // emit just "LSR" rather than "LSR A"? + public bool mBankSelectBackQuote; // use '`' rather than '^' for bank selector? public string mForceDirectOperandPrefix; // these may be null or empty public string mForceAbsOpcodeSuffix; @@ -68,7 +69,8 @@ namespace Asm65 { public string mForceLongOpcodeSuffix; public string mForceLongOperandPrefix; - public string mLocalVariableLablePrefix; // Merlin 32 puts ']' before var names + public string mLocalVariableLabelPrefix; // e.g. Merlin 32 puts ']' before var names + public string mNonUniqueLabelPrefix; // e.g. ':' or '@' before local label public string mEndOfLineCommentDelimiter; // usually ';' public string mFullLineCommentDelimiterBase; // usually ';' or '*', WITHOUT extra space @@ -291,7 +293,7 @@ namespace Asm65 { /// public FormatConfig Config { get { return mFormatConfig; } } - // Bits and pieces. + // Cached bits and pieces. char mHexFmtChar; string mHexPrefix; string mAccChar; @@ -365,6 +367,13 @@ namespace Asm65 { get { return mFormatConfig.mBoxLineCommentDelimiter; } } + /// + /// Prefix for non-unique address labels. + /// + public string NonUniqueLabelPrefix { + get { return mFormatConfig.mNonUniqueLabelPrefix; } + } + /// /// When formatting a symbol with an offset, if this flag is set, generate code that /// assumes the assembler applies the adjustment, then shifts the result. If not, @@ -391,6 +400,10 @@ namespace Asm65 { mFormatConfig.mBoxLineCommentDelimiter = string.Empty; } + if (string.IsNullOrEmpty(mFormatConfig.mNonUniqueLabelPrefix)) { + mFormatConfig.mNonUniqueLabelPrefix = "@"; + } + if (mFormatConfig.mAddSpaceLongComment) { mFullLineCommentDelimiterPlus = mFormatConfig.mFullLineCommentDelimiterBase + " "; } else { @@ -617,8 +630,8 @@ namespace Asm65 { /// specified. /// public string FormatVariableLabel(string label) { - if (!string.IsNullOrEmpty(mFormatConfig.mLocalVariableLablePrefix)) { - return mFormatConfig.mLocalVariableLablePrefix + label; + if (!string.IsNullOrEmpty(mFormatConfig.mLocalVariableLabelPrefix)) { + return mFormatConfig.mLocalVariableLabelPrefix + label; } else { return label; } diff --git a/SourceGen/AppSettings.cs b/SourceGen/AppSettings.cs index 1ac0013..2f34016 100644 --- a/SourceGen/AppSettings.cs +++ b/SourceGen/AppSettings.cs @@ -66,6 +66,7 @@ namespace SourceGen { public const string FMT_PSEUDO_OP_NAMES = "fmt-pseudo-op-names"; public const string FMT_CHAR_DELIM = "fmt-char-delim"; public const string FMT_STRING_DELIM = "fmt-string-delim"; + public const string FMT_NON_UNIQUE_LABEL_PREFIX = "fmt-non-unique-label-prefix"; public const string FMT_LOCAL_VARIABLE_PREFIX = "fmt-local-variable-prefix"; public const string CLIP_LINE_FORMAT = "clip-line-format"; diff --git a/SourceGen/AsmGen/AsmAcme.cs b/SourceGen/AsmGen/AsmAcme.cs index 3f860a5..eaf1a80 100644 --- a/SourceGen/AsmGen/AsmAcme.cs +++ b/SourceGen/AsmGen/AsmAcme.cs @@ -190,10 +190,11 @@ namespace SourceGen.AsmGen { config.mForceDirectOperandPrefix = string.Empty; config.mForceAbsOperandPrefix = string.Empty; config.mForceLongOperandPrefix = string.Empty; - config.mLocalVariableLablePrefix = "."; + config.mLocalVariableLabelPrefix = "."; config.mEndOfLineCommentDelimiter = ";"; config.mFullLineCommentDelimiterBase = ";"; config.mBoxLineCommentDelimiter = ";"; + config.mNonUniqueLabelPrefix = "@"; config.mExpressionMode = Formatter.FormatConfig.ExpressionMode.Common; Formatter.DelimiterSet charSet = new Formatter.DelimiterSet(); @@ -387,7 +388,7 @@ namespace SourceGen.AsmGen { operand = RawData.GetWord(data, offset, length, false); operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable, mLocalizer.LabelMap, dfd, operand, length, - PseudoOp.FormatNumericOpFlags.StripAnnotation); + PseudoOp.FormatNumericOpFlags.StripLabelPrefixSuffix); break; case FormatDescriptor.Type.NumericBE: opcodeStr = sDataOpNames.GetDefineBigData(length); @@ -398,7 +399,7 @@ namespace SourceGen.AsmGen { operand = RawData.GetWord(data, offset, length, true); operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable, mLocalizer.LabelMap, dfd, operand, length, - PseudoOp.FormatNumericOpFlags.StripAnnotation); + PseudoOp.FormatNumericOpFlags.StripLabelPrefixSuffix); } break; case FormatDescriptor.Type.Fill: @@ -515,7 +516,7 @@ namespace SourceGen.AsmGen { string valueStr = PseudoOp.FormatNumericOperand(SourceFormatter, Project.SymbolTable, null, defSym.DataDescriptor, defSym.Value, 1, - PseudoOp.FormatNumericOpFlags.StripAnnotation); + PseudoOp.FormatNumericOpFlags.StripLabelPrefixSuffix); OutputEquDirective(SourceFormatter.FormatVariableLabel(defSym.Label), valueStr, defSym.Comment); } diff --git a/SourceGen/AsmGen/AsmCc65.cs b/SourceGen/AsmGen/AsmCc65.cs index 35cdfce..43b6d8a 100644 --- a/SourceGen/AsmGen/AsmCc65.cs +++ b/SourceGen/AsmGen/AsmCc65.cs @@ -189,6 +189,7 @@ namespace SourceGen.AsmGen { config.mEndOfLineCommentDelimiter = ";"; config.mFullLineCommentDelimiterBase = ";"; config.mBoxLineCommentDelimiter = ";"; + config.mNonUniqueLabelPrefix = "@"; config.mExpressionMode = Formatter.FormatConfig.ExpressionMode.Cc65; Formatter.DelimiterSet charSet = new Formatter.DelimiterSet(); @@ -421,7 +422,7 @@ namespace SourceGen.AsmGen { operand = RawData.GetWord(data, offset, length, false); operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable, mLocalizer.LabelMap, dfd, operand, length, - PseudoOp.FormatNumericOpFlags.StripAnnotation); + PseudoOp.FormatNumericOpFlags.StripLabelPrefixSuffix); break; case FormatDescriptor.Type.NumericBE: opcodeStr = sDataOpNames.GetDefineBigData(length); @@ -432,7 +433,7 @@ namespace SourceGen.AsmGen { operand = RawData.GetWord(data, offset, length, true); operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable, mLocalizer.LabelMap, dfd, operand, length, - PseudoOp.FormatNumericOpFlags.StripAnnotation); + PseudoOp.FormatNumericOpFlags.StripLabelPrefixSuffix); } break; case FormatDescriptor.Type.Fill: @@ -547,7 +548,7 @@ namespace SourceGen.AsmGen { // Use an operand length of 1 so values are shown as concisely as possible. string valueStr = PseudoOp.FormatNumericOperand(SourceFormatter, Project.SymbolTable, null, defSym.DataDescriptor, defSym.Value, 1, - PseudoOp.FormatNumericOpFlags.StripAnnotation); + PseudoOp.FormatNumericOpFlags.StripLabelPrefixSuffix); OutputLine(SourceFormatter.FormatVariableLabel(defSym.Label), SourceFormatter.FormatPseudoOp(sDataOpNames.VarDirective), valueStr, SourceFormatter.FormatEolComment(defSym.Comment)); diff --git a/SourceGen/AsmGen/AsmMerlin32.cs b/SourceGen/AsmGen/AsmMerlin32.cs index 6faae0e..f6e3223 100644 --- a/SourceGen/AsmGen/AsmMerlin32.cs +++ b/SourceGen/AsmGen/AsmMerlin32.cs @@ -160,10 +160,11 @@ namespace SourceGen.AsmGen { config.mForceDirectOperandPrefix = string.Empty; config.mForceAbsOperandPrefix = string.Empty; config.mForceLongOperandPrefix = string.Empty; - config.mLocalVariableLablePrefix = "]"; + config.mLocalVariableLabelPrefix = "]"; config.mEndOfLineCommentDelimiter = ";"; config.mFullLineCommentDelimiterBase = ";"; config.mBoxLineCommentDelimiter = string.Empty; + config.mNonUniqueLabelPrefix = ":"; config.mExpressionMode = Formatter.FormatConfig.ExpressionMode.Merlin; Formatter.DelimiterSet charSet = new Formatter.DelimiterSet(); @@ -248,7 +249,7 @@ namespace SourceGen.AsmGen { operand = RawData.GetWord(data, offset, length, false); operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable, mLocalizer.LabelMap, dfd, operand, length, - PseudoOp.FormatNumericOpFlags.StripAnnotation); + PseudoOp.FormatNumericOpFlags.StripLabelPrefixSuffix); break; case FormatDescriptor.Type.NumericBE: opcodeStr = sDataOpNames.GetDefineBigData(length); @@ -259,7 +260,7 @@ namespace SourceGen.AsmGen { operand = RawData.GetWord(data, offset, length, true); operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable, mLocalizer.LabelMap, dfd, operand, length, - PseudoOp.FormatNumericOpFlags.StripAnnotation); + PseudoOp.FormatNumericOpFlags.StripLabelPrefixSuffix); } break; case FormatDescriptor.Type.Fill: @@ -439,7 +440,7 @@ namespace SourceGen.AsmGen { foreach (DefSymbol defSym in newDefs) { string valueStr = PseudoOp.FormatNumericOperand(SourceFormatter, Project.SymbolTable, null, defSym.DataDescriptor, defSym.Value, 1, - PseudoOp.FormatNumericOpFlags.StripAnnotation); + PseudoOp.FormatNumericOpFlags.StripLabelPrefixSuffix); OutputLine(SourceFormatter.FormatVariableLabel(defSym.Label), SourceFormatter.FormatPseudoOp(sDataOpNames.VarDirective), valueStr, SourceFormatter.FormatEolComment(defSym.Comment)); diff --git a/SourceGen/AsmGen/AsmTass64.cs b/SourceGen/AsmGen/AsmTass64.cs index 78bf284..767c223 100644 --- a/SourceGen/AsmGen/AsmTass64.cs +++ b/SourceGen/AsmGen/AsmTass64.cs @@ -197,6 +197,7 @@ namespace SourceGen.AsmGen { config.mEndOfLineCommentDelimiter = ";"; config.mFullLineCommentDelimiterBase = ";"; config.mBoxLineCommentDelimiter = ";"; + config.mNonUniqueLabelPrefix = ""; // should be '_', but that's a valid label char config.mExpressionMode = Formatter.FormatConfig.ExpressionMode.Common; } @@ -453,7 +454,7 @@ namespace SourceGen.AsmGen { UpdateCharacterEncoding(dfd); operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable, mLocalizer.LabelMap, dfd, operand, length, - PseudoOp.FormatNumericOpFlags.StripAnnotation); + PseudoOp.FormatNumericOpFlags.StripLabelPrefixSuffix); break; case FormatDescriptor.Type.NumericBE: opcodeStr = sDataOpNames.GetDefineBigData(length); @@ -465,7 +466,7 @@ namespace SourceGen.AsmGen { operand = RawData.GetWord(data, offset, length, true); operandStr = PseudoOp.FormatNumericOperand(formatter, Project.SymbolTable, mLocalizer.LabelMap, dfd, operand, length, - PseudoOp.FormatNumericOpFlags.StripAnnotation); + PseudoOp.FormatNumericOpFlags.StripLabelPrefixSuffix); } break; case FormatDescriptor.Type.Fill: @@ -582,7 +583,7 @@ namespace SourceGen.AsmGen { foreach (DefSymbol defSym in newDefs) { string valueStr = PseudoOp.FormatNumericOperand(SourceFormatter, Project.SymbolTable, null, defSym.DataDescriptor, defSym.Value, 1, - PseudoOp.FormatNumericOpFlags.StripAnnotation); + PseudoOp.FormatNumericOpFlags.StripLabelPrefixSuffix); OutputLine(SourceFormatter.FormatVariableLabel(defSym.Label), SourceFormatter.FormatPseudoOp(sDataOpNames.VarDirective), valueStr, SourceFormatter.FormatEolComment(defSym.Comment)); diff --git a/SourceGen/AsmGen/GenCommon.cs b/SourceGen/AsmGen/GenCommon.cs index 48c3db3..ef6f522 100644 --- a/SourceGen/AsmGen/GenCommon.cs +++ b/SourceGen/AsmGen/GenCommon.cs @@ -157,7 +157,7 @@ namespace SourceGen.AsmGen { // Use an operand length of 1 so values are shown as concisely as possible. string valueStr = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable, gen.Localizer.LabelMap, defSym.DataDescriptor, defSym.Value, 1, - PseudoOp.FormatNumericOpFlags.StripAnnotation); + PseudoOp.FormatNumericOpFlags.StripLabelPrefixSuffix); gen.OutputEquDirective(defSym.Label, valueStr, defSym.Comment); prevConst = defSym.IsConstant; @@ -204,7 +204,7 @@ namespace SourceGen.AsmGen { string formattedOperand = null; int operandLen = instrLen - 1; - PseudoOp.FormatNumericOpFlags opFlags = PseudoOp.FormatNumericOpFlags.StripAnnotation; + PseudoOp.FormatNumericOpFlags opFlags = PseudoOp.FormatNumericOpFlags.StripLabelPrefixSuffix; bool isPcRelBankWrap = false; // Tweak branch instructions. We want to show the absolute address rather @@ -245,10 +245,10 @@ namespace SourceGen.AsmGen { // Special handling for the double-operand block move. string opstr1 = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable, gen.Localizer.LabelMap, dfd, operand >> 8, 1, - PseudoOp.FormatNumericOpFlags.StripAnnotation); + PseudoOp.FormatNumericOpFlags.StripLabelPrefixSuffix); string opstr2 = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable, gen.Localizer.LabelMap, dfd, operand & 0xff, 1, - PseudoOp.FormatNumericOpFlags.StripAnnotation); + PseudoOp.FormatNumericOpFlags.StripLabelPrefixSuffix); if (gen.Quirks.BlockMoveArgsReversed) { string tmp = opstr1; opstr1 = opstr2; diff --git a/SourceGen/DisasmProject.cs b/SourceGen/DisasmProject.cs index 08f73cc..7c8fbdf 100644 --- a/SourceGen/DisasmProject.cs +++ b/SourceGen/DisasmProject.cs @@ -1098,6 +1098,10 @@ namespace SourceGen { /// Merges symbols from UserLabels into SymbolTable. Existing entries with matching /// labels will be replaced. /// + /// + /// It might make sense to exclude non-unique labels, but that's probably better done + /// with a UI filter option. + /// private void UpdateAndMergeUserLabels() { // We store symbols as label+value, but for a user label the actual value is // the address of the offset the label is associated with, which can change if diff --git a/SourceGen/DisplayList.cs b/SourceGen/DisplayList.cs index f8941af..14bb69f 100644 --- a/SourceGen/DisplayList.cs +++ b/SourceGen/DisplayList.cs @@ -322,6 +322,9 @@ namespace SourceGen { OnPropertyChanged(CountString); OnPropertyChanged(IndexerName); + // TODO(performance): this causes the ListView to format the entire listing, despite + // being virtual. So we're regenerating the entire list after something trivial, + // like renaming a label. Need to figure this out. OnCollectionReset(); } diff --git a/SourceGen/LineListGen.cs b/SourceGen/LineListGen.cs index 666b007..4b604fc 100644 --- a/SourceGen/LineListGen.cs +++ b/SourceGen/LineListGen.cs @@ -871,7 +871,8 @@ namespace SourceGen { PseudoOp.FormatNumericOpFlags.None); valueStr = PseudoOp.AnnotateEquDirective(formatter, valueStr, defSym); string comment = formatter.FormatEolComment(defSym.Comment); - FormattedParts parts = FormattedParts.CreateEquDirective(defSym.AnnotatedLabel, + FormattedParts parts = FormattedParts.CreateEquDirective( + defSym.GenerateDisplayLabel(formatter), formatter.FormatPseudoOp(opNames.EquDirective), valueStr, comment); line.Parts = parts; @@ -1183,7 +1184,7 @@ namespace SourceGen { string labelStr = string.Empty; if (attr.Symbol != null) { - labelStr = attr.Symbol.AnnotatedLabel; + labelStr = attr.Symbol.GenerateDisplayLabel(mFormatter); } OpDef op = mProject.CpuDef.GetOpDef(data[offset]); @@ -1316,7 +1317,7 @@ namespace SourceGen { addrStr = mFormatter.FormatAddress(attr.Address, !mProject.CpuDef.HasAddr16); if (attr.Symbol != null) { - labelStr = attr.Symbol.AnnotatedLabel; + labelStr = attr.Symbol.GenerateDisplayLabel(mFormatter); } bytesStr = mFormatter.FormatBytes(data, offset, attr.Length); @@ -1394,7 +1395,7 @@ namespace SourceGen { addrStr = PseudoOp.AnnotateEquDirective(mFormatter, addrStr, defSym); string comment = mFormatter.FormatEolComment(defSym.Comment); return FormattedParts.CreateEquDirective( - mFormatter.FormatVariableLabel(defSym.AnnotatedLabel), + mFormatter.FormatVariableLabel(defSym.GenerateDisplayLabel(mFormatter)), mFormatter.FormatPseudoOp(mPseudoOpNames.VarDirective), addrStr, comment); } @@ -1436,7 +1437,7 @@ namespace SourceGen { addrStr = mFormatter.FormatAddress(attr.Address, !mProject.CpuDef.HasAddr16); if (attr.Symbol != null) { - labelStr = attr.Symbol.AnnotatedLabel; + labelStr = attr.Symbol.GenerateDisplayLabel(mFormatter); } else { labelStr = string.Empty; } diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs index bb577dd..4f6ad03 100644 --- a/SourceGen/MainController.cs +++ b/SourceGen/MainController.cs @@ -466,7 +466,9 @@ namespace SourceGen { mFormatterConfig.mFullLineCommentDelimiterBase = ";"; mFormatterConfig.mBoxLineCommentDelimiter = string.Empty; - mFormatterConfig.mLocalVariableLablePrefix = + mFormatterConfig.mNonUniqueLabelPrefix = + settings.GetString(AppSettings.FMT_NON_UNIQUE_LABEL_PREFIX, string.Empty); + mFormatterConfig.mLocalVariableLabelPrefix = settings.GetString(AppSettings.FMT_LOCAL_VARIABLE_PREFIX, string.Empty); string chrDelCereal = settings.GetString(AppSettings.FMT_CHAR_DELIM, null); @@ -1709,8 +1711,8 @@ namespace SourceGen { int offset = CodeLineList[selIndex].FileOffset; Anattrib attr = mProject.GetAnattrib(offset); - EditLabel dlg = new EditLabel(mMainWin, attr.Symbol, attr.Address, - mProject.SymbolTable); + EditLabel dlg = new EditLabel(mMainWin, attr.Symbol, attr.Address, offset, + mProject.SymbolTable, mOutputFormatter); if (dlg.ShowDialog() != true) { return; } @@ -3456,6 +3458,10 @@ namespace SourceGen { #region Symbols panel + /// + /// Populates the ItemsSource for the Symbols window. Each entry in the project + /// symbol table is added. + /// private void PopulateSymbolsList() { mMainWin.SymbolsList.Clear(); foreach (Symbol sym in mProject.SymbolTable) { @@ -3475,7 +3481,7 @@ namespace SourceGen { } MainWindow.SymbolsListItem sli = new MainWindow.SymbolsListItem(sym, - sourceTypeStr, valueStr, sym.AnnotatedLabel); + sourceTypeStr, valueStr, sym.GenerateDisplayLabel(mOutputFormatter)); mMainWin.SymbolsList.Add(sli); } } diff --git a/SourceGen/PseudoOp.cs b/SourceGen/PseudoOp.cs index eb4564e..4d8a0c7 100644 --- a/SourceGen/PseudoOp.cs +++ b/SourceGen/PseudoOp.cs @@ -549,10 +549,10 @@ namespace SourceGen { /// [Flags] public enum FormatNumericOpFlags { - None = 0, - IsPcRel = 1, // opcode is PC relative, e.g. branch or PER - HasHashPrefix = 1 << 1, // operand has a leading '#', avoiding ambiguity sometimes - StripAnnotation = 1 << 2, // don't show annotation character + None = 0, + IsPcRel = 1, // opcode is PC relative, e.g. branch or PER + HasHashPrefix = 1 << 1, // operand has a leading '#', reducing ambiguity + StripLabelPrefixSuffix = 1 << 2, // don't show annotation char or non-unique prefix } /// @@ -641,7 +641,7 @@ namespace SourceGen { Debug.Assert(operandLen == 1); // only doing 8-bit stuff DefSymbol defSym = lvLookup.GetSymbol(offset, dfd.SymbolRef); if (defSym != null) { - // For local variables we're doing trivial add/subtract and don't + // For local variables we're doing a trivial add and don't // wrap, so the "common" format works for everybody. StringBuilder sb = new StringBuilder(); FormatNumericSymbolCommon(formatter, defSym, null, @@ -699,6 +699,8 @@ namespace SourceGen { int adjustment, symbolValue; + // Start by remapping the label, if necessary. The remapped label may have a + // local-variable prefix character. string symLabel = sym.Label; if (labelMap != null && labelMap.TryGetValue(symLabel, out string newLabel)) { symLabel = newLabel; @@ -706,9 +708,16 @@ namespace SourceGen { if (sym.IsVariable) { symLabel = formatter.FormatVariableLabel(symLabel); } - // TODO(xyzzy): non-unique prefix - if ((flags & FormatNumericOpFlags.StripAnnotation) == 0) { - symLabel = Symbol.AppendAnnotation(symLabel, sym.LabelAnno); + + // Now put the prefix/suffix back on if desired. We don't want to mess with it + // if it's from the assembler. + if ((flags & FormatNumericOpFlags.StripLabelPrefixSuffix) == 0) { + symLabel = Symbol.ConvertLabelForDisplay(symLabel, sym.LabelAnno, + sym.IsNonUnique, formatter); + } else { + // TODO(xyzzy): remapper will handle this + symLabel = Symbol.ConvertLabelForDisplay(symLabel, Symbol.LabelAnnotation.None, + false, formatter); } if (operandLen == 1) { @@ -845,9 +854,13 @@ namespace SourceGen { if (labelMap != null && labelMap.TryGetValue(symLabel, out string newLabel)) { symLabel = newLabel; } - // TODO(xyzzy): non-unique prefix - if ((flags & FormatNumericOpFlags.StripAnnotation) == 0) { - symLabel = Symbol.AppendAnnotation(symLabel, sym.LabelAnno); + if ((flags & FormatNumericOpFlags.StripLabelPrefixSuffix) == 0) { + symLabel = Symbol.ConvertLabelForDisplay(symLabel, sym.LabelAnno, + sym.IsNonUnique, formatter); + } else { + // TODO(xyzzy): remapper will handle this + symLabel = Symbol.ConvertLabelForDisplay(symLabel, Symbol.LabelAnnotation.None, + false, formatter); } if (operandLen == 1) { @@ -942,9 +955,13 @@ namespace SourceGen { if (labelMap != null && labelMap.TryGetValue(symLabel, out string newLabel)) { symLabel = newLabel; } - // TODO(xyzzy): non-unique prefix - if ((flags & FormatNumericOpFlags.StripAnnotation) == 0) { - symLabel = Symbol.AppendAnnotation(symLabel, sym.LabelAnno); + if ((flags & FormatNumericOpFlags.StripLabelPrefixSuffix) == 0) { + symLabel = Symbol.ConvertLabelForDisplay(symLabel, sym.LabelAnno, + sym.IsNonUnique, formatter); + } else { + // TODO(xyzzy): remapper will handle this + symLabel = Symbol.ConvertLabelForDisplay(symLabel, Symbol.LabelAnnotation.None, + false, formatter); } int adjustment; diff --git a/SourceGen/Sandbox/ScriptManager.cs b/SourceGen/Sandbox/ScriptManager.cs index cc4b848..9970a32 100644 --- a/SourceGen/Sandbox/ScriptManager.cs +++ b/SourceGen/Sandbox/ScriptManager.cs @@ -224,6 +224,9 @@ namespace SourceGen.Sandbox { } PlSymbol.Type plsType; switch (sym.SymbolType) { + case Symbol.Type.NonUniqueLocalAddr: + // don't forward these to plugins + continue; case Symbol.Type.LocalOrGlobalAddr: case Symbol.Type.GlobalAddr: case Symbol.Type.GlobalAddrExport: diff --git a/SourceGen/Symbol.cs b/SourceGen/Symbol.cs index cc4c857..af15c1a 100644 --- a/SourceGen/Symbol.cs +++ b/SourceGen/Symbol.cs @@ -15,6 +15,7 @@ */ using System; using System.Diagnostics; +using System.Text; namespace SourceGen { /// @@ -22,6 +23,9 @@ namespace SourceGen { /// public class Symbol { public const char UNCERTAIN_CHAR = '?'; + private const char NO_ANNO_CHAR = '\ufffd'; // REPLACEMENT CHARACTER '�' + private const char UNIQUE_TAG_CHAR = '\u00a7'; // SECTION SIGN + private const int NON_UNIQUE_LEN = 7; // NON_UNIQUE_CHAR + 6 hex digits /// /// How was the symbol defined? @@ -42,6 +46,16 @@ namespace SourceGen { /// Unique or non-unique address label? Is it required to be global or exported? /// Constants get a separate type. /// + /// + /// There's really just three types: unique address symbol, non-unique address symbol, + /// and constant. There's also a set of boolean flags indicating whether the symbol + /// should be forced to be global, whether it should be included in the export table, + /// and whether it's internal or external. + /// + /// It turns out that many combinations of type and flag don't actually make sense, + /// e.g. I don't know what a non-unique exported external constant is, so we just + /// enumerate the combinations that make sense. + /// public enum Type { Unknown = 0, @@ -103,11 +117,6 @@ namespace SourceGen { /// public string SourceTypeString { get; private set; } - /// - /// Label with annotations. Generated from Label and LabelAnno. - /// - public string AnnotatedLabel { get; private set; } - /// /// True if the symbol's type is an internal label (auto or user). Will be false @@ -124,6 +133,13 @@ namespace SourceGen { get { return SymbolSource == Source.Variable; } } + /// + /// True if the symbol is a non-unique local. + /// + public bool IsNonUnique { + get { return SymbolType == Type.NonUniqueLocalAddr; } + } + /// /// True if the symbol represents a constant value. /// @@ -142,10 +158,11 @@ namespace SourceGen { /// Symbol value. /// User-defined, auto-generated, ? /// Type of symbol this is. + /// Optional annotation. public Symbol(string label, int value, Source source, Type type, LabelAnnotation labelAnno) { Debug.Assert(Asm65.Label.ValidateLabel(label)); - Debug.Assert(type != Type.NonUniqueLocalAddr); + Debug.Assert(type != Type.NonUniqueLocalAddr || value == 0xdead); // use other ctor Label = label; Value = value; SymbolType = type; @@ -172,24 +189,80 @@ namespace SourceGen { default: tc = '?'; break; } SourceTypeString = "" + sc + tc; - - // Generate AnnotatedLabel. - AnnotatedLabel = AppendAnnotation(Label, LabelAnno); } /// /// Constructor for non-unique labels. /// - /// - /// - /// - /// - /// - /// - public Symbol(string label, int value, Source source, Type type, - LabelAnnotation labelAnno, int offset) - : this(label, value, source, type, labelAnno) { - Debug.Assert(false); // TODO(xyzzy) + /// Label string. Syntax assumed valid. + /// Symbol value. + /// Optional annotation. + /// Tag that makes a non-unique label unique, e.g. the + /// offset for which a user label has been created. + public Symbol(string label, int value, LabelAnnotation labelAnno, int uniqueTag) + : this(label, 0xdead, Source.User, Type.NonUniqueLocalAddr, labelAnno) { + 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 + + // Add tag to label to make it unique. + Label = label + UNIQUE_TAG_CHAR + uniqueTag.ToString("x6"); + } + + /// + /// Generates a displayable form of the label. This will have the non-unique label + /// prefix and annotation suffix, and will have the non-unique tag removed. + /// + /// Formatter object. + /// Label suitable for display. + public string GenerateDisplayLabel(Asm65.Formatter formatter) { + return ConvertLabelForDisplay(Label, LabelAnno, IsNonUnique, formatter); + } + + /// + /// Returns the annotation suffix character, or NO_ANNO_CHAR if nothing appropriate. + /// + private static char GetLabelAnnoChar(LabelAnnotation anno) { + char ch = NO_ANNO_CHAR; + if (anno == LabelAnnotation.Uncertain) { + ch = UNCERTAIN_CHAR; + } else if (anno == LabelAnnotation.Generated) { + //ch = '\u00a4'; // CURRENCY SIGN '¤' + } + return ch; + } + + /// + /// 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. + /// + /// Base label string. Has the uniquification tag, but no + /// annotation char or non-unique prefix. + /// Annotation; may be None. + /// Formatted label. + public static string ConvertLabelForDisplay(string label, LabelAnnotation anno, + bool isNonUnique, Asm65.Formatter formatter) { + StringBuilder sb = new StringBuilder(label.Length + 2); + + if (isNonUnique) { + 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)); + } else { + sb.Append(label); + } + + char annoChar = GetLabelAnnoChar(anno); + if (annoChar != NO_ANNO_CHAR) { + sb.Append(annoChar); + } + return sb.ToString(); } /// @@ -199,14 +272,20 @@ namespace SourceGen { /// trimmed version of the string is returned. /// /// Label to examine. + /// For address symbols, the prefix string for + /// non-unique labels. 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. + /// True if the first character indicates that this is + /// a non-unique label. /// Annotation found, or None if none found. /// Trimmed version of the string. - public static string TrimAndValidateLabel(string label, out bool isValid, - out bool isLenValid, out bool isFirstCharValid, out LabelAnnotation anno) { + public static string TrimAndValidateLabel(string label, string nonUniquePrefix, + out bool isValid, out bool isLenValid, out bool isFirstCharValid, + out bool hasNonUniquePrefix, out LabelAnnotation anno) { anno = LabelAnnotation.None; + hasNonUniquePrefix = false; // Do we have at least one char? if (string.IsNullOrEmpty(label)) { @@ -221,6 +300,14 @@ namespace SourceGen { trimLabel = trimLabel.Substring(0, trimLabel.Length - 1); } + // Check for leading non-unique ident char. + if (trimLabel.Length > 0 && !string.IsNullOrEmpty(nonUniquePrefix)) { + if (trimLabel[0] == nonUniquePrefix[0]) { + hasNonUniquePrefix = true; + trimLabel = trimLabel.Substring(1); + } + } + // Now that we're down to the base string, do the full validation test. If it // passes, we don't need to dig any deeper. isValid = Asm65.Label.ValidateLabelDetail(trimLabel, out isLenValid, @@ -229,22 +316,6 @@ namespace SourceGen { return trimLabel; } - /// - /// Augments a label string with an annotation identifier. - /// - /// String to augment. - /// Annotation; may be None. - /// Original or updated string. - public static string AppendAnnotation(string label, LabelAnnotation anno) { - if (anno == LabelAnnotation.Uncertain) { - return label + UNCERTAIN_CHAR; - //} else if (anno == LabelAnnotation.Generated) { - // return label + '\u00a4'; // CURRENCY_SIGN '¤' - } else { - return label; - } - } - public override string ToString() { return Label + "{" + SymbolSource + "," + SymbolType + diff --git a/SourceGen/WpfGui/EditAppSettings.xaml b/SourceGen/WpfGui/EditAppSettings.xaml index d09c3de..cadf92f 100644 --- a/SourceGen/WpfGui/EditAppSettings.xaml +++ b/SourceGen/WpfGui/EditAppSettings.xaml @@ -504,7 +504,8 @@ limitations under the License. + IsChecked="{Binding DisableLabelLocalization}" + Visibility="Collapsed"/> @@ -571,6 +572,14 @@ limitations under the License. + + + + + + + + _Non-unique local ('{0}') + + @@ -47,18 +53,24 @@ limitations under the License. - + - - - + + + + - - private SymbolTable mSymbolTable; + /// + /// Label formatter. + /// + private Formatter mFormatter; + // Dialog label text color, saved off at dialog load time. private Brush mDefaultLabelColor; + /// + /// Recursion guard. + /// + private bool mInUpdateControls; + + public string NonUniqueButtonLabel { get; private set; } + /// /// Set to true when input is valid. Controls whether the OK button is enabled. /// public bool IsValid { get { return mIsValid; } - set { - mIsValid = value; - OnPropertyChanged(); - } + set { mIsValid = value; OnPropertyChanged(); } } private bool mIsValid; @@ -61,14 +77,51 @@ namespace SourceGen.WpfGui { /// public string LabelText { get { return mLabelText; } - set { - mLabelText = value; - LabelTextBox_TextChanged(); - OnPropertyChanged(); - } + set { mLabelText = value; OnPropertyChanged(); UpdateControls(); } } string mLabelText; + // Radio buttons. + public bool mIsNonUniqueChecked, mIsNonUniqueEnabled; + public bool IsNonUniqueChecked { + get { return mIsNonUniqueChecked; } + set { mIsNonUniqueChecked = value; OnPropertyChanged(); UpdateControls(); } + } + public bool IsNonUniqueEnabled { + get { return mIsNonUniqueEnabled; } + set { mIsNonUniqueEnabled = value; OnPropertyChanged(); } + } + + public bool mIsLocalChecked, mIsLocalEnabled; + public bool IsLocalChecked { + get { return mIsLocalChecked; } + set { mIsLocalChecked = value; OnPropertyChanged(); UpdateControls(); } + } + public bool IsLocalEnabled { + get { return mIsLocalEnabled; } + set { mIsLocalEnabled = value; OnPropertyChanged(); } + } + + public bool mIsGlobalChecked, mIsGlobalEnabled; + public bool IsGlobalChecked { + get { return mIsGlobalChecked; } + set { mIsGlobalChecked = value; OnPropertyChanged(); UpdateControls(); } + } + public bool IsGlobalEnabled { + get { return mIsGlobalEnabled; } + set { mIsGlobalEnabled = value; OnPropertyChanged(); } + } + + public bool mIsExportedChecked, mIsExportedEnabled; + public bool IsExportedChecked { + get { return mIsExportedChecked; } + set { mIsExportedChecked = value; OnPropertyChanged(); UpdateControls(); } + } + public bool IsExportedEnabled { + get { return mIsExportedEnabled; } + set { mIsExportedEnabled = value; OnPropertyChanged(); } + } + // INotifyPropertyChanged implementation public event PropertyChangedEventHandler PropertyChanged; private void OnPropertyChanged([CallerMemberName] string propertyName = "") { @@ -76,43 +129,53 @@ namespace SourceGen.WpfGui { } - public EditLabel(Window owner, Symbol origSym, int address, SymbolTable symbolTable) { + public EditLabel(Window owner, Symbol origSym, int address, int uniqueTag, + SymbolTable symbolTable, Formatter formatter) { InitializeComponent(); Owner = owner; DataContext = this; LabelSym = origSym; mAddress = address; + mUniqueTag = uniqueTag; mSymbolTable = symbolTable; + mFormatter = formatter; + + string fmt = (string)FindResource("str_NonUniqueLocalFmt"); + NonUniqueButtonLabel = string.Format(fmt, mFormatter.NonUniqueLabelPrefix); } private void Window_Loaded(object sender, RoutedEventArgs e) { mDefaultLabelColor = maxLengthLabel.Foreground; + IsNonUniqueEnabled = IsLocalEnabled = IsGlobalEnabled = IsExportedEnabled = true; + if (LabelSym == null) { LabelText = string.Empty; - radioButtonLocal.IsChecked = true; + IsGlobalChecked = true; } else { - LabelText = LabelSym.AnnotatedLabel; + LabelText = LabelSym.GenerateDisplayLabel(mFormatter); switch (LabelSym.SymbolType) { case Symbol.Type.NonUniqueLocalAddr: - Debug.Assert(false); // TODO(xyzzy) + IsNonUniqueChecked = true; break; case Symbol.Type.LocalOrGlobalAddr: - radioButtonLocal.IsChecked = true; + IsLocalChecked = true; break; case Symbol.Type.GlobalAddr: - radioButtonGlobal.IsChecked = true; + IsGlobalChecked = true; break; case Symbol.Type.GlobalAddrExport: - radioButtonExport.IsChecked = true; + IsExportedChecked = true; break; default: Debug.Assert(false); // WTF - radioButtonLocal.IsChecked = true; + IsGlobalChecked = true; break; } } + + UpdateControls(); } private void Window_ContentRendered(object sender, EventArgs e) { @@ -120,48 +183,39 @@ namespace SourceGen.WpfGui { labelTextBox.Focus(); } - private void LabelTextBox_TextChanged() { -#if false - string str = LabelText; - bool valid = true; - - if (str.Length == 1 || str.Length > Asm65.Label.MAX_LABEL_LEN) { - valid = false; - maxLengthLabel.Foreground = Brushes.Red; - } else { - maxLengthLabel.Foreground = mDefaultLabelColor; + private void UpdateControls() { + if (mInUpdateControls) { + return; } + mInUpdateControls = true; - // Regex never matches on strings of length 0 or 1, but we don't want - // to complain about that since we're already doing that above. - // TODO(maybe): Ideally this wouldn't light up if the only problem was a - // non-alpha first character, since the next test will call that out. - if (str.Length > 1) { - if (!Asm65.Label.ValidateLabel(str)) { - valid = false; - validCharsLabel.Foreground = Brushes.Red; - } else { - validCharsLabel.Foreground = mDefaultLabelColor; - } - } else { - validCharsLabel.Foreground = mDefaultLabelColor; - } + LabelTextChanged(); - if (str.Length > 0 && - !((str[0] >= 'A' && str[0] <= 'Z') || (str[0] >= 'a' && str[0] <= 'z') || - str[0] == '_')) { - // This should have been caught by the regex. We just want to set the - // color on the "first character must be" instruction text. - Debug.Assert(!valid); - firstLetterLabel.Foreground = Brushes.Red; - } else { - firstLetterLabel.Foreground = mDefaultLabelColor; - } -#endif + mInUpdateControls = false; + } + + private bool mHadNonUniquePrefix = false; + + private void LabelTextChanged() { bool isBlank = (LabelText.Length == 0); - string trimLabel = Symbol.TrimAndValidateLabel(LabelText, out bool isValid, - out bool isLenValid, out bool isFirstCharValid, out Symbol.LabelAnnotation anno); + // Strip leading non-unique prefix and the trailing annotation. + string trimLabel = Symbol.TrimAndValidateLabel(LabelText, + mFormatter.NonUniqueLabelPrefix, out bool isValid, out bool isLenValid, + out bool isFirstCharValid, out bool hasNonUniquePrefix, + out Symbol.LabelAnnotation anno); + + // If they type '@'/':'/'.' at the start of the label, switch the radio button. + // Alternatively, if they choose a different radio button, remove the prefix. + // We only want to do this on the first event so we don't wedge the control. + if (hasNonUniquePrefix && !mHadNonUniquePrefix && !IsNonUniqueChecked) { + IsNonUniqueChecked = true; + } else if (hasNonUniquePrefix && mHadNonUniquePrefix && !IsNonUniqueChecked) { + LabelText = LabelText.Substring(1); + hasNonUniquePrefix = false; + } + mHadNonUniquePrefix = hasNonUniquePrefix; + if (isBlank || isLenValid) { maxLengthLabel.Foreground = mDefaultLabelColor; } else { @@ -180,18 +234,30 @@ namespace SourceGen.WpfGui { validCharsLabel.Foreground = Brushes.Red; } - // Refuse to continue if the label already exists. The only exception is if - // it's the same symbol, and it's user-defined. (If they're trying to edit an - // auto label, we want to force them to change the name.) +#if false + if (hasNonUniqueTag) { + IsNonUniqueChecked = true; + IsLocalEnabled = IsGlobalEnabled = IsExportedEnabled = false; + } else { + IsNonUniqueEnabled = IsLocalEnabled = IsGlobalEnabled = IsExportedEnabled = true; + } +#endif + + // Refuse to continue if the label already exists and this isn't a non-unique label. + // The only exception is if it's the same symbol, and it's user-defined. (If + // they're trying to edit an auto label, we want to force them to change the name.) // // NOTE: if label matching is case-insensitive, we want to allow a situation // where a label is being renamed from "FOO" to "Foo". We should be able to // test for object equality on the Symbol to determine if we're renaming a // symbol to itself. - if (isValid && mSymbolTable.TryGetValue(trimLabel, out Symbol sym) && + if (!IsNonUniqueChecked && isValid && + mSymbolTable.TryGetValue(trimLabel, out Symbol sym) && (sym != LabelSym || LabelSym.SymbolSource != Symbol.Source.User)) { isValid = false; notDuplicateLabel.Foreground = Brushes.Red; + } else if (IsNonUniqueChecked) { + notDuplicateLabel.Foreground = Brushes.Gray; } else { notDuplicateLabel.Foreground = mDefaultLabelColor; } @@ -204,23 +270,32 @@ namespace SourceGen.WpfGui { LabelSym = null; } else { Symbol.Type symbolType; - // TODO(xyzzy): non-local - if (radioButtonLocal.IsChecked == true) { + if (IsNonUniqueChecked) { + symbolType = Symbol.Type.NonUniqueLocalAddr; + } else if (IsLocalChecked == true) { symbolType = Symbol.Type.LocalOrGlobalAddr; - } else if (radioButtonGlobal.IsChecked == true) { + } else if (IsGlobalChecked == true) { symbolType = Symbol.Type.GlobalAddr; - } else if (radioButtonExport.IsChecked == true) { + } else if (IsExportedChecked == true) { symbolType = Symbol.Type.GlobalAddrExport; } else { Debug.Assert(false); // WTF - symbolType = Symbol.Type.LocalOrGlobalAddr; + symbolType = Symbol.Type.GlobalAddr; } - // Parse and strip the annotation. - string trimLabel = Symbol.TrimAndValidateLabel(LabelText, out bool unused1, - out bool unused2, out bool unused3, out Symbol.LabelAnnotation anno); + // Parse and strip the annotation and optional non-unique tag. + string trimLabel = Symbol.TrimAndValidateLabel(LabelText, + mFormatter.NonUniqueLabelPrefix, out bool unused1, out bool unused2, + out bool unused3, out bool hasNonUniquePrefix, + out Symbol.LabelAnnotation anno); - LabelSym = new Symbol(trimLabel, mAddress, Symbol.Source.User, symbolType, anno); + if (IsNonUniqueChecked) { + LabelSym = new Symbol(trimLabel, mAddress, anno, mUniqueTag); + } else { + Debug.Assert(!hasNonUniquePrefix); + LabelSym = new Symbol(trimLabel, mAddress, Symbol.Source.User, symbolType, + anno); + } } DialogResult = true; } diff --git a/SourceGen/WpfGui/EditLocalVariableTable.xaml.cs b/SourceGen/WpfGui/EditLocalVariableTable.xaml.cs index 1f55e79..a94ddcf 100644 --- a/SourceGen/WpfGui/EditLocalVariableTable.xaml.cs +++ b/SourceGen/WpfGui/EditLocalVariableTable.xaml.cs @@ -175,7 +175,7 @@ namespace SourceGen.WpfGui { FormattedSymbol fsym = new FormattedSymbol( defSym, - defSym.AnnotatedLabel, + defSym.GenerateDisplayLabel(mFormatter), mFormatter.FormatValueInBase(defSym.Value, defSym.DataDescriptor.NumBase), typeStr, defSym.DataDescriptor.Length, diff --git a/SourceGen/WpfGui/EditProjectProperties.xaml.cs b/SourceGen/WpfGui/EditProjectProperties.xaml.cs index d7056c7..0b1f6a9 100644 --- a/SourceGen/WpfGui/EditProjectProperties.xaml.cs +++ b/SourceGen/WpfGui/EditProjectProperties.xaml.cs @@ -485,7 +485,7 @@ namespace SourceGen.WpfGui { FormattedSymbol fsym = new FormattedSymbol( defSym, - defSym.AnnotatedLabel, + defSym.GenerateDisplayLabel(mFormatter), mFormatter.FormatValueInBase(defSym.Value, defSym.DataDescriptor.NumBase), typeStr, defSym.HasWidth ? defSym.DataDescriptor.Length.ToString() : NO_WIDTH_STR,