diff --git a/src/core/apu.zig b/src/core/apu.zig index 61cc64c..e5880be 100644 --- a/src/core/apu.zig +++ b/src/core/apu.zig @@ -306,15 +306,15 @@ pub const Apu = struct { }; // Add all PSG channels together - left += if (ch_left & 1 == 1) self.ch1.amplitude() else 0; - left += if (ch_left >> 1 & 1 == 1) self.ch2.amplitude() else 0; - left += if (ch_left >> 2 & 1 == 1) self.ch3.amplitude() else 0; - left += if (ch_left >> 3 == 1) self.ch4.amplitude() else 0; + left += if (ch_left & 1 == 1) @as(i16, self.ch1.sample) else 0; + left += if (ch_left >> 1 & 1 == 1) @as(i16, self.ch2.sample) else 0; + left += if (ch_left >> 2 & 1 == 1) @as(i16, self.ch3.sample) else 0; + left += if (ch_left >> 3 == 1) @as(i16, self.ch4.sample) else 0; - right += if (ch_right & 1 == 1) self.ch1.amplitude() else 0; - right += if (ch_right >> 1 & 1 == 1) self.ch2.amplitude() else 0; - right += if (ch_right >> 2 & 1 == 1) self.ch3.amplitude() else 0; - right += if (ch_right >> 3 == 1) self.ch4.amplitude() else 0; + right += if (ch_right & 1 == 1) @as(i16, self.ch1.sample) else 0; + right += if (ch_right >> 1 & 1 == 1) @as(i16, self.ch2.sample) else 0; + right += if (ch_right >> 2 & 1 == 1) @as(i16, self.ch3.sample) else 0; + right += if (ch_right >> 3 == 1) @as(i16, self.ch4.sample) else 0; // Multiply by master channel volume left *= 1 + @as(i16, self.psg_cnt.left_vol.read()); diff --git a/src/core/apu/Noise.zig b/src/core/apu/Noise.zig index 6f816c5..fd8e30a 100644 --- a/src/core/apu/Noise.zig +++ b/src/core/apu/Noise.zig @@ -1,4 +1,5 @@ const io = @import("../bus/io.zig"); +const util = @import("../../util.zig"); const Scheduler = @import("../scheduler.zig").Scheduler; const FrameSequencer = @import("../apu.zig").FrameSequencer; @@ -123,7 +124,7 @@ pub fn setNr44(self: *Self, fs: *const FrameSequencer, byte: u8) void { self.enabled = self.isDacEnabled(); } - self.lfsr.updateLength(fs, self, new); + util.audio.length.ch4.update(self, fs, new); self.cnt = new; } @@ -135,10 +136,6 @@ pub fn channelTimerOverflow(self: *Self, late: u64) void { self.sample = if (self.enabled) self.lfsr.sample() * @as(i8, self.env_dev.vol) else 0; } -pub fn amplitude(self: *const Self) i16 { - return @as(i16, self.sample); -} - fn isDacEnabled(self: *const Self) bool { return self.envelope.raw & 0xF8 != 0x00; } diff --git a/src/core/apu/Tone.zig b/src/core/apu/Tone.zig index a60ce3b..3b4b7f5 100644 --- a/src/core/apu/Tone.zig +++ b/src/core/apu/Tone.zig @@ -1,4 +1,5 @@ const io = @import("../bus/io.zig"); +const util = @import("../../util.zig"); const Scheduler = @import("../scheduler.zig").Scheduler; const FrameSequencer = @import("../apu.zig").FrameSequencer; @@ -65,10 +66,6 @@ pub fn channelTimerOverflow(self: *Self, late: u64) void { self.sample = if (self.enabled) self.square.sample(self.duty) * @as(i8, self.env_dev.vol) else 0; } -pub fn amplitude(self: *const Self) i16 { - return @as(i16, self.sample); -} - /// NR21, NR22 pub fn getSoundCntL(self: *const Self) u16 { return @as(u16, self.envelope.raw) << 8 | (self.duty.raw & 0xC0); @@ -131,7 +128,7 @@ pub fn setNr24(self: *Self, fs: *const FrameSequencer, byte: u8) void { self.enabled = self.isDacEnabled(); } - self.square.updateLength(Self, fs, self, new); + util.audio.length.update(Self, self, fs, new); self.freq = new; } diff --git a/src/core/apu/ToneSweep.zig b/src/core/apu/ToneSweep.zig index 3502327..8af7efb 100644 --- a/src/core/apu/ToneSweep.zig +++ b/src/core/apu/ToneSweep.zig @@ -1,5 +1,5 @@ -// const std = @import("std"); const io = @import("../bus/io.zig"); +const util = @import("../../util.zig"); const Scheduler = @import("../scheduler.zig").Scheduler; const FrameSequencer = @import("../apu.zig").FrameSequencer; @@ -79,10 +79,6 @@ pub fn channelTimerOverflow(self: *Self, late: u64) void { self.sample = if (self.enabled) self.square.sample(self.duty) * @as(i8, self.env_dev.vol) else 0; } -pub fn amplitude(self: *const Self) i16 { - return @as(i16, self.sample); -} - /// NR10, NR11, NR12 pub fn setSoundCnt(self: *Self, value: u32) void { self.setSoundCntL(@truncate(u8, value)); @@ -181,7 +177,7 @@ pub fn setNr14(self: *Self, fs: *const FrameSequencer, byte: u8) void { self.enabled = self.isDacEnabled(); } - self.square.updateLength(Self, fs, self, new); + util.audio.length.update(Self, self, fs, new); self.freq = new; } diff --git a/src/core/apu/Wave.zig b/src/core/apu/Wave.zig index ec38960..9a64657 100644 --- a/src/core/apu/Wave.zig +++ b/src/core/apu/Wave.zig @@ -1,4 +1,5 @@ const io = @import("../bus/io.zig"); +const util = @import("../../util.zig"); const Scheduler = @import("../scheduler.zig").Scheduler; const FrameSequencer = @import("../apu.zig").FrameSequencer; @@ -111,7 +112,7 @@ pub fn setNr34(self: *Self, fs: *const FrameSequencer, byte: u8) void { self.enabled = self.select.enabled.read(); } - self.wave_dev.updateLength(fs, self, new); + util.audio.length.update(Self, self, fs, new); self.freq = new; } @@ -123,7 +124,3 @@ pub fn channelTimerOverflow(self: *Self, late: u64) void { // Convert unsigned 4-bit wave sample to signed 8-bit sample self.sample = (2 * @as(i8, self.wave_dev.sample(self.select)) - 15) >> self.wave_dev.shift(self.vol); } - -pub fn amplitude(self: *const Self) i16 { - return @as(i16, self.sample); -} diff --git a/src/core/apu/signal/Lfsr.zig b/src/core/apu/signal/Lfsr.zig index ffa5fe8..6d7a704 100644 --- a/src/core/apu/signal/Lfsr.zig +++ b/src/core/apu/signal/Lfsr.zig @@ -25,22 +25,6 @@ pub fn sample(self: *const Self) i8 { return if ((~self.shift & 1) == 1) 1 else -1; } -/// Update the sate of the Channel Length TImer -pub fn updateLength(_: *Self, fs: *const FrameSequencer, ch4: *Noise, nr44: io.NoiseControl) void { - // Write to NRx4 when FS's next step is not one that clocks the length counter - if (!fs.isLengthNext()) { - // If length_enable was disabled but is now enabled and length timer is not 0 already, - // decrement the length timer - - if (!ch4.cnt.length_enable.read() and nr44.length_enable.read() and ch4.len_dev.timer != 0) { - ch4.len_dev.timer -= 1; - - // If Length Timer is now 0 and trigger is clear, disable the channel - if (ch4.len_dev.timer == 0 and !nr44.trigger.read()) ch4.enabled = false; - } - } -} - /// Reload LFSR Timer pub fn reload(self: *Self, poly: io.PolyCounter) void { self.sched.removeScheduledEvent(.{ .ApuChannel = 3 }); diff --git a/src/core/apu/signal/Square.zig b/src/core/apu/signal/Square.zig index 444dd39..c026f41 100644 --- a/src/core/apu/signal/Square.zig +++ b/src/core/apu/signal/Square.zig @@ -21,23 +21,6 @@ pub fn init(sched: *Scheduler) Self { }; } -/// Updates the State of either Ch1 or Ch2's Length Timer -pub fn updateLength(_: *const Self, comptime T: type, fs: *const FrameSequencer, ch: *T, nrx34: io.Frequency) void { - comptime std.debug.assert(T == ToneSweep or T == Tone); - // Write to NRx4 when FS's next step is not one that clocks the length counter - if (!fs.isLengthNext()) { - // If length_enable was disabled but is now enabled and length timer is not 0 already, - // decrement the length timer - - if (!ch.freq.length_enable.read() and nrx34.length_enable.read() and ch.len_dev.timer != 0) { - ch.len_dev.timer -= 1; - - // If Length Timer is now 0 and trigger is clear, disable the channel - if (ch.len_dev.timer == 0 and !nrx34.trigger.read()) ch.enabled = false; - } - } -} - /// Scheduler Event Handler for Square Synth Timer Expire pub fn onSquareTimerExpire(self: *Self, comptime T: type, nrx34: io.Frequency, late: u64) void { comptime std.debug.assert(T == ToneSweep or T == Tone); diff --git a/src/core/apu/signal/Wave.zig b/src/core/apu/signal/Wave.zig index dc0f852..f7c852e 100644 --- a/src/core/apu/signal/Wave.zig +++ b/src/core/apu/signal/Wave.zig @@ -77,19 +77,3 @@ pub fn shift(_: *const Self, nr32: io.WaveVolume) u2 { 0b11 => 2, // 25% Volume }; } - -/// Update state of Channel 3 Length Device -pub fn updateLength(_: *Self, fs: *const FrameSequencer, ch3: *Wave, nrx34: io.Frequency) void { - // Write to NRx4 when FS's next step is not one that clocks the length counter - if (!fs.isLengthNext()) { - // If length_enable was disabled but is now enabled and length timer is not 0 already, - // decrement the length timer - - if (!ch3.freq.length_enable.read() and nrx34.length_enable.read() and ch3.len_dev.timer != 0) { - ch3.len_dev.timer -= 1; - - // If Length Timer is now 0 and trigger is clear, disable the channel - if (ch3.len_dev.timer == 0 and !nrx34.trigger.read()) ch3.enabled = false; - } - } -} diff --git a/src/util.zig b/src/util.zig index 57896e8..1a4f3c2 100644 --- a/src/util.zig +++ b/src/util.zig @@ -158,17 +158,6 @@ pub const io = struct { } }; }; -pub fn readUndefined(log: anytype, comptime format: []const u8, args: anytype) u8 { - log.warn(format, args); - if (builtin.mode == .Debug) std.debug.panic("TODO: Implement I/O Register", .{}); - - return 0; -} - -pub fn writeUndefined(log: anytype, comptime format: []const u8, args: anytype) void { - log.warn(format, args); - if (builtin.mode == .Debug) std.debug.panic("TODO: Implement I/O Register", .{}); -} pub const Logger = struct { const Self = @This(); @@ -230,3 +219,52 @@ pub const Logger = struct { }; const FmtArgTuple = std.meta.Tuple(&.{ u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32, u32 }); + +pub const audio = struct { + const _io = @import("core/bus/io.zig"); + + const ToneSweep = @import("core/apu/ToneSweep.zig"); + const Tone = @import("core/apu/Tone.zig"); + const Wave = @import("core/apu/Wave.zig"); + const Noise = @import("core/apu/Noise.zig"); + + pub const length = struct { + const FrameSequencer = @import("core/apu.zig").FrameSequencer; + + /// Update State of Ch1, Ch2 and Ch3 length timer + pub fn update(comptime T: type, self: *T, fs: *const FrameSequencer, nrx34: _io.Frequency) void { + comptime std.debug.assert(T == ToneSweep or T == Tone or T == Wave); + + // Write to NRx4 when FS's next step is not one that clocks the length counter + if (!fs.isLengthNext()) { + // If length_enable was disabled but is now enabled and length timer is not 0 already, + // decrement the length timer + + if (!self.freq.length_enable.read() and nrx34.length_enable.read() and self.len_dev.timer != 0) { + self.len_dev.timer -= 1; + + // If Length Timer is now 0 and trigger is clear, disable the channel + if (self.len_dev.timer == 0 and !nrx34.trigger.read()) self.enabled = false; + } + } + } + + pub const ch4 = struct { + /// update state of ch4 length timer + pub fn update(self: *Noise, fs: *const FrameSequencer, nr44: _io.NoiseControl) void { + // Write to NRx4 when FS's next step is not one that clocks the length counter + if (!fs.isLengthNext()) { + // If length_enable was disabled but is now enabled and length timer is not 0 already, + // decrement the length timer + + if (!self.cnt.length_enable.read() and nr44.length_enable.read() and self.len_dev.timer != 0) { + self.len_dev.timer -= 1; + + // If Length Timer is now 0 and trigger is clear, disable the channel + if (self.len_dev.timer == 0 and !nr44.trigger.read()) self.enabled = false; + } + } + } + }; + }; +};