From 5f3be3f2f52d08f4198ec1a52570c2ea38ce4aad Mon Sep 17 00:00:00 2001 From: Rekai Musuka Date: Mon, 13 Feb 2023 20:01:40 -0600 Subject: [PATCH] feat: allow gui and gdbstub to run in parallel --- lib/zba-gdbstub | 2 +- src/core/ppu.zig | 3 +-- src/main.zig | 47 +++++++++++++++++++++++++++++------------------ src/platform.zig | 28 ++++++++++++++++++++-------- 4 files changed, 51 insertions(+), 29 deletions(-) diff --git a/lib/zba-gdbstub b/lib/zba-gdbstub index c1158b5..6d6a109 160000 --- a/lib/zba-gdbstub +++ b/lib/zba-gdbstub @@ -1 +1 @@ -Subproject commit c1158b547e54d82f5ac7aba4cf372c6e061b1eab +Subproject commit 6d6a109a086361d300da675db8445ede74be58f6 diff --git a/src/core/ppu.zig b/src/core/ppu.zig index fc88cfe..b50fdd9 100644 --- a/src/core/ppu.zig +++ b/src/core/ppu.zig @@ -990,8 +990,7 @@ pub const Ppu = struct { cpu.handleInterrupt(); } - // See if HBlank DMA is present and not enabled - + // If we're not also in VBlank, attempt to run any pending DMA Reqs if (!self.dispstat.vblank.read()) dma.onBlanking(cpu.bus, .HBlank); diff --git a/src/main.zig b/src/main.zig index 1f72bb1..6493aa0 100644 --- a/src/main.zig +++ b/src/main.zig @@ -4,14 +4,17 @@ const known_folders = @import("known_folders"); const clap = @import("clap"); const config = @import("config.zig"); +const emu = @import("core/emu.zig"); const Gui = @import("platform.zig").Gui; const Bus = @import("core/Bus.zig"); const Arm7tdmi = @import("core/cpu.zig").Arm7tdmi; const Scheduler = @import("core/scheduler.zig").Scheduler; const FilePaths = @import("util.zig").FilePaths; - +const FpsTracker = @import("util.zig").FpsTracker; const Allocator = std.mem.Allocator; +const Atomic = std.atomic.Atomic; + const log = std.log.scoped(.Cli); const width = @import("core/ppu.zig").width; const height = @import("core/ppu.zig").height; @@ -88,6 +91,10 @@ pub fn main() void { cpu.fastBoot(); } + var quit = Atomic(bool).init(false); + var gui = Gui.init(&bus.pak.title, &bus.apu, width, height) catch |e| exitln("failed to init gui: {}", .{e}); + defer gui.deinit(); + if (result.args.gdb) { const Server = @import("gdbstub").Server; const EmuThing = @import("core/emu.zig").EmuThing; @@ -96,29 +103,33 @@ pub fn main() void { var emulator = wrapper.interface(allocator); defer emulator.deinit(); - { - const frames_per_second: usize = 60; - const emu = @import("core/emu.zig"); - - var i: usize = 0; - while (i < frames_per_second * 120) : (i += 1) { - emu.runFrame(&scheduler, &cpu); - - std.debug.print("Frame {:0>3}/{:0>3}\r", .{ i, frames_per_second * 120 }); - } - } - log.info("Ready to connect", .{}); var server = Server.init(emulator) catch |e| exitln("failed to init gdb server: {}", .{e}); defer server.deinit(allocator); - server.run(allocator) catch |e| exitln("gdb server crashed: {}", .{e}); - } else { - var gui = Gui.init(&bus.pak.title, &bus.apu, width, height) catch |e| exitln("failed to init gui: {}", .{e}); - defer gui.deinit(); + log.info("Starting GDB Server Thread", .{}); - gui.run(&cpu, &scheduler) catch |e| exitln("failed to run gui thread: {}", .{e}); + const thread = std.Thread.spawn(.{}, Server.run, .{ &server, allocator, &quit }) catch |e| exitln("gdb server thread crashed: {}", .{e}); + defer thread.join(); + + gui.run(.{ + .cpu = &cpu, + .scheduler = &scheduler, + .quit = &quit, + }) catch |e| exitln("main thread panicked: {}", .{e}); + } else { + var tracker = FpsTracker.init(); + + const thread = std.Thread.spawn(.{}, emu.run, .{ &quit, &scheduler, &cpu, &tracker }) catch |e| exitln("emu thread panicked: {}", .{e}); + defer thread.join(); + + gui.run(.{ + .cpu = &cpu, + .scheduler = &scheduler, + .tracker = &tracker, + .quit = &quit, + }) catch |e| exitln("main thread panicked: {}", .{e}); } } diff --git a/src/platform.zig b/src/platform.zig index 44316a6..c5edb98 100644 --- a/src/platform.zig +++ b/src/platform.zig @@ -154,9 +154,17 @@ pub const Gui = struct { return tex_id; } - pub fn run(self: *Self, cpu: *Arm7tdmi, scheduler: *Scheduler) !void { - var quit = std.atomic.Atomic(bool).init(false); - var tracker = FpsTracker.init(); + const RunOptions = struct { + quit: *std.atomic.Atomic(bool), + tracker: ?*FpsTracker = null, + cpu: *Arm7tdmi, + scheduler: *Scheduler, + }; + + pub fn run(self: *Self, opt: RunOptions) !void { + const cpu = opt.cpu; + const tracker = opt.tracker; + const quit = opt.quit; var buffer_ids = Self.generateBuffers(); defer { @@ -169,13 +177,15 @@ pub const Gui = struct { const tex_id = Self.generateTexture(cpu.bus.ppu.framebuf.get(.Renderer)); defer gl.deleteTextures(1, &tex_id); - const thread = try std.Thread.spawn(.{}, emu.run, .{ &quit, scheduler, cpu, &tracker }); - defer thread.join(); - var title_buf: [0x100]u8 = undefined; emu_loop: while (true) { var event: SDL.SDL_Event = undefined; + + // This might be true if the emu is running via a gdbstub server + // and the gdb stub exits first + if (quit.load(.Monotonic)) break :emu_loop; + while (SDL.SDL_PollEvent(&event) != 0) { switch (event.type) { SDL.SDL_QUIT => break :emu_loop, @@ -238,8 +248,10 @@ pub const Gui = struct { gl.drawElements(gl.TRIANGLES, 6, gl.UNSIGNED_INT, null); SDL.SDL_GL_SwapWindow(self.window); - const dyn_title = std.fmt.bufPrintZ(&title_buf, "ZBA | {s} [Emu: {}fps] ", .{ self.title, tracker.value() }) catch unreachable; - SDL.SDL_SetWindowTitle(self.window, dyn_title.ptr); + if (tracker) |t| { + const dyn_title = std.fmt.bufPrintZ(&title_buf, "ZBA | {s} [Emu: {}fps] ", .{ self.title, t.value() }) catch unreachable; + SDL.SDL_SetWindowTitle(self.window, dyn_title.ptr); + } } quit.store(true, .Monotonic); // Terminate Emulator Thread