nesfuzz/src/apu/dmc.rs

164 lines
7.1 KiB
Rust
Raw Normal View History

2020-08-09 17:10:11 +00:00
// number of CPU cycles between sample output level being adjusted
2021-11-22 07:20:04 +00:00
pub const SAMPLE_RATES: [u16; 16] = [
428, 380, 340, 320, 286, 254, 226, 214, 190, 160, 142, 128, 106, 84, 72, 54,
];
2020-08-08 16:25:31 +00:00
#[derive(serde::Serialize, serde::Deserialize, Clone)]
2019-12-17 04:06:00 +00:00
pub struct DMC {
2020-08-09 17:10:11 +00:00
pub sample: u16, // "output value" that goes to the mixer
2020-09-02 23:33:17 +00:00
pub enabled: bool,
2020-08-09 17:36:47 +00:00
irq_enabled: bool,
2019-12-17 04:06:00 +00:00
pub interrupt: bool,
2020-08-09 17:10:11 +00:00
loop_flag: bool,
2020-08-09 04:30:41 +00:00
pub cpu_stall: bool,
2020-08-09 17:10:11 +00:00
rate_index: usize,
cpu_cycles_left: u16,
2020-08-08 16:25:31 +00:00
// Memory reader
sample_byte: u8, // passed in every APU clock cycle, need to think of a better way to read CPU from APU
2020-08-09 04:30:41 +00:00
sample_buffer: Option<u8>, // buffer that the output unit draws into its shift register, wrapped in Option to denote 'emptiness'
2020-08-09 17:10:11 +00:00
pub sample_address: usize, // start of sample in memory
pub sample_length: usize, // number of bytes starting from sample_address that constitute the sample. each byte has 8 bits that can raise or lower the output level, at a speed determined by rate_index
2020-08-08 16:25:31 +00:00
pub current_address: usize, // address of the next byte of the sample to play
pub bytes_remaining: usize, // bytes left in the sample
// Output unit
2020-08-09 04:30:41 +00:00
shift_register: u8,
bits_remaining: usize,
2019-12-17 04:06:00 +00:00
}
impl DMC {
2019-11-27 01:11:51 +00:00
pub fn new() -> Self {
2019-12-17 04:06:00 +00:00
DMC {
2019-12-05 02:57:05 +00:00
sample: 0,
2019-12-15 00:15:06 +00:00
enabled: false,
2020-08-09 17:36:47 +00:00
irq_enabled: false,
2019-12-15 00:15:06 +00:00
interrupt: false,
2020-08-08 16:25:31 +00:00
loop_flag: false,
2020-08-09 04:30:41 +00:00
cpu_stall: false,
2020-08-08 16:25:31 +00:00
rate_index: 0,
cpu_cycles_left: 0,
sample_byte: 0,
sample_buffer: None,
sample_address: 0,
sample_length: 0,
current_address: 0,
bytes_remaining: 0,
2020-08-09 04:30:41 +00:00
shift_register: 0,
bits_remaining: 0,
2019-11-27 01:11:51 +00:00
}
}
2019-12-05 02:57:05 +00:00
2020-08-08 16:25:31 +00:00
pub fn clock(&mut self, sample_byte: u8) {
2020-12-31 01:18:17 +00:00
if self.enabled {
self.clock_memory_reader(sample_byte);
self.clock_output_unit();
}
2020-08-08 16:25:31 +00:00
}
fn clock_memory_reader(&mut self, sample_byte: u8) {
// When a sample is (re)started, the current address is set to the sample address, and bytes remaining is set to the sample length.
if self.bytes_remaining == 0 && self.loop_flag {
self.current_address = self.sample_address;
self.bytes_remaining = self.sample_length;
}
// Any time the sample buffer is in an empty state and bytes remaining is not zero (including just after a write to $4015 that enables the channel,
// regardless of where that write occurs relative to the bit counter mentioned below), the following occur:
if self.sample_buffer.is_none() && self.bytes_remaining != 0 {
// The CPU is stalled for up to 4 CPU cycles to allow the longest possible write (the return address and write after an IRQ) to finish.
// If OAM DMA is in progress, it is paused for two cycles. The sample fetch always occurs on an even CPU cycle due to its alignment with the APU.
2020-08-09 04:30:41 +00:00
self.cpu_stall = true;
2020-08-08 16:25:31 +00:00
// The sample buffer is filled with the next sample byte read from the current address, subject to whatever mapping hardware is present.
self.sample_buffer = Some(sample_byte);
// The address is incremented; if it exceeds $FFFF, it is wrapped around to $8000.
if self.current_address == 0xFFFF {
self.current_address = 0x8000
} else {
self.current_address += 1;
}
2020-08-09 17:36:47 +00:00
// The bytes remaining counter is decremented; if it becomes zero and the loop flag is set, the sample is restarted (see above);
// otherwise, if the bytes remaining counter becomes zero and the IRQ enabled flag is set, the interrupt flag is set.
2020-08-08 16:25:31 +00:00
self.bytes_remaining -= 1;
2020-08-09 17:36:47 +00:00
} else if self.sample_buffer.is_none() && self.irq_enabled {
self.interrupt = true;
2020-08-09 04:30:41 +00:00
}
2019-12-26 03:51:54 +00:00
}
2020-08-09 04:30:41 +00:00
fn clock_output_unit(&mut self) {
// When the timer outputs a clock, the following actions occur in order:
// If the silence flag is clear, the output level changes based on bit 0 of the shift register.
// If the bit is 1, add 2; otherwise, subtract 2. But if adding or subtracting 2 would cause the output level to leave the 0-127 range,
// leave the output level unchanged. This means subtract 2 only if the current level is at least 2, or add 2 only if the current level is at most 125.
// The right shift register is clocked.
// As stated above, the bits-remaining counter is decremented. If it becomes zero, a new output cycle is started.
2020-12-31 01:18:17 +00:00
if self.cpu_cycles_left > 0 {
self.cpu_cycles_left -= 2;
}
2020-08-09 04:30:41 +00:00
if self.cpu_cycles_left == 0 {
self.cpu_cycles_left = SAMPLE_RATES[self.rate_index];
2020-09-02 23:33:17 +00:00
if self.enabled {
2020-08-09 04:30:41 +00:00
match self.shift_register & 1 {
2021-11-22 07:20:04 +00:00
0 => {
if self.sample >= 2 {
self.sample -= 2
}
}
1 => {
if self.sample <= 125 {
self.sample += 2
}
}
2020-08-09 17:10:11 +00:00
_ => panic!("uh oh! magical bits!"),
2020-08-09 04:30:41 +00:00
}
2020-09-02 23:33:17 +00:00
} else {
self.sample = 0;
2020-08-09 04:30:41 +00:00
}
self.shift_register >>= 1;
2020-12-31 01:18:17 +00:00
if self.bits_remaining > 0 {
self.bits_remaining -= 1;
}
2020-08-09 04:30:41 +00:00
// When an output cycle ends, a new cycle is started as follows:
// The bits-remaining counter is loaded with 8.
// If the sample buffer is empty, then the silence flag is set; otherwise, the silence flag is cleared and the sample buffer is emptied into the shift register.
if self.bits_remaining == 0 {
self.bits_remaining = 8;
match self.sample_buffer {
Some(s) => {
2020-09-02 23:33:17 +00:00
self.enabled = true;
2020-08-09 04:30:41 +00:00
self.shift_register = s;
self.sample_buffer = None;
2021-11-22 07:20:04 +00:00
}
2020-09-02 23:33:17 +00:00
None => self.enabled = false,
2020-08-09 04:30:41 +00:00
}
}
}
}
2019-12-22 00:02:32 +00:00
pub fn write_control(&mut self, value: u8) {
2020-08-08 16:25:31 +00:00
// $4010 IL--.RRRR Flags and Rate (write)
2020-08-09 17:36:47 +00:00
self.irq_enabled = value & 0b1000_0000 != 0;
if !self.irq_enabled {
self.interrupt = false;
}
2020-08-09 17:10:11 +00:00
self.loop_flag = value & 0b0100_0000 != 0;
self.rate_index = value as usize & 0b0000_1111;
2019-12-05 02:57:05 +00:00
}
2021-11-22 07:20:04 +00:00
2019-12-05 02:57:05 +00:00
pub fn direct_load(&mut self, value: u8) {
2020-08-09 17:10:11 +00:00
// $4011 -DDD.DDDD Direct load (write)
self.sample = value as u16 & 0b0111_1111;
2019-12-05 02:57:05 +00:00
}
2021-11-22 07:20:04 +00:00
2019-12-22 00:02:32 +00:00
pub fn write_sample_address(&mut self, value: u8) {
2020-08-09 17:10:11 +00:00
// $4012 AAAA.AAAA Sample address (write)
// bits 7-0 AAAA.AAAA Sample address = %11AAAAAA.AA000000 = $C000 + (A * 64)
self.sample_address = ((value as usize) << 6) + 0xC000;
2019-12-05 02:57:05 +00:00
}
2021-11-22 07:20:04 +00:00
2019-12-22 00:02:32 +00:00
pub fn write_sample_length(&mut self, value: u8) {
2020-08-09 17:10:11 +00:00
// $4013 LLLL.LLLL Sample length (write)
// bits 7-0 LLLL.LLLL Sample length = %LLLL.LLLL0001 = (L * 16) + 1 bytes
self.sample_length = ((value as usize) << 4) + 1;
2019-12-05 02:57:05 +00:00
}
2020-08-09 17:37:11 +00:00
}