Add operand highlighting

When a code or data line is selected in the code list, if the operand
is an address inside the file, we highlight the address and label.
It's also useful to highlight the other way: when a code or data line
is selected, find all lines whose operands reference it, and highlight
the operand field.

This is a little trickier because there can be multiple references,
but all of the information we need is in the cross-reference table.
This commit is contained in:
Andy McFadden 2021-11-17 11:18:23 -08:00
parent 8c5509d69d
commit 33aa0ff004
6 changed files with 123 additions and 17 deletions

View File

@ -55,6 +55,8 @@ namespace PluginCommon {
mNextColorIdx = 0;
}
// TODO: add GetPixelIndex, which returns the index we passed into SetPixelIndex
public int GetPixel(int x, int y) {
byte pix = mData[x + y * Width];
return mPalette[pix];

View File

@ -369,6 +369,10 @@ namespace SourceGen {
// examined by a data trigger in CodeListItemStyle.xaml.
public bool HasAddrLabelHighlight { get; private set; }
// Set to true if we want to highlight the operand field. This is
// examined by a data trigger in CodeListItemStyle.xaml.
public bool HasOperandHighlight { get; private set; }
// Set to true if the Flags field has been modified.
public bool HasModifiedFlags {
get { return (mPartFlags & PartFlags.HasModifiedFlags) != 0; }
@ -502,18 +506,30 @@ namespace SourceGen {
return parts;
}
public static FormattedParts AddSelectionHighlight(FormattedParts orig) {
public static FormattedParts AddSelectionAddrHighlight(FormattedParts orig) {
FormattedParts newParts = Clone(orig);
newParts.HasAddrLabelHighlight = true;
return newParts;
}
public static FormattedParts RemoveSelectionHighlight(FormattedParts orig) {
public static FormattedParts RemoveSelectionAddrHighlight(FormattedParts orig) {
FormattedParts newParts = Clone(orig);
newParts.HasAddrLabelHighlight = false;
return newParts;
}
public static FormattedParts AddSelectionOperHighlight(FormattedParts orig) {
FormattedParts newParts = Clone(orig);
newParts.HasOperandHighlight = true;
return newParts;
}
public static FormattedParts RemoveSelectionOperHighlight(FormattedParts orig) {
FormattedParts newParts = Clone(orig);
newParts.HasOperandHighlight = false;
return newParts;
}
public override string ToString() {
return "[Parts: index=" + ListIndex + " off=" + Offset + "]";
}

View File

@ -181,6 +181,11 @@ namespace SourceGen {
/// </summary>
private int mTargetHighlightIndex = -1;
/// <summary>
/// Tracks the operands we have highlighted.
/// </summary>
private List<int> mOperandHighlights = new List<int>();
/// <summary>
/// Code list color scheme.
/// </summary>
@ -879,8 +884,20 @@ namespace SourceGen {
CodeLineList, mMainWin.CodeDisplayList.SelectedIndices, topItemIndex);
//savedSel.DebugDump();
// Clear this so we don't try to fiddle with it later.
// Clear the addr/label highlight index.
// (Certain changes will blow away the CodeDisplayList and affect the selection,
// which will cause the selection-changed handler to try to un-highlight something
// that doesn't exist. We want to clear the index here, but we probably also want
// to clear the highlighting before we do it. As it happens, changes will either
// be big enough to wipe out our highlight, or small enough that we immediately
// re-highlight the thing that's already highlighted, so it doesn't really matter.
// If we start to see vestigial highlighting after a change, we'll need to be
// more rigorous here.)
mTargetHighlightIndex = -1;
// Clear operand highlighting indices as well.
mOperandHighlights.Clear();
mReanalysisTimer.EndTask("Save selection");
mReanalysisTimer.StartTask("Apply changes");
@ -1404,7 +1421,11 @@ namespace SourceGen {
}
mDataPathName = null;
mProjectPathName = null;
// We may get a "selection changed" message as things are being torn down. Clear
// these so we don't try to remove the highlight from something that doesn't exist.
mTargetHighlightIndex = -1;
mOperandHighlights.Clear();
mMainWin.ShowCodeListView = false;
mMainWin.ProjectClosing();
@ -3405,16 +3426,19 @@ namespace SourceGen {
private bool mUpdatingSelectionHighlight; // recursion guard for next method
/// <summary>
/// Updates the selection highlight. When a code item with an operand offset is
/// Updates the selection highlights. When a code or data item with an operand offset is
/// selected, such as a branch, we want to highlight the address and label of the
/// target.
/// target. When a code or data item is referenced by another instruction, such as a
/// branch, we want to highlight the operands of all such instructions.
/// </summary>
private void UpdateSelectionHighlight() {
if (mUpdatingSelectionHighlight) {
return;
}
mUpdatingSelectionHighlight = true;
int targetIndex = FindSelectionHighlight();
int targetIndex = FindSelectionAddrHighlight(out bool isSingleCodeData,
out int selIndex);
if (mTargetHighlightIndex != targetIndex) {
Debug.WriteLine("Target highlight moving from " + mTargetHighlightIndex +
" to " + targetIndex);
@ -3429,25 +3453,47 @@ namespace SourceGen {
// 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;
mMainWin.CodeListView_RemoveSelectionAddrHighlight(mTargetHighlightIndex);
mMainWin.CodeListView_AddSelectionAddrHighlight(targetIndex);
mTargetHighlightIndex = targetIndex;
}
if (mOperandHighlights.Count > 0) {
foreach (int index in mOperandHighlights) {
mMainWin.CodeListView_RemoveSelectionOperHighlight(index);
}
mOperandHighlights.Clear();
}
if (isSingleCodeData) {
LineListGen.Line line = CodeLineList[selIndex];
XrefSet xrefs = mProject.GetXrefSet(line.FileOffset);
if (xrefs != null) {
foreach (XrefSet.Xref xr in xrefs) {
int refIndex = CodeLineList.FindCodeDataIndexByOffset(xr.Offset);
mMainWin.CodeListView_AddSelectionOperHighlight(refIndex);
mOperandHighlights.Add(refIndex);
}
}
}
mUpdatingSelectionHighlight = false;
}
private int FindSelectionHighlight() {
private int FindSelectionAddrHighlight(out bool isSingleCodeData, out int selIndex) {
if (mMainWin.CodeListView_GetSelectionCount() != 1) {
isSingleCodeData = false;
selIndex = -1;
return -1;
}
int selIndex = mMainWin.CodeListView_GetFirstSelectedIndex();
selIndex = mMainWin.CodeListView_GetFirstSelectedIndex();
LineListGen.Line line = CodeLineList[selIndex];
if (!line.IsCodeOrData) {
isSingleCodeData = false;
return -1;
}
Debug.Assert(line.FileOffset >= 0);
isSingleCodeData = true;
// Does this have an operand with an in-file target offset?
// TODO: may not work correctly with reloc data?

View File

@ -407,4 +407,22 @@ See also https://github.com/fadden/DisasmUiTest
</TextBlock.Text>
</TextBlock>
</DataTemplate>
<DataTemplate x:Key="operandHighlightTemplate">
<TextBlock>
<TextBlock.Style>
<Style TargetType="TextBlock">
<Setter Property="TextTrimming" Value="CharacterEllipsis"/>
<Style.Triggers>
<DataTrigger Binding="{Binding Path=HasOperandHighlight}" Value="True">
<Setter Property="TextBlock.Background" Value="{StaticResource HighlightedCellFill}"/>
<Setter Property="TextBlock.Foreground" Value="{DynamicResource Brush_ListViewForeground}"/>
</DataTrigger>
</Style.Triggers>
</Style>
</TextBlock.Style>
<TextBlock.Text>
<Binding Path="Operand"/>
</TextBlock.Text>
</TextBlock>
</DataTemplate>
</ResourceDictionary>

View File

@ -771,7 +771,7 @@ limitations under the License.
<GridViewColumn Header="Opcode" Width="60"
DisplayMemberBinding="{Binding Opcode}"/>
<GridViewColumn Header="Operand" Width="100"
DisplayMemberBinding="{Binding Operand}"/>
CellTemplate="{StaticResource operandHighlightTemplate}"/>
<GridViewColumn Header="Comment" Width="344"
DisplayMemberBinding="{Binding Comment}"/>
</GridView>

View File

@ -981,24 +981,48 @@ namespace SourceGen.WpfGui {
/// Adds an address/label selection highlight to the specified line.
/// </summary>
/// <param name="index">Line index. If &lt; 0, method has no effect.</param>
public void CodeListView_AddSelectionHighlight(int index) {
public void CodeListView_AddSelectionAddrHighlight(int index) {
if (index < 0) {
return;
}
CodeListView_ReplaceEntry(index,
DisplayList.FormattedParts.AddSelectionHighlight(CodeDisplayList[index]));
DisplayList.FormattedParts.AddSelectionAddrHighlight(CodeDisplayList[index]));
}
/// <summary>
/// Removes an address/label selection highlight from the specified line.
/// </summary>
/// <param name="index">Line index. If &lt; 0, method has no effect.</param>
public void CodeListView_RemoveSelectionHighlight(int index) {
public void CodeListView_RemoveSelectionAddrHighlight(int index) {
if (index < 0) {
return;
}
CodeListView_ReplaceEntry(index,
DisplayList.FormattedParts.RemoveSelectionHighlight(CodeDisplayList[index]));
DisplayList.FormattedParts.RemoveSelectionAddrHighlight(CodeDisplayList[index]));
}
/// <summary>
/// Adds an operand selection highlight to the specified line.
/// </summary>
public void CodeListView_AddSelectionOperHighlight(int index) {
Debug.Assert(index >= 0);
CodeListView_ReplaceEntry(index,
DisplayList.FormattedParts.AddSelectionOperHighlight(CodeDisplayList[index]));
}
/// <summary>
/// Removes an operand selection highlight from the specified line.
/// </summary>
public void CodeListView_RemoveSelectionOperHighlight(int index) {
Debug.Assert(index >= 0);
if (index >= CodeDisplayList.Count) {
// Shouldn't happen unless we resize the list without clearing the highlights.
Debug.WriteLine("NOTE: selection oper high index exceeds count (" +
index + " vs. " + CodeDisplayList.Count + ")");
return;
}
CodeListView_ReplaceEntry(index,
DisplayList.FormattedParts.RemoveSelectionOperHighlight(CodeDisplayList[index]));
}
/// <summary>