80 lines
2.6 KiB
Zig
80 lines
2.6 KiB
Zig
const std = @import("std");
|
|
const io = @import("../../bus/io.zig");
|
|
|
|
const Scheduler = @import("../../scheduler.zig").Scheduler;
|
|
const FrameSequencer = @import("../../apu.zig").FrameSequencer;
|
|
const Wave = @import("../Wave.zig");
|
|
|
|
const buf_len = 0x20;
|
|
pub const interval: u64 = (1 << 24) / (1 << 22);
|
|
const Self = @This();
|
|
|
|
buf: [buf_len]u8,
|
|
timer: u16,
|
|
offset: u12,
|
|
|
|
sched: *Scheduler,
|
|
|
|
pub fn read(self: *const Self, comptime T: type, nr30: io.WaveSelect, addr: u32) T {
|
|
// TODO: Handle reads when Channel 3 is disabled
|
|
const base = if (!nr30.bank.read()) @as(u32, 0x10) else 0; // Read from the Opposite Bank in Use
|
|
|
|
const i = base + addr - 0x0400_0090;
|
|
return std.mem.readIntSliceLittle(T, self.buf[i..][0..@sizeOf(T)]);
|
|
}
|
|
|
|
pub fn write(self: *Self, comptime T: type, nr30: io.WaveSelect, addr: u32, value: T) void {
|
|
// TODO: Handle writes when Channel 3 is disabled
|
|
const base = if (!nr30.bank.read()) @as(u32, 0x10) else 0; // Write to the Opposite Bank in Use
|
|
|
|
const i = base + addr - 0x0400_0090;
|
|
std.mem.writeIntSliceLittle(T, self.buf[i..][0..@sizeOf(T)], value);
|
|
}
|
|
|
|
pub fn init(sched: *Scheduler) Self {
|
|
return .{
|
|
.buf = [_]u8{0x00} ** buf_len,
|
|
.timer = 0,
|
|
.offset = 0,
|
|
.sched = sched,
|
|
};
|
|
}
|
|
|
|
/// Reload internal Wave Timer
|
|
pub fn reload(self: *Self, value: u11) void {
|
|
self.sched.removeScheduledEvent(.{ .ApuChannel = 2 });
|
|
|
|
self.timer = (@as(u16, 2048) - value) * 2;
|
|
self.sched.push(.{ .ApuChannel = 2 }, @as(u64, self.timer) * interval);
|
|
}
|
|
|
|
/// Scheduler Event Handler
|
|
pub fn onWaveTimerExpire(self: *Self, nrx34: io.Frequency, nr30: io.WaveSelect, late: u64) void {
|
|
if (nr30.dimension.read()) {
|
|
self.offset = (self.offset + 1) % 0x40; // 0x20 bytes (both banks), which contain 2 samples each
|
|
} else {
|
|
self.offset = (self.offset + 1) % 0x20; // 0x10 bytes, which contain 2 samples each
|
|
}
|
|
|
|
self.timer = (@as(u16, 2048) - nrx34.frequency.read()) * 2;
|
|
self.sched.push(.{ .ApuChannel = 2 }, @as(u64, self.timer) * interval -| late);
|
|
}
|
|
|
|
/// Generate Sample from Wave Synth
|
|
pub fn sample(self: *const Self, nr30: io.WaveSelect) u4 {
|
|
const base = if (nr30.bank.read()) @as(u32, 0x10) else 0;
|
|
|
|
const value = self.buf[base + self.offset / 2];
|
|
return if (self.offset & 1 == 0) @truncate(u4, value >> 4) else @truncate(u4, value);
|
|
}
|
|
|
|
/// TODO: Write comment
|
|
pub fn shift(_: *const Self, nr32: io.WaveVolume) u2 {
|
|
return switch (nr32.kind.read()) {
|
|
0b00 => 3, // Mute / Zero
|
|
0b01 => 0, // 100% Volume
|
|
0b10 => 1, // 50% Volume
|
|
0b11 => 2, // 25% Volume
|
|
};
|
|
}
|