5#include <grpcpp/grpcpp.h>
10#include <unordered_set>
23 case agent::A:
return SnesButton::A;
24 case agent::B:
return SnesButton::B;
25 case agent::X:
return SnesButton::X;
26 case agent::Y:
return SnesButton::Y;
27 case agent::L:
return SnesButton::L;
28 case agent::R:
return SnesButton::R;
29 case agent::SELECT:
return SnesButton::SELECT;
30 case agent::START:
return SnesButton::START;
31 case agent::UP:
return SnesButton::UP;
32 case agent::DOWN:
return SnesButton::DOWN;
33 case agent::LEFT:
return SnesButton::LEFT;
34 case agent::RIGHT:
return SnesButton::RIGHT;
35 default:
return SnesButton::B;
42 case agent::EXECUTE:
return BreakpointManager::Type::EXECUTE;
43 case agent::READ:
return BreakpointManager::Type::READ;
44 case agent::WRITE:
return BreakpointManager::Type::WRITE;
45 case agent::ACCESS:
return BreakpointManager::Type::ACCESS;
46 case agent::CONDITIONAL:
return BreakpointManager::Type::CONDITIONAL;
47 default:
return BreakpointManager::Type::EXECUTE;
54 case agent::CPU_65816:
return BreakpointManager::CpuType::CPU_65816;
55 case agent::SPC700:
return BreakpointManager::CpuType::SPC700;
56 default:
return BreakpointManager::CpuType::CPU_65816;
63 case BreakpointManager::Type::EXECUTE:
return agent::EXECUTE;
64 case BreakpointManager::Type::READ:
return agent::READ;
65 case BreakpointManager::Type::WRITE:
return agent::WRITE;
66 case BreakpointManager::Type::ACCESS:
return agent::ACCESS;
67 case BreakpointManager::Type::CONDITIONAL:
return agent::CONDITIONAL;
68 default:
return agent::EXECUTE;
75 case BreakpointManager::CpuType::CPU_65816:
return agent::CPU_65816;
76 case BreakpointManager::CpuType::SPC700:
return agent::SPC700;
77 default:
return agent::CPU_65816;
85 : emulator_(emulator), rom_getter_(rom_getter), rom_loader_(rom_loader) {}
90 grpc::ServerContext* context,
const agent::LoadRomRequest* request,
91 agent::LoadRomResponse* response) {
93 response->set_success(
false);
94 response->set_message(
"ROM loading not available (no loader callback set)");
95 return grpc::Status::OK;
98 const std::string& filepath = request->filepath();
99 if (filepath.empty()) {
100 response->set_success(
false);
101 response->set_message(
"Filepath is required");
102 return grpc::Status::OK;
107 response->set_success(
true);
108 response->set_message(
"ROM loaded successfully");
113 response->set_rom_title(rom->
title());
114 response->set_rom_size(rom->
size());
118 response->set_success(
false);
119 response->set_message(
"Failed to load ROM: " + filepath);
121 return grpc::Status::OK;
125 grpc::ServerContext* context,
const agent::Empty* request,
126 agent::LoadedRomPathResponse* response) {
128 response->set_has_rom(
false);
129 return grpc::Status::OK;
134 response->set_has_rom(
true);
135 response->set_filepath(rom->
filename());
136 response->set_title(rom->
title());
138 response->set_has_rom(
false);
140 return grpc::Status::OK;
148 state->set_pc(cpu.PC);
149 state->set_pb(cpu.PB);
150 state->set_db(cpu.DB);
151 state->set_sp(cpu.SP());
153 state->set_status(cpu.status);
154 state->set_flag_n(cpu.GetNegativeFlag());
155 state->set_flag_v(cpu.GetOverflowFlag());
156 state->set_flag_z(cpu.GetZeroFlag());
157 state->set_flag_c(cpu.GetCarryFlag());
164 const agent::ControlRequest* request,
165 agent::CommandResponse* response) {
166 if (!
emulator_)
return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"Emulator not initialized.");
168 std::string action = request->action();
169 if (action ==
"start" || action ==
"resume") {
171 response->set_message(
"Emulator started/resumed.");
172 }
else if (action ==
"stop" || action ==
"pause") {
174 response->set_message(
"Emulator stopped/paused.");
175 }
else if (action ==
"reset") {
177 response->set_message(
"Emulator reset.");
178 }
else if (action ==
"init" || action ==
"initialize") {
182 response->set_message(
"Emulator initialized with active ROM.");
184 return grpc::Status(grpc::StatusCode::FAILED_PRECONDITION,
"ROM not loaded in core.");
187 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
"Unknown action: " + action);
190 response->set_success(
true);
191 return grpc::Status::OK;
195 const agent::StepControlRequest* request,
196 agent::StepResponse* response) {
198 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"SNES is not initialized.");
201 std::string mode = request->mode();
202 if (mode ==
"instruction") {
204 response->set_message(
"Stepped 1 instruction.");
205 }
else if (mode ==
"over") {
208 response->set_message(result.message);
209 }
else if (mode ==
"out") {
212 response->set_message(result.message);
214 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
"Unknown step mode: " + mode);
218 response->set_success(
true);
219 return grpc::Status::OK;
223 const agent::Empty* request,
224 agent::BreakpointHitResponse* response) {
226 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"SNES is not initialized.");
229 const int kMaxInstructions = 1000000;
230 int instruction_count = 0;
234 while (instruction_count++ < kMaxInstructions) {
235 uint32_t pc = (cpu.PB << 16) | cpu.PC;
237 response->set_hit(
true);
238 auto* last_hit = bp_manager.GetLastHit();
240 auto* bp_info = response->mutable_breakpoint();
241 bp_info->set_id(last_hit->id);
242 bp_info->set_address(last_hit->address);
243 bp_info->set_type(ToProtoBreakpointType(last_hit->type));
244 bp_info->set_cpu(ToProtoCpuType(last_hit->cpu));
245 bp_info->set_enabled(last_hit->enabled);
248 return grpc::Status::OK;
252 response->set_hit(
false);
253 return grpc::Status::OK;
259 const agent::ButtonRequest* request,
260 agent::CommandResponse* response) {
261 if (!
emulator_)
return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"Emulator not initialized.");
263 for (
int i = 0; i < request->buttons_size(); i++) {
264 input_manager.
PressButton(ToSnesButton(
static_cast<agent::Button
>(request->buttons(i))));
266 std::this_thread::sleep_for(std::chrono::milliseconds(50));
267 for (
int i = 0; i < request->buttons_size(); i++) {
268 input_manager.ReleaseButton(ToSnesButton(
static_cast<agent::Button
>(request->buttons(i))));
270 response->set_success(
true);
271 return grpc::Status::OK;
275 const agent::ButtonRequest* request,
276 agent::CommandResponse* response) {
277 if (!
emulator_)
return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"Emulator not initialized.");
279 for (
int i = 0; i < request->buttons_size(); i++) {
280 input_manager.
ReleaseButton(ToSnesButton(
static_cast<agent::Button
>(request->buttons(i))));
282 response->set_success(
true);
283 return grpc::Status::OK;
287 const agent::ButtonHoldRequest* request,
288 agent::CommandResponse* response) {
289 if (!
emulator_)
return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"Emulator not initialized.");
291 for (
int i = 0; i < request->buttons_size(); i++) {
292 input_manager.
PressButton(ToSnesButton(
static_cast<agent::Button
>(request->buttons(i))));
294 std::this_thread::sleep_for(std::chrono::milliseconds(request->duration_ms()));
295 for (
int i = 0; i < request->buttons_size(); i++) {
296 input_manager.ReleaseButton(ToSnesButton(
static_cast<agent::Button
>(request->buttons(i))));
298 response->set_success(
true);
299 return grpc::Status::OK;
303 const agent::GameStateRequest* request,
304 agent::GameStateResponse* response) {
306 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"SNES is not initialized.");
309 response->set_game_mode(memory.ReadByte(0x7E0010));
310 response->set_link_state(memory.ReadByte(0x7E005D));
311 response->set_link_pos_x(memory.ReadWord(0x7E0020));
312 response->set_link_pos_y(memory.ReadWord(0x7E0022));
313 response->set_link_health(memory.ReadByte(0x7EF36D));
315 for (
const auto& mem_req : request->memory_reads()) {
316 auto* mem_resp = response->add_memory_responses();
317 mem_resp->set_address(mem_req.address());
318 std::vector<uint8_t> data(mem_req.size());
319 for (uint32_t i = 0; i < mem_req.size(); ++i) {
320 data[i] = memory.ReadByte(mem_req.address() + i);
322 mem_resp->set_data(data.data(), data.size());
326 if (request->include_screenshot()) {
327 auto screenshot = yaze::test::CaptureHarnessScreenshot();
328 if (screenshot.ok()) {
329 std::ifstream file(screenshot->file_path, std::ios::binary);
331 std::string png_data((std::istreambuf_iterator<char>(file)),
332 std::istreambuf_iterator<char>());
333 response->set_screenshot_png(png_data);
338 return grpc::Status::OK;
342 const agent::MemoryRequest* request,
343 agent::MemoryResponse* response) {
345 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"SNES is not initialized.");
349 response->set_address(request->address());
350 std::vector<uint8_t> data(request->size());
351 for (uint32_t i = 0; i < request->size(); ++i) {
352 data[i] = snes.Read(request->address() + i);
354 response->set_data(data.data(), data.size());
355 return grpc::Status::OK;
359 const agent::MemoryWriteRequest* request,
360 agent::CommandResponse* response) {
362 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"SNES is not initialized.");
365 const std::string& data = request->data();
366 for (uint32_t i = 0; i < data.size(); ++i) {
367 memory.WriteByte(request->address() + i,
static_cast<uint8_t
>(data[i]));
369 response->set_success(
true);
370 return grpc::Status::OK;
376 const agent::BreakpointControlRequest* request,
377 agent::BreakpointControlResponse* response) {
378 if (!
emulator_)
return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"Emulator not initialized.");
380 std::string action = request->action();
382 if (action ==
"add") {
383 uint32_t
id = bp_manager.
AddBreakpoint(request->address(), ToBreakpointType(request->type()), ToCpuType(request->cpu()), request->condition(), request->description());
384 response->set_breakpoint_id(
id);
385 response->set_message(
"Breakpoint added.");
386 }
else if (action ==
"remove") {
387 bp_manager.RemoveBreakpoint(request->id());
388 response->set_message(
"Breakpoint removed.");
389 }
else if (action ==
"toggle") {
390 bp_manager.SetEnabled(request->id(), request->enabled());
391 response->set_message(
"Breakpoint toggled.");
392 }
else if (action ==
"list") {
393 auto breakpoints = bp_manager.GetAllBreakpoints();
394 for (
const auto& bp : breakpoints) {
395 auto* info = response->add_breakpoints();
397 info->set_address(bp.address);
398 info->set_type(ToProtoBreakpointType(bp.type));
399 info->set_cpu(ToProtoCpuType(bp.cpu));
400 info->set_enabled(bp.enabled);
403 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
"Unknown action: " + action);
406 response->set_success(
true);
407 return grpc::Status::OK;
411 const agent::WatchpointControlRequest* request,
412 agent::WatchpointControlResponse* response) {
413 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
"WatchpointManager integration pending.");
419 const agent::DisassemblyRequest* request,
420 agent::DisassemblyResponse* response) {
427 uint32_t addr = request->start_address();
428 for (uint32_t i = 0; i < request->count(); ++i) {
429 auto inst = dis.
Disassemble(addr, [&memory](uint32_t a){
return memory.ReadByte(a); }, cpu.GetAccumulatorSize(), cpu.GetIndexSize());
430 auto* line = response->add_lines();
431 line->set_address(inst.address);
432 line->set_mnemonic(inst.mnemonic);
433 line->set_operand_str(inst.operand_str);
436 return grpc::Status::OK;
440 const agent::TraceRequest* request,
441 agent::TraceResponse* response) {
442 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
"Trace not implemented.");
446 const agent::SymbolLookupRequest* request,
447 agent::SymbolLookupResponse* response) {
450 response->set_found(
true);
451 response->set_symbol_name(sym->name);
452 response->set_address(sym->address);
454 return grpc::Status::OK;
458 const agent::AddressRequest* request,
459 agent::SymbolLookupResponse* response) {
462 response->set_found(
true);
463 response->set_symbol_name(sym->name);
464 response->set_address(sym->address);
466 return grpc::Status::OK;
470 const agent::SymbolFileRequest* request,
471 agent::CommandResponse* response) {
473 response->set_success(status.ok());
474 response->set_message(std::string(status.message()));
475 return grpc::Status::OK;
481 const agent::Empty* request,
482 agent::DebugStatusResponse* response) {
483 if (!
emulator_)
return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"Emulator not initialized.");
487 return grpc::Status::OK;
491 const agent::TestRunRequest* request,
492 agent::TestRunResponse* response) {
495 auto& cpu = snes.cpu();
496 auto& memory = snes.memory();
498 uint32_t addr = request->address();
499 const std::string& data = request->data();
500 for (
size_t i = 0; i < data.size(); ++i) { memory.WriteByte(addr + i, (uint8_t)data[i]); }
504 cpu.PB = (addr >> 16) & 0xFF;
505 cpu.PC = addr & 0xFFFF;
507 uint32_t frames = request->frame_count() > 0 ? request->frame_count() : 60;
508 for (uint32_t i = 0; i < frames; ++i) {
510 if (cpu.PC == 0 && cpu.PB == 0) { response->set_crashed(
true);
break; }
515 response->set_success(!response->crashed());
516 return grpc::Status::OK;
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Manages CPU and SPC700 breakpoints for debugging.
std::vector< Breakpoint > GetAllBreakpoints() const
Get all breakpoints.
uint32_t AddBreakpoint(uint32_t address, Type type, CpuType cpu, const std::string &condition="", const std::string &description="")
Add a new breakpoint.
A class for emulating and debugging SNES games.
BreakpointManager & breakpoint_manager()
uint64_t GetCurrentCycle()
void StepSingleInstruction()
input::InputManager & input_manager()
bool EnsureInitialized(Rom *rom)
void set_running(bool running)
bool is_snes_initialized() const
auto running() const -> bool
65816 CPU disassembler for debugging and ROM hacking
DisassembledInstruction Disassemble(uint32_t address, MemoryReader read_byte, bool m_flag=true, bool x_flag=true) const
Disassemble a single instruction.
void SetMemoryReader(MemoryReader reader)
void SetPcGetter(PcGetter getter)
StepResult StepOver(uint32_t max_instructions=1000000)
Step over the current instruction.
void SetSingleStepper(SingleStepper stepper)
StepResult StepOut(uint32_t max_instructions=1000000)
Step out of the current subroutine.
absl::Status LoadSymbolFile(const std::string &path, SymbolFormat format=SymbolFormat::kAuto)
Load symbols from a .sym file (various formats)
std::optional< Symbol > GetSymbol(uint32_t address) const
Get full symbol info for an address.
std::optional< Symbol > FindSymbol(const std::string &name) const
Find symbol by name.
grpc::Status GetSymbolAt(grpc::ServerContext *context, const agent::AddressRequest *request, agent::SymbolLookupResponse *response) override
std::function< bool(const std::string &path)> RomLoader
grpc::Status ControlEmulator(grpc::ServerContext *context, const agent::ControlRequest *request, agent::CommandResponse *response) override
yaze::emu::debug::StepController step_controller_
grpc::Status TestRun(grpc::ServerContext *context, const agent::TestRunRequest *request, agent::TestRunResponse *response) override
grpc::Status GetDebugStatus(grpc::ServerContext *context, const agent::Empty *request, agent::DebugStatusResponse *response) override
grpc::Status WriteMemory(grpc::ServerContext *context, const agent::MemoryWriteRequest *request, agent::CommandResponse *response) override
grpc::Status ReleaseButtons(grpc::ServerContext *context, const agent::ButtonRequest *request, agent::CommandResponse *response) override
grpc::Status PressButtons(grpc::ServerContext *context, const agent::ButtonRequest *request, agent::CommandResponse *response) override
grpc::Status GetGameState(grpc::ServerContext *context, const agent::GameStateRequest *request, agent::GameStateResponse *response) override
grpc::Status BreakpointControl(grpc::ServerContext *context, const agent::BreakpointControlRequest *request, agent::BreakpointControlResponse *response) override
grpc::Status GetDisassembly(grpc::ServerContext *context, const agent::DisassemblyRequest *request, agent::DisassemblyResponse *response) override
grpc::Status LoadSymbols(grpc::ServerContext *context, const agent::SymbolFileRequest *request, agent::CommandResponse *response) override
yaze::emu::debug::SymbolProvider symbol_provider_
void InitializeStepController()
grpc::Status ReadMemory(grpc::ServerContext *context, const agent::MemoryRequest *request, agent::MemoryResponse *response) override
grpc::Status StepEmulator(grpc::ServerContext *context, const agent::StepControlRequest *request, agent::StepResponse *response) override
grpc::Status HoldButtons(grpc::ServerContext *context, const agent::ButtonHoldRequest *request, agent::CommandResponse *response) override
grpc::Status LoadRom(grpc::ServerContext *context, const agent::LoadRomRequest *request, agent::LoadRomResponse *response) override
EmulatorServiceImpl(yaze::emu::Emulator *emulator, RomGetter rom_getter=nullptr, RomLoader rom_loader=nullptr)
grpc::Status GetLoadedRomPath(grpc::ServerContext *context, const agent::Empty *request, agent::LoadedRomPathResponse *response) override
std::function< Rom *()> RomGetter
grpc::Status ResolveSymbol(grpc::ServerContext *context, const agent::SymbolLookupRequest *request, agent::SymbolLookupResponse *response) override
yaze::emu::Emulator * emulator_
grpc::Status GetExecutionTrace(grpc::ServerContext *context, const agent::TraceRequest *request, agent::TraceResponse *response) override
grpc::Status RunToBreakpoint(grpc::ServerContext *context, const agent::Empty *request, agent::BreakpointHitResponse *response) override
void CaptureCPUState(agent::CPUState *state)
grpc::Status WatchpointControl(grpc::ServerContext *context, const agent::WatchpointControlRequest *request, agent::WatchpointControlResponse *response) override
SymbolFormat
Supported symbol file formats.
agent::CpuType ToProtoCpuType(emu::BreakpointManager::CpuType cpu)
agent::BreakpointType ToProtoBreakpointType(emu::BreakpointManager::Type type)
emu::BreakpointManager::Type ToBreakpointType(agent::BreakpointType proto_type)
emu::BreakpointManager::CpuType ToCpuType(agent::CpuType proto_cpu)
emu::input::SnesButton ToSnesButton(agent::Button button)