summaryrefslogtreecommitdiff
path: root/emulator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'emulator.cpp')
-rw-r--r--emulator.cpp149
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;