feat(config): add support for (and read from) TOML config file
This commit is contained in:
parent
d21c860eb5
commit
8b9ab6f4b5
|
@ -0,0 +1,74 @@
|
||||||
|
const std = @import("std");
|
||||||
|
const toml = @import("toml");
|
||||||
|
|
||||||
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
|
var state: Config = .{};
|
||||||
|
|
||||||
|
const Config = struct {
|
||||||
|
host: Host = .{},
|
||||||
|
guest: Guest = .{},
|
||||||
|
debug: Debug = .{},
|
||||||
|
|
||||||
|
/// Settings related to the Computer the Emulator is being run on
|
||||||
|
const Host = struct {
|
||||||
|
/// Using Nearest-Neighbor, multiply the resolution of the GBA Window
|
||||||
|
win_scale: i64 = 3,
|
||||||
|
/// Enable Vsync
|
||||||
|
///
|
||||||
|
/// Note: This does not affect whether Emulation is synced to 59Hz
|
||||||
|
vsync: bool = true,
|
||||||
|
};
|
||||||
|
|
||||||
|
// Settings realted to the emulation itself
|
||||||
|
const Guest = struct {
|
||||||
|
/// Whether Emulation thread to sync to Audio Callbacks
|
||||||
|
audio_sync: bool = true,
|
||||||
|
/// Whether Emulation thread should sync to 59Hz
|
||||||
|
video_sync: bool = true,
|
||||||
|
/// Whether RTC I/O should always be enabled
|
||||||
|
force_rtc: bool = false,
|
||||||
|
};
|
||||||
|
|
||||||
|
/// Settings related to debugging ZBA
|
||||||
|
const Debug = struct {
|
||||||
|
/// Enable CPU Trace logs
|
||||||
|
cpu_trace: bool = false,
|
||||||
|
/// If false and ZBA is build in debug mode, ZBA will panic on unhandled I/O
|
||||||
|
unhandled_io: bool = true,
|
||||||
|
};
|
||||||
|
};
|
||||||
|
|
||||||
|
pub fn config() *const Config {
|
||||||
|
return &state;
|
||||||
|
}
|
||||||
|
|
||||||
|
/// Reads a config file and then loads it into the global state
|
||||||
|
pub fn load(allocator: Allocator, config_path: []const u8) !void {
|
||||||
|
var config_file = try std.fs.cwd().openFile(config_path, .{});
|
||||||
|
defer config_file.close();
|
||||||
|
|
||||||
|
const contents = try config_file.readToEndAlloc(allocator, try config_file.getEndPos());
|
||||||
|
defer allocator.free(contents);
|
||||||
|
|
||||||
|
const table = try toml.parseContents(allocator, contents, null);
|
||||||
|
defer table.deinit();
|
||||||
|
|
||||||
|
// TODO: Report unknown config options
|
||||||
|
|
||||||
|
if (table.keys.get("Host")) |host| {
|
||||||
|
if (host.Table.keys.get("win_scale")) |scale| state.host.win_scale = scale.Integer;
|
||||||
|
if (host.Table.keys.get("vsync")) |vsync| state.host.vsync = vsync.Boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (table.keys.get("Guest")) |guest| {
|
||||||
|
if (guest.Table.keys.get("audio_sync")) |sync| state.guest.audio_sync = sync.Boolean;
|
||||||
|
if (guest.Table.keys.get("video_sync")) |sync| state.guest.video_sync = sync.Boolean;
|
||||||
|
if (guest.Table.keys.get("force_rtc")) |forced| state.guest.force_rtc = forced.Boolean;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (table.keys.get("Debug")) |debug| {
|
||||||
|
if (debug.Table.keys.get("cpu_trace")) |trace| state.debug.cpu_trace = trace.Boolean;
|
||||||
|
if (debug.Table.keys.get("unhandled_io")) |unhandled| state.debug.unhandled_io = unhandled.Boolean;
|
||||||
|
}
|
||||||
|
}
|
|
@ -1,4 +1,6 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
|
const config = @import("../../config.zig");
|
||||||
|
|
||||||
const Bit = @import("bitfield").Bit;
|
const Bit = @import("bitfield").Bit;
|
||||||
const Bitfield = @import("bitfield").Bitfield;
|
const Bitfield = @import("bitfield").Bitfield;
|
||||||
const DateTime = @import("datetime").datetime.Datetime;
|
const DateTime = @import("datetime").datetime.Datetime;
|
||||||
|
@ -8,7 +10,6 @@ const Backup = @import("backup.zig").Backup;
|
||||||
const Gpio = @import("gpio.zig").Gpio;
|
const Gpio = @import("gpio.zig").Gpio;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
const force_rtc = @import("../emu.zig").force_rtc;
|
|
||||||
const log = std.log.scoped(.GamePak);
|
const log = std.log.scoped(.GamePak);
|
||||||
|
|
||||||
const Self = @This();
|
const Self = @This();
|
||||||
|
@ -190,7 +191,7 @@ pub fn init(allocator: Allocator, cpu: *Arm7tdmi, rom_path: []const u8, save_pat
|
||||||
const file_buf = try file.readToEndAlloc(allocator, try file.getEndPos());
|
const file_buf = try file.readToEndAlloc(allocator, try file.getEndPos());
|
||||||
const title = file_buf[0xA0..0xAC].*;
|
const title = file_buf[0xA0..0xAC].*;
|
||||||
const kind = Backup.guess(file_buf);
|
const kind = Backup.guess(file_buf);
|
||||||
const device = if (force_rtc) .Rtc else guessDevice(file_buf);
|
const device = if (config.config().guest.force_rtc) .Rtc else guessDevice(file_buf);
|
||||||
|
|
||||||
logHeader(file_buf, &title);
|
logHeader(file_buf, &title);
|
||||||
|
|
||||||
|
|
|
@ -236,7 +236,6 @@ pub const thumb = struct {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
const cpu_logging = @import("emu.zig").cpu_logging;
|
|
||||||
const log = std.log.scoped(.Arm7Tdmi);
|
const log = std.log.scoped(.Arm7Tdmi);
|
||||||
|
|
||||||
pub const Arm7tdmi = struct {
|
pub const Arm7tdmi = struct {
|
||||||
|
@ -428,12 +427,12 @@ pub const Arm7tdmi = struct {
|
||||||
pub fn step(self: *Self) void {
|
pub fn step(self: *Self) void {
|
||||||
if (self.cpsr.t.read()) {
|
if (self.cpsr.t.read()) {
|
||||||
const opcode = self.fetch(u16);
|
const opcode = self.fetch(u16);
|
||||||
if (cpu_logging) self.logger.?.mgbaLog(self, opcode);
|
if (self.logger) |*trace| trace.mgbaLog(self, opcode);
|
||||||
|
|
||||||
thumb.lut[thumb.idx(opcode)](self, self.bus, opcode);
|
thumb.lut[thumb.idx(opcode)](self, self.bus, opcode);
|
||||||
} else {
|
} else {
|
||||||
const opcode = self.fetch(u32);
|
const opcode = self.fetch(u32);
|
||||||
if (cpu_logging) self.logger.?.mgbaLog(self, opcode);
|
if (self.logger) |*trace| trace.mgbaLog(self, opcode);
|
||||||
|
|
||||||
if (checkCond(self.cpsr, @truncate(u4, opcode >> 28))) {
|
if (checkCond(self.cpsr, @truncate(u4, opcode >> 28))) {
|
||||||
arm.lut[arm.idx(opcode)](self, self.bus, opcode);
|
arm.lut[arm.idx(opcode)](self, self.bus, opcode);
|
||||||
|
|
|
@ -1,5 +1,6 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const SDL = @import("sdl2");
|
const SDL = @import("sdl2");
|
||||||
|
const config = @import("../config.zig");
|
||||||
|
|
||||||
const Bus = @import("Bus.zig");
|
const Bus = @import("Bus.zig");
|
||||||
const Scheduler = @import("scheduler.zig").Scheduler;
|
const Scheduler = @import("scheduler.zig").Scheduler;
|
||||||
|
@ -12,14 +13,6 @@ const Thread = std.Thread;
|
||||||
const Atomic = std.atomic.Atomic;
|
const Atomic = std.atomic.Atomic;
|
||||||
const Allocator = std.mem.Allocator;
|
const Allocator = std.mem.Allocator;
|
||||||
|
|
||||||
// TODO: Move these to a TOML File
|
|
||||||
const sync_audio = false; // Enable Audio Sync
|
|
||||||
const sync_video: RunKind = .LimitedFPS; // Configure Video Sync
|
|
||||||
pub const win_scale = 4; // 1x, 2x, 3x, etc. Window Scaling
|
|
||||||
pub const cpu_logging = false; // Enable detailed CPU logging
|
|
||||||
pub const allow_unhandled_io = true; // Only relevant in Debug Builds
|
|
||||||
pub const force_rtc = false;
|
|
||||||
|
|
||||||
// 228 Lines which consist of 308 dots (which are 4 cycles long)
|
// 228 Lines which consist of 308 dots (which are 4 cycles long)
|
||||||
const cycles_per_frame: u64 = 228 * (308 * 4); //280896
|
const cycles_per_frame: u64 = 228 * (308 * 4); //280896
|
||||||
const clock_rate: u64 = 1 << 24; // 16.78MHz
|
const clock_rate: u64 = 1 << 24; // 16.78MHz
|
||||||
|
@ -40,18 +33,20 @@ const RunKind = enum {
|
||||||
UnlimitedFPS,
|
UnlimitedFPS,
|
||||||
Limited,
|
Limited,
|
||||||
LimitedFPS,
|
LimitedFPS,
|
||||||
LimitedBusy,
|
|
||||||
};
|
};
|
||||||
|
|
||||||
pub fn run(quit: *Atomic(bool), fps: *FpsTracker, sched: *Scheduler, cpu: *Arm7tdmi) void {
|
pub fn run(quit: *Atomic(bool), fps: *FpsTracker, sched: *Scheduler, cpu: *Arm7tdmi) void {
|
||||||
if (sync_audio) log.info("Audio sync enabled", .{});
|
if (config.config().guest.audio_sync) log.info("Audio sync enabled", .{});
|
||||||
|
|
||||||
switch (sync_video) {
|
// TODO: Support the other modes?
|
||||||
.Unlimited => runUnsynchronized(quit, sched, cpu, null),
|
const video_sync: RunKind = if (config.config().guest.video_sync) .LimitedFPS else .UnlimitedFPS;
|
||||||
.Limited => runSynchronized(quit, sched, cpu, null),
|
const audio_sync = config.config().guest.audio_sync;
|
||||||
.UnlimitedFPS => runUnsynchronized(quit, sched, cpu, fps),
|
|
||||||
.LimitedFPS => runSynchronized(quit, sched, cpu, fps),
|
switch (video_sync) {
|
||||||
.LimitedBusy => runBusyLoop(quit, sched, cpu),
|
.Unlimited => runUnsynchronized(audio_sync, quit, sched, cpu, null),
|
||||||
|
.Limited => runSynchronized(audio_sync, quit, sched, cpu, null),
|
||||||
|
.UnlimitedFPS => runUnsynchronized(audio_sync, quit, sched, cpu, fps),
|
||||||
|
.LimitedFPS => runSynchronized(audio_sync, quit, sched, cpu, fps),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -72,7 +67,7 @@ pub fn runFrame(sched: *Scheduler, cpu: *Arm7tdmi) void {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fn syncToAudio(stream: *SDL.SDL_AudioStream, is_buffer_full: *bool) void {
|
fn syncToAudio(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;
|
||||||
|
|
||||||
|
@ -85,11 +80,11 @@ fn syncToAudio(stream: *SDL.SDL_AudioStream, is_buffer_full: *bool) void {
|
||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
still_full = SDL.SDL_AudioStreamAvailable(stream) > sample_size * max_buf_size >> 1;
|
still_full = SDL.SDL_AudioStreamAvailable(stream) > sample_size * max_buf_size >> 1;
|
||||||
if (!sync_audio or !still_full) break;
|
if (!audio_sync or !still_full) break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runUnsynchronized(quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi, fps: ?*FpsTracker) void {
|
pub fn runUnsynchronized(audio_sync: bool, quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi, fps: ?*FpsTracker) void {
|
||||||
log.info("Emulation thread w/out video sync", .{});
|
log.info("Emulation thread w/out video sync", .{});
|
||||||
|
|
||||||
if (fps) |tracker| {
|
if (fps) |tracker| {
|
||||||
|
@ -97,19 +92,19 @@ pub fn runUnsynchronized(quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi,
|
||||||
|
|
||||||
while (!quit.load(.SeqCst)) {
|
while (!quit.load(.SeqCst)) {
|
||||||
runFrame(sched, cpu);
|
runFrame(sched, cpu);
|
||||||
syncToAudio(cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full);
|
syncToAudio(audio_sync, cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full);
|
||||||
|
|
||||||
tracker.tick();
|
tracker.tick();
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
while (!quit.load(.SeqCst)) {
|
while (!quit.load(.SeqCst)) {
|
||||||
runFrame(sched, cpu);
|
runFrame(sched, cpu);
|
||||||
syncToAudio(cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full);
|
syncToAudio(audio_sync, cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runSynchronized(quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi, fps: ?*FpsTracker) void {
|
pub fn runSynchronized(audio_sync: bool, quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi, fps: ?*FpsTracker) void {
|
||||||
log.info("Emulation thread w/ video sync", .{});
|
log.info("Emulation thread w/ video sync", .{});
|
||||||
var timer = Timer.start() catch std.debug.panic("Failed to initialize std.timer.Timer", .{});
|
var timer = Timer.start() catch std.debug.panic("Failed to initialize std.timer.Timer", .{});
|
||||||
var wake_time: u64 = frame_period;
|
var wake_time: u64 = frame_period;
|
||||||
|
@ -125,8 +120,8 @@ pub fn runSynchronized(quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi, f
|
||||||
// If we happen to also be syncing to audio, we choose to spin on
|
// 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
|
// the amount of time needed for audio to catch up rather than
|
||||||
// our expected wake-up time
|
// our expected wake-up time
|
||||||
syncToAudio(cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full);
|
syncToAudio(audio_sync, cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full);
|
||||||
if (!sync_audio) spinLoop(&timer, wake_time);
|
if (!audio_sync) spinLoop(&timer, wake_time);
|
||||||
wake_time = new_wake_time;
|
wake_time = new_wake_time;
|
||||||
|
|
||||||
tracker.tick();
|
tracker.tick();
|
||||||
|
@ -137,8 +132,8 @@ pub fn runSynchronized(quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi, f
|
||||||
const new_wake_time = blockOnVideo(&timer, wake_time);
|
const new_wake_time = blockOnVideo(&timer, wake_time);
|
||||||
|
|
||||||
// see above comment
|
// see above comment
|
||||||
syncToAudio(cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full);
|
syncToAudio(audio_sync, cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full);
|
||||||
if (!sync_audio) spinLoop(&timer, wake_time);
|
if (!audio_sync) spinLoop(&timer, wake_time);
|
||||||
wake_time = new_wake_time;
|
wake_time = new_wake_time;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -153,22 +148,6 @@ inline fn blockOnVideo(timer: *Timer, wake_time: u64) u64 {
|
||||||
return if (maybe_recalc_wake_time) |recalc| recalc else wake_time + frame_period;
|
return if (maybe_recalc_wake_time) |recalc| recalc else wake_time + frame_period;
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn runBusyLoop(quit: *Atomic(bool), sched: *Scheduler, cpu: *Arm7tdmi) void {
|
|
||||||
log.info("Emulation thread with video sync using busy loop", .{});
|
|
||||||
var timer = Timer.start() catch unreachable;
|
|
||||||
var wake_time: u64 = frame_period;
|
|
||||||
|
|
||||||
while (!quit.load(.SeqCst)) {
|
|
||||||
runFrame(sched, cpu);
|
|
||||||
spinLoop(&timer, wake_time);
|
|
||||||
|
|
||||||
syncToAudio(cpu.bus.apu.stream, &cpu.bus.apu.is_buffer_full);
|
|
||||||
|
|
||||||
// Update to the new wake time
|
|
||||||
wake_time += frame_period;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
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();
|
||||||
|
|
31
src/main.zig
31
src/main.zig
|
@ -2,7 +2,8 @@ const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
const known_folders = @import("known_folders");
|
const known_folders = @import("known_folders");
|
||||||
const clap = @import("clap");
|
const clap = @import("clap");
|
||||||
const toml = @import("toml");
|
|
||||||
|
const config = @import("config.zig");
|
||||||
|
|
||||||
const Gui = @import("platform.zig").Gui;
|
const Gui = @import("platform.zig").Gui;
|
||||||
const Bus = @import("core/Bus.zig");
|
const Bus = @import("core/Bus.zig");
|
||||||
|
@ -14,11 +15,8 @@ const Allocator = std.mem.Allocator;
|
||||||
const log = std.log.scoped(.Cli);
|
const log = std.log.scoped(.Cli);
|
||||||
const width = @import("core/ppu.zig").width;
|
const width = @import("core/ppu.zig").width;
|
||||||
const height = @import("core/ppu.zig").height;
|
const height = @import("core/ppu.zig").height;
|
||||||
const cpu_logging = @import("core/emu.zig").cpu_logging;
|
|
||||||
pub const log_level = if (builtin.mode != .Debug) .info else std.log.default_level;
|
pub const log_level = if (builtin.mode != .Debug) .info else std.log.default_level;
|
||||||
|
|
||||||
// TODO: Reimpl Logging
|
|
||||||
|
|
||||||
// CLI Arguments + Help Text
|
// CLI Arguments + Help Text
|
||||||
const params = clap.parseParamsComptime(
|
const params = clap.parseParamsComptime(
|
||||||
\\-h, --help Display this help and exit.
|
\\-h, --help Display this help and exit.
|
||||||
|
@ -43,10 +41,7 @@ pub fn main() anyerror!void {
|
||||||
const save_path = try savePath(allocator, data_path);
|
const save_path = try savePath(allocator, data_path);
|
||||||
defer allocator.free(save_path);
|
defer allocator.free(save_path);
|
||||||
|
|
||||||
const config = try loadConfig(allocator, config_path);
|
try config.load(allocator, config_path);
|
||||||
_ = config;
|
|
||||||
|
|
||||||
log.debug("got past loadConfig()", .{});
|
|
||||||
|
|
||||||
// Handle CLI Input
|
// Handle CLI Input
|
||||||
const result = try clap.parse(clap.Help, ¶ms, clap.parsers.default, .{});
|
const result = try clap.parse(clap.Help, ¶ms, clap.parsers.default, .{});
|
||||||
|
@ -55,7 +50,8 @@ pub fn main() anyerror!void {
|
||||||
const paths = try handleArguments(allocator, data_path, &result);
|
const paths = try handleArguments(allocator, data_path, &result);
|
||||||
defer if (paths.save) |path| allocator.free(path);
|
defer if (paths.save) |path| allocator.free(path);
|
||||||
|
|
||||||
const log_file: ?std.fs.File = if (cpu_logging) try std.fs.cwd().createFile("zba.log", .{}) else null;
|
const cpu_trace = config.config().debug.cpu_trace;
|
||||||
|
const log_file: ?std.fs.File = if (cpu_trace) try std.fs.cwd().createFile("zba.log", .{}) else null;
|
||||||
defer if (log_file) |file| file.close();
|
defer if (log_file) |file| file.close();
|
||||||
|
|
||||||
// TODO: Take Emulator Init Code out of main.zig
|
// TODO: Take Emulator Init Code out of main.zig
|
||||||
|
@ -92,23 +88,6 @@ pub fn handleArguments(allocator: Allocator, data_path: []const u8, result: *con
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
const Config = struct {
|
|
||||||
// TODO: Add Config Fields
|
|
||||||
// TODO: Move to it's own file? config.zig?
|
|
||||||
};
|
|
||||||
|
|
||||||
// FIXME: TOML Library Leaks memory here
|
|
||||||
// Either I should improve the TOML library, or try some of the even less used ones :thinking
|
|
||||||
fn loadConfig(allocator: Allocator, config_path: []const u8) !Config {
|
|
||||||
// _ = allocator;
|
|
||||||
// _ = config_path;
|
|
||||||
|
|
||||||
const table = try toml.parseFile(allocator, config_path, null);
|
|
||||||
table.deinit();
|
|
||||||
|
|
||||||
return .{};
|
|
||||||
}
|
|
||||||
|
|
||||||
fn configFilePath(allocator: Allocator, data_path: []const u8) ![]const u8 {
|
fn configFilePath(allocator: Allocator, data_path: []const u8) ![]const u8 {
|
||||||
const path = try std.fs.path.join(allocator, &[_][]const u8{ data_path, "zba", "config.toml" });
|
const path = try std.fs.path.join(allocator, &[_][]const u8{ data_path, "zba", "config.toml" });
|
||||||
|
|
||||||
|
|
|
@ -1,6 +1,7 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const SDL = @import("sdl2");
|
const SDL = @import("sdl2");
|
||||||
const emu = @import("core/emu.zig");
|
const emu = @import("core/emu.zig");
|
||||||
|
const config = @import("config.zig");
|
||||||
|
|
||||||
const Apu = @import("core/apu.zig").Apu;
|
const Apu = @import("core/apu.zig").Apu;
|
||||||
const Arm7tdmi = @import("core/cpu.zig").Arm7tdmi;
|
const Arm7tdmi = @import("core/cpu.zig").Arm7tdmi;
|
||||||
|
@ -10,8 +11,6 @@ const FpsTracker = @import("util.zig").FpsTracker;
|
||||||
const span = @import("util.zig").span;
|
const span = @import("util.zig").span;
|
||||||
|
|
||||||
const pitch = @import("core/ppu.zig").framebuf_pitch;
|
const pitch = @import("core/ppu.zig").framebuf_pitch;
|
||||||
const scale = @import("core/emu.zig").win_scale;
|
|
||||||
|
|
||||||
const default_title: []const u8 = "ZBA";
|
const default_title: []const u8 = "ZBA";
|
||||||
|
|
||||||
pub const Gui = struct {
|
pub const Gui = struct {
|
||||||
|
@ -28,16 +27,19 @@ pub const Gui = struct {
|
||||||
const ret = SDL.SDL_Init(SDL.SDL_INIT_VIDEO | SDL.SDL_INIT_EVENTS | SDL.SDL_INIT_AUDIO | SDL.SDL_INIT_GAMECONTROLLER);
|
const ret = SDL.SDL_Init(SDL.SDL_INIT_VIDEO | SDL.SDL_INIT_EVENTS | SDL.SDL_INIT_AUDIO | SDL.SDL_INIT_GAMECONTROLLER);
|
||||||
if (ret < 0) panic();
|
if (ret < 0) panic();
|
||||||
|
|
||||||
|
const win_scale = @intCast(c_int, config.config().host.win_scale);
|
||||||
|
|
||||||
const window = SDL.SDL_CreateWindow(
|
const window = SDL.SDL_CreateWindow(
|
||||||
default_title.ptr,
|
default_title.ptr,
|
||||||
SDL.SDL_WINDOWPOS_CENTERED,
|
SDL.SDL_WINDOWPOS_CENTERED,
|
||||||
SDL.SDL_WINDOWPOS_CENTERED,
|
SDL.SDL_WINDOWPOS_CENTERED,
|
||||||
@as(c_int, width * scale),
|
@as(c_int, width * win_scale),
|
||||||
@as(c_int, height * scale),
|
@as(c_int, height * win_scale),
|
||||||
SDL.SDL_WINDOW_SHOWN,
|
SDL.SDL_WINDOW_SHOWN,
|
||||||
) orelse panic();
|
) orelse panic();
|
||||||
|
|
||||||
const renderer = SDL.SDL_CreateRenderer(window, -1, SDL.SDL_RENDERER_ACCELERATED | SDL.SDL_RENDERER_PRESENTVSYNC) orelse panic();
|
const renderer_flags = SDL.SDL_RENDERER_ACCELERATED | if (config.config().host.vsync) SDL.SDL_RENDERER_PRESENTVSYNC else 0;
|
||||||
|
const renderer = SDL.SDL_CreateRenderer(window, -1, @bitCast(u32, renderer_flags)) orelse panic();
|
||||||
|
|
||||||
const texture = SDL.SDL_CreateTexture(
|
const texture = SDL.SDL_CreateTexture(
|
||||||
renderer,
|
renderer,
|
||||||
|
|
12
src/util.zig
12
src/util.zig
|
@ -1,10 +1,10 @@
|
||||||
const std = @import("std");
|
const std = @import("std");
|
||||||
const builtin = @import("builtin");
|
const builtin = @import("builtin");
|
||||||
|
const config = @import("config.zig");
|
||||||
|
|
||||||
const Log2Int = std.math.Log2Int;
|
const Log2Int = std.math.Log2Int;
|
||||||
const Arm7tdmi = @import("core/cpu.zig").Arm7tdmi;
|
const Arm7tdmi = @import("core/cpu.zig").Arm7tdmi;
|
||||||
|
|
||||||
const allow_unhandled_io = @import("core/emu.zig").allow_unhandled_io;
|
|
||||||
|
|
||||||
// Sign-Extend value of type `T` to type `U`
|
// Sign-Extend value of type `T` to type `U`
|
||||||
pub fn sext(comptime T: type, comptime U: type, value: T) T {
|
pub fn sext(comptime T: type, comptime U: type, value: T) T {
|
||||||
// U must have less bits than T
|
// U must have less bits than T
|
||||||
|
@ -144,8 +144,10 @@ pub const io = struct {
|
||||||
}
|
}
|
||||||
|
|
||||||
pub fn undef(comptime T: type, log: anytype, comptime format: []const u8, args: anytype) ?T {
|
pub fn undef(comptime T: type, log: anytype, comptime format: []const u8, args: anytype) ?T {
|
||||||
|
const unhandled_io = config.config().debug.unhandled_io;
|
||||||
|
|
||||||
log.warn(format, args);
|
log.warn(format, args);
|
||||||
if (builtin.mode == .Debug and !allow_unhandled_io) std.debug.panic("TODO: Implement I/O Register", .{});
|
if (builtin.mode == .Debug and !unhandled_io) std.debug.panic("TODO: Implement I/O Register", .{});
|
||||||
|
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
@ -153,8 +155,10 @@ pub const io = struct {
|
||||||
|
|
||||||
pub const write = struct {
|
pub const write = struct {
|
||||||
pub fn undef(log: anytype, comptime format: []const u8, args: anytype) void {
|
pub fn undef(log: anytype, comptime format: []const u8, args: anytype) void {
|
||||||
|
const unhandled_io = config.config().debug.unhandled_io;
|
||||||
|
|
||||||
log.warn(format, args);
|
log.warn(format, args);
|
||||||
if (builtin.mode == .Debug and !allow_unhandled_io) std.debug.panic("TODO: Implement I/O Register", .{});
|
if (builtin.mode == .Debug and !unhandled_io) std.debug.panic("TODO: Implement I/O Register", .{});
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
};
|
};
|
||||||
|
|
Loading…
Reference in New Issue