mirror of
https://github.com/fadden/6502bench.git
synced 2024-11-25 14:34:27 +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:
parent
9e4949ab21
commit
6a2532588b
@ -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 {
|
||||
}
|
||||
}
|
||||
|
||||
/// <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>
|
||||
/// Formats an adjustment, as "+decimal" or "-decimal". If no adjustment
|
||||
/// is required, an empty string is returned.
|
||||
|
@ -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>
|
||||
/// True if the operand's width is uniquely determined by the opcode mnemonic, even
|
||||
/// if the operation supports operands with varying widths.
|
||||
|
@ -94,7 +94,7 @@ namespace SourceGen.AsmGen {
|
||||
private static PseudoOp.PseudoOpNames sDataOpNames =
|
||||
new PseudoOp.PseudoOpNames(new Dictionary<string, string> {
|
||||
{ "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));
|
||||
}
|
||||
|
||||
|
@ -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]) {
|
||||
|
@ -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<DefSymbol> 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<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;
|
||||
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.
|
||||
|
@ -911,76 +911,45 @@ namespace SourceGen {
|
||||
/// for the benefit of uniqueness checks.
|
||||
/// </summary>
|
||||
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<DefSymbol> 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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 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;
|
||||
|
@ -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:
|
||||
|
@ -69,6 +69,11 @@ namespace SourceGen {
|
||||
/// </summary>
|
||||
private FormattedOperandCache mFormattedLineCache;
|
||||
|
||||
/// <summary>
|
||||
/// Local variable table data extractor.
|
||||
/// </summary>
|
||||
private LocalVariableLookup mLvLookup;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// 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);
|
||||
}
|
||||
|
199
SourceGen/LocalVariableLookup.cs
Normal file
199
SourceGen/LocalVariableLookup.cs
Normal 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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
@ -159,7 +159,8 @@ namespace SourceGen {
|
||||
/// not clash.
|
||||
/// </summary>
|
||||
/// <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>
|
||||
public DefSymbol GetByValueRange(int value, int width, Symbol.Type type) {
|
||||
foreach (KeyValuePair<string, DefSymbol> 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;
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
@ -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.</param>
|
||||
/// <param name="flags">Special handling.</param>
|
||||
public static string FormatNumericOperand(Formatter formatter, SymbolTable symbolTable,
|
||||
Dictionary<string, string> labelMap, FormatDescriptor dfd,
|
||||
int operandValue, int operandLen, FormatNumericOpFlags flags) {
|
||||
Dictionary<string, string> labelMap, FormatDescriptor dfd, int operandValue,
|
||||
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);
|
||||
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
|
||||
|
@ -77,6 +77,7 @@
|
||||
<Compile Include="AsmGen\IGenerator.cs" />
|
||||
<Compile Include="AsmGen\LabelLocalizer.cs" />
|
||||
<Compile Include="FormattedOperandCache.cs" />
|
||||
<Compile Include="LocalVariableLookup.cs" />
|
||||
<Compile Include="Tests\GenTest.cs" />
|
||||
<Compile Include="Tests\ProgressMessage.cs" />
|
||||
<Compile Include="Tests\WpfGui\GenTestRunner.xaml.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.
|
||||
/// <summary>
|
||||
/// True if the symbol's type is an internal label (auto or user). Will be false
|
||||
/// for external addresses and constants.
|
||||
/// </summary>
|
||||
public bool IsInternalLabel {
|
||||
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>
|
||||
/// Label sent to assembler.
|
||||
|
@ -48,6 +48,16 @@ namespace SourceGen {
|
||||
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>
|
||||
/// Label of symbol of interest.
|
||||
/// </summary>
|
||||
@ -59,14 +69,32 @@ namespace SourceGen {
|
||||
public Part ValuePart { get; private set; }
|
||||
|
||||
/// <summary>
|
||||
/// Full constructor.
|
||||
/// Is this a local variable reference, and if so, what type.
|
||||
/// </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);
|
||||
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() {
|
||||
|
@ -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.
|
||||
|
@ -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 {
|
||||
|
Loading…
Reference in New Issue
Block a user