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

View File

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