1
0
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:
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 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.

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>
/// True if the operand's width is uniquely determined by the opcode mnemonic, even
/// if the operation supports operands with varying widths.

View File

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

View File

@ -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]) {

View File

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

View File

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

View File

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

View File

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

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.
/// </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;
}

View File

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

View File

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

View File

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

View File

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

View File

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

View File

@ -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() {

View File

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

View File

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