1
0
mirror of https://github.com/fadden/6502bench.git synced 2024-12-29 08:29:52 +00:00

Instruction operand editor rework, part 2

Implemented local variable editing.  Operands that have a local
variable reference, or are eligible to have one, can now be edited
directly from the instruction operand edit dialog.

Also, updated the code list double-click handler so that, if you
double-click on the opcode of an instruction that uses a local
variable reference, the selection and view will jump to the place
where that variable was defined.

Also, tweaked the way the References window refers to references
to an address that didn't use a symbol at that address.  Updated
the explanation in the manual, which was a bit confusing.

Also, fixed some odds and ends in the manual.

Also, fixed a nasty infinite recursion bug (issue #47).
This commit is contained in:
Andy McFadden 2019-09-07 18:57:22 -07:00
parent 2633720c82
commit e8ae534879
13 changed files with 486 additions and 58 deletions

View File

@ -339,12 +339,13 @@ namespace Asm65 {
}
/// <summary>
/// True if the operand is an "extended immediate" value, which includes PEA in
/// addition to Imm/ImmLongA/ImmLongXY.
/// True if the operand is an "extended immediate" value, which includes PEA and MVN/MVP
/// in addition to Imm/ImmLongA/ImmLongXY.
/// </summary>
public bool IsExtendedImmediate {
get {
return IsImmediate || AddrMode == AddressMode.StackAbs;
return IsImmediate || AddrMode == AddressMode.StackAbs ||
AddrMode == AddressMode.BlockMove;
}
}

View File

@ -250,7 +250,7 @@ namespace SourceGen.AsmGen {
XrefSet xrefs = mProject.GetXrefSet(i);
if (xrefs != null) {
foreach (XrefSet.Xref xref in xrefs) {
if (!xref.IsSymbolic) {
if (!xref.IsByName) {
continue;
}

View File

@ -195,6 +195,8 @@ namespace SourceGen {
// the current offset. We may need to do this even when variables can be
// redefined, because we might have a variable that's a duplicate of a user label
// or project symbol.
// Start by applying the de-duplication map.
string label = symRef.Label;
if (mDupRemap.TryGetValue(symRef.Label, out string remap)) {
label = remap;
@ -213,6 +215,36 @@ namespace SourceGen {
return defSym;
}
/// <summary>
/// Identifies the LocalVariableTable that defined the symbol reference.
/// </summary>
/// <param name="offset">Offset at which the symbol was referenced.</param>
/// <param name="symRef">Reference to symbol.</param>
/// <returns>Table index, or -1 if not found.</returns>
public int GetDefiningTableOffset(int offset, WeakSymbolRef symRef) {
// symRef is the non-uniquified, non-de-duplicated symbol that was generated
// during the analysis pass. This matches the contents of the tables, so we don't
// need to transform it at all.
//
// Walk backward through the list of tables until we find a match.
IList<int> keys = mLvTables.Keys;
for (int i = keys.Count - 1; i >= 0; i--) {
if (keys[i] > offset) {
// table comes after the point of reference
continue;
}
if (mLvTables.Values[i].GetByLabel(symRef.Label) != null) {
// found it
return keys[i];
}
}
// if we didn't find it, it doesn't exist... right?
Debug.Assert(mCurrentTable.GetByLabel(symRef.Label) == null);
return -1;
}
/// <summary>
/// Gets a LocalVariableTable that is the result of merging all tables up to this point.
/// </summary>
@ -223,6 +255,25 @@ namespace SourceGen {
return mCurrentTable;
}
/// <summary>
/// Finds the closest table that is defined at or before the specified offset.
/// </summary>
/// <param name="offset">Target offset.</param>
/// <returns>The table's definition offset, or -1 if no tables were defined before this
/// point.</returns>
public int GetNearestTableOffset(int offset) {
int nearest = -1;
// Could do a smarter search, but I'm expecting the set to be small.
foreach (KeyValuePair<int, LocalVariableTable> kvp in mLvTables) {
if (kvp.Key > offset) {
break;
}
nearest = kvp.Key;
}
return nearest;
}
/// <summary>
/// Generates a list of variables defined at the specified offset, if a table is
/// associated with that offset.

View File

@ -1508,10 +1508,14 @@ namespace SourceGen {
GoToOffset(attr.OperandOffset, false, true);
} else if (dfd != null && dfd.HasSymbol) {
// Operand has a symbol, do a symbol lookup.
int labelOffset = mProject.FindLabelOffsetByName(
dfd.SymbolRef.Label);
if (labelOffset >= 0) {
GoToOffset(labelOffset, false, true);
if (dfd.SymbolRef.IsVariable) {
GoToVarDefinition(line.FileOffset, dfd.SymbolRef, true);
} else {
int labelOffset = mProject.FindLabelOffsetByName(
dfd.SymbolRef.Label);
if (labelOffset >= 0) {
GoToOffset(labelOffset, false, true);
}
}
} else if (attr.IsDataStart || attr.IsInlineDataStart) {
// If it's an Address or Symbol, we can try to resolve
@ -1926,8 +1930,23 @@ namespace SourceGen {
UndoableChange uc = UndoableChange.CreateOperandFormatChange(offset,
dfd, dlg.FormatDescriptorResult);
cs.Add(uc);
} else {
Debug.WriteLine("No change to operand format");
}
// We can't delete an LvTable, so a null value here means no changes made.
if (dlg.LocalVariableResult != null) {
int tableOffset = dlg.LocalVariableTableOffsetResult;
LocalVariableTable lvt = mProject.LvTables[tableOffset];
Debug.Assert(lvt != null); // cannot create or delete a table
UndoableChange uc = UndoableChange.CreateLocalVariableTableChange(tableOffset,
lvt, dlg.LocalVariableResult);
cs.Add(uc);
} else {
Debug.WriteLine("No change to LvTable");
}
#endif
Debug.WriteLine("EditInstructionOperand: " + cs.Count + " changes");
if (cs.Count != 0) {
ApplyUndoableChanges(cs);
}
@ -1996,7 +2015,7 @@ namespace SourceGen {
Debug.Assert(origDefSym.SymbolSource == Symbol.Source.Project);
EditDefSymbol dlg = new EditDefSymbol(mMainWin, mOutputFormatter,
mProject.ProjectProps.ProjectSyms, origDefSym, null, false);
mProject.ProjectProps.ProjectSyms, origDefSym, null);
if (dlg.ShowDialog() == true) {
ProjectProperties newProps = new ProjectProperties(mProject.ProjectProps);
newProps.ProjectSyms.Remove(origDefSym.Label);
@ -2345,6 +2364,67 @@ namespace SourceGen {
}
}
/// <summary>
/// Moves the view and selection to the definition of a local variable.
/// </summary>
/// <param name="offset">Offset at which the variable was referenced.</param>
/// <param name="symRef">Reference to variable.</param>
public void GoToVarDefinition(int offset, WeakSymbolRef symRef, bool doPush) {
Debug.Assert(offset >= 0);
Debug.Assert(symRef.IsVariable);
LocalVariableLookup lvLookup = new LocalVariableLookup(mProject.LvTables, mProject,
false);
int varOffset = lvLookup.GetDefiningTableOffset(offset, symRef);
if (varOffset <= 0) {
Debug.WriteLine("Local variable not found; offset=" + offset + " ref=" + symRef);
return;
}
// We have the offset to which the local variable table is bound. We need to
// walk down until we find the variable definitions, and find the line with the
// matching symbol.
//
// We're comparing to the formatted strings -- safer than trying to find the symbol
// in the table and then guess at how the table arranges itself for display -- so we
// need to compare the formatted form of the label.
string cmpStr = mOutputFormatter.FormatVariableLabel(symRef.Label);
int lineIndex = CodeLineList.FindLineIndexByOffset(varOffset);
while (lineIndex < mProject.FileDataLength) {
LineListGen.Line line = CodeLineList[lineIndex];
if (line.FileOffset != varOffset) {
// we've gone too far
Debug.WriteLine("ran out of LV table");
return;
}
if (line.LineType == LineListGen.Line.Type.LocalVariableTable) {
DisplayList.FormattedParts parts = CodeLineList.GetFormattedParts(lineIndex);
if (cmpStr.Equals(parts.Label)) {
// Eureka
NavStack.Location prevLoc = GetCurrentlySelectedLocation();
mMainWin.CodeListView_EnsureVisible(lineIndex);
// Update the selection.
mMainWin.CodeListView_DeselectAll();
mMainWin.CodeListView_SelectRange(lineIndex, 1);
if (doPush) {
// Update the back stack and associated controls.
mNavStack.Push(prevLoc);
}
return;
} else {
//Debug.WriteLine("Var: '" + cmpStr + "' != '" + parts.Label + "'");
}
}
lineIndex++;
}
}
private NavStack.Location GetCurrentlySelectedLocation() {
int index = mMainWin.CodeListView_GetFirstSelectedIndex();
if (index < 0) {
@ -2604,6 +2684,7 @@ namespace SourceGen {
return false;
}
private bool mUpdatingSelectionHighlight; // recursion guard
/// <summary>
/// Updates the selection highlight. When a code item with an operand offset is
/// selected, such as a branch, we want to highlight the address and label of the
@ -2612,12 +2693,26 @@ namespace SourceGen {
private void UpdateSelectionHighlight() {
int targetIndex = FindSelectionHighlight();
if (mTargetHighlightIndex != targetIndex) {
if (mTargetHighlightIndex != targetIndex && !mUpdatingSelectionHighlight) {
Debug.WriteLine("Target highlight moving from " + mTargetHighlightIndex +
" to " + targetIndex);
// The highlight is currently implemented by modifying the item in the
// display list. Because those items are immutable, we have to remove the
// old and add a new. The WPF ListView maintains its selection by object
// reference, so replacing an item requires removing the old item from the
// selection set and adding it to the new.
//
// So if a line references itself (like the ZipGS cache conditioner loop does),
// it will be the selected line while we're doing this little dance. When the
// calls below update the selection, this method will be called again. This
// turns into infinite recursion.
mUpdatingSelectionHighlight = true;
mMainWin.CodeListView_RemoveSelectionHighlight(mTargetHighlightIndex);
mMainWin.CodeListView_AddSelectionHighlight(targetIndex);
mUpdatingSelectionHighlight = false;
mTargetHighlightIndex = targetIndex;
Debug.WriteLine("Selection highlight now " + targetIndex);
}
}
@ -3075,7 +3170,7 @@ namespace SourceGen {
MainWindow.ReferencesListItem rli = new MainWindow.ReferencesListItem(xr.Offset,
formatter.FormatOffset24(xr.Offset),
formatter.FormatAddress(mProject.GetAnattrib(xr.Offset).Address, showBank),
(xr.IsSymbolic ? "Sym " : "Num ") + typeStr +
(xr.IsByName ? "Sym " : "Oth ") + typeStr +
formatter.FormatAdjustment(-xr.Adjustment));
mMainWin.ReferencesList.Add(rli);

View File

@ -59,7 +59,7 @@ and 65816 code. The official web site is
<li><a href="mainwin.html#navigation">Navigation</a></li>
<li><a href="mainwin.html#hints">Adding and Removing Hints</a></li>
<li><a href="mainwin.html#split-address">Format Split-Address Table</a></li>
<li><a href="mainwin.html#toggle-format">Toggle Single-Byte Format</a></li>
<li><a href="mainwin.html#toggle-single">Toggle Single-Byte Format</a></li>
<li><a href="mainwin.html#format-as-word">Format As Word</a></li>
<li><a href="mainwin.html#toggle-data">Toggle Data Scan</a></li>
<li><a href="mainwin.html#clipboard">Copying to Clipboard</a></li>

View File

@ -92,8 +92,8 @@ assembler directive.</p>
<p>The list is divided into columns:</p>
<ul>
<li><b>Offset</b>. The offset within the file where the instruction
or data item starts. Always shown as a six-digit hex value
with a leading '+'.</li>
or data item starts. Throughout the UI, file offsets are shown as
six-digit hex values with a leading '+'.</li>
<li><b>Address</b>. The address where the assembled code will execute.
For 8-bit CPUs this is shown as a 4-digit hex number, for 16-bit
CPUs the bank is shown as well. Double-click on this field to open the
@ -104,8 +104,8 @@ assembler directive.</p>
to open the
<a href="tools.html#hexdump">Hex Dump Viewer</a>. This is
a floating window, so you can keep it open while you work.
Double-clicking in the bytes column in other rows will update
the window position and selection.</li>
Double-clicking in the bytes column while the window is open will
update the viewer's position and selection.</li>
<li><b>Flags</b>. This shows the state of the status flags as they
are before the instruction is executed. Double-click on this
field to open the
@ -126,7 +126,8 @@ assembler directive.</p>
will appear.
If you double-click this field for an instruction or data item
whose operand refers to an address in the file, the selection will
jump to that location.</li>
jump to that location. If the operand is a local variable, the
selection will jump to the point where the variable was defined.</li>
<li><b>Operand</b>. The instruction or data operand. Data operands
may span a large number of bytes. Double-click on this field to
open the
@ -151,8 +152,8 @@ to select an item, ctrl-left-click to toggle individual items on and
off, and shift-left-click to select a range. You can select all lines
with Edit &gt; Select All. Resize columns by
left-clicking on the divider in the header and dragging it.</p>
<p>Multi-line items, such as long comments or character strings, are
selected as a whole when any part is selected.</p>
<p>Selecting any part of a multi-line item, such as a long comment
or character string, effectively selects the entire item.</p>
<p>Right-clicking opens a menu. The contents are the same as those in
the Actions menu item in the menu bar. The set of options that are
@ -185,15 +186,22 @@ enabled will depend on what you have selected in the main window.</p>
Sets the name, value, and comment of the project symbol. Enabled
when a single equate directive, generated from a project symbol, is
selected.</li>
<li><a href="editors.html#lvtable">Edit Local Variable Table</a>.
Create, modify, or delete entries in a local variable table.</li>
<li><a href="#hints">Hinting</a> (Hint As Code Entry Point, Hint As
Data Start, Hint As Inline Data, Remove Hints). Enabled when one or more
code and data lines are selected. Remove Hints is only enabled when
at least one line has hints. The keyboard shortcuts for hints are
two-key combinations.</li>
<li><a href="#split-address">Format Split-Address Table</a>. Formats
a series of bytes as parts of a table of addresses.</li>
<li><a href="#toggle-single">Toggle Single-Byte Format</a>. Toggles
a range of lines between default format and single-byte format. Enabled
when one or more data lines are selected.</li>
<li><a href="#format-as-word">Format As Word</a>. Formats two bytes as
a 16-bit little-endian word.</li>
<li>Delete Note / Long Comment. Deletes the selected note or long
comment. Enabled when a single note or long comment is selected.</li>
<li><a href="tools.html#hexdump">Show Hex Dump</a>. Opens the hex dump
@ -211,7 +219,7 @@ are added to the undo/redo buffer. This has no fixed size limit, so no
matter how much you change, you can always undo back to the point where
the project was opened.</p>
<p>The undo history is not saved as part of the project. Closing a project
clears the buffer.</p>
clears it.</p>
<h3><a name="references">References Window</a></h3>
@ -224,21 +232,47 @@ type of reference will be shown.</p>
<p>The reference type indicates whether the origin is an instruction or
data operand, and provides an indication of the nature of the reference:</p>
<ul>
<li>call - subroutine call (e.g. JSR addr, JSL addr)</li>
<li>branch - conditional or unconditional branch (e.g. JMP addr, BCC addr)</li>
<li>read - read from memory (e.g. LDA addr, BIT addr)</li>
<li>write - write to memory (e.g. STA addr)</li>
<li>rmw - read-modify-write (e.g. LSR addr, TSB addr)</li>
<li>ref - address reference only (e.g. LDA #&lt;addr, PEA addr)</li>
<li>data - address referenced by data (e.g. .DD2 addr)</li>
<li>call - subroutine call
(e.g. <code>JSR addr</code>, <code>JSL addr</code>)</li>
<li>branch - conditional or unconditional branch
(e.g. <code>JMP addr</code>, <code>BCC addr</code>)</li>
<li>read - read from memory
(e.g. <code>LDA addr</code>, <code>BIT addr</code>)</li>
<li>write - write to memory
(e.g. <code>STA addr</code>)</li>
<li>rmw - read-modify-write
(e.g. <code>LSR addr</code>, <code>TSB addr</code>)</li>
<li>ref - reference to address by instruction
(e.g. <code>LDA #&lt;addr</code>, <code>PEA addr</code>)</li>
<li>data - reference to address by data
(e.g. <code>.DD2 addr</code>)</li>
</ul>
<p>In addition, the source will be identified as a symbolic ("Sym") or
numeric ("Num") reference. Most will be symbolic, because SourceGen
generates symbols automatically. However, references to addresses that
are offset from a symbol will also create a numeric reference entry.
For example, "LDA addr+1" will create a symbolic reference at &lt;addr&gt;
(shown in the table with an adjustment of "+1") and a numeric reference
at &lt;addr+1&gt;.</p>
<p>This will be prefixed with "Sym" or "Oth" to indicate whether or not
the reference used the label at the current address. To understand
this, consider that addresses can be referenced in different ways.
For example:</p>
<pre>
LDA DATA0
LDX DATA0+1
RTS
DATA0 .DD1 $80
DATA1 .DD2 $90
</pre>
<p>Both <code>DATA0</code> and <code>DATA1</code> are accessed, but
both operands used <code>DATA0</code>. When the <code>DATA0</code> line
is selected in the code list, the references window will show the
<code>LDA</code> and <code>LDX</code> instructions, because both
instructions referenced it. When <code>DATA1</code> is selected, the
references window will show the <code>LDX</code>, because that
instruction accessed <code>DATA1</code>'s location even though it didn't
use the symbol. To make the difference clear, the lines in the references
window will either show "Sym" (to indicate that the symbol at the selected
line was referenced) or "Oth" (to indicate that some other symbol, or no
symbol, was used).</p>
<p>This is also relevant for project and platform symbols. If the
DATA0/1 labels were project symbols, the entry for <code>DATA1</code>
would not be included in the list of equates, because the symbol is
never used.</p>
<p>Double-clicking on a reference moves the code list selection to that
reference, and adds the previous selection to the navigation stack.</p>
@ -412,7 +446,7 @@ is just significantly more convenient. It also does everything as a single
undoable action, so if it comes out looking wrong, just hit "undo".</p>
<h3><a name="toggle-format">Toggle Single-Byte Format</a></h3>
<h3><a name="toggle-single">Toggle Single-Byte Format</a></h3>
<p>The "Toggle Single-Byte Format" feature provides a quick way to
change a range of bytes to single bytes

View File

@ -51,7 +51,8 @@ limitations under the License.
<TextBlock Grid.Column="0" Grid.Row="1" Text="Value:"/>
<StackPanel Grid.Column="1" Grid.Row="1">
<TextBox Margin="0,1,0,0" Text="{Binding Value, UpdateSourceTrigger=PropertyChanged}"
FontFamily="{StaticResource GeneralMonoFont}"/>
FontFamily="{StaticResource GeneralMonoFont}"
IsReadOnly="{Binding ReadOnlyValueAndType}"/>
<TextBlock Name="valueRangeLabel" Text="• Value between 0-255, including width" Margin="0,4,0,0"/>
<TextBlock Name="valueUniqueLabel" Text="• Values in table must not overlap" Margin="0,4,0,0"/>
<TextBlock Name="valueNotesLabel" Text="• Decimal, hex ($), or binary (%)" Margin="0,4,0,16"/>
@ -72,7 +73,7 @@ limitations under the License.
</StackPanel>
<GroupBox Grid.Column="0" Grid.Row="4" Grid.ColumnSpan="2" Header="Symbol Type" Padding="4">
<StackPanel Orientation="Horizontal">
<StackPanel Orientation="Horizontal" IsEnabled="{Binding NotReadOnlyValueAndType}">
<RadioButton Content="Address" IsChecked="{Binding IsAddress}"/>
<RadioButton Content="Constant" Margin="24,0,0,0" IsChecked="{Binding IsConstant}"/>
</StackPanel>

View File

@ -79,6 +79,15 @@ namespace SourceGen.WpfGui {
}
private bool mIsConstant;
public bool ReadOnlyValueAndType {
get { return mReadOnlyValueAndType; }
set { mReadOnlyValueAndType = value; OnPropertyChanged(); }
}
public bool NotReadOnlyValueAndType {
get { return !mReadOnlyValueAndType; }
}
private bool mReadOnlyValueAndType;
/// <summary>
/// Format object to use when formatting addresses and constants.
/// </summary>
@ -115,11 +124,19 @@ namespace SourceGen.WpfGui {
/// <summary>
/// Constructor, for editing a project symbol.
/// Constructor, for editing a project or platform symbol.
/// </summary>
public EditDefSymbol(Window owner, Formatter formatter,
SortedList<string, DefSymbol> defList, DefSymbol defSym,
SymbolTable symbolTable, bool isVariable) {
SymbolTable symbolTable)
: this(owner, formatter, defList, defSym, symbolTable, false, false) { }
/// <summary>
/// Constructor, for editing a local variable.
/// </summary>
public EditDefSymbol(Window owner, Formatter formatter,
SortedList<string, DefSymbol> defList, DefSymbol defSym,
SymbolTable symbolTable, bool isVariable, bool lockValueAndType) {
InitializeComponent();
Owner = owner;
DataContext = this;
@ -129,6 +146,7 @@ namespace SourceGen.WpfGui {
mOldSym = defSym;
mSymbolTable = symbolTable;
mIsVariable = isVariable;
mReadOnlyValueAndType = lockValueAndType;
Label = Value = VarWidth = Comment = string.Empty;
if (isVariable) {

View File

@ -34,6 +34,9 @@ limitations under the License.
<system:String x:Key="str_SymbolNotUsed">N/A</system:String>
<system:String x:Key="str_SymbolNotValid">[invalid symbol name]</system:String>
<system:String x:Key="str_SymbolUnknown">?</system:String>
<system:String x:Key="str_CreateLocalVariable">Create Local Variable</system:String>
<system:String x:Key="str_EditLocalVariable">Edit Local Variable</system:String>
</Window.Resources>
<Grid Margin="8" Width="500">
@ -101,7 +104,7 @@ limitations under the License.
Visibility="{Binding IsSymbolAuto, Converter={StaticResource BoolToVis}}"/>
</StackPanel>
<StackPanel Grid.Column="0" Grid.Row="2" Orientation="Horizontal" Margin="18,4,0,0"
IsEnabled="{Binding IsChecked, ElementName=formatSymbolButton}">
IsEnabled="{Binding IsPartPanelEnabled}">
<RadioButton GroupName="Part" Content="Low"
IsChecked="{Binding FormatPartLow}"/>
<RadioButton GroupName="Part" Content="High" Margin="10,0,0,0"
@ -130,14 +133,19 @@ limitations under the License.
</Grid>
</GroupBox>
<StackPanel Grid.Column="0" Grid.Row="3" Grid.ColumnSpan="2" Orientation="Horizontal" Margin="0,8">
<TextBlock Text="Preview:"/>
<TextBox IsReadOnly="True" Margin="8,1,0,0" Width="300"
<!-- Preview box spans the full window. Most of the time this isn't necessary, but
sometimes you get long symbols with complex expressions, and the window needs to be
fairly wide. So might as well just use it all. -->
<DockPanel Grid.Column="0" Grid.Row="3" Grid.ColumnSpan="2" Margin="0,8">
<TextBlock DockPanel.Dock="Left" Text="Preview:"/>
<TextBox DockPanel.Dock="Left" IsReadOnly="True" Margin="8,1,0,0"
FontFamily="{StaticResource GeneralMonoFont}" Text="{Binding PreviewText}"/>
</StackPanel>
</DockPanel>
<!-- numeric reference box, for labels and project/platform symbols -->
<GroupBox Grid.Column="0" Grid.Row="4" Margin="0,4,0,0" Padding="2,4" Header="Numeric Address Reference">
<StackPanel>
<TextBlock Text="Operand is not an 8, 16, or 24-bit address"/>
<TextBlock Text="Address is inside the project"/>
<TextBlock Text="Address is NOT the start of a thing"/>
<TextBlock Text="Label at target address: ______"/>
@ -151,13 +159,25 @@ limitations under the License.
</StackPanel>
</GroupBox>
<!-- local variable controls -->
<GroupBox Grid.Column="1" Grid.Row="4" Margin="0,4,0,0" Padding="2,4" Header="Local Variable">
<StackPanel>
<TextBlock Text="Operand is NOT ZP or stack relative"/>
<TextBlock Text="No local variable tables found before this point" TextWrapping="Wrap"/>
<TextBlock Text="No entry in nearest table"/>
<TextBlock Text="Match found: [VAR]"/>
<Button Width="120" Content="Create/Edit LV Entry"/>
<TextBlock Text="Operand is not a zero page address or stack relative" TextWrapping="Wrap"
Visibility="{Binding ShowLvNotApplicable, Converter={StaticResource BoolToVis}}"/>
<TextBlock Text="No local variable tables have been defined before this point" TextWrapping="Wrap"
Visibility="{Binding ShowLvTableNotFound, Converter={StaticResource BoolToVis}}"/>
<TextBlock Text="No matching local variable found" TextWrapping="Wrap"
Visibility="{Binding ShowLvNoMatchFound, Converter={StaticResource BoolToVis}}"/>
<StackPanel Orientation="Horizontal"
Visibility="{Binding ShowLvMatchFound, Converter={StaticResource BoolToVis}}">
<TextBlock Text="Match found:"/>
<TextBlock Margin="4,2,0,0" FontFamily="{StaticResource GeneralMonoFont}"
Text="{Binding LocalVariableLabel, FallbackValue=VAR_NAME}"/>
</StackPanel>
<Button Width="150" Margin="0,4,0,0"
Content="{Binding CreateEditLocalVariableText, FallbackValue=Diddle Local Variable}"
Visibility="{Binding ShowLvCreateEditButton, Converter={StaticResource BoolToVis}}"
Click="EditLocalVariableButton_Click"/>
</StackPanel>
</GroupBox>

View File

@ -33,10 +33,24 @@ namespace SourceGen.WpfGui {
/// </summary>
public FormatDescriptor FormatDescriptorResult { get; private set; }
/// <summary>
/// Updated local variable table. Will be null if no changes were made.
/// </summary>
public LocalVariableTable LocalVariableResult { get; private set; }
/// <summary>
/// Offset of the local variable table we updated in LocalVariableResult.
/// </summary>
public int LocalVariableTableOffsetResult { get; private set; }
private readonly string SYMBOL_NOT_USED;
private readonly string SYMBOL_UNKNOWN;
private readonly string SYMBOL_INVALID;
private readonly string CREATE_LOCAL_VARIABLE;
private readonly string EDIT_LOCAL_VARIABLE;
/// <summary>
/// Project reference.
/// </summary>
@ -111,6 +125,9 @@ namespace SourceGen.WpfGui {
SYMBOL_INVALID = (string)FindResource("str_SymbolNotValid");
SYMBOL_UNKNOWN = (string)FindResource("str_SymbolUnknown");
CREATE_LOCAL_VARIABLE = (string)FindResource("str_CreateLocalVariable");
EDIT_LOCAL_VARIABLE = (string)FindResource("str_EditLocalVariable");
Debug.Assert(offset >= 0 && offset < project.FileDataLength);
mOpDef = project.CpuDef.GetOpDef(project.FileData[offset]);
Anattrib attr = project.GetAnattrib(offset);
@ -129,6 +146,7 @@ namespace SourceGen.WpfGui {
private void Window_Loaded(object sender, RoutedEventArgs e) {
BasicFormat_Loaded();
NumericRefs_Loaded();
mLoadDone = true;
}
@ -140,6 +158,20 @@ namespace SourceGen.WpfGui {
private void OkButton_Click(object sender, RoutedEventArgs e) {
FormatDescriptorResult = CreateDescriptorFromControls();
// Export the updated local variable table if we made changes.
if (mEditedLvTable != null) {
LocalVariableTable lvt = mProject.LvTables[mLvTableOffset];
if (mEditedLvTable != lvt) {
LocalVariableResult = mEditedLvTable;
LocalVariableTableOffsetResult = mLvTableOffset;
Debug.WriteLine("NEW TABLE:");
mEditedLvTable.DebugDump(mLvTableOffset);
} else {
Debug.WriteLine("No change to LvTable, not exporting");
}
}
DialogResult = true;
}
@ -154,8 +186,11 @@ namespace SourceGen.WpfGui {
// Parts panel IsEnabled depends directly on formatSymbolButton.IsChecked.
IsValid = true;
IsSymbolAuto = false;
IsPartPanelEnabled = false;
SymbolValueDecimal = string.Empty;
if (FormatSymbol) {
IsPartPanelEnabled = mOpDef.IsExtendedImmediate;
if (!Asm65.Label.ValidateLabel(SymbolLabel)) {
SymbolValueHex = SYMBOL_INVALID;
IsValid = false;
@ -417,6 +452,12 @@ namespace SourceGen.WpfGui {
}
private string mSymbolValueDecimal;
public bool IsPartPanelEnabled {
get { return mIsPartPanelEnabled; }
set { mIsPartPanelEnabled = value; OnPropertyChanged(); }
}
private bool mIsPartPanelEnabled;
public bool FormatPartLow {
get { return mFormatPartLow; }
set { mFormatPartLow = value; OnPropertyChanged(); UpdateControls(); }
@ -600,6 +641,167 @@ namespace SourceGen.WpfGui {
#endregion Basic Format
#region Numeric References
public bool ShowLvNotApplicable {
get { return mShowLvNotApplicable; }
set { mShowLvNotApplicable = value; OnPropertyChanged(); }
}
private bool mShowLvNotApplicable;
public bool ShowLvTableNotFound {
get { return mShowLvTableNotFound; }
set { mShowLvTableNotFound = value; OnPropertyChanged(); }
}
private bool mShowLvTableNotFound;
public bool ShowLvNoMatchFound {
get { return mShowLvNoMatchFound; }
set { mShowLvNoMatchFound = value; OnPropertyChanged(); }
}
private bool mShowLvNoMatchFound;
public bool ShowLvMatchFound {
get { return mShowLvMatchFound; }
set { mShowLvMatchFound = value; OnPropertyChanged(); }
}
private bool mShowLvMatchFound;
public string LocalVariableLabel {
get { return mLocalVariableLabel; }
set { mLocalVariableLabel = value; OnPropertyChanged(); }
}
private string mLocalVariableLabel;
public bool ShowLvCreateEditButton {
get { return mShowLvCreateEditButton; }
set { mShowLvCreateEditButton = value; OnPropertyChanged(); }
}
private bool mShowLvCreateEditButton;
public string CreateEditLocalVariableText {
get { return mCreateEditLocalVariableText; }
set { mCreateEditLocalVariableText = value; OnPropertyChanged(); }
}
private string mCreateEditLocalVariableText;
/// <summary>
/// Offset of LocalVariableTable we're going to modify.
/// </summary>
private int mLvTableOffset;
/// <summary>
/// Local variable value. If there's already a definition, this will be pre-filled
/// with the current contents. Otherwise it will be null.
/// </summary>
private DefSymbol mEditedLocalVar;
/// <summary>
/// Clone of original table, with local edits.
/// </summary>
private LocalVariableTable mEditedLvTable;
/// <summary>
/// Configures the UI on the bottom half of the dialog.
/// </summary>
private void NumericRefs_Loaded() {
if (mOpDef.IsDirectPageInstruction || mOpDef.IsStackRelInstruction) {
LocalVariableLookup lvLookup =
new LocalVariableLookup(mProject.LvTables, mProject, false);
// If the operand is already a local variable, use whichever one the
// analyzer found.
Anattrib attr = mProject.GetAnattrib(mOffset);
if (attr.DataDescriptor != null && attr.DataDescriptor.HasSymbol &&
attr.DataDescriptor.SymbolRef.IsVariable) {
// Select the table that defines the local variable that's currently
// associated with this operand.
mLvTableOffset = lvLookup.GetDefiningTableOffset(mOffset,
attr.DataDescriptor.SymbolRef);
Debug.Assert(mLvTableOffset >= 0);
Debug.WriteLine("Symbol " + attr.DataDescriptor.SymbolRef +
" from var table at +" + mLvTableOffset.ToString("x6"));
} else {
// Operand is not a local variable. Find the closest table.
mLvTableOffset = lvLookup.GetNearestTableOffset(mOffset);
Debug.WriteLine("Closest table is at +" + mLvTableOffset.ToString("x6"));
}
if (mLvTableOffset < 0) {
ShowLvTableNotFound = true;
} else {
// Found a table. Do we have a matching symbol?
ShowLvCreateEditButton = true;
mEditedLocalVar = lvLookup.GetSymbol(mOffset, mOperandValue,
mOpDef.IsDirectPageInstruction ?
Symbol.Type.ExternalAddr : Symbol.Type.Constant);
if (mEditedLocalVar == null) {
ShowLvNoMatchFound = true;
CreateEditLocalVariableText = CREATE_LOCAL_VARIABLE;
} else {
ShowLvMatchFound = true;
CreateEditLocalVariableText = EDIT_LOCAL_VARIABLE;
LocalVariableLabel = mEditedLocalVar.Label;
}
// We need to update the symbol table while we work to make the uniqueness
// check come out right. Otherwise if you edit, rename FOO to BAR,
// then edit again, you won't be able to rename BAR back to FOO because
// it's already in the list and it's not self.
//
// We don't need the full LVT, just the list of symbols, but we'll want
// to hand the modified table to the caller when we exit.
LocalVariableTable lvt = mProject.LvTables[mLvTableOffset];
mEditedLvTable = new LocalVariableTable(lvt);
}
} else {
ShowLvNotApplicable = true;
}
}
private void EditLocalVariableButton_Click(object sender, RoutedEventArgs e) {
Debug.Assert(mOpDef.IsDirectPageInstruction || mOpDef.IsStackRelInstruction);
Debug.Assert(mLvTableOffset >= 0);
DefSymbol initialVar = mEditedLocalVar;
if (initialVar == null) {
Symbol.Type symType;
if (mOpDef.IsDirectPageInstruction) {
symType = Symbol.Type.ExternalAddr;
} else {
symType = Symbol.Type.Constant;
}
// We need to pre-load the value and type, but we can't create a symbol with
// an empty name. We don't really need to create something unique since the
// dialog will handle it.
initialVar = new DefSymbol("VAR", mOperandValue,
Symbol.Source.Variable, symType, FormatDescriptor.SubType.None,
string.Empty, string.Empty, 1);
}
EditDefSymbol dlg = new EditDefSymbol(this, mFormatter,
mEditedLvTable.GetSortedByLabel(), initialVar, mProject.SymbolTable,
true, true);
if (dlg.ShowDialog() == true) {
if (mEditedLocalVar != dlg.NewSym) {
// Integrate result. Future edits will start with this.
// We can't delete a symbol, just create or modify.
Debug.Assert(dlg.NewSym != null);
mEditedLocalVar = dlg.NewSym;
mEditedLvTable.AddOrReplace(dlg.NewSym);
LocalVariableLabel = mEditedLocalVar.Label;
CreateEditLocalVariableText = EDIT_LOCAL_VARIABLE;
} else {
Debug.WriteLine("No change to def symbol, ignoring edit");
}
}
}
#endregion Numeric References
#if false
/// <summary>
/// Configures the buttons in the "symbol shortcuts" group box. The entire box is

View File

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

View File

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

View File

@ -51,7 +51,13 @@ namespace SourceGen {
/// <summary>
/// True if this reference is by name.
/// </summary>
public bool IsSymbolic { get; private set; }
/// <remarks>
/// The time this is of use is when determining the set of project/platform symbols
/// that are actually used. If we have FOO1=$101 and FOO2=$102, and we LDA FOO1
/// and LDA FOO1+1, we only want to output an equate for FOO1 even though FOO2's
/// address was referenced.
/// </remarks>
public bool IsByName { get; private set; }
/// <summary>
/// Type of reference.
@ -69,17 +75,17 @@ namespace SourceGen {
/// </summary>
public int Adjustment { get; private set; }
public Xref(int offset, bool isSymbolic, XrefType type,
public Xref(int offset, bool isByName, XrefType type,
Asm65.OpDef.MemoryEffect accType, int adjustment) {
Offset = offset;
IsSymbolic = isSymbolic;
IsByName = isByName;
Type = type;
AccType = accType;
Adjustment = adjustment;
}
public override string ToString() {
return "Xref off=+" + Offset.ToString("x6") + " sym=" + IsSymbolic +
return "Xref off=+" + Offset.ToString("x6") + " sym=" + IsByName +
" type=" + Type + " accType= " + AccType + " adj=" + Adjustment;
}
}