From 71b89bc9ceb59f2603cf4b0635849269597a4823 Mon Sep 17 00:00:00 2001 From: Oxore Date: Tue, 30 Apr 2024 01:38:42 +0300 Subject: Impl --stop and --gdb options --- emulator.cpp | 246 ++++++++++++++++++++++++++++++++++++++++++++--------------- 1 file changed, 183 insertions(+), 63 deletions(-) (limited to 'emulator.cpp') 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 #include #include -#include #include #include +#include #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( + &((struct sockaddr_in*)rp->ai_addr)->sin_addr); + } else if (rp->ai_addr->sa_family == AF_INET6) { + addr_ptr = reinterpret_cast( + &((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 \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] \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; } -- cgit v1.2.3