Add TOML Support #2
121
src/core/emu.zig
121
src/core/emu.zig
|
@ -35,18 +35,55 @@ const RunKind = enum {
|
||||||
LimitedFPS,
|
LimitedFPS,
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn run(quit: *Atomic(bool), fps: *FpsTracker, sched: *Scheduler, cpu: *Arm7tdmi) void {
|
pub fn run(quit: *Atomic(bool), scheduler: *Scheduler, cpu: *Arm7tdmi, tracker: *FpsTracker) void {
|
||||||
if (config.config().guest.audio_sync) log.info("Audio sync enabled", .{});
|
|
||||||
|
|
||||||
// TODO: Support the other modes?
|
|
||||||
const video_sync: RunKind = if (config.config().guest.video_sync) .LimitedFPS else .UnlimitedFPS;
|
|
||||||
const audio_sync = config.config().guest.audio_sync;
|
const audio_sync = config.config().guest.audio_sync;
|
||||||
|
if (audio_sync) log.info("Audio sync enabled", .{});
|
||||||
|
|
||||||
switch (video_sync) {
|
if (config.config().guest.video_sync) {
|
||||||
.Unlimited => runUnsynchronized(audio_sync, quit, sched, cpu, null),
|
inner(.LimitedFPS, audio_sync, quit, scheduler, cpu, tracker);
|
||||||
.Limited => runSynchronized(audio_sync, quit, sched, cpu, null),
|
} else {
|
||||||
.UnlimitedFPS => runUnsynchronized(audio_sync, quit, sched, cpu, fps),
|
inner(.UnlimitedFPS, audio_sync, quit, scheduler, cpu, tracker);
|
||||||
.LimitedFPS => runSynchronized(audio_sync, quit, sched, cpu, fps),
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn inner(comptime kind: RunKind, audio_sync: bool, quit: *Atomic(bool), scheduler: *Scheduler, cpu: *Arm7tdmi, tracker: ?*FpsTracker) void {
|
||||||
|
if (kind == .UnlimitedFPS or kind == .LimitedFPS) {
|
||||||
|
std.debug.assert(tracker != null);
|
||||||
|
log.info("FPS tracking enabled", .{});
|
||||||
|
}
|
||||||
|
|
||||||
|
switch (kind) {
|
||||||
|
.Unlimited, .UnlimitedFPS => {
|
||||||
|
log.info("Emulation w/out video sync", .{});
|
||||||
|
|
||||||
|
while (!quit.load(.SeqCst)) {
|
||||||
|
runFrame(scheduler, cpu);
|
||||||
|
audioSync(audio_sync, cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full);
|
||||||
|
|
||||||
|
if (kind == .UnlimitedFPS) tracker.?.tick();
|
||||||
|
}
|
||||||
|
},
|
||||||
|
.Limited, .LimitedFPS => {
|
||||||
|
log.info("Emulation w/ video sync", .{});
|
||||||
|
var timer = Timer.start() catch @panic("failed to initalize std.timer.Timer");
|
||||||
|
var wake_time: u64 = frame_period;
|
||||||
|
|
||||||
|
while (!quit.load(.SeqCst)) {
|
||||||
|
runFrame(scheduler, cpu);
|
||||||
|
const new_wake_time = videoSync(&timer, wake_time);
|
||||||
|
|
||||||
|
// Spin to make up the difference of OS scheduler innacuracies
|
||||||
|
// If we happen to also be syncing to audio, we choose to spin on
|
||||||
|
// the amount of time needed for audio to catch up rather than
|
||||||
|
// our expected wake-up time
|
||||||
|
|
||||||
|
audioSync(audio_sync, cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full);
|
||||||
|
if (!audio_sync) spinLoop(&timer, wake_time);
|
||||||
|
wake_time = new_wake_time;
|
||||||
|
|
||||||
|
if (kind == .LimitedFPS) tracker.?.tick();
|
||||||
|
}
|
||||||
|
},
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -67,7 +104,7 @@ pub fn runFrame(sched: *Scheduler, cpu: *Arm7tdmi) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn syncToAudio(audio_sync: bool, stream: *SDL.SDL_AudioStream, is_buffer_full: *bool) void {
|
fn audioSync(audio_sync: bool, stream: *SDL.SDL_AudioStream, is_buffer_full: *bool) void {
|
||||||
const sample_size = 2 * @sizeOf(u16);
|
const sample_size = 2 * @sizeOf(u16);
|
||||||
const max_buf_size: c_int = 0x400;
|
const max_buf_size: c_int = 0x400;
|
||||||
|
|
||||||
|
@ -84,70 +121,16 @@ fn syncToAudio(audio_sync: bool, stream: *SDL.SDL_AudioStream, is_buffer_full: *
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runUnsynchronized(audio_sync: bool, quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi, fps: ?*FpsTracker) void {
|
fn videoSync(timer: *Timer, wake_time: u64) u64 {
|
||||||
log.info("Emulation thread w/out video sync", .{});
|
|
||||||
|
|
||||||
if (fps) |tracker| {
|
|
||||||
log.info("FPS Tracking Enabled", .{});
|
|
||||||
|
|
||||||
while (!quit.load(.SeqCst)) {
|
|
||||||
runFrame(sched, cpu);
|
|
||||||
syncToAudio(audio_sync, cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full);
|
|
||||||
|
|
||||||
tracker.tick();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while (!quit.load(.SeqCst)) {
|
|
||||||
runFrame(sched, cpu);
|
|
||||||
syncToAudio(audio_sync, cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
pub fn runSynchronized(audio_sync: bool, quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi, fps: ?*FpsTracker) void {
|
|
||||||
log.info("Emulation thread w/ video sync", .{});
|
|
||||||
var timer = Timer.start() catch std.debug.panic("Failed to initialize std.timer.Timer", .{});
|
|
||||||
var wake_time: u64 = frame_period;
|
|
||||||
|
|
||||||
if (fps) |tracker| {
|
|
||||||
log.info("FPS Tracking Enabled", .{});
|
|
||||||
|
|
||||||
while (!quit.load(.SeqCst)) {
|
|
||||||
runFrame(sched, cpu);
|
|
||||||
const new_wake_time = blockOnVideo(&timer, wake_time);
|
|
||||||
|
|
||||||
// Spin to make up the difference of OS scheduler innacuracies
|
|
||||||
// If we happen to also be syncing to audio, we choose to spin on
|
|
||||||
// the amount of time needed for audio to catch up rather than
|
|
||||||
// our expected wake-up time
|
|
||||||
syncToAudio(audio_sync, cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full);
|
|
||||||
if (!audio_sync) spinLoop(&timer, wake_time);
|
|
||||||
wake_time = new_wake_time;
|
|
||||||
|
|
||||||
tracker.tick();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
while (!quit.load(.SeqCst)) {
|
|
||||||
runFrame(sched, cpu);
|
|
||||||
const new_wake_time = blockOnVideo(&timer, wake_time);
|
|
||||||
|
|
||||||
// see above comment
|
|
||||||
syncToAudio(audio_sync, cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full);
|
|
||||||
if (!audio_sync) spinLoop(&timer, wake_time);
|
|
||||||
wake_time = new_wake_time;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
inline fn blockOnVideo(timer: *Timer, wake_time: u64) u64 {
|
|
||||||
// Use the OS scheduler to put the emulation thread to sleep
|
// Use the OS scheduler to put the emulation thread to sleep
|
||||||
const maybe_recalc_wake_time = sleep(timer, wake_time);
|
const recalculated = sleep(timer, wake_time);
|
||||||
|
|
||||||
// If sleep() determined we need to adjust our wake up time, do so
|
// If sleep() determined we need to adjust our wake up time, do so
|
||||||
// otherwise predict our next wake up time according to the frame period
|
// otherwise predict our next wake up time according to the frame period
|
||||||
return if (maybe_recalc_wake_time) |recalc| recalc else wake_time + frame_period;
|
return recalculated orelse wake_time + frame_period;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TODO: Better sleep impl?
|
||||||
fn sleep(timer: *Timer, wake_time: u64) ?u64 {
|
fn sleep(timer: *Timer, wake_time: u64) ?u64 {
|
||||||
// const step = std.time.ns_per_ms * 10; // 10ms
|
// const step = std.time.ns_per_ms * 10; // 10ms
|
||||||
const timestamp = timer.read();
|
const timestamp = timer.read();
|
||||||
|
|
|
@ -60,9 +60,9 @@ pub const Gui = struct {
|
||||||
|
|
||||||
pub fn run(self: *Self, cpu: *Arm7tdmi, scheduler: *Scheduler) !void {
|
pub fn run(self: *Self, cpu: *Arm7tdmi, scheduler: *Scheduler) !void {
|
||||||
var quit = std.atomic.Atomic(bool).init(false);
|
var quit = std.atomic.Atomic(bool).init(false);
|
||||||
var frame_rate = FpsTracker.init();
|
var tracker = FpsTracker.init();
|
||||||
|
|
||||||
const thread = try std.Thread.spawn(.{}, emu.run, .{ &quit, &frame_rate, scheduler, cpu });
|
const thread = try std.Thread.spawn(.{}, emu.run, .{ &quit, scheduler, cpu, &tracker });
|
||||||
defer thread.join();
|
defer thread.join();
|
||||||
|
|
||||||
var title_buf: [0x100]u8 = [_]u8{0} ** 0x100;
|
var title_buf: [0x100]u8 = [_]u8{0} ** 0x100;
|
||||||
|
@ -129,7 +129,7 @@ pub const Gui = struct {
|
||||||
_ = SDL.SDL_RenderCopy(self.renderer, self.texture, null, null);
|
_ = SDL.SDL_RenderCopy(self.renderer, self.texture, null, null);
|
||||||
SDL.SDL_RenderPresent(self.renderer);
|
SDL.SDL_RenderPresent(self.renderer);
|
||||||
|
|
||||||
const dyn_title = std.fmt.bufPrint(&title_buf, "ZBA | {s} [Emu: {}fps] ", .{ self.title, frame_rate.value() }) catch unreachable;
|
const dyn_title = std.fmt.bufPrint(&title_buf, "ZBA | {s} [Emu: {}fps] ", .{ self.title, tracker.value() }) catch unreachable;
|
||||||
SDL.SDL_SetWindowTitle(self.window, dyn_title.ptr);
|
SDL.SDL_SetWindowTitle(self.window, dyn_title.ptr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue