Implement more features for PPU and APU

This commit is contained in:
2026-01-19 17:28:12 -06:00
parent 13e4158b7b
commit ac745f60e9
2 changed files with 139 additions and 26 deletions

View File

@@ -4,7 +4,7 @@ bitfield::bitfield! {
pub struct DutyVol(u8);
impl Debug;
duty, set_duty: 7, 6;
r#loop, set_loop: 5;
length_counter_halt, set_length_counter_halt: 5;
const_vol, set_const_vol: 4;
volume, set_volume: 3, 0;
}
@@ -27,6 +27,62 @@ bitfield::bitfield! {
struct PulseChannel {
enabled: bool,
duty_vol: DutyVol,
sweep: Sweep,
timer_low: u8,
length_timer_high: LengthTimerHigh,
cur_time: u16,
cur_length: u8,
}
impl PulseChannel {
pub fn new() -> Self {
Self {
enabled: false,
duty_vol: DutyVol(0),
sweep: Sweep(0),
timer_low: 0,
length_timer_high: LengthTimerHigh(0),
cur_time: 0,
cur_length: 0,
}
}
fn use_envelope(&self) -> bool {
!self.duty_vol.const_vol()
}
fn volume(&self) -> u8 {
self.duty_vol.volume()
}
fn timer(&self) -> u16 {
self.timer_low as u16 | ((self.length_timer_high.timer_high() as u16) << 8)
}
fn length(&self) -> u8 {
self.length_timer_high.length()
}
pub fn reset(&mut self) {
// TODO
self.cur_time = self.timer();
}
pub fn clock(&mut self) {
if self.cur_time == 0 {
self.cur_time = self.timer();
} else {
self.cur_time -= 1;
}
// TODO
}
pub fn q_frame_clock(&mut self) {
if !self.duty_vol.length_counter_halt() && self.cur_length > 0 {
self.cur_length -= 1;
}
}
pub fn h_frame_clock(&mut self) {
if !self.duty_vol.length_counter_halt() && self.cur_length > 0 {
self.cur_length -= 1;
}
}
}
bitfield::bitfield! {
@@ -46,6 +102,12 @@ struct DeltaChannel {
enabled: bool,
}
impl DeltaChannel {
pub fn int(&self) -> bool {
false
}
}
struct FrameCounter {
count: usize,
mode_5_step: bool,
@@ -76,8 +138,8 @@ impl std::fmt::Debug for APU {
impl APU {
pub fn init() -> Self {
Self {
pulse_1: PulseChannel { enabled: false },
pulse_2: PulseChannel { enabled: false },
pulse_1: PulseChannel::new(),
pulse_2: PulseChannel::new(),
triangle: TriangleChannel { enabled: false },
noise: NoiseChannel { enabled: false },
dmc: DeltaChannel { enabled: false },
@@ -90,18 +152,38 @@ impl APU {
}
}
pub fn read_reg(&mut self, offset: u16) -> u8 {
// println!("APU read: {offset:02X}");
match offset {
0x15 => {
let val = (u8::from(self.dmc.int()) << 7)
| (u8::from(self.frame_counter.irq) << 6)
| (u8::from(self.dmc.enabled) << 4)
| (u8::from(self.noise.enabled) << 3)
| (u8::from(self.triangle.enabled) << 2)
| (u8::from(self.pulse_2.enabled) << 1)
| (u8::from(self.pulse_1.enabled) << 0);
self.frame_counter.irq = false;
val
}
_ => panic!("No register at {:X}", offset),
}
}
pub fn write_reg(&mut self, offset: u16, val: u8) {
// println!("APU write: {offset:02X} <= {val:02X}");
match offset {
0x15 => {
self.dmc.enabled = val & 0b0001_0000 != 0;
self.noise.enabled = val & 0b0000_1000 != 0;
self.triangle.enabled = val & 0b0000_0100 != 0;
self.pulse_2.enabled = val & 0b0000_0010 != 0;
self.pulse_1.enabled = val & 0b0000_0001 != 0;
0x00 => self.pulse_1.duty_vol.0 = val,
0x01 => self.pulse_1.sweep.0 = val,
0x02 => self.pulse_1.timer_low = val,
0x03 => {
self.pulse_1.length_timer_high.0 = val;
self.pulse_1.reset();
}
0x04 => self.pulse_2.duty_vol.0 = val,
0x05 => self.pulse_2.sweep.0 = val,
0x06 => self.pulse_2.timer_low = val,
0x07 => {
self.pulse_2.length_timer_high.0 = val;
self.pulse_2.reset();
}
0x10 => {
assert_eq!(val, 0x00);
@@ -110,27 +192,60 @@ impl APU {
0x11 => {
// TODO: load dmc counter with (val & 7F)
}
_ => (),
// _ => panic!("No register at {:X}", offset),
0x15 => {
self.dmc.enabled = val & 0b0001_0000 != 0;
self.noise.enabled = val & 0b0000_1000 != 0;
self.triangle.enabled = val & 0b0000_0100 != 0;
self.pulse_2.enabled = val & 0b0000_0010 != 0;
self.pulse_1.enabled = val & 0b0000_0001 != 0;
}
0x17 => {
self.frame_counter.mode_5_step = val & 0b1000_0000 == 0;
self.frame_counter.interrupt_enabled = val & 0b0100_0000 == 0;
if !self.frame_counter.interrupt_enabled {
self.frame_counter.irq = false;
}
}
// _ => (),
_ => panic!("No register at {:X}", offset),
}
}
fn q_frame_clock(&mut self) {
self.pulse_1.q_frame_clock();
self.pulse_2.q_frame_clock(); // TODO: clock all
}
fn h_frame_clock(&mut self) {
self.pulse_1.q_frame_clock();
self.pulse_1.h_frame_clock();
self.pulse_2.q_frame_clock();
self.pulse_2.h_frame_clock();
}
pub fn run_one_clock_cycle(&mut self, ppu_cycle: usize) -> bool {
if ppu_cycle % 6 == 1 {
// APU Frame Counter clock cycle
if !self.frame_counter.mode_5_step
&& self.frame_counter.interrupt_enabled
&& self.frame_counter.count == 14914
{
self.frame_counter.irq = true;
} else if !self.frame_counter.mode_5_step
&& self.frame_counter.interrupt_enabled
&& self.frame_counter.count == 14915
{
self.frame_counter.irq = true;
if self.frame_counter.mode_5_step {
todo!()
} else {
if self.frame_counter.count == 3728 {
self.q_frame_clock();
} else if self.frame_counter.count == 7456 {
self.h_frame_clock();
} else if self.frame_counter.count == 11185 {
self.q_frame_clock();
} else if self.frame_counter.count == 14914 {
self.frame_counter.irq = self.frame_counter.interrupt_enabled;
self.h_frame_clock();
} else if self.frame_counter.count == 14915 {
self.frame_counter.count = 0;
self.frame_counter.irq = self.frame_counter.interrupt_enabled;
}
}
self.frame_counter.count += 1;
self.pulse_1.clock();
self.pulse_2.clock();
}
false
}
@@ -144,8 +259,6 @@ impl APU {
self.frame_counter.irq
}
pub fn irq_waiting(&mut self) -> bool {
let res = self.frame_counter.irq;
self.frame_counter.irq = false;
res
self.frame_counter.irq
}
}

View File

@@ -467,7 +467,7 @@ impl Palette {
pub struct PPU {
// registers: PPURegisters,
frame_count: usize,
pub frame_count: usize,
nmi_enabled: bool,
// nmi_waiting: bool,
pub even: bool,