8#include "absl/strings/escaping.h"
9#include "absl/strings/str_format.h"
20 case A:
return SnesButton::A;
21 case B:
return SnesButton::B;
22 case X:
return SnesButton::X;
23 case Y:
return SnesButton::Y;
24 case L:
return SnesButton::L;
25 case R:
return SnesButton::R;
26 case SELECT:
return SnesButton::SELECT;
27 case START:
return SnesButton::START;
28 case UP:
return SnesButton::UP;
29 case DOWN:
return SnesButton::DOWN;
30 case LEFT:
return SnesButton::LEFT;
31 case RIGHT:
return SnesButton::RIGHT;
39 : emulator_(emulator) {}
44 if (!
emulator_)
return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"Emulator not initialized.");
46 response->set_success(
true);
47 response->set_message(
"Emulator started.");
48 return grpc::Status::OK;
52 if (!
emulator_)
return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"Emulator not initialized.");
54 response->set_success(
true);
55 response->set_message(
"Emulator stopped.");
56 return grpc::Status::OK;
60 if (!
emulator_)
return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"Emulator not initialized.");
62 response->set_success(
true);
63 response->set_message(
"Emulator paused.");
64 return grpc::Status::OK;
68 if (!
emulator_)
return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"Emulator not initialized.");
70 response->set_success(
true);
71 response->set_message(
"Emulator resumed.");
72 return grpc::Status::OK;
76 if (!
emulator_)
return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"Emulator not initialized.");
78 response->set_success(
true);
79 response->set_message(
"Emulator reset.");
80 return grpc::Status::OK;
86 if (!
emulator_)
return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"Emulator not initialized.");
88 for (
int i = 0; i < request->buttons_size(); i++) {
89 input_manager.
PressButton(ToSnesButton(
static_cast<Button
>(request->buttons(i))));
91 std::this_thread::sleep_for(std::chrono::milliseconds(50));
92 for (
int i = 0; i < request->buttons_size(); i++) {
93 input_manager.ReleaseButton(ToSnesButton(
static_cast<Button
>(request->buttons(i))));
95 response->set_success(
true);
96 return grpc::Status::OK;
100 if (!
emulator_)
return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"Emulator not initialized.");
102 for (
int i = 0; i < request->buttons_size(); i++) {
103 input_manager.
ReleaseButton(ToSnesButton(
static_cast<Button
>(request->buttons(i))));
105 response->set_success(
true);
106 return grpc::Status::OK;
110 if (!
emulator_)
return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"Emulator not initialized.");
112 for (
int i = 0; i < request->buttons_size(); i++) {
113 input_manager.
PressButton(ToSnesButton(
static_cast<Button
>(request->buttons(i))));
115 std::this_thread::sleep_for(std::chrono::milliseconds(request->duration_ms()));
116 for (
int i = 0; i < request->buttons_size(); i++) {
117 input_manager.ReleaseButton(ToSnesButton(
static_cast<Button
>(request->buttons(i))));
119 response->set_success(
true);
120 return grpc::Status::OK;
127 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"SNES is not initialized.");
131 response->set_game_mode(memory.ReadByte(0x7E0010));
132 response->set_link_state(memory.ReadByte(0x7E005D));
133 response->set_link_pos_x(memory.ReadWord(0x7E0020));
134 response->set_link_pos_y(memory.ReadWord(0x7E0022));
135 response->set_link_health(memory.ReadByte(0x7EF36D));
137 for (
const auto& mem_req : request->memory_reads()) {
138 auto* mem_resp = response->add_memory_responses();
139 mem_resp->set_address(mem_req.address());
140 std::vector<uint8_t> data(mem_req.size());
141 for (uint32_t i = 0; i < mem_req.size(); ++i) {
142 data[i] = memory.ReadByte(mem_req.address() + i);
144 mem_resp->set_data(data.data(), data.size());
148 if (request->include_screenshot()) {
149 auto screenshot = yaze::test::CaptureHarnessScreenshot();
150 if (screenshot.ok()) {
152 std::ifstream file(screenshot->file_path, std::ios::binary);
154 std::string png_data((std::istreambuf_iterator<char>(file)),
155 std::istreambuf_iterator<char>());
156 response->set_screenshot_png(png_data);
162 return grpc::Status::OK;
167 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"SNES is not initialized.");
170 response->set_address(request->address());
171 std::vector<uint8_t> data(request->size());
172 for (uint32_t i = 0; i < request->size(); ++i) {
173 data[i] = memory.ReadByte(request->address() + i);
175 response->set_data(data.data(), data.size());
176 return grpc::Status::OK;
181 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"SNES is not initialized.");
184 const std::string& data = request->data();
185 for (uint32_t i = 0; i < data.size(); ++i) {
186 memory.WriteByte(request->address() + i,
static_cast<uint8_t
>(data[i]));
188 response->set_success(
true);
189 response->set_message(absl::StrFormat(
"Wrote %d bytes to 0x%X.", data.size(), request->address()));
190 return grpc::Status::OK;
198 switch (proto_type) {
199 case EXECUTE:
return BreakpointManager::Type::EXECUTE;
200 case READ:
return BreakpointManager::Type::READ;
201 case WRITE:
return BreakpointManager::Type::WRITE;
202 case ACCESS:
return BreakpointManager::Type::ACCESS;
203 case CONDITIONAL:
return BreakpointManager::Type::CONDITIONAL;
204 default:
return BreakpointManager::Type::EXECUTE;
212 case CPU_65816:
return BreakpointManager::CpuType::CPU_65816;
213 case SPC700:
return BreakpointManager::CpuType::SPC700;
214 default:
return BreakpointManager::CpuType::CPU_65816;
222 case BreakpointManager::Type::EXECUTE:
return EXECUTE;
223 case BreakpointManager::Type::READ:
return READ;
224 case BreakpointManager::Type::WRITE:
return WRITE;
225 case BreakpointManager::Type::ACCESS:
return ACCESS;
226 case BreakpointManager::Type::CONDITIONAL:
return CONDITIONAL;
227 default:
return EXECUTE;
235 case BreakpointManager::CpuType::CPU_65816:
return CPU_65816;
236 case BreakpointManager::CpuType::SPC700:
return SPC700;
237 default:
return CPU_65816;
242 const BreakpointRequest* request,
243 BreakpointResponse* response) {
245 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"Emulator not initialized.");
253 request->condition(),
254 request->description()
257 response->set_success(
true);
258 response->set_breakpoint_id(
id);
259 response->set_message(absl::StrFormat(
"Breakpoint %d added at 0x%06X",
id, request->address()));
260 return grpc::Status::OK;
264 const BreakpointIdRequest* request,
265 CommandResponse* response) {
267 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"Emulator not initialized.");
271 response->set_success(
true);
272 response->set_message(absl::StrFormat(
"Breakpoint %d removed", request->breakpoint_id()));
273 return grpc::Status::OK;
277 const Empty* request,
278 BreakpointListResponse* response) {
280 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"Emulator not initialized.");
284 for (
const auto& bp : breakpoints) {
285 auto* info = response->add_breakpoints();
287 info->set_address(bp.address);
290 info->set_enabled(bp.enabled);
291 info->set_condition(bp.condition);
292 info->set_description(bp.description);
293 info->set_hit_count(bp.hit_count);
296 return grpc::Status::OK;
300 const BreakpointStateRequest* request,
301 CommandResponse* response) {
303 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"Emulator not initialized.");
307 response->set_success(
true);
308 response->set_message(absl::StrFormat(
"Breakpoint %d %s",
309 request->breakpoint_id(),
310 request->enabled() ?
"enabled" :
"disabled"));
311 return grpc::Status::OK;
316 const WatchpointRequest* request,
317 WatchpointResponse* response) {
319 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
320 "Watchpoints require WatchpointManager integration");
324 const WatchpointIdRequest* request,
325 CommandResponse* response) {
326 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
327 "Watchpoints require WatchpointManager integration");
331 const Empty* request,
332 WatchpointListResponse* response) {
333 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
334 "Watchpoints require WatchpointManager integration");
338 const WatchpointHistoryRequest* request,
339 WatchpointHistoryResponse* response) {
340 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
341 "Watchpoints require WatchpointManager integration");
346 const Empty* request,
347 StepResponse* response) {
349 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"SNES is not initialized.");
354 uint32_t pc_before = (cpu.PB << 16) | cpu.PC;
360 auto* cpu_state = response->mutable_cpu_state();
361 cpu_state->set_a(cpu.A);
362 cpu_state->set_x(cpu.X);
363 cpu_state->set_y(cpu.Y);
364 cpu_state->set_sp(cpu.SP());
365 cpu_state->set_pc(cpu.PC);
366 cpu_state->set_db(cpu.DB);
367 cpu_state->set_pb(cpu.PB);
368 cpu_state->set_d(cpu.D);
369 cpu_state->set_status(cpu.status);
370 cpu_state->set_flag_n(cpu.GetNegativeFlag());
371 cpu_state->set_flag_v(cpu.GetOverflowFlag());
372 cpu_state->set_flag_d(cpu.GetDecimalFlag());
373 cpu_state->set_flag_i(cpu.GetInterruptFlag());
374 cpu_state->set_flag_z(cpu.GetZeroFlag());
375 cpu_state->set_flag_c(cpu.GetCarryFlag());
378 response->set_success(
true);
379 response->set_message(absl::StrFormat(
"Stepped from 0x%06X to 0x%06X",
381 (cpu.PB << 16) | cpu.PC));
382 return grpc::Status::OK;
386 const Empty* request,
387 BreakpointHitResponse* response) {
389 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"SNES is not initialized.");
393 const int kMaxInstructions = 1000000;
394 int instruction_count = 0;
399 while (instruction_count++ < kMaxInstructions) {
400 uint32_t pc = (cpu.PB << 16) | cpu.PC;
404 response->set_hit(
true);
405 response->set_message(absl::StrFormat(
"Breakpoint hit at 0x%06X after %d instructions", pc, instruction_count));
408 auto* last_hit = bp_manager.GetLastHit();
410 auto* bp_info = response->mutable_breakpoint();
411 bp_info->set_id(last_hit->id);
412 bp_info->set_address(last_hit->address);
415 bp_info->set_enabled(last_hit->enabled);
416 bp_info->set_condition(last_hit->condition);
417 bp_info->set_description(last_hit->description);
418 bp_info->set_hit_count(last_hit->hit_count);
422 auto* cpu_state = response->mutable_cpu_state();
423 cpu_state->set_pc(cpu.PC);
424 cpu_state->set_pb(cpu.PB);
425 cpu_state->set_a(cpu.A);
426 cpu_state->set_x(cpu.X);
427 cpu_state->set_y(cpu.Y);
428 cpu_state->set_sp(cpu.SP());
429 cpu_state->set_db(cpu.DB);
430 cpu_state->set_d(cpu.D);
432 return grpc::Status::OK;
440 response->set_hit(
false);
441 response->set_message(absl::StrFormat(
"No breakpoint hit after %d instructions (timeout)", kMaxInstructions));
442 return grpc::Status::OK;
446 const Empty* request,
447 StepResponse* response) {
449 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
450 "StepOver not yet implemented");
454 const Empty* request,
455 StepResponse* response) {
457 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
458 "StepOut not yet implemented");
463 const DisassemblyRequest* request,
464 DisassemblyResponse* response) {
466 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"SNES is not initialized.");
477 uint32_t current_address = request->start_address();
478 uint32_t instructions_added = 0;
480 while (instructions_added < request->count() && instructions_added < 1000) {
481 uint8_t bank = (current_address >> 16) & 0xFF;
482 uint16_t offset = current_address & 0xFFFF;
485 uint8_t opcode = memory.ReadByte(current_address);
488 auto* line = response->add_lines();
489 line->set_address(current_address);
490 line->set_opcode(opcode);
494 line->set_mnemonic(absl::StrFormat(
"OPCODE_%02X", opcode));
496 line->set_execution_count(0);
497 line->set_is_breakpoint(
false);
500 instructions_added++;
503 return grpc::Status::OK;
507 const TraceRequest* request,
508 TraceResponse* response) {
510 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
511 "Execution trace not yet implemented");
516 const SymbolFileRequest* request,
517 CommandResponse* response) {
519 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
520 "Symbol loading not yet implemented");
524 const SymbolLookupRequest* request,
525 SymbolLookupResponse* response) {
527 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
528 "Symbol resolution not yet implemented");
532 const AddressRequest* request,
533 SymbolLookupResponse* response) {
535 return grpc::Status(grpc::StatusCode::UNIMPLEMENTED,
536 "Reverse symbol lookup not yet implemented");
541 const DebugSessionRequest* request,
542 DebugSessionResponse* response) {
544 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"Emulator not initialized.");
550 response->set_success(
true);
551 response->set_session_id(request->session_name());
552 response->set_message(absl::StrFormat(
"Debug session '%s' created", request->session_name()));
553 return grpc::Status::OK;
557 const Empty* request,
558 DebugStatusResponse* response) {
560 return grpc::Status(grpc::StatusCode::UNAVAILABLE,
"Emulator not initialized.");
570 uint32_t active_bp_count = 0;
571 for (
const auto& bp : breakpoints) {
572 if (bp.enabled) active_bp_count++;
574 response->set_active_breakpoints(active_bp_count);
575 response->set_active_watchpoints(0);
579 auto* cpu_state = response->mutable_cpu_state();
580 cpu_state->set_a(cpu.A);
581 cpu_state->set_x(cpu.X);
582 cpu_state->set_y(cpu.Y);
583 cpu_state->set_sp(cpu.SP());
584 cpu_state->set_pc(cpu.PC);
585 cpu_state->set_db(cpu.DB);
586 cpu_state->set_pb(cpu.PB);
587 cpu_state->set_d(cpu.D);
588 cpu_state->set_status(cpu.status);
589 cpu_state->set_flag_n(cpu.GetNegativeFlag());
590 cpu_state->set_flag_v(cpu.GetOverflowFlag());
591 cpu_state->set_flag_d(cpu.GetDecimalFlag());
592 cpu_state->set_flag_i(cpu.GetInterruptFlag());
593 cpu_state->set_flag_z(cpu.GetZeroFlag());
594 cpu_state->set_flag_c(cpu.GetCarryFlag());
600 auto* bp_info = response->mutable_last_breakpoint_hit();
601 bp_info->set_id(last_hit->id);
602 bp_info->set_address(last_hit->address);
605 bp_info->set_enabled(last_hit->enabled);
606 bp_info->set_hit_count(last_hit->hit_count);
609 return grpc::Status::OK;
grpc::Status RemoveBreakpoint(grpc::ServerContext *context, const BreakpointIdRequest *request, CommandResponse *response) override
grpc::Status GetWatchpointHistory(grpc::ServerContext *context, const WatchpointHistoryRequest *request, WatchpointHistoryResponse *response) override
grpc::Status Stop(grpc::ServerContext *context, const Empty *request, CommandResponse *response) override
EmulatorServiceImpl(yaze::emu::Emulator *emulator)
grpc::Status PressButtons(grpc::ServerContext *context, const ButtonRequest *request, CommandResponse *response) override
grpc::Status ListWatchpoints(grpc::ServerContext *context, const Empty *request, WatchpointListResponse *response) override
grpc::Status StepOver(grpc::ServerContext *context, const Empty *request, StepResponse *response) override
grpc::Status GetSymbolAt(grpc::ServerContext *context, const AddressRequest *request, SymbolLookupResponse *response) override
grpc::Status ListBreakpoints(grpc::ServerContext *context, const Empty *request, BreakpointListResponse *response) override
grpc::Status GetExecutionTrace(grpc::ServerContext *context, const TraceRequest *request, TraceResponse *response) override
grpc::Status ResolveSymbol(grpc::ServerContext *context, const SymbolLookupRequest *request, SymbolLookupResponse *response) override
grpc::Status Pause(grpc::ServerContext *context, const Empty *request, CommandResponse *response) override
grpc::Status StepOut(grpc::ServerContext *context, const Empty *request, StepResponse *response) override
grpc::Status ReleaseButtons(grpc::ServerContext *context, const ButtonRequest *request, CommandResponse *response) override
grpc::Status RunToBreakpoint(grpc::ServerContext *context, const Empty *request, BreakpointHitResponse *response) override
grpc::Status Reset(grpc::ServerContext *context, const Empty *request, CommandResponse *response) override
grpc::Status LoadSymbols(grpc::ServerContext *context, const SymbolFileRequest *request, CommandResponse *response) override
grpc::Status WriteMemory(grpc::ServerContext *context, const MemoryWriteRequest *request, CommandResponse *response) override
grpc::Status GetDisassembly(grpc::ServerContext *context, const DisassemblyRequest *request, DisassemblyResponse *response) override
grpc::Status CreateDebugSession(grpc::ServerContext *context, const DebugSessionRequest *request, DebugSessionResponse *response) override
grpc::Status HoldButtons(grpc::ServerContext *context, const ButtonHoldRequest *request, CommandResponse *response) override
grpc::Status ReadMemory(grpc::ServerContext *context, const MemoryRequest *request, MemoryResponse *response) override
grpc::Status AddBreakpoint(grpc::ServerContext *context, const BreakpointRequest *request, BreakpointResponse *response) override
grpc::Status Start(grpc::ServerContext *context, const Empty *request, CommandResponse *response) override
grpc::Status RemoveWatchpoint(grpc::ServerContext *context, const WatchpointIdRequest *request, CommandResponse *response) override
grpc::Status StepInstruction(grpc::ServerContext *context, const Empty *request, StepResponse *response) override
grpc::Status SetBreakpointEnabled(grpc::ServerContext *context, const BreakpointStateRequest *request, CommandResponse *response) override
grpc::Status GetGameState(grpc::ServerContext *context, const GameStateRequest *request, GameStateResponse *response) override
grpc::Status AddWatchpoint(grpc::ServerContext *context, const WatchpointRequest *request, WatchpointResponse *response) override
yaze::emu::Emulator * emulator_
grpc::Status GetDebugStatus(grpc::ServerContext *context, const Empty *request, DebugStatusResponse *response) override
grpc::Status Resume(grpc::ServerContext *context, const Empty *request, CommandResponse *response) override
Manages CPU and SPC700 breakpoints for debugging.
void RemoveBreakpoint(uint32_t id)
Remove a breakpoint by ID.
void SetEnabled(uint32_t id, bool enabled)
Enable or disable a breakpoint.
std::vector< Breakpoint > GetAllBreakpoints() const
Get all breakpoints.
const Breakpoint * GetLastHit() const
Get the last breakpoint that was hit.
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()
double GetCurrentFPS() const
void set_debugging(bool debugging)
uint64_t GetCurrentCycle()
void StepSingleInstruction()
input::InputManager & input_manager()
void set_running(bool running)
bool is_snes_initialized() const
auto running() const -> bool
emu::input::SnesButton ToSnesButton(Button button)
emu::BreakpointManager::CpuType ToCpuType(CpuType proto_cpu)
CpuType ToProtoCpuType(emu::BreakpointManager::CpuType cpu)
BreakpointType ToProtoBreakpointType(emu::BreakpointManager::Type type)
emu::BreakpointManager::Type ToBreakpointType(BreakpointType proto_type)