diff --git a/lib/zba-gdbstub b/lib/zba-gdbstub
index 479319e..eb8e517 160000
--- a/lib/zba-gdbstub
+++ b/lib/zba-gdbstub
@@ -1 +1 @@
-Subproject commit 479319e7cad78c3fd38b6865c56e9fe9e78d495c
+Subproject commit eb8e5175bd9738e92d10c47f75abb174f3624082
diff --git a/src/core/emu.zig b/src/core/emu.zig
index 1ae155f..de275cb 100644
--- a/src/core/emu.zig
+++ b/src/core/emu.zig
@@ -403,3 +403,128 @@ pub fn fastBoot(system: System) void {
cpu.bank.spsr[Bank.spsrIdx(.Supervisor)] = .{ .raw = 0x0000_0000 };
}
}
+
+pub const debug = struct {
+ const Interface = @import("gdbstub").Emulator;
+ const Server = @import("gdbstub").Server;
+ const AtomicBool = std.atomic.Atomic(bool);
+ const log = std.log.scoped(.gdbstub);
+
+ const nds7 = struct {
+ const target: []const u8 =
+ \\
+ \\ armv4t
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ ;
+
+ // Remember that a lot of memory regions are mirrored
+ const memory_map: []const u8 =
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ \\
+ ;
+ };
+
+ // FIXME: for now, assume ARM7
+ pub const Wrapper = struct {
+ system: System,
+ scheduler: *Scheduler,
+
+ pub fn init(system: System, scheduler: *Scheduler) @This() {
+ return .{ .system = system, .scheduler = scheduler };
+ }
+
+ pub fn interface(self: *@This(), allocator: Allocator) Interface {
+ return Interface.init(allocator, self);
+ }
+
+ // FIXME: What about ICTM? DTCM?
+ pub fn read(self: *const @This(), addr: u32) u8 {
+ return self.system.bus7.dbgRead(u8, addr);
+ }
+
+ pub fn write(self: *@This(), addr: u32, value: u8) void {
+ self.system.bus7.dbgWrite(u8, addr, value);
+ }
+
+ pub fn registers(self: *const @This()) *[16]u32 {
+ return &self.system.arm7tdmi.r;
+ }
+
+ pub fn cpsr(self: *const @This()) u32 {
+ return self.system.arm7tdmi.cpsr.raw;
+ }
+
+ pub fn step(self: *@This()) void {
+ const scheduler = self.scheduler;
+ const system = self.system;
+
+ var did_step: bool = false;
+
+ // TODO: keep in lockstep with runFrame
+ while (true) {
+ if (did_step) break;
+
+ switch (isHalted(system)) {
+ .both => scheduler.tick = scheduler.peekTimestamp(),
+ inline else => |halt| {
+ if (!dma9.step(system.arm946es) and comptime halt != .arm9) {
+ system.arm946es.step();
+ system.arm946es.step();
+ }
+
+ if (!dma7.step(system.arm7tdmi) and comptime halt != .arm7) {
+ system.arm7tdmi.step();
+ did_step = true;
+ }
+ },
+ }
+
+ if (scheduler.check()) |ev| {
+ const late = scheduler.tick - ev.tick;
+ scheduler.handle(system, ev, late);
+ }
+ }
+ }
+ };
+
+ pub fn run(allocator: Allocator, system: System, scheduler: *Scheduler, should_quit: *AtomicBool) !void {
+ var wrapper = Wrapper.init(system, scheduler);
+
+ var emu_interface = wrapper.interface(allocator);
+ defer emu_interface.deinit();
+
+ var server = try Server.init(emu_interface, .{ .target = nds7.target, .memory_map = nds7.memory_map });
+ defer server.deinit(allocator);
+
+ const thread = try std.Thread.spawn(.{}, Server.run, .{ &server, allocator, should_quit });
+ defer thread.join();
+ }
+};
diff --git a/src/main.zig b/src/main.zig
index 5d4a9e5..6cbc7b6 100644
--- a/src/main.zig
+++ b/src/main.zig
@@ -68,11 +68,17 @@ pub fn main() !void {
emu.fastBoot(system);
- var ui = try Ui.init(allocator);
- defer ui.deinit(allocator);
+ if (result.args.gdb == 0) {
+ var ui = try Ui.init(allocator);
+ defer ui.deinit(allocator);
- ui.setTitle(rom_title);
- try ui.run(&scheduler, system);
+ ui.setTitle(rom_title);
+ try ui.run(&scheduler, system);
+ } else {
+ var should_quit: std.atomic.Atomic(bool) = std.atomic.Atomic(bool).init(false);
+
+ try emu.debug.run(allocator, system, &scheduler, &should_quit);
+ }
}
fn handlePositional(result: ClapResult) ![]const u8 {