Compare commits
No commits in common. "ccdc2cbad4f09f9469e089ef48a29e88e52cd1d6" and "79514b0cd0b00613818b6b88169e8ac1c9eedcf5" have entirely different histories.
ccdc2cbad4
...
79514b0cd0
|
@ -1 +1 @@
|
||||||
Subproject commit 77d11c9903d7f4a3a31ef59fc954421d7dfa0eb1
|
Subproject commit 72349459ec29ee2d910a3dd3274be00254a7acd4
|
|
@ -5,9 +5,7 @@ const config = @import("../config.zig");
|
||||||
const Scheduler = @import("scheduler.zig").Scheduler;
|
const Scheduler = @import("scheduler.zig").Scheduler;
|
||||||
const Arm7tdmi = @import("cpu.zig").Arm7tdmi;
|
const Arm7tdmi = @import("cpu.zig").Arm7tdmi;
|
||||||
const Tracker = @import("../util.zig").FpsTracker;
|
const Tracker = @import("../util.zig").FpsTracker;
|
||||||
const Channel = @import("zba-util").Channel(Message, 0x100);
|
const TwoWayChannel = @import("zba-util").TwoWayChannel;
|
||||||
|
|
||||||
pub const Message = enum { Pause, Resume, Quit };
|
|
||||||
|
|
||||||
const Timer = std.time.Timer;
|
const Timer = std.time.Timer;
|
||||||
|
|
||||||
|
@ -37,18 +35,18 @@ const RunKind = enum {
|
||||||
LimitedFPS,
|
LimitedFPS,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn run(cpu: *Arm7tdmi, scheduler: *Scheduler, tracker: *Tracker, rx: Channel.Receiver) void {
|
pub fn run(cpu: *Arm7tdmi, scheduler: *Scheduler, tracker: *Tracker, channel: *TwoWayChannel) void {
|
||||||
const audio_sync = config.config().guest.audio_sync and !config.config().host.mute;
|
const audio_sync = config.config().guest.audio_sync and !config.config().host.mute;
|
||||||
if (audio_sync) log.info("Audio sync enabled", .{});
|
if (audio_sync) log.info("Audio sync enabled", .{});
|
||||||
|
|
||||||
if (config.config().guest.video_sync) {
|
if (config.config().guest.video_sync) {
|
||||||
inner(.LimitedFPS, audio_sync, cpu, scheduler, tracker, rx);
|
inner(.LimitedFPS, audio_sync, cpu, scheduler, tracker, channel);
|
||||||
} else {
|
} else {
|
||||||
inner(.UnlimitedFPS, audio_sync, cpu, scheduler, tracker, rx);
|
inner(.UnlimitedFPS, audio_sync, cpu, scheduler, tracker, channel);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn inner(comptime kind: RunKind, audio_sync: bool, cpu: *Arm7tdmi, scheduler: *Scheduler, tracker: ?*Tracker, rx: Channel.Receiver) void {
|
fn inner(comptime kind: RunKind, audio_sync: bool, cpu: *Arm7tdmi, scheduler: *Scheduler, tracker: ?*Tracker, channel: *TwoWayChannel) void {
|
||||||
if (kind == .UnlimitedFPS or kind == .LimitedFPS) {
|
if (kind == .UnlimitedFPS or kind == .LimitedFPS) {
|
||||||
std.debug.assert(tracker != null);
|
std.debug.assert(tracker != null);
|
||||||
log.info("FPS tracking enabled", .{});
|
log.info("FPS tracking enabled", .{});
|
||||||
|
@ -61,9 +59,13 @@ 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 (true) {
|
while (true) {
|
||||||
if (rx.recv()) |m| switch (m) {
|
if (channel.emu.pop()) |e| switch (e) {
|
||||||
.Quit => break,
|
.Quit => break,
|
||||||
.Resume, .Pause => paused = m == .Pause,
|
.Resume => paused = false,
|
||||||
|
.Pause => {
|
||||||
|
paused = true;
|
||||||
|
channel.gui.push(.Paused);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (paused) continue;
|
if (paused) continue;
|
||||||
|
@ -80,9 +82,13 @@ 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 (true) {
|
while (true) {
|
||||||
if (rx.recv()) |m| switch (m) {
|
if (channel.emu.pop()) |e| switch (e) {
|
||||||
.Quit => break,
|
.Quit => break,
|
||||||
.Resume, .Pause => paused = m == .Pause,
|
.Resume => paused = false,
|
||||||
|
.Pause => {
|
||||||
|
paused = true;
|
||||||
|
channel.gui.push(.Paused);
|
||||||
|
},
|
||||||
};
|
};
|
||||||
|
|
||||||
if (paused) continue;
|
if (paused) continue;
|
||||||
|
|
|
@ -56,7 +56,7 @@ pub const State = struct {
|
||||||
|
|
||||||
return .{
|
return .{
|
||||||
.title = handleTitle(title_opt),
|
.title = handleTitle(title_opt),
|
||||||
.emulation = if (title_opt == null) .Inactive else .{ .Transition = .Active },
|
.emulation = if (title_opt == null) .Inactive else .Active,
|
||||||
.fps_hist = RingBuffer(u32).init(history),
|
.fps_hist = RingBuffer(u32).init(history),
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
14
src/main.zig
14
src/main.zig
|
@ -6,7 +6,7 @@ const clap = @import("clap");
|
||||||
const config = @import("config.zig");
|
const config = @import("config.zig");
|
||||||
const emu = @import("core/emu.zig");
|
const emu = @import("core/emu.zig");
|
||||||
|
|
||||||
const Channel = @import("zba-util").Channel(emu.Message, 0x100);
|
const TwoWayChannel = @import("zba-util").TwoWayChannel;
|
||||||
const Gui = @import("platform.zig").Gui;
|
const Gui = @import("platform.zig").Gui;
|
||||||
const Bus = @import("core/Bus.zig");
|
const Bus = @import("core/Bus.zig");
|
||||||
const Arm7tdmi = @import("core/cpu.zig").Arm7tdmi;
|
const Arm7tdmi = @import("core/cpu.zig").Arm7tdmi;
|
||||||
|
@ -98,8 +98,8 @@ pub fn main() void {
|
||||||
|
|
||||||
var quit = std.atomic.Atomic(bool).init(false);
|
var quit = std.atomic.Atomic(bool).init(false);
|
||||||
|
|
||||||
var ch = Channel.init(allocator) catch |e| exitln("failed to initialize ui -> emu thread message channel: {}", .{e});
|
var items: [0x100]u8 = undefined;
|
||||||
defer ch.deinit(allocator);
|
var channel = TwoWayChannel.init(&items);
|
||||||
|
|
||||||
if (result.args.gdb != 0) {
|
if (result.args.gdb != 0) {
|
||||||
const Server = @import("gdbstub").Server;
|
const Server = @import("gdbstub").Server;
|
||||||
|
@ -122,22 +122,22 @@ pub fn main() void {
|
||||||
gui.run(.Debug, .{
|
gui.run(.Debug, .{
|
||||||
.cpu = &cpu,
|
.cpu = &cpu,
|
||||||
.scheduler = &scheduler,
|
.scheduler = &scheduler,
|
||||||
.ch = ch.tx,
|
.channel = &channel,
|
||||||
}) catch |e| exitln("main thread panicked: {}", .{e});
|
}) catch |e| exitln("main thread panicked: {}", .{e});
|
||||||
} else {
|
} else {
|
||||||
var tracker = FpsTracker.init();
|
var tracker = FpsTracker.init();
|
||||||
|
|
||||||
// emu should start paused if there's no ROM to run
|
// emu should start paused if there's no ROM to run
|
||||||
if (paths.rom == null)
|
if (paths.rom == null)
|
||||||
ch.tx.send(.Pause);
|
channel.emu.push(.Pause);
|
||||||
|
|
||||||
const thread = std.Thread.spawn(.{}, emu.run, .{ &cpu, &scheduler, &tracker, ch.rx }) catch |e| exitln("emu thread panicked: {}", .{e});
|
const thread = std.Thread.spawn(.{}, emu.run, .{ &cpu, &scheduler, &tracker, &channel }) catch |e| exitln("emu thread panicked: {}", .{e});
|
||||||
defer thread.join();
|
defer thread.join();
|
||||||
|
|
||||||
gui.run(.Standard, .{
|
gui.run(.Standard, .{
|
||||||
.cpu = &cpu,
|
.cpu = &cpu,
|
||||||
.scheduler = &scheduler,
|
.scheduler = &scheduler,
|
||||||
.ch = ch.tx,
|
.channel = &channel,
|
||||||
.tracker = &tracker,
|
.tracker = &tracker,
|
||||||
}) catch |e| exitln("main thread panicked: {}", .{e});
|
}) catch |e| exitln("main thread panicked: {}", .{e});
|
||||||
}
|
}
|
||||||
|
|
|
@ -11,7 +11,7 @@ const Apu = @import("core/apu.zig").Apu;
|
||||||
const Arm7tdmi = @import("core/cpu.zig").Arm7tdmi;
|
const Arm7tdmi = @import("core/cpu.zig").Arm7tdmi;
|
||||||
const Scheduler = @import("core/scheduler.zig").Scheduler;
|
const Scheduler = @import("core/scheduler.zig").Scheduler;
|
||||||
const FpsTracker = @import("util.zig").FpsTracker;
|
const FpsTracker = @import("util.zig").FpsTracker;
|
||||||
const Channel = @import("zba-util").Channel(emu.Message, 0x100);
|
const TwoWayChannel = @import("zba-util").TwoWayChannel;
|
||||||
const KeyInput = @import("core/bus/io.zig").KeyInput;
|
const KeyInput = @import("core/bus/io.zig").KeyInput;
|
||||||
|
|
||||||
const gba_width = @import("core/ppu.zig").width;
|
const gba_width = @import("core/ppu.zig").width;
|
||||||
|
@ -94,7 +94,7 @@ pub const Gui = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
const RunOptions = struct {
|
const RunOptions = struct {
|
||||||
ch: Channel.Sender,
|
channel: *TwoWayChannel,
|
||||||
tracker: ?*FpsTracker = null,
|
tracker: ?*FpsTracker = null,
|
||||||
cpu: *Arm7tdmi,
|
cpu: *Arm7tdmi,
|
||||||
scheduler: *Scheduler,
|
scheduler: *Scheduler,
|
||||||
|
@ -105,7 +105,7 @@ pub const Gui = struct {
|
||||||
pub fn run(self: *Self, comptime mode: RunMode, opt: RunOptions) !void {
|
pub fn run(self: *Self, comptime mode: RunMode, opt: RunOptions) !void {
|
||||||
const cpu = opt.cpu;
|
const cpu = opt.cpu;
|
||||||
const tracker = opt.tracker;
|
const tracker = opt.tracker;
|
||||||
const ch = opt.ch;
|
const channel = opt.channel;
|
||||||
|
|
||||||
const objects = opengl_impl.createObjects();
|
const objects = opengl_impl.createObjects();
|
||||||
defer gl.deleteBuffers(3, @as(*const [3]GLuint, &.{ objects.vao, objects.vbo, objects.ebo }));
|
defer gl.deleteBuffers(3, @as(*const [3]GLuint, &.{ objects.vao, objects.vbo, objects.ebo }));
|
||||||
|
@ -193,50 +193,50 @@ pub const Gui = struct {
|
||||||
switch (self.state.emulation) {
|
switch (self.state.emulation) {
|
||||||
.Transition => |inner| switch (inner) {
|
.Transition => |inner| switch (inner) {
|
||||||
.Active => {
|
.Active => {
|
||||||
ch.send(.Resume);
|
_ = channel.gui.pop();
|
||||||
if (!config.config().host.mute) SDL.SDL_PauseAudioDevice(self.audio.device, 0);
|
|
||||||
|
|
||||||
|
channel.emu.push(.Resume);
|
||||||
self.state.emulation = .Active;
|
self.state.emulation = .Active;
|
||||||
},
|
},
|
||||||
.Inactive => {
|
.Inactive => {
|
||||||
// Assert that double pausing is impossible
|
// Assert that double pausing is impossible
|
||||||
|
if (channel.gui.peek()) |value|
|
||||||
|
std.debug.assert(value != .Paused);
|
||||||
|
|
||||||
SDL.SDL_PauseAudioDevice(self.audio.device, 1);
|
channel.emu.push(.Pause);
|
||||||
ch.send(.Pause);
|
|
||||||
|
|
||||||
self.state.emulation = .Inactive;
|
self.state.emulation = .Inactive;
|
||||||
},
|
},
|
||||||
},
|
},
|
||||||
.Active => {
|
.Active => skip_draw: {
|
||||||
const is_std = mode == .Standard;
|
const is_std = mode == .Standard;
|
||||||
|
|
||||||
if (is_std) ch.send(.Pause);
|
if (is_std) channel.emu.push(.Pause);
|
||||||
defer if (is_std) ch.send(.Resume);
|
defer if (is_std) channel.emu.push(.Resume);
|
||||||
|
|
||||||
// switch (mode) {
|
switch (mode) {
|
||||||
// .Standard => blk: {
|
.Standard => blk: {
|
||||||
// const limit = 15; // TODO: What should this be?
|
const limit = 15; // TODO: What should this be?
|
||||||
|
|
||||||
// // TODO: learn more about std.atomic.spinLoopHint();
|
// TODO: learn more about std.atomic.spinLoopHint();
|
||||||
// for (0..limit) |_| {
|
for (0..limit) |_| {
|
||||||
// const message = channel.gui.pop() orelse continue;
|
const message = channel.gui.pop() orelse continue;
|
||||||
|
|
||||||
// switch (message) {
|
switch (message) {
|
||||||
// .Paused => break :blk,
|
.Paused => break :blk,
|
||||||
// .Quit => unreachable,
|
.Quit => unreachable,
|
||||||
// }
|
}
|
||||||
// }
|
}
|
||||||
|
|
||||||
// log.info("timed out waiting for emu thread to pause (limit: {})", .{limit});
|
log.info("timed out waiting for emu thread to pause (limit: {})", .{limit});
|
||||||
// break :skip_draw;
|
break :skip_draw;
|
||||||
// },
|
},
|
||||||
// .Debug => blk: {
|
.Debug => blk: {
|
||||||
// switch (channel.gui.pop() orelse break :blk) {
|
switch (channel.gui.pop() orelse break :blk) {
|
||||||
// .Paused => unreachable, // only in standard mode
|
.Paused => unreachable, // only in standard mode
|
||||||
// .Quit => break :emu_loop, // FIXME: gdb side of emu is seriously out-of-date...
|
.Quit => break :emu_loop, // FIXME: gdb side of emu is seriously out-of-date...
|
||||||
// }
|
}
|
||||||
// },
|
},
|
||||||
// }
|
}
|
||||||
|
|
||||||
// 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 {};
|
||||||
|
@ -251,12 +251,6 @@ pub const Gui = struct {
|
||||||
opengl_impl.drawScreenTexture(emu_tex, prog_id, objects, buf);
|
opengl_impl.drawScreenTexture(emu_tex, prog_id, objects, buf);
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME: We only really care about locking the audio device (and therefore writing silence)
|
|
||||||
// since if nfd-zig is used the emu may be paused for way too long. Perhaps we should try and limit
|
|
||||||
// spurious calls to SDL_LockAudioDevice?
|
|
||||||
SDL.SDL_LockAudioDevice(self.audio.device);
|
|
||||||
defer SDL.SDL_UnlockAudioDevice(self.audio.device);
|
|
||||||
|
|
||||||
zgui_redraw = imgui.draw(&self.state, win_dim, out_tex, cpu);
|
zgui_redraw = imgui.draw(&self.state, win_dim, out_tex, cpu);
|
||||||
},
|
},
|
||||||
.Inactive => zgui_redraw = imgui.draw(&self.state, win_dim, out_tex, cpu),
|
.Inactive => zgui_redraw = imgui.draw(&self.state, win_dim, out_tex, cpu),
|
||||||
|
@ -275,7 +269,7 @@ pub const Gui = struct {
|
||||||
SDL.SDL_GL_SwapWindow(self.window);
|
SDL.SDL_GL_SwapWindow(self.window);
|
||||||
}
|
}
|
||||||
|
|
||||||
ch.send(.Quit);
|
channel.emu.push(.Quit);
|
||||||
}
|
}
|
||||||
|
|
||||||
fn glGetProcAddress(ctx: SDL.SDL_GLContext, proc: [:0]const u8) ?*anyopaque {
|
fn glGetProcAddress(ctx: SDL.SDL_GLContext, proc: [:0]const u8) ?*anyopaque {
|
||||||
|
@ -306,6 +300,11 @@ const Audio = struct {
|
||||||
const device = SDL.SDL_OpenAudioDevice(null, 0, &want, &have, 0);
|
const device = SDL.SDL_OpenAudioDevice(null, 0, &want, &have, 0);
|
||||||
if (device == 0) panic();
|
if (device == 0) panic();
|
||||||
|
|
||||||
|
if (!config.config().host.mute) {
|
||||||
|
SDL.SDL_PauseAudioDevice(device, 0); // Unpause Audio
|
||||||
|
log.info("Unpaused Device", .{});
|
||||||
|
}
|
||||||
|
|
||||||
return .{ .device = device };
|
return .{ .device = device };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue