mirror of
https://github.com/fadden/6502bench.git
synced 2024-12-28 16:31:17 +00:00
Label rework, part 6
Correct handling of local variables. We now correctly uniquify them with regard to non-unique labels. Because local vars can effectively have global scope we mostly want to treat them as global, but they're uniquified relative to other globals very late in the process, so we can't just throw them in the symbol table and be done. Fortunately local variables exist in a separate namespace, so we just need to uniquify the variables relative to the post-localization symbol table. In other words, we take the symbol table, apply the label map, and rename any variable that clashes. This also fixes an older problem where we weren't masking the leading '_' on variable labels when generating 64tass output. The code list now makes non-unique labels obvious, but you can't tell the difference between unique global and unique local. What's more, the default type value in Edit Label is now adjusted to Global for unique locals that were auto-generated. To make it a bit easier to figure out what's what, the Info panel now has a "label type" line that reports the type. The 2023-non-unique-labels test had some additional tests added to exercise conflicts with local variables. The 2019-local-variables test output changed slightly because the de-duplicated variable naming convention was simplified.
This commit is contained in:
parent
88c56616f7
commit
d3670c48e8
@ -163,6 +163,7 @@ namespace SourceGen.AsmGen {
|
||||
Project = project;
|
||||
Quirks = new AssemblerQuirks();
|
||||
Quirks.StackIntOperandIsImmediate = true;
|
||||
Quirks.LeadingUnderscoreSpecial = true;
|
||||
|
||||
mWorkDirectory = workDirectory;
|
||||
mFileNameBase = fileNameBase;
|
||||
|
@ -40,6 +40,7 @@ namespace SourceGen.AsmGen {
|
||||
bool doAddCycles = gen.Settings.GetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS, false);
|
||||
|
||||
LocalVariableLookup lvLookup = new LocalVariableLookup(proj.LvTables, proj,
|
||||
gen.Localizer.LabelMap, gen.Quirks.LeadingUnderscoreSpecial,
|
||||
gen.Quirks.NoRedefinableSymbols);
|
||||
|
||||
GenerateHeader(gen, sw);
|
||||
|
@ -201,6 +201,11 @@ namespace SourceGen.AsmGen {
|
||||
/// Enumeration of quirky or buggy behavior that GenCommon needs to handle.
|
||||
/// </summary>
|
||||
public class AssemblerQuirks {
|
||||
/// <summary>
|
||||
/// Does a leading underscore in a label have a special meaning? (e.g. 64tass)
|
||||
/// </summary>
|
||||
public bool LeadingUnderscoreSpecial { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Are 8-bit constant args to MVN/MVP output without a leading '#'?
|
||||
/// </summary>
|
||||
|
@ -43,7 +43,8 @@ Another common form allows backward references to labels that don't go out of sc
|
||||
they're re-used. This is useful for short loops. (We use this for variables.)
|
||||
|
||||
As a further limitation, assemblers seem to want the first label encountered in a program
|
||||
to be global.
|
||||
to be global. On the plus side, all tested assemblers treat local labels as existing in
|
||||
a separate namespace, so you can have both "loop" and ":loop".
|
||||
|
||||
The Symbol.SymbolType enum allows a label to be defined as "local or global". We can output
|
||||
these with the local-symbol syntax, potentially rewriting them to have non-unique names like
|
||||
@ -80,7 +81,7 @@ namespace SourceGen.AsmGen {
|
||||
public class LabelLocalizer {
|
||||
// Prefix string to use for labels that start with '_' when generating code for
|
||||
// assemblers that assign a special meaning to leading underscores.
|
||||
private const string NO_UNDER_PFX = "X";
|
||||
public const string NO_UNDER_PFX = "X";
|
||||
|
||||
/// <summary>
|
||||
/// A pairing of an offset with a label string. (Essentially mAnattribs[n].Symbol
|
||||
|
@ -1179,7 +1179,8 @@ namespace SourceGen {
|
||||
/// for the benefit of future uniqueness checks.
|
||||
/// </summary>
|
||||
private void GenerateVariableRefs() {
|
||||
LocalVariableLookup lvLookup = new LocalVariableLookup(LvTables, this, false);
|
||||
LocalVariableLookup lvLookup = new LocalVariableLookup(LvTables, this,
|
||||
null, false, false);
|
||||
|
||||
for (int offset = 0; offset < FileData.Length; ) {
|
||||
// Was a table defined at this offset?
|
||||
@ -1408,7 +1409,8 @@ namespace SourceGen {
|
||||
}
|
||||
}
|
||||
|
||||
LocalVariableLookup lvLookup = new LocalVariableLookup(LvTables, this, false);
|
||||
LocalVariableLookup lvLookup = new LocalVariableLookup(LvTables, this,
|
||||
null, false, false);
|
||||
|
||||
// Walk through the Anattrib array, adding xref entries to things referenced
|
||||
// by the entity at the current offset.
|
||||
|
@ -437,7 +437,7 @@ namespace SourceGen {
|
||||
mFormattedLineCache = new FormattedOperandCache();
|
||||
mShowCycleCounts = AppSettings.Global.GetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS,
|
||||
false);
|
||||
mLvLookup = new LocalVariableLookup(mProject.LvTables, mProject, false);
|
||||
mLvLookup = new LocalVariableLookup(mProject.LvTables, mProject, null, false, false);
|
||||
|
||||
mDisplayList.ListGen = this;
|
||||
}
|
||||
|
@ -27,8 +27,9 @@ namespace SourceGen {
|
||||
/// different levels:
|
||||
/// (1) If the local variable label is present in the main symbol table, we use the
|
||||
/// "de-duplication" table to remap it. We try not to let this happen, but it can.
|
||||
/// The symbol table is latched when the object is constructed.
|
||||
/// (2) If the assembler doesn't define a way to re-use variable names, we make them
|
||||
/// globally unique. [currently unused]
|
||||
/// globally unique. [currently not needed]
|
||||
/// </remarks>
|
||||
public class LocalVariableLookup {
|
||||
/// <summary>
|
||||
@ -36,22 +37,30 @@ namespace SourceGen {
|
||||
/// </summary>
|
||||
private SortedList<int, LocalVariableTable> mLvTables;
|
||||
|
||||
/// <summary>
|
||||
/// Table of symbols, used to ensure that symbols are globally unique.
|
||||
/// </summary>
|
||||
private SymbolTable mSymbolTable;
|
||||
|
||||
/// <summary>
|
||||
/// Reference to project, so we can query the Anattrib array to identify "hidden" tables.
|
||||
/// </summary>
|
||||
private DisasmProject mProject;
|
||||
|
||||
/// <summary>
|
||||
/// Set to true when generating symbols for assemblers like 64tass, which assign a
|
||||
/// special meaning to labels with leading underscores.
|
||||
/// </summary>
|
||||
private bool mMaskLeadingUnderscores;
|
||||
|
||||
/// <summary>
|
||||
/// Set to true if we want all variables to be globally unique (because the assembler
|
||||
/// can't redefine them).
|
||||
/// </summary>
|
||||
private bool mDoUniquify;
|
||||
|
||||
/// <summary>
|
||||
/// List of all non-variable symbols, for uniquification. This is generated from the
|
||||
/// project symbol table. When generating assembly sources, the labels are transformed
|
||||
/// through the label map.
|
||||
/// </summary>
|
||||
private Dictionary<string, Symbol> mAllNvSymbols;
|
||||
|
||||
/// <summary>
|
||||
/// Label uniquification helper.
|
||||
///
|
||||
@ -86,7 +95,7 @@ namespace SourceGen {
|
||||
/// re-defined.
|
||||
/// </summary>
|
||||
/// <param name="symbolTable">Symbol table, for uniqueness check.</param>
|
||||
public void MakeUnique(SymbolTable symbolTable) {
|
||||
public void MakeUnique(Dictionary<string, Symbol> allNvSymbols) {
|
||||
// The main symbol table might have user-supplied labels like "ptr_2", so we
|
||||
// need to keep testing against that. However, it should not be possible for
|
||||
// us to clash with other uniquified variables. So we don't need to check
|
||||
@ -98,7 +107,7 @@ namespace SourceGen {
|
||||
do {
|
||||
Counter++;
|
||||
testLabel = BaseLabel + "_" + Counter;
|
||||
} while (symbolTable.TryGetValue(testLabel, out Symbol sym));
|
||||
} while (allNvSymbols.TryGetValue(testLabel, out Symbol unused));
|
||||
Label = testLabel;
|
||||
}
|
||||
}
|
||||
@ -141,15 +150,18 @@ namespace SourceGen {
|
||||
/// Constructor.
|
||||
/// </summary>
|
||||
/// <param name="lvTables">List of tables from the DisasmProject.</param>
|
||||
/// <param name="symbolTable">Full SymbolTable from the DisasmProject. Used to
|
||||
/// generate globally unique symbol names.</param>
|
||||
/// <param name="project">Project reference.</param>
|
||||
/// <param name="labelMap">Label map dictionary, used to rename the labels in
|
||||
/// the project symbol table. May be null.</param>
|
||||
/// <param name="maskLeadingUnderscores">If true, labels with leading underscores
|
||||
/// will be prefixed.</param>
|
||||
/// <param name="uniquify">Set to true if variable names cannot be redefined.</param>
|
||||
public LocalVariableLookup(SortedList<int, LocalVariableTable> lvTables,
|
||||
DisasmProject project, bool uniquify) {
|
||||
DisasmProject project, Dictionary<string, string> labelMap,
|
||||
bool maskLeadingUnderscores, bool uniquify) {
|
||||
mLvTables = lvTables;
|
||||
mSymbolTable = project.SymbolTable;
|
||||
mProject = project;
|
||||
mMaskLeadingUnderscores = maskLeadingUnderscores;
|
||||
mDoUniquify = uniquify;
|
||||
|
||||
mCurrentTable = new LocalVariableTable();
|
||||
@ -157,9 +169,29 @@ namespace SourceGen {
|
||||
if (uniquify) {
|
||||
mUniqueLabels = new Dictionary<string, UniqueLabel>();
|
||||
}
|
||||
CreateAllSymbolsDict(labelMap);
|
||||
Reset();
|
||||
}
|
||||
|
||||
private void CreateAllSymbolsDict(Dictionary<string, string> labelMap) {
|
||||
SymbolTable symTab = mProject.SymbolTable;
|
||||
mAllNvSymbols = new Dictionary<string, Symbol>(symTab.Count);
|
||||
foreach (Symbol sym in symTab) {
|
||||
if (sym.SymbolSource == Symbol.Source.Variable) {
|
||||
continue;
|
||||
}
|
||||
if (labelMap != null && labelMap.TryGetValue(sym.Label, out string newLabel)) {
|
||||
// Non-unique labels may map multiple entries to a single entry. That's
|
||||
// fine; our goal here is just to avoid duplication. Besides, any symbols
|
||||
// being output as locals will have the local prefix character and won't
|
||||
// be a match.
|
||||
mAllNvSymbols[newLabel] = sym;
|
||||
} else {
|
||||
mAllNvSymbols[sym.Label] = sym;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset() {
|
||||
mRecentOffset = -1;
|
||||
mRecentSymbols = null;
|
||||
@ -344,15 +376,26 @@ namespace SourceGen {
|
||||
// discards entries that clash by name or value.
|
||||
for (int i = 0; i < lvt.Count; i++) {
|
||||
DefSymbol defSym = lvt[i];
|
||||
string newLabel = defSym.Label;
|
||||
|
||||
if (mMaskLeadingUnderscores && newLabel[0] == '_') {
|
||||
newLabel = AsmGen.LabelLocalizer.NO_UNDER_PFX + newLabel;
|
||||
}
|
||||
|
||||
// Look for non-variable symbols with the same label. Ordinarily the
|
||||
// editor prevents this from happening, but there are ways to trick
|
||||
// the system (e.g. add a symbol while the LvTable is hidden). We
|
||||
// deal with it here.
|
||||
if (mSymbolTable.TryGetNonVariableValue(defSym.Label, out Symbol unused)) {
|
||||
Debug.WriteLine("Detected duplicate non-var label " + defSym.Label +
|
||||
// the system (e.g. add a symbol while the LvTable is hidden, or have
|
||||
// a non-unique local promoted to global). We deal with it here.
|
||||
//
|
||||
// TODO(someday): this is not necessary for assemblers like Merlin 32
|
||||
// that put variables in a separate namespace.
|
||||
if (mAllNvSymbols.TryGetValue(newLabel, out Symbol unused)) {
|
||||
Debug.WriteLine("Detected duplicate non-var label " + newLabel +
|
||||
" at +" + mNextLvtOffset.ToString("x6"));
|
||||
string newLabel = DeDupLabel(defSym.Label);
|
||||
newLabel = DeDupLabel(newLabel);
|
||||
}
|
||||
|
||||
if (newLabel != defSym.Label) {
|
||||
mDupRemap[defSym.Label] = newLabel;
|
||||
defSym = new DefSymbol(defSym, newLabel);
|
||||
}
|
||||
@ -361,7 +404,7 @@ namespace SourceGen {
|
||||
if (mUniqueLabels.TryGetValue(defSym.Label, out UniqueLabel ulab)) {
|
||||
// We've seen this label before; generate a unique version by
|
||||
// increasing the appended number.
|
||||
ulab.MakeUnique(mSymbolTable);
|
||||
ulab.MakeUnique(mAllNvSymbols);
|
||||
defSym = new DefSymbol(defSym, ulab.Label);
|
||||
} else {
|
||||
// Haven't seen this before. Add it to the unique-labels table.
|
||||
@ -394,8 +437,8 @@ namespace SourceGen {
|
||||
int counter = 0;
|
||||
do {
|
||||
counter++;
|
||||
testLabel = baseLabel + "_DUP" + counter; // make it ugly and obvious
|
||||
} while (mSymbolTable.TryGetNonVariableValue(testLabel, out Symbol unused));
|
||||
testLabel = baseLabel + "_" + counter;
|
||||
} while (mAllNvSymbols.TryGetValue(testLabel, out Symbol unused));
|
||||
return testLabel;
|
||||
}
|
||||
|
||||
|
@ -1784,8 +1784,8 @@ namespace SourceGen {
|
||||
int selIndex = mMainWin.CodeListView_GetFirstSelectedIndex();
|
||||
int offset = CodeLineList[selIndex].FileOffset;
|
||||
|
||||
LocalVariableLookup lvLookup = new LocalVariableLookup(mProject.LvTables,
|
||||
mProject, false);
|
||||
LocalVariableLookup lvLookup = new LocalVariableLookup(mProject.LvTables, mProject,
|
||||
null, false, false);
|
||||
int bestOffset = lvLookup.GetNearestTableOffset(offset);
|
||||
Debug.Assert(bestOffset >= 0);
|
||||
CreateOrEditLocalVariableTable(bestOffset);
|
||||
@ -2507,7 +2507,7 @@ namespace SourceGen {
|
||||
Debug.Assert(symRef.IsVariable);
|
||||
|
||||
LocalVariableLookup lvLookup = new LocalVariableLookup(mProject.LvTables, mProject,
|
||||
false);
|
||||
null, false, false);
|
||||
int varOffset = lvLookup.GetDefiningTableOffset(offset, symRef);
|
||||
if (varOffset <= 0) {
|
||||
Debug.WriteLine("Local variable not found; offset=" + offset + " ref=" + symRef);
|
||||
@ -3565,11 +3565,13 @@ namespace SourceGen {
|
||||
if (line.IsCodeOrData) {
|
||||
// Show number of bytes of code/data.
|
||||
if (line.OffsetSpan == 1) {
|
||||
mMainWin.InfoLineDescrText = string.Format(Res.Strings.INFO_LINE_SUM_SINGULAR_FMT,
|
||||
lineIndex, line.OffsetSpan, lineTypeStr);
|
||||
mMainWin.InfoLineDescrText =
|
||||
string.Format(Res.Strings.INFO_LINE_SUM_SINGULAR_FMT,
|
||||
lineIndex, line.OffsetSpan, lineTypeStr);
|
||||
} else {
|
||||
mMainWin.InfoLineDescrText = string.Format(Res.Strings.INFO_LINE_SUM_PLURAL_FMT,
|
||||
lineIndex, line.OffsetSpan, lineTypeStr);
|
||||
mMainWin.InfoLineDescrText =
|
||||
string.Format(Res.Strings.INFO_LINE_SUM_PLURAL_FMT,
|
||||
lineIndex, line.OffsetSpan, lineTypeStr);
|
||||
}
|
||||
} else {
|
||||
mMainWin.InfoLineDescrText = string.Format(Res.Strings.INFO_LINE_SUM_NON_FMT,
|
||||
@ -3587,7 +3589,11 @@ namespace SourceGen {
|
||||
string str = string.Empty;
|
||||
if (mProject.LvTables.TryGetValue(line.FileOffset,
|
||||
out LocalVariableTable lvt)) {
|
||||
str = lvt.Count + " entries";
|
||||
if (lvt.Count == 1) {
|
||||
str = "1 entry";
|
||||
} else {
|
||||
str = lvt.Count + " entries";
|
||||
}
|
||||
if (lvt.ClearPrevious) {
|
||||
str += "; clear previous";
|
||||
}
|
||||
@ -3663,6 +3669,34 @@ namespace SourceGen {
|
||||
StringBuilder sb = new StringBuilder(250);
|
||||
Anattrib attr = mProject.GetAnattrib(line.FileOffset);
|
||||
|
||||
if (attr.Symbol != null) {
|
||||
string descr;
|
||||
switch (attr.Symbol.SymbolType) {
|
||||
case Symbol.Type.NonUniqueLocalAddr:
|
||||
descr = "non-unique local";
|
||||
break;
|
||||
case Symbol.Type.LocalOrGlobalAddr:
|
||||
descr = "unique local";
|
||||
break;
|
||||
case Symbol.Type.GlobalAddr:
|
||||
descr = "unique global";
|
||||
break;
|
||||
case Symbol.Type.GlobalAddrExport:
|
||||
descr = "global + marked for export";
|
||||
break;
|
||||
default:
|
||||
descr = "???";
|
||||
break;
|
||||
}
|
||||
if (attr.Symbol.SymbolSource == Symbol.Source.Auto) {
|
||||
descr += ", auto-generated";
|
||||
} else if (attr.Symbol.LabelAnno == Symbol.LabelAnnotation.Generated) {
|
||||
descr += " [gen]";
|
||||
}
|
||||
mMainWin.InfoLabelDescrText =
|
||||
string.Format(Res.Strings.INFO_LABEL_DESCR_FMT, descr);
|
||||
}
|
||||
|
||||
if (!mProject.OperandFormats.TryGetValue(line.FileOffset, out FormatDescriptor dfd)) {
|
||||
// No user-specified format, but there may be a generated format.
|
||||
mMainWin.InfoFormatBoxBrush = Brushes.Blue;
|
||||
|
@ -721,10 +721,6 @@ namespace SourceGen {
|
||||
if ((flags & FormatNumericOpFlags.OmitLabelPrefixSuffix) == 0) {
|
||||
symLabel = Symbol.ConvertLabelForDisplay(symLabel, sym.LabelAnno,
|
||||
true, formatter);
|
||||
} else {
|
||||
// TODO(xyzzy): remapper will handle this
|
||||
symLabel = Symbol.ConvertLabelForDisplay(symLabel, Symbol.LabelAnnotation.None,
|
||||
false, formatter);
|
||||
}
|
||||
|
||||
if (operandLen == 1) {
|
||||
@ -864,10 +860,6 @@ namespace SourceGen {
|
||||
if ((flags & FormatNumericOpFlags.OmitLabelPrefixSuffix) == 0) {
|
||||
symLabel = Symbol.ConvertLabelForDisplay(symLabel, sym.LabelAnno,
|
||||
true, formatter);
|
||||
} else {
|
||||
// TODO(xyzzy): remapper will handle this
|
||||
symLabel = Symbol.ConvertLabelForDisplay(symLabel, Symbol.LabelAnnotation.None,
|
||||
false, formatter);
|
||||
}
|
||||
|
||||
if (operandLen == 1) {
|
||||
@ -965,10 +957,6 @@ namespace SourceGen {
|
||||
if ((flags & FormatNumericOpFlags.OmitLabelPrefixSuffix) == 0) {
|
||||
symLabel = Symbol.ConvertLabelForDisplay(symLabel, sym.LabelAnno,
|
||||
true, formatter);
|
||||
} else {
|
||||
// TODO(xyzzy): remapper will handle this
|
||||
symLabel = Symbol.ConvertLabelForDisplay(symLabel, Symbol.LabelAnnotation.None,
|
||||
false, formatter);
|
||||
}
|
||||
|
||||
int adjustment;
|
||||
|
@ -96,6 +96,7 @@ limitations under the License.
|
||||
<system:String x:Key="str_InfoAutoFormat">Format (auto):</system:String>
|
||||
<system:String x:Key="str_InfoCustomFormat">Format:</system:String>
|
||||
<system:String x:Key="str_InfoDefaultFormat">Format: default</system:String>
|
||||
<system:String x:Key="str_InfoLabelDescrFmt">Label type: {0}</system:String>
|
||||
<system:String x:Key="str_InfoLineSumNonFmt">Line {0}: {1}</system:String>
|
||||
<system:String x:Key="str_InfoLineSumPluralFmt">Line {0}: {1} bytes of {2}</system:String>
|
||||
<system:String x:Key="str_InfoLineSumSingularFmt">Line {0}: {1} byte of {2}</system:String>
|
||||
|
@ -173,6 +173,8 @@ namespace SourceGen.Res {
|
||||
(string)Application.Current.FindResource("str_InfoCustomFormat");
|
||||
public static string INFO_DEFAULT_FORMAT =
|
||||
(string)Application.Current.FindResource("str_InfoDefaultFormat");
|
||||
public static string INFO_LABEL_DESCR_FMT =
|
||||
(string)Application.Current.FindResource("str_InfoLabelDescrFmt");
|
||||
public static string INFO_LINE_SUM_NON_FMT =
|
||||
(string)Application.Current.FindResource("str_InfoLineSumNonFmt");
|
||||
public static string INFO_LINE_SUM_PLURAL_FMT =
|
||||
|
Binary file not shown.
@ -1,6 +1,6 @@
|
||||
### 6502bench SourceGen dis65 v1.0 ###
|
||||
{
|
||||
"_ContentVersion":3,"FileDataLength":154,"FileDataCrc32":-1096720699,"ProjectProps":{
|
||||
"_ContentVersion":3,"FileDataLength":180,"FileDataCrc32":381569947,"ProjectProps":{
|
||||
"CpuName":"6502","IncludeUndocumentedInstr":false,"TwoByteBrk":false,"EntryFlags":32702671,"AutoLabelStyle":"Simple","AnalysisParams":{
|
||||
"AnalyzeUncategorizedData":true,"DefaultTextScanMode":"LowHighAscii","MinCharsForString":4,"SeekNearbyTargets":true,"SmartPlpHandling":true},
|
||||
"PlatformSymbolFileIdentifiers":[],"ExtensionScriptFileIdentifiers":[],"ProjectSyms":{
|
||||
@ -69,7 +69,7 @@
|
||||
"105":{
|
||||
"Label":"anno","Value":4201,"Source":"User","Type":"LocalOrGlobalAddr","LabelAnno":"Uncertain"},
|
||||
"107":{
|
||||
"Label":"T106B","Value":4203,"Source":"User","Type":"LocalOrGlobalAddr","LabelAnno":"Generated"},
|
||||
"Label":"T106B","Value":4203,"Source":"User","Type":"GlobalAddr","LabelAnno":"Generated"},
|
||||
"115":{
|
||||
"Label":"skip","Value":4211,"Source":"User","Type":"NonUniqueLocalAddr","LabelAnno":"None"},
|
||||
"116":{
|
||||
@ -95,7 +95,17 @@
|
||||
"146":{
|
||||
"Label":"brl","Value":4242,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||
"149":{
|
||||
"Label":"LDAL","Value":4245,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"}},
|
||||
"Label":"LDAL","Value":4245,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||
"160":{
|
||||
"Label":"plain","Value":4256,"Source":"User","Type":"NonUniqueLocalAddr","LabelAnno":"None"},
|
||||
"163":{
|
||||
"Label":"plain","Value":4259,"Source":"User","Type":"NonUniqueLocalAddr","LabelAnno":"None"},
|
||||
"166":{
|
||||
"Label":"global8","Value":4262,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||
"169":{
|
||||
"Label":"X_under1","Value":4265,"Source":"User","Type":"GlobalAddr","LabelAnno":"None"},
|
||||
"172":{
|
||||
"Label":"X__dub1","Value":4268,"Source":"User","Type":"NonUniqueLocalAddr","LabelAnno":"None"}},
|
||||
"OperandFormats":{
|
||||
"58":{
|
||||
"Length":3,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||
@ -146,4 +156,21 @@
|
||||
"Length":2,"Format":"NumericLE","SubFormat":"Symbol","SymbolRef":{
|
||||
"Label":"T106B","Part":"Low"}}},
|
||||
"LvTables":{
|
||||
}}
|
||||
"154":{
|
||||
"Variables":[{
|
||||
"DataDescriptor":{
|
||||
"Length":1,"Format":"NumericLE","SubFormat":"Hex","SymbolRef":null},
|
||||
"Comment":"","HasWidth":true,"Direction":"ReadWrite","MultiMask":null,"Label":"plain","Value":17,"Source":"Variable","Type":"ExternalAddr","LabelAnno":"None"},
|
||||
{
|
||||
"DataDescriptor":{
|
||||
"Length":1,"Format":"NumericLE","SubFormat":"Hex","SymbolRef":null},
|
||||
"Comment":"","HasWidth":true,"Direction":"ReadWrite","MultiMask":null,"Label":"_under1","Value":18,"Source":"Variable","Type":"ExternalAddr","LabelAnno":"None"},
|
||||
{
|
||||
"DataDescriptor":{
|
||||
"Length":1,"Format":"NumericLE","SubFormat":"Hex","SymbolRef":null},
|
||||
"Comment":"","HasWidth":true,"Direction":"ReadWrite","MultiMask":null,"Label":"__dub1","Value":19,"Source":"Variable","Type":"ExternalAddr","LabelAnno":"None"}],"ClearPrevious":false},
|
||||
"175":{
|
||||
"Variables":[{
|
||||
"DataDescriptor":{
|
||||
"Length":1,"Format":"NumericLE","SubFormat":"Hex","SymbolRef":null},
|
||||
"Comment":"","HasWidth":true,"Direction":"ReadWrite","MultiMask":null,"Label":"_under1","Value":34,"Source":"Variable","Type":"ExternalAddr","LabelAnno":"None"}],"ClearPrevious":false}}}
|
||||
|
@ -28,14 +28,14 @@ CONST_ZERO_VAR .var $f0
|
||||
sta $f1,s
|
||||
eor 0
|
||||
ora 240,s
|
||||
PROJ_ZERO_DUP1 .var $10 ;clash with project symbol
|
||||
DPCODE_DUP1 .var $80 ;clash with user label
|
||||
PROJ_ZERO_1 .var $10 ;clash with project symbol
|
||||
DPCODE_1 .var $80 ;clash with user label
|
||||
lda VAR_ZERO
|
||||
lda VAR_ZERO+1
|
||||
lda VAR_TWO
|
||||
lda VAR_THREE
|
||||
lda $04
|
||||
lda PROJ_ZERO_DUP1
|
||||
lda PROJ_ZERO_1
|
||||
lda $11
|
||||
lda DPCODE
|
||||
ldx PROJ_ZERO
|
||||
|
@ -23,14 +23,14 @@ PROJ_ONE equ $01 ;project addr
|
||||
sta $f1,S
|
||||
eor 0
|
||||
ora 240,S
|
||||
]PROJ_ZERO_DUP1 equ $10 ;clash with project symbol
|
||||
]DPCODE_DUP1 equ $80 ;clash with user label
|
||||
]PROJ_ZERO_1 equ $10 ;clash with project symbol
|
||||
]DPCODE_1 equ $80 ;clash with user label
|
||||
lda ]VAR_ZERO
|
||||
lda ]VAR_ZERO+1
|
||||
lda ]VAR_TWO
|
||||
lda ]VAR_THREE
|
||||
lda $04
|
||||
lda ]PROJ_ZERO_DUP1
|
||||
lda ]PROJ_ZERO_1
|
||||
lda $11
|
||||
lda DPCODE
|
||||
ldx PROJ_ZERO
|
||||
|
@ -31,8 +31,8 @@ PROJ_ONE = $01 ;project addr
|
||||
.VAR_ZERO = $00
|
||||
.VAR_TWO = $02
|
||||
.VAR_THREE = $03
|
||||
.PROJ_ZERO_DUP1 = $10 ;clash with project symbol
|
||||
.DPCODE_DUP1 = $80 ;clash with user label
|
||||
.PROJ_ZERO_1 = $10 ;clash with project symbol
|
||||
.DPCODE_1 = $80 ;clash with user label
|
||||
.CONST_ZERO_VAR = $f0
|
||||
lda .VAR_ZERO
|
||||
lda .VAR_ZERO+1
|
||||
@ -41,12 +41,12 @@ PROJ_ONE = $01 ;project addr
|
||||
.VAR_ZERO = $00
|
||||
.VAR_TWO = $02
|
||||
.VAR_THREE = $03
|
||||
.PROJ_ZERO_DUP1 = $10 ;clash with project symbol
|
||||
.DPCODE_DUP1 = $80 ;clash with user label
|
||||
.PROJ_ZERO_1 = $10 ;clash with project symbol
|
||||
.DPCODE_1 = $80 ;clash with user label
|
||||
.CONST_ZERO_VAR = $f0
|
||||
lda .VAR_THREE
|
||||
lda $04
|
||||
lda .PROJ_ZERO_DUP1
|
||||
lda .PROJ_ZERO_1
|
||||
lda $11
|
||||
lda+1 DPCODE
|
||||
!zone Z00002c
|
||||
|
@ -27,14 +27,14 @@ CONST_ZERO_VAR .set $f0
|
||||
sta $f1,S
|
||||
eor 0
|
||||
ora 240,S
|
||||
PROJ_ZERO_DUP1 .set $10 ;clash with project symbol
|
||||
DPCODE_DUP1 .set $80 ;clash with user label
|
||||
PROJ_ZERO_1 .set $10 ;clash with project symbol
|
||||
DPCODE_1 .set $80 ;clash with user label
|
||||
lda VAR_ZERO
|
||||
lda VAR_ZERO+1
|
||||
lda VAR_TWO
|
||||
lda VAR_THREE
|
||||
lda $04
|
||||
lda PROJ_ZERO_DUP1
|
||||
lda PROJ_ZERO_1
|
||||
lda $11
|
||||
lda z:DPCODE
|
||||
ldx PROJ_ZERO
|
||||
|
@ -68,12 +68,12 @@ X___ ldx #$41
|
||||
_X__ dex
|
||||
bne _X__
|
||||
nop
|
||||
_anno lda #$42
|
||||
_T106B lda _anno
|
||||
anno lda #$42
|
||||
T106B lda anno
|
||||
clc
|
||||
bcc _skip
|
||||
|
||||
.word _T106B
|
||||
.word T106B
|
||||
|
||||
_skip nop
|
||||
JMP1 lda JMP1
|
||||
@ -91,5 +91,21 @@ LDAL .byte $af
|
||||
.byte $95
|
||||
.byte $10
|
||||
.byte $00
|
||||
nop
|
||||
plain_1 .var $11
|
||||
X_under1_1 .var $12
|
||||
X__dub1 .var $13
|
||||
lda plain_1
|
||||
lda X_under1_1
|
||||
lda X__dub1
|
||||
_plain lda _plain
|
||||
plain lda plain
|
||||
global8 dex
|
||||
bne plain
|
||||
X_under1 lda X_under1
|
||||
_X__dub1 lda _X__dub1
|
||||
X_under1_1 .var $22
|
||||
lda plain_1
|
||||
lda X_under1_1
|
||||
rts
|
||||
|
||||
|
@ -67,12 +67,12 @@ ___ ldx #$41
|
||||
:__ dex
|
||||
bne :__
|
||||
nop
|
||||
:anno lda #$42
|
||||
:T106B lda :anno
|
||||
anno lda #$42
|
||||
T106B lda anno
|
||||
clc
|
||||
bcc :skip
|
||||
|
||||
dw :T106B
|
||||
dw T106B
|
||||
|
||||
:skip nop
|
||||
JMP lda JMP
|
||||
@ -90,5 +90,21 @@ LDAL dfb $af
|
||||
dfb $95
|
||||
dfb $10
|
||||
dfb $00
|
||||
nop
|
||||
]plain_1 equ $11
|
||||
]_under1 equ $12
|
||||
]__dub1 equ $13
|
||||
lda ]plain_1
|
||||
lda ]_under1
|
||||
lda ]__dub1
|
||||
:plain lda :plain
|
||||
plain lda plain
|
||||
global8 dex
|
||||
bne plain
|
||||
X_under1 lda X_under1
|
||||
:X__dub1 lda :X__dub1
|
||||
]_under1 equ $22
|
||||
lda ]plain_1
|
||||
lda ]_under1
|
||||
rts
|
||||
|
||||
|
@ -68,12 +68,12 @@ ___ ldx #$41
|
||||
@__ dex
|
||||
bne @__
|
||||
nop
|
||||
@anno lda #$42
|
||||
@T106B lda @anno
|
||||
anno lda #$42
|
||||
T106B lda anno
|
||||
clc
|
||||
bcc @skip
|
||||
|
||||
!word @T106B
|
||||
!word T106B
|
||||
|
||||
@skip nop
|
||||
JMP1 lda JMP1
|
||||
@ -91,5 +91,25 @@ LDAL !byte $af
|
||||
!byte $95
|
||||
!byte $10
|
||||
!byte $00
|
||||
nop
|
||||
!zone Z00009a
|
||||
.plain_1 = $11
|
||||
._under1 = $12
|
||||
.__dub1 = $13
|
||||
lda .plain_1
|
||||
lda ._under1
|
||||
lda .__dub1
|
||||
@plain lda @plain
|
||||
plain lda plain
|
||||
global8 dex
|
||||
bne plain
|
||||
X_under1 lda X_under1
|
||||
@X__dub1 lda @X__dub1
|
||||
!zone Z0000af
|
||||
.plain_1 = $11
|
||||
.__dub1 = $13
|
||||
._under1 = $22
|
||||
lda .plain_1
|
||||
lda ._under1
|
||||
rts
|
||||
|
||||
|
@ -69,12 +69,12 @@ ___: ldx #$41
|
||||
@__: dex
|
||||
bne @__
|
||||
nop
|
||||
@anno: lda #$42
|
||||
@T106B: lda @anno
|
||||
anno: lda #$42
|
||||
T106B: lda anno
|
||||
clc
|
||||
bcc @skip
|
||||
|
||||
.word @T106B
|
||||
.word T106B
|
||||
|
||||
@skip: nop
|
||||
JMP1: lda JMP1
|
||||
@ -92,5 +92,21 @@ LDAL: .byte $af
|
||||
.byte $95
|
||||
.byte $10
|
||||
.byte $00
|
||||
nop
|
||||
plain_1 .set $11
|
||||
_under1 .set $12
|
||||
__dub1 .set $13
|
||||
lda plain_1
|
||||
lda _under1
|
||||
lda __dub1
|
||||
@plain: lda @plain
|
||||
plain: lda plain
|
||||
global8: dex
|
||||
bne plain
|
||||
X_under1: lda X_under1
|
||||
@X__dub1: lda @X__dub1
|
||||
_under1 .set $22
|
||||
lda plain_1
|
||||
lda _under1
|
||||
rts
|
||||
|
||||
|
@ -1,7 +1,7 @@
|
||||
# 6502bench SourceGen generated linker script for 2023-non-unique-labels
|
||||
MEMORY {
|
||||
MAIN: file=%O, start=%S, size=65536;
|
||||
# MEM000: file=%O, start=$1000, size=154;
|
||||
# MEM000: file=%O, start=$1000, size=180;
|
||||
}
|
||||
SEGMENTS {
|
||||
CODE: load=MAIN, type=rw;
|
||||
|
@ -109,17 +109,40 @@ anno1 lda anno ;NOTE: do not label, let table gen do it
|
||||
; Semi-related: test opcode name labels (which are illegal for assemblers
|
||||
; other than Merlin 32). We're configured for plain 6502, so it should
|
||||
; remap some but not others.
|
||||
JMP lda JMP ;EDIT set label (should be remapped)
|
||||
JMP lda JMP ;EDIT set label (becomes JMP1 on non-Merlin)
|
||||
JMP0 lda JMP0 ;EDIT set label
|
||||
JMP1 lda JMP1 ;EDIT set label
|
||||
JMP1 lda JMP1 ;EDIT set label (becomes JMP11 on non-Merlin)
|
||||
:JMP lda :JMP ;EDIT set label
|
||||
:JMP0 lda :JMP0 ;EDIT set label
|
||||
:JMP1 lda :JMP1 ;EDIT set label
|
||||
:JMP_ lda :JMP_ ;EDIT set label :JMP
|
||||
:JMP_ lda :JMP_ ;EDIT set label :JMP (should become :JMP2)
|
||||
jmp lda jmp ;EDIT set label
|
||||
Jmp lda Jmp ;EDIT set label
|
||||
BRA lda BRA ;EDIT set label (should NOT be remapped)
|
||||
brl lda brl ;EDIT set label (should NOT be remapped)
|
||||
LDAL ldal LDAL ;EDIT set label (should NOT be remapped)
|
||||
|
||||
; Test local-variable interaction. The goal here is to try to trick the
|
||||
; code generator into creating duplicate labels.
|
||||
global7 nop
|
||||
]plain equ $11 ;EDIT create local var table
|
||||
]_under1 equ $12
|
||||
]__dub1 equ $13
|
||||
lda ]plain
|
||||
lda ]_under1
|
||||
lda ]__dub1
|
||||
|
||||
:plain0 lda :plain0 ;EDIT: set label :plain
|
||||
plain1 lda plain1 ;EDIT: set label :plain (should uniquify to plain)
|
||||
global8 dex ; (which will then clash with the local var)
|
||||
bne plain1
|
||||
|
||||
X_under1 lda X_under1 ;EDIT: set label (global) (trying to clash on 64tass)
|
||||
|
||||
:X__dub1 lda :X__dub1 ;EDIT: set label
|
||||
|
||||
]_under1 equ $22 ;EDIT: create local var table (don't clear)
|
||||
lda ]plain
|
||||
lda ]_under1
|
||||
|
||||
rts
|
||||
|
@ -272,10 +272,10 @@ namespace SourceGen {
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns the number of symbols in the table.
|
||||
/// The number of symbols in the table.
|
||||
/// </summary>
|
||||
public int Count() {
|
||||
return mSymbols.Count;
|
||||
public int Count {
|
||||
get { return mSymbols.Count; }
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
|
@ -609,8 +609,10 @@ namespace SourceGen.Tests {
|
||||
if (!FileUtil.CompareTextFiles(path, compareName, out int firstDiffLine,
|
||||
out string line1, out string line2)) {
|
||||
ReportErrMsg("file '" + fileName + "' differs on line " + firstDiffLine);
|
||||
Debug.WriteLine("File #1: " + line1);
|
||||
Debug.WriteLine("File #2: " + line2);
|
||||
|
||||
Debug.WriteLine("Difference on line " + firstDiffLine);
|
||||
Debug.WriteLine(" generated: " + line1);
|
||||
Debug.WriteLine(" expected : " + line2);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -1127,7 +1127,7 @@ namespace SourceGen.WpfGui {
|
||||
LV_MATCH_FOUND_ADDRESS : LV_MATCH_FOUND_CONSTANT;
|
||||
|
||||
LocalVariableLookup lvLookup =
|
||||
new LocalVariableLookup(mProject.LvTables, mProject, false);
|
||||
new LocalVariableLookup(mProject.LvTables, mProject, null, false, false);
|
||||
|
||||
// If the operand is already a local variable, use whichever one the
|
||||
// analyzer found.
|
||||
|
@ -160,7 +160,14 @@ namespace SourceGen.WpfGui {
|
||||
IsNonUniqueChecked = true;
|
||||
break;
|
||||
case Symbol.Type.LocalOrGlobalAddr:
|
||||
IsLocalChecked = true;
|
||||
if (LabelSym.SymbolSource == Symbol.Source.Auto ||
|
||||
LabelSym.LabelAnno == Symbol.LabelAnnotation.Generated) {
|
||||
// Default to global, otherwise you get different behavior when
|
||||
// adding a new label vs. replacing an auto or generated label.
|
||||
IsGlobalChecked = true;
|
||||
} else {
|
||||
IsLocalChecked = true;
|
||||
}
|
||||
break;
|
||||
case Symbol.Type.GlobalAddr:
|
||||
IsGlobalChecked = true;
|
||||
|
@ -499,8 +499,9 @@ namespace SourceGen.WpfGui {
|
||||
Symbol tmpSym = AutoLabel.GenerateUniqueForAddress(addr,
|
||||
mProject.SymbolTable, "T");
|
||||
// tmpSym was returned as an auto-label, make it a user label instead
|
||||
// (with global scope)
|
||||
tmpSym = new Symbol(tmpSym.Label, tmpSym.Value, Symbol.Source.User,
|
||||
Symbol.Type.LocalOrGlobalAddr, Symbol.LabelAnnotation.Generated);
|
||||
Symbol.Type.GlobalAddr, Symbol.LabelAnnotation.Generated);
|
||||
newLabels[targetOffset] = tmpSym; // overwrites previous
|
||||
targetLabel = tmpSym.Label;
|
||||
AddPreviewItem(addr, targetOffset, "(+) " + targetLabel);
|
||||
|
@ -692,6 +692,7 @@ limitations under the License.
|
||||
<RowDefinition Height="*" MinHeight="50"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<!-- symbols panel -->
|
||||
<GroupBox Grid.Row="0" Header="Symbols">
|
||||
<Grid>
|
||||
<Grid.RowDefinitions>
|
||||
@ -738,6 +739,7 @@ limitations under the License.
|
||||
</Grid>
|
||||
</GroupBox>
|
||||
|
||||
<!-- info panel -->
|
||||
<GroupBox Grid.Row="2" Header="Info">
|
||||
<ScrollViewer>
|
||||
<Grid>
|
||||
@ -747,6 +749,7 @@ limitations under the License.
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
<RowDefinition Height="Auto"/>
|
||||
</Grid.RowDefinitions>
|
||||
|
||||
<TextBlock Grid.Row="0" TextWrapping="Wrap"
|
||||
@ -754,8 +757,10 @@ limitations under the License.
|
||||
<TextBlock Grid.Row="1" TextWrapping="Wrap" Foreground="DarkGray"
|
||||
Text="{Binding InfoOffsetText, FallbackValue=[offset: +123456]}"
|
||||
Visibility="{Binding InfoShowDebug, Converter={StaticResource BoolToVis}}"/>
|
||||
<TextBlock Grid.Row="2" TextWrapping="Wrap"
|
||||
Text="{Binding InfoLabelDescrText, FallbackValue=Label FUBAR (local)}"/>
|
||||
|
||||
<StackPanel Grid.Row="2"
|
||||
<StackPanel Grid.Row="3"
|
||||
Visibility="{Binding InfoFormatShowDashes, Converter={StaticResource BoolToVis}}">
|
||||
<!-- For some reason putting the Visibility on the Border causes the
|
||||
border to disappear even when it's supposed to be Visible. So I'm
|
||||
@ -775,16 +780,16 @@ limitations under the License.
|
||||
Text="{Binding InfoFormatText, FallbackValue=Format: good stuff}"/>
|
||||
</Border>
|
||||
</StackPanel>
|
||||
<Border Grid.Row="2" BorderThickness="1" Padding="1" Margin="0,2"
|
||||
<Border Grid.Row="3" BorderThickness="1" Padding="1" Margin="0,2"
|
||||
BorderBrush="{Binding InfoFormatBoxBrush}"
|
||||
Visibility="{Binding InfoFormatShowSolid, Converter={StaticResource BoolToVis}}">
|
||||
<TextBlock TextWrapping="Wrap" Text="{Binding InfoFormatText}"/>
|
||||
</Border>
|
||||
|
||||
<TextBox Grid.Row="3" Text="{Binding InfoPanelDetail1}" IsReadOnly="True"
|
||||
<TextBox Grid.Row="4" Text="{Binding InfoPanelDetail1}" IsReadOnly="True"
|
||||
TextWrapping="Wrap" BorderThickness="0"/>
|
||||
|
||||
<!--<TextBox Grid.Row="4" Text="{Binding InfoPanelMonoContents}" IsReadOnly="True"
|
||||
<!--<TextBox Grid.Row="5" Text="{Binding InfoPanelMonoContents}" IsReadOnly="True"
|
||||
FontFamily="{StaticResource GeneralMonoFont}"
|
||||
TextWrapping="Wrap" BorderThickness="0"/>-->
|
||||
</Grid>
|
||||
|
@ -1778,6 +1778,15 @@ namespace SourceGen.WpfGui {
|
||||
}
|
||||
private string mInfoOffsetText;
|
||||
|
||||
/// <summary>
|
||||
/// Text for the label description.
|
||||
/// </summary>
|
||||
public string InfoLabelDescrText {
|
||||
get { return mInfoLabelDescrText; }
|
||||
set { mInfoLabelDescrText = value; OnPropertyChanged(); }
|
||||
}
|
||||
private string mInfoLabelDescrText;
|
||||
|
||||
public SolidColorBrush InfoFormatBoxBrush {
|
||||
get { return mInfoFormatBoxBrush; }
|
||||
set { mInfoFormatBoxBrush = value; OnPropertyChanged(); }
|
||||
@ -1825,8 +1834,13 @@ namespace SourceGen.WpfGui {
|
||||
//private string mInfoPanelMonoContents;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Clears the contents of the info panel. Call this whenever the contents have
|
||||
/// been updated.
|
||||
/// </summary>
|
||||
public void ClearInfoPanel() {
|
||||
InfoLineDescrText = InfoOffsetText = InfoFormatText = InfoPanelDetail1 = string.Empty;
|
||||
InfoLineDescrText = InfoOffsetText = InfoLabelDescrText = InfoFormatText =
|
||||
InfoPanelDetail1 = string.Empty;
|
||||
InfoFormatShowDashes = InfoFormatShowSolid = false;
|
||||
}
|
||||
|
||||
|
Loading…
Reference in New Issue
Block a user