diff --git a/Asm65/Formatter.cs b/Asm65/Formatter.cs
index fbbeb90..f836dc3 100644
--- a/Asm65/Formatter.cs
+++ b/Asm65/Formatter.cs
@@ -68,6 +68,8 @@ namespace Asm65 {
public string mForceLongOpcodeSuffix;
public string mForceLongOperandPrefix;
+ public string mLocalVariableLablePrefix; // Merlin 32 puts ']' before var names
+
public string mEndOfLineCommentDelimiter; // usually ';'
public string mFullLineCommentDelimiterBase; // usually ';' or '*', WITHOUT extra space
public string mBoxLineCommentDelimiter; // usually blank or ';'
@@ -610,6 +612,17 @@ namespace Asm65 {
}
}
+ ///
+ /// Formats a local variable label, prepending a prefix if needed.
+ ///
+ public string FormatVariableLabel(string label) {
+ if (!string.IsNullOrEmpty(mFormatConfig.mLocalVariableLablePrefix)) {
+ return mFormatConfig.mLocalVariableLablePrefix + label;
+ } else {
+ return label;
+ }
+ }
+
///
/// Formats an adjustment, as "+decimal" or "-decimal". If no adjustment
/// is required, an empty string is returned.
diff --git a/Asm65/OpDef.cs b/Asm65/OpDef.cs
index a795bd5..4d2f074 100644
--- a/Asm65/OpDef.cs
+++ b/Asm65/OpDef.cs
@@ -217,6 +217,16 @@ namespace Asm65 {
}
}
+ ///
+ /// True if the instruction's operand is a stack-relative offset.
+ ///
+ public bool IsStackRelInstruction {
+ get {
+ return AddrMode == AddressMode.StackRel ||
+ AddrMode == AddressMode.StackRelIndIndexY;
+ }
+ }
+
///
/// True if the operand's width is uniquely determined by the opcode mnemonic, even
/// if the operation supports operands with varying widths.
diff --git a/SourceGen/AsmGen/AsmMerlin32.cs b/SourceGen/AsmGen/AsmMerlin32.cs
index 1e36550..13c9e5f 100644
--- a/SourceGen/AsmGen/AsmMerlin32.cs
+++ b/SourceGen/AsmGen/AsmMerlin32.cs
@@ -94,7 +94,7 @@ namespace SourceGen.AsmGen {
private static PseudoOp.PseudoOpNames sDataOpNames =
new PseudoOp.PseudoOpNames(new Dictionary {
{ "EquDirective", "equ" },
- { "VarDirective", "~=" }, // not really
+ { "VarDirective", "equ" },
{ "OrgDirective", "org" },
//RegWidthDirective
{ "DefineData1", "dfb" },
@@ -158,6 +158,7 @@ namespace SourceGen.AsmGen {
config.mForceDirectOperandPrefix = string.Empty;
config.mForceAbsOperandPrefix = string.Empty;
config.mForceLongOperandPrefix = string.Empty;
+ config.mLocalVariableLablePrefix = "]";
config.mEndOfLineCommentDelimiter = ";";
config.mFullLineCommentDelimiterBase = ";";
config.mBoxLineCommentDelimiter = string.Empty;
@@ -400,7 +401,8 @@ namespace SourceGen.AsmGen {
// IGenerator
public void OutputVarDirective(string name, string valueStr, string comment) {
- OutputLine("]" + name, SourceFormatter.FormatPseudoOp(sDataOpNames.EquDirective),
+ OutputLine(SourceFormatter.FormatVariableLabel(name),
+ SourceFormatter.FormatPseudoOp(sDataOpNames.VarDirective),
valueStr, SourceFormatter.FormatEolComment(comment));
}
diff --git a/SourceGen/AsmGen/AsmTass64.cs b/SourceGen/AsmGen/AsmTass64.cs
index a784926..13b7ccc 100644
--- a/SourceGen/AsmGen/AsmTass64.cs
+++ b/SourceGen/AsmGen/AsmTass64.cs
@@ -589,9 +589,11 @@ namespace SourceGen.AsmGen {
// IGenerator
public void OutputLine(string label, string opcode, string operand, string comment) {
- // Break the line if the label is long and it's not a .EQ directive.
+ // Break the line if the label is long and it's not a .EQ/.VAR directive.
if (!string.IsNullOrEmpty(label) &&
!string.Equals(opcode, sDataOpNames.EquDirective,
+ StringComparison.InvariantCultureIgnoreCase) &&
+ !string.Equals(opcode, sDataOpNames.VarDirective,
StringComparison.InvariantCultureIgnoreCase)) {
if (mLongLabelNewLine && label.Length >= mColumnWidths[0]) {
diff --git a/SourceGen/AsmGen/GenCommon.cs b/SourceGen/AsmGen/GenCommon.cs
index 5fbec47..6d321a9 100644
--- a/SourceGen/AsmGen/GenCommon.cs
+++ b/SourceGen/AsmGen/GenCommon.cs
@@ -39,6 +39,10 @@ namespace SourceGen.AsmGen {
bool doAddCycles = gen.Settings.GetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS, false);
+ // TODO: switch uniqueness based on quirk
+ LocalVariableLookup lvLookup = new LocalVariableLookup(proj.LvTables,
+ proj.SymbolTable, proj);
+
GenerateHeader(gen, sw);
// Used for M/X flag tracking.
@@ -69,6 +73,11 @@ namespace SourceGen.AsmGen {
gen.OutputOrgDirective(offset, orgAddr);
}
+ List lvars = lvLookup.GetVariablesDefinedAtOffset(offset);
+ if (lvars != null) {
+ GenerateLocalVariables(gen, sw, lvars);
+ }
+
if (attr.IsInstructionStart) {
// Generate M/X reg width directive, if necessary.
// NOTE: we can suppress the initial directive if we know what the
@@ -96,7 +105,7 @@ namespace SourceGen.AsmGen {
}
// Output instruction.
- GenerateInstruction(gen, sw, offset, len, doAddCycles);
+ GenerateInstruction(gen, sw, lvLookup, offset, len, doAddCycles);
if (attr.DoesNotContinue) {
gen.OutputLine(string.Empty);
@@ -139,7 +148,7 @@ namespace SourceGen.AsmGen {
// Format symbols.
foreach (DefSymbol defSym in proj.ActiveDefSymbolList) {
- // Use an operand length of 1 so things are shown as concisely as possible.
+ // 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.None);
@@ -152,8 +161,22 @@ namespace SourceGen.AsmGen {
}
}
- private static void GenerateInstruction(IGenerator gen, StreamWriter sw, int offset,
- int instrBytes, bool doAddCycles) {
+ private static void GenerateLocalVariables(IGenerator gen, StreamWriter sw,
+ List vars) {
+ foreach (DefSymbol defSym in vars) {
+ DisasmProject proj = gen.Project;
+ Formatter formatter = gen.SourceFormatter;
+
+ // Use an operand length of 1 so values are shown as concisely as possible.
+ string valueStr = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable,
+ null, defSym.DataDescriptor, defSym.Value, 1,
+ PseudoOp.FormatNumericOpFlags.None);
+ gen.OutputVarDirective(defSym.Label, valueStr, defSym.Comment);
+ }
+ }
+
+ private static void GenerateInstruction(IGenerator gen, StreamWriter sw,
+ LocalVariableLookup lvLookup, int offset, int instrBytes, bool doAddCycles) {
DisasmProject proj = gen.Project;
Formatter formatter = gen.SourceFormatter;
byte[] data = proj.FileData;
@@ -241,8 +264,8 @@ namespace SourceGen.AsmGen {
gen.UpdateCharacterEncoding(attr.DataDescriptor);
}
formattedOperand = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable,
- gen.Localizer.LabelMap, attr.DataDescriptor,
- operandForSymbol, operandLen, opFlags);
+ lvLookup, gen.Localizer.LabelMap, attr.DataDescriptor,
+ offset, operandForSymbol, operandLen, opFlags);
}
} else {
// Show operand value in hex.
diff --git a/SourceGen/DisasmProject.cs b/SourceGen/DisasmProject.cs
index 75954ef..1090f45 100644
--- a/SourceGen/DisasmProject.cs
+++ b/SourceGen/DisasmProject.cs
@@ -911,76 +911,45 @@ namespace SourceGen {
/// for the benefit of uniqueness checks.
///
private void GenerateVariableRefs() {
- LocalVariableTable curTab = new LocalVariableTable();
-
- int nextLvtIndex, nextLvtOffset;
- if (LvTables.Count > 0) {
- nextLvtIndex = 0;
- nextLvtOffset = LvTables.Keys[0];
- } else {
- nextLvtIndex = -1;
- nextLvtOffset = FileData.Length;
- }
+ LocalVariableLookup lvLookup = new LocalVariableLookup(LvTables, SymbolTable, this);
for (int offset = 0; offset < FileData.Length; ) {
- // Have we reached the start of the next LV table?
- while (offset >= nextLvtOffset) {
- // We want to skip over any "hidden" tables. It's possible, if a bunch of
- // tables got collapsed inside a data area, that we need to skip more than one.
- if (offset == nextLvtOffset && mAnattribs[offset].IsStart) {
- //Debug.WriteLine("FOUND +" + offset.ToString("x6"));
- LocalVariableTable lvt = LvTables.Values[nextLvtIndex];
- if (lvt.ClearPrevious) {
- curTab.Clear();
+ // All entries also get added to the main SymbolTable. This is a little
+ // wonky because the symbol might already exist with a different value.
+ // So long as the previous thing was also a variable, it doesn't matter.
+ List vars = lvLookup.GetVariablesDefinedAtOffset(offset);
+ if (vars != null) {
+ foreach (DefSymbol defSym in vars) {
+ if (!SymbolTable.TryGetValue(defSym.Label, out Symbol sym)) {
+ SymbolTable[defSym.Label] = defSym;
+ } else if (!sym.IsVariable) {
+ // Shouldn't happen, and will cause trouble if we're not
+ // uniquifying names.
+ Debug.Assert(false,
+ "Found non-variable with var name in symbol table: " + sym);
}
-
- // Merge the new entries into the work table. This automatically
- // discards entries that clash.
- for (int i = 0; i < lvt.Count; i++) {
- curTab.AddOrReplace(lvt[i]);
- }
- //curTab.DebugDump();
-
- // All entries also get added to the main SymbolTable. This is a little
- // wonky because the symbol might already exist with a different value.
- // So long as the previous thing was also a variable, it doesn't matter.
- AddVariablesToSymbolTable(lvt);
- } else {
- // Either this wasn't an instruction/data start, or we passed this
- // one, which only happens for non-start items. Whatever the case,
- // we're going to ignore it.
- Debug.WriteLine("Ignoring LvTable +" + offset.ToString("x6"));
- }
- // Advance to next table.
- nextLvtIndex++;
- if (nextLvtIndex < LvTables.Keys.Count) {
- nextLvtOffset = LvTables.Keys[nextLvtIndex];
- } else {
- nextLvtOffset = FileData.Length;
}
}
Anattrib attr = mAnattribs[offset];
if (attr.IsInstructionStart && attr.DataDescriptor == null) {
OpDef op = CpuDef.GetOpDef(FileData[offset]);
+ DefSymbol defSym = null;
if (op.IsDirectPageInstruction) {
Debug.Assert(attr.OperandAddress == FileData[offset + 1]);
- DefSymbol defSym = curTab.GetByValueRange(attr.OperandAddress, 1,
+ defSym = lvLookup.GetSymbol(offset, FileData[offset + 1],
Symbol.Type.ExternalAddr);
- if (defSym != null) {
- mAnattribs[offset].DataDescriptor =
- FormatDescriptor.Create(attr.Length,
- new WeakSymbolRef(defSym.Label, WeakSymbolRef.Part.Low), false);
- }
- } else if (op.AddrMode == OpDef.AddressMode.StackRel ||
- op.AddrMode == OpDef.AddressMode.StackRelIndIndexY) {
- DefSymbol defSym = curTab.GetByValueRange(FileData[offset + 1], 1,
+ } else if (op.IsStackRelInstruction) {
+ defSym = lvLookup.GetSymbol(offset, FileData[offset + 1],
Symbol.Type.Constant);
- if (defSym != null) {
- mAnattribs[offset].DataDescriptor =
- FormatDescriptor.Create(attr.Length,
- new WeakSymbolRef(defSym.Label, WeakSymbolRef.Part.Low), false);
- }
+ }
+ if (defSym != null) {
+ WeakSymbolRef vref = new WeakSymbolRef(defSym.Label,
+ WeakSymbolRef.Part.Low, op.IsStackRelInstruction ?
+ WeakSymbolRef.LocalVariableType.StackRelConst :
+ WeakSymbolRef.LocalVariableType.DpAddr);
+ mAnattribs[offset].DataDescriptor =
+ FormatDescriptor.Create(attr.Length, vref, false);
}
}
@@ -993,15 +962,6 @@ namespace SourceGen {
}
}
- private void AddVariablesToSymbolTable(LocalVariableTable lvt) {
- for (int i = 0; i < lvt.Count; i++) {
- DefSymbol defSym = lvt[i];
- if (!SymbolTable.TryGetValue(defSym.Label, out Symbol sym)) {
- SymbolTable[defSym.Label] = defSym;
- }
- }
- }
-
///
/// Generates references to symbols in the project/platform symbol tables.
///
@@ -1048,7 +1008,7 @@ namespace SourceGen {
if (sym == null && (attr.OperandAddress & 0xffff) > 1 && checkNearby) {
sym = SymbolTable.FindAddressByValue(attr.OperandAddress - 2);
}
- if (sym != null) {
+ if (sym != null && !sym.IsVariable) {
mAnattribs[offset].DataDescriptor =
FormatDescriptor.Create(mAnattribs[offset].Length,
new WeakSymbolRef(sym.Label, WeakSymbolRef.Part.Low), false);
@@ -1126,6 +1086,8 @@ namespace SourceGen {
}
}
+ LocalVariableLookup lvLookup = new LocalVariableLookup(LvTables, null, this);
+
// Walk through the Anattrib array, adding xref entries to things referenced
// by the entity at the current offset.
for (int offset = 0; offset < mAnattribs.Length; ) {
@@ -1174,11 +1136,21 @@ namespace SourceGen {
if (adj == 0) {
hasZeroOffsetSym = true;
}
+ } else if (dfd.SymbolRef.IsVariable) {
+ DefSymbol defSym = lvLookup.GetSymbol(offset, dfd.SymbolRef);
+ if (defSym != null) {
+ int adj = 0;
+ if (operandOffset >= 0) {
+ adj = defSym.Value - operandOffset;
+ }
+ defSym.Xrefs.Add(
+ new XrefSet.Xref(offset, true, xrefType, accType, adj));
+ }
} else if (SymbolTable.TryGetValue(dfd.SymbolRef.Label, out Symbol sym)) {
- // Is this a reference to a project/platform symbol?
+ // Is this a reference to a project/platform symbol? We also handle
+ // local variables here.
if (sym.SymbolSource == Symbol.Source.Project ||
- sym.SymbolSource == Symbol.Source.Platform ||
- sym.SymbolSource == Symbol.Source.Variable) {
+ sym.SymbolSource == Symbol.Source.Platform) {
DefSymbol defSym = sym as DefSymbol;
int adj = 0;
if (operandOffset >= 0) {
@@ -1286,7 +1258,7 @@ namespace SourceGen {
ActiveDefSymbolList.Clear();
foreach (Symbol sym in SymbolTable) {
- if (!(sym is DefSymbol)) {
+ if (!(sym is DefSymbol) || sym.IsVariable) {
continue;
}
DefSymbol defSym = sym as DefSymbol;
diff --git a/SourceGen/FormatDescriptor.cs b/SourceGen/FormatDescriptor.cs
index bea50db..2dbc227 100644
--- a/SourceGen/FormatDescriptor.cs
+++ b/SourceGen/FormatDescriptor.cs
@@ -440,7 +440,11 @@ namespace SourceGen {
case SubType.Address:
return "Address";
case SubType.Symbol:
- return "Symbol \"" + SymbolRef.Label + "\"";
+ if (SymbolRef.IsVariable) {
+ return "Local var \"" + SymbolRef.Label + "\"";
+ } else {
+ return "Symbol \"" + SymbolRef.Label + "\"";
+ }
case SubType.Ascii:
return "Numeric, ASCII";
case SubType.HighAscii:
diff --git a/SourceGen/LineListGen.cs b/SourceGen/LineListGen.cs
index 9cae379..9c112f1 100644
--- a/SourceGen/LineListGen.cs
+++ b/SourceGen/LineListGen.cs
@@ -69,6 +69,11 @@ namespace SourceGen {
///
private FormattedOperandCache mFormattedLineCache;
+ ///
+ /// Local variable table data extractor.
+ ///
+ private LocalVariableLookup mLvLookup;
+
///
/// One of these per line of output in the display. It should be possible to draw
@@ -437,6 +442,8 @@ namespace SourceGen {
mFormattedLineCache = new FormattedOperandCache();
mShowCycleCounts = AppSettings.Global.GetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS,
false);
+ // TODO: remove SymbolTable -- don't need unique
+ mLvLookup = new LocalVariableLookup(mProject.LvTables, mProject.SymbolTable, mProject);
mDisplayList.ListGen = this;
}
@@ -886,6 +893,9 @@ namespace SourceGen {
Debug.Assert(startOffset >= 0);
Debug.Assert(endOffset >= startOffset);
+ // Assume variables may have changed.
+ mLvLookup.Reset();
+
// Find the previous status flags for M/X tracking.
StatusFlags prevFlags = StatusFlags.AllIndeterminate;
if (mProject.CpuDef.HasEmuFlag) {
@@ -1220,8 +1230,9 @@ namespace SourceGen {
PseudoOp.FormatNumericOpFlags.None);
formattedOperand = '#' + opstr1 + "," + '#' + opstr2;
} else {
- formattedOperand = PseudoOp.FormatNumericOperand(mFormatter, mProject.SymbolTable,
- null, attr.DataDescriptor, operandForSymbol, operandLen, opFlags);
+ formattedOperand = PseudoOp.FormatNumericOperand(mFormatter,
+ mProject.SymbolTable, mLvLookup, null, attr.DataDescriptor, offset,
+ operandForSymbol, operandLen, opFlags);
}
} else {
// Show operand value in hex.
@@ -1330,7 +1341,8 @@ namespace SourceGen {
null, defSym.DataDescriptor, defSym.Value, 1,
PseudoOp.FormatNumericOpFlags.None);
string comment = mFormatter.FormatEolComment(defSym.Comment);
- return FormattedParts.CreateEquDirective(defSym.Label,
+ return FormattedParts.CreateEquDirective(
+ mFormatter.FormatVariableLabel(defSym.Label),
mFormatter.FormatPseudoOp(mPseudoOpNames.VarDirective),
addrStr, comment);
}
diff --git a/SourceGen/LocalVariableLookup.cs b/SourceGen/LocalVariableLookup.cs
new file mode 100644
index 0000000..5272e4e
--- /dev/null
+++ b/SourceGen/LocalVariableLookup.cs
@@ -0,0 +1,199 @@
+/*
+ * Copyright 2019 faddenSoft
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+using System;
+using System.Collections.Generic;
+using System.Diagnostics;
+
+namespace SourceGen {
+ ///
+ /// Given a list of LocalVariableTables, this determines the mapping of values to symbols
+ /// at a specific offset.
+ ///
+ public class LocalVariableLookup {
+ ///
+ /// List of tables. The table's file offset is used as the key.
+ ///
+ private SortedList mLvTables;
+
+ ///
+ /// Table of symbols, used to ensure that all symbols are globally unique. Only used
+ /// when generating code for an assembler that doesn't support redefinable variables.
+ ///
+ private SymbolTable mSymbolTable;
+
+ ///
+ /// Reference to project, so we can query the Anattrib array to identify "hidden" tables.
+ ///
+ private DisasmProject mProject;
+
+ ///
+ /// Most recently processed offset.
+ ///
+ private int mRecentOffset;
+
+ ///
+ /// Symbols defined at mRecentOffset.
+ ///
+ private List mRecentSymbols;
+
+ ///
+ /// Cumulative symbols defined at the current offset.
+ ///
+ private LocalVariableTable mCurrentTable;
+
+ private int mNextLvtIndex;
+ private int mNextLvtOffset;
+
+
+ ///
+ /// Constructor.
+ ///
+ /// List of tables from the DisasmProject.
+ /// Full SymbolTable from the DisasmProject. Used to
+ /// generate globally unique symbol names. Pass null if uniqueness is not a
+ /// requirement.
+ /// Project reference.
+ public LocalVariableLookup(SortedList lvTables,
+ SymbolTable symbolTable, DisasmProject project) {
+ mLvTables = lvTables;
+ mSymbolTable = symbolTable;
+ mProject = project;
+
+ mCurrentTable = new LocalVariableTable();
+ Reset();
+ }
+
+ public void Reset() {
+ mRecentOffset = -1;
+ mRecentSymbols = null;
+ mCurrentTable.Clear();
+ if (mLvTables.Count == 0) {
+ mNextLvtIndex = -1;
+ mNextLvtOffset = mProject.FileDataLength;
+ } else {
+ mNextLvtIndex = 0;
+ mNextLvtOffset = mLvTables.Keys[0];
+ }
+ }
+
+ ///
+ /// Gets the symbol associated with the operand of an instruction.
+ ///
+ /// Offset of start of instruction.
+ /// Operand value.
+ /// Operand type. Should be ExternalAddress for DP ops, or
+ /// Constant for StackRel ops.
+ /// Symbol, or null if no match found.
+ public DefSymbol GetSymbol(int offset, int operandValue, Symbol.Type type) {
+ AdvanceToOffset(offset);
+
+ return mCurrentTable.GetByValueRange(operandValue, 1, type);
+ }
+
+ ///
+ /// Gets the symbol associated with a symbol reference.
+ ///
+ /// Offset of start of instruction.
+ /// Reference to symbol.
+ /// Symbol, or null if no match found.
+ public DefSymbol GetSymbol(int offset, WeakSymbolRef symRef) {
+ AdvanceToOffset(offset);
+
+ return mCurrentTable.GetByLabel(symRef.Label);
+ }
+
+ ///
+ /// Gets a LocalVariableTable that is the result of merging all tables up to this point.
+ ///
+ /// Target offset.
+ /// Combined table.
+ public LocalVariableTable GetMergedTableAtOffset(int offset) {
+ AdvanceToOffset(offset);
+ return mCurrentTable;
+ }
+
+ ///
+ /// Generates a list of variables defined at the specified offset, if a table is
+ /// associated with that offset.
+ ///
+ /// File data offset.
+ /// List of symbols, uniquified if desired, or null if no LocalVariableTable
+ /// exists at the specified offset.
+ public List GetVariablesDefinedAtOffset(int offset) {
+ AdvanceToOffset(offset);
+
+ if (mRecentOffset == offset) {
+ return mRecentSymbols;
+ }
+
+ return null;
+ }
+
+ ///
+ /// Updates internal state to reflect the state of the world at the specified offset.
+ ///
+ ///
+ /// When the offset is greater than or equal to its value on a previous call, we can
+ /// do an incremental update. If the offset moves backward, we have to reset and walk
+ /// forward again.
+ ///
+ /// Target offset.
+ private void AdvanceToOffset(int offset) {
+ if (mNextLvtIndex < 0) {
+ return;
+ }
+ if (offset < mRecentOffset) {
+ // We went backwards.
+ Reset();
+ }
+ while (mNextLvtOffset <= offset) {
+ if (!mProject.GetAnattrib(mNextLvtOffset).IsStart) {
+ // Hidden table, ignore it.
+ Debug.WriteLine("Ignoring LvTable at +" + mNextLvtOffset.ToString("x6"));
+ } else {
+ // Process this table.
+ LocalVariableTable lvt = mLvTables.Values[mNextLvtIndex];
+ if (lvt.ClearPrevious) {
+ mCurrentTable.Clear();
+ }
+
+ // Create a list for GetVariablesDefinedAtOffset
+ mRecentSymbols = new List();
+ mRecentOffset = mNextLvtOffset;
+
+ // Merge the new entries into the work table. This automatically
+ // discards entries that clash.
+ for (int i = 0; i < lvt.Count; i++) {
+ // TODO: uniquify
+ mCurrentTable.AddOrReplace(lvt[i]);
+
+ mRecentSymbols.Add(lvt[i]);
+ }
+
+ mCurrentTable.DebugDump();
+ }
+
+ // Update state to look for next table.
+ mNextLvtIndex++;
+ if (mNextLvtIndex < mLvTables.Keys.Count) {
+ mNextLvtOffset = mLvTables.Keys[mNextLvtIndex];
+ } else {
+ mNextLvtOffset = mProject.FileDataLength; // never reached
+ }
+ }
+ }
+ }
+}
diff --git a/SourceGen/LocalVariableTable.cs b/SourceGen/LocalVariableTable.cs
index 2a98cc4..168eb1c 100644
--- a/SourceGen/LocalVariableTable.cs
+++ b/SourceGen/LocalVariableTable.cs
@@ -159,7 +159,8 @@ namespace SourceGen {
/// not clash.
///
/// Value to compare.
- /// Width to check.
+ /// Width to check, useful when checking for collisions. When
+ /// doing a simple variable lookup, this should be set to 1.
/// One matching symbol, or null if none matched.
public DefSymbol GetByValueRange(int value, int width, Symbol.Type type) {
foreach (KeyValuePair kvp in mVarByLabel) {
@@ -181,7 +182,7 @@ namespace SourceGen {
Debug.Assert(false, "Unexpected symbol type " + newSym.SymbolType);
return;
}
- if (newSym.SymbolSource != Symbol.Source.Variable) {
+ if (!newSym.IsVariable) {
Debug.Assert(false, "Unexpected symbol source " + newSym.SymbolSource);
return;
}
diff --git a/SourceGen/MainController.cs b/SourceGen/MainController.cs
index 3e18ce2..4683e2f 100644
--- a/SourceGen/MainController.cs
+++ b/SourceGen/MainController.cs
@@ -435,6 +435,7 @@ namespace SourceGen {
mFormatterConfig = new Formatter.FormatConfig();
AsmGen.GenCommon.ConfigureFormatterFromSettings(AppSettings.Global,
ref mFormatterConfig);
+ //mFormatterConfig.mLocalVariableLablePrefix = "\u00a4"; // CURRENCY SIGN
mFormatterConfig.mEndOfLineCommentDelimiter = ";";
mFormatterConfig.mFullLineCommentDelimiterBase = ";";
mFormatterConfig.mBoxLineCommentDelimiter = string.Empty;
diff --git a/SourceGen/ProjectFile.cs b/SourceGen/ProjectFile.cs
index 36fb8de..99ba153 100644
--- a/SourceGen/ProjectFile.cs
+++ b/SourceGen/ProjectFile.cs
@@ -831,7 +831,7 @@ namespace SourceGen {
if (!CreateDefSymbol(serDef, contentVersion, report, out DefSymbol defSym)) {
return false;
}
- if (defSym.SymbolSource != Symbol.Source.Variable) {
+ if (!defSym.IsVariable) {
// not expected to happen
Debug.WriteLine("Found local variable with bad source: " +
defSym.SymbolSource);
diff --git a/SourceGen/PseudoOp.cs b/SourceGen/PseudoOp.cs
index 4ad47fa..613fe7a 100644
--- a/SourceGen/PseudoOp.cs
+++ b/SourceGen/PseudoOp.cs
@@ -332,14 +332,14 @@ namespace SourceGen {
case FormatDescriptor.Type.NumericLE:
po.Opcode = opNames.GetDefineData(length);
operand = RawData.GetWord(data, offset, length, false);
- po.Operand = FormatNumericOperand(formatter, symbolTable, labelMap, dfd,
- operand, length, FormatNumericOpFlags.None);
+ po.Operand = FormatNumericOperand(formatter, symbolTable, labelMap,
+ dfd, operand, length, FormatNumericOpFlags.None);
break;
case FormatDescriptor.Type.NumericBE:
po.Opcode = opNames.GetDefineBigData(length);
operand = RawData.GetWord(data, offset, length, true);
- po.Operand = FormatNumericOperand(formatter, symbolTable, labelMap, dfd,
- operand, length, FormatNumericOpFlags.None);
+ po.Operand = FormatNumericOperand(formatter, symbolTable, labelMap,
+ dfd, operand, length, FormatNumericOpFlags.None);
break;
case FormatDescriptor.Type.Fill:
po.Opcode = opNames.Fill;
@@ -533,8 +533,33 @@ namespace SourceGen {
/// does not include the opcode byte. For a relative branch, this will be 2.
/// Special handling.
public static string FormatNumericOperand(Formatter formatter, SymbolTable symbolTable,
- Dictionary labelMap, FormatDescriptor dfd,
- int operandValue, int operandLen, FormatNumericOpFlags flags) {
+ Dictionary labelMap, FormatDescriptor dfd, int operandValue,
+ int operandLen, FormatNumericOpFlags flags) {
+ return FormatNumericOperand(formatter, symbolTable, null, labelMap, dfd, -1,
+ operandValue, operandLen, flags);
+ }
+
+ ///
+ /// Format a numeric operand value according to the specified sub-format.
+ ///
+ /// Text formatter.
+ /// Full table of project symbols.
+ /// Local variable lookup object. May be null if not
+ /// formatting an instruction.
+ /// Symbol label remap, for local label conversion. May be
+ /// null.
+ /// Operand format descriptor.
+ /// Offset of start of instruction or data pseudo-op, for
+ /// variable name lookup. Okay to pass -1 when not formatting an instruction.
+ /// Operand's value. For most things this comes directly
+ /// out of the code, for relative branches it's a 24-bit absolute address.
+ /// Length of operand, in bytes. For an instruction, this
+ /// does not include the opcode byte. For a relative branch, this will be 2.
+ /// Special handling.
+ public static string FormatNumericOperand(Formatter formatter, SymbolTable symbolTable,
+ LocalVariableLookup lvLookup, Dictionary labelMap,
+ FormatDescriptor dfd, int offset, int operandValue, int operandLen,
+ FormatNumericOpFlags flags) {
Debug.Assert(operandLen > 0);
int hexMinLen = operandLen * 2;
@@ -554,7 +579,28 @@ namespace SourceGen {
CharEncoding.Encoding enc = SubTypeToEnc(dfd.FormatSubType);
return formatter.FormatCharacterValue(operandValue, enc);
case FormatDescriptor.SubType.Symbol:
- if (symbolTable.TryGetValue(dfd.SymbolRef.Label, out Symbol sym)) {
+ if (lvLookup != null && dfd.SymbolRef.IsVariable) {
+ Debug.Assert(operandLen == 1); // only doing 8-bit stuff
+ //Symbol.Type symType;
+ //if (dfd.SymbolRef.VarType == WeakSymbolRef.LocalVariableType.DpAddr) {
+ // symType = Symbol.Type.ExternalAddr;
+ //} else {
+ // symType = Symbol.Type.Constant;
+ //}
+ //DefSymbol defSym = lvLookup.GetSymbol(offset, operandValue, symType);
+ DefSymbol defSym = lvLookup.GetSymbol(offset, dfd.SymbolRef);
+ if (defSym != null) {
+ StringBuilder sb = new StringBuilder();
+ FormatNumericSymbolCommon(formatter, defSym, null,
+ dfd, operandValue, operandLen, flags, sb);
+ return sb.ToString();
+ } else {
+ Debug.WriteLine("Local variable format failed");
+ Debug.Assert(false);
+ return formatter.FormatHexValue(operandValue, hexMinLen);
+ }
+ } else if (symbolTable.TryGetValue(dfd.SymbolRef.Label, out Symbol sym) &&
+ !sym.IsVariable) {
StringBuilder sb = new StringBuilder();
switch (formatter.ExpressionMode) {
@@ -604,6 +650,9 @@ namespace SourceGen {
if (labelMap != null && labelMap.TryGetValue(symLabel, out string newLabel)) {
symLabel = newLabel;
}
+ if (sym.IsVariable) {
+ symLabel = formatter.FormatVariableLabel(symLabel);
+ }
if (operandLen == 1) {
// Use the byte-selection operator to get the right piece. In 64tass the
diff --git a/SourceGen/SourceGen.csproj b/SourceGen/SourceGen.csproj
index 58d38d9..683e4af 100644
--- a/SourceGen/SourceGen.csproj
+++ b/SourceGen/SourceGen.csproj
@@ -77,6 +77,7 @@
+
diff --git a/SourceGen/Symbol.cs b/SourceGen/Symbol.cs
index ff6ab31..a4315e2 100644
--- a/SourceGen/Symbol.cs
+++ b/SourceGen/Symbol.cs
@@ -50,8 +50,9 @@ namespace SourceGen {
Constant // constant value
}
- /// Returns true if the symbol's type is an internal label (auto or user). Returns
- /// false for external addresses and constants.
+ ///
+ /// True if the symbol's type is an internal label (auto or user). Will be false
+ /// for external addresses and constants.
///
public bool IsInternalLabel {
get {
@@ -60,6 +61,15 @@ namespace SourceGen {
}
}
+ ///
+ /// True if the symbol is a local variable.
+ ///
+ public bool IsVariable {
+ get {
+ return SymbolSource == Source.Variable;
+ }
+ }
+
///
/// Label sent to assembler.
diff --git a/SourceGen/WeakSymbolRef.cs b/SourceGen/WeakSymbolRef.cs
index fe19b9a..a221dbc 100644
--- a/SourceGen/WeakSymbolRef.cs
+++ b/SourceGen/WeakSymbolRef.cs
@@ -48,6 +48,16 @@ namespace SourceGen {
Bank, // LDA #^label
}
+ ///
+ /// If this is a local varaiable reference, what type is it.
+ ///
+ public enum LocalVariableType {
+ Unknown = 0,
+ NotVar,
+ DpAddr,
+ StackRelConst
+ }
+
///
/// Label of symbol of interest.
///
@@ -59,14 +69,32 @@ namespace SourceGen {
public Part ValuePart { get; private set; }
///
- /// Full constructor.
+ /// Is this a local variable reference, and if so, what type.
///
- public WeakSymbolRef(string label, Part part) {
+ public LocalVariableType VarType { get; private set; }
+
+ ///
+ /// True for local variable references.
+ ///
+ public bool IsVariable { get { return VarType != LocalVariableType.NotVar; } }
+
+ ///
+ /// Constructor.
+ ///
+ public WeakSymbolRef(string label, Part part) :
+ this(label, part, LocalVariableType.NotVar) { }
+
+ ///
+ /// Constructor.
+ ///
+ public WeakSymbolRef(string label, Part part, LocalVariableType varType) {
Debug.Assert(label != null);
Label = label;
ValuePart = part;
+ VarType = varType;
}
+
public static bool operator ==(WeakSymbolRef a, WeakSymbolRef b) {
if (ReferenceEquals(a, b)) {
return true; // same object, or both null
@@ -75,7 +103,8 @@ namespace SourceGen {
return false; // one is null
}
return Asm65.Label.LABEL_COMPARER.Equals(a.Label, b.Label) &&
- a.ValuePart == b.ValuePart;
+ a.ValuePart == b.ValuePart &&
+ a.VarType == b.VarType;
}
public static bool operator !=(WeakSymbolRef a, WeakSymbolRef b) {
return !(a == b);
@@ -84,7 +113,7 @@ namespace SourceGen {
return obj is WeakSymbolRef && this == (WeakSymbolRef)obj;
}
public override int GetHashCode() {
- return Asm65.Label.ToNormal(Label).GetHashCode() ^ (int)ValuePart;
+ return Asm65.Label.ToNormal(Label).GetHashCode() ^ (int)ValuePart ^ (int)VarType;
}
public override string ToString() {
diff --git a/SourceGen/WpfGui/EditDefSymbol.xaml.cs b/SourceGen/WpfGui/EditDefSymbol.xaml.cs
index 9889d9d..25c77f4 100644
--- a/SourceGen/WpfGui/EditDefSymbol.xaml.cs
+++ b/SourceGen/WpfGui/EditDefSymbol.xaml.cs
@@ -171,7 +171,7 @@ namespace SourceGen.WpfGui {
return;
}
- // Label must be valid and not already exist in project symbol list. (For project
+ // 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.)
bool labelValid = Asm65.Label.ValidateLabel(Label);
bool labelUnique;
@@ -187,6 +187,11 @@ namespace SourceGen.WpfGui {
// For local variables, do a secondary uniqueness check across the full symbol table.
if (labelUnique && mSymbolTable != null) {
labelUnique = !mSymbolTable.TryGetValue(Label, out Symbol sym);
+
+ // It's okay if this and the other are both variables.
+ if (!labelUnique && mIsVariable && sym.IsVariable) {
+ labelUnique = true;
+ }
}
// Value must be blank, meaning "erase any earlier definition", or valid value.
diff --git a/SourceGen/WpfGui/MainWindow.xaml.cs b/SourceGen/WpfGui/MainWindow.xaml.cs
index b71abbd..329d5a4 100644
--- a/SourceGen/WpfGui/MainWindow.xaml.cs
+++ b/SourceGen/WpfGui/MainWindow.xaml.cs
@@ -1505,7 +1505,8 @@ namespace SourceGen.WpfGui {
(SymFilterPlatformSymbols != true && sli.Sym.SymbolSource == Symbol.Source.Platform) ||
(SymFilterAutoLabels != true && sli.Sym.SymbolSource == Symbol.Source.Auto) ||
(SymFilterAddresses != true && sli.Sym.SymbolType != Symbol.Type.Constant) ||
- (SymFilterConstants != true && sli.Sym.SymbolType == Symbol.Type.Constant))
+ (SymFilterConstants != true && sli.Sym.SymbolType == Symbol.Type.Constant) ||
+ sli.Sym.IsVariable)
{
e.Accepted = false;
} else {