mirror of
https://github.com/fadden/6502bench.git
synced 2025-07-24 22:25:06 +00:00
Set Anattrib DataDescriptor for local variable references
We now generate FormatDescriptors with WeakSymbolRefs for direct page references that match variable table entries. LocalVariableTable got a rewrite. We need to be unique in both name and address, but for the address we have to take the width into account as well. We also want to sort the display by address rather than name. (Some people might want it sorted by name, but we can worry about that some other time.) Updated the DefSymbol editor to require value uniqueness. Note addresses and constants exist in separate namespaces. The various symbols are added to the SymbolTable so that uniqueness checks work correctly. This also allows the operand generation to appear to work, but it doesn't yet handle redefinition of symbols.
This commit is contained in:
@@ -20,23 +20,28 @@ using System.Diagnostics;
|
||||
namespace SourceGen {
|
||||
/// <summary>
|
||||
/// Table of redefinable variables. A project may have several of these, at different
|
||||
/// offsets. The contents of later tables overwrite the contents of earlier tables.
|
||||
/// offsets.
|
||||
///
|
||||
/// The class is mutable, but may only be modified by the LvTable editor (which makes
|
||||
/// changes to a work object that moves through the undo/redo buffer) or the
|
||||
/// deserializer.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// The contents of later tables overwrite the contents of earlier tables. A
|
||||
/// variable is replaced if the name is re-used (because a symbol can have only one
|
||||
/// value at a time) or if the value is re-used (because they're applied automatically
|
||||
/// and we need to know which symbol to use).
|
||||
///
|
||||
/// The DefSymbols should have symbol type Constant or ExternalAddr. These do not clash
|
||||
/// with each other, e.g. the statements "LDA $10,S" and "STA $10" use two different
|
||||
/// variables, because one is an 8-bit stack offset while the other is an 8-bit direct page
|
||||
/// address.
|
||||
///
|
||||
/// (Referring to these as "local" variables is a bit of a misnomer, since they have
|
||||
/// global scope from the point where they're defined. The name reflects their intended
|
||||
/// usage, rather than how the assembler will treat them.)
|
||||
/// </summary>
|
||||
/// </remarks>
|
||||
public class LocalVariableTable {
|
||||
/// <summary>
|
||||
/// List of variables. The symbol's label must be unique within a table, so we sort
|
||||
/// on that.
|
||||
/// </summary>
|
||||
public SortedList<string, DefSymbol> Variables;
|
||||
|
||||
/// <summary>
|
||||
/// If set, all values from previous VariableTables should be discarded when this
|
||||
/// table is encountered.
|
||||
@@ -54,12 +59,25 @@ namespace SourceGen {
|
||||
/// </remarks>
|
||||
public bool ClearPrevious { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// List of variables, sorted by label.
|
||||
/// </summary>
|
||||
private SortedList<string, DefSymbol> mVarByLabel;
|
||||
|
||||
/// <summary>
|
||||
/// List of variables. This is manually sorted when needed. The key is a combination
|
||||
/// of the value and the symbol type, so we can't use a simple SortedList.
|
||||
/// </summary>
|
||||
private List<DefSymbol> mVarByValue;
|
||||
private bool mNeedSort = true;
|
||||
|
||||
|
||||
/// <summary>
|
||||
/// Constructs an empty table.
|
||||
/// </summary>
|
||||
public LocalVariableTable() {
|
||||
Variables = new SortedList<string, DefSymbol>();
|
||||
mVarByLabel = new SortedList<string, DefSymbol>();
|
||||
mVarByValue = new List<DefSymbol>();
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
@@ -69,13 +87,132 @@ namespace SourceGen {
|
||||
public LocalVariableTable(LocalVariableTable src) : this() {
|
||||
ClearPrevious = src.ClearPrevious;
|
||||
|
||||
foreach (KeyValuePair<string, DefSymbol> kvp in src.Variables) {
|
||||
Variables[kvp.Key] = kvp.Value;
|
||||
foreach (KeyValuePair<string, DefSymbol> kvp in src.mVarByLabel) {
|
||||
mVarByLabel[kvp.Value.Label] = kvp.Value;
|
||||
mVarByValue.Add(kvp.Value);
|
||||
}
|
||||
|
||||
Debug.Assert(this == src);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Number of entries in the variable table.
|
||||
/// </summary>
|
||||
public int Count { get { return mVarByLabel.Count; } }
|
||||
|
||||
/// <summary>
|
||||
/// Returns the Nth item, sorted by value. This is NOT a lookup by value.
|
||||
/// </summary>
|
||||
public DefSymbol this[int index] {
|
||||
get {
|
||||
SortIfNeeded();
|
||||
return mVarByValue[index];
|
||||
}
|
||||
}
|
||||
|
||||
private void SortIfNeeded() {
|
||||
if (mNeedSort) {
|
||||
// Currently sorting primarily by value, secondarily by label. This ordering
|
||||
// determines how it appears in the code list. If we want to make it
|
||||
// configurable we just need to replace the sort function.
|
||||
mVarByValue.Sort((a, b) => {
|
||||
int diff = a.Value - b.Value;
|
||||
if (diff != 0) {
|
||||
return diff;
|
||||
}
|
||||
return a.Label.CompareTo(b.Label);
|
||||
});
|
||||
mNeedSort = false;
|
||||
}
|
||||
}
|
||||
|
||||
public void Clear() {
|
||||
mVarByLabel.Clear();
|
||||
mVarByValue.Clear();
|
||||
}
|
||||
|
||||
public DefSymbol GetByLabel(string label) {
|
||||
return mVarByLabel[label];
|
||||
}
|
||||
|
||||
public void RemoveByLabel(string label) {
|
||||
if (mVarByLabel.TryGetValue(label, out DefSymbol defSym)) {
|
||||
mVarByLabel.Remove(defSym.Label);
|
||||
mVarByValue.Remove(defSym);
|
||||
}
|
||||
Debug.Assert(mVarByValue.Count == mVarByLabel.Count);
|
||||
|
||||
// Should not be necessary to re-sort the by-value list.
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Finds symbols that overlap with the specified value and width. If more than one
|
||||
/// matching symbol is found, an arbitrary match will be returned. Comparisons are
|
||||
/// only performed between symbols of the same type, so addresses and constants do
|
||||
/// not clash.
|
||||
/// </summary>
|
||||
/// <param name="value">Value to compare.</param>
|
||||
/// <param name="width">Width to check.</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) {
|
||||
if (DefSymbol.CheckOverlap(kvp.Value, value, width, type)) {
|
||||
return kvp.Value;
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Adds a symbol to the variable table. Existing entries with the same name or
|
||||
/// overlapping values will be removed.
|
||||
/// </summary>
|
||||
/// <param name="newSym">Symbol to add.</param>
|
||||
public void AddOrReplace(DefSymbol newSym) {
|
||||
if (newSym.SymbolType != Symbol.Type.Constant &&
|
||||
newSym.SymbolType != Symbol.Type.ExternalAddr) {
|
||||
Debug.Assert(false, "Unexpected symbol type " + newSym.SymbolType);
|
||||
return;
|
||||
}
|
||||
if (newSym.SymbolSource != Symbol.Source.Variable) {
|
||||
Debug.Assert(false, "Unexpected symbol source " + newSym.SymbolSource);
|
||||
return;
|
||||
}
|
||||
|
||||
// Remove existing entries that match on label or value. The value check must
|
||||
// take the width into account.
|
||||
if (mVarByLabel.TryGetValue(newSym.Label, out DefSymbol labelSym)) {
|
||||
mVarByLabel.Remove(labelSym.Label);
|
||||
mVarByValue.Remove(labelSym);
|
||||
}
|
||||
|
||||
// Inefficient, but the list should be small.
|
||||
DefSymbol valSym;
|
||||
while ((valSym = GetByValueRange(newSym.Value,
|
||||
newSym.DataDescriptor.Length, newSym.SymbolType)) != null) {
|
||||
mVarByLabel.Remove(valSym.Label);
|
||||
mVarByValue.Remove(valSym);
|
||||
}
|
||||
|
||||
mVarByLabel.Add(newSym.Label, newSym);
|
||||
mVarByValue.Add(newSym);
|
||||
Debug.Assert(mVarByValue.Count == mVarByLabel.Count);
|
||||
|
||||
mNeedSort = true;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Returns a reference to the sorted-by-label list. The caller must not modify it.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This exists primarily for EditDefSymbol, which wants a list of this type to
|
||||
/// perform uniqueness checks.
|
||||
/// </remarks>
|
||||
public SortedList<string, DefSymbol> GetSortedByLabel() {
|
||||
return mVarByLabel;
|
||||
}
|
||||
|
||||
|
||||
public static bool operator ==(LocalVariableTable a, LocalVariableTable b) {
|
||||
if (ReferenceEquals(a, b)) {
|
||||
return true; // same object, or both null
|
||||
@@ -87,12 +224,12 @@ namespace SourceGen {
|
||||
if (a.ClearPrevious != b.ClearPrevious) {
|
||||
return false;
|
||||
}
|
||||
if (a.Variables.Count != b.Variables.Count) {
|
||||
if (a.mVarByLabel.Count != b.mVarByLabel.Count) {
|
||||
return false;
|
||||
}
|
||||
// Compare all list entries.
|
||||
for (int i = 0; i < a.Variables.Count; i++) {
|
||||
if (a.Variables.Values[i] != b.Variables.Values[i]) {
|
||||
for (int i = 0; i < a.mVarByLabel.Count; i++) {
|
||||
if (a.mVarByLabel.Values[i] != b.mVarByLabel.Values[i]) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@@ -106,7 +243,7 @@ namespace SourceGen {
|
||||
}
|
||||
public override int GetHashCode() {
|
||||
int hashCode = 0;
|
||||
foreach (KeyValuePair<string, DefSymbol> kvp in Variables) {
|
||||
foreach (KeyValuePair<string, DefSymbol> kvp in mVarByLabel) {
|
||||
hashCode ^= kvp.Value.GetHashCode();
|
||||
}
|
||||
if (ClearPrevious) {
|
||||
@@ -114,5 +251,13 @@ namespace SourceGen {
|
||||
}
|
||||
return hashCode;
|
||||
}
|
||||
|
||||
public void DebugDump() {
|
||||
Debug.WriteLine("LocalVariableTable count=" + Count + " clear-previous=" +
|
||||
ClearPrevious);
|
||||
for (int i = 0; i < Count; i++) {
|
||||
Debug.WriteLine(" " + i + ": " + this[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
Reference in New Issue
Block a user