Branch Watch Tool: New Conditional Branch Inspection Tools
Invert conditions, invert decrement checks, and make conditional branches unconditional. USnapshotMetadata in prior versions of Dolphin is forward-compatible with these changes (tested on x86_64).
This commit is contained in:
parent
8bdfdc88b2
commit
8f76a32be4
|
@ -38,14 +38,14 @@ union USnapshotMetadata
|
|||
using Inspection = BranchWatch::SelectionInspection;
|
||||
using StorageType = unsigned long long;
|
||||
|
||||
static_assert(Inspection::EndOfEnumeration == Inspection{(1u << 3) + 1});
|
||||
static_assert(Inspection::EndOfEnumeration == Inspection{(1u << 5) + 1});
|
||||
|
||||
StorageType hex;
|
||||
|
||||
BitField<0, 1, bool, StorageType> is_virtual;
|
||||
BitField<1, 1, bool, StorageType> condition;
|
||||
BitField<2, 1, bool, StorageType> is_selected;
|
||||
BitField<3, 4, Inspection, StorageType> inspection;
|
||||
BitField<3, 6, Inspection, StorageType> inspection;
|
||||
|
||||
USnapshotMetadata() : hex(0) {}
|
||||
explicit USnapshotMetadata(bool is_virtual_, bool condition_, bool is_selected_,
|
||||
|
|
|
@ -63,6 +63,8 @@ enum class BranchWatchSelectionInspection : u8
|
|||
SetDestinBLR = 1u << 1,
|
||||
SetOriginSymbolBLR = 1u << 2,
|
||||
SetDestinSymbolBLR = 1u << 3,
|
||||
InvertBranchOption = 1u << 4, // Used for both conditions and decrement checks.
|
||||
MakeUnconditional = 1u << 5,
|
||||
EndOfEnumeration,
|
||||
};
|
||||
|
||||
|
|
|
@ -294,6 +294,14 @@ union UGeckoInstruction
|
|||
};
|
||||
};
|
||||
|
||||
// Precondition: inst is a bx, bcx, bclrx, or bcctrx instruction.
|
||||
constexpr bool BranchIsConditional(UGeckoInstruction inst)
|
||||
{
|
||||
if (inst.OPCD == 18) // bx
|
||||
return false;
|
||||
return (inst.BO & 0b10100) != 0b10100; // 1z1zz - Branch always
|
||||
}
|
||||
|
||||
// Used in implementations of rlwimi, rlwinm, and rlwnm
|
||||
inline u32 MakeRotationMask(u32 mb, u32 me)
|
||||
{
|
||||
|
|
|
@ -105,6 +105,8 @@ public:
|
|||
|
||||
bool IsBranchTypeAllowed(UGeckoInstruction inst) const;
|
||||
void SetInspected(const QModelIndex& index) const;
|
||||
const Core::BranchWatchSelectionValueType&
|
||||
GetBranchWatchSelection(const QModelIndex& index) const;
|
||||
|
||||
private:
|
||||
const Core::BranchWatch& m_branch_watch;
|
||||
|
@ -157,34 +159,25 @@ bool BranchWatchProxyModel::filterAcceptsRow(int source_row, const QModelIndex&)
|
|||
return true;
|
||||
}
|
||||
|
||||
static constexpr bool BranchSavesLR(UGeckoInstruction inst)
|
||||
{
|
||||
DEBUG_ASSERT(inst.OPCD == 18 || inst.OPCD == 16 ||
|
||||
(inst.OPCD == 19 && (inst.SUBOP10 == 16 || inst.SUBOP10 == 528)));
|
||||
// Every branch instruction uses the same LK field.
|
||||
return inst.LK;
|
||||
}
|
||||
|
||||
bool BranchWatchProxyModel::IsBranchTypeAllowed(UGeckoInstruction inst) const
|
||||
{
|
||||
const bool lr_saved = BranchSavesLR(inst);
|
||||
switch (inst.OPCD)
|
||||
{
|
||||
case 18:
|
||||
return lr_saved ? m_bl : m_b;
|
||||
return inst.LK ? m_bl : m_b;
|
||||
case 16:
|
||||
return lr_saved ? m_bcl : m_bc;
|
||||
return inst.LK ? m_bcl : m_bc;
|
||||
case 19:
|
||||
switch (inst.SUBOP10)
|
||||
{
|
||||
case 16:
|
||||
if ((inst.BO & 0b10100) == 0b10100) // 1z1zz - Branch always
|
||||
return lr_saved ? m_blrl : m_blr;
|
||||
return lr_saved ? m_bclrl : m_bclr;
|
||||
return inst.LK ? m_blrl : m_blr;
|
||||
return inst.LK ? m_bclrl : m_bclr;
|
||||
case 528:
|
||||
if ((inst.BO & 0b10100) == 0b10100) // 1z1zz - Branch always
|
||||
return lr_saved ? m_bctrl : m_bctr;
|
||||
return lr_saved ? m_bcctrl : m_bcctr;
|
||||
return inst.LK ? m_bctrl : m_bctr;
|
||||
return inst.LK ? m_bcctrl : m_bcctr;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
|
@ -195,6 +188,12 @@ void BranchWatchProxyModel::SetInspected(const QModelIndex& index) const
|
|||
sourceModel()->SetInspected(mapToSource(index));
|
||||
}
|
||||
|
||||
const Core::BranchWatchSelectionValueType&
|
||||
BranchWatchProxyModel::GetBranchWatchSelection(const QModelIndex& index) const
|
||||
{
|
||||
return sourceModel()->GetBranchWatchSelection(mapToSource(index));
|
||||
}
|
||||
|
||||
BranchWatchDialog::BranchWatchDialog(Core::System& system, Core::BranchWatch& branch_watch,
|
||||
PPCSymbolDB& ppc_symbol_db, CodeWidget* code_widget,
|
||||
QWidget* parent)
|
||||
|
@ -419,6 +418,18 @@ BranchWatchDialog::BranchWatchDialog(Core::System& system, Core::BranchWatch& br
|
|||
auto* const delete_action = new QAction(tr("&Delete"), this);
|
||||
connect(delete_action, &QAction::triggered, this, &BranchWatchDialog::OnTableDelete);
|
||||
|
||||
m_act_invert_condition = new QAction(tr("Invert &Condition"), this);
|
||||
connect(m_act_invert_condition, &QAction::triggered, this,
|
||||
&BranchWatchDialog::OnTableInvertCondition);
|
||||
|
||||
m_act_invert_decrement_check = new QAction(tr("Invert &Decrement Check"), this);
|
||||
connect(m_act_invert_decrement_check, &QAction::triggered, this,
|
||||
&BranchWatchDialog::OnTableInvertDecrementCheck);
|
||||
|
||||
m_act_make_unconditional = new QAction(tr("Make &Unconditional"), this);
|
||||
connect(m_act_make_unconditional, &QAction::triggered, this,
|
||||
&BranchWatchDialog::OnTableMakeUnconditional);
|
||||
|
||||
m_act_copy_address = new QAction(tr("&Copy Address"), this);
|
||||
connect(m_act_copy_address, &QAction::triggered, this, &BranchWatchDialog::OnTableCopyAddress);
|
||||
|
||||
|
@ -436,6 +447,13 @@ BranchWatchDialog::BranchWatchDialog(Core::System& system, Core::BranchWatch& br
|
|||
m_act_both_on_hit = m_mnu_set_breakpoint->addAction(tr("Break &and Log on Hit"), this,
|
||||
&BranchWatchDialog::OnTableSetBreakpointBoth);
|
||||
|
||||
m_mnu_table_context_instruction = new QMenu(this);
|
||||
m_mnu_table_context_instruction->addActions(
|
||||
{delete_action, m_act_invert_condition, m_act_invert_decrement_check});
|
||||
|
||||
m_mnu_table_context_condition = new QMenu(this);
|
||||
m_mnu_table_context_condition->addActions({delete_action, m_act_make_unconditional});
|
||||
|
||||
m_mnu_table_context_origin = new QMenu(this);
|
||||
m_mnu_table_context_origin->addActions(
|
||||
{delete_action, m_act_insert_nop, m_act_copy_address, m_mnu_set_breakpoint->menuAction()});
|
||||
|
@ -734,6 +752,15 @@ void BranchWatchDialog::OnHelp()
|
|||
"destination symbol columns, these actions will only be enabled if every row in the "
|
||||
"selection has a symbol."
|
||||
"\n\n"
|
||||
"If the instruction column of a row selection is right-clicked, an action to invert the "
|
||||
"branch instruction's condition and an action to invert the branch instruction's "
|
||||
"decrement check will be available, but only if the branch instruction is a conditional "
|
||||
"one."
|
||||
"\n\n"
|
||||
"If the condition column of a row selection is right-clicked, an action to make the "
|
||||
"branch instruction unconditional will be available, but only if the branch instruction "
|
||||
"is a conditional one."
|
||||
"\n\n"
|
||||
"If the origin column of a row selection is right-clicked, an action to replace the "
|
||||
"branch instruction at the origin(s) with a NOP instruction (No Operation) will be "
|
||||
"available."
|
||||
|
@ -851,6 +878,33 @@ void BranchWatchDialog::OnTableSetNOP() const
|
|||
SetStubPatches(0x60000000);
|
||||
}
|
||||
|
||||
void BranchWatchDialog::OnTableInvertCondition() const
|
||||
{
|
||||
SetEditPatches([](u32 hex) {
|
||||
UGeckoInstruction inst = hex;
|
||||
inst.BO ^= 0b01000;
|
||||
return inst.hex;
|
||||
});
|
||||
}
|
||||
|
||||
void BranchWatchDialog::OnTableInvertDecrementCheck() const
|
||||
{
|
||||
SetEditPatches([](u32 hex) {
|
||||
UGeckoInstruction inst = hex;
|
||||
inst.BO ^= 0b00010;
|
||||
return inst.hex;
|
||||
});
|
||||
}
|
||||
|
||||
void BranchWatchDialog::OnTableMakeUnconditional() const
|
||||
{
|
||||
SetEditPatches([](u32 hex) {
|
||||
UGeckoInstruction inst = hex;
|
||||
inst.BO = 0b10100; // 1z1zz - Branch always
|
||||
return inst.hex;
|
||||
});
|
||||
}
|
||||
|
||||
void BranchWatchDialog::OnTableCopyAddress() const
|
||||
{
|
||||
auto iter = m_index_list_temp.begin();
|
||||
|
@ -1047,6 +1101,21 @@ void BranchWatchDialog::SetStubPatches(u32 value) const
|
|||
m_code_widget->Update();
|
||||
}
|
||||
|
||||
void BranchWatchDialog::SetEditPatches(u32 (*transform)(u32)) const
|
||||
{
|
||||
auto& debug_interface = m_system.GetPowerPC().GetDebugInterface();
|
||||
for (const Core::CPUThreadGuard guard(m_system); const QModelIndex& index : m_index_list_temp)
|
||||
{
|
||||
const Core::BranchWatchCollectionKey& k =
|
||||
m_table_proxy->GetBranchWatchSelection(index).collection_ptr->first;
|
||||
// This function assumes patches apply to the origin address, unlike SetStubPatches.
|
||||
debug_interface.SetPatch(guard, k.origin_addr, transform(k.original_inst.hex));
|
||||
m_table_proxy->SetInspected(index);
|
||||
}
|
||||
// TODO: Same issue as SetStubPatches.
|
||||
m_code_widget->Update();
|
||||
}
|
||||
|
||||
void BranchWatchDialog::SetBreakpoints(bool break_on_hit, bool log_on_hit) const
|
||||
{
|
||||
auto& breakpoints = m_system.GetPowerPC().GetBreakPoints();
|
||||
|
@ -1092,8 +1161,9 @@ QMenu* BranchWatchDialog::GetTableContextMenu(const QModelIndex& index) const
|
|||
switch (index.column())
|
||||
{
|
||||
case Column::Instruction:
|
||||
return GetTableContextMenu_Instruction(core_initialized);
|
||||
case Column::Condition:
|
||||
return m_mnu_table_context_other;
|
||||
return GetTableContextMenu_Condition(core_initialized);
|
||||
case Column::Origin:
|
||||
return GetTableContextMenu_Origin(core_initialized);
|
||||
case Column::Destination:
|
||||
|
@ -1109,6 +1179,29 @@ QMenu* BranchWatchDialog::GetTableContextMenu(const QModelIndex& index) const
|
|||
Common::Unreachable();
|
||||
}
|
||||
|
||||
QMenu* BranchWatchDialog::GetTableContextMenu_Instruction(bool core_initialized) const
|
||||
{
|
||||
const bool all_branches_conditional = // Taking advantage of short-circuit evaluation here.
|
||||
core_initialized && std::ranges::all_of(m_index_list_temp, [this](const QModelIndex& index) {
|
||||
return BranchIsConditional(
|
||||
m_table_proxy->GetBranchWatchSelection(index).collection_ptr->first.original_inst);
|
||||
});
|
||||
m_act_invert_condition->setEnabled(all_branches_conditional);
|
||||
m_act_invert_decrement_check->setEnabled(all_branches_conditional);
|
||||
return m_mnu_table_context_instruction;
|
||||
}
|
||||
|
||||
QMenu* BranchWatchDialog::GetTableContextMenu_Condition(bool core_initialized) const
|
||||
{
|
||||
const bool all_branches_conditional = // Taking advantage of short-circuit evaluation here.
|
||||
core_initialized && std::ranges::all_of(m_index_list_temp, [this](const QModelIndex& index) {
|
||||
return BranchIsConditional(
|
||||
m_table_proxy->GetBranchWatchSelection(index).collection_ptr->first.original_inst);
|
||||
});
|
||||
m_act_make_unconditional->setEnabled(all_branches_conditional);
|
||||
return m_mnu_table_context_condition;
|
||||
}
|
||||
|
||||
QMenu* BranchWatchDialog::GetTableContextMenu_Origin(bool core_initialized) const
|
||||
{
|
||||
SetBreakpointMenuActionsIcons();
|
||||
|
@ -1123,8 +1216,7 @@ QMenu* BranchWatchDialog::GetTableContextMenu_Destin(bool core_initialized) cons
|
|||
SetBreakpointMenuActionsIcons();
|
||||
const bool all_branches_save_lr = // Taking advantage of short-circuit evaluation here.
|
||||
core_initialized && std::ranges::all_of(m_index_list_temp, [this](const QModelIndex& index) {
|
||||
const QModelIndex sibling = index.siblingAtColumn(Column::Instruction);
|
||||
return BranchSavesLR(m_table_proxy->data(sibling, UserRole::ClickRole).value<u32>());
|
||||
return m_table_proxy->GetBranchWatchSelection(index).collection_ptr->first.original_inst.LK;
|
||||
});
|
||||
m_act_insert_blr->setEnabled(all_branches_save_lr);
|
||||
m_act_copy_address->setEnabled(true);
|
||||
|
|
|
@ -93,6 +93,9 @@ private:
|
|||
void OnTableDeleteKeypress() const;
|
||||
void OnTableSetBLR() const;
|
||||
void OnTableSetNOP() const;
|
||||
void OnTableInvertCondition() const;
|
||||
void OnTableInvertDecrementCheck() const;
|
||||
void OnTableMakeUnconditional() const;
|
||||
void OnTableCopyAddress() const;
|
||||
void OnTableSetBreakpointBreak() const;
|
||||
void OnTableSetBreakpointLog() const;
|
||||
|
@ -116,10 +119,13 @@ private:
|
|||
void Load(const Core::CPUThreadGuard& guard, const std::string& filepath);
|
||||
void AutoSave(const Core::CPUThreadGuard& guard);
|
||||
void SetStubPatches(u32 value) const;
|
||||
void SetEditPatches(u32 (*transform)(u32)) const;
|
||||
void SetBreakpoints(bool break_on_hit, bool log_on_hit) const;
|
||||
|
||||
void SetBreakpointMenuActionsIcons() const;
|
||||
QMenu* GetTableContextMenu(const QModelIndex& index) const;
|
||||
QMenu* GetTableContextMenu_Instruction(bool core_initialized) const;
|
||||
QMenu* GetTableContextMenu_Condition(bool core_initialized) const;
|
||||
QMenu* GetTableContextMenu_Origin(bool core_initialized) const;
|
||||
QMenu* GetTableContextMenu_Destin(bool core_initialized) const;
|
||||
QMenu* GetTableContextMenu_Symbol(bool core_initialized) const;
|
||||
|
@ -131,6 +137,9 @@ private:
|
|||
QPushButton *m_btn_start_pause, *m_btn_clear_watch, *m_btn_path_was_taken, *m_btn_path_not_taken,
|
||||
*m_btn_was_overwritten, *m_btn_not_overwritten, *m_btn_wipe_recent_hits;
|
||||
QAction* m_act_autosave;
|
||||
QAction* m_act_invert_condition;
|
||||
QAction* m_act_invert_decrement_check;
|
||||
QAction* m_act_make_unconditional;
|
||||
QAction* m_act_insert_nop;
|
||||
QAction* m_act_insert_blr;
|
||||
QAction* m_act_copy_address;
|
||||
|
@ -138,7 +147,8 @@ private:
|
|||
QAction* m_act_break_on_hit;
|
||||
QAction* m_act_log_on_hit;
|
||||
QAction* m_act_both_on_hit;
|
||||
QMenu *m_mnu_table_context_origin, *m_mnu_table_context_destin_or_symbol,
|
||||
QMenu *m_mnu_table_context_instruction, *m_mnu_table_context_condition,
|
||||
*m_mnu_table_context_origin, *m_mnu_table_context_destin_or_symbol,
|
||||
*m_mnu_table_context_other;
|
||||
QMenu* m_mnu_column_visibility;
|
||||
|
||||
|
|
|
@ -195,6 +195,12 @@ void BranchWatchTableModel::SetInspected(const QModelIndex& index)
|
|||
const int row = index.row();
|
||||
switch (index.column())
|
||||
{
|
||||
case Column::Instruction:
|
||||
SetInspected(index, Core::BranchWatchSelectionInspection::InvertBranchOption);
|
||||
return;
|
||||
case Column::Condition:
|
||||
SetInspected(index, Core::BranchWatchSelectionInspection::MakeUnconditional);
|
||||
return;
|
||||
case Column::Origin:
|
||||
SetOriginInspected(m_branch_watch.GetSelection()[row].collection_ptr->first.origin_addr);
|
||||
return;
|
||||
|
@ -210,35 +216,35 @@ void BranchWatchTableModel::SetInspected(const QModelIndex& index)
|
|||
}
|
||||
}
|
||||
|
||||
void BranchWatchTableModel::SetInspected(const QModelIndex& index,
|
||||
Core::BranchWatchSelectionInspection inspection)
|
||||
{
|
||||
static const QList<int> roles = {Qt::FontRole, Qt::ForegroundRole};
|
||||
m_branch_watch.SetSelectedInspected(index.row(), inspection);
|
||||
emit dataChanged(index, index, roles);
|
||||
}
|
||||
|
||||
void BranchWatchTableModel::SetOriginInspected(u32 origin_addr)
|
||||
{
|
||||
using Inspection = Core::BranchWatchSelectionInspection;
|
||||
static const QList<int> roles = {Qt::FontRole, Qt::ForegroundRole};
|
||||
|
||||
const Core::BranchWatch::Selection& selection = m_branch_watch.GetSelection();
|
||||
for (std::size_t i = 0; i < selection.size(); ++i)
|
||||
{
|
||||
if (selection[i].collection_ptr->first.origin_addr != origin_addr)
|
||||
continue;
|
||||
m_branch_watch.SetSelectedInspected(i, Inspection::SetOriginNOP);
|
||||
const QModelIndex index = createIndex(static_cast<int>(i), Column::Origin);
|
||||
emit dataChanged(index, index, roles);
|
||||
SetInspected(createIndex(static_cast<int>(i), Column::Origin),
|
||||
Core::BranchWatchSelectionInspection::SetOriginNOP);
|
||||
}
|
||||
}
|
||||
|
||||
void BranchWatchTableModel::SetDestinInspected(u32 destin_addr, bool nested)
|
||||
{
|
||||
using Inspection = Core::BranchWatchSelectionInspection;
|
||||
static const QList<int> roles = {Qt::FontRole, Qt::ForegroundRole};
|
||||
|
||||
const Core::BranchWatch::Selection& selection = m_branch_watch.GetSelection();
|
||||
for (std::size_t i = 0; i < selection.size(); ++i)
|
||||
{
|
||||
if (selection[i].collection_ptr->first.destin_addr != destin_addr)
|
||||
continue;
|
||||
m_branch_watch.SetSelectedInspected(i, Inspection::SetDestinBLR);
|
||||
const QModelIndex index = createIndex(static_cast<int>(i), Column::Destination);
|
||||
emit dataChanged(index, index, roles);
|
||||
SetInspected(createIndex(static_cast<int>(i), Column::Destination),
|
||||
Core::BranchWatchSelectionInspection::SetDestinBLR);
|
||||
}
|
||||
|
||||
if (nested)
|
||||
|
@ -248,23 +254,18 @@ void BranchWatchTableModel::SetDestinInspected(u32 destin_addr, bool nested)
|
|||
|
||||
void BranchWatchTableModel::SetSymbolInspected(u32 symbol_addr, bool nested)
|
||||
{
|
||||
using Inspection = Core::BranchWatchSelectionInspection;
|
||||
static const QList<int> roles = {Qt::FontRole, Qt::ForegroundRole};
|
||||
|
||||
for (qsizetype i = 0; i < m_symbol_list.size(); ++i)
|
||||
{
|
||||
const SymbolListValueType& value = m_symbol_list[i];
|
||||
if (value.origin_addr.isValid() && value.origin_addr.value<u32>() == symbol_addr)
|
||||
{
|
||||
m_branch_watch.SetSelectedInspected(i, Inspection::SetOriginSymbolBLR);
|
||||
const QModelIndex index = createIndex(i, Column::OriginSymbol);
|
||||
emit dataChanged(index, index, roles);
|
||||
SetInspected(createIndex(static_cast<int>(i), Column::OriginSymbol),
|
||||
Core::BranchWatchSelectionInspection::SetOriginSymbolBLR);
|
||||
}
|
||||
if (value.destin_addr.isValid() && value.destin_addr.value<u32>() == symbol_addr)
|
||||
{
|
||||
m_branch_watch.SetSelectedInspected(i, Inspection::SetDestinSymbolBLR);
|
||||
const QModelIndex index = createIndex(i, Column::DestinSymbol);
|
||||
emit dataChanged(index, index, roles);
|
||||
SetInspected(createIndex(static_cast<int>(i), Column::DestinSymbol),
|
||||
Core::BranchWatchSelectionInspection::SetDestinSymbolBLR);
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -273,6 +274,13 @@ void BranchWatchTableModel::SetSymbolInspected(u32 symbol_addr, bool nested)
|
|||
SetDestinInspected(symbol_addr, true);
|
||||
}
|
||||
|
||||
const Core::BranchWatchSelectionValueType&
|
||||
BranchWatchTableModel::GetBranchWatchSelection(const QModelIndex& index) const
|
||||
{
|
||||
ASSERT(index.isValid());
|
||||
return m_branch_watch.GetSelection()[index.row()];
|
||||
}
|
||||
|
||||
void BranchWatchTableModel::PrefetchSymbols()
|
||||
{
|
||||
if (m_branch_watch.GetRecordingPhase() != Core::BranchWatch::Phase::Reduction)
|
||||
|
@ -306,25 +314,14 @@ static QString GetInstructionMnemonic(u32 hex)
|
|||
return QString::fromLatin1(disas.data(), split);
|
||||
}
|
||||
|
||||
static bool BranchIsUnconditional(UGeckoInstruction inst)
|
||||
{
|
||||
if (inst.OPCD == 18) // bx
|
||||
return true;
|
||||
// If BranchWatch is doing its job, the input will be only bcx, bclrx, and bcctrx instructions.
|
||||
DEBUG_ASSERT(inst.OPCD == 16 || (inst.OPCD == 19 && (inst.SUBOP10 == 16 || inst.SUBOP10 == 528)));
|
||||
if ((inst.BO & 0b10100) == 0b10100) // 1z1zz - Branch always
|
||||
return true;
|
||||
return false;
|
||||
}
|
||||
|
||||
static QString GetConditionString(const Core::BranchWatch::Selection::value_type& value,
|
||||
const Core::BranchWatch::Collection::value_type* kv)
|
||||
{
|
||||
if (value.condition == false)
|
||||
return BranchWatchTableModel::tr("false");
|
||||
if (BranchIsUnconditional(kv->first.original_inst))
|
||||
return QStringLiteral("");
|
||||
return BranchWatchTableModel::tr("true");
|
||||
if (BranchIsConditional(kv->first.original_inst))
|
||||
return BranchWatchTableModel::tr("true");
|
||||
return QStringLiteral("");
|
||||
}
|
||||
|
||||
QVariant BranchWatchTableModel::DisplayRoleData(const QModelIndex& index) const
|
||||
|
@ -361,21 +358,25 @@ QVariant BranchWatchTableModel::DisplayRoleData(const QModelIndex& index) const
|
|||
QVariant BranchWatchTableModel::FontRoleData(const QModelIndex& index) const
|
||||
{
|
||||
m_font.setBold([&]() -> bool {
|
||||
using Inspection = Core::BranchWatchSelectionInspection;
|
||||
const auto get_bit_test = [this, &index](Inspection inspection_mask) -> bool {
|
||||
const Inspection inspection = m_branch_watch.GetSelection()[index.row()].inspection;
|
||||
return (inspection & inspection_mask) != Inspection{};
|
||||
};
|
||||
switch (index.column())
|
||||
{
|
||||
using Inspection = Core::BranchWatchSelectionInspection;
|
||||
case Column::Instruction:
|
||||
return get_bit_test(Inspection::InvertBranchOption);
|
||||
case Column::Condition:
|
||||
return get_bit_test(Inspection::MakeUnconditional);
|
||||
case Column::Origin:
|
||||
return (m_branch_watch.GetSelection()[index.row()].inspection & Inspection::SetOriginNOP) !=
|
||||
Inspection{};
|
||||
return get_bit_test(Inspection::SetOriginNOP);
|
||||
case Column::Destination:
|
||||
return (m_branch_watch.GetSelection()[index.row()].inspection & Inspection::SetDestinBLR) !=
|
||||
Inspection{};
|
||||
return get_bit_test(Inspection::SetDestinBLR);
|
||||
case Column::OriginSymbol:
|
||||
return (m_branch_watch.GetSelection()[index.row()].inspection &
|
||||
Inspection::SetOriginSymbolBLR) != Inspection{};
|
||||
return get_bit_test(Inspection::SetOriginSymbolBLR);
|
||||
case Column::DestinSymbol:
|
||||
return (m_branch_watch.GetSelection()[index.row()].inspection &
|
||||
Inspection::SetDestinSymbolBLR) != Inspection{};
|
||||
return get_bit_test(Inspection::SetDestinSymbolBLR);
|
||||
}
|
||||
// Importantly, this code path avoids subscripting the selection to get an inspection value.
|
||||
return false;
|
||||
|
@ -406,31 +407,25 @@ QVariant BranchWatchTableModel::TextAlignmentRoleData(const QModelIndex& index)
|
|||
|
||||
QVariant BranchWatchTableModel::ForegroundRoleData(const QModelIndex& index) const
|
||||
{
|
||||
using Inspection = Core::BranchWatchSelectionInspection;
|
||||
const auto get_brush_v = [this, &index](Inspection inspection_mask) -> QVariant {
|
||||
const Inspection inspection = m_branch_watch.GetSelection()[index.row()].inspection;
|
||||
return (inspection & inspection_mask) != Inspection{} ? QBrush(Qt::red) : QVariant();
|
||||
};
|
||||
switch (index.column())
|
||||
{
|
||||
using Inspection = Core::BranchWatchSelectionInspection;
|
||||
case Column::Instruction:
|
||||
return get_brush_v(Inspection::InvertBranchOption);
|
||||
case Column::Condition:
|
||||
return get_brush_v(Inspection::MakeUnconditional);
|
||||
case Column::Origin:
|
||||
{
|
||||
const Inspection inspection = m_branch_watch.GetSelection()[index.row()].inspection;
|
||||
return (inspection & Inspection::SetOriginNOP) != Inspection{} ? QBrush(Qt::red) : QVariant();
|
||||
}
|
||||
return get_brush_v(Inspection::SetOriginNOP);
|
||||
case Column::Destination:
|
||||
{
|
||||
const Inspection inspection = m_branch_watch.GetSelection()[index.row()].inspection;
|
||||
return (inspection & Inspection::SetDestinBLR) != Inspection{} ? QBrush(Qt::red) : QVariant();
|
||||
}
|
||||
return get_brush_v(Inspection::SetDestinBLR);
|
||||
case Column::OriginSymbol:
|
||||
{
|
||||
const Inspection inspection = m_branch_watch.GetSelection()[index.row()].inspection;
|
||||
return (inspection & Inspection::SetOriginSymbolBLR) != Inspection{} ? QBrush(Qt::red) :
|
||||
QVariant();
|
||||
}
|
||||
return get_brush_v(Inspection::SetOriginSymbolBLR);
|
||||
case Column::DestinSymbol:
|
||||
{
|
||||
const Inspection inspection = m_branch_watch.GetSelection()[index.row()].inspection;
|
||||
return (inspection & Inspection::SetDestinSymbolBLR) != Inspection{} ? QBrush(Qt::red) :
|
||||
QVariant();
|
||||
}
|
||||
return get_brush_v(Inspection::SetDestinSymbolBLR);
|
||||
}
|
||||
// Importantly, this code path avoids subscripting the selection to get an inspection value.
|
||||
return QVariant();
|
||||
|
@ -465,9 +460,9 @@ static int GetConditionInteger(const Core::BranchWatch::Selection::value_type& v
|
|||
{
|
||||
if (value.condition == false)
|
||||
return 0;
|
||||
if (BranchIsUnconditional(kv->first.original_inst))
|
||||
return 2;
|
||||
return 1;
|
||||
if (BranchIsConditional(kv->first.original_inst))
|
||||
return 1;
|
||||
return 2;
|
||||
}
|
||||
|
||||
QVariant BranchWatchTableModel::SortRoleData(const QModelIndex& index) const
|
||||
|
|
|
@ -15,6 +15,8 @@
|
|||
namespace Core
|
||||
{
|
||||
class BranchWatch;
|
||||
enum class BranchWatchSelectionInspection : u8;
|
||||
struct BranchWatchSelectionValueType;
|
||||
class CPUThreadGuard;
|
||||
class System;
|
||||
} // namespace Core
|
||||
|
@ -106,9 +108,12 @@ public:
|
|||
void UpdateHits();
|
||||
void SetInspected(const QModelIndex& index);
|
||||
|
||||
const Core::BranchWatchSelectionValueType&
|
||||
GetBranchWatchSelection(const QModelIndex& index) const;
|
||||
const SymbolList& GetSymbolList() const { return m_symbol_list; }
|
||||
|
||||
private:
|
||||
void SetInspected(const QModelIndex& index, Core::BranchWatchSelectionInspection inspection);
|
||||
void SetOriginInspected(u32 origin_addr);
|
||||
void SetDestinInspected(u32 destin_addr, bool nested);
|
||||
void SetSymbolInspected(u32 symbol_addr, bool nested);
|
||||
|
|
Loading…
Reference in New Issue