feat(emulator): implement gRPC control server and emulator commands
- Added `AgentControlServer` to manage gRPC connections for emulator control. - Introduced `EmulatorServiceImpl` with methods for starting, stopping, and controlling the emulator, including button presses and memory operations. - Created new command handlers for pressing, releasing, and holding emulator buttons. - Updated CMake configuration to include new source files and proto definitions for the emulator service. Benefits: - Enhanced control over the emulator through gRPC, allowing for remote interactions. - Improved modularity and maintainability of the emulator's command handling. - Streamlined integration of new features for emulator control and state inspection.
This commit is contained in:
@@ -34,6 +34,87 @@
|
|||||||
"required": ["type", "query"]
|
"required": ["type", "query"]
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
|
{
|
||||||
|
"name": "emulator-press-buttons",
|
||||||
|
"description": "Presses and immediately releases one or more buttons on the SNES controller.",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"buttons": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "A comma-separated list of buttons to press (e.g., 'A,Right,Start')."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["buttons"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "emulator-hold-buttons",
|
||||||
|
"description": "Holds down one or more buttons for a specified duration.",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"buttons": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "A comma-separated list of buttons to hold."
|
||||||
|
},
|
||||||
|
"duration": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "The duration in milliseconds to hold the buttons."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["buttons", "duration"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "emulator-get-state",
|
||||||
|
"description": "Retrieves the current state of the game from the emulator.",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"screenshot": {
|
||||||
|
"type": "boolean",
|
||||||
|
"description": "Whether to include a screenshot of the current frame."
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "emulator-read-memory",
|
||||||
|
"description": "Reads a block of memory from the SNES WRAM.",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"address": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The memory address to read from (e.g., '0x7E0010')."
|
||||||
|
},
|
||||||
|
"length": {
|
||||||
|
"type": "integer",
|
||||||
|
"description": "The number of bytes to read."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["address"]
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "emulator-write-memory",
|
||||||
|
"description": "Writes a block of data to the SNES WRAM.",
|
||||||
|
"parameters": {
|
||||||
|
"type": "object",
|
||||||
|
"properties": {
|
||||||
|
"address": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The memory address to write to."
|
||||||
|
},
|
||||||
|
"data": {
|
||||||
|
"type": "string",
|
||||||
|
"description": "The data to write, as a hex string (e.g., 'AABBCCDD')."
|
||||||
|
}
|
||||||
|
},
|
||||||
|
"required": ["address", "data"]
|
||||||
|
}
|
||||||
|
},
|
||||||
{
|
{
|
||||||
"name": "dungeon-list-sprites",
|
"name": "dungeon-list-sprites",
|
||||||
"description": "List all sprites in a specific dungeon or room",
|
"description": "List all sprites in a specific dungeon or room",
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
set(YAZE_AGENT_SOURCES
|
set(YAZE_AGENT_SOURCES
|
||||||
cli/service/agent/proposal_executor.cc
|
cli/service/agent/proposal_executor.cc
|
||||||
cli/handlers/agent/todo_commands.cc
|
cli/handlers/agent/todo_commands.cc
|
||||||
|
cli/service/agent/agent_control_server.cc
|
||||||
cli/service/agent/conversational_agent_service.cc
|
cli/service/agent/conversational_agent_service.cc
|
||||||
|
cli/service/agent/emulator_service_impl.cc
|
||||||
cli/service/agent/simple_chat_session.cc
|
cli/service/agent/simple_chat_session.cc
|
||||||
cli/service/agent/enhanced_tui.cc
|
cli/service/agent/enhanced_tui.cc
|
||||||
cli/service/agent/tool_dispatcher.cc
|
cli/service/agent/tool_dispatcher.cc
|
||||||
@@ -111,7 +113,8 @@ if(YAZE_WITH_GRPC)
|
|||||||
# Generate proto files for yaze_agent
|
# Generate proto files for yaze_agent
|
||||||
target_add_protobuf(yaze_agent
|
target_add_protobuf(yaze_agent
|
||||||
${PROJECT_SOURCE_DIR}/src/protos/imgui_test_harness.proto
|
${PROJECT_SOURCE_DIR}/src/protos/imgui_test_harness.proto
|
||||||
${PROJECT_SOURCE_DIR}/src/protos/canvas_automation.proto)
|
${PROJECT_SOURCE_DIR}/src/protos/canvas_automation.proto
|
||||||
|
${PROJECT_SOURCE_DIR}/src/protos/emulator_service.proto)
|
||||||
|
|
||||||
target_link_libraries(yaze_agent PUBLIC
|
target_link_libraries(yaze_agent PUBLIC
|
||||||
grpc++
|
grpc++
|
||||||
|
|||||||
@@ -21,3 +21,7 @@ ABSL_FLAG(std::string, prompt_version, "default",
|
|||||||
"Prompt version to use: 'default' or 'v2'");
|
"Prompt version to use: 'default' or 'v2'");
|
||||||
ABSL_FLAG(bool, use_function_calling, false,
|
ABSL_FLAG(bool, use_function_calling, false,
|
||||||
"Enable native Gemini function calling (incompatible with JSON output mode)");
|
"Enable native Gemini function calling (incompatible with JSON output mode)");
|
||||||
|
|
||||||
|
// --- Agent Control Flags ---
|
||||||
|
ABSL_FLAG(bool, agent_control, false,
|
||||||
|
"Enable the gRPC server to allow the agent to control the emulator.");
|
||||||
|
|||||||
@@ -1,35 +1,256 @@
|
|||||||
#include "cli/handlers/tools/emulator_commands.h"
|
#include "cli/handlers/tools/emulator_commands.h"
|
||||||
|
|
||||||
#include "absl/strings/numbers.h"
|
#include <grpcpp/grpcpp.h>
|
||||||
#include "absl/strings/str_format.h"
|
#include "protos/emulator_service.grpc.pb.h"
|
||||||
|
#include "absl/strings/str_split.h"
|
||||||
|
#include "absl/strings/string_view.h"
|
||||||
|
#include "absl/time/time.h"
|
||||||
|
#include "absl/status/statusor.h"
|
||||||
|
#include "absl/strings/escaping.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace cli {
|
namespace cli {
|
||||||
namespace handlers {
|
namespace handlers {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
|
||||||
|
// A simple client for the EmulatorService
|
||||||
|
class EmulatorClient {
|
||||||
|
public:
|
||||||
|
EmulatorClient() {
|
||||||
|
auto channel = grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials());
|
||||||
|
stub_ = agent::EmulatorService::NewStub(channel);
|
||||||
|
}
|
||||||
|
|
||||||
|
template <typename TRequest, typename TResponse>
|
||||||
|
absl::StatusOr<TResponse> CallRpc(
|
||||||
|
grpc::Status (agent::EmulatorService::Stub::*rpc_method)(grpc::ClientContext*, const TRequest&, TResponse*),
|
||||||
|
const TRequest& request) {
|
||||||
|
|
||||||
|
TResponse response;
|
||||||
|
grpc::ClientContext context;
|
||||||
|
|
||||||
|
auto deadline = std::chrono::system_clock::now() + std::chrono::seconds(5);
|
||||||
|
context.set_deadline(deadline);
|
||||||
|
|
||||||
|
grpc::Status status = (stub_.get()->*rpc_method)(&context, request, &response);
|
||||||
|
|
||||||
|
if (!status.ok()) {
|
||||||
|
return absl::UnavailableError(absl::StrFormat(
|
||||||
|
"RPC failed: (%d) %s", status.error_code(), status.error_message()));
|
||||||
|
}
|
||||||
|
return response;
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
std::unique_ptr<agent::EmulatorService::Stub> stub_;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Helper to parse button from string
|
||||||
|
absl::StatusOr<agent::Button> StringToButton(absl::string_view s) {
|
||||||
|
if (s == "A") return agent::Button::A;
|
||||||
|
if (s == "B") return agent::Button::B;
|
||||||
|
if (s == "X") return agent::Button::X;
|
||||||
|
if (s == "Y") return agent::Button::Y;
|
||||||
|
if (s == "L") return agent::Button::L;
|
||||||
|
if (s == "R") return agent::Button::R;
|
||||||
|
if (s == "SELECT") return agent::Button::SELECT;
|
||||||
|
if (s == "START") return agent::Button::START;
|
||||||
|
if (s == "UP") return agent::Button::UP;
|
||||||
|
if (s == "DOWN") return agent::Button::DOWN;
|
||||||
|
if (s == "LEFT") return agent::Button::LEFT;
|
||||||
|
if (s == "RIGHT") return agent::Button::RIGHT;
|
||||||
|
return absl::InvalidArgumentError(absl::StrCat("Unknown button: ", s));
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
// --- Command Implementations ---
|
||||||
|
|
||||||
|
absl::Status EmulatorResetCommandHandler::Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
|
resources::OutputFormatter& formatter) {
|
||||||
|
EmulatorClient client;
|
||||||
|
agent::Empty request;
|
||||||
|
auto response_or = client.CallRpc(&agent::EmulatorService::Stub::Reset, request);
|
||||||
|
if (!response_or.ok()) {
|
||||||
|
return response_or.status();
|
||||||
|
}
|
||||||
|
auto response = response_or.value();
|
||||||
|
|
||||||
|
formatter.BeginObject("EmulatorReset");
|
||||||
|
formatter.AddField("success", response.success());
|
||||||
|
formatter.AddField("message", response.message());
|
||||||
|
formatter.EndObject();
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status EmulatorGetStateCommandHandler::Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
|
resources::OutputFormatter& formatter) {
|
||||||
|
EmulatorClient client;
|
||||||
|
agent::GameStateRequest request;
|
||||||
|
request.set_include_screenshot(parser.HasFlag("screenshot"));
|
||||||
|
|
||||||
|
auto response_or = client.CallRpc(&agent::EmulatorService::Stub::GetGameState, request);
|
||||||
|
if (!response_or.ok()) {
|
||||||
|
return response_or.status();
|
||||||
|
}
|
||||||
|
auto response = response_or.value();
|
||||||
|
|
||||||
|
formatter.BeginObject("EmulatorState");
|
||||||
|
formatter.AddField("game_mode", static_cast<uint64_t>(response.game_mode()));
|
||||||
|
formatter.AddField("link_state", static_cast<uint64_t>(response.link_state()));
|
||||||
|
formatter.AddField("link_pos_x", static_cast<uint64_t>(response.link_pos_x()));
|
||||||
|
formatter.AddField("link_pos_y", static_cast<uint64_t>(response.link_pos_y()));
|
||||||
|
formatter.AddField("link_health", static_cast<uint64_t>(response.link_health()));
|
||||||
|
if (!response.screenshot_png().empty()) {
|
||||||
|
formatter.AddField("screenshot_size", static_cast<uint64_t>(response.screenshot_png().size()));
|
||||||
|
}
|
||||||
|
formatter.EndObject();
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status EmulatorReadMemoryCommandHandler::Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
|
resources::OutputFormatter& formatter) {
|
||||||
|
EmulatorClient client;
|
||||||
|
agent::MemoryRequest request;
|
||||||
|
|
||||||
|
uint32_t address;
|
||||||
|
if (!absl::SimpleHexAtoi(parser.GetString("address").value(), &address)) {
|
||||||
|
return absl::InvalidArgumentError("Invalid address format.");
|
||||||
|
}
|
||||||
|
request.set_address(address);
|
||||||
|
request.set_size(parser.GetInt("length").value_or(16));
|
||||||
|
|
||||||
|
auto response_or = client.CallRpc(&agent::EmulatorService::Stub::ReadMemory, request);
|
||||||
|
if (!response_or.ok()) {
|
||||||
|
return response_or.status();
|
||||||
|
}
|
||||||
|
auto response = response_or.value();
|
||||||
|
|
||||||
|
formatter.BeginObject("MemoryRead");
|
||||||
|
formatter.AddHexField("address", response.address());
|
||||||
|
formatter.AddField("data_hex", absl::BytesToHexString(response.data()));
|
||||||
|
formatter.EndObject();
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status EmulatorWriteMemoryCommandHandler::Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
|
resources::OutputFormatter& formatter) {
|
||||||
|
EmulatorClient client;
|
||||||
|
agent::MemoryWriteRequest request;
|
||||||
|
|
||||||
|
uint32_t address;
|
||||||
|
if (!absl::SimpleHexAtoi(parser.GetString("address").value(), &address)) {
|
||||||
|
return absl::InvalidArgumentError("Invalid address format.");
|
||||||
|
}
|
||||||
|
request.set_address(address);
|
||||||
|
|
||||||
|
std::string data_hex = parser.GetString("data").value();
|
||||||
|
request.set_data(absl::HexStringToBytes(data_hex));
|
||||||
|
|
||||||
|
auto response_or = client.CallRpc(&agent::EmulatorService::Stub::WriteMemory, request);
|
||||||
|
if (!response_or.ok()) {
|
||||||
|
return response_or.status();
|
||||||
|
}
|
||||||
|
auto response = response_or.value();
|
||||||
|
|
||||||
|
formatter.BeginObject("MemoryWrite");
|
||||||
|
formatter.AddField("success", response.success());
|
||||||
|
formatter.AddField("message", response.message());
|
||||||
|
formatter.EndObject();
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
absl::Status EmulatorPressButtonsCommandHandler::Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
|
resources::OutputFormatter& formatter) {
|
||||||
|
EmulatorClient client;
|
||||||
|
agent::ButtonRequest request;
|
||||||
|
std::vector<std::string> buttons = absl::StrSplit(parser.GetString("buttons").value(), ',');
|
||||||
|
for (const auto& btn_str : buttons) {
|
||||||
|
auto button_or = StringToButton(btn_str);
|
||||||
|
if (!button_or.ok()) return button_or.status();
|
||||||
|
request.add_buttons(button_or.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto response_or = client.CallRpc(&agent::EmulatorService::Stub::PressButtons, request);
|
||||||
|
if (!response_or.ok()) {
|
||||||
|
return response_or.status();
|
||||||
|
}
|
||||||
|
auto response = response_or.value();
|
||||||
|
|
||||||
|
formatter.BeginObject("PressButtons");
|
||||||
|
formatter.AddField("success", response.success());
|
||||||
|
formatter.AddField("message", response.message());
|
||||||
|
formatter.EndObject();
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status EmulatorReleaseButtonsCommandHandler::Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
|
resources::OutputFormatter& formatter) {
|
||||||
|
EmulatorClient client;
|
||||||
|
agent::ButtonRequest request;
|
||||||
|
std::vector<std::string> buttons = absl::StrSplit(parser.GetString("buttons").value(), ',');
|
||||||
|
for (const auto& btn_str : buttons) {
|
||||||
|
auto button_or = StringToButton(btn_str);
|
||||||
|
if (!button_or.ok()) return button_or.status();
|
||||||
|
request.add_buttons(button_or.value());
|
||||||
|
}
|
||||||
|
|
||||||
|
auto response_or = client.CallRpc(&agent::EmulatorService::Stub::ReleaseButtons, request);
|
||||||
|
if (!response_or.ok()) {
|
||||||
|
return response_or.status();
|
||||||
|
}
|
||||||
|
auto response = response_or.value();
|
||||||
|
|
||||||
|
formatter.BeginObject("ReleaseButtons");
|
||||||
|
formatter.AddField("success", response.success());
|
||||||
|
formatter.AddField("message", response.message());
|
||||||
|
formatter.EndObject();
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
absl::Status EmulatorHoldButtonsCommandHandler::Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
|
resources::OutputFormatter& formatter) {
|
||||||
|
EmulatorClient client;
|
||||||
|
agent::ButtonHoldRequest request;
|
||||||
|
std::vector<std::string> buttons = absl::StrSplit(parser.GetString("buttons").value(), ',');
|
||||||
|
for (const auto& btn_str : buttons) {
|
||||||
|
auto button_or = StringToButton(btn_str);
|
||||||
|
if (!button_or.ok()) return button_or.status();
|
||||||
|
request.add_buttons(button_or.value());
|
||||||
|
}
|
||||||
|
request.set_duration_ms(parser.GetInt("duration").value());
|
||||||
|
|
||||||
|
auto response_or = client.CallRpc(&agent::EmulatorService::Stub::HoldButtons, request);
|
||||||
|
if (!response_or.ok()) {
|
||||||
|
return response_or.status();
|
||||||
|
}
|
||||||
|
auto response = response_or.value();
|
||||||
|
|
||||||
|
formatter.BeginObject("HoldButtons");
|
||||||
|
formatter.AddField("success", response.success());
|
||||||
|
formatter.AddField("message", response.message());
|
||||||
|
formatter.EndObject();
|
||||||
|
return absl::OkStatus();
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
// --- Placeholder Implementations for commands not yet migrated to gRPC ---
|
||||||
|
|
||||||
absl::Status EmulatorStepCommandHandler::Execute(Rom* rom, const resources::ArgumentParser& parser,
|
absl::Status EmulatorStepCommandHandler::Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
resources::OutputFormatter& formatter) {
|
resources::OutputFormatter& formatter) {
|
||||||
auto count = parser.GetInt("count").value_or(1);
|
|
||||||
|
|
||||||
formatter.BeginObject("Emulator Step");
|
formatter.BeginObject("Emulator Step");
|
||||||
formatter.AddField("steps_executed", count);
|
|
||||||
formatter.AddField("status", "not_implemented");
|
formatter.AddField("status", "not_implemented");
|
||||||
formatter.AddField("message", "Emulator stepping requires emulator integration");
|
|
||||||
formatter.EndObject();
|
formatter.EndObject();
|
||||||
|
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status EmulatorRunCommandHandler::Execute(Rom* rom, const resources::ArgumentParser& parser,
|
absl::Status EmulatorRunCommandHandler::Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
resources::OutputFormatter& formatter) {
|
resources::OutputFormatter& formatter) {
|
||||||
auto until_breakpoint = parser.GetString("until").value_or("");
|
|
||||||
|
|
||||||
formatter.BeginObject("Emulator Run");
|
formatter.BeginObject("Emulator Run");
|
||||||
formatter.AddField("until_breakpoint", until_breakpoint);
|
|
||||||
formatter.AddField("status", "not_implemented");
|
formatter.AddField("status", "not_implemented");
|
||||||
formatter.AddField("message", "Emulator running requires emulator integration");
|
|
||||||
formatter.EndObject();
|
formatter.EndObject();
|
||||||
|
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -37,134 +258,31 @@ absl::Status EmulatorPauseCommandHandler::Execute(Rom* rom, const resources::Arg
|
|||||||
resources::OutputFormatter& formatter) {
|
resources::OutputFormatter& formatter) {
|
||||||
formatter.BeginObject("Emulator Pause");
|
formatter.BeginObject("Emulator Pause");
|
||||||
formatter.AddField("status", "not_implemented");
|
formatter.AddField("status", "not_implemented");
|
||||||
formatter.AddField("message", "Emulator pause requires emulator integration");
|
|
||||||
formatter.EndObject();
|
formatter.EndObject();
|
||||||
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status EmulatorResetCommandHandler::Execute(Rom* rom, const resources::ArgumentParser& parser,
|
|
||||||
resources::OutputFormatter& formatter) {
|
|
||||||
formatter.BeginObject("Emulator Reset");
|
|
||||||
formatter.AddField("status", "not_implemented");
|
|
||||||
formatter.AddField("message", "Emulator reset requires emulator integration");
|
|
||||||
formatter.EndObject();
|
|
||||||
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status EmulatorGetStateCommandHandler::Execute(Rom* rom, const resources::ArgumentParser& parser,
|
|
||||||
resources::OutputFormatter& formatter) {
|
|
||||||
formatter.BeginObject("Emulator State");
|
|
||||||
formatter.AddField("status", "not_implemented");
|
|
||||||
formatter.AddField("message", "Emulator state requires emulator integration");
|
|
||||||
|
|
||||||
formatter.BeginObject("state");
|
|
||||||
formatter.AddField("running", false);
|
|
||||||
formatter.AddField("paused", true);
|
|
||||||
formatter.AddField("pc", "0x000000");
|
|
||||||
formatter.EndObject();
|
|
||||||
formatter.EndObject();
|
|
||||||
|
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status EmulatorSetBreakpointCommandHandler::Execute(Rom* rom, const resources::ArgumentParser& parser,
|
absl::Status EmulatorSetBreakpointCommandHandler::Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
resources::OutputFormatter& formatter) {
|
resources::OutputFormatter& formatter) {
|
||||||
auto address_str = parser.GetString("address").value();
|
|
||||||
auto condition = parser.GetString("condition").value_or("");
|
|
||||||
|
|
||||||
uint32_t address;
|
|
||||||
if (!absl::SimpleHexAtoi(address_str, &address)) {
|
|
||||||
return absl::InvalidArgumentError(
|
|
||||||
"Invalid address format. Must be hex.");
|
|
||||||
}
|
|
||||||
|
|
||||||
formatter.BeginObject("Emulator Breakpoint Set");
|
formatter.BeginObject("Emulator Breakpoint Set");
|
||||||
formatter.AddHexField("address", address, 6);
|
|
||||||
formatter.AddField("condition", condition);
|
|
||||||
formatter.AddField("status", "not_implemented");
|
formatter.AddField("status", "not_implemented");
|
||||||
formatter.AddField("message", "Breakpoint setting requires emulator integration");
|
|
||||||
formatter.EndObject();
|
formatter.EndObject();
|
||||||
|
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status EmulatorClearBreakpointCommandHandler::Execute(Rom* rom, const resources::ArgumentParser& parser,
|
absl::Status EmulatorClearBreakpointCommandHandler::Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
resources::OutputFormatter& formatter) {
|
resources::OutputFormatter& formatter) {
|
||||||
auto address_str = parser.GetString("address").value();
|
|
||||||
|
|
||||||
uint32_t address;
|
|
||||||
if (!absl::SimpleHexAtoi(address_str, &address)) {
|
|
||||||
return absl::InvalidArgumentError(
|
|
||||||
"Invalid address format. Must be hex.");
|
|
||||||
}
|
|
||||||
|
|
||||||
formatter.BeginObject("Emulator Breakpoint Cleared");
|
formatter.BeginObject("Emulator Breakpoint Cleared");
|
||||||
formatter.AddHexField("address", address, 6);
|
|
||||||
formatter.AddField("status", "not_implemented");
|
formatter.AddField("status", "not_implemented");
|
||||||
formatter.AddField("message", "Breakpoint clearing requires emulator integration");
|
|
||||||
formatter.EndObject();
|
formatter.EndObject();
|
||||||
|
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status EmulatorListBreakpointsCommandHandler::Execute(Rom* rom, const resources::ArgumentParser& parser,
|
absl::Status EmulatorListBreakpointsCommandHandler::Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
resources::OutputFormatter& formatter) {
|
resources::OutputFormatter& formatter) {
|
||||||
formatter.BeginObject("Emulator Breakpoints");
|
formatter.BeginObject("Emulator Breakpoints");
|
||||||
formatter.AddField("total_breakpoints", 0);
|
|
||||||
formatter.AddField("status", "not_implemented");
|
formatter.AddField("status", "not_implemented");
|
||||||
formatter.AddField("message", "Breakpoint listing requires emulator integration");
|
|
||||||
|
|
||||||
formatter.BeginArray("breakpoints");
|
|
||||||
formatter.EndArray();
|
|
||||||
formatter.EndObject();
|
formatter.EndObject();
|
||||||
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status EmulatorReadMemoryCommandHandler::Execute(Rom* rom, const resources::ArgumentParser& parser,
|
|
||||||
resources::OutputFormatter& formatter) {
|
|
||||||
auto address_str = parser.GetString("address").value();
|
|
||||||
auto length = parser.GetInt("length").value_or(16);
|
|
||||||
|
|
||||||
uint32_t address;
|
|
||||||
if (!absl::SimpleHexAtoi(address_str, &address)) {
|
|
||||||
return absl::InvalidArgumentError(
|
|
||||||
"Invalid address format. Must be hex.");
|
|
||||||
}
|
|
||||||
|
|
||||||
formatter.BeginObject("Emulator Memory Read");
|
|
||||||
formatter.AddHexField("address", address, 6);
|
|
||||||
formatter.AddField("length", length);
|
|
||||||
formatter.AddField("status", "not_implemented");
|
|
||||||
formatter.AddField("message", "Memory reading requires emulator integration");
|
|
||||||
|
|
||||||
formatter.BeginArray("data");
|
|
||||||
formatter.EndArray();
|
|
||||||
formatter.EndObject();
|
|
||||||
|
|
||||||
return absl::OkStatus();
|
|
||||||
}
|
|
||||||
|
|
||||||
absl::Status EmulatorWriteMemoryCommandHandler::Execute(Rom* rom, const resources::ArgumentParser& parser,
|
|
||||||
resources::OutputFormatter& formatter) {
|
|
||||||
auto address_str = parser.GetString("address").value();
|
|
||||||
auto data_str = parser.GetString("data").value();
|
|
||||||
|
|
||||||
uint32_t address;
|
|
||||||
if (!absl::SimpleHexAtoi(address_str, &address)) {
|
|
||||||
return absl::InvalidArgumentError(
|
|
||||||
"Invalid address format. Must be hex.");
|
|
||||||
}
|
|
||||||
|
|
||||||
formatter.BeginObject("Emulator Memory Write");
|
|
||||||
formatter.AddHexField("address", address, 6);
|
|
||||||
formatter.AddField("data", data_str);
|
|
||||||
formatter.AddField("status", "not_implemented");
|
|
||||||
formatter.AddField("message", "Memory writing requires emulator integration");
|
|
||||||
formatter.EndObject();
|
|
||||||
|
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -172,19 +290,7 @@ absl::Status EmulatorGetRegistersCommandHandler::Execute(Rom* rom, const resourc
|
|||||||
resources::OutputFormatter& formatter) {
|
resources::OutputFormatter& formatter) {
|
||||||
formatter.BeginObject("Emulator Registers");
|
formatter.BeginObject("Emulator Registers");
|
||||||
formatter.AddField("status", "not_implemented");
|
formatter.AddField("status", "not_implemented");
|
||||||
formatter.AddField("message", "Register reading requires emulator integration");
|
|
||||||
|
|
||||||
formatter.BeginObject("registers");
|
|
||||||
formatter.AddField("A", std::string("0x0000"));
|
|
||||||
formatter.AddField("X", std::string("0x0000"));
|
|
||||||
formatter.AddField("Y", std::string("0x0000"));
|
|
||||||
formatter.AddField("PC", std::string("0x000000"));
|
|
||||||
formatter.AddField("SP", std::string("0x01FF"));
|
|
||||||
formatter.AddField("DB", std::string("0x00"));
|
|
||||||
formatter.AddField("DP", std::string("0x0000"));
|
|
||||||
formatter.EndObject();
|
formatter.EndObject();
|
||||||
formatter.EndObject();
|
|
||||||
|
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -192,16 +298,7 @@ absl::Status EmulatorGetMetricsCommandHandler::Execute(Rom* rom, const resources
|
|||||||
resources::OutputFormatter& formatter) {
|
resources::OutputFormatter& formatter) {
|
||||||
formatter.BeginObject("Emulator Metrics");
|
formatter.BeginObject("Emulator Metrics");
|
||||||
formatter.AddField("status", "not_implemented");
|
formatter.AddField("status", "not_implemented");
|
||||||
formatter.AddField("message", "Metrics require emulator integration");
|
|
||||||
|
|
||||||
formatter.BeginObject("metrics");
|
|
||||||
formatter.AddField("instructions_per_second", 0);
|
|
||||||
formatter.AddField("total_instructions", 0);
|
|
||||||
formatter.AddField("cycles_per_frame", 0);
|
|
||||||
formatter.AddField("frame_rate", 0);
|
|
||||||
formatter.EndObject();
|
formatter.EndObject();
|
||||||
formatter.EndObject();
|
|
||||||
|
|
||||||
return absl::OkStatus();
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -7,9 +7,6 @@ namespace yaze {
|
|||||||
namespace cli {
|
namespace cli {
|
||||||
namespace handlers {
|
namespace handlers {
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Command handler for emulator step execution
|
|
||||||
*/
|
|
||||||
class EmulatorStepCommandHandler : public resources::CommandHandler {
|
class EmulatorStepCommandHandler : public resources::CommandHandler {
|
||||||
public:
|
public:
|
||||||
std::string GetName() const { return "emulator-step"; }
|
std::string GetName() const { return "emulator-step"; }
|
||||||
@@ -21,16 +18,13 @@ class EmulatorStepCommandHandler : public resources::CommandHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
absl::Status ValidateArgs(const resources::ArgumentParser& parser) override {
|
absl::Status ValidateArgs(const resources::ArgumentParser& parser) override {
|
||||||
return absl::OkStatus(); // No required args
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
|
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
resources::OutputFormatter& formatter) override;
|
resources::OutputFormatter& formatter) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Command handler for emulator run execution
|
|
||||||
*/
|
|
||||||
class EmulatorRunCommandHandler : public resources::CommandHandler {
|
class EmulatorRunCommandHandler : public resources::CommandHandler {
|
||||||
public:
|
public:
|
||||||
std::string GetName() const { return "emulator-run"; }
|
std::string GetName() const { return "emulator-run"; }
|
||||||
@@ -42,16 +36,13 @@ class EmulatorRunCommandHandler : public resources::CommandHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
absl::Status ValidateArgs(const resources::ArgumentParser& parser) override {
|
absl::Status ValidateArgs(const resources::ArgumentParser& parser) override {
|
||||||
return absl::OkStatus(); // No required args
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
|
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
resources::OutputFormatter& formatter) override;
|
resources::OutputFormatter& formatter) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Command handler for emulator pause
|
|
||||||
*/
|
|
||||||
class EmulatorPauseCommandHandler : public resources::CommandHandler {
|
class EmulatorPauseCommandHandler : public resources::CommandHandler {
|
||||||
public:
|
public:
|
||||||
std::string GetName() const { return "emulator-pause"; }
|
std::string GetName() const { return "emulator-pause"; }
|
||||||
@@ -63,16 +54,13 @@ class EmulatorPauseCommandHandler : public resources::CommandHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
absl::Status ValidateArgs(const resources::ArgumentParser& parser) override {
|
absl::Status ValidateArgs(const resources::ArgumentParser& parser) override {
|
||||||
return absl::OkStatus(); // No required args
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
|
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
resources::OutputFormatter& formatter) override;
|
resources::OutputFormatter& formatter) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Command handler for emulator reset
|
|
||||||
*/
|
|
||||||
class EmulatorResetCommandHandler : public resources::CommandHandler {
|
class EmulatorResetCommandHandler : public resources::CommandHandler {
|
||||||
public:
|
public:
|
||||||
std::string GetName() const { return "emulator-reset"; }
|
std::string GetName() const { return "emulator-reset"; }
|
||||||
@@ -84,16 +72,13 @@ class EmulatorResetCommandHandler : public resources::CommandHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
absl::Status ValidateArgs(const resources::ArgumentParser& parser) override {
|
absl::Status ValidateArgs(const resources::ArgumentParser& parser) override {
|
||||||
return absl::OkStatus(); // No required args
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
|
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
resources::OutputFormatter& formatter) override;
|
resources::OutputFormatter& formatter) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Command handler for getting emulator state
|
|
||||||
*/
|
|
||||||
class EmulatorGetStateCommandHandler : public resources::CommandHandler {
|
class EmulatorGetStateCommandHandler : public resources::CommandHandler {
|
||||||
public:
|
public:
|
||||||
std::string GetName() const { return "emulator-get-state"; }
|
std::string GetName() const { return "emulator-get-state"; }
|
||||||
@@ -105,16 +90,13 @@ class EmulatorGetStateCommandHandler : public resources::CommandHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
absl::Status ValidateArgs(const resources::ArgumentParser& parser) override {
|
absl::Status ValidateArgs(const resources::ArgumentParser& parser) override {
|
||||||
return absl::OkStatus(); // No required args
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
|
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
resources::OutputFormatter& formatter) override;
|
resources::OutputFormatter& formatter) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Command handler for setting breakpoints
|
|
||||||
*/
|
|
||||||
class EmulatorSetBreakpointCommandHandler : public resources::CommandHandler {
|
class EmulatorSetBreakpointCommandHandler : public resources::CommandHandler {
|
||||||
public:
|
public:
|
||||||
std::string GetName() const { return "emulator-set-breakpoint"; }
|
std::string GetName() const { return "emulator-set-breakpoint"; }
|
||||||
@@ -133,9 +115,6 @@ class EmulatorSetBreakpointCommandHandler : public resources::CommandHandler {
|
|||||||
resources::OutputFormatter& formatter) override;
|
resources::OutputFormatter& formatter) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Command handler for clearing breakpoints
|
|
||||||
*/
|
|
||||||
class EmulatorClearBreakpointCommandHandler : public resources::CommandHandler {
|
class EmulatorClearBreakpointCommandHandler : public resources::CommandHandler {
|
||||||
public:
|
public:
|
||||||
std::string GetName() const { return "emulator-clear-breakpoint"; }
|
std::string GetName() const { return "emulator-clear-breakpoint"; }
|
||||||
@@ -154,9 +133,6 @@ class EmulatorClearBreakpointCommandHandler : public resources::CommandHandler {
|
|||||||
resources::OutputFormatter& formatter) override;
|
resources::OutputFormatter& formatter) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Command handler for listing breakpoints
|
|
||||||
*/
|
|
||||||
class EmulatorListBreakpointsCommandHandler : public resources::CommandHandler {
|
class EmulatorListBreakpointsCommandHandler : public resources::CommandHandler {
|
||||||
public:
|
public:
|
||||||
std::string GetName() const { return "emulator-list-breakpoints"; }
|
std::string GetName() const { return "emulator-list-breakpoints"; }
|
||||||
@@ -168,16 +144,13 @@ class EmulatorListBreakpointsCommandHandler : public resources::CommandHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
absl::Status ValidateArgs(const resources::ArgumentParser& parser) override {
|
absl::Status ValidateArgs(const resources::ArgumentParser& parser) override {
|
||||||
return absl::OkStatus(); // No required args
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
|
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
resources::OutputFormatter& formatter) override;
|
resources::OutputFormatter& formatter) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Command handler for reading emulator memory
|
|
||||||
*/
|
|
||||||
class EmulatorReadMemoryCommandHandler : public resources::CommandHandler {
|
class EmulatorReadMemoryCommandHandler : public resources::CommandHandler {
|
||||||
public:
|
public:
|
||||||
std::string GetName() const { return "emulator-read-memory"; }
|
std::string GetName() const { return "emulator-read-memory"; }
|
||||||
@@ -196,9 +169,6 @@ class EmulatorReadMemoryCommandHandler : public resources::CommandHandler {
|
|||||||
resources::OutputFormatter& formatter) override;
|
resources::OutputFormatter& formatter) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Command handler for writing emulator memory
|
|
||||||
*/
|
|
||||||
class EmulatorWriteMemoryCommandHandler : public resources::CommandHandler {
|
class EmulatorWriteMemoryCommandHandler : public resources::CommandHandler {
|
||||||
public:
|
public:
|
||||||
std::string GetName() const { return "emulator-write-memory"; }
|
std::string GetName() const { return "emulator-write-memory"; }
|
||||||
@@ -217,9 +187,6 @@ class EmulatorWriteMemoryCommandHandler : public resources::CommandHandler {
|
|||||||
resources::OutputFormatter& formatter) override;
|
resources::OutputFormatter& formatter) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Command handler for getting emulator registers
|
|
||||||
*/
|
|
||||||
class EmulatorGetRegistersCommandHandler : public resources::CommandHandler {
|
class EmulatorGetRegistersCommandHandler : public resources::CommandHandler {
|
||||||
public:
|
public:
|
||||||
std::string GetName() const { return "emulator-get-registers"; }
|
std::string GetName() const { return "emulator-get-registers"; }
|
||||||
@@ -231,16 +198,13 @@ class EmulatorGetRegistersCommandHandler : public resources::CommandHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
absl::Status ValidateArgs(const resources::ArgumentParser& parser) override {
|
absl::Status ValidateArgs(const resources::ArgumentParser& parser) override {
|
||||||
return absl::OkStatus(); // No required args
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
|
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
resources::OutputFormatter& formatter) override;
|
resources::OutputFormatter& formatter) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
|
||||||
* @brief Command handler for getting emulator metrics
|
|
||||||
*/
|
|
||||||
class EmulatorGetMetricsCommandHandler : public resources::CommandHandler {
|
class EmulatorGetMetricsCommandHandler : public resources::CommandHandler {
|
||||||
public:
|
public:
|
||||||
std::string GetName() const { return "emulator-get-metrics"; }
|
std::string GetName() const { return "emulator-get-metrics"; }
|
||||||
@@ -252,13 +216,65 @@ class EmulatorGetMetricsCommandHandler : public resources::CommandHandler {
|
|||||||
}
|
}
|
||||||
|
|
||||||
absl::Status ValidateArgs(const resources::ArgumentParser& parser) override {
|
absl::Status ValidateArgs(const resources::ArgumentParser& parser) override {
|
||||||
return absl::OkStatus(); // No required args
|
return absl::OkStatus();
|
||||||
}
|
}
|
||||||
|
|
||||||
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
|
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
resources::OutputFormatter& formatter) override;
|
resources::OutputFormatter& formatter) override;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class EmulatorPressButtonsCommandHandler : public resources::CommandHandler {
|
||||||
|
public:
|
||||||
|
std::string GetName() const { return "emulator-press-buttons"; }
|
||||||
|
std::string GetDescription() const {
|
||||||
|
return "Press and release emulator buttons";
|
||||||
|
}
|
||||||
|
std::string GetUsage() const {
|
||||||
|
return "emulator-press-buttons --buttons <button1>,<button2>,...";
|
||||||
|
}
|
||||||
|
absl::Status ValidateArgs(
|
||||||
|
const resources::ArgumentParser& parser) override {
|
||||||
|
return parser.RequireArgs({"buttons"});
|
||||||
|
}
|
||||||
|
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
|
resources::OutputFormatter& formatter) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EmulatorReleaseButtonsCommandHandler : public resources::CommandHandler {
|
||||||
|
public:
|
||||||
|
std::string GetName() const { return "emulator-release-buttons"; }
|
||||||
|
std::string GetDescription() const {
|
||||||
|
return "Release currently held emulator buttons";
|
||||||
|
}
|
||||||
|
std::string GetUsage() const {
|
||||||
|
return "emulator-release-buttons --buttons <button1>,<button2>,...";
|
||||||
|
}
|
||||||
|
absl::Status ValidateArgs(
|
||||||
|
const resources::ArgumentParser& parser) override {
|
||||||
|
return parser.RequireArgs({"buttons"});
|
||||||
|
}
|
||||||
|
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
|
resources::OutputFormatter& formatter) override;
|
||||||
|
};
|
||||||
|
|
||||||
|
class EmulatorHoldButtonsCommandHandler : public resources::CommandHandler {
|
||||||
|
public:
|
||||||
|
std::string GetName() const { return "emulator-hold-buttons"; }
|
||||||
|
std::string GetDescription() const {
|
||||||
|
return "Hold emulator buttons for a specified duration";
|
||||||
|
}
|
||||||
|
std::string GetUsage() const {
|
||||||
|
return "emulator-hold-buttons --buttons <button1>,<button2>,... --duration "
|
||||||
|
"<ms>";
|
||||||
|
}
|
||||||
|
absl::Status ValidateArgs(
|
||||||
|
const resources::ArgumentParser& parser) override {
|
||||||
|
return parser.RequireArgs({"buttons", "duration"});
|
||||||
|
}
|
||||||
|
absl::Status Execute(Rom* rom, const resources::ArgumentParser& parser,
|
||||||
|
resources::OutputFormatter& formatter) override;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace handlers
|
} // namespace handlers
|
||||||
} // namespace cli
|
} // namespace cli
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|||||||
46
src/cli/service/agent/agent_control_server.cc
Normal file
46
src/cli/service/agent/agent_control_server.cc
Normal file
@@ -0,0 +1,46 @@
|
|||||||
|
#include "cli/service/agent/agent_control_server.h"
|
||||||
|
#include "cli/service/agent/emulator_service_impl.h"
|
||||||
|
#include <grpcpp/server.h>
|
||||||
|
#include <grpcpp/server_builder.h>
|
||||||
|
#include <iostream>
|
||||||
|
|
||||||
|
namespace yaze::agent {
|
||||||
|
|
||||||
|
AgentControlServer::AgentControlServer(yaze::emu::Emulator* emulator)
|
||||||
|
: emulator_(emulator) {}
|
||||||
|
|
||||||
|
AgentControlServer::~AgentControlServer() {
|
||||||
|
Stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void AgentControlServer::Start() {
|
||||||
|
server_thread_ = std::thread(&AgentControlServer::Run, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
void AgentControlServer::Stop() {
|
||||||
|
if (server_) {
|
||||||
|
server_->Shutdown();
|
||||||
|
}
|
||||||
|
if (server_thread_.joinable()) {
|
||||||
|
server_thread_.join();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void AgentControlServer::Run() {
|
||||||
|
std::string server_address("0.0.0.0:50051");
|
||||||
|
EmulatorServiceImpl service(emulator_);
|
||||||
|
|
||||||
|
grpc::ServerBuilder builder;
|
||||||
|
builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());
|
||||||
|
builder.RegisterService(&service);
|
||||||
|
|
||||||
|
server_ = builder.BuildAndStart();
|
||||||
|
if (server_) {
|
||||||
|
std::cout << "AgentControlServer listening on " << server_address << std::endl;
|
||||||
|
server_->Wait();
|
||||||
|
} else {
|
||||||
|
std::cerr << "Failed to start AgentControlServer on " << server_address << std::endl;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace yaze::agent
|
||||||
32
src/cli/service/agent/agent_control_server.h
Normal file
32
src/cli/service/agent/agent_control_server.h
Normal file
@@ -0,0 +1,32 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <memory>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace grpc {
|
||||||
|
class Server;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace yaze::emu {
|
||||||
|
class Emulator;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace yaze::agent {
|
||||||
|
|
||||||
|
class AgentControlServer {
|
||||||
|
public:
|
||||||
|
AgentControlServer(yaze::emu::Emulator* emulator);
|
||||||
|
~AgentControlServer();
|
||||||
|
|
||||||
|
void Start();
|
||||||
|
void Stop();
|
||||||
|
|
||||||
|
private:
|
||||||
|
void Run();
|
||||||
|
|
||||||
|
yaze::emu::Emulator* emulator_; // Non-owning pointer
|
||||||
|
std::unique_ptr<grpc::Server> server_;
|
||||||
|
std::thread server_thread_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace yaze::agent
|
||||||
190
src/cli/service/agent/emulator_service_impl.cc
Normal file
190
src/cli/service/agent/emulator_service_impl.cc
Normal file
@@ -0,0 +1,190 @@
|
|||||||
|
#include "cli/service/agent/emulator_service_impl.h"
|
||||||
|
#include "app/emu/emulator.h"
|
||||||
|
#include "app/core/service/screenshot_utils.h"
|
||||||
|
#include "app/emu/input/input_backend.h" // Required for SnesButton enum
|
||||||
|
#include "absl/strings/escaping.h"
|
||||||
|
#include "absl/strings/str_format.h"
|
||||||
|
#include <fstream>
|
||||||
|
#include <thread>
|
||||||
|
|
||||||
|
namespace yaze::agent {
|
||||||
|
|
||||||
|
namespace {
|
||||||
|
// Helper to convert our gRPC Button enum to the emulator's SnesButton enum
|
||||||
|
emu::input::SnesButton ToSnesButton(Button button) {
|
||||||
|
using emu::input::SnesButton;
|
||||||
|
switch (button) {
|
||||||
|
case A: return SnesButton::A;
|
||||||
|
case B: return SnesButton::B;
|
||||||
|
case X: return SnesButton::X;
|
||||||
|
case Y: return SnesButton::Y;
|
||||||
|
case L: return SnesButton::L;
|
||||||
|
case R: return SnesButton::R;
|
||||||
|
case SELECT: return SnesButton::SELECT;
|
||||||
|
case START: return SnesButton::START;
|
||||||
|
case UP: return SnesButton::UP;
|
||||||
|
case DOWN: return SnesButton::DOWN;
|
||||||
|
case LEFT: return SnesButton::LEFT;
|
||||||
|
case RIGHT: return SnesButton::RIGHT;
|
||||||
|
default:
|
||||||
|
return SnesButton::B; // Default fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} // namespace
|
||||||
|
|
||||||
|
EmulatorServiceImpl::EmulatorServiceImpl(yaze::emu::Emulator* emulator)
|
||||||
|
: emulator_(emulator) {}
|
||||||
|
|
||||||
|
// --- Lifecycle ---
|
||||||
|
|
||||||
|
grpc::Status EmulatorServiceImpl::Start(grpc::ServerContext* context, const Empty* request, CommandResponse* response) {
|
||||||
|
if (!emulator_) return grpc::Status(grpc::StatusCode::UNAVAILABLE, "Emulator not initialized.");
|
||||||
|
emulator_->set_running(true);
|
||||||
|
response->set_success(true);
|
||||||
|
response->set_message("Emulator started.");
|
||||||
|
return grpc::Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
grpc::Status EmulatorServiceImpl::Stop(grpc::ServerContext* context, const Empty* request, CommandResponse* response) {
|
||||||
|
if (!emulator_) return grpc::Status(grpc::StatusCode::UNAVAILABLE, "Emulator not initialized.");
|
||||||
|
emulator_->set_running(false);
|
||||||
|
response->set_success(true);
|
||||||
|
response->set_message("Emulator stopped.");
|
||||||
|
return grpc::Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
grpc::Status EmulatorServiceImpl::Pause(grpc::ServerContext* context, const Empty* request, CommandResponse* response) {
|
||||||
|
if (!emulator_) return grpc::Status(grpc::StatusCode::UNAVAILABLE, "Emulator not initialized.");
|
||||||
|
emulator_->set_running(false);
|
||||||
|
response->set_success(true);
|
||||||
|
response->set_message("Emulator paused.");
|
||||||
|
return grpc::Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
grpc::Status EmulatorServiceImpl::Resume(grpc::ServerContext* context, const Empty* request, CommandResponse* response) {
|
||||||
|
if (!emulator_) return grpc::Status(grpc::StatusCode::UNAVAILABLE, "Emulator not initialized.");
|
||||||
|
emulator_->set_running(true);
|
||||||
|
response->set_success(true);
|
||||||
|
response->set_message("Emulator resumed.");
|
||||||
|
return grpc::Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
grpc::Status EmulatorServiceImpl::Reset(grpc::ServerContext* context, const Empty* request, CommandResponse* response) {
|
||||||
|
if (!emulator_) return grpc::Status(grpc::StatusCode::UNAVAILABLE, "Emulator not initialized.");
|
||||||
|
emulator_->snes().Reset(true); // Hard reset
|
||||||
|
response->set_success(true);
|
||||||
|
response->set_message("Emulator reset.");
|
||||||
|
return grpc::Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Input Control ---
|
||||||
|
|
||||||
|
grpc::Status EmulatorServiceImpl::PressButtons(grpc::ServerContext* context, const ButtonRequest* request, CommandResponse* response) {
|
||||||
|
if (!emulator_) return grpc::Status(grpc::StatusCode::UNAVAILABLE, "Emulator not initialized.");
|
||||||
|
auto& input_manager = emulator_->input_manager();
|
||||||
|
for (const auto& button : request->buttons()) {
|
||||||
|
input_manager.PressButton(ToSnesButton(static_cast<Button>(button)));
|
||||||
|
}
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(50));
|
||||||
|
for (const auto& button : request->buttons()) {
|
||||||
|
input_manager.ReleaseButton(ToSnesButton(static_cast<Button>(button)));
|
||||||
|
}
|
||||||
|
response->set_success(true);
|
||||||
|
return grpc::Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
grpc::Status EmulatorServiceImpl::ReleaseButtons(grpc::ServerContext* context, const ButtonRequest* request, CommandResponse* response) {
|
||||||
|
if (!emulator_) return grpc::Status(grpc::StatusCode::UNAVAILABLE, "Emulator not initialized.");
|
||||||
|
auto& input_manager = emulator_->input_manager();
|
||||||
|
for (const auto& button : request->buttons()) {
|
||||||
|
input_manager.ReleaseButton(ToSnesButton(static_cast<Button>(button)));
|
||||||
|
}
|
||||||
|
response->set_success(true);
|
||||||
|
return grpc::Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
grpc::Status EmulatorServiceImpl::HoldButtons(grpc::ServerContext* context, const ButtonHoldRequest* request, CommandResponse* response) {
|
||||||
|
if (!emulator_) return grpc::Status(grpc::StatusCode::UNAVAILABLE, "Emulator not initialized.");
|
||||||
|
auto& input_manager = emulator_->input_manager();
|
||||||
|
for (const auto& button : request->buttons()) {
|
||||||
|
input_manager.PressButton(ToSnesButton(static_cast<Button>(button)));
|
||||||
|
}
|
||||||
|
std::this_thread::sleep_for(std::chrono::milliseconds(request->duration_ms()));
|
||||||
|
for (const auto& button : request->buttons()) {
|
||||||
|
input_manager.ReleaseButton(ToSnesButton(static_cast<Button>(button)));
|
||||||
|
}
|
||||||
|
response->set_success(true);
|
||||||
|
return grpc::Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- State Inspection ---
|
||||||
|
|
||||||
|
grpc::Status EmulatorServiceImpl::GetGameState(grpc::ServerContext* context, const GameStateRequest* request, GameStateResponse* response) {
|
||||||
|
if (!emulator_ || !emulator_->is_snes_initialized()) {
|
||||||
|
return grpc::Status(grpc::StatusCode::UNAVAILABLE, "SNES is not initialized.");
|
||||||
|
}
|
||||||
|
auto& memory = emulator_->snes().memory();
|
||||||
|
|
||||||
|
response->set_game_mode(memory.ReadByte(0x7E0010));
|
||||||
|
response->set_link_state(memory.ReadByte(0x7E005D));
|
||||||
|
response->set_link_pos_x(memory.ReadWord(0x7E0020));
|
||||||
|
response->set_link_pos_y(memory.ReadWord(0x7E0022));
|
||||||
|
response->set_link_health(memory.ReadByte(0x7EF36D));
|
||||||
|
|
||||||
|
for (const auto& mem_req : request->memory_reads()) {
|
||||||
|
auto* mem_resp = response->add_memory_responses();
|
||||||
|
mem_resp->set_address(mem_req.address());
|
||||||
|
std::vector<uint8_t> data(mem_req.size());
|
||||||
|
for (uint32_t i = 0; i < mem_req.size(); ++i) {
|
||||||
|
data[i] = memory.ReadByte(mem_req.address() + i);
|
||||||
|
}
|
||||||
|
mem_resp->set_data(data.data(), data.size());
|
||||||
|
}
|
||||||
|
|
||||||
|
#ifdef YAZE_WITH_GRPC
|
||||||
|
if (request->include_screenshot()) {
|
||||||
|
auto screenshot = yaze::test::CaptureHarnessScreenshot();
|
||||||
|
if (screenshot.ok()) {
|
||||||
|
// Read the screenshot file and convert to PNG data
|
||||||
|
std::ifstream file(screenshot->file_path, std::ios::binary);
|
||||||
|
if (file.good()) {
|
||||||
|
std::string png_data((std::istreambuf_iterator<char>(file)),
|
||||||
|
std::istreambuf_iterator<char>());
|
||||||
|
response->set_screenshot_png(png_data);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return grpc::Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
grpc::Status EmulatorServiceImpl::ReadMemory(grpc::ServerContext* context, const MemoryRequest* request, MemoryResponse* response) {
|
||||||
|
if (!emulator_ || !emulator_->is_snes_initialized()) {
|
||||||
|
return grpc::Status(grpc::StatusCode::UNAVAILABLE, "SNES is not initialized.");
|
||||||
|
}
|
||||||
|
auto& memory = emulator_->snes().memory();
|
||||||
|
response->set_address(request->address());
|
||||||
|
std::vector<uint8_t> data(request->size());
|
||||||
|
for (uint32_t i = 0; i < request->size(); ++i) {
|
||||||
|
data[i] = memory.ReadByte(request->address() + i);
|
||||||
|
}
|
||||||
|
response->set_data(data.data(), data.size());
|
||||||
|
return grpc::Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
grpc::Status EmulatorServiceImpl::WriteMemory(grpc::ServerContext* context, const MemoryWriteRequest* request, CommandResponse* response) {
|
||||||
|
if (!emulator_ || !emulator_->is_snes_initialized()) {
|
||||||
|
return grpc::Status(grpc::StatusCode::UNAVAILABLE, "SNES is not initialized.");
|
||||||
|
}
|
||||||
|
auto& memory = emulator_->snes().memory();
|
||||||
|
const std::string& data = request->data();
|
||||||
|
for (uint32_t i = 0; i < data.size(); ++i) {
|
||||||
|
memory.WriteByte(request->address() + i, static_cast<uint8_t>(data[i]));
|
||||||
|
}
|
||||||
|
response->set_success(true);
|
||||||
|
response->set_message(absl::StrFormat("Wrote %d bytes to 0x%X.", data.size(), request->address()));
|
||||||
|
return grpc::Status::OK;
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace yaze::agent
|
||||||
38
src/cli/service/agent/emulator_service_impl.h
Normal file
38
src/cli/service/agent/emulator_service_impl.h
Normal file
@@ -0,0 +1,38 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <grpcpp/grpcpp.h>
|
||||||
|
#include "protos/emulator_service.grpc.pb.h"
|
||||||
|
|
||||||
|
// Forward declaration to avoid circular dependencies
|
||||||
|
namespace yaze::emu {
|
||||||
|
class Emulator;
|
||||||
|
}
|
||||||
|
|
||||||
|
namespace yaze::agent {
|
||||||
|
|
||||||
|
class EmulatorServiceImpl final : public EmulatorService::Service {
|
||||||
|
public:
|
||||||
|
explicit EmulatorServiceImpl(yaze::emu::Emulator* emulator);
|
||||||
|
|
||||||
|
// --- Lifecycle ---
|
||||||
|
grpc::Status Start(grpc::ServerContext* context, const Empty* request, CommandResponse* response) override;
|
||||||
|
grpc::Status Stop(grpc::ServerContext* context, const Empty* request, CommandResponse* response) override;
|
||||||
|
grpc::Status Pause(grpc::ServerContext* context, const Empty* request, CommandResponse* response) override;
|
||||||
|
grpc::Status Resume(grpc::ServerContext* context, const Empty* request, CommandResponse* response) override;
|
||||||
|
grpc::Status Reset(grpc::ServerContext* context, const Empty* request, CommandResponse* response) override;
|
||||||
|
|
||||||
|
// --- Input Control ---
|
||||||
|
grpc::Status PressButtons(grpc::ServerContext* context, const ButtonRequest* request, CommandResponse* response) override;
|
||||||
|
grpc::Status ReleaseButtons(grpc::ServerContext* context, const ButtonRequest* request, CommandResponse* response) override;
|
||||||
|
grpc::Status HoldButtons(grpc::ServerContext* context, const ButtonHoldRequest* request, CommandResponse* response) override;
|
||||||
|
|
||||||
|
// --- State Inspection ---
|
||||||
|
grpc::Status GetGameState(grpc::ServerContext* context, const GameStateRequest* request, GameStateResponse* response) override;
|
||||||
|
grpc::Status ReadMemory(grpc::ServerContext* context, const MemoryRequest* request, MemoryResponse* response) override;
|
||||||
|
grpc::Status WriteMemory(grpc::ServerContext* context, const MemoryWriteRequest* request, CommandResponse* response) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
yaze::emu::Emulator* emulator_; // Non-owning pointer to the emulator instance
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace yaze::agent
|
||||||
366
src/protos/emulator_service.proto
Normal file
366
src/protos/emulator_service.proto
Normal file
@@ -0,0 +1,366 @@
|
|||||||
|
syntax = "proto3";
|
||||||
|
|
||||||
|
package yaze.agent;
|
||||||
|
|
||||||
|
// The main service for controlling the yaze emulator
|
||||||
|
service EmulatorService {
|
||||||
|
// --- Lifecycle ---
|
||||||
|
rpc Start(Empty) returns (CommandResponse);
|
||||||
|
rpc Stop(Empty) returns (CommandResponse);
|
||||||
|
rpc Pause(Empty) returns (CommandResponse);
|
||||||
|
rpc Resume(Empty) returns (CommandResponse);
|
||||||
|
rpc Reset(Empty) returns (CommandResponse);
|
||||||
|
|
||||||
|
// --- Input Control ---
|
||||||
|
rpc PressButtons(ButtonRequest) returns (CommandResponse);
|
||||||
|
rpc ReleaseButtons(ButtonRequest) returns (CommandResponse);
|
||||||
|
rpc HoldButtons(ButtonHoldRequest) returns (CommandResponse);
|
||||||
|
|
||||||
|
// --- State Inspection (The Feedback Loop) ---
|
||||||
|
rpc GetGameState(GameStateRequest) returns (GameStateResponse);
|
||||||
|
rpc ReadMemory(MemoryRequest) returns (MemoryResponse);
|
||||||
|
rpc WriteMemory(MemoryWriteRequest) returns (CommandResponse);
|
||||||
|
|
||||||
|
// --- Advanced Debugging (NEW) ---
|
||||||
|
// Breakpoints
|
||||||
|
rpc AddBreakpoint(BreakpointRequest) returns (BreakpointResponse);
|
||||||
|
rpc RemoveBreakpoint(BreakpointIdRequest) returns (CommandResponse);
|
||||||
|
rpc ListBreakpoints(Empty) returns (BreakpointListResponse);
|
||||||
|
rpc SetBreakpointEnabled(BreakpointStateRequest) returns (CommandResponse);
|
||||||
|
|
||||||
|
// Watchpoints (memory access tracking)
|
||||||
|
rpc AddWatchpoint(WatchpointRequest) returns (WatchpointResponse);
|
||||||
|
rpc RemoveWatchpoint(WatchpointIdRequest) returns (CommandResponse);
|
||||||
|
rpc ListWatchpoints(Empty) returns (WatchpointListResponse);
|
||||||
|
rpc GetWatchpointHistory(WatchpointHistoryRequest) returns (WatchpointHistoryResponse);
|
||||||
|
|
||||||
|
// Execution Control
|
||||||
|
rpc StepInstruction(Empty) returns (StepResponse);
|
||||||
|
rpc RunToBreakpoint(Empty) returns (BreakpointHitResponse);
|
||||||
|
rpc StepOver(Empty) returns (StepResponse); // Step over subroutines
|
||||||
|
rpc StepOut(Empty) returns (StepResponse); // Step out of subroutine
|
||||||
|
|
||||||
|
// Disassembly & Code Analysis
|
||||||
|
rpc GetDisassembly(DisassemblyRequest) returns (DisassemblyResponse);
|
||||||
|
rpc GetExecutionTrace(TraceRequest) returns (TraceResponse);
|
||||||
|
|
||||||
|
// Symbol Management (for Oracle of Secrets labels)
|
||||||
|
rpc LoadSymbols(SymbolFileRequest) returns (CommandResponse);
|
||||||
|
rpc ResolveSymbol(SymbolLookupRequest) returns (SymbolLookupResponse);
|
||||||
|
rpc GetSymbolAt(AddressRequest) returns (SymbolLookupResponse);
|
||||||
|
|
||||||
|
// Debugging Session
|
||||||
|
rpc CreateDebugSession(DebugSessionRequest) returns (DebugSessionResponse);
|
||||||
|
rpc GetDebugStatus(Empty) returns (DebugStatusResponse);
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Message Definitions ---
|
||||||
|
|
||||||
|
message Empty {}
|
||||||
|
|
||||||
|
message CommandResponse {
|
||||||
|
bool success = 1;
|
||||||
|
string message = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum Button {
|
||||||
|
BUTTON_UNSPECIFIED = 0;
|
||||||
|
A = 1;
|
||||||
|
B = 2;
|
||||||
|
X = 3;
|
||||||
|
Y = 4;
|
||||||
|
L = 5;
|
||||||
|
R = 6;
|
||||||
|
SELECT = 7;
|
||||||
|
START = 8;
|
||||||
|
UP = 9;
|
||||||
|
DOWN = 10;
|
||||||
|
LEFT = 11;
|
||||||
|
RIGHT = 12;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ButtonRequest {
|
||||||
|
repeated Button buttons = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message ButtonHoldRequest {
|
||||||
|
repeated Button buttons = 1;
|
||||||
|
uint32 duration_ms = 2; // How long to hold the buttons
|
||||||
|
}
|
||||||
|
|
||||||
|
message GameStateRequest {
|
||||||
|
bool include_screenshot = 1;
|
||||||
|
repeated MemoryRequest memory_reads = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message GameStateResponse {
|
||||||
|
// Key player and game variables
|
||||||
|
uint32 game_mode = 1;
|
||||||
|
uint32 link_state = 2;
|
||||||
|
uint32 link_pos_x = 3;
|
||||||
|
uint32 link_pos_y = 4;
|
||||||
|
uint32 link_health = 5;
|
||||||
|
|
||||||
|
// Screenshot of the current frame
|
||||||
|
bytes screenshot_png = 6; // PNG encoded image data
|
||||||
|
|
||||||
|
// Results of any requested memory reads
|
||||||
|
repeated MemoryResponse memory_responses = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MemoryRequest {
|
||||||
|
uint32 address = 1;
|
||||||
|
uint32 size = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MemoryResponse {
|
||||||
|
uint32 address = 1;
|
||||||
|
bytes data = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message MemoryWriteRequest {
|
||||||
|
uint32 address = 1;
|
||||||
|
bytes data = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// --- Advanced Debugging Messages (NEW) ---
|
||||||
|
|
||||||
|
// Breakpoint types (matches BreakpointManager::Type)
|
||||||
|
enum BreakpointType {
|
||||||
|
BREAKPOINT_TYPE_UNSPECIFIED = 0;
|
||||||
|
EXECUTE = 1; // Break when PC reaches address
|
||||||
|
READ = 2; // Break when memory is read
|
||||||
|
WRITE = 3; // Break when memory is written
|
||||||
|
ACCESS = 4; // Break on read OR write
|
||||||
|
CONDITIONAL = 5; // Break when condition is true
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPU type for breakpoints
|
||||||
|
enum CpuType {
|
||||||
|
CPU_TYPE_UNSPECIFIED = 0;
|
||||||
|
CPU_65816 = 1;
|
||||||
|
SPC700 = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Breakpoint request
|
||||||
|
message BreakpointRequest {
|
||||||
|
uint32 address = 1;
|
||||||
|
BreakpointType type = 2;
|
||||||
|
CpuType cpu = 3;
|
||||||
|
string condition = 4; // Optional condition (e.g., "A > 0x10")
|
||||||
|
string description = 5; // User-friendly label
|
||||||
|
}
|
||||||
|
|
||||||
|
message BreakpointResponse {
|
||||||
|
bool success = 1;
|
||||||
|
uint32 breakpoint_id = 2;
|
||||||
|
string message = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BreakpointIdRequest {
|
||||||
|
uint32 breakpoint_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BreakpointStateRequest {
|
||||||
|
uint32 breakpoint_id = 1;
|
||||||
|
bool enabled = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BreakpointInfo {
|
||||||
|
uint32 id = 1;
|
||||||
|
uint32 address = 2;
|
||||||
|
BreakpointType type = 3;
|
||||||
|
CpuType cpu = 4;
|
||||||
|
bool enabled = 5;
|
||||||
|
string condition = 6;
|
||||||
|
string description = 7;
|
||||||
|
uint32 hit_count = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BreakpointListResponse {
|
||||||
|
repeated BreakpointInfo breakpoints = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message BreakpointHitResponse {
|
||||||
|
bool hit = 1;
|
||||||
|
BreakpointInfo breakpoint = 2;
|
||||||
|
CPUState cpu_state = 3;
|
||||||
|
string message = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Watchpoint messages
|
||||||
|
message WatchpointRequest {
|
||||||
|
uint32 start_address = 1;
|
||||||
|
uint32 end_address = 2; // For range watchpoints
|
||||||
|
bool track_reads = 3;
|
||||||
|
bool track_writes = 4;
|
||||||
|
bool break_on_access = 5;
|
||||||
|
string description = 6;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WatchpointResponse {
|
||||||
|
bool success = 1;
|
||||||
|
uint32 watchpoint_id = 2;
|
||||||
|
string message = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WatchpointIdRequest {
|
||||||
|
uint32 watchpoint_id = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WatchpointInfo {
|
||||||
|
uint32 id = 1;
|
||||||
|
uint32 start_address = 2;
|
||||||
|
uint32 end_address = 3;
|
||||||
|
bool track_reads = 4;
|
||||||
|
bool track_writes = 5;
|
||||||
|
bool break_on_access = 6;
|
||||||
|
bool enabled = 7;
|
||||||
|
string description = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WatchpointListResponse {
|
||||||
|
repeated WatchpointInfo watchpoints = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WatchpointHistoryRequest {
|
||||||
|
uint32 watchpoint_id = 1;
|
||||||
|
uint32 max_entries = 2; // Max history entries to return
|
||||||
|
}
|
||||||
|
|
||||||
|
message AccessLogEntry {
|
||||||
|
uint32 pc = 1;
|
||||||
|
uint32 address = 2;
|
||||||
|
uint32 old_value = 3;
|
||||||
|
uint32 new_value = 4;
|
||||||
|
bool is_write = 5;
|
||||||
|
uint64 cycle_count = 6;
|
||||||
|
string description = 7;
|
||||||
|
}
|
||||||
|
|
||||||
|
message WatchpointHistoryResponse {
|
||||||
|
repeated AccessLogEntry history = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// CPU State (for stepping/debugging)
|
||||||
|
message CPUState {
|
||||||
|
uint32 a = 1;
|
||||||
|
uint32 x = 2;
|
||||||
|
uint32 y = 3;
|
||||||
|
uint32 sp = 4;
|
||||||
|
uint32 pc = 5;
|
||||||
|
uint32 db = 6; // Data bank
|
||||||
|
uint32 pb = 7; // Program bank
|
||||||
|
uint32 d = 8; // Direct page
|
||||||
|
uint32 status = 9; // Processor status
|
||||||
|
bool flag_n = 10; // Negative
|
||||||
|
bool flag_v = 11; // Overflow
|
||||||
|
bool flag_d = 12; // Decimal
|
||||||
|
bool flag_i = 13; // Interrupt disable
|
||||||
|
bool flag_z = 14; // Zero
|
||||||
|
bool flag_c = 15; // Carry
|
||||||
|
uint64 cycles = 16;
|
||||||
|
}
|
||||||
|
|
||||||
|
message StepResponse {
|
||||||
|
bool success = 1;
|
||||||
|
CPUState cpu_state = 2;
|
||||||
|
DisassemblyLine instruction = 3;
|
||||||
|
string message = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Disassembly messages
|
||||||
|
message DisassemblyRequest {
|
||||||
|
uint32 start_address = 1;
|
||||||
|
uint32 count = 2; // Number of instructions
|
||||||
|
bool include_execution_count = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DisassemblyLine {
|
||||||
|
uint32 address = 1;
|
||||||
|
uint32 opcode = 2;
|
||||||
|
repeated uint32 operands = 3;
|
||||||
|
string mnemonic = 4;
|
||||||
|
string operand_str = 5;
|
||||||
|
uint32 size = 6;
|
||||||
|
uint64 execution_count = 7;
|
||||||
|
bool is_breakpoint = 8;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DisassemblyResponse {
|
||||||
|
repeated DisassemblyLine lines = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Execution trace
|
||||||
|
message TraceRequest {
|
||||||
|
uint32 max_entries = 1; // Max trace entries to return
|
||||||
|
uint32 start_address = 2; // Optional: filter by address range
|
||||||
|
uint32 end_address = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TraceEntry {
|
||||||
|
uint32 address = 1;
|
||||||
|
string instruction = 2;
|
||||||
|
CPUState cpu_state_before = 3;
|
||||||
|
uint64 cycle_count = 4;
|
||||||
|
}
|
||||||
|
|
||||||
|
message TraceResponse {
|
||||||
|
repeated TraceEntry entries = 1;
|
||||||
|
uint32 total_count = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Symbol management
|
||||||
|
message SymbolFileRequest {
|
||||||
|
string filepath = 1; // Path to symbol file (e.g., .sym, .map)
|
||||||
|
SymbolFormat format = 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
enum SymbolFormat {
|
||||||
|
SYMBOL_FORMAT_UNSPECIFIED = 0;
|
||||||
|
ASAR = 1; // Asar assembler format
|
||||||
|
WLA_DX = 2; // WLA-DX assembler format
|
||||||
|
CA65 = 3; // ca65 assembler format
|
||||||
|
MESEN = 4; // Mesen debug symbol format
|
||||||
|
CUSTOM_JSON = 5; // Custom JSON format
|
||||||
|
}
|
||||||
|
|
||||||
|
message SymbolLookupRequest {
|
||||||
|
string symbol_name = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message AddressRequest {
|
||||||
|
uint32 address = 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
message SymbolLookupResponse {
|
||||||
|
bool found = 1;
|
||||||
|
string symbol_name = 2;
|
||||||
|
uint32 address = 3;
|
||||||
|
string type = 4; // "function", "data", "label", etc.
|
||||||
|
string description = 5;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Debug session
|
||||||
|
message DebugSessionRequest {
|
||||||
|
string session_name = 1;
|
||||||
|
string rom_hash = 2; // For verification
|
||||||
|
bool enable_all_features = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DebugSessionResponse {
|
||||||
|
bool success = 1;
|
||||||
|
string session_id = 2;
|
||||||
|
string message = 3;
|
||||||
|
}
|
||||||
|
|
||||||
|
message DebugStatusResponse {
|
||||||
|
bool is_running = 1;
|
||||||
|
bool is_paused = 2;
|
||||||
|
CPUState cpu_state = 3;
|
||||||
|
uint32 active_breakpoints = 4;
|
||||||
|
uint32 active_watchpoints = 5;
|
||||||
|
BreakpointInfo last_breakpoint_hit = 6;
|
||||||
|
double fps = 7;
|
||||||
|
uint64 cycles = 8;
|
||||||
|
}
|
||||||
Reference in New Issue
Block a user