apu
This commit is contained in:
parent
5855d6b251
commit
1c19b9d10a
|
@ -2,6 +2,10 @@ impl super::DMC {
|
|||
pub fn new() -> Self {
|
||||
super::DMC {
|
||||
sample: 0,
|
||||
enabled: false,
|
||||
bytes_remaining: 0,
|
||||
interrupt: false,
|
||||
length_counter: 0,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -30,6 +30,7 @@ pub struct Apu {
|
|||
frame_counter: u8,
|
||||
mode: u8,
|
||||
interrupt_inhibit: u8,
|
||||
frame_interrupt: bool,
|
||||
}
|
||||
|
||||
struct Square {
|
||||
|
@ -41,6 +42,7 @@ struct Square {
|
|||
length_counter: usize,
|
||||
envelope: usize,
|
||||
sweep: usize,
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
// $4008 Hlll.llll Triangle channel length counter halt and linear counter load (write)
|
||||
|
@ -50,6 +52,7 @@ struct Triangle {
|
|||
timer: usize,
|
||||
length_counter: usize, // (this bit is also the linear counter's control flag)
|
||||
linear_counter: usize,
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
// $400E M---.PPPP Mode and period (write)
|
||||
|
@ -61,10 +64,15 @@ struct Noise {
|
|||
envelope: usize,
|
||||
linear_feedback_sr: u16,
|
||||
mode: bool, // also called loop noise, bit 7 of $400E
|
||||
enabled: bool,
|
||||
}
|
||||
|
||||
struct DMC {
|
||||
sample: u16,
|
||||
enabled: bool,
|
||||
interrupt: bool,
|
||||
length_counter: usize,
|
||||
bytes_remaining: usize,
|
||||
}
|
||||
|
||||
struct Envelope {
|
||||
|
@ -94,14 +102,15 @@ impl Apu {
|
|||
frame_counter: 0,
|
||||
mode: 0,
|
||||
interrupt_inhibit: 0,
|
||||
frame_interrupt: false,
|
||||
}
|
||||
}
|
||||
|
||||
pub fn clock(&mut self) {
|
||||
pub fn step(&mut self) {
|
||||
|
||||
}
|
||||
|
||||
fn write_reg(&mut self, address: usize, value: u8) {
|
||||
pub fn write_reg(&mut self, address: usize, value: u8) {
|
||||
match address {
|
||||
0x4000 => self.square1.duty(value),
|
||||
0x4001 => self.square1.sweep(value),
|
||||
|
@ -158,6 +167,85 @@ impl Apu {
|
|||
}
|
||||
|
||||
fn control(&mut self, value: u8) {
|
||||
// Writing to this register clears the DMC interrupt flag.
|
||||
self.dmc.interrupt = false;
|
||||
// Writing a zero to any of the channel enable bits will silence that channel and immediately set its length counter to 0.
|
||||
if value & (1<<0) != 0 {
|
||||
self.square1.enabled = true;
|
||||
} else {
|
||||
self.square1.enabled = false;
|
||||
self.square1.length_counter = 0;
|
||||
}
|
||||
if value & (1<<1) != 0 {
|
||||
self.square2.enabled = true;
|
||||
|
||||
} else {
|
||||
self.square2.enabled = false;
|
||||
self.square2.length_counter = 0;
|
||||
|
||||
}
|
||||
if value & (1<<2) != 0 {
|
||||
self.triangle.enabled = true;
|
||||
|
||||
} else {
|
||||
self.triangle.enabled = false;
|
||||
self.triangle.length_counter = 0;
|
||||
|
||||
}
|
||||
if value & (1<<3) != 0 {
|
||||
self.noise.enabled = true;
|
||||
|
||||
} else {
|
||||
self.noise.enabled = false;
|
||||
self.noise.length_counter = 0;
|
||||
|
||||
}
|
||||
if value & (1<<4) != 0 {
|
||||
self.dmc.enabled = true;
|
||||
// If the DMC bit is set, the DMC sample will be restarted only if its bytes remaining is 0.
|
||||
// If there are bits remaining in the 1-byte sample buffer, these will finish playing before the next sample is fetched.
|
||||
if self.dmc.bytes_remaining != 0 {
|
||||
// TODO: how does dmc repeat?
|
||||
}
|
||||
} else {
|
||||
self.dmc.enabled = false;
|
||||
self.dmc.length_counter = 0;
|
||||
// If the DMC bit is clear, the DMC bytes remaining will be set to 0 and the DMC will silence when it empties.
|
||||
self.dmc.bytes_remaining = 0;
|
||||
}
|
||||
}
|
||||
|
||||
pub fn read_status(&mut self) -> u8 {
|
||||
// IF-D NT21: DMC interrupt (I), frame interrupt (F), DMC active (D), length counter > 0 (N/T/2/1)
|
||||
let mut val = 0;
|
||||
// N/T/2/1 will read as 1 if the corresponding length counter is greater than 0. For the triangle channel, the status of the linear counter is irrelevant.
|
||||
if self.square1.length_counter != 0 {
|
||||
val |= 1<<0;
|
||||
}
|
||||
if self.square2.length_counter != 0 {
|
||||
val |= 1<<1;
|
||||
}
|
||||
if self.triangle.length_counter != 0 {
|
||||
val |= 1<<2;
|
||||
}
|
||||
if self.noise.length_counter != 0 {
|
||||
val |= 1<<3;
|
||||
}
|
||||
// D will read as 1 if the DMC bytes remaining is more than 0.
|
||||
if self.dmc.bytes_remaining != 0 {
|
||||
val |= 1<<4;
|
||||
}
|
||||
if self.frame_interrupt {
|
||||
val |= 1<<6;
|
||||
}
|
||||
if self.dmc.interrupt {
|
||||
val |= 1<<7;
|
||||
}
|
||||
|
||||
|
||||
// Reading this register clears the frame interrupt flag (but not the DMC interrupt flag).
|
||||
self.frame_interrupt = false;
|
||||
// TODO: If an interrupt flag was set at the same moment of the read, it will read back as 1 but it will not be cleared.
|
||||
val
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,6 +6,8 @@ impl super::Noise {
|
|||
envelope: 0,
|
||||
linear_feedback_sr: 1, // On power-up, the shift register is loaded with the value 1.
|
||||
mode: false, // also called loop noise, bit 7 of $400E
|
||||
sample: 0,
|
||||
enabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -9,6 +9,7 @@ impl super::Square {
|
|||
envelope: 0,
|
||||
sweep: 0,
|
||||
sample: 0,
|
||||
enabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -18,7 +19,7 @@ impl super::Square {
|
|||
|
||||
pub fn duty(&mut self, value: u8) {
|
||||
self.duty_cycle = value >> 6;
|
||||
self.length_counter_halt =
|
||||
// self.length_counter_halt =
|
||||
}
|
||||
|
||||
pub fn sweep(&mut self, value: u8) {
|
||||
|
|
|
@ -4,6 +4,8 @@ impl super::Triangle {
|
|||
timer: 0,
|
||||
length_counter: 0,
|
||||
linear_counter: 0,
|
||||
sample: 0,
|
||||
enabled: false,
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -100,6 +100,7 @@ impl Cpu {
|
|||
mapper_func: cart.cpu_mapper_func,
|
||||
ppu: ppu,
|
||||
apu: apu,
|
||||
even: true,
|
||||
strobe: 0,
|
||||
button_states: 0,
|
||||
button_number: 0,
|
||||
|
@ -148,11 +149,7 @@ impl Cpu {
|
|||
|
||||
pub fn step(&mut self) -> u64 {
|
||||
// for apu
|
||||
if self.even {
|
||||
self.even = false;
|
||||
} else {
|
||||
self.even = true;
|
||||
}
|
||||
self.even = if self.even {false} else {true};
|
||||
|
||||
// skip cycles from OAM DMA if necessary
|
||||
if self.delay > 0 {
|
||||
|
|
Loading…
Reference in New Issue