mirror of
https://github.com/RPCSX/rpcsx.git
synced 2026-01-02 06:40:05 +01:00
Added a list widget that displays all the currently active breakpoints. You can rename each one for convenience, delete them in bulk, or double click to jump to that address.
766 lines
19 KiB
C++
766 lines
19 KiB
C++
#include "debugger_frame.h"
|
|
|
|
#include <QScrollBar>
|
|
#include <QApplication>
|
|
#include <QFontDatabase>
|
|
#include <QCompleter>
|
|
#include <QMenu>
|
|
|
|
constexpr auto qstr = QString::fromStdString;
|
|
extern bool user_asked_for_frame_capture;
|
|
|
|
debugger_frame::debugger_frame(std::shared_ptr<gui_settings> settings, QWidget *parent)
|
|
: QDockWidget(tr("Debugger"), parent), xgui_settings(settings)
|
|
{
|
|
m_update = new QTimer(this);
|
|
connect(m_update, &QTimer::timeout, this, &debugger_frame::UpdateUI);
|
|
EnableUpdateTimer(true);
|
|
|
|
m_mono = QFontDatabase::systemFont(QFontDatabase::FixedFont);
|
|
m_mono.setPointSize(10);
|
|
|
|
QVBoxLayout* vbox_p_main = new QVBoxLayout();
|
|
QHBoxLayout* hbox_b_main = new QHBoxLayout();
|
|
|
|
m_list = new debugger_list(this);
|
|
m_breakpoints_list = new QListWidget(this);
|
|
m_breakpoints_list->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
|
m_breakpoints_list->setContextMenuPolicy(Qt::CustomContextMenu);
|
|
m_breakpoints_list->setSelectionMode(QAbstractItemView::ExtendedSelection);
|
|
m_choice_units = new QComboBox(this);
|
|
m_choice_units->setSizeAdjustPolicy(QComboBox::AdjustToContents);
|
|
m_choice_units->setMaxVisibleItems(30);
|
|
m_choice_units->setMaximumWidth(500);
|
|
m_choice_units->setEditable(true);
|
|
m_choice_units->setInsertPolicy(QComboBox::NoInsert);
|
|
m_choice_units->lineEdit()->setPlaceholderText("Choose a thread");
|
|
m_choice_units->completer()->setCompletionMode(QCompleter::PopupCompletion);
|
|
m_choice_units->completer()->setMaxVisibleItems(30);
|
|
m_choice_units->completer()->setFilterMode(Qt::MatchContains);
|
|
|
|
m_breakpoints_list_delete = new QAction("Delete", m_breakpoints_list);
|
|
m_breakpoints_list_delete->setShortcut(Qt::Key_Delete);
|
|
m_breakpoints_list_delete->setShortcutContext(Qt::WidgetShortcut);
|
|
m_breakpoints_list->addAction(m_breakpoints_list_delete);
|
|
|
|
m_go_to_addr = new QPushButton(tr("Go To Address"), this);
|
|
m_go_to_pc = new QPushButton(tr("Go To PC"), this);
|
|
m_btn_capture = new QPushButton(tr("Capture"), this);
|
|
m_btn_step = new QPushButton(tr("Step"), this);
|
|
m_btn_run = new QPushButton(Run, this);
|
|
|
|
EnableButtons(!Emu.IsStopped());
|
|
ChangeColors();
|
|
|
|
hbox_b_main->addWidget(m_go_to_addr);
|
|
hbox_b_main->addWidget(m_go_to_pc);
|
|
hbox_b_main->addWidget(m_btn_capture);
|
|
hbox_b_main->addWidget(m_btn_step);
|
|
hbox_b_main->addWidget(m_btn_run);
|
|
hbox_b_main->addWidget(m_choice_units);
|
|
hbox_b_main->addStretch();
|
|
|
|
//Registers
|
|
m_regs = new QTextEdit(this);
|
|
m_regs->setLineWrapMode(QTextEdit::NoWrap);
|
|
m_regs->setTextInteractionFlags(Qt::TextSelectableByMouse | Qt::TextSelectableByKeyboard);
|
|
|
|
m_list->setFont(m_mono);
|
|
m_regs->setFont(m_mono);
|
|
|
|
m_right_splitter = new QSplitter(this);
|
|
m_right_splitter->setOrientation(Qt::Vertical);
|
|
m_right_splitter->addWidget(m_regs);
|
|
m_right_splitter->addWidget(m_breakpoints_list);
|
|
m_right_splitter->setStretchFactor(0, 1);
|
|
|
|
m_splitter = new QSplitter(this);
|
|
m_splitter->addWidget(m_list);
|
|
m_splitter->addWidget(m_right_splitter);
|
|
|
|
QHBoxLayout* hbox_w_list = new QHBoxLayout();
|
|
hbox_w_list->addWidget(m_splitter);
|
|
|
|
vbox_p_main->addLayout(hbox_b_main);
|
|
vbox_p_main->addLayout(hbox_w_list);
|
|
|
|
QWidget* body = new QWidget(this);
|
|
body->setLayout(vbox_p_main);
|
|
setWidget(body);
|
|
|
|
m_list->setWindowTitle(tr("ASM"));
|
|
for (uint i = 0; i < m_list->m_item_count; ++i)
|
|
{
|
|
m_list->insertItem(i, new QListWidgetItem(""));
|
|
}
|
|
m_list->setSizeAdjustPolicy(QListWidget::AdjustToContents);
|
|
|
|
connect(m_go_to_addr, &QAbstractButton::clicked, this, &debugger_frame::Show_Val);
|
|
connect(m_go_to_pc, &QAbstractButton::clicked, this, &debugger_frame::Show_PC);
|
|
|
|
connect(m_btn_capture, &QAbstractButton::clicked, [=]()
|
|
{
|
|
user_asked_for_frame_capture = true;
|
|
});
|
|
|
|
connect(m_btn_step, &QAbstractButton::clicked, this, &debugger_frame::DoStep);
|
|
|
|
connect(m_btn_run, &QAbstractButton::clicked, [=]()
|
|
{
|
|
if (const auto cpu = this->cpu.lock())
|
|
{
|
|
if (m_btn_run->text() == Run && cpu->state.test_and_reset(cpu_flag::dbg_pause))
|
|
{
|
|
if (!test(cpu->state, cpu_flag::dbg_pause + cpu_flag::dbg_global_pause))
|
|
{
|
|
cpu->notify();
|
|
}
|
|
}
|
|
else
|
|
{
|
|
cpu->state += cpu_flag::dbg_pause;
|
|
}
|
|
}
|
|
UpdateUI();
|
|
});
|
|
|
|
connect(m_choice_units->lineEdit(), &QLineEdit::editingFinished, [&]
|
|
{
|
|
m_choice_units->clearFocus();
|
|
});
|
|
|
|
connect(m_choice_units, static_cast<void (QComboBox::*)(int)>(&QComboBox::activated), this, &debugger_frame::UpdateUI);
|
|
connect(m_choice_units, static_cast<void (QComboBox::*)(int)>(&QComboBox::currentIndexChanged), this, &debugger_frame::OnSelectUnit);
|
|
connect(this, &QDockWidget::visibilityChanged, this, &debugger_frame::EnableUpdateTimer);
|
|
|
|
connect(m_breakpoints_list, &QListWidget::itemDoubleClicked, this, &debugger_frame::OnBreakpointList_doubleClicked);
|
|
connect(m_breakpoints_list, &QListWidget::customContextMenuRequested, this, &debugger_frame::OnBreakpointList_rightClicked);
|
|
connect(m_breakpoints_list_delete, &QAction::triggered, this, &debugger_frame::OnBreakpointList_delete);
|
|
|
|
m_list->ShowAddr(CentrePc(m_list->m_pc));
|
|
UpdateUnitList();
|
|
}
|
|
|
|
void debugger_frame::SaveSettings()
|
|
{
|
|
xgui_settings->SetValue(GUI::d_splitterState, m_splitter->saveState());
|
|
}
|
|
|
|
void debugger_frame::ChangeColors()
|
|
{
|
|
if (m_list)
|
|
{
|
|
m_list->m_color_bp = GUI::get_Label_Color("debugger_frame_breakpoint", QPalette::Background);
|
|
m_list->m_color_pc = GUI::get_Label_Color("debugger_frame_pc", QPalette::Background);
|
|
m_list->m_text_color_bp = GUI::get_Label_Color("debugger_frame_breakpoint");;
|
|
m_list->m_text_color_pc = GUI::get_Label_Color("debugger_frame_pc");;
|
|
}
|
|
}
|
|
|
|
void debugger_frame::closeEvent(QCloseEvent *event)
|
|
{
|
|
QDockWidget::closeEvent(event);
|
|
Q_EMIT DebugFrameClosed();
|
|
}
|
|
|
|
void debugger_frame::showEvent(QShowEvent * event)
|
|
{
|
|
// resize splitter widgets
|
|
QByteArray state = xgui_settings->GetValue(GUI::d_splitterState).toByteArray();
|
|
|
|
if (state.isEmpty()) // resize 2:1
|
|
{
|
|
const int width_right = width() / 3;
|
|
const int width_left = width() - width_right;
|
|
m_splitter->setSizes({ width_left, width_right });
|
|
}
|
|
else
|
|
{
|
|
m_splitter->restoreState(state);
|
|
}
|
|
QDockWidget::showEvent(event);
|
|
}
|
|
|
|
void debugger_frame::hideEvent(QHideEvent * event)
|
|
{
|
|
// save splitter state or it will resume its initial state on next show
|
|
xgui_settings->SetValue(GUI::d_splitterState, m_splitter->saveState());
|
|
QDockWidget::hideEvent(event);
|
|
}
|
|
|
|
#include <map>
|
|
|
|
std::map<u32, bool> g_breakpoints;
|
|
|
|
extern void ppu_breakpoint(u32 addr);
|
|
|
|
u32 debugger_frame::GetPc() const
|
|
{
|
|
const auto cpu = this->cpu.lock();
|
|
|
|
if (!cpu)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
switch (g_system)
|
|
{
|
|
case system_type::ps3: return cpu->id_type() == 1 ? static_cast<ppu_thread*>(cpu.get())->cia : static_cast<SPUThread*>(cpu.get())->pc;
|
|
case system_type::psv: return static_cast<ARMv7Thread*>(cpu.get())->PC;
|
|
}
|
|
|
|
return 0xabadcafe;
|
|
}
|
|
|
|
u32 debugger_frame::CentrePc(u32 pc) const
|
|
{
|
|
return pc - ((m_list->m_item_count / 2) * 4);
|
|
}
|
|
|
|
void debugger_frame::UpdateUI()
|
|
{
|
|
UpdateUnitList();
|
|
|
|
if (m_no_thread_selected) return;
|
|
|
|
const auto cpu = this->cpu.lock();
|
|
|
|
if (!cpu)
|
|
{
|
|
if (m_last_pc != -1 || m_last_stat)
|
|
{
|
|
m_last_pc = -1;
|
|
m_last_stat = 0;
|
|
DoUpdate();
|
|
|
|
m_btn_run->setEnabled(false);
|
|
m_btn_step->setEnabled(false);
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const auto cia = GetPc();
|
|
const auto state = cpu->state.load();
|
|
|
|
if (m_last_pc != cia || m_last_stat != static_cast<u32>(state))
|
|
{
|
|
m_last_pc = cia;
|
|
m_last_stat = static_cast<u32>(state);
|
|
DoUpdate();
|
|
|
|
if (test(state & cpu_flag::dbg_pause))
|
|
{
|
|
m_btn_run->setText(Run);
|
|
m_btn_step->setEnabled(true);
|
|
}
|
|
else
|
|
{
|
|
m_btn_run->setText(Pause);
|
|
m_btn_step->setEnabled(false);
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void debugger_frame::UpdateUnitList()
|
|
{
|
|
const u64 threads_created = cpu_thread::g_threads_created;
|
|
const u64 threads_deleted = cpu_thread::g_threads_deleted;
|
|
|
|
if (threads_created != m_threads_created || threads_deleted != m_threads_deleted)
|
|
{
|
|
m_threads_created = threads_created;
|
|
m_threads_deleted = threads_deleted;
|
|
}
|
|
else
|
|
{
|
|
// Nothing to do
|
|
return;
|
|
}
|
|
|
|
QVariant old_cpu = m_choice_units->currentData();
|
|
|
|
m_choice_units->clear();
|
|
m_choice_units->addItem(NoThread);
|
|
|
|
const auto on_select = [&](u32, cpu_thread& cpu)
|
|
{
|
|
QVariant var_cpu = qVariantFromValue((void *)&cpu);
|
|
m_choice_units->addItem(qstr(cpu.get_name()), var_cpu);
|
|
if (old_cpu == var_cpu) m_choice_units->setCurrentIndex(m_choice_units->count() - 1);
|
|
};
|
|
|
|
{
|
|
const QSignalBlocker blocker(m_choice_units);
|
|
|
|
idm::select<ppu_thread>(on_select);
|
|
idm::select<ARMv7Thread>(on_select);
|
|
idm::select<RawSPUThread>(on_select);
|
|
idm::select<SPUThread>(on_select);
|
|
}
|
|
|
|
OnSelectUnit();
|
|
|
|
m_choice_units->update();
|
|
}
|
|
|
|
void debugger_frame::OnSelectUnit()
|
|
{
|
|
if (m_choice_units->count() < 1 || m_current_choice == m_choice_units->currentText()) return;
|
|
|
|
m_current_choice = m_choice_units->currentText();
|
|
m_no_thread_selected = m_current_choice == NoThread;
|
|
m_list->m_no_thread_selected = m_no_thread_selected;
|
|
|
|
m_disasm.reset();
|
|
cpu.reset();
|
|
|
|
if (!m_no_thread_selected)
|
|
{
|
|
const auto on_select = [&](u32, cpu_thread& cpu)
|
|
{
|
|
cpu_thread* data = (cpu_thread *)m_choice_units->currentData().value<void *>();
|
|
return data == &cpu;
|
|
};
|
|
|
|
if (auto ppu = idm::select<ppu_thread>(on_select))
|
|
{
|
|
m_disasm = std::make_unique<PPUDisAsm>(CPUDisAsm_InterpreterMode);
|
|
cpu = ppu.ptr;
|
|
}
|
|
else if (auto spu1 = idm::select<SPUThread>(on_select))
|
|
{
|
|
m_disasm = std::make_unique<SPUDisAsm>(CPUDisAsm_InterpreterMode);
|
|
cpu = spu1.ptr;
|
|
}
|
|
else if (auto rspu = idm::select<RawSPUThread>(on_select))
|
|
{
|
|
m_disasm = std::make_unique<SPUDisAsm>(CPUDisAsm_InterpreterMode);
|
|
cpu = rspu.ptr;
|
|
}
|
|
else if (auto arm = idm::select<ARMv7Thread>(on_select))
|
|
{
|
|
m_disasm = std::make_unique<ARMv7DisAsm>(CPUDisAsm_InterpreterMode);
|
|
cpu = arm.ptr;
|
|
}
|
|
}
|
|
|
|
DoUpdate();
|
|
}
|
|
|
|
void debugger_frame::DoUpdate()
|
|
{
|
|
Show_PC();
|
|
WriteRegs();
|
|
}
|
|
|
|
void debugger_frame::WriteRegs()
|
|
{
|
|
const auto cpu = this->cpu.lock();
|
|
|
|
if (!cpu)
|
|
{
|
|
m_regs->clear();
|
|
return;
|
|
}
|
|
int loc = m_regs->verticalScrollBar()->value();
|
|
m_regs->clear();
|
|
m_regs->setText(qstr(cpu->dump()));
|
|
m_regs->verticalScrollBar()->setValue(loc);
|
|
}
|
|
|
|
void debugger_frame::OnUpdate()
|
|
{
|
|
//WriteRegs();
|
|
}
|
|
|
|
void debugger_frame::Show_Val()
|
|
{
|
|
QDialog* diag = new QDialog(this);
|
|
diag->setWindowTitle(tr("Set value"));
|
|
diag->setModal(true);
|
|
|
|
QPushButton* button_ok = new QPushButton(tr("Ok"));
|
|
QPushButton* button_cancel = new QPushButton(tr("Cancel"));
|
|
QVBoxLayout* vbox_panel(new QVBoxLayout());
|
|
QHBoxLayout* hbox_text_panel(new QHBoxLayout());
|
|
QHBoxLayout* hbox_button_panel(new QHBoxLayout());
|
|
QLineEdit* p_pc(new QLineEdit(diag));
|
|
p_pc->setFont(m_mono);
|
|
p_pc->setMaxLength(8);
|
|
p_pc->setFixedWidth(90);
|
|
QLabel* addr(new QLabel(diag));
|
|
addr->setFont(m_mono);
|
|
|
|
hbox_text_panel->addWidget(addr);
|
|
hbox_text_panel->addWidget(p_pc);
|
|
|
|
hbox_button_panel->addWidget(button_ok);
|
|
hbox_button_panel->addWidget(button_cancel);
|
|
|
|
vbox_panel->addLayout(hbox_text_panel);
|
|
vbox_panel->addSpacing(8);
|
|
vbox_panel->addLayout(hbox_button_panel);
|
|
|
|
diag->setLayout(vbox_panel);
|
|
|
|
const auto cpu = this->cpu.lock();
|
|
|
|
if (cpu)
|
|
{
|
|
unsigned long pc = cpu ? GetPc() : 0x0;
|
|
addr->setText("Address: " + QString("%1").arg(pc, 8, 16, QChar('0'))); // set address input line to 8 digits
|
|
p_pc->setPlaceholderText(QString("%1").arg(pc, 8, 16, QChar('0')));
|
|
}
|
|
else
|
|
{
|
|
p_pc->setPlaceholderText("00000000");
|
|
addr->setText("Address: 00000000");
|
|
}
|
|
|
|
auto l_changeLabel = [=]()
|
|
{
|
|
if (p_pc->text().isEmpty())
|
|
{
|
|
addr->setText("Address: " + p_pc->placeholderText());
|
|
}
|
|
else
|
|
{
|
|
bool ok;
|
|
ulong ul_addr = p_pc->text().toULong(&ok, 16);
|
|
addr->setText("Address: " + QString("%1").arg(ul_addr, 8, 16, QChar('0'))); // set address input line to 8 digits
|
|
}
|
|
};
|
|
|
|
connect(p_pc, &QLineEdit::textChanged, l_changeLabel);
|
|
connect(button_ok, &QAbstractButton::clicked, diag, &QDialog::accept);
|
|
connect(button_cancel, &QAbstractButton::clicked, diag, &QDialog::reject);
|
|
|
|
diag->move(QCursor::pos());
|
|
|
|
if (diag->exec() == QDialog::Accepted)
|
|
{
|
|
unsigned long pc = cpu ? GetPc() : 0x0;
|
|
if (p_pc->text().isEmpty())
|
|
{
|
|
addr->setText(p_pc->placeholderText());
|
|
}
|
|
else
|
|
{
|
|
bool ok;
|
|
pc = p_pc->text().toULong(&ok, 16);
|
|
addr->setText(p_pc->text());
|
|
}
|
|
m_list->ShowAddr(CentrePc(pc));
|
|
}
|
|
|
|
diag->deleteLater();
|
|
}
|
|
|
|
void debugger_frame::Show_PC()
|
|
{
|
|
m_list->ShowAddr(CentrePc(GetPc()));
|
|
}
|
|
|
|
void debugger_frame::DoStep()
|
|
{
|
|
if (const auto cpu = this->cpu.lock())
|
|
{
|
|
if (test(cpu_flag::dbg_pause, cpu->state.fetch_op([](bs_t<cpu_flag>& state)
|
|
{
|
|
state += cpu_flag::dbg_step;
|
|
state -= cpu_flag::dbg_pause;
|
|
})))
|
|
{
|
|
cpu->notify();
|
|
}
|
|
}
|
|
UpdateUI();
|
|
}
|
|
|
|
void debugger_frame::EnableUpdateTimer(bool enable)
|
|
{
|
|
enable ? m_update->start(50) : m_update->stop();
|
|
}
|
|
|
|
void debugger_frame::EnableButtons(bool enable)
|
|
{
|
|
m_go_to_addr->setEnabled(enable);
|
|
m_go_to_pc->setEnabled(enable);
|
|
m_btn_step->setEnabled(enable);
|
|
m_btn_run->setEnabled(enable);
|
|
}
|
|
|
|
void debugger_frame::ClearBreakpoints()
|
|
{
|
|
//This is required to actually delete the breakpoints, not just visually.
|
|
for (std::map<u32, bool>::iterator it = g_breakpoints.begin(); it != g_breakpoints.end(); it++)
|
|
m_list->RemoveBreakPoint(it->first, false);
|
|
|
|
g_breakpoints.clear();
|
|
}
|
|
|
|
void debugger_frame::OnBreakpointList_doubleClicked()
|
|
{
|
|
m_list->ShowAddr(CentrePc(m_breakpoints_list->currentItem()->data(Qt::UserRole).value<u32>()));
|
|
m_list->setCurrentRow(16);
|
|
}
|
|
|
|
void debugger_frame::OnBreakpointList_rightClicked(const QPoint &pos)
|
|
{
|
|
if (m_breakpoints_list->itemAt(pos) == NULL)
|
|
return;
|
|
|
|
QMenu* menu = new QMenu();
|
|
|
|
if (m_breakpoints_list->selectedItems().count() == 1)
|
|
{
|
|
menu->addAction("Rename");
|
|
menu->addSeparator();
|
|
}
|
|
menu->addAction(m_breakpoints_list_delete);
|
|
|
|
QAction* selectedItem = menu->exec(QCursor::pos());
|
|
if (selectedItem != NULL)
|
|
{
|
|
if (selectedItem->text() == "Rename")
|
|
{
|
|
QListWidgetItem* currentItem = m_breakpoints_list->selectedItems().at(0);
|
|
|
|
currentItem->setFlags(currentItem->flags() | Qt::ItemIsEditable);
|
|
m_breakpoints_list->editItem(currentItem);
|
|
}
|
|
}
|
|
}
|
|
|
|
void debugger_frame::OnBreakpointList_delete()
|
|
{
|
|
int selectedCount = m_breakpoints_list->selectedItems().count();
|
|
|
|
for (int i = selectedCount - 1; i >= 0; i--)
|
|
{
|
|
m_list->RemoveBreakPoint(m_breakpoints_list->item(i)->data(Qt::UserRole).value<u32>());
|
|
}
|
|
}
|
|
|
|
debugger_list::debugger_list(debugger_frame* parent) : QListWidget(parent)
|
|
{
|
|
m_pc = 0;
|
|
m_item_count = 30;
|
|
m_debugFrame = parent;
|
|
};
|
|
|
|
void debugger_list::ShowAddr(u32 addr)
|
|
{
|
|
m_pc = addr;
|
|
|
|
const auto cpu = m_debugFrame->cpu.lock();
|
|
|
|
if (!cpu)
|
|
{
|
|
for (uint i = 0; i<m_item_count; ++i, m_pc += 4)
|
|
{
|
|
item(i)->setText(qstr(fmt::format("[%08x] illegal address", m_pc)));
|
|
}
|
|
}
|
|
else
|
|
{
|
|
const u32 cpu_offset = g_system == system_type::ps3 && cpu->id_type() != 1 ? static_cast<SPUThread&>(*cpu).offset : 0;
|
|
m_debugFrame->m_disasm->offset = (u8*)vm::base(cpu_offset);
|
|
for (uint i = 0, count = 4; i<m_item_count; ++i, m_pc += count)
|
|
{
|
|
if (!vm::check_addr(cpu_offset + m_pc, 4))
|
|
{
|
|
item(i)->setText((IsBreakPoint(m_pc) ? ">>> " : " ") + qstr(fmt::format("[%08x] illegal address", m_pc)));
|
|
count = 4;
|
|
continue;
|
|
}
|
|
|
|
count = m_debugFrame->m_disasm->disasm(m_debugFrame->m_disasm->dump_pc = m_pc);
|
|
|
|
item(i)->setText((IsBreakPoint(m_pc) ? ">>> " : " ") + qstr(m_debugFrame->m_disasm->last_opcode));
|
|
|
|
if (test(cpu->state & cpu_state_pause) && m_pc == m_debugFrame->GetPc())
|
|
{
|
|
item(i)->setTextColor(m_text_color_pc);
|
|
item(i)->setBackgroundColor(m_color_pc);
|
|
}
|
|
else if (IsBreakPoint(m_pc))
|
|
{
|
|
item(i)->setTextColor(m_text_color_bp);
|
|
item(i)->setBackgroundColor(m_color_bp);
|
|
}
|
|
else
|
|
{
|
|
item(i)->setTextColor(palette().color(foregroundRole()));
|
|
item(i)->setBackgroundColor(palette().color(backgroundRole()));
|
|
}
|
|
}
|
|
}
|
|
|
|
setLineWidth(-1);
|
|
}
|
|
|
|
bool debugger_list::IsBreakPoint(u32 pc)
|
|
{
|
|
return g_breakpoints.count(pc) != 0;
|
|
}
|
|
|
|
void debugger_list::AddBreakPoint(u32 pc)
|
|
{
|
|
g_breakpoints.emplace(pc, false);
|
|
ppu_breakpoint(pc);
|
|
|
|
const auto cpu = m_debugFrame->cpu.lock();
|
|
const u32 cpu_offset = g_system == system_type::ps3 && cpu->id_type() != 1 ? static_cast<SPUThread&>(*cpu).offset : 0;
|
|
m_debugFrame->m_disasm->offset = (u8*)vm::base(cpu_offset);
|
|
|
|
m_debugFrame->m_disasm->disasm(m_debugFrame->m_disasm->dump_pc = pc);
|
|
|
|
QString breakpointItemText = qstr(m_debugFrame->m_disasm->last_opcode);
|
|
|
|
breakpointItemText.remove(10, 13);
|
|
|
|
QListWidgetItem* breakpointItem = new QListWidgetItem(breakpointItemText);
|
|
breakpointItem->setTextColor(m_text_color_bp);
|
|
breakpointItem->setBackgroundColor(m_color_bp);
|
|
QVariant pcVariant;
|
|
pcVariant.setValue(pc);
|
|
breakpointItem->setData(Qt::UserRole, pcVariant);
|
|
m_debugFrame->m_breakpoints_list->addItem(breakpointItem);
|
|
}
|
|
|
|
void debugger_list::RemoveBreakPoint(u32 pc, bool eraseFromMap)
|
|
{
|
|
if(eraseFromMap)
|
|
g_breakpoints.erase(pc);
|
|
ppu_breakpoint(pc);
|
|
|
|
int breakpointsListCount = m_debugFrame->m_breakpoints_list->count();
|
|
|
|
for (int i = 0; i < breakpointsListCount; i++)
|
|
{
|
|
QListWidgetItem* currentItem = m_debugFrame->m_breakpoints_list->item(i);
|
|
|
|
if (currentItem->data(Qt::UserRole).value<u32>() == pc)
|
|
{
|
|
delete m_debugFrame->m_breakpoints_list->takeItem(i);
|
|
break;
|
|
}
|
|
}
|
|
|
|
ShowAddr(m_pc - (m_item_count) * 4);
|
|
|
|
}
|
|
|
|
void debugger_list::keyPressEvent(QKeyEvent* event)
|
|
{
|
|
if (!isActiveWindow())
|
|
{
|
|
return;
|
|
}
|
|
|
|
const auto cpu = m_debugFrame->cpu.lock();
|
|
long i = currentRow();
|
|
|
|
if (i < 0 || !cpu)
|
|
{
|
|
return;
|
|
}
|
|
|
|
const u32 start_pc = m_pc - m_item_count * 4;
|
|
const u32 pc = start_pc + i * 4;
|
|
|
|
if (event->key() == Qt::Key_Space && QApplication::keyboardModifiers() & Qt::ControlModifier)
|
|
{
|
|
m_debugFrame->DoStep();
|
|
return;
|
|
}
|
|
else
|
|
{
|
|
switch (event->key())
|
|
{
|
|
case Qt::Key_PageUp: ShowAddr(m_pc - (m_item_count * 2) * 4); return;
|
|
case Qt::Key_PageDown: ShowAddr(m_pc); return;
|
|
case Qt::Key_Up: ShowAddr(m_pc - (m_item_count + 1) * 4); return;
|
|
case Qt::Key_Down: ShowAddr(m_pc - (m_item_count - 1) * 4); return;
|
|
case Qt::Key_E:
|
|
{
|
|
instruction_editor_dialog* dlg = new instruction_editor_dialog(this, pc, cpu, m_debugFrame->m_disasm.get());
|
|
dlg->show();
|
|
return;
|
|
}
|
|
case Qt::Key_R:
|
|
{
|
|
register_editor_dialog* dlg = new register_editor_dialog(this, pc, cpu, m_debugFrame->m_disasm.get());
|
|
dlg->show();
|
|
return;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
void debugger_list::mouseDoubleClickEvent(QMouseEvent* event)
|
|
{
|
|
if (event->button() == Qt::LeftButton && !Emu.IsStopped() && !m_no_thread_selected)
|
|
{
|
|
long i = currentRow();
|
|
if (i < 0) return;
|
|
|
|
const u32 start_pc = m_pc - m_item_count * 4;
|
|
const u32 pc = start_pc + i * 4;
|
|
//ConLog.Write("pc=0x%llx", pc);
|
|
|
|
if (IsBreakPoint(pc))
|
|
{
|
|
RemoveBreakPoint(pc);
|
|
}
|
|
else
|
|
{
|
|
const auto cpu = m_debugFrame->cpu.lock();
|
|
|
|
if (g_system == system_type::ps3 && cpu->id_type() == 1 && vm::check_addr(pc))
|
|
{
|
|
AddBreakPoint(pc);
|
|
}
|
|
}
|
|
|
|
ShowAddr(start_pc);
|
|
}
|
|
}
|
|
|
|
void debugger_list::wheelEvent(QWheelEvent* event)
|
|
{
|
|
QPoint numSteps = event->angleDelta() / 8 / 15; // http://doc.qt.io/qt-5/qwheelevent.html#pixelDelta
|
|
const int value = numSteps.y();
|
|
|
|
ShowAddr(m_pc - (event->modifiers() == Qt::ControlModifier ? m_item_count * (value + 1) : m_item_count + value) * 4);
|
|
}
|
|
|
|
void debugger_list::resizeEvent(QResizeEvent* event)
|
|
{
|
|
Q_UNUSED(event);
|
|
|
|
if (count() < 1 || visualItemRect(item(0)).height() < 1)
|
|
{
|
|
return;
|
|
}
|
|
|
|
m_item_count = (rect().height() - frameWidth()*2) / visualItemRect(item(0)).height();
|
|
|
|
clear();
|
|
|
|
for (u32 i = 0; i < m_item_count; ++i)
|
|
{
|
|
insertItem(i, new QListWidgetItem(""));
|
|
}
|
|
|
|
if (horizontalScrollBar())
|
|
{
|
|
m_item_count--;
|
|
delete item(m_item_count);
|
|
}
|
|
|
|
ShowAddr(m_pc - m_item_count * 4);
|
|
}
|