Compare commits

..

No commits in common. "10aec67ee0c5a84b6f4e7f430a77f01ecaa5ed16" and "4b8ed3cebbd43fb6af13e5cead3c5d5fbb29ec32" have entirely different histories.

21 changed files with 81 additions and 46 deletions

View File

@ -1,5 +1,6 @@
const std = @import("std"); const std = @import("std");
const AudioDeviceId = @import("sdl2").SDL_AudioDeviceID;
const Arm7tdmi = @import("cpu.zig").Arm7tdmi; const Arm7tdmi = @import("cpu.zig").Arm7tdmi;
const Bios = @import("bus/Bios.zig"); const Bios = @import("bus/Bios.zig");
const Ewram = @import("bus/Ewram.zig"); const Ewram = @import("bus/Ewram.zig");

View File

@ -3,6 +3,8 @@ const SDL = @import("sdl2");
const io = @import("bus/io.zig"); const io = @import("bus/io.zig");
const util = @import("../util.zig"); const util = @import("../util.zig");
const AudioDeviceId = SDL.SDL_AudioDeviceID;
const Arm7tdmi = @import("cpu.zig").Arm7tdmi; const Arm7tdmi = @import("cpu.zig").Arm7tdmi;
const Scheduler = @import("scheduler.zig").Scheduler; const Scheduler = @import("scheduler.zig").Scheduler;
const ToneSweep = @import("apu/ToneSweep.zig"); const ToneSweep = @import("apu/ToneSweep.zig");
@ -18,8 +20,7 @@ const setLo = @import("../util.zig").setLo;
const log = std.log.scoped(.APU); const log = std.log.scoped(.APU);
pub const host_rate = @import("../platform.zig").sample_rate; pub const host_sample_rate = 1 << 15;
pub const host_format = @import("../platform.zig").sample_format;
pub fn read(comptime T: type, apu: *const Apu, addr: u32) ?T { pub fn read(comptime T: type, apu: *const Apu, addr: u32) ?T {
const byte = @truncate(u8, addr); const byte = @truncate(u8, addr);
@ -188,7 +189,7 @@ pub const Apu = struct {
.bias = .{ .raw = 0x0200 }, .bias = .{ .raw = 0x0200 },
.sampling_cycle = 0b00, .sampling_cycle = 0b00,
.stream = SDL.SDL_NewAudioStream(SDL.AUDIO_U16, 2, 1 << 15, host_format, 2, host_rate).?, .stream = SDL.SDL_NewAudioStream(SDL.AUDIO_U16, 2, 1 << 15, SDL.AUDIO_U16, 2, host_sample_rate).?,
.sched = sched, .sched = sched,
.capacitor = 0, .capacitor = 0,
@ -355,7 +356,7 @@ pub const Apu = struct {
defer SDL.SDL_FreeAudioStream(old_stream); defer SDL.SDL_FreeAudioStream(old_stream);
self.sampling_cycle = self.bias.sampling_cycle.read(); self.sampling_cycle = self.bias.sampling_cycle.read();
self.stream = SDL.SDL_NewAudioStream(SDL.AUDIO_U16, 2, @intCast(c_int, sample_rate), host_format, 2, host_rate).?; self.stream = SDL.SDL_NewAudioStream(SDL.AUDIO_U16, 2, @intCast(c_int, sample_rate), SDL.AUDIO_U16, 2, host_sample_rate).?;
} }
fn interval(self: *const Self) u64 { fn interval(self: *const Self) u64 {
@ -455,8 +456,8 @@ const DmaSoundKind = enum {
}; };
pub const FrameSequencer = struct { pub const FrameSequencer = struct {
const interval = (1 << 24) / 512;
const Self = @This(); const Self = @This();
pub const interval = (1 << 24) / 512;
step: u3, step: u3,

View File

@ -1,7 +1,9 @@
//! Linear Feedback Shift Register
const io = @import("../../bus/io.zig"); const io = @import("../../bus/io.zig");
/// Linear Feedback Shift Register
const Scheduler = @import("../../scheduler.zig").Scheduler; const Scheduler = @import("../../scheduler.zig").Scheduler;
const FrameSequencer = @import("../../apu.zig").FrameSequencer;
const Noise = @import("../Noise.zig");
const Self = @This(); const Self = @This();
pub const interval: u64 = (1 << 24) / (1 << 22); pub const interval: u64 = (1 << 24) / (1 << 22);

View File

@ -2,6 +2,7 @@ const std = @import("std");
const io = @import("../../bus/io.zig"); const io = @import("../../bus/io.zig");
const Scheduler = @import("../../scheduler.zig").Scheduler; const Scheduler = @import("../../scheduler.zig").Scheduler;
const FrameSequencer = @import("../../apu.zig").FrameSequencer;
const ToneSweep = @import("../ToneSweep.zig"); const ToneSweep = @import("../ToneSweep.zig");
const Tone = @import("../Tone.zig"); const Tone = @import("../Tone.zig");

View File

@ -2,6 +2,8 @@ const std = @import("std");
const io = @import("../../bus/io.zig"); const io = @import("../../bus/io.zig");
const Scheduler = @import("../../scheduler.zig").Scheduler; const Scheduler = @import("../../scheduler.zig").Scheduler;
const FrameSequencer = @import("../../apu.zig").FrameSequencer;
const Wave = @import("../Wave.zig");
const buf_len = 0x20; const buf_len = 0x20;
pub const interval: u64 = (1 << 24) / (1 << 22); pub const interval: u64 = (1 << 24) / (1 << 22);

View File

@ -1,6 +1,10 @@
const std = @import("std"); const std = @import("std");
const config = @import("../../config.zig"); const config = @import("../../config.zig");
const Bit = @import("bitfield").Bit;
const Bitfield = @import("bitfield").Bitfield;
const DateTime = @import("datetime").datetime.Datetime;
const Arm7tdmi = @import("../cpu.zig").Arm7tdmi; const Arm7tdmi = @import("../cpu.zig").Arm7tdmi;
const Backup = @import("backup.zig").Backup; const Backup = @import("backup.zig").Backup;
const Gpio = @import("gpio.zig").Gpio; const Gpio = @import("gpio.zig").Gpio;

View File

@ -1,5 +1,6 @@
const std = @import("std"); const std = @import("std");
const Bit = @import("bitfield").Bit; const Bit = @import("bitfield").Bit;
const Bitfield = @import("bitfield").Bitfield;
const DateTime = @import("datetime").datetime.Datetime; const DateTime = @import("datetime").datetime.Datetime;
const Arm7tdmi = @import("../cpu.zig").Arm7tdmi; const Arm7tdmi = @import("../cpu.zig").Arm7tdmi;

View File

@ -1,4 +1,5 @@
const std = @import("std"); const std = @import("std");
const builtin = @import("builtin");
const timer = @import("timer.zig"); const timer = @import("timer.zig");
const dma = @import("dma.zig"); const dma = @import("dma.zig");
const apu = @import("../apu.zig"); const apu = @import("../apu.zig");
@ -7,6 +8,8 @@ const util = @import("../../util.zig");
const Bit = @import("bitfield").Bit; const Bit = @import("bitfield").Bit;
const Bitfield = @import("bitfield").Bitfield; const Bitfield = @import("bitfield").Bitfield;
const Bus = @import("../Bus.zig"); const Bus = @import("../Bus.zig");
const DmaController = @import("dma.zig").DmaController;
const Scheduler = @import("../scheduler.zig").Scheduler;
const setHi = util.setHi; const setHi = util.setHi;
const setLo = util.setLo; const setLo = util.setLo;

View File

@ -2,7 +2,9 @@ const std = @import("std");
const util = @import("../../util.zig"); const util = @import("../../util.zig");
const TimerControl = @import("io.zig").TimerControl; const TimerControl = @import("io.zig").TimerControl;
const Io = @import("io.zig").Io;
const Scheduler = @import("../scheduler.zig").Scheduler; const Scheduler = @import("../scheduler.zig").Scheduler;
const Event = @import("../scheduler.zig").Event;
const Arm7tdmi = @import("../cpu.zig").Arm7tdmi; const Arm7tdmi = @import("../cpu.zig").Arm7tdmi;
pub const TimerTuple = std.meta.Tuple(&[_]type{ Timer(0), Timer(1), Timer(2), Timer(3) }); pub const TimerTuple = std.meta.Tuple(&[_]type{ Timer(0), Timer(1), Timer(2), Timer(3) });

View File

@ -1,9 +1,11 @@
const std = @import("std"); const std = @import("std");
const util = @import("../util.zig");
const Bus = @import("Bus.zig"); const Bus = @import("Bus.zig");
const Bit = @import("bitfield").Bit; const Bit = @import("bitfield").Bit;
const Bitfield = @import("bitfield").Bitfield; const Bitfield = @import("bitfield").Bitfield;
const Scheduler = @import("scheduler.zig").Scheduler; const Scheduler = @import("scheduler.zig").Scheduler;
const FilePaths = @import("../util.zig").FilePaths;
const Logger = @import("../util.zig").Logger; const Logger = @import("../util.zig").Logger;
const File = std.fs.File; const File = std.fs.File;

View File

@ -1,3 +1,5 @@
const std = @import("std");
const Bus = @import("../../Bus.zig"); const Bus = @import("../../Bus.zig");
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
const InstrFn = @import("../../cpu.zig").arm.InstrFn; const InstrFn = @import("../../cpu.zig").arm.InstrFn;

View File

@ -1,3 +1,5 @@
const std = @import("std");
const Bus = @import("../../Bus.zig"); const Bus = @import("../../Bus.zig");
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
const InstrFn = @import("../../cpu.zig").arm.InstrFn; const InstrFn = @import("../../cpu.zig").arm.InstrFn;

View File

@ -1,3 +1,5 @@
const std = @import("std");
const Bus = @import("../../Bus.zig"); const Bus = @import("../../Bus.zig");
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
const InstrFn = @import("../../cpu.zig").arm.InstrFn; const InstrFn = @import("../../cpu.zig").arm.InstrFn;

View File

@ -1,3 +1,6 @@
const std = @import("std");
const util = @import("../../../util.zig");
const shifter = @import("../barrel_shifter.zig"); const shifter = @import("../barrel_shifter.zig");
const Bus = @import("../../Bus.zig"); const Bus = @import("../../Bus.zig");
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;

View File

@ -1,3 +1,5 @@
const std = @import("std");
const Arm7tdmi = @import("../cpu.zig").Arm7tdmi; const Arm7tdmi = @import("../cpu.zig").Arm7tdmi;
const CPSR = @import("../cpu.zig").PSR; const CPSR = @import("../cpu.zig").PSR;

View File

@ -1,3 +1,5 @@
const std = @import("std");
const Bus = @import("../../Bus.zig"); const Bus = @import("../../Bus.zig");
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
const InstrFn = @import("../../cpu.zig").thumb.InstrFn; const InstrFn = @import("../../cpu.zig").thumb.InstrFn;

View File

@ -1,3 +1,5 @@
const std = @import("std");
const Bus = @import("../../Bus.zig"); const Bus = @import("../../Bus.zig");
const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi; const Arm7tdmi = @import("../../cpu.zig").Arm7tdmi;
const InstrFn = @import("../../cpu.zig").thumb.InstrFn; const InstrFn = @import("../../cpu.zig").thumb.InstrFn;

View File

@ -2,29 +2,29 @@ const std = @import("std");
const SDL = @import("sdl2"); const SDL = @import("sdl2");
const config = @import("../config.zig"); const config = @import("../config.zig");
const Bus = @import("Bus.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 FpsTracker = @import("../util.zig").FpsTracker; const FpsTracker = @import("../util.zig").FpsTracker;
const FilePaths = @import("../util.zig").FilePaths;
const Timer = std.time.Timer; const Timer = std.time.Timer;
const Thread = std.Thread;
const Atomic = std.atomic.Atomic; const Atomic = std.atomic.Atomic;
const Allocator = std.mem.Allocator;
/// 4 Cycles in 1 dot // 228 Lines which consist of 308 dots (which are 4 cycles long)
const cycles_per_dot = 4; const cycles_per_frame: u64 = 228 * (308 * 4); //280896
const clock_rate: u64 = 1 << 24; // 16.78MHz
/// The GBA draws 228 Horizontal which each consist 308 dots // TODO: Don't truncate this, be more accurate w/ timing
/// (note: not all lines are visible) // 59.6046447754ns (truncated to just 59ns)
const cycles_per_frame = 228 * (308 * cycles_per_dot); //280896 const clock_period: u64 = std.time.ns_per_s / clock_rate;
const frame_period = (clock_period * cycles_per_frame);
/// The GBA ARM7TDMI runs at 2^24 Hz // 59.7275005696Hz
const clock_rate = 1 << 24; // 16.78MHz pub const frame_rate = @intToFloat(f64, std.time.ns_per_s) /
((@intToFloat(f64, std.time.ns_per_s) / @intToFloat(f64, clock_rate)) * @intToFloat(f64, cycles_per_frame));
/// The # of nanoseconds a frame should take
const frame_period = (std.time.ns_per_s * cycles_per_frame) / clock_rate;
/// Exact Value: 59.7275005696Hz
/// The inverse of the frame period
pub const frame_rate: f64 = @intToFloat(f64, clock_rate) / cycles_per_frame;
const log = std.log.scoped(.Emulation); const log = std.log.scoped(.Emulation);
@ -36,7 +36,7 @@ const RunKind = enum {
}; };
pub fn run(quit: *Atomic(bool), scheduler: *Scheduler, cpu: *Arm7tdmi, tracker: *FpsTracker) void { pub fn run(quit: *Atomic(bool), scheduler: *Scheduler, cpu: *Arm7tdmi, tracker: *FpsTracker) void {
const audio_sync = config.config().guest.audio_sync and !config.config().host.mute; const audio_sync = config.config().guest.audio_sync;
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) {
@ -132,10 +132,11 @@ fn videoSync(timer: *Timer, wake_time: u64) u64 {
// TODO: Better sleep impl? // 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 timestamp = timer.read(); const timestamp = timer.read();
// ns_late is non zero if we are late. // ns_late is non zero if we are late.
var ns_late = timestamp -| wake_time; const ns_late = timestamp -| wake_time;
// If we're more than a frame late, skip the rest of this loop // If we're more than a frame late, skip the rest of this loop
// Recalculate what our new wake time should be so that we can // Recalculate what our new wake time should be so that we can
@ -143,18 +144,15 @@ fn sleep(timer: *Timer, wake_time: u64) ?u64 {
if (ns_late > frame_period) return timestamp + frame_period; if (ns_late > frame_period) return timestamp + frame_period;
const sleep_for = frame_period - ns_late; const sleep_for = frame_period - ns_late;
const step = 2 * std.time.ns_per_ms; // Granularity of 2ms // // Employ several sleep calls in periods of 10ms
const times = sleep_for / step; // // By doing this the behaviour should average out to be
var i: usize = 0; // // more consistent
// const loop_count = sleep_for / step; // How many groups of 10ms
while (i < times) : (i += 1) { // var i: usize = 0;
std.time.sleep(step); // while (i < loop_count) : (i += 1) std.time.sleep(step);
// Upon wakeup, check to see if this particular sleep was longer than expected std.time.sleep(sleep_for);
// if so we should exit early, but probably not skip a whole frame period
ns_late = timer.read() -| wake_time;
if (ns_late > frame_period) return null;
}
return null; return null;
} }

View File

@ -1,6 +1,7 @@
const std = @import("std"); const std = @import("std");
const io = @import("bus/io.zig"); const io = @import("bus/io.zig");
const EventKind = @import("scheduler.zig").EventKind;
const Scheduler = @import("scheduler.zig").Scheduler; const Scheduler = @import("scheduler.zig").Scheduler;
const Arm7tdmi = @import("cpu.zig").Arm7tdmi; const Arm7tdmi = @import("cpu.zig").Arm7tdmi;

View File

@ -1,5 +1,6 @@
const std = @import("std"); const std = @import("std");
const Bus = @import("Bus.zig");
const Arm7tdmi = @import("cpu.zig").Arm7tdmi; const Arm7tdmi = @import("cpu.zig").Arm7tdmi;
const Clock = @import("bus/gpio.zig").Clock; const Clock = @import("bus/gpio.zig").Clock;

View File

@ -11,12 +11,10 @@ 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 gba_width = @import("core/ppu.zig").width; const gba_width = @import("core/ppu.zig").width;
const gba_height = @import("core/ppu.zig").height; const gba_height = @import("core/ppu.zig").height;
pub const sample_rate = 44100;
pub const sample_format = SDL.AUDIO_F32;
const default_title: []const u8 = "ZBA"; const default_title: []const u8 = "ZBA";
pub const Gui = struct { pub const Gui = struct {
@ -255,6 +253,7 @@ pub const Gui = struct {
const Audio = struct { const Audio = struct {
const Self = @This(); const Self = @This();
const log = std.log.scoped(.PlatformAudio); const log = std.log.scoped(.PlatformAudio);
const sample_rate = @import("core/apu.zig").host_sample_rate;
device: SDL.SDL_AudioDeviceID, device: SDL.SDL_AudioDeviceID,
@ -262,22 +261,16 @@ const Audio = struct {
var have: SDL.SDL_AudioSpec = undefined; var have: SDL.SDL_AudioSpec = undefined;
var want: SDL.SDL_AudioSpec = std.mem.zeroes(SDL.SDL_AudioSpec); var want: SDL.SDL_AudioSpec = std.mem.zeroes(SDL.SDL_AudioSpec);
want.freq = sample_rate; want.freq = sample_rate;
want.format = sample_format; want.format = SDL.AUDIO_U16;
want.channels = 2; want.channels = 2;
want.samples = 0x100; want.samples = 0x100;
want.callback = Self.callback; want.callback = Self.callback;
want.userdata = apu; want.userdata = apu;
std.debug.assert(sample_format == SDL.AUDIO_F32);
log.info("Host Sample Rate: {}Hz, Host Format: SDL.AUDIO_F32", .{sample_rate});
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
SDL.SDL_PauseAudioDevice(device, 0); // Unpause Audio
log.info("Unpaused Device", .{});
}
return .{ .device = device }; return .{ .device = device };
} }
@ -288,10 +281,18 @@ const Audio = struct {
} }
export fn callback(userdata: ?*anyopaque, stream: [*c]u8, len: c_int) void { export fn callback(userdata: ?*anyopaque, stream: [*c]u8, len: c_int) void {
const T = *Apu; const apu = @ptrCast(*Apu, @alignCast(@alignOf(*Apu), userdata));
const apu = @ptrCast(T, @alignCast(@alignOf(T), userdata));
_ = SDL.SDL_AudioStreamGet(apu.stream, stream, len); // TODO: Find a better way to mute this
if (!config.config().host.mute) {
_ = SDL.SDL_AudioStreamGet(apu.stream, stream, len);
} else {
// FIXME: I don't think this hack to remove DC Offset is acceptable :thinking:
std.mem.set(u8, stream[0..@intCast(usize, len)], 0x40);
}
// If we don't write anything, play silence otherwise garbage will be played
// if (written == 0) std.mem.set(u8, stream[0..@intCast(usize, len)], 0x40);
} }
}; };