pwm registers enabled
This commit is contained in:
parent
6f5a1c648c
commit
645b5e365a
|
@ -3,6 +3,10 @@ use crate::{
|
||||||
util::{get_bit, set_or_clear_bit},
|
util::{get_bit, set_or_clear_bit},
|
||||||
};
|
};
|
||||||
|
|
||||||
|
use self::channels::{EnvelopeMode, NoiseChannel, PwmChannel, WaveChannel};
|
||||||
|
|
||||||
|
mod channels;
|
||||||
|
|
||||||
const MEM_START: usize = 0xFF10;
|
const MEM_START: usize = 0xFF10;
|
||||||
const MEM_SIZE: usize = 0xFF40 - MEM_START;
|
const MEM_SIZE: usize = 0xFF40 - MEM_START;
|
||||||
|
|
||||||
|
@ -10,36 +14,20 @@ const fn reg(a: Address) -> usize {
|
||||||
(a as usize) - MEM_START
|
(a as usize) - MEM_START
|
||||||
}
|
}
|
||||||
|
|
||||||
struct Channel {
|
|
||||||
enabled: bool,
|
|
||||||
pan_left: bool,
|
|
||||||
pan_right: bool,
|
|
||||||
}
|
|
||||||
|
|
||||||
impl Channel {
|
|
||||||
fn new(enabled: bool, pan_left: bool, pan_right: bool) -> Self {
|
|
||||||
Self {
|
|
||||||
enabled,
|
|
||||||
pan_left,
|
|
||||||
pan_right,
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
struct Channels {
|
struct Channels {
|
||||||
one: Channel,
|
one: PwmChannel,
|
||||||
two: Channel,
|
two: PwmChannel,
|
||||||
three: Channel,
|
three: WaveChannel,
|
||||||
four: Channel,
|
four: NoiseChannel,
|
||||||
}
|
}
|
||||||
|
|
||||||
impl Default for Channels {
|
impl Default for Channels {
|
||||||
fn default() -> Self {
|
fn default() -> Self {
|
||||||
Self {
|
Self {
|
||||||
one: Channel::new(true, true, true),
|
one: PwmChannel::new(true, true, true),
|
||||||
two: Channel::new(false, true, true),
|
two: PwmChannel::new(false, true, true),
|
||||||
three: Channel::new(false, true, false),
|
three: WaveChannel::new(false, true, false),
|
||||||
four: Channel::new(false, true, false),
|
four: NoiseChannel::new(false, true, false),
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -89,6 +77,13 @@ impl Apu {
|
||||||
|
|
||||||
fn make_register(&self, addr: Address) -> u8 {
|
fn make_register(&self, addr: Address) -> u8 {
|
||||||
match addr {
|
match addr {
|
||||||
|
0xFF10 => self.channels.one.get_sweep_register(),
|
||||||
|
0xFF11 => self.channels.one.get_length_timer_and_duty_cycle(),
|
||||||
|
0xFF12 => self.channels.one.get_volume_and_envelope(),
|
||||||
|
0xFF14 => self.channels.one.get_control(),
|
||||||
|
0xFF16 => self.channels.two.get_length_timer_and_duty_cycle(),
|
||||||
|
0xFF17 => self.channels.two.get_volume_and_envelope(),
|
||||||
|
0xFF19 => self.channels.two.get_control(),
|
||||||
0xFF24 => {
|
0xFF24 => {
|
||||||
// NR50 - Master volume + VIN panning
|
// NR50 - Master volume + VIN panning
|
||||||
let mut v = ((self.vol_left << 4) & 0b1110000) | (self.vol_right & 0b111);
|
let mut v = ((self.vol_left << 4) & 0b1110000) | (self.vol_right & 0b111);
|
||||||
|
@ -129,7 +124,24 @@ impl Apu {
|
||||||
|
|
||||||
pub fn mmio_write(&mut self, addr: Address, data: u8) {
|
pub fn mmio_write(&mut self, addr: Address, data: u8) {
|
||||||
match addr {
|
match addr {
|
||||||
0xFF10 => self.masked_io(reg(addr), data, 0b01111111),
|
0xFF10 => {
|
||||||
|
let pace = (data & 0b1110000) >> 4;
|
||||||
|
let mode = if get_bit(data, 3) {
|
||||||
|
EnvelopeMode::Decrease
|
||||||
|
} else {
|
||||||
|
EnvelopeMode::Increase
|
||||||
|
};
|
||||||
|
let slope = data & 0b111;
|
||||||
|
self.channels.one.update_sweep(pace, mode, slope);
|
||||||
|
}
|
||||||
|
0xFF11 => self.channels.one.update_length_timer_and_duty_cycle(data),
|
||||||
|
0xFF12 => self.channels.one.update_volume_and_envelope(data),
|
||||||
|
0xFF13 => self.channels.one.update_wavelength_low(data),
|
||||||
|
0xFF14 => self.channels.one.update_wavelength_high_and_control(data),
|
||||||
|
0xFF16 => self.channels.two.update_length_timer_and_duty_cycle(data),
|
||||||
|
0xFF17 => self.channels.two.update_volume_and_envelope(data),
|
||||||
|
0xFF18 => self.channels.two.update_wavelength_low(data),
|
||||||
|
0xFF19 => self.channels.two.update_wavelength_high_and_control(data),
|
||||||
0xFF1A => self.masked_io(reg(addr), data, 0b10000000),
|
0xFF1A => self.masked_io(reg(addr), data, 0b10000000),
|
||||||
0xFF1C => self.masked_io(reg(addr), data, 0b1100000),
|
0xFF1C => self.masked_io(reg(addr), data, 0b1100000),
|
||||||
0xFF20 => self.masked_io(reg(addr), data, 0b111111),
|
0xFF20 => self.masked_io(reg(addr), data, 0b111111),
|
||||||
|
|
167
src/processor/memory/mmio/apu/channels.rs
Normal file
167
src/processor/memory/mmio/apu/channels.rs
Normal file
|
@ -0,0 +1,167 @@
|
||||||
|
use crate::util::{get_bit, set_or_clear_bit};
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
pub(super) enum EnvelopeMode {
|
||||||
|
Increase,
|
||||||
|
Decrease,
|
||||||
|
}
|
||||||
|
|
||||||
|
struct Sweep {
|
||||||
|
pace: u8,
|
||||||
|
mode: EnvelopeMode,
|
||||||
|
slope: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Sweep {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
pace: 0,
|
||||||
|
mode: EnvelopeMode::Increase,
|
||||||
|
slope: 0,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[derive(Clone, Copy, PartialEq)]
|
||||||
|
struct Envelope {
|
||||||
|
initial_volume: u8,
|
||||||
|
mode: EnvelopeMode,
|
||||||
|
rate: u8,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl Default for Envelope {
|
||||||
|
fn default() -> Self {
|
||||||
|
Self {
|
||||||
|
initial_volume: 0xF,
|
||||||
|
mode: EnvelopeMode::Decrease,
|
||||||
|
rate: 0x3,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct PwmChannel {
|
||||||
|
pub(super) enabled: bool,
|
||||||
|
pub(super) pan_left: bool,
|
||||||
|
pub(super) pan_right: bool,
|
||||||
|
sweep: Sweep,
|
||||||
|
duty_cycle: u8,
|
||||||
|
length_enable: bool,
|
||||||
|
length_timer: u8,
|
||||||
|
envelope: Envelope,
|
||||||
|
queued_envelope: Envelope,
|
||||||
|
wavelength: u16,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl PwmChannel {
|
||||||
|
pub(super) fn new(enabled: bool, pan_left: bool, pan_right: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
enabled,
|
||||||
|
pan_left,
|
||||||
|
pan_right,
|
||||||
|
sweep: Sweep::default(),
|
||||||
|
duty_cycle: 2,
|
||||||
|
length_enable: false,
|
||||||
|
length_timer: 63,
|
||||||
|
envelope: Envelope::default(),
|
||||||
|
queued_envelope: Envelope::default(),
|
||||||
|
wavelength: 0x700,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
fn trigger(&mut self) {
|
||||||
|
self.enabled = true;
|
||||||
|
self.envelope = self.queued_envelope;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn update_sweep(&mut self, pace: u8, mode: EnvelopeMode, slope: u8) {
|
||||||
|
self.sweep.pace = pace;
|
||||||
|
self.sweep.mode = mode;
|
||||||
|
self.sweep.slope = slope;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn get_sweep_register(&self) -> u8 {
|
||||||
|
set_or_clear_bit(
|
||||||
|
((self.sweep.pace & 0b1110000) << 4) | (self.sweep.slope & 0b111),
|
||||||
|
3,
|
||||||
|
self.sweep.mode == EnvelopeMode::Decrease,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn update_length_timer_and_duty_cycle(&mut self, data: u8) {
|
||||||
|
self.length_timer = (data & 0b11000000) >> 6;
|
||||||
|
self.duty_cycle = data & 0b11111;
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn get_length_timer_and_duty_cycle(&self) -> u8 {
|
||||||
|
((self.duty_cycle & 0b11) << 6) | 0b11111
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn update_volume_and_envelope(&mut self, data: u8) {
|
||||||
|
self.queued_envelope = Envelope {
|
||||||
|
initial_volume: (data & 0b11110000) >> 4,
|
||||||
|
mode: if (data & 0b1000) == 0b1000 {
|
||||||
|
EnvelopeMode::Increase
|
||||||
|
} else {
|
||||||
|
EnvelopeMode::Decrease
|
||||||
|
},
|
||||||
|
rate: data & 0b111,
|
||||||
|
};
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn get_volume_and_envelope(&self) -> u8 {
|
||||||
|
set_or_clear_bit(
|
||||||
|
(self.queued_envelope.rate & 0b111)
|
||||||
|
| ((self.queued_envelope.initial_volume & 0b1111) << 4),
|
||||||
|
3,
|
||||||
|
self.queued_envelope.mode == EnvelopeMode::Decrease,
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn update_wavelength_low(&mut self, data: u8) {
|
||||||
|
self.wavelength = (self.wavelength & 0xFF00) | (data as u16);
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn update_wavelength_high_and_control(&mut self, data: u8) {
|
||||||
|
self.length_enable = get_bit(data, 6);
|
||||||
|
self.wavelength = (self.wavelength & 0xFF) | (((data & 0b111) as u16) << 8);
|
||||||
|
if get_bit(data, 7) {
|
||||||
|
self.trigger();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) fn get_control(&self) -> u8 {
|
||||||
|
set_or_clear_bit(0, 6, self.length_enable)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct WaveChannel {
|
||||||
|
pub(super) enabled: bool,
|
||||||
|
pub(super) pan_left: bool,
|
||||||
|
pub(super) pan_right: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl WaveChannel {
|
||||||
|
pub(super) fn new(enabled: bool, pan_left: bool, pan_right: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
enabled,
|
||||||
|
pan_left,
|
||||||
|
pan_right,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
pub(super) struct NoiseChannel {
|
||||||
|
pub(super) enabled: bool,
|
||||||
|
pub(super) pan_left: bool,
|
||||||
|
pub(super) pan_right: bool,
|
||||||
|
}
|
||||||
|
|
||||||
|
impl NoiseChannel {
|
||||||
|
pub(super) fn new(enabled: bool, pan_left: bool, pan_right: bool) -> Self {
|
||||||
|
Self {
|
||||||
|
enabled,
|
||||||
|
pan_left,
|
||||||
|
pan_right,
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
Loading…
Reference in a new issue