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:
parent
9e4949ab21
commit
6a2532588b
@ -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.
|
||||||
|
@ -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.
|
||||||
|
@ -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));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -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]) {
|
||||||
|
@ -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.
|
||||||
|
@ -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;
|
||||||
|
@ -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:
|
||||||
|
@ -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);
|
||||||
}
|
}
|
||||||
|
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.
|
/// 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;
|
||||||
}
|
}
|
||||||
|
@ -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;
|
||||||
|
@ -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);
|
||||||
|
@ -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
|
||||||
|
@ -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">
|
||||||
|
@ -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.
|
||||||
|
@ -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() {
|
||||||
|
@ -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.
|
||||||
|
@ -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 {
|
||||||
|
Loading…
Reference in New Issue
Block a user