CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
CoCalc provides the best real-time collaborative environment for Jupyter Notebooks, LaTeX documents, and SageMath, scalable from individual users to large groups and classes!
Path: blob/master/Windows/Debugger/CtrlDisAsmView.cpp
Views: 1401
#include "Windows/resource.h"1#include "Core/MemMap.h"2#include "Core/MIPS/JitCommon/JitCommon.h"3#include "Windows/W32Util/ContextMenu.h"4#include "Windows/W32Util/Misc.h"5#include "Windows/W32Util/ShellUtil.h"6#include "Windows/MainWindow.h"7#include "Windows/InputBox.h"89#include "Core/MIPS/MIPSAsm.h"10#include "Core/MIPS/MIPSAnalyst.h"11#include "Core/MIPS/MIPSTables.h"12#include "Core/Config.h"13#include "Core/Debugger/SymbolMap.h"14#include "Core/Reporting.h"15#include "Common/StringUtils.h"16#include "Windows/Debugger/CtrlDisAsmView.h"17#include "Windows/Debugger/Debugger_MemoryDlg.h"18#include "Windows/Debugger/DebuggerShared.h"19#include "Windows/Debugger/BreakpointWindow.h"20#include "Windows/Debugger/EditSymbolsWindow.h"21#include "Windows/main.h"2223#include "Common/CommonWindows.h"24#include "Common/Data/Encoding/Utf8.h"25#include "ext/xxhash.h"26#include "Common/System/Display.h"2728#include <CommDlg.h>29#include <tchar.h>30#include <set>3132TCHAR CtrlDisAsmView::szClassName[] = _T("CtrlDisAsmView");3334static constexpr UINT_PTR IDT_REDRAW = 0xC0DE0001;35static constexpr UINT REDRAW_DELAY = 1000 / 60;3637void CtrlDisAsmView::init()38{39WNDCLASSEX wc;4041wc.cbSize = sizeof(wc);42wc.lpszClassName = szClassName;43wc.hInstance = GetModuleHandle(0);44wc.lpfnWndProc = CtrlDisAsmView::wndProc;45wc.hCursor = LoadCursor (NULL, IDC_ARROW);46wc.hIcon = 0;47wc.lpszMenuName = 0;48wc.hbrBackground = (HBRUSH)GetSysColorBrush(COLOR_WINDOW);49wc.style = 0;50wc.cbClsExtra = 0;51wc.cbWndExtra = sizeof( CtrlDisAsmView * );52wc.hIconSm = 0;5354RegisterClassEx(&wc);55}5657void CtrlDisAsmView::deinit()58{59//UnregisterClass(szClassName, hInst)60}6162void CtrlDisAsmView::scanFunctions()63{64manager.analyze(windowStart,manager.getNthNextAddress(windowStart,visibleRows)-windowStart);65}6667LRESULT CALLBACK CtrlDisAsmView::wndProc(HWND hwnd, UINT msg, WPARAM wParam, LPARAM lParam)68{69CtrlDisAsmView *ccp = CtrlDisAsmView::getFrom(hwnd);70static bool lmbDown=false,rmbDown=false;71switch(msg)72{73case WM_NCCREATE:74// Allocate a new CustCtrl structure for this window.75ccp = new CtrlDisAsmView(hwnd);7677// Continue with window creation.78return ccp != NULL;7980// Clean up when the window is destroyed.81case WM_NCDESTROY:82delete ccp;83break;84case WM_SETFONT:85break;86case WM_SIZE:87ccp->redraw();88break;89case WM_PAINT:90ccp->onPaint(wParam,lParam);91break;92case WM_VSCROLL:93ccp->onVScroll(wParam,lParam);94break;95case WM_MOUSEWHEEL:96ccp->dontRedraw = false;97if (GET_WHEEL_DELTA_WPARAM(wParam) > 0)98{99ccp->scrollWindow(-3);100} else if (GET_WHEEL_DELTA_WPARAM(wParam) < 0) {101ccp->scrollWindow(3);102}103break;104case WM_ERASEBKGND:105return FALSE;106case WM_KEYDOWN:107ccp->onKeyDown(wParam,lParam);108return 0;109case WM_CHAR:110ccp->onChar(wParam,lParam);111return 0;112case WM_SYSKEYDOWN:113ccp->onKeyDown(wParam,lParam);114return 0; // return a value so that windows doesn't execute the standard syskey action115case WM_KEYUP:116ccp->onKeyUp(wParam,lParam);117return 0;118case WM_LBUTTONDOWN: lmbDown=true; ccp->onMouseDown(wParam,lParam,1); break;119case WM_RBUTTONDOWN: rmbDown=true; ccp->onMouseDown(wParam,lParam,2); break;120case WM_MOUSEMOVE: ccp->onMouseMove(wParam,lParam,(lmbDown?1:0) | (rmbDown?2:0)); break;121case WM_LBUTTONUP: lmbDown=false; ccp->onMouseUp(wParam,lParam,1); break;122case WM_RBUTTONUP: rmbDown=false; ccp->onMouseUp(wParam,lParam,2); break;123case WM_SETFOCUS:124SetFocus(hwnd);125ccp->hasFocus=true;126ccp->redraw();127break;128case WM_KILLFOCUS:129ccp->hasFocus=false;130lmbDown = false;131rmbDown = false;132ccp->redraw();133break;134case WM_GETDLGCODE:135if (lParam && ((MSG*)lParam)->message == WM_KEYDOWN)136{137switch (wParam)138{139case VK_TAB:140return DLGC_WANTMESSAGE;141default:142return DLGC_WANTCHARS|DLGC_WANTARROWS;143}144}145return DLGC_WANTCHARS|DLGC_WANTARROWS;146147case WM_TIMER:148if (wParam == IDT_REDRAW) {149InvalidateRect(hwnd, nullptr, FALSE);150UpdateWindow(hwnd);151ccp->redrawScheduled_ = false;152KillTimer(hwnd, wParam);153}154break;155156default:157break;158}159160return DefWindowProc(hwnd, msg, wParam, lParam);161}162163164CtrlDisAsmView *CtrlDisAsmView::getFrom(HWND hwnd)165{166return (CtrlDisAsmView *)GetWindowLongPtr(hwnd, GWLP_USERDATA);167}168169CtrlDisAsmView::CtrlDisAsmView(HWND _wnd)170{171wnd=_wnd;172SetWindowLongPtr(wnd, GWLP_USERDATA, (LONG_PTR)this);173SetWindowLong(wnd, GWL_STYLE, GetWindowLong(wnd,GWL_STYLE) | WS_VSCROLL);174SetScrollRange(wnd, SB_VERT, -1, 1, TRUE);175176const float fontScale = 1.0f / g_display.dpi_scale_real_y;177charWidth = g_Config.iFontWidth * fontScale;178rowHeight = (g_Config.iFontHeight + 2) * fontScale;179int scaledFontHeight = g_Config.iFontHeight * fontScale;180font = CreateFont(scaledFontHeight, charWidth, 0, 0,181FW_DONTCARE, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH,182L"Lucida Console");183boldfont = CreateFont(scaledFontHeight, charWidth, 0, 0,184FW_DEMIBOLD, FALSE, FALSE, FALSE, DEFAULT_CHARSET, OUT_DEFAULT_PRECIS, CLIP_DEFAULT_PRECIS, DEFAULT_QUALITY, DEFAULT_PITCH,185L"Lucida Console");186187curAddress = 0;188showHex = false;189hasFocus = false;190dontRedraw = false;191keyTaken = false;192193matchAddress = -1;194searching = false;195searchQuery.clear();196windowStart = curAddress;197whiteBackground = false;198displaySymbols = true;199calculatePixelPositions();200}201202203CtrlDisAsmView::~CtrlDisAsmView()204{205DeleteObject(font);206DeleteObject(boldfont);207manager.clear();208}209210static COLORREF scaleColor(COLORREF color, float factor)211{212unsigned char r = color & 0xFF;213unsigned char g = (color >> 8) & 0xFF;214unsigned char b = (color >> 16) & 0xFF;215216r = std::min(255, std::max((int)(r * factor), 0));217g = std::min(255, std::max((int)(g * factor), 0));218b = std::min(255, std::max((int)(b * factor), 0));219220return (color & 0xFF000000) | (b << 16) | (g << 8) | r;221}222223bool CtrlDisAsmView::getDisasmAddressText(u32 address, char* dest, bool abbreviateLabels, bool showData)224{225if (!PSP_IsInited())226return false;227228if (displaySymbols)229{230const std::string addressSymbol = g_symbolMap->GetLabelString(address);231if (!addressSymbol.empty())232{233for (int k = 0; addressSymbol[k] != 0; k++)234{235// abbreviate long names236if (abbreviateLabels && k == 16 && addressSymbol[k+1] != 0)237{238*dest++ = '+';239break;240}241*dest++ = addressSymbol[k];242}243*dest++ = ':';244*dest = 0;245return true;246} else {247sprintf(dest," %08X",address);248return false;249}250} else {251if (showData) {252u32 encoding = Memory::IsValidAddress(address) ? Memory::Read_Instruction(address, true).encoding : 0;253sprintf(dest, "%08X %08X", address, encoding);254} else {255sprintf(dest, "%08X", address);256}257return false;258}259}260261std::string trimString(std::string input)262{263size_t pos = input.find_first_not_of(" \t");264if (pos != 0 && pos != std::string::npos)265{266input = input.erase(0,pos);267}268269pos = input.find_last_not_of(" \t");270if (pos != std::string::npos)271{272size_t size = input.length()-pos-1;273input = input.erase(pos+1,size);274}275276return input;277}278279void CtrlDisAsmView::assembleOpcode(u32 address, const std::string &defaultText)280{281auto memLock = Memory::Lock();282if (!Core_IsStepping()) {283MessageBox(wnd,L"Cannot change code while the core is running!",L"Error",MB_OK);284return;285}286std::string op;287bool result = InputBox_GetString(MainWindow::GetHInstance(), wnd, L"Assemble opcode", defaultText, op, InputBoxFlags::Default);288if (!result) {289return;290}291292// check if it changes registers first293auto separator = op.find('=');294if (separator != std::string::npos)295{296std::string registerName = trimString(op.substr(0,separator));297std::string expression = trimString(op.substr(separator+1));298299u32 value;300if (parseExpression(expression.c_str(),debugger,value) == true)301{302for (int cat = 0; cat < debugger->GetNumCategories(); cat++)303{304for (int reg = 0; reg < debugger->GetNumRegsInCategory(cat); reg++)305{306if (strcasecmp(debugger->GetRegName(cat,reg).c_str(), registerName.c_str()) == 0)307{308debugger->SetRegValue(cat,reg,value);309Reporting::NotifyDebugger();310SendMessage(GetParent(wnd),WM_DEB_UPDATE,0,0);311return;312}313}314}315}316317// try to assemble the input if it failed318}319320result = MIPSAsm::MipsAssembleOpcode(op.c_str(), debugger, address);321Reporting::NotifyDebugger();322if (result == true)323{324scanFunctions();325326if (address == curAddress)327gotoAddr(manager.getNthNextAddress(curAddress,1));328329redraw();330} else {331std::wstring error = ConvertUTF8ToWString(MIPSAsm::GetAssembleError());332MessageBox(wnd,error.c_str(),L"Error",MB_OK);333}334}335336void CtrlDisAsmView::drawBranchLine(HDC hdc, std::map<u32,int> &addressPositions, const BranchLine &line) {337HPEN pen;338u32 windowEnd = manager.getNthNextAddress(windowStart,visibleRows);339340int topY;341int bottomY;342if (line.first < windowStart)343{344topY = -1;345} else if (line.first >= windowEnd)346{347topY = rect.bottom+1;348} else {349topY = addressPositions[line.first] + rowHeight/2;350}351352if (line.second < windowStart)353{354bottomY = -1;355} else if (line.second >= windowEnd)356{357bottomY = rect.bottom+1;358} else {359bottomY = addressPositions[line.second] + rowHeight/2;360}361362if ((topY < 0 && bottomY < 0) || (topY > rect.bottom && bottomY > rect.bottom))363{364return;365}366367// highlight line in a different color if it affects the currently selected opcode368if (line.first == curAddress || line.second == curAddress)369{370pen = CreatePen(0,0,0x257AFA);371} else {372pen = CreatePen(0,0,0xFF3020);373}374375HPEN oldPen = (HPEN) SelectObject(hdc,pen);376int x = pixelPositions.arrowsStart+line.laneIndex*8;377378if (topY < 0) // first is not visible, but second is379{380MoveToEx(hdc,x-2,bottomY,0);381LineTo(hdc,x+2,bottomY);382LineTo(hdc,x+2,0);383384if (line.type == LINE_DOWN)385{386MoveToEx(hdc,x,bottomY-4,0);387LineTo(hdc,x-4,bottomY);388LineTo(hdc,x+1,bottomY+5);389}390} else if (bottomY > rect.bottom) // second is not visible, but first is391{392MoveToEx(hdc,x-2,topY,0);393LineTo(hdc,x+2,topY);394LineTo(hdc,x+2,rect.bottom);395396if (line.type == LINE_UP)397{398MoveToEx(hdc,x,topY-4,0);399LineTo(hdc,x-4,topY);400LineTo(hdc,x+1,topY+5);401}402} else { // both are visible403if (line.type == LINE_UP)404{405MoveToEx(hdc,x-2,bottomY,0);406LineTo(hdc,x+2,bottomY);407LineTo(hdc,x+2,topY);408LineTo(hdc,x-4,topY);409410MoveToEx(hdc,x,topY-4,0);411LineTo(hdc,x-4,topY);412LineTo(hdc,x+1,topY+5);413} else {414MoveToEx(hdc,x-2,topY,0);415LineTo(hdc,x+2,topY);416LineTo(hdc,x+2,bottomY);417LineTo(hdc,x-4,bottomY);418419MoveToEx(hdc,x,bottomY-4,0);420LineTo(hdc,x-4,bottomY);421LineTo(hdc,x+1,bottomY+5);422}423}424425SelectObject(hdc,oldPen);426DeleteObject(pen);427}428429std::set<std::string> CtrlDisAsmView::getSelectedLineArguments() {430std::set<std::string> args;431432DisassemblyLineInfo line;433for (u32 addr = selectRangeStart; addr < selectRangeEnd; addr += 4) {434manager.getLine(addr, displaySymbols, line);435size_t p = 0, nextp = line.params.find(',');436while (nextp != line.params.npos) {437args.emplace(line.params.substr(p, nextp - p));438p = nextp + 1;439nextp = line.params.find(',', p);440}441if (p < line.params.size()) {442args.emplace(line.params.substr(p));443}444}445446return args;447}448449void CtrlDisAsmView::drawArguments(HDC hdc, const DisassemblyLineInfo &line, int x, int y, int textColor, const std::set<std::string> ¤tArguments) {450if (line.params.empty()) {451return;452}453// Don't highlight the selected lines.454if (isInInterval(selectRangeStart, selectRangeEnd - selectRangeStart, line.info.opcodeAddress)) {455TextOutA(hdc, x, y, line.params.c_str(), (int)line.params.size());456return;457}458459int highlightedColor = 0xaabb00;460if (textColor == 0x0000ff) {461highlightedColor = 0xaabb77;462}463464UINT prevAlign = SetTextAlign(hdc, TA_UPDATECP);465MoveToEx(hdc, x, y, NULL);466467size_t p = 0, nextp = line.params.find(',');468while (nextp != line.params.npos) {469const std::string arg = line.params.substr(p, nextp - p);470if (currentArguments.find(arg) != currentArguments.end() && textColor != 0xffffff) {471SetTextColor(hdc, highlightedColor);472}473TextOutA(hdc, 0, 0, arg.c_str(), (int)arg.size());474SetTextColor(hdc,textColor);475p = nextp + 1;476nextp = line.params.find(',', p);477TextOutA(hdc, 0, 0, ",", 1);478}479if (p < line.params.size()) {480const std::string arg = line.params.substr(p);481if (currentArguments.find(arg) != currentArguments.end() && textColor != 0xffffff) {482SetTextColor(hdc, highlightedColor);483}484TextOutA(hdc, 0, 0, arg.c_str(), (int)arg.size());485SetTextColor(hdc,textColor);486}487488SetTextAlign(hdc, prevAlign);489}490491void CtrlDisAsmView::onPaint(WPARAM wParam, LPARAM lParam)492{493auto memLock = Memory::Lock();494if (!debugger->isAlive()) return;495496PAINTSTRUCT ps;497HDC actualHdc = BeginPaint(wnd, &ps);498HDC hdc = CreateCompatibleDC(actualHdc);499HBITMAP hBM = CreateCompatibleBitmap(actualHdc, rect.right-rect.left, rect.bottom-rect.top);500SelectObject(hdc, hBM);501502SetBkMode(hdc, TRANSPARENT);503504HPEN nullPen=CreatePen(0,0,0xffffff);505HBRUSH nullBrush=CreateSolidBrush(0xffffff);506HBRUSH currentBrush=CreateSolidBrush(0xffefe8);507508HPEN oldPen=(HPEN)SelectObject(hdc,nullPen);509HBRUSH oldBrush=(HBRUSH)SelectObject(hdc,nullBrush);510HFONT oldFont = (HFONT)SelectObject(hdc,(HGDIOBJ)font);511HICON breakPoint = (HICON)LoadIcon(GetModuleHandle(0),(LPCWSTR)IDI_STOP);512HICON breakPointDisable = (HICON)LoadIcon(GetModuleHandle(0),(LPCWSTR)IDI_STOPDISABLE);513514unsigned int address = windowStart;515std::map<u32,int> addressPositions;516517const std::set<std::string> currentArguments = getSelectedLineArguments();518DisassemblyLineInfo line;519for (int i = 0; i < visibleRows; i++)520{521manager.getLine(address,displaySymbols,line);522523int rowY1 = rowHeight*i;524int rowY2 = rowHeight*(i+1);525526addressPositions[address] = rowY1;527528// draw background529COLORREF backgroundColor = whiteBackground ? 0xFFFFFF : debugger->getColor(address);530COLORREF textColor = 0x000000;531532if (isInInterval(address,line.totalSize,debugger->getPC()))533{534backgroundColor = scaleColor(backgroundColor,1.05f);535}536537if (address >= selectRangeStart && address < selectRangeEnd && searching == false)538{539if (hasFocus)540{541backgroundColor = address == curAddress ? 0xFF8822 : 0xFF9933;542textColor = 0xFFFFFF;543} else {544backgroundColor = 0xC0C0C0;545}546}547548HBRUSH backgroundBrush = CreateSolidBrush(backgroundColor);549HPEN backgroundPen = CreatePen(0,0,backgroundColor);550SelectObject(hdc,backgroundBrush);551SelectObject(hdc,backgroundPen);552Rectangle(hdc,0,rowY1,rect.right,rowY1+rowHeight);553554SelectObject(hdc,currentBrush);555SelectObject(hdc,nullPen);556557DeleteObject(backgroundBrush);558DeleteObject(backgroundPen);559560// display address/symbol561bool enabled;562if (CBreakPoints::IsAddressBreakPoint(address,&enabled))563{564if (enabled) textColor = 0x0000FF;565int yOffset = std::max(-1, (rowHeight - 14 + 1) / 2);566if (!enabled) yOffset++;567DrawIconEx(hdc,2,rowY1+1+yOffset,enabled ? breakPoint : breakPointDisable,32,32,0,0,DI_NORMAL);568}569SetTextColor(hdc,textColor);570571char addressText[64];572getDisasmAddressText(address,addressText,true,line.type == DISTYPE_OPCODE);573TextOutA(hdc,pixelPositions.addressStart,rowY1+2,addressText,(int)strlen(addressText));574575if (isInInterval(address,line.totalSize,debugger->getPC()))576{577TextOut(hdc,pixelPositions.opcodeStart-8,rowY1,L"\x25A0",1);578}579580// display whether the condition of a branch is met581if (line.info.isConditional && address == debugger->getPC())582{583line.params += line.info.conditionMet ? " ; true" : " ; false";584}585586drawArguments(hdc, line, pixelPositions.argumentsStart, rowY1 + 2, textColor, currentArguments);587588SelectObject(hdc,boldfont);589TextOutA(hdc,pixelPositions.opcodeStart,rowY1+2,line.name.c_str(),(int)line.name.size());590SelectObject(hdc,font);591592address += line.totalSize;593}594595std::vector<BranchLine> branchLines = manager.getBranchLines(windowStart,address-windowStart);596for (size_t i = 0; i < branchLines.size(); i++)597{598drawBranchLine(hdc,addressPositions,branchLines[i]);599}600601SelectObject(hdc,oldFont);602SelectObject(hdc,oldPen);603SelectObject(hdc,oldBrush);604605// copy bitmap to the actual hdc606BitBlt(actualHdc, 0, 0, rect.right, rect.bottom, hdc, 0, 0, SRCCOPY);607DeleteObject(hBM);608DeleteDC(hdc);609610DeleteObject(nullPen);611612DeleteObject(nullBrush);613DeleteObject(currentBrush);614615DestroyIcon(breakPoint);616DestroyIcon(breakPointDisable);617618EndPaint(wnd, &ps);619}620621622623void CtrlDisAsmView::onVScroll(WPARAM wParam, LPARAM lParam)624{625switch (wParam & 0xFFFF)626{627case SB_LINEDOWN:628windowStart = manager.getNthNextAddress(windowStart,1);629break;630case SB_LINEUP:631windowStart = manager.getNthPreviousAddress(windowStart,1);632break;633case SB_PAGEDOWN:634windowStart = manager.getNthNextAddress(windowStart,visibleRows);635break;636case SB_PAGEUP:637windowStart = manager.getNthPreviousAddress(windowStart,visibleRows);638break;639default:640return;641}642643scanFunctions();644redraw();645}646647void CtrlDisAsmView::followBranch()648{649DisassemblyLineInfo line;650manager.getLine(curAddress,true,line);651652if (line.type == DISTYPE_OPCODE || line.type == DISTYPE_MACRO)653{654if (line.info.isBranch)655{656jumpStack.push_back(curAddress);657gotoAddr(line.info.branchTarget);658} else if (line.info.hasRelevantAddress)659{660// well, not exactly a branch, but we can do something anyway661SendMessage(GetParent(wnd),WM_DEB_GOTOHEXEDIT,line.info.relevantAddress,0);662SetFocus(wnd);663}664} else if (line.type == DISTYPE_DATA)665{666// jump to the start of the current line667SendMessage(GetParent(wnd),WM_DEB_GOTOHEXEDIT,curAddress,0);668SetFocus(wnd);669}670}671672void CtrlDisAsmView::onChar(WPARAM wParam, LPARAM lParam)673{674if (keyTaken) return;675676char str[2];677str[0] = wParam;678str[1] = 0;679assembleOpcode(curAddress,str);680}681682683void CtrlDisAsmView::editBreakpoint()684{685BreakpointWindow win(wnd,debugger);686687bool exists = false;688if (CBreakPoints::IsAddressBreakPoint(curAddress))689{690auto breakpoints = CBreakPoints::GetBreakpoints();691for (size_t i = 0; i < breakpoints.size(); i++)692{693if (breakpoints[i].addr == curAddress)694{695win.loadFromBreakpoint(breakpoints[i]);696exists = true;697break;698}699}700}701702if (!exists)703win.initBreakpoint(curAddress);704705if (win.exec())706{707if (exists)708CBreakPoints::RemoveBreakPoint(curAddress);709win.addBreakpoint();710}711}712713void CtrlDisAsmView::onKeyDown(WPARAM wParam, LPARAM lParam)714{715dontRedraw = false;716u32 windowEnd = manager.getNthNextAddress(windowStart,visibleRows);717keyTaken = true;718719if (KeyDownAsync(VK_CONTROL))720{721switch (tolower(wParam & 0xFFFF))722{723case 'f':724case 's':725search(false);726break;727case 'c':728case VK_INSERT:729CopyInstructions(selectRangeStart, selectRangeEnd, CopyInstructionsMode::DISASM);730break;731case 'x':732disassembleToFile();733break;734case 'a':735assembleOpcode(curAddress,"");736break;737case 'g':738{739u32 addr;740if (executeExpressionWindow(wnd,debugger,addr) == false) return;741gotoAddr(addr);742}743break;744case 'e': // edit breakpoint745editBreakpoint();746break;747case 'd': // toogle breakpoint enabled748toggleBreakpoint(true);749break;750case VK_UP:751scrollWindow(-1);752scanFunctions();753break;754case VK_DOWN:755scrollWindow(1);756scanFunctions();757break;758case VK_NEXT:759setCurAddress(manager.getNthPreviousAddress(windowEnd,1),KeyDownAsync(VK_SHIFT));760break;761case VK_PRIOR:762setCurAddress(windowStart,KeyDownAsync(VK_SHIFT));763break;764}765} else {766switch (wParam & 0xFFFF)767{768case VK_DOWN:769setCurAddress(manager.getNthNextAddress(curAddress,1), KeyDownAsync(VK_SHIFT));770scrollAddressIntoView();771break;772case VK_UP:773setCurAddress(manager.getNthPreviousAddress(curAddress,1), KeyDownAsync(VK_SHIFT));774scrollAddressIntoView();775break;776case VK_NEXT:777if (manager.getNthNextAddress(curAddress,1) != windowEnd && curAddressIsVisible()) {778setCurAddress(manager.getNthPreviousAddress(windowEnd,1), KeyDownAsync(VK_SHIFT));779scrollAddressIntoView();780} else {781setCurAddress(manager.getNthNextAddress(windowEnd,visibleRows-1), KeyDownAsync(VK_SHIFT));782scrollAddressIntoView();783}784break;785case VK_PRIOR:786if (curAddress != windowStart && curAddressIsVisible()) {787setCurAddress(windowStart, KeyDownAsync(VK_SHIFT));788scrollAddressIntoView();789} else {790setCurAddress(manager.getNthPreviousAddress(windowStart,visibleRows), KeyDownAsync(VK_SHIFT));791scrollAddressIntoView();792}793break;794case VK_LEFT:795if (jumpStack.empty())796{797gotoPC();798} else {799u32 addr = jumpStack[jumpStack.size()-1];800jumpStack.pop_back();801gotoAddr(addr);802}803return;804case VK_RIGHT:805followBranch();806return;807case VK_TAB:808displaySymbols = !displaySymbols;809break;810case VK_SPACE:811debugger->toggleBreakpoint(curAddress);812break;813case VK_F3:814search(true);815break;816default:817keyTaken = false;818return;819}820}821redraw();822}823824void CtrlDisAsmView::onKeyUp(WPARAM wParam, LPARAM lParam)825{826827}828829void CtrlDisAsmView::scrollAddressIntoView()830{831u32 windowEnd = manager.getNthNextAddress(windowStart,visibleRows);832833if (curAddress < windowStart)834windowStart = curAddress;835else if (curAddress >= windowEnd)836windowStart = manager.getNthPreviousAddress(curAddress,visibleRows-1);837838scanFunctions();839}840841bool CtrlDisAsmView::curAddressIsVisible()842{843u32 windowEnd = manager.getNthNextAddress(windowStart,visibleRows);844return curAddress >= windowStart && curAddress < windowEnd;845}846847void CtrlDisAsmView::redraw()848{849if (dontRedraw == true) return;850851GetClientRect(wnd, &rect);852visibleRows = rect.bottom/rowHeight;853854if (!redrawScheduled_) {855SetTimer(wnd, IDT_REDRAW, REDRAW_DELAY, nullptr);856redrawScheduled_ = true;857}858}859860void CtrlDisAsmView::toggleBreakpoint(bool toggleEnabled)861{862bool enabled;863if (CBreakPoints::IsAddressBreakPoint(curAddress, &enabled)) {864if (!enabled) {865// enable disabled breakpoints866CBreakPoints::ChangeBreakPoint(curAddress, true);867} else if (!toggleEnabled && CBreakPoints::GetBreakPointCondition(curAddress) != nullptr) {868// don't just delete a breakpoint with a custom condition869int ret = MessageBox(wnd,L"This breakpoint has a custom condition.\nDo you want to remove it?",L"Confirmation",MB_YESNO);870if (ret == IDYES)871CBreakPoints::RemoveBreakPoint(curAddress);872} else if (toggleEnabled) {873// disable breakpoint874CBreakPoints::ChangeBreakPoint(curAddress, false);875} else {876// otherwise just remove breakpoint877CBreakPoints::RemoveBreakPoint(curAddress);878}879} else {880CBreakPoints::AddBreakPoint(curAddress);881}882}883884void CtrlDisAsmView::onMouseDown(WPARAM wParam, LPARAM lParam, int button)885{886dontRedraw = false;887int y = HIWORD(lParam);888889u32 newAddress = yToAddress(y);890bool extend = KeyDownAsync(VK_SHIFT);891if (button == 1)892{893if (newAddress == curAddress && hasFocus)894{895toggleBreakpoint();896}897}898else if (button == 2)899{900// Maintain the current selection if right clicking into it.901if (newAddress >= selectRangeStart && newAddress < selectRangeEnd)902extend = true;903}904setCurAddress(newAddress, extend);905906SetFocus(wnd);907redraw();908}909910void CtrlDisAsmView::CopyInstructions(u32 startAddr, u32 endAddr, CopyInstructionsMode mode) {911_assert_msg_((startAddr & 3) == 0, "readMemory() can't handle unaligned reads");912913if (mode != CopyInstructionsMode::DISASM) {914int instructionSize = debugger->getInstructionSize(0);915int count = (endAddr - startAddr) / instructionSize;916int space = count * 32;917char *temp = new char[space];918919char *p = temp, *end = temp + space;920for (u32 pos = startAddr; pos < endAddr && p < end; pos += instructionSize)921{922u32 data = mode == CopyInstructionsMode::OPCODES ? debugger->readMemory(pos) : pos;923p += snprintf(p, end - p, "%08X", data);924925// Don't leave a trailing newline.926if (pos + instructionSize < endAddr && p < end)927p += snprintf(p, end - p, "\r\n");928}929W32Util::CopyTextToClipboard(wnd, temp);930delete [] temp;931} else {932std::string disassembly = disassembleRange(startAddr,endAddr-startAddr);933W32Util::CopyTextToClipboard(wnd, disassembly.c_str());934}935}936937void CtrlDisAsmView::NopInstructions(u32 selectRangeStart, u32 selectRangeEnd) {938for (u32 addr = selectRangeStart; addr < selectRangeEnd; addr += 4) {939Memory::Write_U32(0, addr);940}941942if (currentMIPS) {943currentMIPS->InvalidateICache(selectRangeStart, selectRangeEnd - selectRangeStart);944}945}946947void CtrlDisAsmView::onMouseUp(WPARAM wParam, LPARAM lParam, int button)948{949if (button == 1)950{951int y = HIWORD(lParam);952setCurAddress(yToAddress(y), KeyDownAsync(VK_SHIFT));953redraw();954}955else if (button == 2)956{957// We don't want to let the users play with deallocated or uninitialized debugging objects958GlobalUIState state = GetUIState();959if (state != UISTATE_INGAME && state != UISTATE_PAUSEMENU) {960return;961}962963switch (TriggerContextMenu(ContextMenuID::DISASM, wnd, ContextPoint::FromEvent(lParam)))964{965case ID_DISASM_GOTOINMEMORYVIEW:966SendMessage(GetParent(wnd),WM_DEB_GOTOHEXEDIT,curAddress,0);967break;968case ID_DISASM_TOGGLEBREAKPOINT:969toggleBreakpoint();970redraw();971break;972case ID_DISASM_ASSEMBLE:973assembleOpcode(curAddress,"");974break;975case ID_DISASM_COPYINSTRUCTIONDISASM:976CopyInstructions(selectRangeStart, selectRangeEnd, CopyInstructionsMode::DISASM);977break;978case ID_DISASM_COPYADDRESS:979CopyInstructions(selectRangeStart, selectRangeEnd, CopyInstructionsMode::ADDRESSES);980break;981case ID_DISASM_COPYINSTRUCTIONHEX:982CopyInstructions(selectRangeStart, selectRangeEnd, CopyInstructionsMode::OPCODES);983break;984case ID_DISASM_NOPINSTRUCTION:985NopInstructions(selectRangeStart, selectRangeEnd);986redraw();987break;988case ID_DISASM_EDITSYMBOLS:989{990EditSymbolsWindow esw(wnd, debugger);991if (esw.exec()) {992esw.eval();993SendMessage(GetParent(wnd), WM_DEB_MAPLOADED, 0, 0);994redraw();995}996}997break;998case ID_DISASM_SETPCTOHERE:999debugger->setPC(curAddress);1000redraw();1001break;1002case ID_DISASM_FOLLOWBRANCH:1003followBranch();1004break;1005case ID_DISASM_RUNTOHERE:1006{1007SendMessage(GetParent(wnd), WM_COMMAND, ID_DEBUG_RUNTOLINE, 0);1008redraw();1009}1010break;1011case ID_DISASM_RENAMEFUNCTION:1012{1013u32 funcBegin = g_symbolMap->GetFunctionStart(curAddress);1014if (funcBegin != -1)1015{1016char name[256];1017std::string newname;1018truncate_cpy(name, g_symbolMap->GetLabelString(funcBegin).c_str());1019if (InputBox_GetString(MainWindow::GetHInstance(), MainWindow::GetHWND(), L"New function name", name, newname)) {1020g_symbolMap->SetLabelName(newname.c_str(), funcBegin);1021u32 funcSize = g_symbolMap->GetFunctionSize(funcBegin);1022MIPSAnalyst::RegisterFunction(funcBegin, funcSize, newname.c_str());1023MIPSAnalyst::UpdateHashMap();1024MIPSAnalyst::ApplyHashMap();1025SendMessage(GetParent(wnd),WM_DEB_MAPLOADED,0,0);1026redraw();1027}1028}1029else1030{1031MessageBox(MainWindow::GetHWND(), L"No symbol selected",0,0);1032}1033}1034break;1035case ID_DISASM_REMOVEFUNCTION:1036{1037char statusBarTextBuff[256];1038u32 funcBegin = g_symbolMap->GetFunctionStart(curAddress);1039if (funcBegin != -1)1040{1041u32 prevBegin = g_symbolMap->GetFunctionStart(funcBegin-1);1042if (prevBegin != -1)1043{1044u32 expandedSize = g_symbolMap->GetFunctionSize(prevBegin) + g_symbolMap->GetFunctionSize(funcBegin);1045g_symbolMap->SetFunctionSize(prevBegin,expandedSize);1046}10471048g_symbolMap->RemoveFunction(funcBegin,true);1049g_symbolMap->SortSymbols();1050manager.clear();10511052SendMessage(GetParent(wnd), WM_DEB_MAPLOADED, 0, 0);1053}1054else1055{1056snprintf(statusBarTextBuff,256, "WARNING: unable to find function symbol here");1057SendMessage(GetParent(wnd), WM_DEB_SETSTATUSBARTEXT, 0, (LPARAM) statusBarTextBuff);1058}1059redraw();1060}1061break;1062case ID_DISASM_ADDFUNCTION:1063{1064char statusBarTextBuff[256];1065u32 prevBegin = g_symbolMap->GetFunctionStart(curAddress);1066if (prevBegin != -1)1067{1068if (prevBegin == curAddress)1069{1070snprintf(statusBarTextBuff,256, "WARNING: There's already a function entry point at this adress");1071SendMessage(GetParent(wnd), WM_DEB_SETSTATUSBARTEXT, 0, (LPARAM) statusBarTextBuff);1072}1073else1074{1075char symname[128];1076u32 prevSize = g_symbolMap->GetFunctionSize(prevBegin);1077u32 newSize = curAddress-prevBegin;1078g_symbolMap->SetFunctionSize(prevBegin,newSize);10791080newSize = prevSize-newSize;1081snprintf(symname,128,"u_un_%08X",curAddress);1082g_symbolMap->AddFunction(symname,curAddress,newSize);1083g_symbolMap->SortSymbols();1084manager.clear();10851086SendMessage(GetParent(wnd), WM_DEB_MAPLOADED, 0, 0);1087}1088}1089else1090{1091char symname[128];1092int newSize = selectRangeEnd - selectRangeStart;1093snprintf(symname, 128, "u_un_%08X", selectRangeStart);1094g_symbolMap->AddFunction(symname, selectRangeStart, newSize);1095g_symbolMap->SortSymbols();10961097SendMessage(GetParent(wnd), WM_DEB_MAPLOADED, 0, 0);1098}1099redraw();1100}1101break;1102case ID_DISASM_DISASSEMBLETOFILE:1103disassembleToFile();1104break;1105}1106return;1107}11081109redraw();1110}11111112void CtrlDisAsmView::onMouseMove(WPARAM wParam, LPARAM lParam, int button)1113{1114if ((button & 1) != 0)1115{1116int y = HIWORD(lParam);1117setCurAddress(yToAddress(y), KeyDownAsync(VK_SHIFT));1118redraw();1119}1120}11211122void CtrlDisAsmView::updateStatusBarText()1123{1124auto memLock = Memory::Lock();1125if (!PSP_IsInited())1126return;11271128char text[512];1129DisassemblyLineInfo line;1130manager.getLine(curAddress,true,line);11311132text[0] = 0;1133if (line.type == DISTYPE_OPCODE || line.type == DISTYPE_MACRO)1134{1135if (line.info.hasRelevantAddress && IsLikelyStringAt(line.info.relevantAddress)) {1136snprintf(text, sizeof(text), "[%08X] = \"%s\"", line.info.relevantAddress, Memory::GetCharPointer(line.info.relevantAddress));1137}11381139if (line.info.isDataAccess) {1140if (!Memory::IsValidAddress(line.info.dataAddress)) {1141snprintf(text, sizeof(text), "Invalid address %08X",line.info.dataAddress);1142} else {1143bool isFloat = MIPSGetInfo(line.info.encodedOpcode) & (IS_FPU | IS_VFPU);1144switch (line.info.dataSize) {1145case 1:1146snprintf(text, sizeof(text), "[%08X] = %02X",line.info.dataAddress,Memory::Read_U8(line.info.dataAddress));1147break;1148case 2:1149snprintf(text, sizeof(text), "[%08X] = %04X",line.info.dataAddress,Memory::Read_U16(line.info.dataAddress));1150break;1151case 4:1152{1153u32 dataInt = Memory::Read_U32(line.info.dataAddress);1154u32 dataFloat = Memory::Read_Float(line.info.dataAddress);1155std::string dataString;1156if (isFloat)1157dataString = StringFromFormat("%08X / %f", dataInt, dataFloat);1158else1159dataString = StringFromFormat("%08X", dataInt);11601161const std::string addressSymbol = g_symbolMap->GetLabelString(dataInt);1162if (!addressSymbol.empty()) {1163snprintf(text, sizeof(text), "[%08X] = %s (%s)", line.info.dataAddress, addressSymbol.c_str(), dataString.c_str());1164} else {1165snprintf(text, sizeof(text), "[%08X] = %s", line.info.dataAddress, dataString.c_str());1166}1167break;1168}1169case 16:1170{1171uint32_t dataInt[4];1172float dataFloat[4];1173for (int i = 0; i < 4; ++i) {1174dataInt[i] = Memory::Read_U32(line.info.dataAddress + i * 4);1175dataFloat[i] = Memory::Read_Float(line.info.dataAddress + i * 4);1176}1177std::string dataIntString = StringFromFormat("%08X,%08X,%08X,%08X", dataInt[0], dataInt[1], dataInt[2], dataInt[3]);1178std::string dataFloatString = StringFromFormat("%f,%f,%f,%f", dataFloat[0], dataFloat[1], dataFloat[2], dataFloat[3]);11791180snprintf(text, sizeof(text), "[%08X] = %s / %s", line.info.dataAddress, dataIntString.c_str(), dataFloatString.c_str());1181break;1182}1183}1184}1185}11861187if (line.info.isBranch)1188{1189const std::string addressSymbol = g_symbolMap->GetLabelString(line.info.branchTarget);1190if (addressSymbol.empty())1191{1192snprintf(text, sizeof(text), "%08X", line.info.branchTarget);1193} else {1194snprintf(text, sizeof(text), "%08X = %s",line.info.branchTarget,addressSymbol.c_str());1195}1196}1197} else if (line.type == DISTYPE_DATA) {1198u32 start = g_symbolMap->GetDataStart(curAddress);1199if (start == -1)1200start = curAddress;12011202u32 diff = curAddress-start;1203const std::string label = g_symbolMap->GetLabelString(start);12041205if (!label.empty()) {1206if (diff != 0)1207snprintf(text, sizeof(text), "%08X (%s) + %08X",start,label.c_str(),diff);1208else1209snprintf(text, sizeof(text), "%08X (%s)",start,label.c_str());1210} else {1211if (diff != 0)1212snprintf(text, sizeof(text), "%08X + %08X",start,diff);1213else1214snprintf(text, sizeof(text), "%08X",start);1215}1216}12171218SendMessage(GetParent(wnd),WM_DEB_SETSTATUSBARTEXT,0,(LPARAM)text);12191220const std::string label = g_symbolMap->GetLabelString(line.info.opcodeAddress);1221if (!label.empty()) {1222SendMessage(GetParent(wnd),WM_DEB_SETSTATUSBARTEXT,1,(LPARAM)label.c_str());1223}1224}12251226u32 CtrlDisAsmView::yToAddress(int y)1227{1228int line = y/rowHeight;1229return manager.getNthNextAddress(windowStart,line);1230}12311232void CtrlDisAsmView::calculatePixelPositions()1233{1234pixelPositions.addressStart = 16;1235pixelPositions.opcodeStart = pixelPositions.addressStart + 18*charWidth;1236pixelPositions.argumentsStart = pixelPositions.opcodeStart + 9*charWidth;1237pixelPositions.arrowsStart = pixelPositions.argumentsStart + 30*charWidth;1238}12391240void CtrlDisAsmView::search(bool continueSearch)1241{1242auto memLock = Memory::Lock();1243u32 searchAddress;12441245if (continueSearch == false || searchQuery[0] == 0)1246{1247if (InputBox_GetString(MainWindow::GetHInstance(), MainWindow::GetHWND(), L"Search for:", searchQuery, searchQuery) == false1248|| searchQuery[0] == 0)1249{1250SetFocus(wnd);1251return;1252}12531254for (size_t i = 0; i < searchQuery.size(); i++)1255{1256searchQuery[i] = tolower(searchQuery[i]);1257}1258SetFocus(wnd);1259searchAddress = manager.getNthNextAddress(curAddress,1);1260} else {1261searchAddress = manager.getNthNextAddress(matchAddress,1);1262}12631264// limit address to sensible ranges1265if (searchAddress < 0x04000000) searchAddress = 0x04000000;1266if (searchAddress >= 0x04200000 && searchAddress < 0x08000000) searchAddress = 0x08000000;1267if (searchAddress >= 0x0A000000) {1268MessageBox(wnd,L"Not found",L"Search",MB_OK);1269return;1270}12711272searching = true;1273redraw(); // so the cursor is disabled12741275DisassemblyLineInfo lineInfo;1276while (searchAddress < 0x0A000000)1277{1278manager.getLine(searchAddress,displaySymbols,lineInfo);12791280char addressText[64];1281getDisasmAddressText(searchAddress,addressText,true,lineInfo.type == DISTYPE_OPCODE);12821283const char* opcode = lineInfo.name.c_str();1284const char* arguments = lineInfo.params.c_str();12851286char merged[512];1287int mergePos = 0;12881289// I'm doing it manually to convert everything to lowercase at the same time1290for (int i = 0; addressText[i] != 0; i++) merged[mergePos++] = tolower(addressText[i]);1291merged[mergePos++] = ' ';1292for (int i = 0; opcode[i] != 0; i++) merged[mergePos++] = tolower(opcode[i]);1293merged[mergePos++] = ' ';1294for (int i = 0; arguments[i] != 0; i++) merged[mergePos++] = tolower(arguments[i]);1295merged[mergePos] = 0;12961297// match!1298if (strstr(merged, searchQuery.c_str()) != NULL)1299{1300matchAddress = searchAddress;1301searching = false;1302gotoAddr(searchAddress);1303return;1304}13051306// cancel search1307if ((searchAddress % 256) == 0 && KeyDownAsync(VK_ESCAPE))1308{1309searching = false;1310return;1311}13121313searchAddress = manager.getNthNextAddress(searchAddress,1);1314if (searchAddress >= 0x04200000 && searchAddress < 0x08000000) searchAddress = 0x08000000;1315}13161317MessageBox(wnd,L"Not found",L"Search",MB_OK);1318searching = false;1319}13201321std::string CtrlDisAsmView::disassembleRange(u32 start, u32 size)1322{1323auto memLock = Memory::Lock();1324std::string result;13251326// gather all branch targets without labels1327std::set<u32> branchAddresses;1328for (u32 i = 0; i < size; i += debugger->getInstructionSize(0))1329{1330MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(debugger,start+i);13311332if (info.isBranch && g_symbolMap->GetLabelString(info.branchTarget).empty())1333{1334if (branchAddresses.find(info.branchTarget) == branchAddresses.end())1335{1336branchAddresses.insert(info.branchTarget);1337}1338}1339}13401341u32 disAddress = start;1342bool previousLabel = true;1343DisassemblyLineInfo line;1344while (disAddress < start+size)1345{1346char addressText[64],buffer[512];13471348manager.getLine(disAddress,displaySymbols,line);1349bool isLabel = getDisasmAddressText(disAddress,addressText,false,line.type == DISTYPE_OPCODE);13501351if (isLabel)1352{1353if (!previousLabel) result += "\r\n";1354sprintf(buffer,"%s\r\n\r\n",addressText);1355result += buffer;1356} else if (branchAddresses.find(disAddress) != branchAddresses.end())1357{1358if (!previousLabel) result += "\r\n";1359sprintf(buffer,"pos_%08X:\r\n\r\n",disAddress);1360result += buffer;1361}13621363if (line.info.isBranch && !line.info.isBranchToRegister1364&& g_symbolMap->GetLabelString(line.info.branchTarget).empty()1365&& branchAddresses.find(line.info.branchTarget) != branchAddresses.end())1366{1367sprintf(buffer,"pos_%08X",line.info.branchTarget);1368line.params = line.params.substr(0,line.params.find("0x")) + buffer;1369}13701371sprintf(buffer,"\t%s\t%s\r\n",line.name.c_str(),line.params.c_str());1372result += buffer;1373previousLabel = isLabel;1374disAddress += line.totalSize;1375}13761377return result;1378}13791380void CtrlDisAsmView::disassembleToFile() {1381// get size1382u32 size;1383if (executeExpressionWindow(wnd,debugger,size) == false)1384return;1385if (size == 0 || size > 10*1024*1024) {1386MessageBox(wnd,L"Invalid size!",L"Error",MB_OK);1387return;1388}13891390std::string filename;1391if (W32Util::BrowseForFileName(false, nullptr, L"Save Disassembly As...", nullptr, L"All Files\0*.*\0\0", nullptr, filename)) {1392std::wstring fileName = ConvertUTF8ToWString(filename);1393FILE *output = _wfopen(fileName.c_str(), L"wb");1394if (output == nullptr) {1395MessageBox(wnd, L"Could not open file!", L"Error", MB_OK);1396return;1397}13981399std::string disassembly = disassembleRange(curAddress, size);1400fprintf(output, "%s", disassembly.c_str());14011402fclose(output);1403MessageBox(wnd, L"Finished!", L"Done", MB_OK);1404}1405}14061407void CtrlDisAsmView::getOpcodeText(u32 address, char* dest, int bufsize)1408{1409DisassemblyLineInfo line;1410address = manager.getStartAddress(address);1411manager.getLine(address,displaySymbols,line);1412snprintf(dest, bufsize, "%s %s",line.name.c_str(),line.params.c_str());1413}14141415void CtrlDisAsmView::scrollStepping(u32 newPc)1416{1417u32 windowEnd = manager.getNthNextAddress(windowStart,visibleRows);14181419newPc = manager.getStartAddress(newPc);1420if (newPc >= windowEnd || newPc >= manager.getNthPreviousAddress(windowEnd,1))1421{1422windowStart = manager.getNthPreviousAddress(newPc,visibleRows-2);1423}1424}14251426u32 CtrlDisAsmView::getInstructionSizeAt(u32 address)1427{1428u32 start = manager.getStartAddress(address);1429u32 next = manager.getNthNextAddress(start,1);1430return next-address;1431}143214331434