1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-09-29 01:59:59 +00:00

Local variables mostly work

Variables are now handled properly end-to-end, except for label
uniquification.  So cc65 and ACME can't yet handle a file that
redefines a local variable.

This required a bunch of plumbing, but I think it came out okay.
This commit is contained in:
Andy McFadden 2019-08-30 18:33:05 -07:00
parent 9e4949ab21
commit 6a2532588b
18 changed files with 436 additions and 102 deletions

View File

@ -68,6 +68,8 @@ namespace Asm65 {
public string mForceLongOpcodeSuffix; public string mForceLongOpcodeSuffix;
public string mForceLongOperandPrefix; public string mForceLongOperandPrefix;
public string mLocalVariableLablePrefix; // Merlin 32 puts ']' before var names
public string mEndOfLineCommentDelimiter; // usually ';' public string mEndOfLineCommentDelimiter; // usually ';'
public string mFullLineCommentDelimiterBase; // usually ';' or '*', WITHOUT extra space public string mFullLineCommentDelimiterBase; // usually ';' or '*', WITHOUT extra space
public string mBoxLineCommentDelimiter; // usually blank or ';' public string mBoxLineCommentDelimiter; // usually blank or ';'
@ -610,6 +612,17 @@ namespace Asm65 {
} }
} }
/// <summary>
/// Formats a local variable label, prepending a prefix if needed.
/// </summary>
public string FormatVariableLabel(string label) {
if (!string.IsNullOrEmpty(mFormatConfig.mLocalVariableLablePrefix)) {
return mFormatConfig.mLocalVariableLablePrefix + label;
} else {
return label;
}
}
/// <summary> /// <summary>
/// Formats an adjustment, as "+decimal" or "-decimal". If no adjustment /// Formats an adjustment, as "+decimal" or "-decimal". If no adjustment
/// is required, an empty string is returned. /// is required, an empty string is returned.

View File

@ -217,6 +217,16 @@ namespace Asm65 {
} }
} }
/// <summary>
/// True if the instruction's operand is a stack-relative offset.
/// </summary>
public bool IsStackRelInstruction {
get {
return AddrMode == AddressMode.StackRel ||
AddrMode == AddressMode.StackRelIndIndexY;
}
}
/// <summary> /// <summary>
/// True if the operand's width is uniquely determined by the opcode mnemonic, even /// True if the operand's width is uniquely determined by the opcode mnemonic, even
/// if the operation supports operands with varying widths. /// if the operation supports operands with varying widths.

View File

@ -94,7 +94,7 @@ namespace SourceGen.AsmGen {
private static PseudoOp.PseudoOpNames sDataOpNames = private static PseudoOp.PseudoOpNames sDataOpNames =
new PseudoOp.PseudoOpNames(new Dictionary<string, string> { new PseudoOp.PseudoOpNames(new Dictionary<string, string> {
{ "EquDirective", "equ" }, { "EquDirective", "equ" },
{ "VarDirective", "~=" }, // not really { "VarDirective", "equ" },
{ "OrgDirective", "org" }, { "OrgDirective", "org" },
//RegWidthDirective //RegWidthDirective
{ "DefineData1", "dfb" }, { "DefineData1", "dfb" },
@ -158,6 +158,7 @@ namespace SourceGen.AsmGen {
config.mForceDirectOperandPrefix = string.Empty; config.mForceDirectOperandPrefix = string.Empty;
config.mForceAbsOperandPrefix = string.Empty; config.mForceAbsOperandPrefix = string.Empty;
config.mForceLongOperandPrefix = string.Empty; config.mForceLongOperandPrefix = string.Empty;
config.mLocalVariableLablePrefix = "]";
config.mEndOfLineCommentDelimiter = ";"; config.mEndOfLineCommentDelimiter = ";";
config.mFullLineCommentDelimiterBase = ";"; config.mFullLineCommentDelimiterBase = ";";
config.mBoxLineCommentDelimiter = string.Empty; config.mBoxLineCommentDelimiter = string.Empty;
@ -400,7 +401,8 @@ namespace SourceGen.AsmGen {
// IGenerator // IGenerator
public void OutputVarDirective(string name, string valueStr, string comment) { 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)); valueStr, SourceFormatter.FormatEolComment(comment));
} }

View File

@ -589,9 +589,11 @@ namespace SourceGen.AsmGen {
// IGenerator // IGenerator
public void OutputLine(string label, string opcode, string operand, string comment) { 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) && if (!string.IsNullOrEmpty(label) &&
!string.Equals(opcode, sDataOpNames.EquDirective, !string.Equals(opcode, sDataOpNames.EquDirective,
StringComparison.InvariantCultureIgnoreCase) &&
!string.Equals(opcode, sDataOpNames.VarDirective,
StringComparison.InvariantCultureIgnoreCase)) { StringComparison.InvariantCultureIgnoreCase)) {
if (mLongLabelNewLine && label.Length >= mColumnWidths[0]) { if (mLongLabelNewLine && label.Length >= mColumnWidths[0]) {

View File

@ -39,6 +39,10 @@ namespace SourceGen.AsmGen {
bool doAddCycles = gen.Settings.GetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS, false); 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); GenerateHeader(gen, sw);
// Used for M/X flag tracking. // Used for M/X flag tracking.
@ -69,6 +73,11 @@ namespace SourceGen.AsmGen {
gen.OutputOrgDirective(offset, orgAddr); gen.OutputOrgDirective(offset, orgAddr);
} }
List<DefSymbol> lvars = lvLookup.GetVariablesDefinedAtOffset(offset);
if (lvars != null) {
GenerateLocalVariables(gen, sw, lvars);
}
if (attr.IsInstructionStart) { if (attr.IsInstructionStart) {
// Generate M/X reg width directive, if necessary. // Generate M/X reg width directive, if necessary.
// NOTE: we can suppress the initial directive if we know what the // NOTE: we can suppress the initial directive if we know what the
@ -96,7 +105,7 @@ namespace SourceGen.AsmGen {
} }
// Output instruction. // Output instruction.
GenerateInstruction(gen, sw, offset, len, doAddCycles); GenerateInstruction(gen, sw, lvLookup, offset, len, doAddCycles);
if (attr.DoesNotContinue) { if (attr.DoesNotContinue) {
gen.OutputLine(string.Empty); gen.OutputLine(string.Empty);
@ -139,7 +148,7 @@ namespace SourceGen.AsmGen {
// Format symbols. // Format symbols.
foreach (DefSymbol defSym in proj.ActiveDefSymbolList) { 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, string valueStr = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable,
gen.Localizer.LabelMap, defSym.DataDescriptor, defSym.Value, 1, gen.Localizer.LabelMap, defSym.DataDescriptor, defSym.Value, 1,
PseudoOp.FormatNumericOpFlags.None); PseudoOp.FormatNumericOpFlags.None);
@ -152,8 +161,22 @@ namespace SourceGen.AsmGen {
} }
} }
private static void GenerateInstruction(IGenerator gen, StreamWriter sw, int offset, private static void GenerateLocalVariables(IGenerator gen, StreamWriter sw,
int instrBytes, bool doAddCycles) { List<DefSymbol> 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; DisasmProject proj = gen.Project;
Formatter formatter = gen.SourceFormatter; Formatter formatter = gen.SourceFormatter;
byte[] data = proj.FileData; byte[] data = proj.FileData;
@ -241,8 +264,8 @@ namespace SourceGen.AsmGen {
gen.UpdateCharacterEncoding(attr.DataDescriptor); gen.UpdateCharacterEncoding(attr.DataDescriptor);
} }
formattedOperand = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable, formattedOperand = PseudoOp.FormatNumericOperand(formatter, proj.SymbolTable,
gen.Localizer.LabelMap, attr.DataDescriptor, lvLookup, gen.Localizer.LabelMap, attr.DataDescriptor,
operandForSymbol, operandLen, opFlags); offset, operandForSymbol, operandLen, opFlags);
} }
} else { } else {
// Show operand value in hex. // Show operand value in hex.

View File

@ -911,76 +911,45 @@ namespace SourceGen {
/// for the benefit of uniqueness checks. /// for the benefit of uniqueness checks.
/// </summary> /// </summary>
private void GenerateVariableRefs() { private void GenerateVariableRefs() {
LocalVariableTable curTab = new LocalVariableTable(); LocalVariableLookup lvLookup = new LocalVariableLookup(LvTables, SymbolTable, this);
int nextLvtIndex, nextLvtOffset;
if (LvTables.Count > 0) {
nextLvtIndex = 0;
nextLvtOffset = LvTables.Keys[0];
} else {
nextLvtIndex = -1;
nextLvtOffset = FileData.Length;
}
for (int offset = 0; offset < FileData.Length; ) { 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();
}
// 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 // All entries also get added to the main SymbolTable. This is a little
// wonky because the symbol might already exist with a different value. // 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. // So long as the previous thing was also a variable, it doesn't matter.
AddVariablesToSymbolTable(lvt); List<DefSymbol> vars = lvLookup.GetVariablesDefinedAtOffset(offset);
} else { if (vars != null) {
// Either this wasn't an instruction/data start, or we passed this foreach (DefSymbol defSym in vars) {
// one, which only happens for non-start items. Whatever the case, if (!SymbolTable.TryGetValue(defSym.Label, out Symbol sym)) {
// we're going to ignore it. SymbolTable[defSym.Label] = defSym;
Debug.WriteLine("Ignoring LvTable +" + offset.ToString("x6")); } 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);
} }
// Advance to next table.
nextLvtIndex++;
if (nextLvtIndex < LvTables.Keys.Count) {
nextLvtOffset = LvTables.Keys[nextLvtIndex];
} else {
nextLvtOffset = FileData.Length;
} }
} }
Anattrib attr = mAnattribs[offset]; Anattrib attr = mAnattribs[offset];
if (attr.IsInstructionStart && attr.DataDescriptor == null) { if (attr.IsInstructionStart && attr.DataDescriptor == null) {
OpDef op = CpuDef.GetOpDef(FileData[offset]); OpDef op = CpuDef.GetOpDef(FileData[offset]);
DefSymbol defSym = null;
if (op.IsDirectPageInstruction) { if (op.IsDirectPageInstruction) {
Debug.Assert(attr.OperandAddress == FileData[offset + 1]); Debug.Assert(attr.OperandAddress == FileData[offset + 1]);
DefSymbol defSym = curTab.GetByValueRange(attr.OperandAddress, 1, defSym = lvLookup.GetSymbol(offset, FileData[offset + 1],
Symbol.Type.ExternalAddr); Symbol.Type.ExternalAddr);
if (defSym != null) { } else if (op.IsStackRelInstruction) {
mAnattribs[offset].DataDescriptor = defSym = lvLookup.GetSymbol(offset, FileData[offset + 1],
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,
Symbol.Type.Constant); 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;
}
}
}
/// <summary> /// <summary>
/// Generates references to symbols in the project/platform symbol tables. /// Generates references to symbols in the project/platform symbol tables.
/// ///
@ -1048,7 +1008,7 @@ namespace SourceGen {
if (sym == null && (attr.OperandAddress & 0xffff) > 1 && checkNearby) { if (sym == null && (attr.OperandAddress & 0xffff) > 1 && checkNearby) {
sym = SymbolTable.FindAddressByValue(attr.OperandAddress - 2); sym = SymbolTable.FindAddressByValue(attr.OperandAddress - 2);
} }
if (sym != null) { if (sym != null && !sym.IsVariable) {
mAnattribs[offset].DataDescriptor = mAnattribs[offset].DataDescriptor =
FormatDescriptor.Create(mAnattribs[offset].Length, FormatDescriptor.Create(mAnattribs[offset].Length,
new WeakSymbolRef(sym.Label, WeakSymbolRef.Part.Low), false); 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 // Walk through the Anattrib array, adding xref entries to things referenced
// by the entity at the current offset. // by the entity at the current offset.
for (int offset = 0; offset < mAnattribs.Length; ) { for (int offset = 0; offset < mAnattribs.Length; ) {
@ -1174,11 +1136,21 @@ namespace SourceGen {
if (adj == 0) { if (adj == 0) {
hasZeroOffsetSym = true; 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)) { } 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 || if (sym.SymbolSource == Symbol.Source.Project ||
sym.SymbolSource == Symbol.Source.Platform || sym.SymbolSource == Symbol.Source.Platform) {
sym.SymbolSource == Symbol.Source.Variable) {
DefSymbol defSym = sym as DefSymbol; DefSymbol defSym = sym as DefSymbol;
int adj = 0; int adj = 0;
if (operandOffset >= 0) { if (operandOffset >= 0) {
@ -1286,7 +1258,7 @@ namespace SourceGen {
ActiveDefSymbolList.Clear(); ActiveDefSymbolList.Clear();
foreach (Symbol sym in SymbolTable) { foreach (Symbol sym in SymbolTable) {
if (!(sym is DefSymbol)) { if (!(sym is DefSymbol) || sym.IsVariable) {
continue; continue;
} }
DefSymbol defSym = sym as DefSymbol; DefSymbol defSym = sym as DefSymbol;

View File

@ -440,7 +440,11 @@ namespace SourceGen {
case SubType.Address: case SubType.Address:
return "Address"; return "Address";
case SubType.Symbol: case SubType.Symbol:
if (SymbolRef.IsVariable) {
return "Local var \"" + SymbolRef.Label + "\"";
} else {
return "Symbol \"" + SymbolRef.Label + "\""; return "Symbol \"" + SymbolRef.Label + "\"";
}
case SubType.Ascii: case SubType.Ascii:
return "Numeric, ASCII"; return "Numeric, ASCII";
case SubType.HighAscii: case SubType.HighAscii:

View File

@ -69,6 +69,11 @@ namespace SourceGen {
/// </summary> /// </summary>
private FormattedOperandCache mFormattedLineCache; private FormattedOperandCache mFormattedLineCache;
/// <summary>
/// Local variable table data extractor.
/// </summary>
private LocalVariableLookup mLvLookup;
/// <summary> /// <summary>
/// One of these per line of output in the display. It should be possible to draw /// 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(); mFormattedLineCache = new FormattedOperandCache();
mShowCycleCounts = AppSettings.Global.GetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS, mShowCycleCounts = AppSettings.Global.GetBool(AppSettings.SRCGEN_SHOW_CYCLE_COUNTS,
false); false);
// TODO: remove SymbolTable -- don't need unique
mLvLookup = new LocalVariableLookup(mProject.LvTables, mProject.SymbolTable, mProject);
mDisplayList.ListGen = this; mDisplayList.ListGen = this;
} }
@ -886,6 +893,9 @@ namespace SourceGen {
Debug.Assert(startOffset >= 0); Debug.Assert(startOffset >= 0);
Debug.Assert(endOffset >= startOffset); Debug.Assert(endOffset >= startOffset);
// Assume variables may have changed.
mLvLookup.Reset();
// Find the previous status flags for M/X tracking. // Find the previous status flags for M/X tracking.
StatusFlags prevFlags = StatusFlags.AllIndeterminate; StatusFlags prevFlags = StatusFlags.AllIndeterminate;
if (mProject.CpuDef.HasEmuFlag) { if (mProject.CpuDef.HasEmuFlag) {
@ -1220,8 +1230,9 @@ namespace SourceGen {
PseudoOp.FormatNumericOpFlags.None); PseudoOp.FormatNumericOpFlags.None);
formattedOperand = '#' + opstr1 + "," + '#' + opstr2; formattedOperand = '#' + opstr1 + "," + '#' + opstr2;
} else { } else {
formattedOperand = PseudoOp.FormatNumericOperand(mFormatter, mProject.SymbolTable, formattedOperand = PseudoOp.FormatNumericOperand(mFormatter,
null, attr.DataDescriptor, operandForSymbol, operandLen, opFlags); mProject.SymbolTable, mLvLookup, null, attr.DataDescriptor, offset,
operandForSymbol, operandLen, opFlags);
} }
} else { } else {
// Show operand value in hex. // Show operand value in hex.
@ -1330,7 +1341,8 @@ namespace SourceGen {
null, defSym.DataDescriptor, defSym.Value, 1, null, defSym.DataDescriptor, defSym.Value, 1,
PseudoOp.FormatNumericOpFlags.None); PseudoOp.FormatNumericOpFlags.None);
string comment = mFormatter.FormatEolComment(defSym.Comment); string comment = mFormatter.FormatEolComment(defSym.Comment);
return FormattedParts.CreateEquDirective(defSym.Label, return FormattedParts.CreateEquDirective(
mFormatter.FormatVariableLabel(defSym.Label),
mFormatter.FormatPseudoOp(mPseudoOpNames.VarDirective), mFormatter.FormatPseudoOp(mPseudoOpNames.VarDirective),
addrStr, comment); addrStr, comment);
} }

View File

@ -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 {
/// <summary>
/// Given a list of LocalVariableTables, this determines the mapping of values to symbols
/// at a specific offset.
/// </summary>
public class LocalVariableLookup {
/// <summary>
/// List of tables. The table's file offset is used as the key.
/// </summary>
private SortedList<int, LocalVariableTable> mLvTables;
/// <summary>
/// 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.
/// </summary>
private SymbolTable mSymbolTable;
/// <summary>
/// Reference to project, so we can query the Anattrib array to identify "hidden" tables.
/// </summary>
private DisasmProject mProject;
/// <summary>
/// Most recently processed offset.
/// </summary>
private int mRecentOffset;
/// <summary>
/// Symbols defined at mRecentOffset.
/// </summary>
private List<DefSymbol> mRecentSymbols;
/// <summary>
/// Cumulative symbols defined at the current offset.
/// </summary>
private LocalVariableTable mCurrentTable;
private int mNextLvtIndex;
private int mNextLvtOffset;
/// <summary>
/// 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. Pass null if uniqueness is not a
/// requirement.</param>
/// <param name="project">Project reference.</param>
public LocalVariableLookup(SortedList<int, LocalVariableTable> 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];
}
}
/// <summary>
/// Gets the symbol associated with the operand of an instruction.
/// </summary>
/// <param name="offset">Offset of start of instruction.</param>
/// <param name="operandValue">Operand value.</param>
/// <param name="type">Operand type. Should be ExternalAddress for DP ops, or
/// Constant for StackRel ops.</param>
/// <returns>Symbol, or null if no match found.</returns>
public DefSymbol GetSymbol(int offset, int operandValue, Symbol.Type type) {
AdvanceToOffset(offset);
return mCurrentTable.GetByValueRange(operandValue, 1, type);
}
/// <summary>
/// Gets the symbol associated with a symbol reference.
/// </summary>
/// <param name="offset">Offset of start of instruction.</param>
/// <param name="symRef">Reference to symbol.</param>
/// <returns>Symbol, or null if no match found.</returns>
public DefSymbol GetSymbol(int offset, WeakSymbolRef symRef) {
AdvanceToOffset(offset);
return mCurrentTable.GetByLabel(symRef.Label);
}
/// <summary>
/// Gets a LocalVariableTable that is the result of merging all tables up to this point.
/// </summary>
/// <param name="offset">Target offset.</param>
/// <returns>Combined table.</returns>
public LocalVariableTable GetMergedTableAtOffset(int offset) {
AdvanceToOffset(offset);
return mCurrentTable;
}
/// <summary>
/// Generates a list of variables defined at the specified offset, if a table is
/// associated with that offset.
/// </summary>
/// <param name="offset">File data offset.</param>
/// <returns>List of symbols, uniquified if desired, or null if no LocalVariableTable
/// exists at the specified offset.</returns>
public List<DefSymbol> GetVariablesDefinedAtOffset(int offset) {
AdvanceToOffset(offset);
if (mRecentOffset == offset) {
return mRecentSymbols;
}
return null;
}
/// <summary>
/// Updates internal state to reflect the state of the world at the specified offset.
/// </summary>
/// <remarks>
/// 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.
/// </remarks>
/// <param name="offset">Target offset.</param>
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<DefSymbol>();
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
}
}
}
}
}

View File

@ -159,7 +159,8 @@ namespace SourceGen {
/// not clash. /// not clash.
/// </summary> /// </summary>
/// <param name="value">Value to compare.</param> /// <param name="value">Value to compare.</param>
/// <param name="width">Width to check.</param> /// <param name="width">Width to check, useful when checking for collisions. When
/// doing a simple variable lookup, this should be set to 1.</param>
/// <returns>One matching symbol, or null if none matched.</returns> /// <returns>One matching symbol, or null if none matched.</returns>
public DefSymbol GetByValueRange(int value, int width, Symbol.Type type) { public DefSymbol GetByValueRange(int value, int width, Symbol.Type type) {
foreach (KeyValuePair<string, DefSymbol> kvp in mVarByLabel) { foreach (KeyValuePair<string, DefSymbol> kvp in mVarByLabel) {
@ -181,7 +182,7 @@ namespace SourceGen {
Debug.Assert(false, "Unexpected symbol type " + newSym.SymbolType); Debug.Assert(false, "Unexpected symbol type " + newSym.SymbolType);
return; return;
} }
if (newSym.SymbolSource != Symbol.Source.Variable) { if (!newSym.IsVariable) {
Debug.Assert(false, "Unexpected symbol source " + newSym.SymbolSource); Debug.Assert(false, "Unexpected symbol source " + newSym.SymbolSource);
return; return;
} }

View File

@ -435,6 +435,7 @@ namespace SourceGen {
mFormatterConfig = new Formatter.FormatConfig(); mFormatterConfig = new Formatter.FormatConfig();
AsmGen.GenCommon.ConfigureFormatterFromSettings(AppSettings.Global, AsmGen.GenCommon.ConfigureFormatterFromSettings(AppSettings.Global,
ref mFormatterConfig); ref mFormatterConfig);
//mFormatterConfig.mLocalVariableLablePrefix = "\u00a4"; // CURRENCY SIGN
mFormatterConfig.mEndOfLineCommentDelimiter = ";"; mFormatterConfig.mEndOfLineCommentDelimiter = ";";
mFormatterConfig.mFullLineCommentDelimiterBase = ";"; mFormatterConfig.mFullLineCommentDelimiterBase = ";";
mFormatterConfig.mBoxLineCommentDelimiter = string.Empty; mFormatterConfig.mBoxLineCommentDelimiter = string.Empty;

View File

@ -831,7 +831,7 @@ namespace SourceGen {
if (!CreateDefSymbol(serDef, contentVersion, report, out DefSymbol defSym)) { if (!CreateDefSymbol(serDef, contentVersion, report, out DefSymbol defSym)) {
return false; return false;
} }
if (defSym.SymbolSource != Symbol.Source.Variable) { if (!defSym.IsVariable) {
// not expected to happen // not expected to happen
Debug.WriteLine("Found local variable with bad source: " + Debug.WriteLine("Found local variable with bad source: " +
defSym.SymbolSource); defSym.SymbolSource);

View File

@ -332,14 +332,14 @@ namespace SourceGen {
case FormatDescriptor.Type.NumericLE: case FormatDescriptor.Type.NumericLE:
po.Opcode = opNames.GetDefineData(length); po.Opcode = opNames.GetDefineData(length);
operand = RawData.GetWord(data, offset, length, false); operand = RawData.GetWord(data, offset, length, false);
po.Operand = FormatNumericOperand(formatter, symbolTable, labelMap, dfd, po.Operand = FormatNumericOperand(formatter, symbolTable, labelMap,
operand, length, FormatNumericOpFlags.None); dfd, operand, length, FormatNumericOpFlags.None);
break; break;
case FormatDescriptor.Type.NumericBE: case FormatDescriptor.Type.NumericBE:
po.Opcode = opNames.GetDefineBigData(length); po.Opcode = opNames.GetDefineBigData(length);
operand = RawData.GetWord(data, offset, length, true); operand = RawData.GetWord(data, offset, length, true);
po.Operand = FormatNumericOperand(formatter, symbolTable, labelMap, dfd, po.Operand = FormatNumericOperand(formatter, symbolTable, labelMap,
operand, length, FormatNumericOpFlags.None); dfd, operand, length, FormatNumericOpFlags.None);
break; break;
case FormatDescriptor.Type.Fill: case FormatDescriptor.Type.Fill:
po.Opcode = opNames.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.</param> /// does not include the opcode byte. For a relative branch, this will be 2.</param>
/// <param name="flags">Special handling.</param> /// <param name="flags">Special handling.</param>
public static string FormatNumericOperand(Formatter formatter, SymbolTable symbolTable, public static string FormatNumericOperand(Formatter formatter, SymbolTable symbolTable,
Dictionary<string, string> labelMap, FormatDescriptor dfd, Dictionary<string, string> labelMap, FormatDescriptor dfd, int operandValue,
int operandValue, int operandLen, FormatNumericOpFlags flags) { int operandLen, FormatNumericOpFlags flags) {
return FormatNumericOperand(formatter, symbolTable, null, labelMap, dfd, -1,
operandValue, operandLen, flags);
}
/// <summary>
/// Format a numeric operand value according to the specified sub-format.
/// </summary>
/// <param name="formatter">Text formatter.</param>
/// <param name="symbolTable">Full table of project symbols.</param>
/// <param name="lvLookup">Local variable lookup object. May be null if not
/// formatting an instruction.</param>
/// <param name="labelMap">Symbol label remap, for local label conversion. May be
/// null.</param>
/// <param name="dfd">Operand format descriptor.</param>
/// <param name="offset">Offset of start of instruction or data pseudo-op, for
/// variable name lookup. Okay to pass -1 when not formatting an instruction.</param>
/// <param name="operandValue">Operand's value. For most things this comes directly
/// out of the code, for relative branches it's a 24-bit absolute address.</param>
/// <param name="operandLen">Length of operand, in bytes. For an instruction, this
/// does not include the opcode byte. For a relative branch, this will be 2.</param>
/// <param name="flags">Special handling.</param>
public static string FormatNumericOperand(Formatter formatter, SymbolTable symbolTable,
LocalVariableLookup lvLookup, Dictionary<string, string> labelMap,
FormatDescriptor dfd, int offset, int operandValue, int operandLen,
FormatNumericOpFlags flags) {
Debug.Assert(operandLen > 0); Debug.Assert(operandLen > 0);
int hexMinLen = operandLen * 2; int hexMinLen = operandLen * 2;
@ -554,7 +579,28 @@ namespace SourceGen {
CharEncoding.Encoding enc = SubTypeToEnc(dfd.FormatSubType); CharEncoding.Encoding enc = SubTypeToEnc(dfd.FormatSubType);
return formatter.FormatCharacterValue(operandValue, enc); return formatter.FormatCharacterValue(operandValue, enc);
case FormatDescriptor.SubType.Symbol: 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(); StringBuilder sb = new StringBuilder();
switch (formatter.ExpressionMode) { switch (formatter.ExpressionMode) {
@ -604,6 +650,9 @@ namespace SourceGen {
if (labelMap != null && labelMap.TryGetValue(symLabel, out string newLabel)) { if (labelMap != null && labelMap.TryGetValue(symLabel, out string newLabel)) {
symLabel = newLabel; symLabel = newLabel;
} }
if (sym.IsVariable) {
symLabel = formatter.FormatVariableLabel(symLabel);
}
if (operandLen == 1) { if (operandLen == 1) {
// Use the byte-selection operator to get the right piece. In 64tass the // Use the byte-selection operator to get the right piece. In 64tass the

View File

@ -77,6 +77,7 @@
<Compile Include="AsmGen\IGenerator.cs" /> <Compile Include="AsmGen\IGenerator.cs" />
<Compile Include="AsmGen\LabelLocalizer.cs" /> <Compile Include="AsmGen\LabelLocalizer.cs" />
<Compile Include="FormattedOperandCache.cs" /> <Compile Include="FormattedOperandCache.cs" />
<Compile Include="LocalVariableLookup.cs" />
<Compile Include="Tests\GenTest.cs" /> <Compile Include="Tests\GenTest.cs" />
<Compile Include="Tests\ProgressMessage.cs" /> <Compile Include="Tests\ProgressMessage.cs" />
<Compile Include="Tests\WpfGui\GenTestRunner.xaml.cs"> <Compile Include="Tests\WpfGui\GenTestRunner.xaml.cs">

View File

@ -50,8 +50,9 @@ namespace SourceGen {
Constant // constant value Constant // constant value
} }
/// Returns true if the symbol's type is an internal label (auto or user). Returns /// <summary>
/// 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.
/// </summary> /// </summary>
public bool IsInternalLabel { public bool IsInternalLabel {
get { get {
@ -60,6 +61,15 @@ namespace SourceGen {
} }
} }
/// <summary>
/// True if the symbol is a local variable.
/// </summary>
public bool IsVariable {
get {
return SymbolSource == Source.Variable;
}
}
/// <summary> /// <summary>
/// Label sent to assembler. /// Label sent to assembler.

View File

@ -48,6 +48,16 @@ namespace SourceGen {
Bank, // LDA #^label Bank, // LDA #^label
} }
/// <summary>
/// If this is a local varaiable reference, what type is it.
/// </summary>
public enum LocalVariableType {
Unknown = 0,
NotVar,
DpAddr,
StackRelConst
}
/// <summary> /// <summary>
/// Label of symbol of interest. /// Label of symbol of interest.
/// </summary> /// </summary>
@ -59,14 +69,32 @@ namespace SourceGen {
public Part ValuePart { get; private set; } public Part ValuePart { get; private set; }
/// <summary> /// <summary>
/// Full constructor. /// Is this a local variable reference, and if so, what type.
/// </summary> /// </summary>
public WeakSymbolRef(string label, Part part) { public LocalVariableType VarType { get; private set; }
/// <summary>
/// True for local variable references.
/// </summary>
public bool IsVariable { get { return VarType != LocalVariableType.NotVar; } }
/// <summary>
/// Constructor.
/// </summary>
public WeakSymbolRef(string label, Part part) :
this(label, part, LocalVariableType.NotVar) { }
/// <summary>
/// Constructor.
/// </summary>
public WeakSymbolRef(string label, Part part, LocalVariableType varType) {
Debug.Assert(label != null); Debug.Assert(label != null);
Label = label; Label = label;
ValuePart = part; ValuePart = part;
VarType = varType;
} }
public static bool operator ==(WeakSymbolRef a, WeakSymbolRef b) { public static bool operator ==(WeakSymbolRef a, WeakSymbolRef b) {
if (ReferenceEquals(a, b)) { if (ReferenceEquals(a, b)) {
return true; // same object, or both null return true; // same object, or both null
@ -75,7 +103,8 @@ namespace SourceGen {
return false; // one is null return false; // one is null
} }
return Asm65.Label.LABEL_COMPARER.Equals(a.Label, b.Label) && 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) { public static bool operator !=(WeakSymbolRef a, WeakSymbolRef b) {
return !(a == b); return !(a == b);
@ -84,7 +113,7 @@ namespace SourceGen {
return obj is WeakSymbolRef && this == (WeakSymbolRef)obj; return obj is WeakSymbolRef && this == (WeakSymbolRef)obj;
} }
public override int GetHashCode() { 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() { public override string ToString() {

View File

@ -171,7 +171,7 @@ namespace SourceGen.WpfGui {
return; 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.) // symbols, it's okay if an identical label exists elsewhere.)
bool labelValid = Asm65.Label.ValidateLabel(Label); bool labelValid = Asm65.Label.ValidateLabel(Label);
bool labelUnique; bool labelUnique;
@ -187,6 +187,11 @@ namespace SourceGen.WpfGui {
// For local variables, do a secondary uniqueness check across the full symbol table. // For local variables, do a secondary uniqueness check across the full symbol table.
if (labelUnique && mSymbolTable != null) { if (labelUnique && mSymbolTable != null) {
labelUnique = !mSymbolTable.TryGetValue(Label, out Symbol sym); 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. // Value must be blank, meaning "erase any earlier definition", or valid value.

View File

@ -1505,7 +1505,8 @@ namespace SourceGen.WpfGui {
(SymFilterPlatformSymbols != true && sli.Sym.SymbolSource == Symbol.Source.Platform) || (SymFilterPlatformSymbols != true && sli.Sym.SymbolSource == Symbol.Source.Platform) ||
(SymFilterAutoLabels != true && sli.Sym.SymbolSource == Symbol.Source.Auto) || (SymFilterAutoLabels != true && sli.Sym.SymbolSource == Symbol.Source.Auto) ||
(SymFilterAddresses != true && sli.Sym.SymbolType != Symbol.Type.Constant) || (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; e.Accepted = false;
} else { } else {