/* SPDX-License-Identifier: Unlicense */ #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; va_start(args, fmt); vfprintf(stderr, fmt, args); va_end(args); fprintf(stderr, "\n"); exit(EXIT_FAILURE); } /* Called when the CPU pulses the RESET line */ void m68k_reset_callback(void) { // TODO } /* Called when the CPU acknowledges an interrupt */ int m68k_irq_ack(int level) { (void) level; // TODO exit_error("IRQ ack"); return M68K_INT_ACK_SPURIOUS; } static void make_hex(char* buff, unsigned int pc, unsigned int length) { char* ptr = buff; for (;length>0;length -= 2) { sprintf(ptr, "%04x", m68k_read_disassembler_16(pc)); pc += 2; ptr += 4; if (length > 2) *ptr++ = ' '; } } void m68k_instr_callback(int pc) { if (!DEBUG_TRACE_INSTRUCTIONS) return; char buff[100]; unsigned int instr_size = m68k_disassemble(buff, pc, M68K_CPU_TYPE_68000); char buff2[100]; make_hex(buff2, pc, instr_size); 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) { printf("Usage: sim \n"); exit(-1); } FILE* const fhandle = fopen(argv[1], "rb"); if (fhandle == NULL) exit_error("Unable to open %s", argv[1]); const size_t fread_ret = fread(g_rom, 1, ROM_SIZE, fhandle); if (fread_ret <= 0) exit_error("Error reading %s", argv[1]); printf("Read into ROM %zu bytes\n", fread_ret); m68k_init(); m68k_set_cpu_type(M68K_CPU_TYPE_68000); m68k_pulse_reset(); M68KDebuggingControl m68k_debug{}; emulator(m68k_debug); while (0) { // Values to execute determine the interleave rate. // Smaller values allow for more accurate interleaving with multiple // devices/CPUs but is more processor intensive. // 100000 is usually a good value to start at, then work from there. // Note that I am not emulating the correct clock speed! m68k_execute(100000); } return 0; }