diff --git a/Core/Debugger/DisassemblyManager.cpp b/Core/Debugger/DisassemblyManager.cpp index b2d74ef5a1..16548279ba 100644 --- a/Core/Debugger/DisassemblyManager.cpp +++ b/Core/Debugger/DisassemblyManager.cpp @@ -1092,3 +1092,87 @@ bool DisassemblyComment::disassemble(u32 address, DisassemblyLineInfo &dest, boo dest.totalSize = size; return true; } + +bool GetDisasmAddressText(u32 address, char* dest, bool abbreviateLabels, bool showData, bool displaySymbols) { + if (displaySymbols) { + const std::string addressSymbol = g_symbolMap->GetLabelString(address); + if (!addressSymbol.empty()) { + for (int k = 0; addressSymbol[k] != 0; k++) { + // abbreviate long names + if (abbreviateLabels && k == 16 && addressSymbol[k+1] != 0) { + *dest++ = '+'; + break; + } + *dest++ = addressSymbol[k]; + } + *dest++ = ':'; + *dest = 0; + return true; + } else { + sprintf(dest," %08X",address); + return false; + } + } else { + if (showData) { + u32 encoding = Memory::IsValidAddress(address) ? Memory::Read_Instruction(address, true).encoding : 0; + sprintf(dest, "%08X %08X", address, encoding); + } else { + sprintf(dest, "%08X", address); + } + return false; + } +} + +// Utilify function from the old debugger. +std::string DisassembleRange(u32 start, u32 size, bool displaySymbols, MIPSDebugInterface *debugger) { + auto memLock = Memory::Lock(); + std::string result; + + // gather all branch targets without labels + std::set branchAddresses; + for (u32 i = 0; i < size; i += debugger->getInstructionSize(0)) { + MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(debugger, start + i); + + if (info.isBranch && g_symbolMap->GetLabelString(info.branchTarget).empty()) { + if (branchAddresses.find(info.branchTarget) == branchAddresses.end()) { + branchAddresses.insert(info.branchTarget); + } + } + } + + u32 disAddress = start; + bool previousLabel = true; + DisassemblyLineInfo line; + while (disAddress < start + size) { + char addressText[64], buffer[512]; + + g_disassemblyManager.getLine(disAddress, displaySymbols, line, debugger); + bool isLabel = GetDisasmAddressText(disAddress, addressText, false, line.type == DISTYPE_OPCODE, displaySymbols); + + if (isLabel) { + if (!previousLabel) + result += "\r\n"; + sprintf(buffer, "%s\r\n\r\n", addressText); + result += buffer; + } else if (branchAddresses.find(disAddress) != branchAddresses.end()) { + if (!previousLabel) + result += "\r\n"; + sprintf(buffer, "pos_%08X:\r\n\r\n", disAddress); + result += buffer; + } + + if (line.info.isBranch && !line.info.isBranchToRegister + && g_symbolMap->GetLabelString(line.info.branchTarget).empty() + && branchAddresses.find(line.info.branchTarget) != branchAddresses.end()) { + sprintf(buffer, "pos_%08X", line.info.branchTarget); + line.params = line.params.substr(0, line.params.find("0x")) + buffer; + } + + sprintf(buffer, "\t%s\t%s\r\n", line.name.c_str(), line.params.c_str()); + result += buffer; + previousLabel = isLabel; + disAddress += line.totalSize; + } + + return result; +} diff --git a/Core/Debugger/DisassemblyManager.h b/Core/Debugger/DisassemblyManager.h index 1478372eb2..720d3b6d67 100644 --- a/Core/Debugger/DisassemblyManager.h +++ b/Core/Debugger/DisassemblyManager.h @@ -226,3 +226,6 @@ extern DisassemblyManager g_disassemblyManager; bool isInInterval(u32 start, u32 size, u32 value); bool IsLikelyStringAt(uint32_t addr); + +std::string DisassembleRange(u32 start, u32 size, bool displaySymbols, MIPSDebugInterface *debugger); +bool GetDisasmAddressText(u32 address, char* dest, bool abbreviateLabels, bool showData, bool displaySymbols); diff --git a/UI/ImDebugger/ImDebugger.cpp b/UI/ImDebugger/ImDebugger.cpp index e80be8586c..6303e3ccc0 100644 --- a/UI/ImDebugger/ImDebugger.cpp +++ b/UI/ImDebugger/ImDebugger.cpp @@ -56,6 +56,12 @@ void ShowInMemoryViewerMenuItem(uint32_t addr, ImControl &control) { } } +void ShowInMemoryDumperMenuItem(uint32_t addr, uint32_t size, MemDumpMode mode, ImControl &control) { + if (ImGui::MenuItem(mode == MemDumpMode::Raw ? "Dump bytes to file..." : "Disassemble to file...")) { + control.command = { ImCmd::SHOW_IN_MEMORY_DUMPER, addr, size, (u32)mode}; + } +} + void ShowInWindowMenuItems(uint32_t addr, ImControl &control) { // Enable when we implement the memory viewer ShowInMemoryViewerMenuItem(addr, control); @@ -1112,6 +1118,7 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu snprintf(title, sizeof(title), "Memory %d", i + 1); ImGui::MenuItem(title, nullptr, &cfg_.memViewOpen[i]); } + ImGui::MenuItem("Memory Dumper", nullptr, &cfg_.memDumpOpen); ImGui::EndMenu(); } if (ImGui::BeginMenu("HLE")) { @@ -1253,6 +1260,10 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu pixelViewer_.Draw(cfg_, control, gpuDebug, draw); } + if (cfg_.memDumpOpen) { + memDumpWindow_.Draw(cfg_, mipsDebug); + } + for (int i = 0; i < 4; i++) { if (cfg_.memViewOpen[i]) { mem_[i].Draw(mipsDebug, cfg_, control, i); @@ -1288,6 +1299,13 @@ void ImDebugger::Frame(MIPSDebugInterface *mipsDebug, GPUDebugInterface *gpuDebu ImGui::SetWindowFocus(ImMemWindow::Title(index)); break; } + case ImCmd::SHOW_IN_MEMORY_DUMPER: + { + cfg_.memDumpOpen = true; + memDumpWindow_.SetRange(control.command.param, control.command.param2, (MemDumpMode)control.command.param3); + ImGui::SetWindowFocus(memDumpWindow_.Title()); + break; + } case ImCmd::TRIGGER_FIND_POPUP: // TODO break; diff --git a/UI/ImDebugger/ImDebugger.h b/UI/ImDebugger/ImDebugger.h index b9d621e6d3..d85d27e6c2 100644 --- a/UI/ImDebugger/ImDebugger.h +++ b/UI/ImDebugger/ImDebugger.h @@ -143,6 +143,7 @@ struct ImConfig { bool pixelViewerOpen; bool npOpen; bool socketsOpen; + bool memDumpOpen; bool memViewOpen[4]; // HLE explorer settings @@ -175,6 +176,7 @@ enum class ImCmd { SHOW_IN_GE_DISASM, SHOW_IN_MEMORY_VIEWER, // param is address, param2 is viewer index SHOW_IN_PIXEL_VIEWER, // param is address, param2 is stride, |0x80000000 if depth, param3 is w/h + SHOW_IN_MEMORY_DUMPER, // param is address, param2 is size, param3 is mode }; struct ImCommand { @@ -211,6 +213,7 @@ private: ImMemWindow mem_[4]; // We support 4 separate instances of the memory viewer. ImStructViewer structViewer_; ImGePixelViewerWindow pixelViewer_; + ImMemDumpWindow memDumpWindow_; ImSnapshotState newSnapshot_; ImSnapshotState snapshot_; @@ -226,4 +229,5 @@ private: void ImClickableAddress(uint32_t addr, ImControl &control, ImCmd cmd); void ShowInWindowMenuItems(uint32_t addr, ImControl &control); void ShowInMemoryViewerMenuItem(uint32_t addr, ImControl &control); +void ShowInMemoryDumperMenuItem(uint32_t addr, uint32_t size, MemDumpMode mode, ImControl &control); void StatusBar(std::string_view str); diff --git a/UI/ImDebugger/ImDisasmView.cpp b/UI/ImDebugger/ImDisasmView.cpp index 49b5901682..d05a3d6582 100644 --- a/UI/ImDebugger/ImDisasmView.cpp +++ b/UI/ImDebugger/ImDisasmView.cpp @@ -53,33 +53,7 @@ bool ImDisasmView::getDisasmAddressText(u32 address, char* dest, bool abbreviate if (!PSP_IsInited()) return false; - if (displaySymbols_) { - const std::string addressSymbol = g_symbolMap->GetLabelString(address); - if (!addressSymbol.empty()) { - for (int k = 0; addressSymbol[k] != 0; k++) { - // abbreviate long names - if (abbreviateLabels && k == 16 && addressSymbol[k + 1] != 0) { - *dest++ = '+'; - break; - } - *dest++ = addressSymbol[k]; - } - *dest++ = ':'; - *dest = 0; - return true; - } else { - sprintf(dest, " %08X", address); - return false; - } - } else { - if (showData) { - u32 encoding = Memory::IsValidAddress(address) ? Memory::Read_Instruction(address, true).encoding : 0; - sprintf(dest, "%08X %08X", address, encoding); - } else { - sprintf(dest, "%08X", address); - } - return false; - } + return GetDisasmAddressText(address, dest, abbreviateLabels, showData, displaySymbols_); } void ImDisasmView::assembleOpcode(u32 address, const std::string &defaultText) { @@ -830,9 +804,9 @@ void ImDisasmView::PopupMenu(ImControl &control) { statusBarText_ = "WARNING: unable to find function symbol here"; } } + u32 prevBegin = g_symbolMap->GetFunctionStart(curAddress_); if (ImGui::MenuItem("Add function")) { char statusBarTextBuff[256]; - u32 prevBegin = g_symbolMap->GetFunctionStart(curAddress_); if (prevBegin != -1) { if (prevBegin == curAddress_) { snprintf(statusBarTextBuff, 256, "WARNING: There's already a function entry point at this adress"); @@ -861,8 +835,9 @@ void ImDisasmView::PopupMenu(ImControl &control) { mapReloaded_ = true; } } - if (ImGui::MenuItem("Disassemble to file")) { - disassembleToFile(); + if (prevBegin != -1) { + u32 prevSize = g_symbolMap->GetFunctionSize(prevBegin); + ShowInMemoryDumperMenuItem(prevBegin, prevSize, MemDumpMode::Disassembly, control); } ImGui::EndPopup(); } diff --git a/UI/ImDebugger/ImMemView.cpp b/UI/ImDebugger/ImMemView.cpp index 6a42948306..f11e949404 100644 --- a/UI/ImDebugger/ImMemView.cpp +++ b/UI/ImDebugger/ImMemView.cpp @@ -1,6 +1,7 @@ #include #include #include +#include #include #include "ext/imgui/imgui.h" @@ -9,6 +10,7 @@ #include "ext/xxhash.h" #include "Common/StringUtils.h" +#include "Common/File/FileUtil.h" #include "Core/Config.h" #include "Core/MemMap.h" #include "Core/Reporting.h" @@ -838,3 +840,62 @@ void ImMemView::setHighlightType(MemBlockFlags flags) { updateStatusBarText(); } } + +void ImMemDumpWindow::Draw(ImConfig &cfg, MIPSDebugInterface *debug) { + ImGui::SetNextWindowSize(ImVec2(200, 300), ImGuiCond_FirstUseEver); + + if (!ImGui::Begin(Title(), &cfg.memDumpOpen)) { + ImGui::End(); + return; + } + + if (ImGui::Button("User RAM (0x08800000)")) { + address_ = 0x08800000; + size_ = 0x01800000; // 24MB + } + + ImGui::InputScalar("Starting address", ImGuiDataType_U32, &address_, NULL, NULL, "%08X"); + ImGui::InputScalar("Size", ImGuiDataType_U32, &size_, NULL, NULL, "%08X"); + + ImGui::InputText("Filename", filename_, ARRAY_SIZE(filename_)); + + const char* modes[] = { "Raw", "Disassembly" }; + int modeIndex = static_cast(mode_); + if (ImGui::Combo("Memory Dump Mode", &modeIndex, modes, IM_ARRAYSIZE(modes))) { + // Update the current mode if the user selects a new one + mode_ = static_cast(modeIndex); + } + + if (ImGui::Button(mode_ == MemDumpMode::Raw ? "Dump to file" : "Disassemble to file")) { + uint32_t validSize = Memory::ValidSize(address_, size_); + if (validSize != size_) { + errorMsg_ = "Address range out of bounds"; + if (Memory::IsValidAddress(address_)) { + size_ = validSize; + } + } else if (strlen(filename_) == 0) { + errorMsg_ = "Please specify a valid filename"; + } else { + FILE *file = File::OpenCFile(Path(filename_), "wb"); + if (!file) { + errorMsg_ = "Couldn't open file for writing"; + } else { + if (mode_ == MemDumpMode::Raw) { + const uint8_t *ptr = Memory::GetPointer(address_); + fwrite(ptr, 1, size_, file); + } else { + std::string disassembly = DisassembleRange(address_, size_, true, debug); + fprintf(file, "%s", disassembly.c_str()); + } + errorMsg_.clear(); + fclose(file); + } + } + } + + if (!errorMsg_.empty()) { + ImGui::TextUnformatted(errorMsg_.data(), errorMsg_.data() + errorMsg_.size()); + } + + ImGui::End(); +} diff --git a/UI/ImDebugger/ImMemView.h b/UI/ImDebugger/ImMemView.h index e0235f2517..750f64aa1c 100644 --- a/UI/ImDebugger/ImMemView.h +++ b/UI/ImDebugger/ImMemView.h @@ -118,3 +118,33 @@ private: std::string statusMessage_; }; + +enum class MemDumpMode { + Raw = 0, + Disassembly = 1, +}; + +class ImMemDumpWindow { +public: + ImMemDumpWindow() { + filename_[0] = 0; + address_ = 0x08800000; + size_ = 0x01800000; + } + static const char *Title() { + return "Memory Dumper"; + } + void Draw(ImConfig &cfg, MIPSDebugInterface *debug); + void SetRange(uint32_t addr, uint32_t size, MemDumpMode mode) { + address_ = addr; + size_ = size; + mode_ = mode; + } + +private: + uint32_t address_; + uint32_t size_; + MemDumpMode mode_ = MemDumpMode::Raw; + char filename_[1024]; + std::string errorMsg_; +}; diff --git a/Windows/Debugger/CtrlDisAsmView.cpp b/Windows/Debugger/CtrlDisAsmView.cpp index dc8bfba1af..eb81d5bfc4 100644 --- a/Windows/Debugger/CtrlDisAsmView.cpp +++ b/Windows/Debugger/CtrlDisAsmView.cpp @@ -218,44 +218,6 @@ static COLORREF scaleColor(COLORREF color, float factor) return (color & 0xFF000000) | (b << 16) | (g << 8) | r; } -bool CtrlDisAsmView::getDisasmAddressText(u32 address, char* dest, bool abbreviateLabels, bool showData) -{ - if (!PSP_IsInited()) - return false; - - if (displaySymbols) - { - const std::string addressSymbol = g_symbolMap->GetLabelString(address); - if (!addressSymbol.empty()) - { - for (int k = 0; addressSymbol[k] != 0; k++) - { - // abbreviate long names - if (abbreviateLabels && k == 16 && addressSymbol[k+1] != 0) - { - *dest++ = '+'; - break; - } - *dest++ = addressSymbol[k]; - } - *dest++ = ':'; - *dest = 0; - return true; - } else { - sprintf(dest," %08X",address); - return false; - } - } else { - if (showData) { - u32 encoding = Memory::IsValidAddress(address) ? Memory::Read_Instruction(address, true).encoding : 0; - sprintf(dest, "%08X %08X", address, encoding); - } else { - sprintf(dest, "%08X", address); - } - return false; - } -} - std::string trimString(std::string input) { size_t pos = input.find_first_not_of(" \t"); @@ -567,7 +529,7 @@ void CtrlDisAsmView::onPaint(WPARAM wParam, LPARAM lParam) SetTextColor(hdc,textColor); char addressText[64]; - getDisasmAddressText(address,addressText,true,line.type == DISTYPE_OPCODE); + GetDisasmAddressText(address,addressText,true,line.type == DISTYPE_OPCODE, displaySymbols); TextOutA(hdc,pixelPositions.addressStart,rowY1+2,addressText,(int)strlen(addressText)); if (isInInterval(address,line.totalSize,debugger->GetPC())) @@ -935,7 +897,7 @@ void CtrlDisAsmView::CopyInstructions(u32 startAddr, u32 endAddr, CopyInstructio W32Util::CopyTextToClipboard(wnd, temp); delete [] temp; } else { - std::string disassembly = disassembleRange(startAddr,endAddr-startAddr); + std::string disassembly = DisassembleRange(startAddr,endAddr-startAddr, displaySymbols, debugger); W32Util::CopyTextToClipboard(wnd, disassembly.c_str()); } } @@ -1290,7 +1252,7 @@ void CtrlDisAsmView::search(bool continueSearch) g_disassemblyManager.getLine(searchAddress,displaySymbols,lineInfo, debugger); char addressText[64]; - getDisasmAddressText(searchAddress,addressText,true,lineInfo.type == DISTYPE_OPCODE); + GetDisasmAddressText(searchAddress,addressText,true,lineInfo.type == DISTYPE_OPCODE, displaySymbols); const char* opcode = lineInfo.name.c_str(); const char* arguments = lineInfo.params.c_str(); @@ -1330,65 +1292,6 @@ void CtrlDisAsmView::search(bool continueSearch) searching = false; } -std::string CtrlDisAsmView::disassembleRange(u32 start, u32 size) -{ - auto memLock = Memory::Lock(); - std::string result; - - // gather all branch targets without labels - std::set branchAddresses; - for (u32 i = 0; i < size; i += debugger->getInstructionSize(0)) - { - MIPSAnalyst::MipsOpcodeInfo info = MIPSAnalyst::GetOpcodeInfo(debugger,start+i); - - if (info.isBranch && g_symbolMap->GetLabelString(info.branchTarget).empty()) - { - if (branchAddresses.find(info.branchTarget) == branchAddresses.end()) - { - branchAddresses.insert(info.branchTarget); - } - } - } - - u32 disAddress = start; - bool previousLabel = true; - DisassemblyLineInfo line; - while (disAddress < start+size) - { - char addressText[64],buffer[512]; - - g_disassemblyManager.getLine(disAddress,displaySymbols,line, debugger); - bool isLabel = getDisasmAddressText(disAddress,addressText,false,line.type == DISTYPE_OPCODE); - - if (isLabel) - { - if (!previousLabel) result += "\r\n"; - sprintf(buffer,"%s\r\n\r\n",addressText); - result += buffer; - } else if (branchAddresses.find(disAddress) != branchAddresses.end()) - { - if (!previousLabel) result += "\r\n"; - sprintf(buffer,"pos_%08X:\r\n\r\n",disAddress); - result += buffer; - } - - if (line.info.isBranch && !line.info.isBranchToRegister - && g_symbolMap->GetLabelString(line.info.branchTarget).empty() - && branchAddresses.find(line.info.branchTarget) != branchAddresses.end()) - { - sprintf(buffer,"pos_%08X",line.info.branchTarget); - line.params = line.params.substr(0,line.params.find("0x")) + buffer; - } - - sprintf(buffer,"\t%s\t%s\r\n",line.name.c_str(),line.params.c_str()); - result += buffer; - previousLabel = isLabel; - disAddress += line.totalSize; - } - - return result; -} - void CtrlDisAsmView::disassembleToFile() { // get size u32 size; @@ -1408,7 +1311,7 @@ void CtrlDisAsmView::disassembleToFile() { return; } - std::string disassembly = disassembleRange(curAddress, size); + std::string disassembly = DisassembleRange(curAddress, size, displaySymbols, debugger); fprintf(output, "%s", disassembly.c_str()); fclose(output); diff --git a/Windows/Debugger/CtrlDisAsmView.h b/Windows/Debugger/CtrlDisAsmView.h index f227cb5aca..fe908ddad0 100644 --- a/Windows/Debugger/CtrlDisAsmView.h +++ b/Windows/Debugger/CtrlDisAsmView.h @@ -70,12 +70,10 @@ class CtrlDisAsmView }; void assembleOpcode(u32 address, const std::string &defaultText); - std::string disassembleRange(u32 start, u32 size); void disassembleToFile(); void search(bool continueSearch); void followBranch(); void calculatePixelPositions(); - bool getDisasmAddressText(u32 address, char* dest, bool abbreviateLabels, bool showData); void updateStatusBarText(); void drawBranchLine(HDC hdc, std::map &addressPositions, const BranchLine &line); void CopyInstructions(u32 startAddr, u32 endAddr, CopyInstructionsMode mode);