diff options
-rw-r--r-- | CMakeLists.txt | 3 | ||||
-rw-r--r-- | emulator.cpp | 149 | ||||
-rw-r--r-- | gdbremote_parser.cpp | 25 | ||||
-rw-r--r-- | gdbremote_parser.hpp | 2 | ||||
-rw-r--r-- | m68k_debugging.hpp | 9 |
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{}; }; |