8#include "absl/strings/str_format.h"
9#include "absl/strings/str_join.h"
10#include "absl/strings/str_split.h"
17using namespace yaze::agent;
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 <<
" ⚠️ RAPID DMA OPERATIONS - CHECK TIMING\n";
437 explanation <<
"\nSummary:\n";
438 explanation <<
"--------\n";
439 explanation <<
"Total instructions: " << trace.size() <<
"\n";
440 explanation <<
"Subroutine depth: " << indent_level <<
"\n";
441 if (!call_stack.empty()) {
442 explanation <<
"Unmatched calls: " << absl::StrJoin(call_stack,
", ")
446 return explanation.str();
449absl::StatusOr<RomDebugAgent::PatchComparisonResult>
451 const std::vector<uint8_t>& original) {
459 grpc::ServerContext context;
460 MemoryRequest mem_req;
461 mem_req.set_address(address);
462 mem_req.set_size(length);
463 MemoryResponse mem_resp;
467 return absl::InternalError(
"Failed to read patched memory");
470 result.
patched_code.assign(mem_resp.data().begin(), mem_resp.data().end());
473 std::stringstream orig_disasm, patch_disasm;
474 size_t orig_offset = 0, patch_offset = 0;
476 while (orig_offset < original.size() &&
479 std::string orig_mnem, orig_operand;
480 std::vector<uint8_t> orig_operands;
482 address + orig_offset, original.data() + orig_offset, orig_mnem,
483 orig_operand, orig_operands);
485 orig_disasm << FormatSnesAddress(address + orig_offset) <<
": " << orig_mnem
486 <<
" " << orig_operand <<
"\n";
489 std::string patch_mnem, patch_operand;
490 std::vector<uint8_t> patch_operands;
492 address + patch_offset, result.
patched_code.data() + patch_offset,
493 patch_mnem, patch_operand, patch_operands);
495 patch_disasm << FormatSnesAddress(address + patch_offset) <<
": "
496 << patch_mnem <<
" " << patch_operand <<
"\n";
499 if (orig_mnem != patch_mnem || orig_operand != patch_operand) {
501 "At %s: '%s %s' → '%s %s'", FormatSnesAddress(address + orig_offset),
502 orig_mnem, orig_operand, patch_mnem, patch_operand));
507 if (IsBranchOrJump(result.
patched_code[patch_offset])) {
509 if (patch_operands.size() >= 2) {
510 target = patch_operands[0] | (patch_operands[1] << 8);
511 if (patch_operands.size() >= 3) {
512 target |= (patch_operands[2] << 16);
518 absl::StrFormat(
"Invalid jump target at %s: %s",
519 FormatSnesAddress(address + patch_offset),
520 FormatSnesAddress(target)));
526 bool orig_modifies_stack = ModifiesStack(original[orig_offset]);
527 bool patch_modifies_stack =
530 if (orig_modifies_stack != patch_modifies_stack) {
532 "Stack modification mismatch - may cause stack imbalance");
537 orig_offset += orig_size;
538 patch_offset += patch_size;
549 "Patch modifies critical system area - verify this is intentional");
562 "Patch contains %d NOPs - possible padding or removed code",
567 for (
size_t i = 0; i < result.
patched_code.size(); ++i) {
570 absl::StrFormat(
"BRK instruction at %s - usually indicates error",
571 FormatSnesAddress(address + i)));
580 uint32_t start_address, uint32_t end_address) {
581 std::vector<DetectedIssue> issues;
584 grpc::ServerContext context;
585 MemoryRequest mem_req;
586 mem_req.set_address(start_address);
587 mem_req.set_size(end_address - start_address);
588 MemoryResponse mem_resp;
595 const uint8_t* code =
596 reinterpret_cast<const uint8_t*
>(mem_resp.data().data());
597 size_t code_size = mem_resp.data().size();
600 while (offset < code_size) {
601 uint32_t current_addr = start_address + offset;
606 if (issue.has_value()) {
607 issues.push_back(issue.value());
611 uint8_t inst_size =
disassembler_->GetInstructionSize(code[offset]);
612 if (inst_size == 0) {
616 absl::StrFormat(
"Invalid opcode $%02X at %s", code[offset],
617 FormatSnesAddress(current_addr)),
618 "Check if this is data being executed as code", 5});
630 if (address < 0x008000) {
634 if (address >= 0x008000 && address < 0x7E0000) {
642 if (address >= 0x800000 && address < 0xC00000) {
652 uint32_t routine_end) {
654 grpc::ServerContext context;
655 MemoryRequest mem_req;
656 mem_req.set_address(routine_start);
657 mem_req.set_size(routine_end - routine_start);
658 MemoryResponse mem_resp;
665 const uint8_t* code =
666 reinterpret_cast<const uint8_t*
>(mem_resp.data().data());
667 size_t code_size = mem_resp.data().size();
673 while (offset < code_size) {
674 uint8_t opcode = code[offset];
705 if (IsReturn(opcode)) {
707 return stack_depth != 0;
710 uint8_t inst_size =
disassembler_->GetInstructionSize(opcode);
711 offset += (inst_size > 0) ? inst_size : 1;
715 return stack_depth != 0;
720 uint32_t end_address = address + length;
723 if ((address <= 0x00FFFF && end_address > 0x00FFE0)) {
728 if (address <= NMI_FLAG && end_address >
NMI_FLAG) {
733 if (address >= 0x002100 && address <= 0x00213F) {
746 if (address >= 0x7E0000 && address < 0x7E2000) {
748 if (address < 0x7E0100) {
766 if (address < 0x100) {
768 }
else if (address >= 0x100 && address < 0x200) {
792 uint32_t sprite_num = offset / 0x10;
793 description = absl::StrFormat(
"Sprite %d Data", sprite_num);
802 description = absl::StrFormat(
"DMA Channel %d", channel);
838 else if (address >= 0x008000 && address < 0x00FFFF) {
839 description = absl::StrFormat(
"ROM Bank $%02X", (address >> 16) & 0xFF);
842 }
else if (address >= 0x800000 && address < 0xC00000) {
844 absl::StrFormat(
"Extended ROM Bank $%02X", (address >> 16) & 0xFF);
868 }
else if (address >= 0x002100 && address <= 0x00213F) {
880 }
else if (address >= 0x008000 && address < 0x7E0000) {
893 const std::map<std::string, uint16_t>& regs)
const {
894 std::stringstream ss;
896 ss << absl::StrFormat(
"A=%04X ", regs.at(
"A"));
897 ss << absl::StrFormat(
"X=%04X ", regs.at(
"X"));
898 ss << absl::StrFormat(
"Y=%04X ", regs.at(
"Y"));
899 ss << absl::StrFormat(
"S=%04X ", regs.at(
"S"));
900 ss << absl::StrFormat(
"PC=%04X ", regs.at(
"PC"));
901 ss << absl::StrFormat(
"P=%02X ", regs.at(
"P"));
902 ss << absl::StrFormat(
"DB=%02X ", regs.at(
"DB"));
903 ss << absl::StrFormat(
"PB=%02X", regs.at(
"PB"));
918 uint32_t address,
const uint8_t* code,
size_t max_length) {
919 if (max_length == 0) {
920 return absl::InvalidArgumentError(
"No code provided");
923 uint8_t opcode = code[0];
924 std::string explanation;
930 explanation =
"Load immediate value into accumulator";
933 explanation =
"Load immediate value into X register";
936 explanation =
"Load immediate value into Y register";
939 explanation =
"Load accumulator from absolute address";
942 explanation =
"Load X register from absolute address";
945 explanation =
"Load Y register from absolute address";
950 explanation =
"Store accumulator to absolute address";
953 explanation =
"Store X register to absolute address";
956 explanation =
"Store Y register to absolute address";
959 explanation =
"Store accumulator to address indexed by X";
962 explanation =
"Store accumulator to address indexed by Y";
967 explanation =
"Branch if plus (N flag clear)";
970 explanation =
"Branch if minus (N flag set)";
973 explanation =
"Branch if overflow clear";
976 explanation =
"Branch if overflow set";
979 explanation =
"Branch always";
982 explanation =
"Branch if carry clear (less than)";
985 explanation =
"Branch if carry set (greater than or equal)";
988 explanation =
"Branch if not equal (Z flag clear)";
991 explanation =
"Branch if equal (Z flag set)";
996 explanation =
"Call subroutine";
999 explanation =
"Call long subroutine (24-bit)";
1002 explanation =
"Jump to address";
1005 explanation =
"Jump long (24-bit)";
1008 explanation =
"Return from subroutine";
1011 explanation =
"Return from long subroutine";
1016 explanation =
"Push accumulator onto stack";
1019 explanation =
"Pull accumulator from stack";
1022 explanation =
"Push processor status onto stack";
1025 explanation =
"Pull processor status from stack";
1030 explanation =
"Add to accumulator with carry";
1033 explanation =
"Subtract from accumulator with borrow";
1036 explanation =
"Compare accumulator with value";
1039 explanation =
"Compare X register with value";
1042 explanation =
"Compare Y register with value";
1047 explanation =
"AND accumulator with value";
1050 explanation =
"OR accumulator with value";
1053 explanation =
"XOR accumulator with value";
1058 explanation =
"Software interrupt (BRK)";
1061 explanation =
"No operation (NOP)";
1064 explanation =
"Clear carry flag";
1067 explanation =
"Set carry flag";
1070 explanation =
"Clear processor flags (REP)";
1073 explanation =
"Set processor flags (SEP)";
1077 explanation = absl::StrFormat(
"Execute opcode $%02X", opcode);
1086 std::vector<std::string> context_lines;
1089 grpc::ServerContext ctx;
1090 DisassemblyRequest req;
1091 req.set_start_address(
1092 address - (before_lines * 3));
1093 req.set_count(before_lines + after_lines + 1);
1094 DisassemblyResponse resp;
1098 return context_lines;
1101 for (
const auto& inst : resp.lines()) {
1103 absl::StrFormat(
"%s: %s %s", FormatSnesAddress(inst.address()),
1104 inst.mnemonic(), inst.operand_str());
1105 if (inst.address() == address) {
1106 line =
">>> " + line +
" <<<";
1108 context_lines.push_back(line);
1111 return context_lines;
1115 std::vector<std::string> stack;
1118 grpc::ServerContext ctx;
1119 yaze::agent::TraceRequest req;
1120 req.set_max_entries(100);
1121 yaze::agent::TraceResponse resp;
1129 for (
int i = resp.entries_size() - 1; i >= 0; --i) {
1130 const auto& entry = resp.entries(i);
1131 uint8_t opcode = entry.opcode();
1133 if (opcode == 0x20 || opcode == 0x22 || opcode == 0xFC) {
1135 std::string caller =
1138 : FormatSnesAddress(entry.address());
1139 stack.push_back(caller);
1140 }
else if (IsReturn(opcode) && !stack.empty()) {
1147 std::reverse(stack.begin(), stack.end());
1153 uint32_t address,
const uint8_t* code,
size_t length) {
1155 return std::nullopt;
1158 uint8_t opcode = code[0];
1161 if (opcode == 0x00) {
1164 "BRK instruction found - usually indicates an error or unimplemented "
1166 "Replace with proper implementation or NOP if intentional padding", 4};
1170 if (opcode == 0x80 && length >= 2 && code[1] == 0xFE) {
1173 "Infinite loop detected (BRA to self)",
1174 "Add proper exit condition or loop counter", 5};
1178 if (opcode == 0x8D && length >= 3) {
1179 uint16_t dest = code[1] | (code[2] << 8);
1180 if (dest >= 0xFFE0) {
1183 absl::StrFormat(
"Writing to interrupt vector at $%04X", dest),
1184 "Verify this vector modification is intentional", 5};
1189 if (ModifiesStack(opcode)) {
1192 int consecutive_pushes = 0;
1193 for (
size_t i = 0; i < std::min(
size_t(10), length); ++i) {
1194 if (code[i] == 0x48 || code[i] == 0x08 || code[i] == 0xDA ||
1196 consecutive_pushes++;
1197 }
else if (code[i] == 0x68 || code[i] == 0x28 || code[i] == 0xFA ||
1199 consecutive_pushes--;
1203 if (consecutive_pushes > 5) {
1206 "Multiple consecutive pushes without pulls - possible stack overflow",
1207 "Verify stack operations are properly balanced", 3};
1211 return std::nullopt;
1216 if (address < 0x200) {
1221 if (address >= 0xFFE0 && address <= 0xFFFF) {
1239 uint32_t address)
const {
1245 return "Sprite State";
1247 return "Sprite X Position Low";
1249 return "Sprite X Position High";
1251 return "Sprite Y Position Low";
1253 return "Sprite Y Position High";
1255 return "Sprite Z Position";
1257 return "Sprite Velocity X";
1259 return "Sprite Velocity Y";
1261 return "Sprite Type";
1263 return "Sprite Subtype";
1265 return "Sprite Graphics";
1267 return "Sprite Properties";
1269 return "Sprite Health";
1271 return "Sprite Damage";
1273 return "Sprite Timer";
1275 return "Sprite Flags";
1281 uint32_t offset = (address -
OAM_BUFFER) % 4;
1284 return "OAM X Position";
1286 return "OAM Y Position";
1288 return "OAM Tile Number";
1290 return "OAM Attributes";
1299 return "DMA Control";
1301 return "DMA Destination";
1303 return "DMA Source Low";
1305 return "DMA Source High";
1307 return "DMA Source Bank";
1309 return "DMA Size Low";
1311 return "DMA Size High";
1313 return "DMA Indirect Bank";
1317 return std::nullopt;
1323 std::shared_ptr<emu::mesen::MesenSocketClient> client) {
1333 return absl::UnavailableError(
"Mesen2 not connected");
1338absl::StatusOr<std::vector<emu::mesen::SpriteInfo>>
1341 return absl::UnavailableError(
"Mesen2 not connected");
1348 return absl::UnavailableError(
"Mesen2 not connected");
1353absl::StatusOr<RomDebugAgent::BreakpointAnalysis>
1356 return absl::UnavailableError(
"Mesen2 not connected");
1365 const auto& cpu = *cpu_or;
1378 if (disasm_or.ok()) {
1391 if (state_or.ok()) {
1392 const auto& state = *state_or;
1394 "Game Mode: %d Submode: %d Link at (%d,%d)", state.game.mode,
1395 state.game.submode, state.link.x, state.link.y);
1404 return state_or.status();
1406 const auto& state = *state_or;
1407 std::stringstream ss;
1408 ss <<
"Current Game State Analysis:\n";
1409 ss << absl::StrFormat(
1410 "- Link is at (%d, %d) in area 0x%02X\n", state.link.x, state.link.y,
1411 state.game.indoors ? state.game.room_id : state.game.overworld_area);
1412 ss << absl::StrFormat(
"- Mode: %d (Submode: %d)\n", state.game.mode,
1413 state.game.submode);
1414 ss << absl::StrFormat(
"- Direction: %d, Health: %d/%d\n",
1415 state.link.direction, state.items.current_health,
1416 state.items.max_health);
1419 if (state.game.mode == 0x07 || state.game.mode == 0x09) {
1420 ss <<
"- Link is currently in the overworld/dungeon transition.\n";
1423 if (state.items.current_health <= 2) {
1424 ss <<
"- Warning: Link is at low health!\n";
1429 if (sprites_or.ok() && !sprites_or->empty()) {
1430 ss << absl::StrFormat(
"- %zu active sprites nearby.\n", sprites_or->size());
1431 for (
const auto& sprite : *sprites_or) {
1432 if (sprite.health > 0) {
1433 ss << absl::StrFormat(
" * Sprite 0x%02X at (%d,%d) HP=%d\n",
1434 sprite.type, sprite.x, sprite.y, sprite.health);
1443 std::vector<std::string> anomalies;
1445 if (!sprites_or.ok())
1448 for (
const auto& sprite : *sprites_or) {
1449 if (sprite.state != 0) {
1451 if (sprite.x > 512 || sprite.y > 512) {
1452 anomalies.push_back(absl::StrFormat(
1453 "Sprite %d (Type 0x%02X) is significantly off-screen at (%d,%d)",
1454 sprite.slot, sprite.type, sprite.x, sprite.y));
1458 if (sprite.health > 200 && sprite.health != 255) {
1459 anomalies.push_back(
1460 absl::StrFormat(
"Sprite %d has unusually high health: %d",
1461 sprite.slot, sprite.health));
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
absl::StatusOr< emu::mesen::CpuState > GetLiveCpuState()
Get live CPU state from Mesen2.
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
std::vector< std::string > AnalyzeSpriteAnomalies()
Detect anomalies in current sprite table.
absl::StatusOr< BreakpointAnalysis > AnalyzeLiveBreakpoint(uint32_t address)
Analyze a live breakpoint hit from Mesen2.
static constexpr uint32_t INVENTORY_START
static constexpr uint32_t GAME_MODE
absl::StatusOr< std::string > ExplainCurrentGameState()
Generate AI-friendly explanation of current game state.
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::StatusOr< std::vector< emu::mesen::SpriteInfo > > GetLiveSprites(bool all=false)
Get live sprite list from Mesen2.
void SetMesenClient(std::shared_ptr< emu::mesen::MesenSocketClient > client)
Set the Mesen2 socket client for live debugging.
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::shared_ptr< emu::mesen::MesenSocketClient > mesen_client_
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
absl::StatusOr< emu::mesen::GameState > GetLiveGameState()
Get live game state from Mesen2.
bool IsMesenConnected() const
Check if connected to Mesen2.
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::string memory_context
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