From ed3cf413dfe5f874f203f8b6a696af29cfc47dcd Mon Sep 17 00:00:00 2001 From: Oxore Date: Tue, 30 Aug 2022 16:49:38 +0300 Subject: Impl emulator stepping with GDB --- emulator.cpp | 168 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 166 insertions(+), 2 deletions(-) (limited to 'emulator.cpp') diff --git a/emulator.cpp b/emulator.cpp index 0e3fb29..1605995 100644 --- a/emulator.cpp +++ b/emulator.cpp @@ -2,17 +2,124 @@ */ #include "bus.hpp" +#include "m68k_debugging.hpp" +#include "gdbremote_parser.hpp" #include "musashi-m68k/m68k.h" +#include #include +#include #include +#include #include #include +#include +#include +#include #if !defined(DEBUG_TRACE_INSTRUCTIONS) # define DEBUG_TRACE_INSTRUCTIONS 0 #endif +#define MESSAGE_BUFFER_SIZE 1024 + +char msg_buf[MESSAGE_BUFFER_SIZE]; + +static int set_socket_reuseaddr(int socket_fd) +{ + const int val = 1; + const socklen_t len = sizeof(val); + return setsockopt(socket_fd, SOL_SOCKET, SO_REUSEADDR, (void*)&val, len); +} + +static inline struct sockaddr_in sockaddr_in_any_ip_with_port(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 int setup_socket(uint16_t port) +{ + // 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"); + 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"); + close(socket_fd); + return -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); + return -1; + } + printf("Binding to 0.0.0.0:%u done\n", port); + return socket_fd; +} + +static inline void ConvertByteToHex(uint8_t byte, char* out) +{ + const uint8_t c1 = (byte >> 4) & 0x0f; + const uint8_t c2 = byte& 0x0f; + out[0] = static_cast(c1 < 0xa ? c1 + '0' : c1 + ('a' - 0xa)); + out[1] = static_cast(c2 < 0xa ? c2 + '0' : c2 + ('a' - 0xa)); +} + +static std::string CreateResponse( + M68KDebuggingControl& m68k_debug, + const GDBRemote::Packet& packet) +{ + using namespace GDBRemote; + if (0) { + } else if (packet.type == PacketType::kQueryHaltReason) { + return "S05"; + } else if (packet.type == PacketType::kStep) { + m68k_execute(1); + return "S05"; + } else if (packet.type == PacketType::kSetThreadForCont) { + return "OK"; + } else if (packet.type == PacketType::kQueryAttached) { + return "1"; + } else if (packet.type == PacketType::kInterrupt) { + return "OK"; + } else if (packet.type == PacketType::kReadMemory) { + const auto * const data = + reinterpret_cast(packet.data.get()); + const uint32_t offset = data->offset; + const uint32_t length = data->length; + auto ret_data = std::string(length * 2, '\0'); + for (uint32_t i = 0; i < length; i++) { + const uint8_t byte = m68k_debug.Read8(i + offset); + ConvertByteToHex(byte, &ret_data[i*2]); + } + return ret_data; + } else if (packet.type == PacketType::kReadGeneralRegisters) { + const M68KCPUState state = m68k_debug.GetCPUState(); + std::string result{}; + for (size_t i = 0; i < state.registers_count ;i++) + { + constexpr size_t value_size = 8; + char value[value_size + 1]{}; + const int ret = snprintf(value, value_size + 1, "%08x", state.registers[i]); + assert(ret == value_size); + result += std::string(value, value_size); + } + return result; + } else if (packet.type == PacketType::kContinueAskSupported) { + return "vCont:c:s"; + } + return ""; +} + static void exit_error(const char* fmt, ...) { va_list args; @@ -61,10 +168,65 @@ void m68k_instr_callback(int pc) m68k_disassemble(buff, pc, M68K_CPU_TYPE_68000); char buff2[100]; make_hex(buff2, pc, instr_size); - printf("E %08X: %-20s: %s\n", pc, buff2, buff); + printf(" %08X: %-20s: %s\n", pc, buff2, buff); fflush(stdout); } +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"); + 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(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)); + } + 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)"); + } + 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)"); + } + } + memset(msg_buf, '\0', MESSAGE_BUFFER_SIZE); + } + if (read_size == 0) { + puts("Client disconnected"); + } else if (read_size == -1) { + perror("Recv failed"); + } + close(socket_fd); + return 0; +} + int main(int argc, char* argv[]) { if (argc != 2) @@ -87,7 +249,9 @@ int main(int argc, char* argv[]) m68k_set_cpu_type(M68K_CPU_TYPE_68000); m68k_pulse_reset(); - while (1) + M68KDebuggingControl m68k_debug{}; + emulator(m68k_debug); + while (0) { // Values to execute determine the interleave rate. // Smaller values allow for more accurate interleaving with multiple -- cgit v1.2.3