Implement more features for PPU and APU
This commit is contained in:
163
src/apu.rs
163
src/apu.rs
@@ -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;
|
||||
self.frame_counter.count = 0;
|
||||
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
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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,
|
||||
|
||||
Reference in New Issue
Block a user