1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-06-11 17:29:29 +00:00

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.
This commit is contained in:
Andy McFadden 2019-11-12 17:24:41 -08:00
parent 4d079c8d14
commit 5dd7576529
22 changed files with 427 additions and 175 deletions

View File

@ -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.)
/// </summary>
public class Formatter {
/// <summary>
@ -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 {
/// </summary>
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; }
}
/// <summary>
/// Prefix for non-unique address labels.
/// </summary>
public string NonUniqueLabelPrefix {
get { return mFormatConfig.mNonUniqueLabelPrefix; }
}
/// <summary>
/// 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.
/// </summary>
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;
}

View File

@ -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";

View File

@ -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);
}

View File

@ -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));

View File

@ -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));

View File

@ -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));

View File

@ -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;

View File

@ -1098,6 +1098,10 @@ namespace SourceGen {
/// Merges symbols from UserLabels into SymbolTable. Existing entries with matching
/// labels will be replaced.
/// </summary>
/// <remarks>
/// It might make sense to exclude non-unique labels, but that's probably better done
/// with a UI filter option.
/// </remarks>
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

View File

@ -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();
}

View File

@ -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;
}

View File

@ -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
/// <summary>
/// Populates the ItemsSource for the Symbols window. Each entry in the project
/// symbol table is added.
/// </summary>
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);
}
}

View File

@ -549,10 +549,10 @@ namespace SourceGen {
/// </summary>
[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
}
/// <summary>
@ -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;

View File

@ -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:

View File

@ -15,6 +15,7 @@
*/
using System;
using System.Diagnostics;
using System.Text;
namespace SourceGen {
/// <summary>
@ -22,6 +23,9 @@ namespace SourceGen {
/// </summary>
public class Symbol {
public const char UNCERTAIN_CHAR = '?';
private const char NO_ANNO_CHAR = '\ufffd'; // REPLACEMENT CHARACTER '<27>'
private const char UNIQUE_TAG_CHAR = '\u00a7'; // SECTION SIGN
private const int NON_UNIQUE_LEN = 7; // NON_UNIQUE_CHAR + 6 hex digits
/// <summary>
/// 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.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
public enum Type {
Unknown = 0,
@ -103,11 +117,6 @@ namespace SourceGen {
/// </summary>
public string SourceTypeString { get; private set; }
/// <summary>
/// Label with annotations. Generated from Label and LabelAnno.
/// </summary>
public string AnnotatedLabel { get; private set; }
/// <summary>
/// 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; }
}
/// <summary>
/// True if the symbol is a non-unique local.
/// </summary>
public bool IsNonUnique {
get { return SymbolType == Type.NonUniqueLocalAddr; }
}
/// <summary>
/// True if the symbol represents a constant value.
/// </summary>
@ -142,10 +158,11 @@ namespace SourceGen {
/// <param name="value">Symbol value.</param>
/// <param name="source">User-defined, auto-generated, ?</param>
/// <param name="type">Type of symbol this is.</param>
/// <param name="labelAnno">Optional annotation.</param>
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);
}
/// <summary>
/// Constructor for non-unique labels.
/// </summary>
/// <param name="label"></param>
/// <param name="value"></param>
/// <param name="source"></param>
/// <param name="type"></param>
/// <param name="labelAnno"></param>
/// <param name="offset"></param>
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)
/// <param name="label">Label string. Syntax assumed valid.</param>
/// <param name="value">Symbol value.</param>
/// <param name="labelAnno">Optional annotation.</param>
/// <param name="uniqueTag">Tag that makes a non-unique label unique, e.g. the
/// offset for which a user label has been created.</param>
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");
}
/// <summary>
/// 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.
/// </summary>
/// <param name="formatter">Formatter object.</param>
/// <returns>Label suitable for display.</returns>
public string GenerateDisplayLabel(Asm65.Formatter formatter) {
return ConvertLabelForDisplay(Label, LabelAnno, IsNonUnique, formatter);
}
/// <summary>
/// Returns the annotation suffix character, or NO_ANNO_CHAR if nothing appropriate.
/// </summary>
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;
}
/// <summary>
/// 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.
/// </summary>
/// <param name="label">Base label string. Has the uniquification tag, but no
/// annotation char or non-unique prefix.</param>
/// <param name="anno">Annotation; may be None.</param>
/// <returns>Formatted label.</returns>
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();
}
/// <summary>
@ -199,14 +272,20 @@ namespace SourceGen {
/// trimmed version of the string is returned.
/// </summary>
/// <param name="label">Label to examine.</param>
/// <param name="nonUniquePrefix">For address symbols, the prefix string for
/// non-unique labels. May be null if not validating a user label.</param>
/// <param name="isValid">True if the entire label is valid.</param>
/// <param name="isLenValid">True if the label has a valid length.</param>
/// <param name="isFirstCharValid">True if the first character is valid.</param>
/// <param name="hasNonUniquePrefix">True if the first character indicates that this is
/// a non-unique label.</param>
/// <param name="anno">Annotation found, or None if none found.</param>
/// <returns>Trimmed version of the string.</returns>
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;
}
/// <summary>
/// Augments a label string with an annotation identifier.
/// </summary>
/// <param name="label">String to augment.</param>
/// <param name="anno">Annotation; may be None.</param>
/// <returns>Original or updated string.</returns>
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 +

