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);
|
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
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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,
|
||||||
|
|||||||
Reference in New Issue
Block a user