2022-10-21 08:12:27 +00:00
|
|
|
const std = @import("std");
|
|
|
|
|
|
|
|
const SoundFifo = std.fifo.LinearFifo(u8, .{ .Static = 0x20 });
|
|
|
|
|
|
|
|
const io = @import("bus/io.zig");
|
|
|
|
|
|
|
|
pub const Apu = struct {
|
|
|
|
const Self = @This();
|
|
|
|
|
|
|
|
ch1: ToneSweep,
|
|
|
|
ch2: Tone,
|
|
|
|
ch3: Wave,
|
|
|
|
ch4: Noise,
|
|
|
|
chA: DmaSound,
|
|
|
|
chB: DmaSound,
|
|
|
|
|
|
|
|
bias: io.SoundBias,
|
|
|
|
ch_vol_cnt: io.ChannelVolumeControl,
|
|
|
|
dma_cnt: io.DmaSoundControl,
|
|
|
|
cnt: io.SoundControl,
|
|
|
|
|
|
|
|
pub fn init() Self {
|
|
|
|
return .{
|
|
|
|
.ch1 = ToneSweep.init(),
|
|
|
|
.ch2 = Tone.init(),
|
|
|
|
.ch3 = Wave.init(),
|
|
|
|
.ch4 = Noise.init(),
|
|
|
|
.chA = DmaSound.init(),
|
|
|
|
.chB = DmaSound.init(),
|
|
|
|
|
|
|
|
.ch_vol_cnt = .{ .raw = 0 },
|
|
|
|
.dma_cnt = .{ .raw = 0 },
|
|
|
|
.cnt = .{ .raw = 0 },
|
|
|
|
.bias = .{ .raw = 0x0200 },
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn setSoundCntX(self: *Self, value: bool) void {
|
|
|
|
self.cnt.apu_enable.write(value);
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn setSoundCntLLow(self: *Self, byte: u8) void {
|
|
|
|
self.ch_vol_cnt.raw = (self.ch_vol_cnt.raw & 0xFF00) | byte;
|
|
|
|
}
|
|
|
|
|
2022-10-21 08:12:30 +00:00
|
|
|
pub fn setSoundCntLHigh(self: *Self, byte: u8) void {
|
|
|
|
self.ch_vol_cnt.raw = @as(u16, byte) << 8 | (self.ch_vol_cnt.raw & 0xFF);
|
|
|
|
}
|
|
|
|
|
2022-10-21 08:12:27 +00:00
|
|
|
pub fn setBiasHigh(self: *Self, byte: u8) void {
|
|
|
|
self.bias.raw = (@as(u16, byte) << 8) | (self.bias.raw & 0xFF);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const ToneSweep = struct {
|
|
|
|
const Self = @This();
|
|
|
|
|
2022-10-21 08:12:30 +00:00
|
|
|
/// NR10
|
2022-10-21 08:12:27 +00:00
|
|
|
sweep: io.Sweep,
|
2022-10-21 08:12:30 +00:00
|
|
|
/// NR11
|
2022-10-21 08:12:27 +00:00
|
|
|
duty: io.Duty,
|
2022-10-21 08:12:30 +00:00
|
|
|
/// NR12
|
2022-10-21 08:12:27 +00:00
|
|
|
envelope: io.Envelope,
|
2022-10-21 08:12:30 +00:00
|
|
|
/// NR13, NR14
|
2022-10-21 08:12:27 +00:00
|
|
|
freq: io.Frequency,
|
|
|
|
|
|
|
|
fn init() Self {
|
|
|
|
return .{
|
|
|
|
.sweep = .{ .raw = 0 },
|
|
|
|
.duty = .{ .raw = 0 },
|
|
|
|
.envelope = .{ .raw = 0 },
|
|
|
|
.freq = .{ .raw = 0 },
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-10-21 08:12:30 +00:00
|
|
|
pub fn setFreqLow(self: *Self, byte: u8) void {
|
|
|
|
self.freq.raw = (self.freq.raw & 0xFF00) | byte;
|
|
|
|
}
|
|
|
|
|
2022-10-21 08:12:27 +00:00
|
|
|
pub fn setFreqHigh(self: *Self, byte: u8) void {
|
|
|
|
self.freq.raw = (@as(u16, byte) << 8) | (self.freq.raw & 0xFF);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const Tone = struct {
|
|
|
|
const Self = @This();
|
|
|
|
|
2022-10-21 08:12:30 +00:00
|
|
|
/// NR21
|
2022-10-21 08:12:27 +00:00
|
|
|
duty: io.Duty,
|
2022-10-21 08:12:30 +00:00
|
|
|
/// NR22
|
2022-10-21 08:12:27 +00:00
|
|
|
envelope: io.Envelope,
|
2022-10-21 08:12:30 +00:00
|
|
|
/// NR23, NR24
|
2022-10-21 08:12:27 +00:00
|
|
|
freq: io.Frequency,
|
|
|
|
|
|
|
|
fn init() Self {
|
|
|
|
return .{
|
|
|
|
.duty = .{ .raw = 0 },
|
|
|
|
.envelope = .{ .raw = 0 },
|
|
|
|
.freq = .{ .raw = 0 },
|
|
|
|
};
|
|
|
|
}
|
|
|
|
|
2022-10-21 08:12:30 +00:00
|
|
|
pub fn setFreqLow(self: *Self, byte: u8) void {
|
|
|
|
self.freq.raw = (self.freq.raw & 0xFF00) | byte;
|
|
|
|
}
|
|
|
|
|
2022-10-21 08:12:27 +00:00
|
|
|
pub fn setFreqHigh(self: *Self, byte: u8) void {
|
2022-10-21 08:12:30 +00:00
|
|
|
self.freq.raw = @as(u16, byte) << 8 | (self.freq.raw & 0xFF);
|
2022-10-21 08:12:27 +00:00
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
const Wave = struct {
|
|
|
|
const Self = @This();
|
|
|
|
|
|
|
|
/// Write-only
|
2022-10-21 08:12:30 +00:00
|
|
|
/// NR30
|
2022-10-21 08:12:27 +00:00
|
|
|
select: io.WaveSelect,
|
|
|
|
/// NR31
|
|
|
|
length: u8,
|
2022-10-21 08:12:30 +00:00
|
|
|
/// NR32
|
2022-10-21 08:12:27 +00:00
|
|
|
vol: io.WaveVolume,
|
2022-10-21 08:12:30 +00:00
|
|
|
/// NR33, NR34
|
2022-10-21 08:12:27 +00:00
|
|
|
freq: io.Frequency,
|
|
|
|
|
|
|
|
fn init() Self {
|
|
|
|
return .{
|
|
|
|
.select = .{ .raw = 0 },
|
|
|
|
.vol = .{ .raw = 0 },
|
|
|
|
.freq = .{ .raw = 0 },
|
|
|
|
.length = 0,
|
|
|
|
};
|
|
|
|
}
|
2022-10-21 08:12:30 +00:00
|
|
|
|
|
|
|
pub fn setFreqLow(self: *Self, byte: u8) void {
|
|
|
|
self.freq.raw = (self.freq.raw & 0xFF00) | byte;
|
|
|
|
}
|
|
|
|
|
|
|
|
pub fn setFreqHigh(self: *Self, byte: u8) void {
|
|
|
|
self.freq.raw = @as(u16, byte) << 8 | (self.freq.raw & 0xFF);
|
|
|
|
}
|
2022-10-21 08:12:27 +00:00
|
|
|
};
|
|
|
|
|
|
|
|
const Noise = struct {
|
|
|
|
const Self = @This();
|
|
|
|
|
|
|
|
/// Write-only
|
|
|
|
/// NR41
|
|
|
|
len: u6,
|
2022-10-21 08:12:30 +00:00
|
|
|
/// NR42
|
2022-10-21 08:12:27 +00:00
|
|
|
envelope: io.Envelope,
|
2022-10-21 08:12:30 +00:00
|
|
|
/// NR43
|
2022-10-21 08:12:27 +00:00
|
|
|
poly: io.PolyCounter,
|
2022-10-21 08:12:30 +00:00
|
|
|
/// NR44
|
2022-10-21 08:12:27 +00:00
|
|
|
cnt: io.NoiseControl,
|
|
|
|
|
|
|
|
fn init() Self {
|
|
|
|
return .{
|
|
|
|
.len = 0,
|
|
|
|
.envelope = .{ .raw = 0 },
|
|
|
|
.poly = .{ .raw = 0 },
|
|
|
|
.cnt = .{ .raw = 0 },
|
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|
|
|
|
const DmaSound = struct {
|
|
|
|
const Self = @This();
|
|
|
|
|
|
|
|
a: SoundFifo,
|
|
|
|
b: SoundFifo,
|
|
|
|
|
|
|
|
fn init() Self {
|
|
|
|
return .{
|
|
|
|
.a = SoundFifo.init(),
|
|
|
|
.b = SoundFifo.init(),
|
|
|
|
};
|
|
|
|
}
|
|
|
|
};
|