9#include "absl/strings/str_format.h"
19 if (status.ok())
return grpc::Status::OK;
21 grpc::StatusCode code = grpc::StatusCode::UNKNOWN;
22 switch (status.code()) {
23 case absl::StatusCode::kOk: code = grpc::StatusCode::OK;
break;
24 case absl::StatusCode::kCancelled: code = grpc::StatusCode::CANCELLED;
break;
25 case absl::StatusCode::kUnknown: code = grpc::StatusCode::UNKNOWN;
break;
26 case absl::StatusCode::kInvalidArgument: code = grpc::StatusCode::INVALID_ARGUMENT;
break;
27 case absl::StatusCode::kDeadlineExceeded: code = grpc::StatusCode::DEADLINE_EXCEEDED;
break;
28 case absl::StatusCode::kNotFound: code = grpc::StatusCode::NOT_FOUND;
break;
29 case absl::StatusCode::kAlreadyExists: code = grpc::StatusCode::ALREADY_EXISTS;
break;
30 case absl::StatusCode::kPermissionDenied: code = grpc::StatusCode::PERMISSION_DENIED;
break;
31 case absl::StatusCode::kUnauthenticated: code = grpc::StatusCode::UNAUTHENTICATED;
break;
32 case absl::StatusCode::kResourceExhausted: code = grpc::StatusCode::RESOURCE_EXHAUSTED;
break;
33 case absl::StatusCode::kFailedPrecondition: code = grpc::StatusCode::FAILED_PRECONDITION;
break;
34 case absl::StatusCode::kAborted: code = grpc::StatusCode::ABORTED;
break;
35 case absl::StatusCode::kOutOfRange: code = grpc::StatusCode::OUT_OF_RANGE;
break;
36 case absl::StatusCode::kUnimplemented: code = grpc::StatusCode::UNIMPLEMENTED;
break;
37 case absl::StatusCode::kInternal: code = grpc::StatusCode::INTERNAL;
break;
38 case absl::StatusCode::kUnavailable: code = grpc::StatusCode::UNAVAILABLE;
break;
39 case absl::StatusCode::kDataLoss: code = grpc::StatusCode::DATA_LOSS;
break;
40 default: code = grpc::StatusCode::UNKNOWN;
break;
42 return grpc::Status(code, std::string(status.message()));
49 : emulator_(emulator), rom_getter_(rom_getter), rom_loader_(rom_loader) {}
54 const agent::LoadRomRequest* request,
55 agent::LoadRomResponse* response) {
57 response->set_success(
false);
58 response->set_message(
"Emulator middleware not initialized");
59 return grpc::Status::OK;
63 response->set_success(
true);
64 response->set_message(
"ROM loaded successfully");
68 response->set_rom_title(rom->
title());
69 response->set_rom_size(rom->
size());
73 response->set_success(
false);
74 response->set_message(
"Failed to load ROM: " +
75 std::string(status.message()));
77 return grpc::Status::OK;
81 grpc::ServerContext* context,
const agent::Empty* request,
82 agent::LoadedRomPathResponse* response) {
86 response->set_has_rom(
true);
87 response->set_filepath(path);
88 response->set_title(std::filesystem::path(path).stem().
string());
89 return grpc::Status::OK;
93 response->set_has_rom(
false);
94 return grpc::Status::OK;
100 grpc::ServerContext* context,
const agent::ControlRequest* request,
101 agent::CommandResponse* response) {
103 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
104 "Emulator not connected.");
106 std::string action = request->action();
107 if (action ==
"start" || action ==
"resume") {
109 response->set_message(
"Emulator resumed.");
110 }
else if (action ==
"stop" || action ==
"pause") {
112 response->set_message(
"Emulator paused.");
113 }
else if (action ==
"reset") {
115 response->set_message(
"Emulator reset.");
116 }
else if (action ==
"init" || action ==
"initialize") {
117 response->set_message(
"Emulator connection active.");
119 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
120 "Unknown action: " + action);
123 response->set_success(
true);
124 return grpc::Status::OK;
128 grpc::ServerContext* context,
const agent::StepControlRequest* request,
129 agent::StepResponse* response) {
131 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
132 "Emulator not connected.");
135 std::string mode = request->mode();
137 if (mode ==
"instruction") {
139 response->set_message(
"Stepped 1 instruction.");
140 }
else if (mode ==
"over") {
142 response->set_message(
"Step Over executed.");
143 }
else if (mode ==
"out") {
145 response->set_message(
"Step Out executed.");
147 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
148 "Unknown step mode: " + mode);
151 if (!status.ok())
return ToGrpcStatus(status);
155 if (!cpu_status.ok())
return ToGrpcStatus(cpu_status);
156 emu::ToProtoCpuState(cpu_snap, response->mutable_cpu_state());
157 response->set_success(
true);
158 return grpc::Status::OK;
162 grpc::ServerContext* context,
const agent::Empty* request,
163 agent::BreakpointHitResponse* response) {
165 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
166 "Emulator not initialized.");
170 if (!status.ok())
return ToGrpcStatus(status);
171 emu::ToProtoBreakpointHitResponse(result, response);
172 return grpc::Status::OK;
178 grpc::ServerContext* context,
const agent::ButtonRequest* request,
179 agent::CommandResponse* response) {
181 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
182 "Emulator not initialized.");
184 std::vector<emu::InputButton> pressed_buttons;
185 pressed_buttons.reserve(request->buttons_size());
186 for (
int i = 0; i < request->buttons_size(); i++) {
187 auto btn = emu::FromProtoButton(
188 static_cast<agent::Button
>(request->buttons(i)));
190 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
191 absl::StrFormat(
"Invalid button at index %d: %d", i,
192 request->buttons(i)));
195 if (!press_status.ok()) {
196 for (
auto it = pressed_buttons.rbegin(); it != pressed_buttons.rend();
200 return ToGrpcStatus(press_status);
202 pressed_buttons.push_back(btn);
204 std::this_thread::sleep_for(std::chrono::milliseconds(50));
205 for (
auto it = pressed_buttons.rbegin(); it != pressed_buttons.rend(); ++it) {
207 if (!release_status.ok())
return ToGrpcStatus(release_status);
209 response->set_success(
true);
210 return grpc::Status::OK;
214 grpc::ServerContext* context,
const agent::ButtonRequest* request,
215 agent::CommandResponse* response) {
217 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
218 "Emulator not initialized.");
220 for (
int i = 0; i < request->buttons_size(); i++) {
221 auto btn = emu::FromProtoButton(
222 static_cast<agent::Button
>(request->buttons(i)));
224 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
225 absl::StrFormat(
"Invalid button at index %d: %d", i,
226 request->buttons(i)));
229 if (!status.ok())
return ToGrpcStatus(status);
231 response->set_success(
true);
232 return grpc::Status::OK;
236 grpc::ServerContext* context,
const agent::ButtonHoldRequest* request,
237 agent::CommandResponse* response) {
239 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
240 "Emulator not initialized.");
242 std::vector<emu::InputButton> held_buttons;
243 held_buttons.reserve(request->buttons_size());
244 for (
int i = 0; i < request->buttons_size(); i++) {
245 auto btn = emu::FromProtoButton(
246 static_cast<agent::Button
>(request->buttons(i)));
248 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
249 absl::StrFormat(
"Invalid button at index %d: %d", i,
250 request->buttons(i)));
253 if (!press_status.ok()) {
254 for (
auto it = held_buttons.rbegin(); it != held_buttons.rend(); ++it) {
257 return ToGrpcStatus(press_status);
259 held_buttons.push_back(btn);
261 std::this_thread::sleep_for(
262 std::chrono::milliseconds(request->duration_ms()));
263 for (
auto it = held_buttons.rbegin(); it != held_buttons.rend(); ++it) {
265 if (!release_status.ok())
return ToGrpcStatus(release_status);
267 response->set_success(
true);
268 return grpc::Status::OK;
272 grpc::ServerContext* context,
const agent::GameStateRequest* request,
273 agent::GameStateResponse* response) {
275 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
276 "Emulator not initialized.");
280 if (!status.ok())
return ToGrpcStatus(status);
281 emu::ToProtoGameState(snapshot, response);
284 for (
const auto& mem_req : request->memory_reads()) {
285 auto* mem_resp = response->add_memory_responses();
286 mem_resp->set_address(mem_req.address());
289 mem_resp->set_data(data_or->data(), data_or->size());
294 if (request->include_screenshot()) {
295 auto screenshot = yaze::test::CaptureHarnessScreenshot();
296 if (screenshot.ok()) {
297 std::ifstream file(screenshot->file_path, std::ios::binary);
299 std::string png_data((std::istreambuf_iterator<char>(file)),
300 std::istreambuf_iterator<char>());
301 response->set_screenshot_png(png_data);
306 return grpc::Status::OK;
310 grpc::ServerContext* context,
const agent::MemoryRequest* request,
311 agent::MemoryResponse* response) {
313 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
314 "Emulator not initialized.");
316 response->set_address(request->address());
318 if (!data_or.ok())
return ToGrpcStatus(data_or.status());
320 response->set_data(data_or->data(), data_or->size());
321 return grpc::Status::OK;
325 grpc::ServerContext* context,
const agent::MemoryWriteRequest* request,
326 agent::CommandResponse* response) {
328 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
329 "Emulator not initialized.");
331 std::vector<uint8_t> data(request->data().begin(), request->data().end());
333 if (!status.ok())
return ToGrpcStatus(status);
335 response->set_success(
true);
336 return grpc::Status::OK;
342 grpc::ServerContext* context,
343 const agent::BreakpointControlRequest* request,
344 agent::BreakpointControlResponse* response) {
346 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
347 "Emulator not initialized.");
349 std::string action = request->action();
350 if (action ==
"add") {
353 emu::FromProtoBreakpointType(request->type()),
354 emu::FromProtoCpuType(request->cpu()),
355 request->condition(), request->description());
356 if (!id_or.ok())
return ToGrpcStatus(id_or.status());
357 response->set_breakpoint_id(*id_or);
358 response->set_message(
"Breakpoint added.");
359 }
else if (action ==
"remove") {
361 if (!status.ok())
return ToGrpcStatus(status);
362 response->set_message(
"Breakpoint removed.");
363 }
else if (action ==
"toggle") {
366 if (!status.ok())
return ToGrpcStatus(status);
367 response->set_message(
"Breakpoint toggled.");
368 }
else if (action ==
"list") {
370 for (
const auto& bp_snap : list) {
371 emu::ToProtoBreakpointInfo(bp_snap, response->add_breakpoints());
374 return grpc::Status(grpc::StatusCode::INVALID_ARGUMENT,
375 "Unknown action: " + action);
378 response->set_success(
true);
379 return grpc::Status::OK;
383 grpc::ServerContext* context,
384 const agent::WatchpointControlRequest* request,
385 agent::WatchpointControlResponse* response) {
386 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
387 "WatchpointManager integration pending.");
393 grpc::ServerContext* context,
const agent::DisassemblyRequest* request,
394 agent::DisassemblyResponse* response) {
396 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
397 "Emulator not initialized.");
401 uint32_t addr = request->start_address();
402 int fetch_size = request->count() * 4;
404 if (!data_or.ok())
return ToGrpcStatus(data_or.status());
406 const auto& data = *data_or;
407 auto mem_reader = [&](uint32_t read_addr) -> uint8_t {
408 if (read_addr >= addr && read_addr < addr + data.size()) {
409 return data[read_addr - addr];
416 if (!cpu_status.ok())
return ToGrpcStatus(cpu_status);
417 bool m_flag = (cpu_snap.
status & 0x20) != 0;
418 bool x_flag = (cpu_snap.
status & 0x10) != 0;
420 for (uint32_t i = 0; i < request->count(); ++i) {
421 auto inst = dis.
Disassemble(addr, mem_reader, m_flag, x_flag);
422 auto* line = response->add_lines();
423 line->set_address(inst.address);
424 line->set_mnemonic(inst.mnemonic);
425 line->set_operand_str(inst.operand_str);
427 if (addr >= request->start_address() + fetch_size)
break;
429 return grpc::Status::OK;
433 grpc::ServerContext* context,
const agent::TraceRequest* request,
434 agent::TraceResponse* response) {
435 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
436 "Trace not implemented.");
440 grpc::ServerContext* context,
const agent::SymbolLookupRequest* request,
441 agent::SymbolLookupResponse* response) {
444 response->set_found(
true);
445 response->set_symbol_name(sym->name);
446 response->set_address(sym->address);
448 return grpc::Status::OK;
452 grpc::ServerContext* context,
const agent::AddressRequest* request,
453 agent::SymbolLookupResponse* response) {
456 response->set_found(
true);
457 response->set_symbol_name(sym->name);
458 response->set_address(sym->address);
460 return grpc::Status::OK;
464 grpc::ServerContext* context,
const agent::SymbolFileRequest* request,
465 agent::CommandResponse* response) {
469 response->set_success(status.ok());
470 response->set_message(std::string(status.message()));
471 return grpc::Status::OK;
477 grpc::ServerContext* context,
const agent::Empty* request,
478 agent::DebugStatusResponse* response) {
480 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
481 "Emulator not initialized.");
487 if (!cpu_status.ok())
return ToGrpcStatus(cpu_status);
488 emu::ToProtoCpuState(cpu_snap, response->mutable_cpu_state());
491 return grpc::Status::OK;
495 grpc::ServerContext* context,
const agent::TestRunRequest* request,
496 agent::TestRunResponse* response) {
497 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
498 "TestRun requires refactoring for IEmulator");
504 grpc::ServerContext* context,
const agent::SaveStateRequest* request,
505 agent::SaveStateResponse* response) {
506 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
507 "SaveState not yet ported to IEmulator");
511 grpc::ServerContext* context,
const agent::LoadStateRequest* request,
512 agent::LoadStateResponse* response) {
513 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
514 "LoadState not yet ported to IEmulator");
518 grpc::ServerContext* context,
const agent::ListStatesRequest* request,
519 agent::ListStatesResponse* response) {
520 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
521 "ListStates not yet ported to IEmulator");
The Rom class is used to load, save, and modify Rom data. This is a generic SNES ROM container and do...
Abstract interface for emulator backends (Internal vs Mesen2)
virtual bool IsRunning() const =0
virtual absl::StatusOr< std::vector< uint8_t > > ReadBlock(uint32_t addr, size_t len)=0
virtual absl::Status StepOver()=0
virtual absl::Status LoadRom(const std::string &path)=0
virtual absl::Status GetCpuState(CpuStateSnapshot *out_state)=0
virtual absl::Status RemoveBreakpoint(uint32_t breakpoint_id)=0
virtual absl::Status ToggleBreakpoint(uint32_t breakpoint_id, bool enabled)=0
virtual absl::Status Step(int count)=0
virtual absl::Status ReleaseButton(InputButton button)=0
virtual absl::Status GetGameState(GameSnapshot *out_state)=0
virtual absl::Status StepOut()=0
virtual std::string GetLoadedRomPath() const =0
virtual bool IsConnected() const =0
virtual absl::Status WriteBlock(uint32_t addr, const std::vector< uint8_t > &data)=0
virtual absl::Status PressButton(InputButton button)=0
virtual std::vector< BreakpointSnapshot > ListBreakpoints()=0
virtual absl::StatusOr< uint32_t > AddBreakpoint(uint32_t addr, BreakpointKind type, CpuKind cpu, const std::string &condition, const std::string &description)=0
virtual absl::Status RunToBreakpoint(BreakpointHitResult *response)=0
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.
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
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
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
grpc::Status GetLoadedRomPath(grpc::ServerContext *context, const agent::Empty *request, agent::LoadedRomPathResponse *response) override
grpc::Status LoadState(grpc::ServerContext *context, const agent::LoadStateRequest *request, agent::LoadStateResponse *response) override
grpc::Status SaveState(grpc::ServerContext *context, const agent::SaveStateRequest *request, agent::SaveStateResponse *response) override
std::function< Rom *()> RomGetter
grpc::Status ResolveSymbol(grpc::ServerContext *context, const agent::SymbolLookupRequest *request, agent::SymbolLookupResponse *response) override
emu::IEmulator * emulator_
emu::debug::SymbolProvider symbol_provider_
grpc::Status ListStates(grpc::ServerContext *context, const agent::ListStatesRequest *request, agent::ListStatesResponse *response) override
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
EmulatorServiceImpl(emu::IEmulator *emulator, RomGetter rom_getter=nullptr, RomLoader rom_loader=nullptr)
grpc::Status WatchpointControl(grpc::ServerContext *context, const agent::WatchpointControlRequest *request, agent::WatchpointControlResponse *response) override
SymbolFormat
Supported symbol file formats.
grpc::Status ToGrpcStatus(const absl::Status &status)