1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-06-25 05:29:31 +00:00

Add LocalVariableTable list to project

This involved adding a list to the DisasmProject, creating a new
UndoableChange type, and writing the project file serialization
code.  While doing the latter I realized that the new Width field
was redundant with the FormatDescriptor Length field, and removed it.

I added a placeholder line type, but we're not yet showing the
table in the display list.  (To edit the tables you just have to
know where they are.)
This commit is contained in:
Andy McFadden 2019-08-26 16:58:53 -07:00
parent 1cc9d2bd70
commit 0eeb36f59a
13 changed files with 362 additions and 114 deletions

View File

@ -20,10 +20,15 @@ namespace SourceGen {
/// <summary>
/// Subclass of Symbol used for symbols defined in the platform or project.
///
/// Instances are immutable.
/// Instances are immutable, except for the Xrefs field.
/// </summary>
/// <remarks>
/// The Xrefs field isn't really part of the object. It's just convenient to reference
/// them from here.
/// </remarks>
public class DefSymbol : Symbol {
public const int NO_WIDTH = -1;
// width to use when width doesn't matter; use 1 to try to get a prefab object
public const int NO_WIDTH = 1;
public const int MIN_WIDTH = 1;
public const int MAX_WIDTH = 4;
@ -41,33 +46,36 @@ namespace SourceGen {
/// Platform symbols only: tag used to organize symbols into groups. Used by
/// extension scripts.
/// </summary>
/// <remarks>
/// This is not included in DefSymbol serialization because symbols with tags are
/// not stored in the project file. It's only set when symbols are parsed out of
/// platform symbol files.
/// </remarks>
public string Tag { get; private set; }
/// <summary>
/// Number of bytes referenced by the symbol. Useful for identifying multi-byte items,
/// such as two-byte and three-byte pointers. Used for Variables. Value will be
/// NO_WIDTH if unset.
/// </summary>
public int Width { get; private set; }
/// <summary>
/// Cross-reference data, generated by the analyzer.
/// </summary>
/// <remarks>
/// This is just a convenient place to reference some data generated at run-time. It's
/// not serialized, and not included in the test for equality.
/// </remarks>
public XrefSet Xrefs { get; private set; }
// NOTE: might be nice to identify the symbol's origin, e.g. which platform
// symbol file it was defined in. This could then be stored in a
// DisplayList line, for benefit of the Info panel.
/// <summary>
/// Internal base-object constructor, called by other constructors.
/// </summary>
private DefSymbol(string label, int value, Source source, Type type)
: base(label, value, source, type) {
Debug.Assert(source == Source.Platform || source == Source.Project);
Debug.Assert(source == Source.Platform || source == Source.Project ||
source == Source.Variable);
Debug.Assert(type == Type.ExternalAddr || type == Type.Constant);
Xrefs = new XrefSet();
Width = NO_WIDTH;
}
/// <summary>
@ -83,20 +91,10 @@ namespace SourceGen {
/// <param name="tag">Symbol tag, used for grouping platform symbols.</param>
public DefSymbol(string label, int value, Source source, Type type,
FormatDescriptor.SubType formatSubType, string comment, string tag)
: this(label, value, source, type) {
Debug.Assert(comment != null);
Debug.Assert(tag != null);
// Length doesn't matter; use 1 to get prefab object.
DataDescriptor = FormatDescriptor.Create(1,
FormatDescriptor.Type.NumericLE, formatSubType);
Comment = comment;
Tag = tag;
}
: this(label, value, source, type, formatSubType, comment, tag, NO_WIDTH) { }
/// <summary>
/// Constructor.
/// Constructor. Used for local variables, which have a meaningful width.
/// </summary>
/// <param name="label">Symbol's label.</param>
/// <param name="value">Symbol's value.</param>
@ -109,9 +107,15 @@ namespace SourceGen {
/// <param name="width">Variable width.</param>
public DefSymbol(string label, int value, Source source, Type type,
FormatDescriptor.SubType formatSubType, string comment, string tag, int width)
: this(label, value, source, type, formatSubType, comment, tag) {
Debug.Assert(width == NO_WIDTH || (width >= MIN_WIDTH && width <= MAX_WIDTH));
Width = width;
: this(label, value, source, type) {
Debug.Assert(comment != null);
Debug.Assert(tag != null);
DataDescriptor = FormatDescriptor.Create(width,
FormatDescriptor.Type.NumericLE, formatSubType);
Comment = comment;
Tag = tag;
}
/// <summary>
@ -130,6 +134,35 @@ namespace SourceGen {
Tag = string.Empty;
}
public static bool operator ==(DefSymbol a, DefSymbol b) {
if (ReferenceEquals(a, b)) {
return true; // same object, or both null
}
if (ReferenceEquals(a, null) || ReferenceEquals(b, null)) {
return false; // one is null
}
// All fields must be equal, except Xrefs.
if (a.DataDescriptor != b.DataDescriptor ||
a.Comment != b.Comment ||
a.Tag != b.Tag) {
return false;
}
return true;
}
public static bool operator !=(DefSymbol a, DefSymbol b) {
return !(a == b);
}
public override bool Equals(object obj) {
return obj is DefSymbol && this == (DefSymbol)obj;
}
public override int GetHashCode() {
return base.GetHashCode() ^
DataDescriptor.GetHashCode() ^
Comment.GetHashCode() ^
Tag.GetHashCode();
}
public override string ToString() {
return base.ToString() + ":" + DataDescriptor + ";" + Comment +
(string.IsNullOrEmpty(Tag) ? "" : " [" + Tag + "]");

View File

@ -43,41 +43,67 @@ namespace SourceGen {
// at any time. Smaller items are kept in arrays, with one entry per byte
// of file data.
// Length of input data. (This is redundant with FileData.Length while in memory,
// but we want this value to be serialized into the project file.)
/// <summary>
/// Length of input data. (This is redundant with FileData.Length while in memory,
/// but we want this value to be serialized into the project file.)
/// </summary>
public int FileDataLength { get; private set; }
// CRC-32 on input data.
/// <summary>
/// CRC-32 on input data.
/// </summary>
public uint FileDataCrc32 { get; private set; }
// Map file offsets to addresses.
public AddressMap AddrMap { get { return mAddrMap; } }
private AddressMap mAddrMap;
/// <summary>
/// Map file offsets to addresses.
/// </summary>
public AddressMap AddrMap { get; private set; }
// Type hints. Default value is "no hint".
/// <summary>
/// Type hints. Default value is "no hint".
/// </summary>
public CodeAnalysis.TypeHint[] TypeHints { get; private set; }
// Status flag overrides. Default value is "all unspecified".
/// <summary>
/// Status flag overrides. Default value is "all unspecified".
/// </summary>
public StatusFlags[] StatusFlagOverrides { get; private set; }
// End-of-line comments. Empty string means "no comment".
/// <summary>
/// End-of-line comments. Empty string means "no comment".
/// </summary>
public string[] Comments { get; private set; }
// Full line, possibly multi-line comments.
/// <summary>
/// Full line, possibly multi-line comments.
/// </summary>
public Dictionary<int, MultiLineComment> LongComments { get; private set; }
// Notes, which are like comments but not included in the assembled output.
/// <summary>
/// Notes, which are like comments but not included in the assembled output.
/// </summary>
public SortedList<int, MultiLineComment> Notes { get; private set; }
// Labels, defined by the user; uses file offset as key. Ideally the label names
// are unique, but there are ways around that.
/// <summary>
/// Labels, defined by the user; uses file offset as key. Ideally the label names
/// are unique, but there are ways around that.
/// </summary>
public Dictionary<int, Symbol> UserLabels { get; private set; }
// Format descriptors for operands and data items; uses file offset as key.
/// <summary>
/// Local variable tables.
/// </summary>
public SortedList<int, LocalVariableTable> LvTables { get; private set; }
/// <summary>
/// Format descriptors for operands and data items; uses file offset as key.
/// </summary>
public SortedList<int, FormatDescriptor> OperandFormats { get; private set; }
// Project properties. Includes CPU type, platform symbol file names, project
// symbols, etc.
/// <summary>
/// Project properties. Includes CPU type, platform symbol file names, project
/// symbols, etc.
/// </summary>
public ProjectProperties ProjectProps { get; private set; }
#endregion // data to save & restore
@ -177,8 +203,8 @@ namespace SourceGen {
FileDataLength = fileDataLen;
ProjectPathName = string.Empty;
mAddrMap = new AddressMap(fileDataLen);
mAddrMap.Set(0, 0x1000); // default load address to $1000; override later
AddrMap = new AddressMap(fileDataLen);
AddrMap.Set(0, 0x1000); // default load address to $1000; override later
// Default value is "no hint".
TypeHints = new CodeAnalysis.TypeHint[fileDataLen];
@ -198,6 +224,7 @@ namespace SourceGen {
UserLabels = new Dictionary<int, Symbol>();
OperandFormats = new SortedList<int, FormatDescriptor>();
LvTables = new SortedList<int, LocalVariableTable>();
ProjectProps = new ProjectProperties();
SymbolTable = new SymbolTable();
@ -273,15 +300,15 @@ namespace SourceGen {
// address as the start of the file. The overlapping-address code should do
// the right thing with it.
int loadAddr = RawData.GetWord(mFileData, 0, 2, false);
mAddrMap.Set(0, loadAddr);
mAddrMap.Set(2, loadAddr);
AddrMap.Set(0, loadAddr);
AddrMap.Set(2, loadAddr);
OperandFormats[0] = FormatDescriptor.Create(2, FormatDescriptor.Type.NumericLE,
FormatDescriptor.SubType.None);
TypeHints[0] = CodeAnalysis.TypeHint.NoHint;
TypeHints[2] = CodeAnalysis.TypeHint.Code;
} else {
int loadAddr = SystemDefaults.GetLoadAddress(sysDef);
mAddrMap.Set(0, loadAddr);
AddrMap.Set(0, loadAddr);
}
foreach (string str in sysDef.SymbolFiles) {
@ -590,7 +617,7 @@ namespace SourceGen {
reanalysisTimer.StartTask("CodeAnalysis.Analyze");
CodeAnalysis ca = new CodeAnalysis(mFileData, CpuDef, mAnattribs, mAddrMap,
CodeAnalysis ca = new CodeAnalysis(mFileData, CpuDef, mAnattribs, AddrMap,
TypeHints, StatusFlagOverrides, ProjectProps.EntryFlags, mScriptManager,
debugLog);
@ -713,7 +740,7 @@ namespace SourceGen {
}
int expectedAddr = kvp.Value.Value;
Debug.Assert(expectedAddr == mAddrMap.OffsetToAddress(offset));
Debug.Assert(expectedAddr == AddrMap.OffsetToAddress(offset));
// Add direct reference to the UserLabels Symbol object.
mAnattribs[offset].Symbol = kvp.Value;
@ -821,7 +848,7 @@ namespace SourceGen {
foreach (KeyValuePair<int, Symbol> kvp in UserLabels) {
int offset = kvp.Key;
Symbol sym = kvp.Value;
int expectedAddr = mAddrMap.OffsetToAddress(offset);
int expectedAddr = AddrMap.OffsetToAddress(offset);
if (sym.Value != expectedAddr) {
Symbol newSym = new Symbol(sym.Label, expectedAddr, sym.SymbolSource,
sym.SymbolType);
@ -960,6 +987,8 @@ namespace SourceGen {
// Clear previous cross-reference data from project/platform symbols. These
// symbols don't have file offsets, so we can't store them in the main mXrefs
// list.
// TODO(someday): DefSymbol is otherwise immutable. We should put these elsewhere,
// maybe a Dictionary<DefSymbol, XrefSet>? Just mind the garbage collection.
foreach (Symbol sym in SymbolTable) {
if (sym is DefSymbol) {
(sym as DefSymbol).Xrefs.Clear();
@ -1466,7 +1495,8 @@ namespace SourceGen {
/// </summary>
/// <param name="cs">Set of changes to apply.</param>
/// <param name="backward">If set, undo the changes instead.</param>
/// <param name="affectedOffsets">List of offsets affected by change.</param>
/// <param name="affectedOffsets">List of offsets affected by change. Only meaningful
/// when the result is not "None".</param>
/// <returns>An indication of the level of reanalysis required. If this returns None,
/// the list of offsets to update will be in affectedOffsets.</returns>
public UndoableChange.ReanalysisScope ApplyChanges(ChangeSet cs, bool backward,
@ -1724,6 +1754,26 @@ namespace SourceGen {
LoadExternalFiles();
}
}
// ignore affectedOffsets
Debug.Assert(uc.ReanalysisRequired ==
UndoableChange.ReanalysisScope.CodeAndData);
break;
case UndoableChange.ChangeType.SetLocalVariableTable: {
LvTables.TryGetValue(offset, out LocalVariableTable current);
if (current != (LocalVariableTable)oldValue) {
Debug.WriteLine("GLITCH: old lvt value mismatch: current=" +
current + " old=" + (LocalVariableTable)oldValue);
Debug.Assert(false);
}
if (newValue == null) {
LvTables.Remove(offset);
} else {
LvTables[offset] = (LocalVariableTable)newValue;
}
// ignore affectedOffsets
Debug.Assert(uc.ReanalysisRequired ==
UndoableChange.ReanalysisScope.DataOnly);
}
break;
default:
break;

View File

@ -101,6 +101,9 @@ namespace SourceGen {
OrgDirective = 1 << 5,
EquDirective = 1 << 6,
RegWidthDirective = 1 << 7,
// Additional metadata.
LocalVariableTable = 1 << 8,
}
/// <summary>

View File

@ -1686,18 +1686,33 @@ namespace SourceGen {
}
public bool CanEditLocalVariableTable() {
return true; // TODO
if (SelectionAnalysis.mNumItemsSelected != 1) {
return false;
}
EntityCounts counts = SelectionAnalysis.mEntityCounts;
// Single line of code, or a local variable table.
return SelectionAnalysis.mLineType == LineListGen.Line.Type.Code ||
SelectionAnalysis.mLineType == LineListGen.Line.Type.LocalVariableTable;
}
private LocalVariableTable lvt = new LocalVariableTable();
public void EditLocalVariableTable() {
// TODO
int selIndex = mMainWin.CodeListView_GetFirstSelectedIndex();
int offset = CodeLineList[selIndex].FileOffset;
mProject.LvTables.TryGetValue(offset, out LocalVariableTable oldLvt);
EditLocalVariableTable dlg = new EditLocalVariableTable(mMainWin, mProject.SymbolTable,
mOutputFormatter, lvt);
mOutputFormatter, oldLvt);
if (dlg.ShowDialog() != true) {
return;
}
lvt = dlg.NewTable;
if (oldLvt == dlg.NewTable) {
Debug.WriteLine("LvTable unchanged");
return;
}
UndoableChange uc = UndoableChange.CreateLocalVariableTableChange(offset,
oldLvt, dlg.NewTable);
ChangeSet cs = new ChangeSet(uc);
ApplyUndoableChanges(cs);
}
public bool CanEditLongComment() {
@ -1721,18 +1736,18 @@ namespace SourceGen {
if (mProject.LongComments.TryGetValue(offset, out MultiLineComment oldComment)) {
dlg.LongComment = oldComment;
}
dlg.ShowDialog();
if (dlg.ShowDialog() != true) {
return;
}
if (dlg.DialogResult == true) {
MultiLineComment newComment = dlg.LongComment;
if (oldComment != newComment) {
Debug.WriteLine("Changing long comment at +" + offset.ToString("x6"));
MultiLineComment newComment = dlg.LongComment;
if (oldComment != newComment) {
Debug.WriteLine("Changing long comment at +" + offset.ToString("x6"));
UndoableChange uc = UndoableChange.CreateLongCommentChange(offset,
oldComment, newComment);
ChangeSet cs = new ChangeSet(uc);
ApplyUndoableChanges(cs);
}
UndoableChange uc = UndoableChange.CreateLongCommentChange(offset,
oldComment, newComment);
ChangeSet cs = new ChangeSet(uc);
ApplyUndoableChanges(cs);
}
}
@ -1940,7 +1955,7 @@ namespace SourceGen {
Debug.Assert(origDefSym.SymbolSource == Symbol.Source.Project);
EditDefSymbol dlg = new EditDefSymbol(mMainWin, mOutputFormatter,
mProject.ProjectProps.ProjectSyms, origDefSym);
mProject.ProjectProps.ProjectSyms, origDefSym, null, false);
if (dlg.ShowDialog() == true) {
ProjectProperties newProps = new ProjectProperties(mProject.ProjectProps);
newProps.ProjectSyms.Remove(origDefSym.Label);

View File

@ -316,6 +316,21 @@ namespace SourceGen {
Comment = defSym.Comment;
}
}
public class SerLocalVariableTable {
public List<SerDefSymbol> Variables { get; set; }
public bool ClearPrevious { get; set; }
public SerLocalVariableTable() { }
public SerLocalVariableTable(LocalVariableTable varTab) {
Variables = new List<SerDefSymbol>(varTab.Variables.Count);
foreach (KeyValuePair<string, DefSymbol> kvp in varTab.Variables) {
// Note kvp.Key is redundant -- same as kvp.Value.Label
Variables.Add(new SerDefSymbol(kvp.Value));
}
ClearPrevious = varTab.ClearPrevious;
}
}
// Fields are serialized to/from JSON. Do not change the field names.
public int _ContentVersion { get; set; }
@ -330,6 +345,7 @@ namespace SourceGen {
public Dictionary<string, SerMultiLineComment> Notes { get; set; }
public Dictionary<string, SerSymbol> UserLabels { get; set; }
public Dictionary<string, SerFormatDescriptor> OperandFormats { get; set; }
public Dictionary<string, SerLocalVariableTable> LvTables { get; set; }
/// <summary>
/// Serializes a DisasmProject into an augmented JSON string.
@ -413,6 +429,12 @@ namespace SourceGen {
spf.OperandFormats.Add(kvp.Key.ToString(), new SerFormatDescriptor(kvp.Value));
}
// Convert local variable tables to serializable form.
spf.LvTables = new Dictionary<string, SerLocalVariableTable>();
foreach (KeyValuePair<int, LocalVariableTable> kvp in proj.LvTables) {
spf.LvTables.Add(kvp.Key.ToString(), new SerLocalVariableTable(kvp.Value));
}
spf.ProjectProps = new SerProjectProperties(proj.ProjectProps);
JavaScriptSerializer ser = new JavaScriptSerializer();
@ -502,16 +524,11 @@ namespace SourceGen {
// Deserialize ProjectProperties: project symbols.
foreach (KeyValuePair<string, SerDefSymbol> kvp in spf.ProjectProps.ProjectSyms) {
if (!CreateSymbol(kvp.Value, report, out Symbol sym)) {
if (!CreateDefSymbol(kvp.Value, spf._ContentVersion, report,
out DefSymbol defSym)) {
continue;
}
if (!CreateFormatDescriptor(kvp.Value.DataDescriptor, spf._ContentVersion, report,
out FormatDescriptor dfd)) {
continue;
}
proj.ProjectProps.ProjectSyms[sym.Label] =
new DefSymbol(sym, dfd, kvp.Value.Comment);
proj.ProjectProps.ProjectSyms[defSym.Label] = defSym;
}
// Deserialize address map.
@ -621,10 +638,9 @@ namespace SourceGen {
}
// Deserialize operand format descriptors.
foreach (KeyValuePair<string,SerFormatDescriptor> kvp in spf.OperandFormats) {
foreach (KeyValuePair<string, SerFormatDescriptor> kvp in spf.OperandFormats) {
if (!ParseValidateKey(kvp.Key, spf.FileDataLength,
Res.Strings.PROJECT_FIELD_OPERAND_FORMAT, report,
out int intKey)) {
Res.Strings.PROJECT_FIELD_OPERAND_FORMAT, report, out int intKey)) {
continue;
}
@ -632,6 +648,7 @@ namespace SourceGen {
out FormatDescriptor dfd)) {
continue;
}
// Extra validation: make sure dfd doesn't run off end.
if (intKey < 0 || intKey + dfd.Length > spf.FileDataLength) {
report.Add(FileLoadItem.Type.Warning,
string.Format(Res.Strings.ERR_BAD_FD_FMT, intKey));
@ -644,6 +661,25 @@ namespace SourceGen {
proj.OperandFormats[intKey] = dfd;
}
// Deserialize local variable tables. These were added in v1.3.
if (spf.LvTables != null) {
foreach (KeyValuePair<string, SerLocalVariableTable> kvp in spf.LvTables) {
if (!ParseValidateKey(kvp.Key, spf.FileDataLength,
Res.Strings.PROJECT_FIELD_LV_TABLE, report, out int intKey)) {
continue;
}
if (!CreateLocalVariableTable(kvp.Value, spf._ContentVersion, report,
out LocalVariableTable lvt)) {
report.Add(FileLoadItem.Type.Warning,
string.Format(Res.Strings.ERR_BAD_LV_TABLE_FMT, intKey));
continue;
}
proj.LvTables[intKey] = lvt;
}
}
return true;
}
@ -652,7 +688,7 @@ namespace SourceGen {
/// </summary>
/// <param name="ssym">Deserialized data.</param>
/// <param name="report">Error report object.</param>
/// <param name="outSym"></param>
/// <param name="outSym">Created symbol.</param>
/// <returns>True on success.</returns>
private static bool CreateSymbol(SerSymbol ssym, FileLoadReport report,
out Symbol outSym) {
@ -671,6 +707,30 @@ namespace SourceGen {
return true;
}
/// <summary>
/// Creates a DefSymbol from a SerDefSymbol.
/// </summary>
/// <param name="serDefSym">Deserialized data.</param>
/// <param name="contentVersion">Serialization version.</param>
/// <param name="report">Error report object.</param>
/// <param name="outDefSym">Created symbol.</param>
/// <returns></returns>
private static bool CreateDefSymbol(SerDefSymbol serDefSym, int contentVersion,
FileLoadReport report, out DefSymbol outDefSym) {
outDefSym = null;
if (!CreateSymbol(serDefSym, report, out Symbol sym)) {
return false;
}
if (!CreateFormatDescriptor(serDefSym.DataDescriptor, contentVersion, report,
out FormatDescriptor dfd)) {
return false;
}
outDefSym = new DefSymbol(sym, dfd, serDefSym.Comment);
return true;
}
/// <summary>
/// Creates a FormatDescriptor from a SerFormatDescriptor.
/// </summary>
@ -755,6 +815,27 @@ namespace SourceGen {
return true;
}
/// <summary>
/// Creates a LocalVariableTable from a SerLocalVariableTable.
/// </summary>
/// <param name="serTable">Deserialized data.</param>
/// <param name="contentVersion">Serialization version.</param>
/// <param name="report">Error report object.</param>
/// <param name="outLvt">Created LocalVariableTable</param>
/// <returns>True on success.</returns>
private static bool CreateLocalVariableTable(SerLocalVariableTable serTable,
int contentVersion, FileLoadReport report, out LocalVariableTable outLvt) {
outLvt = new LocalVariableTable();
outLvt.ClearPrevious = serTable.ClearPrevious;
foreach (SerDefSymbol serDef in serTable.Variables) {
if (!CreateDefSymbol(serDef, contentVersion, report, out DefSymbol defSym)) {
return false;
}
outLvt.Variables.Add(defSym.Label, defSym);
}
return true;
}
/// <summary>
/// Parses an integer key that was stored as a string, and checks to see if the
/// value falls within an acceptable range.
@ -764,7 +845,7 @@ namespace SourceGen {
/// <param name="fieldName">Name of field, for error messages.</param>
/// <param name="report">Error report object.</param>
/// <param name="intKey">Returned integer key.</param>
/// <returns>True on success, false on failure.</returns>
/// <returns>True on success.</returns>
private static bool ParseValidateKey(string keyStr, int fileLen, string fieldName,
FileLoadReport report, out int intKey) {
if (!int.TryParse(keyStr, out intKey)) {

View File

@ -42,6 +42,7 @@ limitations under the License.
<system:String x:Key="str_ErrBadFdFormat">Bad format descriptor type</system:String>
<system:String x:Key="str_ErrBadFileLength">Bad file length</system:String>
<system:String x:Key="str_ErrBadIdent">Invalid file identifier</system:String>
<system:String x:Key="str_ErrBadLvTableFmt">Invalid local variable table at +{0:x6}</system:String>
<system:String x:Key="str_ErrBadRange">Bad range</system:String>
<system:String x:Key="str_ErrBadSymbolSt">Unknown Source or Type in symbol</system:String>
<system:String x:Key="str_ErrBadSymrefPart">Bad symbol reference part</system:String>
@ -101,6 +102,7 @@ limitations under the License.
<system:String x:Key="str_ProgressGeneratingFmt">Generating {0}...</system:String>
<system:String x:Key="str_ProjectFieldComment">comment</system:String>
<system:String x:Key="str_ProjectFieldLongComment">long comment</system:String>
<system:String x:Key="str_ProjectFieldLvTable">local variable table</system:String>
<system:String x:Key="str_ProjectFieldNote">note</system:String>
<system:String x:Key="str_ProjectFieldOperandFormat">operand format</system:String>
<system:String x:Key="str_ProjectFieldStatusFlags">status flag override</system:String>

View File

@ -65,6 +65,8 @@ namespace SourceGen.Res {
(string)Application.Current.FindResource("str_ErrBadFileLength");
public static string ERR_BAD_IDENT =
(string)Application.Current.FindResource("str_ErrBadIdent");
public static string ERR_BAD_LV_TABLE_FMT =
(string)Application.Current.FindResource("str_ErrBadLvTableFmt");
public static string ERR_BAD_RANGE =
(string)Application.Current.FindResource("str_ErrBadRange");
public static string ERR_BAD_SYMBOL_ST =
@ -183,6 +185,8 @@ namespace SourceGen.Res {
(string)Application.Current.FindResource("str_ProjectFieldComment");
public static string PROJECT_FIELD_LONG_COMMENT =
(string)Application.Current.FindResource("str_ProjectFieldLongComment");
public static string PROJECT_FIELD_LV_TABLE =
(string)Application.Current.FindResource("str_ProjectFieldLvTable");
public static string PROJECT_FIELD_NOTE =
(string)Application.Current.FindResource("str_ProjectFieldNote");
public static string PROJECT_FIELD_OPERAND_FORMAT =

View File

@ -22,16 +22,18 @@ namespace SourceGen {
/// </summary>
public class Symbol {
/// <summary>
/// Was the symbol defined by the user, or generated automatically?
/// How was the symbol defined?
/// </summary>
public enum Source {
// These are in order of highest to lowest precedence. This matters when
// looking up a symbol by value, since multiple symbols can have the same value.
// looking up a symbol by value from the symbol table, because multiple symbols
// can have the same value.
Unknown = 0,
User, // user-defined label
Project, // from project configuration file
Platform, // from platform definition file
Auto // auto-generated label
Auto, // auto-generated label
Variable // local variable
}
/// <summary>
@ -108,7 +110,8 @@ namespace SourceGen {
case Source.Auto: sts = "A"; break;
case Source.User: sts = "U"; break;
case Source.Platform: sts = "P"; break;
case Source.Project: sts = "R"; break;
case Source.Project: sts = "R"; break;
case Source.Variable: sts = "V"; break;
default: sts = "?"; break;
}
switch (SymbolType) {

View File

@ -53,7 +53,7 @@ are considered "uncategorized", so the uncategorized-data analysis must be repea
- When altering the way that data is formatted, it's useful to exercise the same code paths,
up to the point where the analyzer is called. We still want to go through all the steps that
update the display list and cause controls to be redrawn, but we don't want to actually change
anything in the DisasmProject. "Misc" means we do nothing but pretend there was a full update.
anything in the DisasmProject.
*** When can we get away with only updating part of the display list (re-analysis=none)?
- Changing a user label. All lines that reference the label need to be updated in the
@ -101,7 +101,10 @@ namespace SourceGen {
SetNote,
// Updates project properties.
SetProjectProperties
SetProjectProperties,
// Adds, updates, or removes a local variable table.
SetLocalVariableTable,
}
/// <summary>
@ -452,6 +455,28 @@ namespace SourceGen {
return uc;
}
/// <summary>
/// Creates an UndoableChange for a local variable table update.
/// </summary>
/// <param name="offset">Affected offset.</param>
/// <param name="oldLvTable">Old table.</param>
/// <param name="newLvTable">New table.</param>
/// <returns>Change record.</returns>
public static UndoableChange CreateLocalVariableTableChange(int offset,
LocalVariableTable oldLvTable, LocalVariableTable newLvTable) {
if (oldLvTable == newLvTable) {
Debug.WriteLine("No-op local variable table change");
}
UndoableChange uc = new UndoableChange();
uc.Type = ChangeType.SetLocalVariableTable;
uc.Offset = offset;
uc.OldValue = oldLvTable;
uc.NewValue = newLvTable;
uc.ReanalysisRequired = ReanalysisScope.DataOnly; // update dfds in Anattribs
return uc;
}
public override string ToString() {
return "[UC type=" + Type + " offset=+" +
(HasOffset ? Offset.ToString("x6") : "N/A") + "]";

View File

@ -87,6 +87,11 @@ namespace SourceGen.WpfGui {
/// </summary>
private SymbolTable mSymbolTable;
/// <summary>
/// Set to true if we should create a Variable rather than a project symbol.
/// </summary>
private bool mIsVariable;
// Saved off at dialog load time.
private Brush mDefaultLabelColor;
@ -101,33 +106,28 @@ namespace SourceGen.WpfGui {
/// Constructor, for editing a project symbol.
/// </summary>
public EditDefSymbol(Window owner, Formatter formatter,
SortedList<string, DefSymbol> defList, DefSymbol defSym) {
SortedList<string, DefSymbol> defList, DefSymbol defSym,
SymbolTable symbolTable, bool isVariable) {
InitializeComponent();
Owner = owner;
DataContext = this;
mNumFormatter = formatter;
mDefSymbolList = defList;
mOldSym = defSym;
mSymbolTable = symbolTable;
mIsVariable = isVariable;
Label = Value = VarWidth = Comment = string.Empty;
widthEntry1.Visibility = widthEntry2.Visibility = labelUniqueLabel.Visibility =
Visibility.Collapsed;
projectLabelUniqueLabel.Visibility = Visibility.Visible;
}
/// <summary>
/// Constructor, for editing a local variable.
/// </summary>
public EditDefSymbol(Window owner, Formatter formatter,
SortedList<string, DefSymbol> defList, DefSymbol defSym,
SymbolTable symbolTable) : this(owner, formatter, defList, defSym) {
mSymbolTable = symbolTable;
widthEntry1.Visibility = widthEntry2.Visibility = labelUniqueLabel.Visibility =
Visibility.Visible;
projectLabelUniqueLabel.Visibility = Visibility.Collapsed;
if (isVariable) {
widthEntry1.Visibility = widthEntry2.Visibility = labelUniqueLabel.Visibility =
Visibility.Visible;
projectLabelUniqueLabel.Visibility = Visibility.Collapsed;
} else {
widthEntry1.Visibility = widthEntry2.Visibility = labelUniqueLabel.Visibility =
Visibility.Collapsed;
labelUniqueLabel.Visibility = Visibility.Collapsed;
}
}
private void Window_Loaded(object sender, RoutedEventArgs e) {
@ -137,7 +137,7 @@ namespace SourceGen.WpfGui {
Label = mOldSym.Label;
Value = mNumFormatter.FormatValueInBase(mOldSym.Value,
mOldSym.DataDescriptor.NumBase);
VarWidth = mOldSym.Width.ToString();
VarWidth = mOldSym.DataDescriptor.Length.ToString();
Comment = mOldSym.Comment;
if (mOldSym.SymbolType == Symbol.Type.Constant) {
@ -222,7 +222,8 @@ namespace SourceGen.WpfGui {
width = int.Parse(VarWidth);
}
NewSym = new DefSymbol(Label, value, Symbol.Source.Project,
NewSym = new DefSymbol(Label, value,
mIsVariable ? Symbol.Source.Project : Symbol.Source.Variable,
isConstant ? Symbol.Type.Constant : Symbol.Type.ExternalAddr,
subType, Comment, string.Empty, width);

View File

@ -19,12 +19,19 @@ limitations under the License.
xmlns:x="http://schemas.microsoft.com/winfx/2006/xaml"
xmlns:d="http://schemas.microsoft.com/expression/blend/2008"
xmlns:mc="http://schemas.openxmlformats.org/markup-compatibility/2006"
xmlns:system="clr-namespace:System;assembly=mscorlib"
xmlns:local="clr-namespace:SourceGen.WpfGui"
mc:Ignorable="d"
Title="Edit Local Variable Table"
SizeToContent="WidthAndHeight" ResizeMode="NoResize"
ShowInTaskbar="False" WindowStartupLocation="CenterOwner"
Loaded="Window_Loaded">
<Window.Resources>
<system:String x:Key="str_ConfirmDelete">Are you sure you want to delete the entire table?</system:String>
<system:String x:Key="str_ConfirmDeleteCaption">Confirm Deletion</system:String>
</Window.Resources>
<Grid Margin="8">
<Grid.ColumnDefinitions>
<ColumnDefinition Width="*"/>
@ -76,7 +83,8 @@ limitations under the License.
Content="Clear values from previous tables"/>
<DockPanel Grid.Column="0" Grid.Row="3" Grid.ColumnSpan="2" Margin="0,8,0,0" LastChildFill="False">
<Button DockPanel.Dock="Left" Content="Delete Table" Width="120" Click="DeleteTableButton_Click"/>
<Button DockPanel.Dock="Left" Content="Delete Table" Width="120" IsEnabled="{Binding IsNotNewTable}"
Click="DeleteTableButton_Click"/>
<Button DockPanel.Dock="Right" Content="Cancel" Width="70" Margin="8,0,0,0" IsCancel="True"/>
<Button DockPanel.Dock="Right" Grid.Column="1" Content="OK" Width="70"
IsDefault="True" Click="OkButton_Click"/>

View File

@ -58,11 +58,24 @@ namespace SourceGen.WpfGui {
public ObservableCollection<FormattedSymbol> Variables { get; private set; } =
new ObservableCollection<FormattedSymbol>();
/// <summary>
/// Clear-previous flag.
/// </summary>
public bool ClearPrevious {
get { return mWorkTable.ClearPrevious; }
set { mWorkTable.ClearPrevious = value; OnPropertyChanged(); }
}
/// <summary>
/// True if this is not a new table. (Using "not" because that's the sense we
/// need in XAML.)
/// </summary>
public bool IsNotNewTable {
get { return mIsNotNewTable; }
set { mIsNotNewTable = value; OnPropertyChanged(); }
}
private bool mIsNotNewTable;
/// <summary>
/// Working set. Used internally to hold state.
/// </summary>
@ -85,6 +98,9 @@ namespace SourceGen.WpfGui {
}
/// <summary>
/// Constructor. lvt will be null when creating a new entry.
/// </summary>
public EditLocalVariableTable(Window owner, SymbolTable symbolTable, Formatter formatter,
LocalVariableTable lvt) {
InitializeComponent();
@ -96,6 +112,7 @@ namespace SourceGen.WpfGui {
if (lvt != null) {
mWorkTable = new LocalVariableTable(lvt);
mIsNotNewTable = true;
} else {
mWorkTable = new LocalVariableTable();
}
@ -126,8 +143,7 @@ namespace SourceGen.WpfGui {
defSym.Label,
mFormatter.FormatValueInBase(defSym.Value, defSym.DataDescriptor.NumBase),
typeStr,
defSym.Width == DefSymbol.NO_WIDTH ?
string.Empty : defSym.Width.ToString(),
defSym.DataDescriptor.Length.ToString(),
defSym.Comment);
Variables.Add(fsym);
@ -148,7 +164,13 @@ namespace SourceGen.WpfGui {
}
private void DeleteTableButton_Click(object sender, RoutedEventArgs e) {
// TODO - get confirmation, then set result=true with LvTable=null
MessageBoxResult result = MessageBox.Show((string)FindResource("str_ConfirmDelete"),
(string)FindResource("str_ConfirmDeleteCaption"),
MessageBoxButton.YesNo, MessageBoxImage.Question);
if (result == MessageBoxResult.Yes) {
NewTable = null;
DialogResult = true;
}
}
private void SymbolsListView_SelectionChanged(object sender, SelectionChangedEventArgs e) {
@ -167,7 +189,7 @@ namespace SourceGen.WpfGui {
private void NewSymbolButton_Click(object sender, RoutedEventArgs e) {
EditDefSymbol dlg = new EditDefSymbol(this, mFormatter, mWorkTable.Variables, null,
mSymbolTable);
mSymbolTable, true);
dlg.ShowDialog();
if (dlg.DialogResult == true) {
Debug.WriteLine("ADD: " + dlg.NewSym);
@ -191,7 +213,7 @@ namespace SourceGen.WpfGui {
private void DoEditSymbol(DefSymbol defSym) {
EditDefSymbol dlg = new EditDefSymbol(this, mFormatter, mWorkTable.Variables, defSym,
mSymbolTable);
mSymbolTable, true);
dlg.ShowDialog();
if (dlg.DialogResult == true) {
// Label might have changed, so remove old before adding new.

View File

@ -431,7 +431,8 @@ namespace SourceGen.WpfGui {
}
private void NewSymbolButton_Click(object sender, RoutedEventArgs e) {
EditDefSymbol dlg = new EditDefSymbol(this, mFormatter, mWorkProps.ProjectSyms, null);
EditDefSymbol dlg = new EditDefSymbol(this, mFormatter, mWorkProps.ProjectSyms, null,
null, false);
dlg.ShowDialog();
if (dlg.DialogResult == true) {
Debug.WriteLine("ADD: " + dlg.NewSym);
@ -467,7 +468,7 @@ namespace SourceGen.WpfGui {
private void DoEditSymbol(DefSymbol defSym) {
EditDefSymbol dlg = new EditDefSymbol(this, mFormatter, mWorkProps.ProjectSyms,
defSym);
defSym, null, false);
dlg.ShowDialog();
if (dlg.DialogResult == true) {
// Label might have changed, so remove old before adding new.