Files
ppsspp/UI/ImDebugger/ImDisasmView.h
T
Henrik RydgårdandClaude Opus 5 ebd2b4757f ImDebugger: prefill the assemble box with the instruction that's there
Assembling started from an empty box, so replacing an instruction meant reading
it off the screen and retyping it, and tweaking one operand meant typing the
whole thing. It now opens with the current instruction already in it, selected,
so typing replaces it and editing is just editing.

Disassembled without symbol substitution for this, unlike what the view itself
shows: a branch displayed as a function name doesn't assemble back, and the
whole point of the prefill is that it's valid input.

Selection is only applied on this path. onChar() also seeds the box - with the
character the user just typed over an instruction - and there the text is the
start of what they're writing, not something to overwrite.

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01GZq8ZtJmFY7bkX5FVkr3P9
2026-08-18 13:42:14 +02:00

236 lines
6.2 KiB
C++

#pragma once
#include <vector>
#include <string>
#include <set>
#include <algorithm>
#include "ext/imgui/imgui.h"
#include "Common/CommonTypes.h"
#include "Common/Math/geom2d.h"
#include "Core/Debugger/DisassemblyManager.h"
#include "Core/MIPS/MIPSDebugInterface.h"
struct ImConfig;
struct ImControl;
class MIPSState;
// Corresponds to CtrlDisAsmView
// TODO: Fold out common code.
class ImDisasmView {
public:
ImDisasmView();
~ImDisasmView();
// Public variables bounds to imgui checkboxes
bool followPC_ = true;
void Draw(ImDrawList *drawList, ImControl &control);
void PopupMenu(MIPSState *mips, ImControl &control);
void NotifyStep();
void ScrollRelative(int amount);
void onChar(int c);
void onMouseDown(float x, float y, int button);
void onMouseUp(float x, float y, int button);
void onMouseMove(float x, float y, int button);
void scrollAddressIntoView();
bool curAddressIsVisible();
void ScanVisibleFunctions();
void clearFunctions() { g_disassemblyManager.clear(); };
void getOpcodeText(u32 address, char *dest, int bufsize, bool insertSymbols);
u32 yToAddress(float y);
void setDebugger(MIPSDebugInterface *deb) {
if (debugger_ != deb) {
debugger_ = deb;
curAddress_ = debugger_->GetPC();
g_disassemblyManager.setCpu(deb);
}
}
MIPSDebugInterface *getDebugger() {
return debugger_;
}
void scrollStepping(u32 newPc);
void gotoAddr(unsigned int addr) {
if (positionLocked_ != 0)
return;
u32 windowEnd = g_disassemblyManager.getNthNextAddress(windowStart_, visibleRows_);
u32 newAddress = g_disassemblyManager.getStartAddress(addr);
if (newAddress < windowStart_ || newAddress >= windowEnd) {
windowStart_ = g_disassemblyManager.getNthPreviousAddress(newAddress, visibleRows_ / 2);
}
setCurAddress(newAddress);
ScanVisibleFunctions();
}
void GotoPC() {
gotoAddr(debugger_->GetPC());
}
void GotoRA() {
gotoAddr(debugger_->GetRA());
}
u32 getSelection() {
return curAddress_;
}
void setShowMode(bool s) {
showHex_ = s;
}
void toggleBreakpoint(bool toggleEnabled = false);
void editBreakpoint(ImConfig &cfg);
void setCurAddress(u32 newAddress, bool extend = false) {
newAddress = g_disassemblyManager.getStartAddress(newAddress);
const u32 after = g_disassemblyManager.getNthNextAddress(newAddress, 1);
curAddress_ = newAddress;
selectRangeStart_ = extend ? std::min(selectRangeStart_, newAddress) : newAddress;
selectRangeEnd_ = extend ? std::max(selectRangeEnd_, after) : after;
updateStatusBarText();
}
void LockPosition() {
positionLocked_++;
}
void UnlockPosition() {
positionLocked_--;
_assert_(positionLocked_ >= 0);
}
void Search(std::string_view needle);
void SearchNext(bool forward);
// Check these every frame!
const std::string &StatusBarText() const {
return statusBarText_;
}
bool SymbolMapReloaded() {
bool retval = mapReloaded_;
mapReloaded_ = false;
return retval;
}
private:
enum class CopyInstructionsMode {
OPCODES,
DISASM,
ADDRESSES,
};
void ProcessKeyboardShortcuts(bool focused);
// Plants the one-shot "run to cursor" breakpoint and resumes. With nextFrame, hits are ignored
// until the next vblank, so the rest of the current frame is skipped over.
void RunToAddress(u32 address, bool nextFrame);
// Requests the assemble popup - the actual input happens in PopupMenu(), since ImGui popups
// can only be opened and drawn from inside the frame that owns them.
void assembleOpcode(u32 address, std::string_view defaultText, bool selectAll = false);
// Same, prefilled with the instruction that's already there, selected so typing replaces it.
void assembleCurrentOpcode(u32 address);
// Applies what was typed into that popup. Returns false and fills in assembleError_ if it
// couldn't be assembled, so the popup can stay open and show why.
bool applyAssembly(u32 address, std::string_view op);
std::string disassembleRange(u32 start, u32 size);
void disassembleToFile();
void FollowBranch();
void calculatePixelPositions();
bool getDisasmAddressText(u32 address, char *dest, size_t bufSize, bool abbreviateLabels, bool showData);
void updateStatusBarText();
void drawBranchLine(ImDrawList *list, Bounds rc, std::map<u32, float> &addressPositions, const BranchLine &line);
void CopyInstructions(u32 startAddr, u32 endAddr, CopyInstructionsMode mode);
std::set<std::string> getSelectedLineArguments();
void drawArguments(ImDrawList *list, Bounds rc, const DisassemblyLineInfo &line, float x, float y, ImColor textColor, const std::set<std::string> &currentArguments);
u32 curAddress_ = 0;
u32 selectRangeStart_ = 0;
u32 selectRangeEnd_ = 0;
float rowHeight_ = 0.f;
float charWidth_ = 0.f;
bool bpPopup_ = false;
bool hasFocus_ = true;
bool showHex_ = false;
MIPSDebugInterface *debugger_ = nullptr;
u32 windowStart_ = 0;
int visibleRows_ = 1;
bool displaySymbols_ = true;
struct {
int addressStart;
int opcodeStart;
int argumentsStart;
int arrowsStart;
} pixelPositions_{};
std::vector<u32> jumpStack_;
std::string searchQuery_;
int matchAddress_;
bool searching_ = false;
bool keyTaken = false;
bool mapReloaded_ = false;
int positionLocked_ = 0;
std::string statusBarText_;
u32 funcBegin_ = 0;
char funcNameTemp_[128]{};
bool assemblePopup_ = false;
u32 assembleAddress_ = 0;
char assembleTemp_[256]{};
bool assembleSelectAll_ = false;
std::string assembleError_;
};
// Corresponds to the CDisasm dialog
class ImDisasmWindow {
public:
void Draw(MIPSDebugInterface *mipsDebug, ImConfig &cfg, ImControl &control, CoreState coreState);
ImDisasmView &View() {
return disasmView_;
}
void NotifyStep() {
disasmView_.NotifyStep();
}
void DirtySymbolMap() {
symsDirty_ = true;
}
const char *Title() const {
return "CPU Debugger";
}
private:
// We just keep the state directly in the window. Can refactor later.
enum {
INVALID_ADDR = 0xFFFFFFFF,
};
u32 gotoAddr_ = 0x08800000;
// Symbol cache
std::vector<SymbolEntry> symCache_;
bool symsDirty_ = true;
int selectedSymbol_ = -1;
char selectedSymbolName_[128];
// Filter over symCache_. Held as indices into it rather than a filtered copy, so
// selectedSymbol_ keeps meaning the same thing whether or not a filter is active.
char symFilter_[64]{};
std::vector<int> symMatches_;
bool symMatchesDirty_ = true;
ImDisasmView disasmView_;
char searchTerm_[64]{};
};