summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--CMakeLists.txt3
-rw-r--r--emulator.cpp149
-rw-r--r--gdbremote_parser.cpp25
-rw-r--r--gdbremote_parser.hpp2
-rw-r--r--m68k_debugging.hpp9
5 files changed, 136 insertions, 52 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index 80b78d3..71ac411 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -47,7 +47,8 @@ target_include_directories(musashi_m68k PRIVATE
add_executable(emulator ${emulator_sources})
target_link_libraries(emulator musashi_m68k)
target_compile_definitions(emulator PRIVATE
- DEBUG_TRACE_INSTRUCTIONS=1
+ DEBUG_TRACE_INSTRUCTIONS=0
+ DEBUG_TRACE_GDB_REMOTE=1
)
# Target for GDB Remote Debugging protocol implementation testing
diff --git a/emulator.cpp b/emulator.cpp
index 1605995..96588cd 100644
--- a/emulator.cpp
+++ b/emulator.cpp
@@ -16,13 +16,19 @@
#include <sys/socket.h>
#include <arpa/inet.h>
#include <unistd.h>
+#include <fcntl.h>
#if !defined(DEBUG_TRACE_INSTRUCTIONS)
# define DEBUG_TRACE_INSTRUCTIONS 0
#endif
+#if !defined(DEBUG_TRACE_GDB_REMOTE)
+# define DEBUG_TRACE_GDB_REMOTE 0
+#endif
+
#define MESSAGE_BUFFER_SIZE 1024
+static constexpr struct timespec kOneMillisecond{0, 1000000};
char msg_buf[MESSAGE_BUFFER_SIZE];
static int set_socket_reuseaddr(int socket_fd)
@@ -40,6 +46,19 @@ static inline struct sockaddr_in sockaddr_in_any_ip_with_port(uint16_t port) {
return server;
}
+static inline int SetNonblock(int fd)
+{
+ const int flags = fcntl(fd, F_GETFL);
+ if (flags == -1) {
+ return errno;
+ }
+ const int ret = fcntl(fd, F_SETFL, flags | O_NONBLOCK);
+ if (ret == -1) {
+ return errno;
+ }
+ return 0;
+}
+
static int setup_socket(uint16_t port)
{
// This creates the socket - or quits
@@ -48,6 +67,13 @@ static int setup_socket(uint16_t port)
perror("Could not create socket");
return -1;
}
+ // Set O_NONBLOCK for socket
+ const int ret = SetNonblock(socket_fd);
+ if (ret) {
+ fprintf(stderr, "fcntl(socket_fd, F_SETFL O_NONBLOCK): %s\n", strerror(ret));
+ close(socket_fd);
+ return -1;
+ }
// Make TCP port reusable in case of kill or crash.
// Deliberate explanation: https://stackoverflow.com/a/3233022
if (set_socket_reuseaddr(socket_fd) == -1) {
@@ -81,10 +107,16 @@ static std::string CreateResponse(
using namespace GDBRemote;
if (0) {
} else if (packet.type == PacketType::kQueryHaltReason) {
- return "S05";
+ return "S05"; // TODO real reason
} else if (packet.type == PacketType::kStep) {
m68k_execute(1);
- return "S05";
+ return "S05"; // TODO real reason
+ } else if (packet.type == PacketType::kContinue) {
+ m68k_debug.SetRunning(true);
+ return "OK";
+ } else if (packet.type == PacketType::kInterrupt) {
+ m68k_debug.SetRunning(false);
+ return "S05"; // TODO real reason
} else if (packet.type == PacketType::kSetThreadForCont) {
return "OK";
} else if (packet.type == PacketType::kQueryAttached) {
@@ -172,56 +204,95 @@ void m68k_instr_callback(int pc)
fflush(stdout);
}
+void ParseAndReact(
+ int conn_fd,
+ GDBRemote::ExchangeContext& exchange_ctx,
+ M68KDebuggingControl& m68k_debug,
+ const char* msg_data,
+ size_t msg_data_len)
+{
+ for (size_t i = 0; i < static_cast<size_t>(msg_data_len); i++) {
+ const auto res = exchange_ctx.Consume(msg_data[i]);
+ if (res == nullptr) continue;
+ if (res->packet.length() > 0) {
+ printf("<- \"%s\"\n", exchange_ctx.GetLastPacket().c_str());
+ const auto packet = GDBRemote::Packet::Parse(res->packet);
+ printf(
+ " Packet type: \"%s\"\n",
+ GDBRemote::Packet::PacketTypeToString(packet.type));
+ }
+ if (res->ack.length() > 0) {
+ printf("-> \"%s\"\n", res->ack.c_str());
+ if (send(conn_fd, &res->ack[0], res->ack.length(), 0) == -1)
+ perror("Send failed (ack/nak)");
+ }
+ if (res->packet.length() > 0) {
+ const auto packet = GDBRemote::Packet::Parse(res->packet);
+ const auto response =
+ exchange_ctx.WrapDataToSend(CreateResponse(m68k_debug, packet));
+ printf("-> \"%s\"\n", response.c_str());
+ if (send(conn_fd, &response[0], response.length(), 0) == -1)
+ perror("Send failed (response)");
+ }
+ }
+}
+
int emulator(M68KDebuggingControl& m68k_debug)
{
const int port = 3333;
const int socket_fd = setup_socket(port);
if (socket_fd == -1)
return EXIT_FAILURE;
- printf("Listening TCP 0.0.0.0:%u\n", port);
- listen(socket_fd, 4); // Mark socket as listener
- struct sockaddr client_address;
- socklen_t address_len;
- const int conn_fd = accept(socket_fd, &client_address, &address_len);
- if (conn_fd == -1) {
- perror("Accept failed");
+ // Mark socket as listener
+ if (listen(socket_fd, 4) == -1 ) {
+ perror("Listen failed");
close(socket_fd);
return EXIT_FAILURE;
}
- puts("Connection accepted");
- GDBRemote::ExchangeContext exchange_ctx{};
- ssize_t read_size;
- while ((read_size = recv(conn_fd, msg_buf, MESSAGE_BUFFER_SIZE, 0)) > 0) {
- for (size_t i = 0; i < static_cast<size_t>(read_size); i++) {
- const auto res = exchange_ctx.Consume(msg_buf[i]);
- if (res == nullptr) continue;
- if (res->packet.length() > 0) {
- if (0) printf("<- \"%s\"\n", exchange_ctx.GetLastPacket().c_str());
- const auto packet = GDBRemote::Packet::Parse(res->packet);
- if (0) printf(
- " Packet type: \"%s\"\n",
- GDBRemote::Packet::PacketTypeToString(packet.type));
+ printf("Listening TCP 0.0.0.0:%u\n", port);
+ struct sockaddr client_address{};
+ socklen_t address_len{};
+ while (1) {
+ const int conn_fd = accept(socket_fd, &client_address, &address_len);
+ if (conn_fd == -1 ) {
+ if (errno == EWOULDBLOCK) {
+ // TODO turn off O_NONBLOCK and poll instead of sleep, only use
+ // nonlock when freerunning
+ clock_nanosleep(CLOCK_MONOTONIC, 0, &kOneMillisecond, nullptr);
+ continue;
}
- if (res->ack.length() > 0) {
- if (0) printf("-> \"%s\"\n", res->ack.c_str());
- if (send(conn_fd, &res->ack[0], res->ack.length(), 0) == -1)
- perror("Send failed (ack/nak)");
+ perror("Accept failed");
+ close(socket_fd);
+ return EXIT_FAILURE;
+ }
+ puts("Connection accepted");
+ // Set O_NONBLOCK for connection
+ const int ret = SetNonblock(conn_fd);
+ if (ret == -1) {
+ perror("fcntl(conn_fd, F_SETFL O_NONBLOCK)");
+ return -1;
+ }
+ GDBRemote::ExchangeContext exchange_ctx{};
+ while (1) {
+ const ssize_t read_size = recv(conn_fd, msg_buf, MESSAGE_BUFFER_SIZE, 0);
+ if (read_size > 0) {
+ ParseAndReact(conn_fd, exchange_ctx, m68k_debug, msg_buf, read_size);
+ continue;
+ } else if (read_size == 0) {
+ puts("Client disconnected");
+ break;
+ } else if (read_size == -1 && errno != EWOULDBLOCK) {
+ perror("Recv failed");
+ break;
}
- if (res->packet.length() > 0) {
- const auto packet = GDBRemote::Packet::Parse(res->packet);
- const auto response =
- exchange_ctx.WrapDataToSend(CreateResponse(m68k_debug, packet));
- if (0) printf("-> \"%s\"\n", response.c_str());
- if (send(conn_fd, &response[0], response.length(), 0) == -1)
- perror("Send failed (response)");
+ if (m68k_debug.IsRunning()) {
+ m68k_execute(1000);
}
+ // TODO turn off O_NONBLOCK and poll instead of sleep, only use
+ // nonlock when freerunning
+ clock_nanosleep(CLOCK_MONOTONIC, 0, &kOneMillisecond, nullptr);
}
- memset(msg_buf, '\0', MESSAGE_BUFFER_SIZE);
- }
- if (read_size == 0) {
- puts("Client disconnected");
- } else if (read_size == -1) {
- perror("Recv failed");
+ close(conn_fd);
}
close(socket_fd);
return 0;
diff --git a/gdbremote_parser.cpp b/gdbremote_parser.cpp
index 204690d..641590f 100644
--- a/gdbremote_parser.cpp
+++ b/gdbremote_parser.cpp
@@ -150,7 +150,7 @@ struct Command {
{
return Packet{PacketType::kInterrupt};
}
- static Packet parseContinue(const std::vector<Token>&& tokens)
+ static Packet parseContinueVCont(const std::vector<Token>&& tokens)
{
const std::string first = tokens[0].data;
constexpr size_t command_len = strlen("vCont");
@@ -161,9 +161,9 @@ struct Command {
// TODO arguments
return Packet{PacketType::kContinue};
}
- static Packet parseReadGeneralRegisters(const std::vector<Token>&&)
+ static Packet parseContinue(const std::vector<Token>&&)
{
- return Packet{PacketType::kReadGeneralRegisters};
+ return Packet{PacketType::kContinue};
}
static Packet parseReadMemory(const std::vector<Token>&& tokens)
{
@@ -187,6 +187,10 @@ struct Command {
std::make_unique<const PacketDataReadMemory>(offset, lenght),
};
}
+ static Packet parseReadGeneralRegisters(const std::vector<Token>&&)
+ {
+ return Packet{PacketType::kReadGeneralRegisters};
+ }
static Packet parseStep(const std::vector<Token>&&)
{
return Packet{PacketType::kStep};
@@ -208,7 +212,8 @@ static const Command commands[] = {
{ "!", Command::parseEnableExtendedMode },
{ "\x03", Command::parseInterrupt },
{ "vCtrlC", Command::parseInterrupt },
- { "vCont", Command::parseContinue },
+ { "vCont", Command::parseContinueVCont },
+ { "c", Command::parseContinue },
{ "m", Command::parseReadMemory },
{ "g", Command::parseReadGeneralRegisters },
{ "s", Command::parseStep },
@@ -217,17 +222,19 @@ static const Command commands[] = {
Packet Packet::Parse(std::string packet_data)
{
const auto tokens = Token::Tokenize(packet_data);
- if (tokens.size() == 0) return Packet::None();
+ if (tokens.size() == 0) {
+ printf("Warning: no tokens to parse\n");
+ return Packet::None();
+ }
const auto first_token = tokens[0];
- if (first_token.type != TokenType::kArbitrary) return Packet::None();
for (const auto& command: commands) {
const auto cmdlen = command.name.length();
const auto toklen = first_token.data.length();
- const auto len = std::min(cmdlen, cmdlen);
// Make sure first token fully includes the command from the beginning
// and not necessary fully equals to it.
+ // TODO tokenize as if the command is always a separate token
if (cmdlen <= toklen && 0 == memcmp(
- &command.name[0], &first_token.data[0], len))
+ &command.name[0], &first_token.data[0], cmdlen))
{
return command.parse(std::move(tokens));
}
@@ -286,7 +293,7 @@ std::unique_ptr<ExchangeResult> ExchangeContext::Consume(uint8_t byte)
return ExchangeResult::Ack(packet_data);
} else {
printf(
- "Checksum mismatch received: %02X, expected: %02X\n",
+ "Checksum mismatch: received=%02x, expected=%02x\n",
given_checksum, checksum);
printf("Packet data: \"%s\"\n", packet_data.c_str());
return ExchangeResult::Nak();
diff --git a/gdbremote_parser.hpp b/gdbremote_parser.hpp
index 88a0cf7..4f2f8f3 100644
--- a/gdbremote_parser.hpp
+++ b/gdbremote_parser.hpp
@@ -128,7 +128,7 @@ struct Packet {
case PacketType::kInterrupt:
return "vCtrlC";
case PacketType::kContinue:
- return "vCont";
+ return "c";
case PacketType::kContinueAskSupported:
return "vCont?";
case PacketType::kReadGeneralRegisters:
diff --git a/m68k_debugging.hpp b/m68k_debugging.hpp
index 4892493..d1acce8 100644
--- a/m68k_debugging.hpp
+++ b/m68k_debugging.hpp
@@ -42,9 +42,14 @@ struct M68KCPUState {
class M68KDebuggingControl {
public:
M68KDebuggingControl() = default;
+ M68KDebuggingControl(M68KDebuggingControl&&) = delete;
+ M68KDebuggingControl(const M68KDebuggingControl&) = delete;
+
uint32_t GetRegister(M68KRegister) const;
M68KCPUState GetCPUState() const;
void SetRegister(M68KRegister, uint32_t value);
+ bool IsRunning() const { return _is_running; }
+ void SetRunning(bool running) { _is_running = running; }
void Reset();
void Continue();
void Pause();
@@ -54,7 +59,7 @@ public:
void Write8(uint32_t address, uint8_t value);
void Write16(uint32_t address, uint16_t value);
void Write32(uint32_t address, uint32_t value);
+
private:
- M68KDebuggingControl(M68KDebuggingControl&&) = delete;
- M68KDebuggingControl(const M68KDebuggingControl&) = delete;
+ bool _is_running{};
};