summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorOxore <oxore@protonmail.com>2022-08-30 16:49:38 +0300
committerOxore <oxore@protonmail.com>2022-08-30 16:49:38 +0300
commited3cf413dfe5f874f203f8b6a696af29cfc47dcd (patch)
tree9d9c181a568cf1833218444c0d2b26b33063435e
parent026894023fa53fa32fd342d18e05f55743cf6c4b (diff)
Impl emulator stepping with GDB
-rw-r--r--CMakeLists.txt2
-rw-r--r--bus.cpp22
-rw-r--r--emulator.cpp168
-rw-r--r--gdbremote_parser.cpp44
-rw-r--r--gdbremote_parser.hpp23
-rw-r--r--m68k_debugging.cpp144
-rw-r--r--m68k_debugging.hpp60
-rw-r--r--utils.hpp12
8 files changed, 458 insertions, 17 deletions
diff --git a/CMakeLists.txt b/CMakeLists.txt
index e5240aa..80b78d3 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -19,6 +19,7 @@ set(emulator_sources
bus.cpp
emulator.cpp
gdbremote_parser.cpp
+ m68k_debugging.cpp
)
set(musashi_m68k_sources
musashi-m68k/m68kcpu.c
@@ -58,4 +59,3 @@ add_executable(gdbremote
include_directories(
.
)
-
diff --git a/bus.cpp b/bus.cpp
index caff07c..aef20a4 100644
--- a/bus.cpp
+++ b/bus.cpp
@@ -3,6 +3,7 @@
#include "bus.hpp"
#include "musashi-m68k/m68k.h"
+#include "utils.hpp"
#include <cassert>
#include <cstdarg>
@@ -62,34 +63,35 @@ static inline unsigned int memory_read_concrete(
(base[address + 2] << 8) |
base[address + 3];
}
- __builtin_unreachable();
+ UNREACHABLE();
}
static inline struct read_result memory_read(
enum bitness bitness,
unsigned int address)
{
- if (is_in_range(address, ROM_START, ROM_SIZE))
- return (struct read_result){
+ if (is_in_range(address, ROM_START, ROM_SIZE)) {
+ return read_result{
memory_read_concrete(bitness, g_rom, address - ROM_START),
true,
};
- else if (is_in_range(address, RAM_START, RAM_SIZE))
- return (struct read_result){
+ } else if (is_in_range(address, RAM_START, RAM_SIZE)) {
+ return read_result{
memory_read_concrete(bitness, g_ram, address - RAM_START),
true,
};
- else if (is_in_range(address, IO1_START, IO1_SIZE))
- return (struct read_result){
+ } else if (is_in_range(address, IO1_START, IO1_SIZE)) {
+ return read_result{
memory_read_concrete(bitness, g_io1, address - IO1_START),
true,
};
- else if (is_in_range(address, IO2_START, IO2_SIZE))
- return (struct read_result){
+ } else if (is_in_range(address, IO2_START, IO2_SIZE)) {
+ return read_result{
memory_read_concrete(bitness, g_io2, address - IO2_START),
true,
};
- return (struct read_result){0, false};
+ }
+ return read_result{0, false};
}
static inline void memory_write_concrete(
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 <cassert>
#include <cstdio>
+#include <cstdint>
#include <cstdlib>
+#include <cstring>
#include <cstdarg>
#include <ctime>
+#include <sys/socket.h>
+#include <arpa/inet.h>
+#include <unistd.h>
#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<char>(c1 < 0xa ? c1 + '0' : c1 + ('a' - 0xa));
+ out[1] = static_cast<char>(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<const PacketDataReadMemory*>(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<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));
+ }
+ 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
diff --git a/gdbremote_parser.cpp b/gdbremote_parser.cpp
index d8f4f50..204690d 100644
--- a/gdbremote_parser.cpp
+++ b/gdbremote_parser.cpp
@@ -24,6 +24,13 @@ static inline bool isseparator(uint8_t byte)
return ':' == byte || ';' == byte || ',' == byte;
}
+static inline bool IsTokenTypeSeparator(TokenType token_type)
+{
+ return token_type == TokenType::kSeparatorColon ||
+ token_type == TokenType::kSeparatorComma ||
+ token_type == TokenType::kSeparatorSemicolon;
+}
+
static inline TokenType separatorTypeFromByte(uint8_t byte)
{
if (':' == byte) return TokenType::kSeparatorColon;
@@ -143,14 +150,47 @@ struct Command {
{
return Packet{PacketType::kInterrupt};
}
- static Packet parseContinue(const std::vector<Token>&&)
+ static Packet parseContinue(const std::vector<Token>&& tokens)
{
+ const std::string first = tokens[0].data;
+ constexpr size_t command_len = strlen("vCont");
+ // Check if it is "vCont?" command
+ if (first.length() > command_len && first[command_len] == '?') {
+ return Packet{PacketType::kContinueAskSupported};
+ }
+ // TODO arguments
return Packet{PacketType::kContinue};
}
static Packet parseReadGeneralRegisters(const std::vector<Token>&&)
{
return Packet{PacketType::kReadGeneralRegisters};
}
+ static Packet parseReadMemory(const std::vector<Token>&& tokens)
+ {
+ if (tokens.size() < 3 ||
+ tokens[0].type != TokenType::kArbitrary ||
+ !IsTokenTypeSeparator(tokens[1].type) ||
+ tokens[2].type != TokenType::kNumeric)
+ {
+ return Packet::None();
+ }
+ constexpr size_t command_len = strlen("m");
+ const std::string first = tokens[0].data;
+ const std::string second = tokens[2].data;
+ const uint32_t offset = strtol(
+ first.substr(command_len, first.length()-command_len).c_str(),
+ nullptr,
+ 16);
+ const uint32_t lenght = strtol(second.c_str(), nullptr, 16);
+ return Packet{
+ PacketType::kReadMemory,
+ std::make_unique<const PacketDataReadMemory>(offset, lenght),
+ };
+ }
+ static Packet parseStep(const std::vector<Token>&&)
+ {
+ return Packet{PacketType::kStep};
+ }
};
static const Command commands[] = {
@@ -169,7 +209,9 @@ static const Command commands[] = {
{ "\x03", Command::parseInterrupt },
{ "vCtrlC", Command::parseInterrupt },
{ "vCont", Command::parseContinue },
+ { "m", Command::parseReadMemory },
{ "g", Command::parseReadGeneralRegisters },
+ { "s", Command::parseStep },
};
Packet Packet::Parse(std::string packet_data)
diff --git a/gdbremote_parser.hpp b/gdbremote_parser.hpp
index 2e9fd88..88a0cf7 100644
--- a/gdbremote_parser.hpp
+++ b/gdbremote_parser.hpp
@@ -57,14 +57,18 @@ enum class PacketType: int {
kEnableExtendedMode,
kInterrupt,
kContinue,
+ kContinueAskSupported,
kReadGeneralRegisters,
+ kReadMemory,
+ kStep,
};
struct PacketData {
- PacketData() = delete;
+ virtual ~PacketData() {};
+protected:
PacketData(const PacketData&) = delete;
PacketData(PacketData&&) = delete;
- virtual ~PacketData() {}
+ PacketData() = default;
};
struct PacketDataSupportedFeatures: public PacketData {
@@ -72,9 +76,16 @@ struct PacketDataSupportedFeatures: public PacketData {
virtual ~PacketDataSupportedFeatures() {}
};
+struct PacketDataReadMemory: public PacketData {
+ PacketDataReadMemory(uint32_t a_offset, uint32_t a_length)
+ : offset(a_offset), length(a_length) {}
+ uint32_t offset{}, length{};
+ virtual ~PacketDataReadMemory() {}
+};
+
struct Packet {
const PacketType type{};
- const std::unique_ptr<PacketData> data{nullptr};
+ const std::unique_ptr<const PacketData> data{nullptr};
/** Convert raw packet data into a Packet
*
@@ -118,8 +129,14 @@ struct Packet {
return "vCtrlC";
case PacketType::kContinue:
return "vCont";
+ case PacketType::kContinueAskSupported:
+ return "vCont?";
case PacketType::kReadGeneralRegisters:
return "g";
+ case PacketType::kReadMemory:
+ return "m";
+ case PacketType::kStep:
+ return "s";
}
return "<unknown>";
}
diff --git a/m68k_debugging.cpp b/m68k_debugging.cpp
new file mode 100644
index 0000000..f9b0b38
--- /dev/null
+++ b/m68k_debugging.cpp
@@ -0,0 +1,144 @@
+/* SPDX-License-Identifier: Unlicense
+ */
+
+#include "m68k_debugging.hpp"
+#include "musashi-m68k/m68k.h"
+#include "utils.hpp"
+
+#include <cstdlib>
+#include <cassert>
+
+static inline m68k_register_t ConvertRegisterId(M68KRegister register_id)
+{
+ switch (register_id) {
+ case M68KRegister::kD0:
+ return M68K_REG_D0;
+ case M68KRegister::kD1:
+ return M68K_REG_D1;
+ case M68KRegister::kD2:
+ return M68K_REG_D2;
+ case M68KRegister::kD3:
+ return M68K_REG_D3;
+ case M68KRegister::kD4:
+ return M68K_REG_D4;
+ case M68KRegister::kD5:
+ return M68K_REG_D5;
+ case M68KRegister::kD6:
+ return M68K_REG_D6;
+ case M68KRegister::kD7:
+ return M68K_REG_D7;
+ case M68KRegister::kA0:
+ return M68K_REG_A0;
+ case M68KRegister::kA1:
+ return M68K_REG_A1;
+ case M68KRegister::kA2:
+ return M68K_REG_A2;
+ case M68KRegister::kA3:
+ return M68K_REG_A3;
+ case M68KRegister::kA4:
+ return M68K_REG_A4;
+ case M68KRegister::kA5:
+ return M68K_REG_A5;
+ case M68KRegister::kA6:
+ return M68K_REG_A6;
+ case M68KRegister::kA7:
+ return M68K_REG_A7;
+ case M68KRegister::kPS:
+ return M68K_REG_SR;
+ case M68KRegister::kPC:
+ return M68K_REG_PC;
+ case M68KRegister::kFP0:
+ case M68KRegister::kFPC:
+ case M68KRegister::kFPS:
+ case M68KRegister::kFPI:
+ case M68KRegister::kRegistersCount:
+ assert(!"Unsupported register_id");
+ break;
+ }
+ assert(!"Unknown register_id");
+ UNREACHABLE();
+}
+
+uint32_t M68KDebuggingControl::GetRegister(M68KRegister register_id) const
+{
+ return m68k_get_reg(nullptr, ConvertRegisterId(register_id));
+}
+
+M68KCPUState M68KDebuggingControl::GetCPUState() const
+{
+ return M68KCPUState{
+ static_cast<size_t>(M68KRegister::kPC) + 1,
+ {
+ GetRegister(M68KRegister::kD0),
+ GetRegister(M68KRegister::kD1),
+ GetRegister(M68KRegister::kD2),
+ GetRegister(M68KRegister::kD3),
+ GetRegister(M68KRegister::kD4),
+ GetRegister(M68KRegister::kD5),
+ GetRegister(M68KRegister::kD6),
+ GetRegister(M68KRegister::kD7),
+ GetRegister(M68KRegister::kA0),
+ GetRegister(M68KRegister::kA1),
+ GetRegister(M68KRegister::kA2),
+ GetRegister(M68KRegister::kA3),
+ GetRegister(M68KRegister::kA4),
+ GetRegister(M68KRegister::kA5),
+ GetRegister(M68KRegister::kA6),
+ GetRegister(M68KRegister::kA7),
+ GetRegister(M68KRegister::kPS),
+ GetRegister(M68KRegister::kPC),
+ },
+ };
+}
+
+void M68KDebuggingControl::SetRegister(M68KRegister register_id, uint32_t value)
+{
+ return m68k_set_reg(ConvertRegisterId(register_id), value);
+}
+
+void M68KDebuggingControl::Reset()
+{
+ m68k_pulse_reset();
+}
+
+void M68KDebuggingControl::Continue()
+{
+ // TODO
+ abort();
+}
+
+void M68KDebuggingControl::Pause()
+{
+ // TODO
+ abort();
+}
+
+uint8_t M68KDebuggingControl::Read8(uint32_t address) const
+{
+ return static_cast<uint8_t>(m68k_read_memory_8(address & 0x00FFFFFF));
+}
+
+uint16_t M68KDebuggingControl::Read16(uint32_t address) const
+{
+ return static_cast<uint16_t>(m68k_read_memory_16(address & 0x00FFFFFF));
+}
+
+uint32_t M68KDebuggingControl::Read32(uint32_t address) const
+{
+ return static_cast<uint32_t>(m68k_read_memory_32(address & 0x00FFFFFF));
+}
+
+void M68KDebuggingControl::Write8(uint32_t address, uint8_t value)
+{
+ m68k_write_memory_8(address, value);
+}
+
+void M68KDebuggingControl::Write16(uint32_t address, uint16_t value)
+{
+ m68k_write_memory_16(address, value);
+}
+
+void M68KDebuggingControl::Write32(uint32_t address, uint32_t value)
+{
+ m68k_write_memory_32(address, value);
+}
diff --git a/m68k_debugging.hpp b/m68k_debugging.hpp
new file mode 100644
index 0000000..4892493
--- /dev/null
+++ b/m68k_debugging.hpp
@@ -0,0 +1,60 @@
+/* SPDX-License-Identifier: Unlicense
+ */
+
+#pragma once
+
+#include <cstdint>
+#include <cstddef>
+
+enum class M68KRegister: size_t {
+ kD0 = 0,
+ kD1 = 1,
+ kD2 = 2,
+ kD3 = 3,
+ kD4 = 4,
+ kD5 = 5,
+ kD6 = 6,
+ kD7 = 7,
+ kA0 = 8,
+ kA1 = 9,
+ kA2 = 10,
+ kA3 = 11,
+ kA4 = 12,
+ kA5 = 13,
+ kA6 = 14, /* Address of executing stack frame */
+ kFP = kA6,
+ kA7 = 15, /* Address of top of stack (Stack pointer) */
+ kSP = kA7,
+ kPS = 16, /* Processor status (Condition code register) */
+ kPC = 17, /* Program counter */
+ kFP0 = 18, /* Floating point register 0 */
+ kFPC = 26, /* 68881 control register */
+ kFPS = 27, /* 68881 status register */
+ kFPI = 28, /* Floating point register 1 */
+ kRegistersCount,
+};
+
+struct M68KCPUState {
+ size_t registers_count{};
+ uint32_t registers[static_cast<size_t>(M68KRegister::kRegistersCount)]{};
+};
+
+class M68KDebuggingControl {
+public:
+ M68KDebuggingControl() = default;
+ uint32_t GetRegister(M68KRegister) const;
+ M68KCPUState GetCPUState() const;
+ void SetRegister(M68KRegister, uint32_t value);
+ void Reset();
+ void Continue();
+ void Pause();
+ uint8_t Read8(uint32_t address) const;
+ uint16_t Read16(uint32_t address) const;
+ uint32_t Read32(uint32_t address) const;
+ void Write8(uint32_t address, uint8_t value);
+ void Write16(uint32_t address, uint16_t value);
+ void Write32(uint32_t address, uint32_t value);
+private:
+ M68KDebuggingControl(M68KDebuggingControl&&) = delete;
+ M68KDebuggingControl(const M68KDebuggingControl&) = delete;
+};
diff --git a/utils.hpp b/utils.hpp
new file mode 100644
index 0000000..4a5e84d
--- /dev/null
+++ b/utils.hpp
@@ -0,0 +1,12 @@
+/* SPDX-License-Identifier: Unlicense
+ */
+
+#pragma once
+
+#ifdef __clang__
+# define UNREACHABLE __builtin_unreachable
+#elif __GNUC__
+# define UNREACHABLE __builtin_unreachable
+#else
+# define UNREACHABLE
+#endif