summaryrefslogtreecommitdiff
path: root/emulator.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'emulator.cpp')
-rw-r--r--emulator.cpp246
1 files changed, 183 insertions, 63 deletions
diff --git a/emulator.cpp b/emulator.cpp
index 72d5184..e3ac6bb 100644
--- a/emulator.cpp
+++ b/emulator.cpp
@@ -1,6 +1,19 @@
/* SPDX-License-Identifier: Unlicense
*/
+#define OPTPARSE_IMPLEMENTATION
+#define OPTPARSE_API static
+#ifdef __GNUC__
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wold-style-cast"
+#pragma GCC diagnostic ignored "-Wshadow"
+#endif
+#include "optparse/optparse.h"
+#ifdef __GNUC__
+#pragma GCC diagnostic pop
+#endif
+
+#include "chardev.hpp"
#include "bus.hpp"
#include "graphics.hpp"
#include "vdp.hpp"
@@ -22,9 +35,9 @@
#include <ctime>
#include <fcntl.h>
#include <sys/socket.h>
-#include <memory>
#include <unistd.h>
#include <signal.h>
+#include <netdb.h>
#if !defined(DEBUG_TRACE_INSTRUCTIONS)
# define DEBUG_TRACE_INSTRUCTIONS 0
@@ -85,15 +98,6 @@ static int set_socket_reuseaddr(const int socket_fd)
return setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (void*)&val, len);
}
-static inline struct sockaddr_in sockaddr_in_any_ip_with_port(const uint16_t port)
-{
- struct sockaddr_in server{};
- server.sin_family = AF_INET;
- server.sin_addr.s_addr = INADDR_ANY;
- server.sin_port = htons(port);
- return server;
-}
-
static inline int SetNonblock(const int fd)
{
const int flags = fcntl(fd, F_GETFL);
@@ -107,36 +111,67 @@ static inline int SetNonblock(const int fd)
return 0;
}
-static int setup_socket(const uint16_t port)
+static int setup_socket(
+ const char* host, uint16_t port, char* addrstr, size_t addrstr_size)
{
- // This creates the socket - or quits
- const int socket_fd = socket(AF_INET, SOCK_STREAM, 0);
- if (socket_fd == -1) {
- perror("Could not create socket");
+ struct addrinfo hints{}, * result{};
+ hints.ai_family = AF_UNSPEC;
+ hints.ai_socktype = SOCK_STREAM;
+ hints.ai_flags = AI_PASSIVE;
+ char port_str[6]{};
+ snprintf(port_str, sizeof(port_str), "%u", port);
+ const int gai_ret = getaddrinfo(host, port_str, &hints, &result);
+ if (gai_ret != 0) {
+ fprintf(stderr, "getaddrinfo(%s): %s\n", host, gai_strerror(gai_ret));
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) {
- perror("setsockopt failed");
+ int socket_fd = -1;
+ for (struct addrinfo* rp = result; rp != nullptr; rp = rp->ai_next) {
+ socket_fd = socket(rp->ai_family, rp->ai_socktype, rp->ai_protocol);
+ if (socket_fd == -1)
+ continue;
+ // Set O_NONBLOCK for the 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);
+ socket_fd = -1;
+ break;
+ }
+ // Make TCP port reusable in case of kill or crash.
+ // Deliberate explanation: https://stackoverflow.com/a/3233022
+ if (set_socket_reuseaddr(socket_fd) == -1) {
+ perror("setsockopt failed");
+ close(socket_fd);
+ socket_fd = -1;
+ break;
+ }
+ if (0 == bind(socket_fd, rp->ai_addr, rp->ai_addrlen)) {
+ void* addr_ptr;
+ if (rp->ai_addr->sa_family == AF_INET) {
+ addr_ptr = reinterpret_cast<void*>(
+ &((struct sockaddr_in*)rp->ai_addr)->sin_addr);
+ } else if (rp->ai_addr->sa_family == AF_INET6) {
+ addr_ptr = reinterpret_cast<void*>(
+ &((struct sockaddr_in6*)rp->ai_addr)->sin6_addr);
+ } else {
+ assert(false);
+ }
+ inet_ntop(rp->ai_addr->sa_family, addr_ptr, addrstr, addrstr_size);
+ addrstr[addrstr_size - 1] = '\0';
+ break;
+ }
+ fprintf(stderr, "Binding to %s:%u failed\n", addrstr, port);
close(socket_fd);
- return -1;
+ socket_fd = -1;
}
- puts("Socket created");
- const struct sockaddr_in server = sockaddr_in_any_ip_with_port(port);
- if (bind(socket_fd, (struct sockaddr *)&server, sizeof(server)) == -1) {
- perror("Bind failed");
- close(socket_fd);
+ freeaddrinfo(result);
+ if (socket_fd == -1) {
+ fprintf(stderr, "Could not bind or create socket: "
+ "getaddrinfo did not give any suitable entry\n");
return -1;
}
- printf("Binding to 0.0.0.0:%u done\n", port);
+ fprintf(stderr, "Binding to %s:%u done\n", addrstr, port);
return socket_fd;
}
@@ -451,10 +486,28 @@ static void RunSingleVideoCycle(M68KDebuggingControl& m68k_debug, Graphics& grap
g_cycles_counter += m68k_execute(400000);
}
-int emulator(M68KDebuggingControl& m68k_debug, Graphics& graphics)
+static int emulator(M68KDebuggingControl& m68k_debug, Graphics& graphics, CharDev &gdb_chardev)
{
- const int port = 3333;
- const int socket_fd = setup_socket(port);
+ if (gdb_chardev.type == CharDevType::kUndefined) {
+ while (!quit) {
+ RunSingleVideoCycle(m68k_debug, graphics);
+ }
+ return EXIT_SUCCESS;
+ }
+ constexpr size_t addrstr_size = 160;
+ char addrstr[addrstr_size]{};
+ const int port = gdb_chardev.port ? gdb_chardev.port : 3333;
+ int socket_fd;
+ if (gdb_chardev.path_len) {
+ char *host = new (std::nothrow) char[gdb_chardev.path_len + 1];
+ assert(host);
+ memcpy(host, gdb_chardev.path, gdb_chardev.path_len);
+ host[gdb_chardev.path_len] = '\0';
+ socket_fd = setup_socket(host, port, addrstr, addrstr_size);
+ delete [] host;
+ } else {
+ socket_fd = setup_socket("0.0.0.0", port, addrstr, addrstr_size);
+ }
if (socket_fd == -1)
return EXIT_FAILURE;
// Mark socket as listener
@@ -463,16 +516,24 @@ int emulator(M68KDebuggingControl& m68k_debug, Graphics& graphics)
close(socket_fd);
return EXIT_FAILURE;
}
- printf("Listening TCP 0.0.0.0:%u\n", port);
+ printf("Listening TCP %s:%u\n", addrstr, port);
while (!quit) {
struct sockaddr client_address{};
socklen_t address_len{};
const int conn_fd = accept(socket_fd, &client_address, &address_len);
- if (conn_fd == -1 ) {
+ if (conn_fd == -1) {
+ gdb_chardev.fd = conn_fd;
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);
+ if (m68k_debug.IsRunning()) {
+ RunSingleVideoCycle(m68k_debug, graphics);
+ if (m68k_debug.HasBreakpoint()) {
+ m68k_debug.SetRunning(false);
+ puts("ResetPendingBreakpoint");
+ m68k_debug.ResetPendingBreakpoint();
+ }
+ } else {
+ clock_nanosleep(CLOCK_MONOTONIC, 0, &kOneMillisecond, nullptr);
+ }
continue;
}
perror("Accept failed");
@@ -512,57 +573,116 @@ int emulator(M68KDebuggingControl& m68k_debug, Graphics& graphics)
if (send(conn_fd, &response[0], response.length(), 0) == -1)
perror("Send failed (response)");
}
+ } else {
+ clock_nanosleep(CLOCK_MONOTONIC, 0, &kOneMillisecond, nullptr);
}
- // TODO turn off O_NONBLOCK and poll instead of sleep, only use
- // nonlock when freerunning
- clock_nanosleep(CLOCK_MONOTONIC, 0, &kOneMillisecond, nullptr);
}
close(conn_fd);
+ gdb_chardev.fd = -1;
g_no_ack_mode = false; // TODO move to GDB::ExchangeContext
}
close(socket_fd);
return 0;
}
-void sigint_handler(int sig)
+static void sigint_handler(int sig)
{
printf("Signal %d received, exiting\n",sig);
quit = true;
}
-int main(int argc, char* argv[])
+static void PrintUsage(FILE *s, const char *argv0)
{
- if (argc != 2)
- {
- printf("Usage: %s <program file>\n", argv[0]);
- exit(-1);
- }
-
- FILE* const fhandle = fopen(argv[1], "rb");
+ // Please, keep all lines in 80 columns range when printed.
+ fprintf(s,
+ "Usage: %s [options] <program-file-name>\n"
+ "Options:\n"
+ " -h, --help Show this message.\n"
+ " -g, --gdb[=DEV] Accept GDB connection on DEV (default: 'tcp::3333').\n"
+ " -S, --stop Freeze CPU at startup (use 'c' in GDB to start\n"
+ " execution).\n"
+ , argv0);
+}
- if (fhandle == NULL)
+int main(int, char* argv[])
+{
+ struct optparse_long longopts[] = {
+ {"help", 'h', OPTPARSE_NONE},
+ {"gdb", 'g', OPTPARSE_OPTIONAL},
+ {"stop", 'S', OPTPARSE_NONE},
+ {},
+ };
+ struct CharDev gdb_chardev{};
+ const char *program_file_name = nullptr;
+ bool stop = false;
+ struct optparse options;
+ optparse_init(&options, argv);
+ // Parse opts
+ int option;
+ while ((option = optparse_long(&options, longopts, nullptr)) != -1) {
+ switch (option) {
+ case 'h':
+ PrintUsage(stdout, argv[0]);
+ return EXIT_SUCCESS;
+ break;
+ case 'g':
+ {
+ const char *path = options.optarg ? options.optarg : "tcp::3333";
+ gdb_chardev = CharDev::Parse(path);
+ if (gdb_chardev.type == CharDevType::kPty) {
+ fprintf(stderr, "main: PTY chardev currently is not supported\n");
+ return EXIT_FAILURE;
+ } else if (gdb_chardev.type == CharDevType::kUndefined) {
+ fprintf(stderr, "invalid chardev specified: \"%s\"\n", path);
+ return EXIT_FAILURE;
+ }
+ }
+ break;
+ case 'S':
+ stop = true;
+ printf("stop=%s\n", stop ? "true" : "false");
+ break;
+ case '?':
+ fprintf(stderr, "main: optparse_long: Error: \"%s\"\n", options.errmsg);
+ return EXIT_FAILURE;
+ }
+ }
+ // Parse input file name
+ char *arg;
+ while ((arg = optparse_arg(&options))) {
+ if (program_file_name == nullptr) {
+ program_file_name = arg;
+ } else {
+ fprintf(stderr, "error: too many free arguments provided\n");
+ return EXIT_FAILURE;
+ }
+ }
+ if (program_file_name == nullptr) {
+ fprintf(stderr, "main: Error: no program file name specified, see usage below.\n");
+ PrintUsage(stderr, argv[0]);
+ return EXIT_FAILURE;
+ }
+ FILE* const fhandle = fopen(program_file_name, "rb");
+ if (fhandle == nullptr) {
exit_error("Unable to open %s", argv[1]);
-
+ }
const size_t fread_ret = fread(g_rom, 1, ROM_SIZE, fhandle);
- if (fread_ret <= 0)
+ if (fread_ret <= 0) {
exit_error("Error reading %s", argv[1]);
+ }
printf("Read into ROM %zu bytes\n", fread_ret);
-
struct sigaction sa;
sa.sa_handler = sigint_handler;
- sigaction(SIGINT, &sa, NULL);
-
+ sigaction(SIGINT, &sa, nullptr);
Graphics graphics{};
if (!graphics.IsOk()) {
return EXIT_FAILURE;
}
-
m68k_init();
m68k_set_cpu_type(M68K_CPU_TYPE_68000);
m68k_pulse_reset();
g_cycles_counter += m68k_execute(1); // Skip reset cycles
-
- emulator(g_m68k_debug, graphics);
-
+ g_m68k_debug.SetRunning(!stop);
+ emulator(g_m68k_debug, graphics, gdb_chardev);
return EXIT_SUCCESS;
}