Compare commits
No commits in common. "05b7a9014d3c763937b0fe1b0a754863a2ab0640" and "9183e6850de553adc3ab53f3768f1fa6314c9c26" have entirely different histories.
05b7a9014d
...
9183e6850d
|
@ -19,8 +19,8 @@
|
||||||
.hash = "1220f48518ce22882e102255ed3bcdb7aeeb4891f50b2cdd3bd74b5b2e24d3149ba2",
|
.hash = "1220f48518ce22882e102255ed3bcdb7aeeb4891f50b2cdd3bd74b5b2e24d3149ba2",
|
||||||
},
|
},
|
||||||
.@"zba-util" = .{
|
.@"zba-util" = .{
|
||||||
.url = "https://git.musuka.dev/paoda/zba-util/archive/14ea006f4ffae77a333de4993c89690ce94d4abc.tar.gz",
|
.url = "https://git.musuka.dev/paoda/zba-util/archive/322c798e384a0d24cc84ffcfa2e4a3ca807798a0.tar.gz",
|
||||||
.hash = "1220f0d7c5802ae0a297844f96b2226ccc3d4d895277278e4345c3660161debbe85d",
|
.hash = "12209ce0e729460b997706e47a53a32f1842672cd120189e612f4871731780a30ed0",
|
||||||
},
|
},
|
||||||
.tomlz = .{
|
.tomlz = .{
|
||||||
.url = "https://github.com/mattyhall/tomlz/archive/47067cd7c902485f7d6e928331fd171ed47f72da.tar.gz",
|
.url = "https://github.com/mattyhall/tomlz/archive/47067cd7c902485f7d6e928331fd171ed47f72da.tar.gz",
|
||||||
|
|
|
@ -6,7 +6,8 @@ const Scheduler = @import("scheduler.zig").Scheduler;
|
||||||
const Arm7tdmi = @import("arm32").Arm7tdmi;
|
const Arm7tdmi = @import("arm32").Arm7tdmi;
|
||||||
const Bus = @import("Bus.zig");
|
const Bus = @import("Bus.zig");
|
||||||
const Tracker = @import("../util.zig").FpsTracker;
|
const Tracker = @import("../util.zig").FpsTracker;
|
||||||
const Channel = @import("../util.zig").Queue;
|
|
||||||
|
pub const Message = enum { Pause, Resume, Quit };
|
||||||
|
|
||||||
const stepDmaTransfer = @import("cpu_util.zig").stepDmaTransfer;
|
const stepDmaTransfer = @import("cpu_util.zig").stepDmaTransfer;
|
||||||
const isHalted = @import("cpu_util.zig").isHalted;
|
const isHalted = @import("cpu_util.zig").isHalted;
|
||||||
|
@ -16,27 +17,10 @@ const Timer = std.time.Timer;
|
||||||
pub const Synchro = struct {
|
pub const Synchro = struct {
|
||||||
const AtomicBool = std.atomic.Atomic(bool);
|
const AtomicBool = std.atomic.Atomic(bool);
|
||||||
|
|
||||||
// FIXME: This Enum ends up being really LARGE!!!
|
|
||||||
pub const Message = union(enum) {
|
|
||||||
rom_path: [std.fs.MAX_PATH_BYTES]u8,
|
|
||||||
bios_path: [std.fs.MAX_PATH_BYTES]u8,
|
|
||||||
restart: void,
|
|
||||||
};
|
|
||||||
|
|
||||||
paused: AtomicBool = AtomicBool.init(true), // FIXME: can ui_busy and paused be the same?
|
paused: AtomicBool = AtomicBool.init(true), // FIXME: can ui_busy and paused be the same?
|
||||||
should_quit: AtomicBool = AtomicBool.init(false),
|
should_quit: AtomicBool = AtomicBool.init(false),
|
||||||
|
|
||||||
ch: Channel(Message),
|
emu_access: std.Thread.Mutex = .{},
|
||||||
|
|
||||||
pub fn init(allocator: std.mem.Allocator) !@This() {
|
|
||||||
const msg_buf = try allocator.alloc(Message, 1);
|
|
||||||
return .{ .ch = Channel(Message).init(msg_buf) };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn deinit(self: *@This(), allocator: std.mem.Allocator) void {
|
|
||||||
allocator.free(self.ch.inner.buf);
|
|
||||||
self.* = undefined;
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/// 4 Cycles in 1 dot
|
/// 4 Cycles in 1 dot
|
||||||
|
@ -91,10 +75,9 @@ fn inner(comptime kind: RunKind, audio_sync: bool, cpu: *Arm7tdmi, scheduler: *S
|
||||||
log.info("Emulation w/out video sync", .{});
|
log.info("Emulation w/out video sync", .{});
|
||||||
|
|
||||||
while (!sync.should_quit.load(.Monotonic)) {
|
while (!sync.should_quit.load(.Monotonic)) {
|
||||||
handleChannel(cpu, &sync.ch);
|
|
||||||
if (sync.paused.load(.Monotonic)) continue;
|
if (sync.paused.load(.Monotonic)) continue;
|
||||||
|
|
||||||
runFrame(scheduler, cpu);
|
runFrame(sync, scheduler, cpu);
|
||||||
audioSync(audio_sync, bus_ptr.apu.stream, &bus_ptr.apu.is_buffer_full);
|
audioSync(audio_sync, bus_ptr.apu.stream, &bus_ptr.apu.is_buffer_full);
|
||||||
|
|
||||||
if (kind == .UnlimitedFPS) tracker.?.tick();
|
if (kind == .UnlimitedFPS) tracker.?.tick();
|
||||||
|
@ -106,10 +89,9 @@ fn inner(comptime kind: RunKind, audio_sync: bool, cpu: *Arm7tdmi, scheduler: *S
|
||||||
var wake_time: u64 = frame_period;
|
var wake_time: u64 = frame_period;
|
||||||
|
|
||||||
while (!sync.should_quit.load(.Monotonic)) {
|
while (!sync.should_quit.load(.Monotonic)) {
|
||||||
handleChannel(cpu, &sync.ch);
|
|
||||||
if (sync.paused.load(.Monotonic)) continue;
|
if (sync.paused.load(.Monotonic)) continue;
|
||||||
|
|
||||||
runFrame(scheduler, cpu);
|
runFrame(sync, scheduler, cpu);
|
||||||
const new_wake_time = videoSync(&timer, wake_time);
|
const new_wake_time = videoSync(&timer, wake_time);
|
||||||
|
|
||||||
// Spin to make up the difference of OS scheduler innacuracies
|
// Spin to make up the difference of OS scheduler innacuracies
|
||||||
|
@ -127,23 +109,10 @@ fn inner(comptime kind: RunKind, audio_sync: bool, cpu: *Arm7tdmi, scheduler: *S
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
inline fn handleChannel(cpu: *Arm7tdmi, channel: *Channel(Synchro.Message)) void {
|
pub fn runFrame(sync: *Synchro, sched: *Scheduler, cpu: *Arm7tdmi) void {
|
||||||
const message = channel.pop() orelse return;
|
sync.emu_access.lock();
|
||||||
|
defer sync.emu_access.unlock();
|
||||||
|
|
||||||
switch (message) {
|
|
||||||
.rom_path => |path_buf| {
|
|
||||||
const path = std.mem.sliceTo(&path_buf, 0);
|
|
||||||
replaceGamepak(cpu, path) catch |e| log.err("failed to replace GamePak: {}", .{e});
|
|
||||||
},
|
|
||||||
.bios_path => |path_buf| {
|
|
||||||
const path = std.mem.sliceTo(&path_buf, 0);
|
|
||||||
replaceBios(cpu, path) catch |e| log.err("failed to replace BIOS: {}", .{e});
|
|
||||||
},
|
|
||||||
.restart => reset(cpu),
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn runFrame(sched: *Scheduler, cpu: *Arm7tdmi) void {
|
|
||||||
const frame_end = sched.tick + cycles_per_frame;
|
const frame_end = sched.tick + cycles_per_frame;
|
||||||
|
|
||||||
while (sched.tick < frame_end) {
|
while (sched.tick < frame_end) {
|
||||||
|
@ -280,21 +249,21 @@ pub const EmuThing = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
fn reset(cpu: *Arm7tdmi) void {
|
pub fn reset(cpu: *Arm7tdmi) void {
|
||||||
// @breakpoint();
|
// @breakpoint();
|
||||||
cpu.sched.reset(); // Yes this is order sensitive, see the PPU reset for why
|
cpu.sched.reset(); // Yes this is order sensitive, see the PPU reset for why
|
||||||
cpu.bus.reset();
|
cpu.bus.reset();
|
||||||
cpu.reset();
|
cpu.reset();
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replaceGamepak(cpu: *Arm7tdmi, file_path: []const u8) !void {
|
pub fn replaceGamepak(cpu: *Arm7tdmi, file_path: []const u8) !void {
|
||||||
const bus_ptr: *Bus = @ptrCast(@alignCast(cpu.bus.ptr));
|
const bus_ptr: *Bus = @ptrCast(@alignCast(cpu.bus.ptr));
|
||||||
|
|
||||||
try bus_ptr.replaceGamepak(file_path);
|
try bus_ptr.replaceGamepak(file_path);
|
||||||
reset(cpu);
|
reset(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn replaceBios(cpu: *Arm7tdmi, file_path: []const u8) !void {
|
pub fn replaceBios(cpu: *Arm7tdmi, file_path: []const u8) !void {
|
||||||
const bus_ptr: *Bus = @ptrCast(@alignCast(cpu.bus.ptr));
|
const bus_ptr: *Bus = @ptrCast(@alignCast(cpu.bus.ptr));
|
||||||
|
|
||||||
const allocator = bus_ptr.bios.allocator;
|
const allocator = bus_ptr.bios.allocator;
|
||||||
|
|
|
@ -13,7 +13,6 @@ const Gui = @import("platform.zig").Gui;
|
||||||
const Arm7tdmi = @import("arm32").Arm7tdmi;
|
const Arm7tdmi = @import("arm32").Arm7tdmi;
|
||||||
const Scheduler = @import("core/scheduler.zig").Scheduler;
|
const Scheduler = @import("core/scheduler.zig").Scheduler;
|
||||||
const Bus = @import("core/Bus.zig");
|
const Bus = @import("core/Bus.zig");
|
||||||
const Synchro = @import("core/emu.zig").Synchro;
|
|
||||||
|
|
||||||
const RingBuffer = @import("zba-util").RingBuffer;
|
const RingBuffer = @import("zba-util").RingBuffer;
|
||||||
const Dimensions = @import("platform.zig").Dimensions;
|
const Dimensions = @import("platform.zig").Dimensions;
|
||||||
|
@ -71,13 +70,11 @@ pub const State = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn draw(state: *State, sync: *Synchro, dim: Dimensions, cpu: *const Arm7tdmi, tex_id: GLuint) bool {
|
pub fn draw(state: *State, win_dim: Dimensions, tex_id: GLuint, cpu: *Arm7tdmi) bool {
|
||||||
const scn_scale = config.config().host.win_scale;
|
const scn_scale = config.config().host.win_scale;
|
||||||
const bus_ptr: *Bus = @ptrCast(@alignCast(cpu.bus.ptr));
|
const bus_ptr: *Bus = @ptrCast(@alignCast(cpu.bus.ptr));
|
||||||
|
|
||||||
zgui.backend.newFrame(@floatFromInt(dim.width), @floatFromInt(dim.height));
|
zgui.backend.newFrame(@floatFromInt(win_dim.width), @floatFromInt(win_dim.height));
|
||||||
|
|
||||||
state.title = handleTitle(&bus_ptr.pak.title);
|
|
||||||
|
|
||||||
{
|
{
|
||||||
_ = zgui.beginMainMenuBar();
|
_ = zgui.beginMainMenuBar();
|
||||||
|
@ -90,59 +87,42 @@ pub fn draw(state: *State, sync: *Synchro, dim: Dimensions, cpu: *const Arm7tdmi
|
||||||
state.should_quit = true;
|
state.should_quit = true;
|
||||||
|
|
||||||
if (zgui.menuItem("Insert ROM", .{})) blk: {
|
if (zgui.menuItem("Insert ROM", .{})) blk: {
|
||||||
const file_path = tmp: {
|
const maybe_path = nfd.openFileDialog("gba", null) catch |e| {
|
||||||
const path_opt = nfd.openFileDialog("gba", null) catch |e| {
|
log.err("failed to open file dialog: {}", .{e});
|
||||||
log.err("file dialog failed to open: {}", .{e});
|
break :blk;
|
||||||
break :blk;
|
};
|
||||||
};
|
|
||||||
|
|
||||||
break :tmp path_opt orelse {
|
const file_path = maybe_path orelse {
|
||||||
log.warn("did not receive a file path", .{});
|
log.warn("did not receive a file path", .{});
|
||||||
break :blk;
|
break :blk;
|
||||||
};
|
|
||||||
};
|
};
|
||||||
defer nfd.freePath(file_path);
|
defer nfd.freePath(file_path);
|
||||||
|
|
||||||
log.info("user chose: \"{s}\"", .{file_path});
|
log.info("user chose: \"{s}\"", .{file_path});
|
||||||
|
emu.replaceGamepak(cpu, file_path) catch |e| {
|
||||||
const message = tmp: {
|
log.err("failed to replace GamePak: {}", .{e});
|
||||||
var msg: Synchro.Message = .{ .rom_path = undefined };
|
|
||||||
@memcpy(msg.rom_path[0..file_path.len], file_path);
|
|
||||||
break :tmp msg;
|
|
||||||
};
|
|
||||||
|
|
||||||
sync.ch.push(message) catch |e| {
|
|
||||||
log.err("failed to send file path to emu thread: {}", .{e});
|
|
||||||
break :blk;
|
break :blk;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
state.title = handleTitle(&bus_ptr.pak.title);
|
||||||
state.emulation = .{ .Transition = .Active };
|
state.emulation = .{ .Transition = .Active };
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zgui.menuItem("Load BIOS", .{})) blk: {
|
if (zgui.menuItem("Load BIOS", .{})) blk: {
|
||||||
const file_path = tmp: {
|
const maybe_path = nfd.openFileDialog("bin", null) catch |e| {
|
||||||
const path_opt = nfd.openFileDialog("bin", null) catch |e| {
|
log.err("failed to open file dialog: {}", .{e});
|
||||||
log.err("file dialog failed to open: {}", .{e});
|
break :blk;
|
||||||
break :blk;
|
};
|
||||||
};
|
|
||||||
|
|
||||||
break :tmp path_opt orelse {
|
const file_path = maybe_path orelse {
|
||||||
log.warn("did not receive a file path", .{});
|
log.warn("did not receive a file path", .{});
|
||||||
break :blk;
|
break :blk;
|
||||||
};
|
|
||||||
};
|
};
|
||||||
defer nfd.freePath(file_path);
|
defer nfd.freePath(file_path);
|
||||||
|
|
||||||
log.info("user chose: \"{s}\"", .{file_path});
|
log.info("user chose: \"{s}\"", .{file_path});
|
||||||
|
emu.replaceBios(cpu, file_path) catch |e| {
|
||||||
const message = tmp: {
|
log.err("failed to replace BIOS: {}", .{e});
|
||||||
var msg: Synchro.Message = .{ .bios_path = undefined };
|
|
||||||
@memcpy(msg.bios_path[0..file_path.len], file_path);
|
|
||||||
break :tmp msg;
|
|
||||||
};
|
|
||||||
|
|
||||||
sync.ch.push(message) catch |e| {
|
|
||||||
log.err("failed to send file path to emu thread: {}", .{e});
|
|
||||||
break :blk;
|
break :blk;
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
@ -169,7 +149,7 @@ pub fn draw(state: *State, sync: *Synchro, dim: Dimensions, cpu: *const Arm7tdmi
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zgui.menuItem("Restart", .{}))
|
if (zgui.menuItem("Restart", .{}))
|
||||||
sync.ch.push(.restart) catch |e| log.err("failed to send restart req to emu thread: {}", .{e});
|
emu.reset(cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zgui.beginMenu("Stats", true)) {
|
if (zgui.beginMenu("Stats", true)) {
|
||||||
|
@ -344,9 +324,9 @@ pub fn draw(state: *State, sync: *Synchro, dim: Dimensions, cpu: *const Arm7tdmi
|
||||||
const Event = std.meta.Child(@TypeOf(sched_ptr.queue.items));
|
const Event = std.meta.Child(@TypeOf(sched_ptr.queue.items));
|
||||||
|
|
||||||
var items: [20]Event = undefined;
|
var items: [20]Event = undefined;
|
||||||
const len = @min(sched_ptr.queue.len, items.len);
|
const len = sched_ptr.queue.len;
|
||||||
|
|
||||||
@memcpy(items[0..len], sched_ptr.queue.items[0..len]);
|
@memcpy(&items, sched_ptr.queue.items);
|
||||||
std.mem.sort(Event, items[0..len], {}, widgets.eventDesc(Event));
|
std.mem.sort(Event, items[0..len], {}, widgets.eventDesc(Event));
|
||||||
|
|
||||||
for (items[0..len]) |event| {
|
for (items[0..len]) |event| {
|
||||||
|
@ -365,9 +345,9 @@ pub fn draw(state: *State, sync: *Synchro, dim: Dimensions, cpu: *const Arm7tdmi
|
||||||
widgets.paletteGrid(.Object, cpu);
|
widgets.paletteGrid(.Object, cpu);
|
||||||
}
|
}
|
||||||
|
|
||||||
// {
|
{
|
||||||
// zgui.showDemoWindow(null);
|
zgui.showDemoWindow(null);
|
||||||
// }
|
}
|
||||||
|
|
||||||
return true; // request redraw
|
return true; // request redraw
|
||||||
}
|
}
|
||||||
|
|
|
@ -119,8 +119,7 @@ pub fn main() void {
|
||||||
var gui = Gui.init(allocator, &bus.apu, title_ptr) catch |e| exitln("failed to init gui: {}", .{e});
|
var gui = Gui.init(allocator, &bus.apu, title_ptr) catch |e| exitln("failed to init gui: {}", .{e});
|
||||||
defer gui.deinit();
|
defer gui.deinit();
|
||||||
|
|
||||||
var sync = Synchro.init(allocator) catch |e| exitln("failed to allocate sync types: {}", .{e});
|
var sync: Synchro = .{};
|
||||||
defer sync.deinit(allocator);
|
|
||||||
|
|
||||||
if (result.args.gdb != 0) {
|
if (result.args.gdb != 0) {
|
||||||
const Server = @import("gdbstub").Server;
|
const Server = @import("gdbstub").Server;
|
||||||
|
|
|
@ -207,6 +207,9 @@ pub const Gui = struct {
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.Active => {
|
.Active => {
|
||||||
|
sync.emu_access.lock();
|
||||||
|
defer sync.emu_access.unlock();
|
||||||
|
|
||||||
// Add FPS count to the histogram
|
// Add FPS count to the histogram
|
||||||
if (tracker) |t| self.state.fps_hist.push(t.value()) catch {};
|
if (tracker) |t| self.state.fps_hist.push(t.value()) catch {};
|
||||||
|
|
||||||
|
@ -225,9 +228,9 @@ pub const Gui = struct {
|
||||||
SDL.SDL_LockAudioDevice(self.audio.device);
|
SDL.SDL_LockAudioDevice(self.audio.device);
|
||||||
defer SDL.SDL_UnlockAudioDevice(self.audio.device);
|
defer SDL.SDL_UnlockAudioDevice(self.audio.device);
|
||||||
|
|
||||||
zgui_redraw = imgui.draw(&self.state, sync, win_dim, cpu, out_tex);
|
zgui_redraw = imgui.draw(&self.state, win_dim, out_tex, cpu);
|
||||||
},
|
},
|
||||||
.Inactive => zgui_redraw = imgui.draw(&self.state, sync, win_dim, cpu, out_tex),
|
.Inactive => zgui_redraw = imgui.draw(&self.state, win_dim, out_tex, cpu),
|
||||||
}
|
}
|
||||||
|
|
||||||
if (zgui_redraw) {
|
if (zgui_redraw) {
|
||||||
|
|
28
src/util.zig
28
src/util.zig
|
@ -295,31 +295,3 @@ pub const FrameBuffer = struct {
|
||||||
return self.layers[if (dev == .Emulator) self.current else ~self.current];
|
return self.layers[if (dev == .Emulator) self.current else ~self.current];
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const RingBuffer = @import("zba-util").RingBuffer;
|
|
||||||
|
|
||||||
// TODO: Lock Free Queue?
|
|
||||||
pub fn Queue(comptime T: type) type {
|
|
||||||
return struct {
|
|
||||||
inner: RingBuffer(T),
|
|
||||||
mtx: std.Thread.Mutex = .{},
|
|
||||||
|
|
||||||
pub fn init(buf: []T) @This() {
|
|
||||||
return .{ .inner = RingBuffer(T).init(buf) };
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn push(self: *@This(), value: T) !void {
|
|
||||||
self.mtx.lock();
|
|
||||||
defer self.mtx.unlock();
|
|
||||||
|
|
||||||
try self.inner.push(value);
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn pop(self: *@This()) ?T {
|
|
||||||
self.mtx.lock();
|
|
||||||
defer self.mtx.unlock();
|
|
||||||
|
|
||||||
return self.inner.pop();
|
|
||||||
}
|
|
||||||
};
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue