8#include "absl/strings/str_format.h"
9#include "absl/strings/str_join.h"
10#include "absl/strings/str_split.h"
23 return absl::StrFormat(
"$%02X:%04X", (address >> 16) & 0xFF,
30 static const std::set<uint8_t> branch_jump_opcodes = {
50 return branch_jump_opcodes.count(opcode) > 0;
55 return opcode == 0x60 ||
62 static const std::set<uint8_t> stack_opcodes = {
76 return stack_opcodes.count(opcode) > 0;
82 : emulator_service_(emulator_service),
84 symbol_provider_(std::make_unique<
yaze::emu::debug::SymbolProvider>()) {
89absl::StatusOr<RomDebugAgent::BreakpointAnalysis>
91 const yaze::agent::BreakpointHitResponse& hit) {
95 if (hit.has_breakpoint()) {
96 analysis.
address = hit.breakpoint().address();
98 analysis.
address = hit.cpu_state().pc();
110 const auto& cpu = hit.cpu_state();
121 grpc::ServerContext context;
122 yaze::agent::DisassemblyRequest disasm_req;
123 disasm_req.set_start_address(analysis.
address);
124 disasm_req.set_count(1);
125 yaze::agent::DisassemblyResponse disasm_resp;
130 return absl::InternalError(
"Failed to get disassembly");
133 if (disasm_resp.lines_size() > 0) {
134 const auto& inst = disasm_resp.lines(0);
135 analysis.
disassembly = inst.mnemonic() +
" " + inst.operand_str();
141 if (explanation.ok()) {
155 analysis.
suggestions.push_back(issue->suggested_fix);
162 uint32_t address,
size_t length) {
168 grpc::ServerContext context;
169 MemoryRequest mem_req;
170 mem_req.set_address(address);
171 mem_req.set_size(length);
172 MemoryResponse mem_resp;
176 return absl::InternalError(
"Failed to read memory");
179 analysis.
data.assign(mem_resp.data().begin(), mem_resp.data().end());
186 if (struct_info.has_value()) {
197 analysis.
fields[
"sprite_index"] = sprite_index;
199 if (length >= 0x10) {
200 analysis.
fields[
"state"] = analysis.
data[0x00];
201 analysis.
fields[
"x_pos_low"] = analysis.
data[0x01];
202 analysis.
fields[
"x_pos_high"] = analysis.
data[0x02];
203 analysis.
fields[
"y_pos_low"] = analysis.
data[0x03];
204 analysis.
fields[
"y_pos_high"] = analysis.
data[0x04];
207 if (analysis.
data[0x00] == 0x00) {
208 analysis.
anomalies.push_back(
"Sprite is inactive (state = 0)");
210 if (analysis.
data[0x02] > 0x01) {
211 analysis.
anomalies.push_back(
"Sprite X position exceeds screen bounds");
217 analysis.
fields[
"current_health"] = analysis.
data[0];
218 if (analysis.
data[0] == 0) {
219 analysis.
anomalies.push_back(
"Player health is zero - death state");
222 analysis.
fields[
"max_health"] = analysis.
data[0];
225 analysis.
fields[
"inventory_slot"] = item_slot;
226 analysis.
fields[
"item_value"] = analysis.
data[0];
231 analysis.
fields[
"dma_channel"] = dma_channel;
236 analysis.
fields[
"dma_control"] = analysis.
data[0];
239 analysis.
fields[
"dma_destination"] = analysis.
data[0];
255 "DMA channel enabled during active display - may cause glitches");
260 if (analysis.
data_type ==
"sprite" && length >= 16) {
263 std::all_of(analysis.
data.begin(),
264 analysis.
data.begin() + std::min(
size_t(16), length),
265 [](uint8_t b) { return b == 0; });
267 std::all_of(analysis.
data.begin(),
268 analysis.
data.begin() + std::min(
size_t(16), length),
269 [](uint8_t b) { return b == 0xFF; });
273 "Sprite data is all zeros - likely uninitialized");
277 "Sprite data is all 0xFF - possible corruption");
285 const std::vector<ExecutionTraceBuffer::TraceEntry>& trace) {
286 std::stringstream explanation;
288 explanation <<
"Execution Trace Analysis:\n";
289 explanation <<
"=========================\n\n";
292 std::vector<std::string> call_stack;
293 int indent_level = 0;
295 for (
size_t i = 0; i < trace.size(); ++i) {
296 const auto& entry = trace[i];
299 std::string indent(indent_level * 2,
' ');
302 if (entry.opcode == 0x20 || entry.opcode == 0x22 || entry.opcode == 0xFC) {
304 uint32_t target_addr = 0;
305 if (entry.operands.size() >= 2) {
306 target_addr = entry.operands[0] | (entry.operands[1] << 8);
307 if (entry.opcode == 0x22 && entry.operands.size() >= 3) {
308 target_addr |= (entry.operands[2] << 16);
312 std::string target_name =
315 : FormatSnesAddress(target_addr);
317 explanation << indent <<
"→ CALL " << target_name <<
" from "
318 << FormatSnesAddress(entry.address) <<
"\n";
320 call_stack.push_back(target_name);
323 }
else if (IsReturn(entry.opcode)) {
325 if (!call_stack.empty()) {
326 indent_level = std::max(0, indent_level - 1);
327 indent = std::string(indent_level * 2,
' ');
328 explanation << indent <<
"← RETURN from " << call_stack.back() <<
"\n";
329 call_stack.pop_back();
332 }
else if (IsBranchOrJump(entry.opcode)) {
334 std::string condition;
335 switch (entry.opcode) {
337 condition =
" (if plus)";
340 condition =
" (if minus)";
343 condition =
" (if overflow clear)";
346 condition =
" (if overflow set)";
349 condition =
" (if carry clear)";
352 condition =
" (if carry set)";
355 condition =
" (if not zero)";
358 condition =
" (if zero)";
364 condition =
" (unconditional)";
368 explanation << indent <<
" " << entry.mnemonic <<
" "
369 << entry.operand_str << condition <<
"\n";
373 bool is_significant =
false;
377 if (entry.mnemonic.find(
"LD") != std::string::npos ||
378 entry.mnemonic.find(
"ST") != std::string::npos) {
379 is_significant =
true;
382 if (entry.operands.size() >= 2) {
383 uint32_t addr = entry.operands[0] | (entry.operands[1] << 8);
384 if (entry.operands.size() >= 3) {
385 addr |= (entry.operands[2] << 16);
392 if (ModifiesStack(entry.opcode)) {
393 is_significant =
true;
395 if (entry.opcode == 0x48 || entry.opcode == 0x08) {
397 }
else if (entry.opcode == 0x68 || entry.opcode == 0x28) {
403 if (entry.mnemonic.find(
"CMP") != std::string::npos ||
404 entry.mnemonic.find(
"BIT") != std::string::npos ||
405 entry.mnemonic.find(
"TST") != std::string::npos) {
406 is_significant =
true;
410 if (is_significant) {
411 explanation << indent <<
" " << FormatSnesAddress(entry.address)
412 <<
": " << entry.mnemonic <<
" " << entry.operand_str
420 if (entry.address == trace[i - 1].address &&
421 IsBranchOrJump(entry.opcode)) {
422 explanation << indent <<
" ⚠️ POSSIBLE INFINITE LOOP DETECTED\n";
430 explanation << indent
431 <<
" ⚠️ RAPID DMA OPERATIONS - CHECK TIMING\n";
438 explanation <<
"\nSummary:\n";
439 explanation <<
"--------\n";
440 explanation <<
"Total instructions: " << trace.size() <<
"\n";
441 explanation <<
"Subroutine depth: " << indent_level <<
"\n";
442 if (!call_stack.empty()) {
443 explanation <<
"Unmatched calls: " << absl::StrJoin(call_stack,
", ")
447 return explanation.str();
450absl::StatusOr<RomDebugAgent::PatchComparisonResult>
452 const std::vector<uint8_t>& original) {
460 grpc::ServerContext context;
461 MemoryRequest mem_req;
462 mem_req.set_address(address);
463 mem_req.set_size(length);
464 MemoryResponse mem_resp;
468 return absl::InternalError(
"Failed to read patched memory");
471 result.
patched_code.assign(mem_resp.data().begin(), mem_resp.data().end());
474 std::stringstream orig_disasm, patch_disasm;
475 size_t orig_offset = 0, patch_offset = 0;
477 while (orig_offset < original.size() &&
480 std::string orig_mnem, orig_operand;
481 std::vector<uint8_t> orig_operands;
483 address + orig_offset, original.data() + orig_offset, orig_mnem,
484 orig_operand, orig_operands);
486 orig_disasm << FormatSnesAddress(address + orig_offset) <<
": " << orig_mnem
487 <<
" " << orig_operand <<
"\n";
490 std::string patch_mnem, patch_operand;
491 std::vector<uint8_t> patch_operands;
493 address + patch_offset, result.
patched_code.data() + patch_offset,
494 patch_mnem, patch_operand, patch_operands);
496 patch_disasm << FormatSnesAddress(address + patch_offset) <<
": "
497 << patch_mnem <<
" " << patch_operand <<
"\n";
500 if (orig_mnem != patch_mnem || orig_operand != patch_operand) {
502 "At %s: '%s %s' → '%s %s'", FormatSnesAddress(address + orig_offset),
503 orig_mnem, orig_operand, patch_mnem, patch_operand));
508 if (IsBranchOrJump(result.
patched_code[patch_offset])) {
510 if (patch_operands.size() >= 2) {
511 target = patch_operands[0] | (patch_operands[1] << 8);
512 if (patch_operands.size() >= 3) {
513 target |= (patch_operands[2] << 16);
519 absl::StrFormat(
"Invalid jump target at %s: %s",
520 FormatSnesAddress(address + patch_offset),
521 FormatSnesAddress(target)));
527 bool orig_modifies_stack = ModifiesStack(original[orig_offset]);
528 bool patch_modifies_stack =
531 if (orig_modifies_stack != patch_modifies_stack) {
533 "Stack modification mismatch - may cause stack imbalance");
538 orig_offset += orig_size;
539 patch_offset += patch_size;
550 "Patch modifies critical system area - verify this is intentional");
563 "Patch contains %d NOPs - possible padding or removed code",
568 for (
size_t i = 0; i < result.
patched_code.size(); ++i) {
571 absl::StrFormat(
"BRK instruction at %s - usually indicates error",
572 FormatSnesAddress(address + i)));
581 uint32_t start_address, uint32_t end_address) {
582 std::vector<DetectedIssue> issues;
585 grpc::ServerContext context;
586 MemoryRequest mem_req;
587 mem_req.set_address(start_address);
588 mem_req.set_size(end_address - start_address);
589 MemoryResponse mem_resp;
596 const uint8_t* code =
597 reinterpret_cast<const uint8_t*
>(mem_resp.data().data());
598 size_t code_size = mem_resp.data().size();
601 while (offset < code_size) {
602 uint32_t current_addr = start_address + offset;
607 if (issue.has_value()) {
608 issues.push_back(issue.value());
612 uint8_t inst_size =
disassembler_->GetInstructionSize(code[offset]);
613 if (inst_size == 0) {
617 absl::StrFormat(
"Invalid opcode $%02X at %s", code[offset],
618 FormatSnesAddress(current_addr)),
619 "Check if this is data being executed as code", 5});
631 if (address < 0x008000) {
635 if (address >= 0x008000 && address < 0x7E0000) {
643 if (address >= 0x800000 && address < 0xC00000) {
653 uint32_t routine_end) {
655 grpc::ServerContext context;
656 MemoryRequest mem_req;
657 mem_req.set_address(routine_start);
658 mem_req.set_size(routine_end - routine_start);
659 MemoryResponse mem_resp;
666 const uint8_t* code =
667 reinterpret_cast<const uint8_t*
>(mem_resp.data().data());
668 size_t code_size = mem_resp.data().size();
674 while (offset < code_size) {
675 uint8_t opcode = code[offset];
706 if (IsReturn(opcode)) {
708 return stack_depth != 0;
711 uint8_t inst_size =
disassembler_->GetInstructionSize(opcode);
712 offset += (inst_size > 0) ? inst_size : 1;
716 return stack_depth != 0;
721 uint32_t end_address = address + length;
724 if ((address <= 0x00FFFF && end_address > 0x00FFE0)) {
729 if (address <= NMI_FLAG && end_address >
NMI_FLAG) {
734 if (address >= 0x002100 && address <= 0x00213F) {
747 if (address >= 0x7E0000 && address < 0x7E2000) {
749 if (address < 0x7E0100) {
767 if (address < 0x100) {
769 }
else if (address >= 0x100 && address < 0x200) {
793 uint32_t sprite_num = offset / 0x10;
794 description = absl::StrFormat(
"Sprite %d Data", sprite_num);
803 description = absl::StrFormat(
"DMA Channel %d", channel);
839 else if (address >= 0x008000 && address < 0x00FFFF) {
840 description = absl::StrFormat(
"ROM Bank $%02X", (address >> 16) & 0xFF);
843 }
else if (address >= 0x800000 && address < 0xC00000) {
845 absl::StrFormat(
"Extended ROM Bank $%02X", (address >> 16) & 0xFF);
869 }
else if (address >= 0x002100 && address <= 0x00213F) {
877 }
else if (address >= 0x008000 && address < 0x7E0000) {
890 const std::map<std::string, uint16_t>& regs)
const {
891 std::stringstream ss;
893 ss << absl::StrFormat(
"A=%04X ", regs.at(
"A"));
894 ss << absl::StrFormat(
"X=%04X ", regs.at(
"X"));
895 ss << absl::StrFormat(
"Y=%04X ", regs.at(
"Y"));
896 ss << absl::StrFormat(
"S=%04X ", regs.at(
"S"));
897 ss << absl::StrFormat(
"PC=%04X ", regs.at(
"PC"));
898 ss << absl::StrFormat(
"P=%02X ", regs.at(
"P"));
899 ss << absl::StrFormat(
"DB=%02X ", regs.at(
"DB"));
900 ss << absl::StrFormat(
"PB=%02X", regs.at(
"PB"));
915 uint32_t address,
const uint8_t* code,
size_t max_length) {
916 if (max_length == 0) {
917 return absl::InvalidArgumentError(
"No code provided");
920 uint8_t opcode = code[0];
921 std::string explanation;
927 explanation =
"Load immediate value into accumulator";
930 explanation =
"Load immediate value into X register";
933 explanation =
"Load immediate value into Y register";
936 explanation =
"Load accumulator from absolute address";
939 explanation =
"Load X register from absolute address";
942 explanation =
"Load Y register from absolute address";
947 explanation =
"Store accumulator to absolute address";
950 explanation =
"Store X register to absolute address";
953 explanation =
"Store Y register to absolute address";
956 explanation =
"Store accumulator to address indexed by X";
959 explanation =
"Store accumulator to address indexed by Y";
964 explanation =
"Branch if plus (N flag clear)";
967 explanation =
"Branch if minus (N flag set)";
970 explanation =
"Branch if overflow clear";
973 explanation =
"Branch if overflow set";
976 explanation =
"Branch always";
979 explanation =
"Branch if carry clear (less than)";
982 explanation =
"Branch if carry set (greater than or equal)";
985 explanation =
"Branch if not equal (Z flag clear)";
988 explanation =
"Branch if equal (Z flag set)";
993 explanation =
"Call subroutine";
996 explanation =
"Call long subroutine (24-bit)";
999 explanation =
"Jump to address";
1002 explanation =
"Jump long (24-bit)";
1005 explanation =
"Return from subroutine";
1008 explanation =
"Return from long subroutine";
1013 explanation =
"Push accumulator onto stack";
1016 explanation =
"Pull accumulator from stack";
1019 explanation =
"Push processor status onto stack";
1022 explanation =
"Pull processor status from stack";
1027 explanation =
"Add to accumulator with carry";
1030 explanation =
"Subtract from accumulator with borrow";
1033 explanation =
"Compare accumulator with value";
1036 explanation =
"Compare X register with value";
1039 explanation =
"Compare Y register with value";
1044 explanation =
"AND accumulator with value";
1047 explanation =
"OR accumulator with value";
1050 explanation =
"XOR accumulator with value";
1055 explanation =
"Software interrupt (BRK)";
1058 explanation =
"No operation (NOP)";
1061 explanation =
"Clear carry flag";
1064 explanation =
"Set carry flag";
1067 explanation =
"Clear processor flags (REP)";
1070 explanation =
"Set processor flags (SEP)";
1074 explanation = absl::StrFormat(
"Execute opcode $%02X", opcode);
1083 std::vector<std::string> context_lines;
1086 grpc::ServerContext ctx;
1087 DisassemblyRequest req;
1088 req.set_start_address(
1089 address - (before_lines * 3));
1090 req.set_count(before_lines + after_lines + 1);
1091 DisassemblyResponse resp;
1095 return context_lines;
1098 for (
const auto& inst : resp.lines()) {
1100 absl::StrFormat(
"%s: %s %s", FormatSnesAddress(inst.address()),
1101 inst.mnemonic(), inst.operand_str());
1102 if (inst.address() == address) {
1103 line =
">>> " + line +
" <<<";
1105 context_lines.push_back(line);
1108 return context_lines;
1112 std::vector<std::string> stack;
1115 grpc::ServerContext ctx;
1116 yaze::agent::TraceRequest req;
1117 req.set_max_entries(100);
1118 yaze::agent::TraceResponse resp;
1126 for (
int i = resp.entries_size() - 1; i >= 0; --i) {
1127 const auto& entry = resp.entries(i);
1128 uint8_t opcode = entry.opcode();
1130 if (opcode == 0x20 || opcode == 0x22 || opcode == 0xFC) {
1132 std::string caller =
1135 : FormatSnesAddress(entry.address());
1136 stack.push_back(caller);
1137 }
else if (IsReturn(opcode) && !stack.empty()) {
1144 std::reverse(stack.begin(), stack.end());
1150 uint32_t address,
const uint8_t* code,
size_t length) {
1152 return std::nullopt;
1155 uint8_t opcode = code[0];
1158 if (opcode == 0x00) {
1161 "BRK instruction found - usually indicates an error or unimplemented "
1163 "Replace with proper implementation or NOP if intentional padding", 4};
1167 if (opcode == 0x80 && length >= 2 && code[1] == 0xFE) {
1170 "Infinite loop detected (BRA to self)",
1171 "Add proper exit condition or loop counter", 5};
1175 if (opcode == 0x8D && length >= 3) {
1176 uint16_t dest = code[1] | (code[2] << 8);
1177 if (dest >= 0xFFE0) {
1180 absl::StrFormat(
"Writing to interrupt vector at $%04X", dest),
1181 "Verify this vector modification is intentional", 5};
1186 if (ModifiesStack(opcode)) {
1189 int consecutive_pushes = 0;
1190 for (
size_t i = 0; i < std::min(
size_t(10), length); ++i) {
1191 if (code[i] == 0x48 || code[i] == 0x08 || code[i] == 0xDA ||
1193 consecutive_pushes++;
1194 }
else if (code[i] == 0x68 || code[i] == 0x28 || code[i] == 0xFA ||
1196 consecutive_pushes--;
1200 if (consecutive_pushes > 5) {
1203 "Multiple consecutive pushes without pulls - possible stack overflow",
1204 "Verify stack operations are properly balanced", 3};
1208 return std::nullopt;
1213 if (address < 0x200) {
1218 if (address >= 0xFFE0 && address <= 0xFFFF) {
1236 uint32_t address)
const {
1242 return "Sprite State";
1244 return "Sprite X Position Low";
1246 return "Sprite X Position High";
1248 return "Sprite Y Position Low";
1250 return "Sprite Y Position High";
1252 return "Sprite Z Position";
1254 return "Sprite Velocity X";
1256 return "Sprite Velocity Y";
1258 return "Sprite Type";
1260 return "Sprite Subtype";
1262 return "Sprite Graphics";
1264 return "Sprite Properties";
1266 return "Sprite Health";
1268 return "Sprite Damage";
1270 return "Sprite Timer";
1272 return "Sprite Flags";
1278 uint32_t offset = (address -
OAM_BUFFER) % 4;
1281 return "OAM X Position";
1283 return "OAM Y Position";
1285 return "OAM Tile Number";
1287 return "OAM Attributes";
1296 return "DMA Control";
1298 return "DMA Destination";
1300 return "DMA Source Low";
1302 return "DMA Source High";
1304 return "DMA Source Bank";
1306 return "DMA Size Low";
1308 return "DMA Size High";
1310 return "DMA Indirect Bank";
1314 return std::nullopt;
static constexpr uint32_t FRAME_COUNTER
yaze::net::EmulatorServiceImpl * emulator_service_
void SetOriginalRom(const std::vector< uint8_t > &rom_data)
Set the original ROM data for comparison.
static constexpr uint32_t OAM_BUFFER
static constexpr uint32_t PLAYER_HEALTH
static constexpr uint32_t LINK_Y_POS
static constexpr uint32_t OAM_BUFFER_END
static constexpr uint32_t DMA_ENABLE
absl::StatusOr< std::string > ExplainExecutionTrace(const std::vector< ExecutionTraceBuffer::TraceEntry > &trace)
Analyze execution trace and explain program flow.
static constexpr uint32_t WRAM_END
absl::StatusOr< BreakpointAnalysis > AnalyzeBreakpoint(const yaze::agent::BreakpointHitResponse &hit)
Analyze a breakpoint hit with full context.
static constexpr uint32_t APU_PORT0
bool IsMemoryWriteSafe(uint32_t address, size_t length) const
Check if memory write is safe.
static constexpr uint32_t PPU_BGMODE
static constexpr uint32_t LINK_X_POS
std::string FormatRegisterState(const std::map< std::string, uint16_t > ®s) const
Format register state for debugging output.
static constexpr uint32_t SPRITE_TABLE_START
bool IsValidJumpTarget(uint32_t address) const
Check if an address is a valid jump target.
static constexpr uint32_t PPU_CGADD
static constexpr uint32_t INVENTORY_START
static constexpr uint32_t GAME_MODE
static constexpr uint32_t SRAM_END
std::optional< std::string > GetStructureInfo(uint32_t address) const
Get structure information for a memory address.
std::map< uint32_t, std::string > address_description_cache_
std::vector< DetectedIssue > ScanForIssues(uint32_t start_address, uint32_t end_address)
Scan for common ROM hacking issues in a code region.
static constexpr uint32_t SRAM_START
std::vector< std::string > GetDisassemblyContext(uint32_t address, int before_lines, int after_lines)
Get surrounding context for an address.
std::vector< std::string > BuildCallStack(uint32_t current_pc)
Build call stack from execution trace.
bool HasStackImbalance(uint32_t routine_start, uint32_t routine_end)
Detect stack imbalance in a subroutine.
std::vector< uint8_t > original_rom_
std::map< uint32_t, std::string > data_type_cache_
static constexpr uint32_t APU_PORT3
absl::Status LoadSymbols(const std::string &symbol_file)
Load symbol table for better disassembly.
absl::StatusOr< PatchComparisonResult > ComparePatch(uint32_t address, size_t length, const std::vector< uint8_t > &original)
Compare original ROM code with patched code.
std::unique_ptr< yaze::emu::debug::SymbolProvider > symbol_provider_
std::optional< DetectedIssue > DetectIssuePattern(uint32_t address, const uint8_t *code, size_t length)
Detect pattern of common issues.
RomDebugAgent(yaze::net::EmulatorServiceImpl *emulator_service)
static constexpr uint32_t SPRITE_TABLE_END
std::string DescribeMemoryLocation(uint32_t address) const
Get human-readable description of a memory address.
static constexpr uint32_t SUBMODULE
absl::StatusOr< std::string > AnalyzeInstruction(uint32_t address, const uint8_t *code, size_t max_length)
Analyze the instruction at an address.
static constexpr uint32_t PPU_INIDISP
bool IsCriticalMemoryArea(uint32_t address) const
Check if address is in a critical system area.
static constexpr uint32_t NMI_FLAG
absl::StatusOr< MemoryAnalysis > AnalyzeMemory(uint32_t address, size_t length)
Analyze a memory region and identify its purpose.
static constexpr uint32_t LINK_STATE
static constexpr uint32_t PLAYER_NAME
static constexpr uint32_t HDMA_ENABLE
static constexpr uint32_t WRAM_START
static constexpr uint32_t PPU_CGDATA
static constexpr uint32_t PLAYER_MAX_HEALTH
static constexpr uint32_t DMA0_CONTROL
std::unique_ptr< Disassembler65816 > disassembler_
static constexpr uint32_t LINK_DIRECTION
std::string IdentifyDataType(uint32_t address) const
Get the data type at a memory address.
grpc::Status GetDisassembly(grpc::ServerContext *context, const agent::DisassemblyRequest *request, agent::DisassemblyResponse *response) override
grpc::Status ReadMemory(grpc::ServerContext *context, const agent::MemoryRequest *request, agent::MemoryResponse *response) override
grpc::Status GetExecutionTrace(grpc::ServerContext *context, const agent::TraceRequest *request, agent::TraceResponse *response) override
bool IsReturn(uint8_t opcode)
std::string FormatSnesAddress(uint32_t address)
bool ModifiesStack(uint8_t opcode)
bool IsBranchOrJump(uint8_t opcode)
Analysis result for a breakpoint hit.
std::map< std::string, uint16_t > registers
std::vector< std::string > context_lines
std::string location_description
std::vector< std::string > suggestions
std::string instruction_explanation
std::vector< std::string > call_stack
Detected issue in ROM execution.
Analysis of a memory region.
std::string structure_name
std::vector< std::string > anomalies
std::vector< uint8_t > data
std::map< std::string, uint32_t > fields
Results from patch comparison.
std::vector< std::string > differences
std::vector< std::string > potential_issues
std::vector< uint8_t > patched_code
std::vector< uint8_t > original_code
std::string patched_disassembly
std::string original_disassembly