1 // Copyright 2018 Dolphin Emulator Project
2 // Licensed under GPLv2+
3 // Refer to the license.txt file included.
4 
5 #include "DolphinQt/Debugger/CodeViewWidget.h"
6 
7 #include <algorithm>
8 #include <cmath>
9 
10 #include <QApplication>
11 #include <QClipboard>
12 #include <QHeaderView>
13 #include <QInputDialog>
14 #include <QKeyEvent>
15 #include <QMenu>
16 #include <QMouseEvent>
17 #include <QPainter>
18 #include <QResizeEvent>
19 #include <QScrollBar>
20 #include <QStyledItemDelegate>
21 #include <QTableWidgetItem>
22 #include <QWheelEvent>
23 
24 #include "Common/GekkoDisassembler.h"
25 #include "Common/StringUtil.h"
26 #include "Core/Core.h"
27 #include "Core/Debugger/PPCDebugInterface.h"
28 #include "Core/PowerPC/MMU.h"
29 #include "Core/PowerPC/PPCAnalyst.h"
30 #include "Core/PowerPC/PPCSymbolDB.h"
31 #include "Core/PowerPC/PowerPC.h"
32 #include "DolphinQt/Debugger/PatchInstructionDialog.h"
33 #include "DolphinQt/Host.h"
34 #include "DolphinQt/Resources.h"
35 #include "DolphinQt/Settings.h"
36 
37 struct CodeViewBranch
38 {
39   u32 src_addr;
40   u32 dst_addr;
41   u32 indentation = 0;
42   bool is_link;
43 };
44 
45 constexpr u32 WIDTH_PER_BRANCH_ARROW = 16;
46 
47 class BranchDisplayDelegate : public QStyledItemDelegate
48 {
49 public:
BranchDisplayDelegate(CodeViewWidget * parent)50   BranchDisplayDelegate(CodeViewWidget* parent) : m_parent(parent) {}
51 
52 private:
53   CodeViewWidget* m_parent;
54 
paint(QPainter * painter,const QStyleOptionViewItem & option,const QModelIndex & index) const55   void paint(QPainter* painter, const QStyleOptionViewItem& option,
56              const QModelIndex& index) const override
57   {
58     QStyledItemDelegate::paint(painter, option, index);
59 
60     painter->save();
61     painter->setClipRect(option.rect);
62     painter->setPen(m_parent->palette().text().color());
63 
64     constexpr u32 x_offset_in_branch_for_vertical_line = 10;
65     const u32 addr = m_parent->AddressForRow(index.row());
66     for (const CodeViewBranch& branch : m_parent->m_branches)
67     {
68       const int y_center = option.rect.top() + option.rect.height() / 2;
69       const int x_left = option.rect.left() + WIDTH_PER_BRANCH_ARROW * branch.indentation;
70       const int x_right = x_left + x_offset_in_branch_for_vertical_line;
71 
72       if (branch.is_link)
73       {
74         // just draw an arrow pointing right from the branch instruction for link branches, they
75         // rarely are close enough to actually see the target and are just visual noise otherwise
76         if (addr == branch.src_addr)
77         {
78           painter->drawLine(x_left, y_center, x_right, y_center);
79           painter->drawLine(x_right, y_center, x_right - 6, y_center - 3);
80           painter->drawLine(x_right, y_center, x_right - 6, y_center + 3);
81         }
82       }
83       else
84       {
85         const u32 addr_lower = std::min(branch.src_addr, branch.dst_addr);
86         const u32 addr_higher = std::max(branch.src_addr, branch.dst_addr);
87         const bool in_range = addr >= addr_lower && addr <= addr_higher;
88 
89         if (in_range)
90         {
91           const bool is_lowest = addr == addr_lower;
92           const bool is_highest = addr == addr_higher;
93           const int top = (is_lowest ? y_center : option.rect.top());
94           const int bottom = (is_highest ? y_center : option.rect.bottom());
95 
96           // draw vertical part of the branch line
97           painter->drawLine(x_right, top, x_right, bottom);
98 
99           if (is_lowest || is_highest)
100           {
101             // draw horizontal part of the branch line if this is either the source or destination
102             painter->drawLine(x_left, y_center, x_right, y_center);
103           }
104 
105           if (addr == branch.dst_addr)
106           {
107             // draw arrow if this is the destination address
108             painter->drawLine(x_left, y_center, x_left + 6, y_center - 3);
109             painter->drawLine(x_left, y_center, x_left + 6, y_center + 3);
110           }
111         }
112       }
113     }
114 
115     painter->restore();
116   }
117 };
118 
119 // "Most mouse types work in steps of 15 degrees, in which case the delta value is a multiple of
120 // 120; i.e., 120 units * 1/8 = 15 degrees." (http://doc.qt.io/qt-5/qwheelevent.html#angleDelta)
121 constexpr double SCROLL_FRACTION_DEGREES = 15.;
122 
123 constexpr size_t VALID_BRANCH_LENGTH = 10;
124 
125 constexpr int CODE_VIEW_COLUMN_BREAKPOINT = 0;
126 constexpr int CODE_VIEW_COLUMN_ADDRESS = 1;
127 constexpr int CODE_VIEW_COLUMN_INSTRUCTION = 2;
128 constexpr int CODE_VIEW_COLUMN_PARAMETERS = 3;
129 constexpr int CODE_VIEW_COLUMN_DESCRIPTION = 4;
130 constexpr int CODE_VIEW_COLUMN_BRANCH_ARROWS = 5;
131 constexpr int CODE_VIEW_COLUMNCOUNT = 6;
132 
CodeViewWidget()133 CodeViewWidget::CodeViewWidget()
134 {
135   setColumnCount(CODE_VIEW_COLUMNCOUNT);
136   setShowGrid(false);
137   setContextMenuPolicy(Qt::CustomContextMenu);
138   setSelectionMode(QAbstractItemView::SingleSelection);
139   setSelectionBehavior(QAbstractItemView::SelectRows);
140 
141   setVerticalScrollBarPolicy(Qt::ScrollBarAlwaysOff);
142   setHorizontalScrollMode(QAbstractItemView::ScrollPerPixel);
143 
144   verticalHeader()->hide();
145   horizontalHeader()->setSectionResizeMode(CODE_VIEW_COLUMN_BREAKPOINT, QHeaderView::Fixed);
146   horizontalHeader()->setStretchLastSection(true);
147   setHorizontalHeaderItem(CODE_VIEW_COLUMN_BREAKPOINT, new QTableWidgetItem());
148   setHorizontalHeaderItem(CODE_VIEW_COLUMN_ADDRESS, new QTableWidgetItem(tr("Address")));
149   setHorizontalHeaderItem(CODE_VIEW_COLUMN_INSTRUCTION, new QTableWidgetItem(tr("Instr.")));
150   setHorizontalHeaderItem(CODE_VIEW_COLUMN_PARAMETERS, new QTableWidgetItem(tr("Parameters")));
151   setHorizontalHeaderItem(CODE_VIEW_COLUMN_DESCRIPTION, new QTableWidgetItem(tr("Symbols")));
152   setHorizontalHeaderItem(CODE_VIEW_COLUMN_BRANCH_ARROWS, new QTableWidgetItem(tr("Branches")));
153 
154   setFont(Settings::Instance().GetDebugFont());
155   setItemDelegateForColumn(CODE_VIEW_COLUMN_BRANCH_ARROWS, new BranchDisplayDelegate(this));
156 
157   FontBasedSizing();
158 
159   connect(this, &CodeViewWidget::customContextMenuRequested, this, &CodeViewWidget::OnContextMenu);
160   connect(this, &CodeViewWidget::itemSelectionChanged, this, &CodeViewWidget::OnSelectionChanged);
161   connect(&Settings::Instance(), &Settings::DebugFontChanged, this, &QWidget::setFont);
162   connect(&Settings::Instance(), &Settings::DebugFontChanged, this,
163           &CodeViewWidget::FontBasedSizing);
164 
165   connect(&Settings::Instance(), &Settings::EmulationStateChanged, this, [this] {
166     m_address = PC;
167     Update();
168   });
169   connect(Host::GetInstance(), &Host::UpdateDisasmDialog, this, [this] {
170     m_address = PC;
171     Update();
172   });
173 
174   connect(&Settings::Instance(), &Settings::ThemeChanged, this, &CodeViewWidget::Update);
175 }
176 
177 CodeViewWidget::~CodeViewWidget() = default;
178 
GetBranchFromAddress(u32 addr)179 static u32 GetBranchFromAddress(u32 addr)
180 {
181   std::string disasm = PowerPC::debug_interface.Disassemble(addr);
182   size_t pos = disasm.find("->0x");
183 
184   if (pos == std::string::npos)
185     return 0;
186 
187   std::string hex = disasm.substr(pos + 2);
188   return std::stoul(hex, nullptr, 16);
189 }
190 
FontBasedSizing()191 void CodeViewWidget::FontBasedSizing()
192 {
193   // just text width is too small with some fonts, so increase by a bit
194   constexpr int extra_text_width = 8;
195 
196   const QFontMetrics fm(Settings::Instance().GetDebugFont());
197 
198   const int rowh = fm.height() + 1;
199   verticalHeader()->setMaximumSectionSize(rowh);
200   horizontalHeader()->setMinimumSectionSize(rowh + 5);
201   setColumnWidth(CODE_VIEW_COLUMN_BREAKPOINT, rowh + 5);
202   setColumnWidth(CODE_VIEW_COLUMN_ADDRESS,
203                  fm.boundingRect(QStringLiteral("80000000")).width() + extra_text_width);
204 
205   // The longest instruction is technically 'ps_merge00' (0x10000420u), but those instructions are
206   // very rare and would needlessly increase the column size, so let's go with 'rlwinm.' instead.
207   // Similarly, the longest parameter set is 'rtoc, rtoc, r10, 10, 10 (00000800)' (0x5c425294u),
208   // but one is unlikely to encounter that in practice, so let's use a slightly more reasonable
209   // 'r31, r31, 16, 16, 31 (ffff0000)'. The user can resize the columns as necessary anyway.
210   const std::string disas = Common::GekkoDisassembler::Disassemble(0x57ff843fu, 0);
211   const auto split = disas.find('\t');
212   const std::string ins = (split == std::string::npos ? disas : disas.substr(0, split));
213   const std::string param = (split == std::string::npos ? "" : disas.substr(split + 1));
214   setColumnWidth(CODE_VIEW_COLUMN_INSTRUCTION,
215                  fm.boundingRect(QString::fromStdString(ins)).width() + extra_text_width);
216   setColumnWidth(CODE_VIEW_COLUMN_PARAMETERS,
217                  fm.boundingRect(QString::fromStdString(param)).width() + extra_text_width);
218   setColumnWidth(CODE_VIEW_COLUMN_DESCRIPTION,
219                  fm.boundingRect(QChar(u'0')).width() * 25 + extra_text_width);
220 
221   Update();
222 }
223 
AddressForRow(int row) const224 u32 CodeViewWidget::AddressForRow(int row) const
225 {
226   // m_address is defined as the center row of the table, so we have rowCount/2 instructions above
227   // it; an instruction is 4 bytes long on GC/Wii so we increment 4 bytes per row
228   const u32 row_zero_address = m_address - ((rowCount() / 2) * 4);
229   return row_zero_address + row * 4;
230 }
231 
IsBranchInstructionWithLink(std::string_view ins)232 static bool IsBranchInstructionWithLink(std::string_view ins)
233 {
234   return StringEndsWith(ins, "l") || StringEndsWith(ins, "la") || StringEndsWith(ins, "l+") ||
235          StringEndsWith(ins, "la+") || StringEndsWith(ins, "l-") || StringEndsWith(ins, "la-");
236 }
237 
Update()238 void CodeViewWidget::Update()
239 {
240   if (!isVisible())
241     return;
242 
243   if (m_updating)
244     return;
245 
246   m_updating = true;
247 
248   clearSelection();
249   if (rowCount() == 0)
250     setRowCount(1);
251 
252   // Calculate (roughly) how many rows will fit in our table
253   int rows = std::round((height() / static_cast<float>(rowHeight(0))) - 0.25);
254 
255   setRowCount(rows);
256 
257   const QFontMetrics fm(Settings::Instance().GetDebugFont());
258   const int rowh = fm.height() + 1;
259 
260   for (int i = 0; i < rows; i++)
261     setRowHeight(i, rowh);
262 
263   u32 pc = PowerPC::ppcState.pc;
264 
265   if (Core::GetState() != Core::State::Paused && PowerPC::debug_interface.IsBreakpoint(pc))
266     Core::SetState(Core::State::Paused);
267 
268   const bool dark_theme = qApp->palette().color(QPalette::Base).valueF() < 0.5;
269 
270   m_branches.clear();
271 
272   for (int i = 0; i < rowCount(); i++)
273   {
274     const u32 addr = AddressForRow(i);
275     const u32 color = PowerPC::debug_interface.GetColor(addr);
276     auto* bp_item = new QTableWidgetItem;
277     auto* addr_item = new QTableWidgetItem(QStringLiteral("%1").arg(addr, 8, 16, QLatin1Char('0')));
278 
279     std::string disas = PowerPC::debug_interface.Disassemble(addr);
280     auto split = disas.find('\t');
281 
282     std::string ins = (split == std::string::npos ? disas : disas.substr(0, split));
283     std::string param = (split == std::string::npos ? "" : disas.substr(split + 1));
284     std::string desc = PowerPC::debug_interface.GetDescription(addr);
285 
286     // Adds whitespace and a minimum size to ins and param. Helps to prevent frequent resizing while
287     // scrolling.
288     const QString ins_formatted =
289         QStringLiteral("%1").arg(QString::fromStdString(ins), -7, QLatin1Char(' '));
290     const QString param_formatted =
291         QStringLiteral("%1").arg(QString::fromStdString(param), -19, QLatin1Char(' '));
292     const QString desc_formatted = QStringLiteral("%1   ").arg(QString::fromStdString(desc));
293 
294     auto* ins_item = new QTableWidgetItem(ins_formatted);
295     auto* param_item = new QTableWidgetItem(param_formatted);
296     auto* description_item = new QTableWidgetItem(desc_formatted);
297     auto* branch_item = new QTableWidgetItem();
298 
299     for (auto* item : {bp_item, addr_item, ins_item, param_item, description_item, branch_item})
300     {
301       item->setFlags(Qt::ItemIsEnabled | Qt::ItemIsSelectable);
302       item->setData(Qt::UserRole, addr);
303 
304       if (addr == pc && item != bp_item)
305       {
306         item->setBackground(QColor(Qt::green));
307         item->setForeground(QColor(Qt::black));
308       }
309       else if (color != 0xFFFFFF)
310       {
311         item->setBackground(dark_theme ? QColor(color).darker(240) : QColor(color));
312       }
313     }
314 
315     // look for hex strings to decode branches
316     std::string hex_str;
317     size_t pos = param.find("0x");
318     if (pos != std::string::npos)
319     {
320       hex_str = param.substr(pos);
321     }
322 
323     if (hex_str.length() == VALID_BRANCH_LENGTH && desc != "---")
324     {
325       u32 branch_addr = GetBranchFromAddress(addr);
326       CodeViewBranch& branch = m_branches.emplace_back();
327       branch.src_addr = addr;
328       branch.dst_addr = branch_addr;
329       branch.is_link = IsBranchInstructionWithLink(ins);
330 
331       description_item->setText(tr("--> %1").arg(
332           QString::fromStdString(PowerPC::debug_interface.GetDescription(branch_addr))));
333       param_item->setForeground(Qt::magenta);
334     }
335 
336     if (ins == "blr")
337       ins_item->setForeground(dark_theme ? QColor(0xa0FFa0) : Qt::darkGreen);
338 
339     if (PowerPC::debug_interface.IsBreakpoint(addr))
340     {
341       bp_item->setData(
342           Qt::DecorationRole,
343           Resources::GetScaledThemeIcon("debugger_breakpoint").pixmap(QSize(rowh - 2, rowh - 2)));
344     }
345 
346     setItem(i, CODE_VIEW_COLUMN_BREAKPOINT, bp_item);
347     setItem(i, CODE_VIEW_COLUMN_ADDRESS, addr_item);
348     setItem(i, CODE_VIEW_COLUMN_INSTRUCTION, ins_item);
349     setItem(i, CODE_VIEW_COLUMN_PARAMETERS, param_item);
350     setItem(i, CODE_VIEW_COLUMN_DESCRIPTION, description_item);
351     setItem(i, CODE_VIEW_COLUMN_BRANCH_ARROWS, branch_item);
352 
353     if (addr == GetAddress())
354     {
355       selectRow(addr_item->row());
356     }
357   }
358 
359   CalculateBranchIndentation();
360 
361   g_symbolDB.FillInCallers();
362 
363   repaint();
364   m_updating = false;
365 }
366 
CalculateBranchIndentation()367 void CodeViewWidget::CalculateBranchIndentation()
368 {
369   const size_t rows = rowCount();
370   const size_t columns = m_branches.size();
371   if (rows < 1 || columns < 1)
372     return;
373 
374   // process in order of how much vertical space the drawn arrow would take up
375   // so shorter arrows go further to the left
376   const auto priority = [](const CodeViewBranch& b) {
377     return b.is_link ? 0 : (std::max(b.src_addr, b.dst_addr) - std::min(b.src_addr, b.dst_addr));
378   };
379   std::stable_sort(m_branches.begin(), m_branches.end(),
380                    [&priority](const CodeViewBranch& lhs, const CodeViewBranch& rhs) {
381                      return priority(lhs) < priority(rhs);
382                    });
383 
384   // build a 2D lookup table representing the columns and rows the arrow could be drawn in
385   // and try to place all branch arrows in it as far left as possible
386   std::vector<bool> arrow_space_used(columns * rows, false);
387   const auto index = [&](u32 column, u32 row) { return column * rows + row; };
388   const u32 first_visible_addr = AddressForRow(0);
389   const u32 last_visible_addr = AddressForRow(static_cast<int>(rows - 1));
390   for (CodeViewBranch& branch : m_branches)
391   {
392     const u32 arrow_src_addr = branch.src_addr;
393     const u32 arrow_dst_addr = branch.is_link ? branch.src_addr : branch.dst_addr;
394     const u32 arrow_addr_lower = std::min(arrow_src_addr, arrow_dst_addr);
395     const u32 arrow_addr_higher = std::max(arrow_src_addr, arrow_dst_addr);
396     const bool is_visible =
397         last_visible_addr >= arrow_addr_lower || first_visible_addr <= arrow_addr_higher;
398     if (!is_visible)
399       continue;
400 
401     const u32 arrow_first_visible_addr =
402         std::clamp(arrow_addr_lower, first_visible_addr, last_visible_addr);
403     const u32 arrow_last_visible_addr =
404         std::clamp(arrow_addr_higher, first_visible_addr, last_visible_addr);
405     const u32 arrow_first_visible_row = (arrow_first_visible_addr - first_visible_addr) / 4;
406     const u32 arrow_last_visible_row = (arrow_last_visible_addr - first_visible_addr) / 4;
407 
408     const auto free_column = [&]() -> std::optional<u32> {
409       for (u32 column = 0; column < columns; ++column)
410       {
411         const bool column_is_free = [&] {
412           for (u32 row = arrow_first_visible_row; row <= arrow_last_visible_row; ++row)
413           {
414             if (arrow_space_used[index(column, row)])
415               return false;
416           }
417           return true;
418         }();
419         if (column_is_free)
420           return column;
421       }
422       return std::nullopt;
423     }();
424 
425     if (!free_column)
426       continue;
427 
428     branch.indentation = *free_column;
429     for (u32 row = arrow_first_visible_row; row <= arrow_last_visible_row; ++row)
430       arrow_space_used[index(*free_column, row)] = true;
431   }
432 }
433 
GetAddress() const434 u32 CodeViewWidget::GetAddress() const
435 {
436   return m_address;
437 }
438 
SetAddress(u32 address,SetAddressUpdate update)439 void CodeViewWidget::SetAddress(u32 address, SetAddressUpdate update)
440 {
441   if (m_address == address)
442     return;
443 
444   m_address = address;
445   if (update == SetAddressUpdate::WithUpdate)
446     Update();
447 }
448 
ReplaceAddress(u32 address,ReplaceWith replace)449 void CodeViewWidget::ReplaceAddress(u32 address, ReplaceWith replace)
450 {
451   PowerPC::debug_interface.UnsetPatch(address);
452   PowerPC::debug_interface.SetPatch(address, replace == ReplaceWith::BLR ? 0x4e800020 : 0x60000000);
453   Update();
454 }
455 
OnContextMenu()456 void CodeViewWidget::OnContextMenu()
457 {
458   QMenu* menu = new QMenu(this);
459 
460   bool running = Core::GetState() != Core::State::Uninitialized;
461 
462   const u32 addr = GetContextAddress();
463 
464   bool has_symbol = g_symbolDB.GetSymbolFromAddr(addr);
465 
466   auto* follow_branch_action =
467       menu->addAction(tr("Follow &branch"), this, &CodeViewWidget::OnFollowBranch);
468 
469   menu->addSeparator();
470 
471   menu->addAction(tr("&Copy address"), this, &CodeViewWidget::OnCopyAddress);
472   auto* copy_address_action =
473       menu->addAction(tr("Copy &function"), this, &CodeViewWidget::OnCopyFunction);
474   auto* copy_line_action =
475       menu->addAction(tr("Copy code &line"), this, &CodeViewWidget::OnCopyCode);
476   auto* copy_hex_action = menu->addAction(tr("Copy &hex"), this, &CodeViewWidget::OnCopyHex);
477 
478   menu->addAction(tr("Show in &memory"), this, &CodeViewWidget::OnShowInMemory);
479 
480   menu->addSeparator();
481 
482   auto* symbol_rename_action =
483       menu->addAction(tr("&Rename symbol"), this, &CodeViewWidget::OnRenameSymbol);
484   auto* symbol_size_action =
485       menu->addAction(tr("Set symbol &size"), this, &CodeViewWidget::OnSetSymbolSize);
486   auto* symbol_end_action =
487       menu->addAction(tr("Set symbol &end address"), this, &CodeViewWidget::OnSetSymbolEndAddress);
488   menu->addSeparator();
489 
490   menu->addAction(tr("Run &To Here"), this, &CodeViewWidget::OnRunToHere);
491   auto* function_action =
492       menu->addAction(tr("&Add function"), this, &CodeViewWidget::OnAddFunction);
493   auto* ppc_action = menu->addAction(tr("PPC vs Host"), this, &CodeViewWidget::OnPPCComparison);
494   auto* insert_blr_action = menu->addAction(tr("&Insert blr"), this, &CodeViewWidget::OnInsertBLR);
495   auto* insert_nop_action = menu->addAction(tr("Insert &nop"), this, &CodeViewWidget::OnInsertNOP);
496   auto* replace_action =
497       menu->addAction(tr("Re&place instruction"), this, &CodeViewWidget::OnReplaceInstruction);
498   auto* restore_action =
499       menu->addAction(tr("Restore instruction"), this, &CodeViewWidget::OnRestoreInstruction);
500 
501   follow_branch_action->setEnabled(running && GetBranchFromAddress(addr));
502 
503   for (auto* action : {copy_address_action, copy_line_action, copy_hex_action, function_action,
504                        ppc_action, insert_blr_action, insert_nop_action, replace_action})
505     action->setEnabled(running);
506 
507   for (auto* action : {symbol_rename_action, symbol_size_action, symbol_end_action})
508     action->setEnabled(has_symbol);
509 
510   restore_action->setEnabled(running && PowerPC::debug_interface.HasEnabledPatch(addr));
511 
512   menu->exec(QCursor::pos());
513   Update();
514 }
515 
OnCopyAddress()516 void CodeViewWidget::OnCopyAddress()
517 {
518   const u32 addr = GetContextAddress();
519 
520   QApplication::clipboard()->setText(QStringLiteral("%1").arg(addr, 8, 16, QLatin1Char('0')));
521 }
522 
OnShowInMemory()523 void CodeViewWidget::OnShowInMemory()
524 {
525   emit ShowMemory(GetContextAddress());
526 }
527 
OnCopyCode()528 void CodeViewWidget::OnCopyCode()
529 {
530   const u32 addr = GetContextAddress();
531 
532   QApplication::clipboard()->setText(
533       QString::fromStdString(PowerPC::debug_interface.Disassemble(addr)));
534 }
535 
OnCopyFunction()536 void CodeViewWidget::OnCopyFunction()
537 {
538   const u32 address = GetContextAddress();
539 
540   const Common::Symbol* symbol = g_symbolDB.GetSymbolFromAddr(address);
541   if (!symbol)
542     return;
543 
544   std::string text = symbol->name + "\r\n";
545   // we got a function
546   const u32 start = symbol->address;
547   const u32 end = start + symbol->size;
548   for (u32 addr = start; addr != end; addr += 4)
549   {
550     const std::string disasm = PowerPC::debug_interface.Disassemble(addr);
551     text += StringFromFormat("%08x: ", addr) + disasm + "\r\n";
552   }
553 
554   QApplication::clipboard()->setText(QString::fromStdString(text));
555 }
556 
OnCopyHex()557 void CodeViewWidget::OnCopyHex()
558 {
559   const u32 addr = GetContextAddress();
560   const u32 instruction = PowerPC::debug_interface.ReadInstruction(addr);
561 
562   QApplication::clipboard()->setText(
563       QStringLiteral("%1").arg(instruction, 8, 16, QLatin1Char('0')));
564 }
565 
OnRunToHere()566 void CodeViewWidget::OnRunToHere()
567 {
568   const u32 addr = GetContextAddress();
569 
570   PowerPC::debug_interface.SetBreakpoint(addr);
571   PowerPC::debug_interface.RunToBreakpoint();
572   Update();
573 }
574 
OnPPCComparison()575 void CodeViewWidget::OnPPCComparison()
576 {
577   const u32 addr = GetContextAddress();
578 
579   emit RequestPPCComparison(addr);
580 }
581 
OnAddFunction()582 void CodeViewWidget::OnAddFunction()
583 {
584   const u32 addr = GetContextAddress();
585 
586   g_symbolDB.AddFunction(addr);
587   emit SymbolsChanged();
588   Update();
589 }
590 
OnInsertBLR()591 void CodeViewWidget::OnInsertBLR()
592 {
593   const u32 addr = GetContextAddress();
594 
595   ReplaceAddress(addr, ReplaceWith::BLR);
596 }
597 
OnInsertNOP()598 void CodeViewWidget::OnInsertNOP()
599 {
600   const u32 addr = GetContextAddress();
601 
602   ReplaceAddress(addr, ReplaceWith::NOP);
603 }
604 
OnFollowBranch()605 void CodeViewWidget::OnFollowBranch()
606 {
607   const u32 addr = GetContextAddress();
608 
609   u32 branch_addr = GetBranchFromAddress(addr);
610 
611   if (!branch_addr)
612     return;
613 
614   SetAddress(branch_addr, SetAddressUpdate::WithUpdate);
615 }
616 
OnRenameSymbol()617 void CodeViewWidget::OnRenameSymbol()
618 {
619   const u32 addr = GetContextAddress();
620 
621   Common::Symbol* symbol = g_symbolDB.GetSymbolFromAddr(addr);
622 
623   if (!symbol)
624     return;
625 
626   bool good;
627   QString name =
628       QInputDialog::getText(this, tr("Rename symbol"), tr("Symbol name:"), QLineEdit::Normal,
629                             QString::fromStdString(symbol->name), &good);
630 
631   if (good && !name.isEmpty())
632   {
633     symbol->Rename(name.toStdString());
634     emit SymbolsChanged();
635     Update();
636   }
637 }
638 
OnSelectionChanged()639 void CodeViewWidget::OnSelectionChanged()
640 {
641   if (m_address == PowerPC::ppcState.pc)
642   {
643     setStyleSheet(
644         QStringLiteral("QTableView::item:selected {background-color: #00FF00; color: #000000;}"));
645   }
646   else if (!styleSheet().isEmpty())
647   {
648     setStyleSheet(QString{});
649   }
650 }
651 
OnSetSymbolSize()652 void CodeViewWidget::OnSetSymbolSize()
653 {
654   const u32 addr = GetContextAddress();
655 
656   Common::Symbol* symbol = g_symbolDB.GetSymbolFromAddr(addr);
657 
658   if (!symbol)
659     return;
660 
661   bool good;
662   int size =
663       QInputDialog::getInt(this, tr("Rename symbol"),
664                            tr("Set symbol size (%1):").arg(QString::fromStdString(symbol->name)),
665                            symbol->size, 1, 0xFFFF, 1, &good);
666 
667   if (!good)
668     return;
669 
670   PPCAnalyst::ReanalyzeFunction(symbol->address, *symbol, size);
671   emit SymbolsChanged();
672   Update();
673 }
674 
OnSetSymbolEndAddress()675 void CodeViewWidget::OnSetSymbolEndAddress()
676 {
677   const u32 addr = GetContextAddress();
678 
679   Common::Symbol* symbol = g_symbolDB.GetSymbolFromAddr(addr);
680 
681   if (!symbol)
682     return;
683 
684   bool good;
685   QString name = QInputDialog::getText(
686       this, tr("Set symbol end address"),
687       tr("Symbol (%1) end address:").arg(QString::fromStdString(symbol->name)), QLineEdit::Normal,
688       QStringLiteral("%1").arg(addr + symbol->size, 8, 16, QLatin1Char('0')), &good);
689 
690   u32 address = name.toUInt(&good, 16);
691 
692   if (!good)
693     return;
694 
695   PPCAnalyst::ReanalyzeFunction(symbol->address, *symbol, address - symbol->address);
696   emit SymbolsChanged();
697   Update();
698 }
699 
OnReplaceInstruction()700 void CodeViewWidget::OnReplaceInstruction()
701 {
702   const u32 addr = GetContextAddress();
703 
704   if (!PowerPC::HostIsInstructionRAMAddress(addr))
705     return;
706 
707   const PowerPC::TryReadInstResult read_result = PowerPC::TryReadInstruction(addr);
708   if (!read_result.valid)
709     return;
710 
711   PatchInstructionDialog dialog(this, addr, PowerPC::debug_interface.ReadInstruction(addr));
712 
713   if (dialog.exec() == QDialog::Accepted)
714   {
715     PowerPC::debug_interface.UnsetPatch(addr);
716     PowerPC::debug_interface.SetPatch(addr, dialog.GetCode());
717     Update();
718   }
719 }
720 
OnRestoreInstruction()721 void CodeViewWidget::OnRestoreInstruction()
722 {
723   const u32 addr = GetContextAddress();
724 
725   PowerPC::debug_interface.UnsetPatch(addr);
726   Update();
727 }
728 
resizeEvent(QResizeEvent *)729 void CodeViewWidget::resizeEvent(QResizeEvent*)
730 {
731   Update();
732 }
733 
keyPressEvent(QKeyEvent * event)734 void CodeViewWidget::keyPressEvent(QKeyEvent* event)
735 {
736   switch (event->key())
737   {
738   case Qt::Key_Up:
739     m_address -= sizeof(u32);
740     Update();
741     return;
742   case Qt::Key_Down:
743     m_address += sizeof(u32);
744     Update();
745     return;
746   case Qt::Key_PageUp:
747     m_address -= rowCount() * sizeof(u32);
748     Update();
749     return;
750   case Qt::Key_PageDown:
751     m_address += rowCount() * sizeof(u32);
752     Update();
753     return;
754   default:
755     QWidget::keyPressEvent(event);
756     break;
757   }
758 }
759 
wheelEvent(QWheelEvent * event)760 void CodeViewWidget::wheelEvent(QWheelEvent* event)
761 {
762   auto delta =
763       -static_cast<int>(std::round((event->angleDelta().y() / (SCROLL_FRACTION_DEGREES * 8))));
764 
765   if (delta == 0)
766     return;
767 
768   m_address += delta * sizeof(u32);
769   Update();
770 }
771 
mousePressEvent(QMouseEvent * event)772 void CodeViewWidget::mousePressEvent(QMouseEvent* event)
773 {
774   auto* item = itemAt(event->pos());
775   if (item == nullptr)
776     return;
777 
778   const u32 addr = item->data(Qt::UserRole).toUInt();
779 
780   m_context_address = addr;
781 
782   switch (event->button())
783   {
784   case Qt::LeftButton:
785     if (column(item) == CODE_VIEW_COLUMN_BREAKPOINT)
786       ToggleBreakpoint();
787     else
788       SetAddress(addr, SetAddressUpdate::WithUpdate);
789 
790     Update();
791     break;
792   default:
793     break;
794   }
795 }
796 
showEvent(QShowEvent * event)797 void CodeViewWidget::showEvent(QShowEvent* event)
798 {
799   Update();
800 }
801 
ToggleBreakpoint()802 void CodeViewWidget::ToggleBreakpoint()
803 {
804   if (PowerPC::debug_interface.IsBreakpoint(GetContextAddress()))
805     PowerPC::breakpoints.Remove(GetContextAddress());
806   else
807     PowerPC::breakpoints.Add(GetContextAddress());
808 
809   emit BreakpointsChanged();
810   Update();
811 }
812 
AddBreakpoint()813 void CodeViewWidget::AddBreakpoint()
814 {
815   PowerPC::breakpoints.Add(GetContextAddress());
816 
817   emit BreakpointsChanged();
818   Update();
819 }
820 
GetContextAddress() const821 u32 CodeViewWidget::GetContextAddress() const
822 {
823   return m_context_address;
824 }
825