diff options
Diffstat (limited to 'emulator.cpp')
-rw-r--r-- | emulator.cpp | 149 |
1 files changed, 110 insertions, 39 deletions
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; |