View File

@ -504,7 +504,8 @@ limitations under the License.
<CheckBox Content="Identify assembler in output" Margin="4,8,0,0"
IsChecked="{Binding AddIdentComment}"/>
<CheckBox Content="Disable label localization" Margin="4,8,0,0"
IsChecked="{Binding DisableLabelLocalization}"/>
IsChecked="{Binding DisableLabelLocalization}"
Visibility="Collapsed"/>
</StackPanel>
</TabItem>
@ -571,6 +572,14 @@ limitations under the License.
</StackPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="4,16,0,0">
<TextBlock Text="Non-unique label prefix:"/>
<TextBox Width="14" MaxLength="1" Margin="8,1,0,0"
Text="{Binding NonUniqueLabelPrefix, UpdateSourceTrigger=PropertyChanged,
FallbackValue=@}"
FontFamily="{StaticResource GeneralMonoFont}"/>
</StackPanel>
<StackPanel DockPanel.Dock="Top" Orientation="Horizontal" Margin="4,4,0,0">
<TextBlock Text="Local variable prefix:"/>
<TextBox Width="66" MaxLength="8" Margin="8,1,0,0"
Text="{Binding LocalVarPrefix, UpdateSourceTrigger=PropertyChanged,

View File

@ -858,6 +858,31 @@ namespace SourceGen.WpfGui {
}
}
}
private string mNonUniqueLabelPrefix;
public string NonUniqueLabelPrefix {
get { return mNonUniqueLabelPrefix; }
set {
if (mNonUniqueLabelPrefix != value) {
mNonUniqueLabelPrefix = value;
OnPropertyChanged();
bool doSave = true;
if (value.Length > 0) {
char ch = value[0];
doSave = !((ch >= 'A' && ch <= 'Z') || (ch >= 'a' && ch <= 'z') ||
ch == '_');
}
if (doSave) {
mSettings.SetString(AppSettings.FMT_NON_UNIQUE_LABEL_PREFIX, value);
} else {
// TODO(someday): add a validation rule
Debug.WriteLine("NOTE: quietly rejecting non-unique prefix '" +
value + "'");
}
UpdateDisplayFormatQuickCombo();
IsDirty = true;
}
}
}
private string mLocalVarPrefix;
public string LocalVarPrefix {
get { return mLocalVarPrefix; }
@ -915,18 +940,20 @@ namespace SourceGen.WpfGui {
public string OpcodeSuffixLong { get; private set; }
public string OperandPrefixAbs { get; private set; }
public string OperandPrefixLong { get; private set; }
public string NonUniqueLabelPrefix { get; private set; }
public string LocalVarPrefix { get; private set; }
public ExpressionMode ExpressionStyle { get; private set; }
public DisplayFormatPreset(int id, string name, string opcSuffixAbs,
string opcSuffixLong, string operPrefixAbs, string operPrefixLong,
string localVarPrefix, ExpressionMode expStyle) {
string nonUniqueLabelPrefix, string localVarPrefix, ExpressionMode expStyle) {
Ident = id;
Name = name;
OpcodeSuffixAbs = opcSuffixAbs;
OpcodeSuffixLong = opcSuffixLong;
OperandPrefixAbs = operPrefixAbs;
OperandPrefixLong = operPrefixLong;
NonUniqueLabelPrefix = nonUniqueLabelPrefix;
LocalVarPrefix = localVarPrefix;
ExpressionStyle = expStyle;
}
@ -938,10 +965,10 @@ namespace SourceGen.WpfGui {
DisplayPresets = new DisplayFormatPreset[AssemblerList.Count + 2];
DisplayPresets[0] = new DisplayFormatPreset(DisplayFormatPreset.ID_CUSTOM,
(string)FindResource("str_PresetCustom"), string.Empty, string.Empty,
string.Empty, string.Empty, string.Empty, ExpressionMode.Unknown);
string.Empty, string.Empty, string.Empty, string.Empty, ExpressionMode.Unknown);
DisplayPresets[1] = new DisplayFormatPreset(DisplayFormatPreset.ID_DEFAULT,
(string)FindResource("str_PresetDefault"), string.Empty, "l", "a:", "f:",
string.Empty, ExpressionMode.Common);
string.Empty, string.Empty, ExpressionMode.Common);
for (int i = 0; i < AssemblerList.Count; i++) {
AssemblerInfo asmInfo = AssemblerList[i];
AsmGen.IGenerator gen = AssemblerInfo.GetGenerator(asmInfo.AssemblerId);
@ -952,8 +979,8 @@ namespace SourceGen.WpfGui {
DisplayPresets[i + 2] = new DisplayFormatPreset((int)asmInfo.AssemblerId,
asmInfo.Name, formatConfig.mForceAbsOpcodeSuffix,
formatConfig.mForceLongOpcodeSuffix, formatConfig.mForceAbsOperandPrefix,
formatConfig.mForceLongOperandPrefix, formatConfig.mLocalVariableLablePrefix,
formatConfig.mExpressionMode);
formatConfig.mForceLongOperandPrefix, formatConfig.mNonUniqueLabelPrefix,
formatConfig.mLocalVariableLabelPrefix, formatConfig.mExpressionMode);
}
}
@ -972,6 +999,8 @@ namespace SourceGen.WpfGui {
mSettings.GetString(AppSettings.FMT_OPERAND_PREFIX_ABS, string.Empty);
OperandPrefixLong =
mSettings.GetString(AppSettings.FMT_OPERAND_PREFIX_LONG, string.Empty);
NonUniqueLabelPrefix =
mSettings.GetString(AppSettings.FMT_NON_UNIQUE_LABEL_PREFIX, string.Empty);
LocalVarPrefix =
mSettings.GetString(AppSettings.FMT_LOCAL_VARIABLE_PREFIX, string.Empty);
@ -1029,6 +1058,7 @@ namespace SourceGen.WpfGui {
OpcodeSuffixLong = preset.OpcodeSuffixLong;
OperandPrefixAbs = preset.OperandPrefixAbs;
OperandPrefixLong = preset.OperandPrefixLong;
NonUniqueLabelPrefix = preset.NonUniqueLabelPrefix;
LocalVarPrefix = preset.LocalVarPrefix;
SelectExpressionStyle(preset.ExpressionStyle);
@ -1053,6 +1083,7 @@ namespace SourceGen.WpfGui {
OpcodeSuffixLong == preset.OpcodeSuffixLong &&
OperandPrefixAbs == preset.OperandPrefixAbs &&
OperandPrefixLong == preset.OperandPrefixLong &&
NonUniqueLabelPrefix == preset.NonUniqueLabelPrefix &&
LocalVarPrefix == preset.LocalVarPrefix &&
expMode == preset.ExpressionStyle) {
// match

View File

@ -208,7 +208,7 @@ namespace SourceGen.WpfGui {
mDefaultLabelColor = labelNotesLabel.Foreground;
if (mOldSym != null) {
Label = mOldSym.AnnotatedLabel;
Label = mOldSym.GenerateDisplayLabel(mNumFormatter);
Value = mNumFormatter.FormatValueInBase(mOldSym.Value,
mOldSym.DataDescriptor.NumBase);
if (mOldSym.HasWidth) {
@ -251,8 +251,9 @@ namespace SourceGen.WpfGui {
// Label must be valid and not already exist in the table we're editing. (For project
// symbols, it's okay if an identical label exists elsewhere.)
string trimLabel = Symbol.TrimAndValidateLabel(Label, out bool labelValid,
out bool unused1, out bool unused2, out Symbol.LabelAnnotation unused3);
string trimLabel = Symbol.TrimAndValidateLabel(Label, string.Empty,
out bool labelValid, out bool unused1, out bool unused2, out bool unused3,
out Symbol.LabelAnnotation unused4);
bool labelUnique;
// NOTE: should be using Asm65.Label.LABEL_COMPARER?
@ -276,7 +277,7 @@ namespace SourceGen.WpfGui {
// Value must be blank, meaning "erase any earlier definition", or valid value.
// (Hmm... don't currently have a way to specify "no symbol" in DefSymbol.)
//if (!string.IsNullOrEmpty(valueTextBox.Text)) {
bool valueValid = ParseValue(out int thisValue, out int unused4);
bool valueValid = ParseValue(out int thisValue, out int unused5);
//} else {
// valueValid = true;
//}
@ -384,8 +385,9 @@ namespace SourceGen.WpfGui {
}
// Parse and strip the annotation.
string trimLabel = Symbol.TrimAndValidateLabel(Label, out bool unused1,
out bool unused2, out bool unused3, out Symbol.LabelAnnotation anno);
string trimLabel = Symbol.TrimAndValidateLabel(Label, string.Empty, out bool unused1,
out bool unused2, out bool unused3, out bool unused4,
out Symbol.LabelAnnotation anno);
NewSym = new DefSymbol(trimLabel, value,
IsVariable ? Symbol.Source.Variable : Symbol.Source.Project,
IsConstant ? Symbol.Type.Constant : Symbol.Type.ExternalAddr, anno,

View File

@ -927,7 +927,7 @@ namespace SourceGen.WpfGui {
private void EditLabel_Click(object sender, RoutedEventArgs e) {
EditLabel dlg = new EditLabel(this, mEditedLabel, mLabelTargetAddress,
mProject.SymbolTable);
mEditedLabelOffset, mProject.SymbolTable, mFormatter);
if (dlg.ShowDialog() != true || mEditedLabel == dlg.LabelSym) {
Debug.WriteLine("No change to label, ignoring edit");
return;

View File

@ -19,6 +19,7 @@ limitations under the License.
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:SourceGen.WpfGui"
mc:Ignorable="d"
Title="Edit Label"
@ -26,6 +27,11 @@ limitations under the License.
ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
Loaded="Window_Loaded"
ContentRendered="Window_ContentRendered">
<Window.Resources>
<system:String x:Key="str_NonUniqueLocalFmt">_Non-unique local ('{0}')</system:String>
</Window.Resources>
<Grid Margin="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
@ -47,18 +53,24 @@ limitations under the License.
<TextBlock Name="notDuplicateLabel" Text="• Must not be a duplicate of an existing label"/>
</StackPanel>
<GroupBox Grid.Column="0" Grid.Row="1" Header="Label Type" Margin="0,12,0,0">
<GroupBox Grid.Column="0" Grid.Row="1" Header="Label Type" Padding="2,4" Margin="0,12,0,0">
<StackPanel>
<RadioButton Name="radioButtonLocal" Content="_Local (if possible)" Margin="0,4,0,0"/>
<RadioButton Name="radioButtonGlobal" Content="_Global" Margin="0,4,0,0"/>
<RadioButton Name="radioButtonExport" Content="Global and _exported" Margin="0,4,0,0"/>
<RadioButton Content="{Binding NonUniqueButtonLabel, FallbackValue=_Non-unique local (\'!\')}"
IsChecked="{Binding IsNonUniqueChecked}" IsEnabled="{Binding IsNonUniqueEnabled}"/>
<RadioButton Content="_Local (if possible)" Margin="0,4,0,0"
IsChecked="{Binding IsLocalChecked}" IsEnabled="{Binding IsLocalEnabled}"/>
<RadioButton Content="_Global" Margin="0,4,0,0"
IsChecked="{Binding IsGlobalChecked}" IsEnabled="{Binding IsGlobalEnabled}"/>
<RadioButton Content="Global and marked for _export" Margin="0,4,0,0"
IsChecked="{Binding IsExportedChecked}" IsEnabled="{Binding IsExportedEnabled}"/>
</StackPanel>
</GroupBox>
<DockPanel Grid.Column="1" Grid.Row="1" LastChildFill="False">
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Right">
<Button Content="OK" Width="70" IsDefault="True" IsEnabled="{Binding IsValid}"
Click="OkButton_Click"/>
<StackPanel DockPanel.Dock="Bottom" Orientation="Horizontal" HorizontalAlignment="Right"
Margin="0,0,0,1">
<Button Content="OK" Width="70" IsDefault="True" Margin="8,0,0,0"
IsEnabled="{Binding IsValid}" Click="OkButton_Click"/>
<Button Content="Cancel" Width="70" IsCancel="True" Margin="4,0,0,0"/>
</StackPanel>
</DockPanel>

View File

@ -20,6 +20,8 @@ using System.Runtime.CompilerServices;
using System.Windows;
using System.Windows.Media;
using Asm65;
namespace SourceGen.WpfGui {
/// <summary>
/// Edit a label.
@ -31,6 +33,11 @@ namespace SourceGen.WpfGui {
/// </summary>
public Symbol LabelSym { get; private set; }
/// <summary>
/// Unique tag, for non-unique label creation. (Currently using offset.)
/// </summary>
private int mUniqueTag;
/// <summary>
/// Address we are editing the label for.
/// </summary>
@ -41,18 +48,27 @@ namespace SourceGen.WpfGui {
/// </summary>
private SymbolTable mSymbolTable;
/// <summary>
/// Label formatter.
/// </summary>
private Formatter mFormatter;
// Dialog label text color, saved off at dialog load time.
private Brush mDefaultLabelColor;
/// <summary>
/// Recursion guard.
/// </summary>
private bool mInUpdateControls;
public string NonUniqueButtonLabel { get; private set; }
/// <summary>
/// Set to true when input is valid. Controls whether the OK button is enabled.
/// </summary>
public bool IsValid {
get { return mIsValid; }
set {
mIsValid = value;
OnPropertyChanged();
}
set { mIsValid = value; OnPropertyChanged(); }
}
private bool mIsValid;
@ -61,14 +77,51 @@ namespace SourceGen.WpfGui {
/// </summary>
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;
}

View File

@ -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,

View File

@ -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,