use super::types::{Apu, ApuStateTail}; impl Apu { pub fn new() -> Self { Self { io: [0; 0x20], frame_cycle: 0, frame_mode_5step: false, frame_irq_inhibit: false, frame_irq_pending: false, channel_enable_mask: 0, length_counters: [0; 4], dmc_bytes_remaining: 0, dmc_irq_enabled: false, dmc_irq_pending: false, dmc_cycle_counter: 0, dmc_current_addr: 0xC000, dmc_sample_buffer: 0, dmc_sample_buffer_valid: false, dmc_shift_reg: 0, dmc_bits_remaining: 8, dmc_silence: true, dmc_output_level: 0, dmc_dma_request: false, envelope_divider: [0; 3], envelope_decay: [0; 3], envelope_start_flags: 0, triangle_linear_counter: 0, triangle_linear_reload_flag: false, sweep_divider: [0; 2], sweep_reload_flags: 0, cpu_cycle_parity: false, frame_reset_pending: false, frame_reset_delay: 0, pending_frame_mode_5step: false, pending_frame_irq_inhibit: false, } } pub fn registers(&self) -> &[u8; 0x20] { &self.io } pub fn set_registers(&mut self, regs: [u8; 0x20]) { self.io = regs; } pub fn read(&mut self, addr: u16) -> u8 { match addr { 0x4000..=0x4013 => self.io[(addr as usize) - 0x4000], 0x4015 => self.read_status(), _ => 0, } } pub fn write(&mut self, addr: u16, value: u8) { match addr { 0x4000..=0x4013 => { self.io[(addr as usize) - 0x4000] = value; match addr { 0x4003 => { self.reload_length_counter(0, value >> 3); self.envelope_start_flags |= 1 << 0; } 0x4001 => { self.sweep_reload_flags |= 1 << 0; } 0x4007 => { self.reload_length_counter(1, value >> 3); self.envelope_start_flags |= 1 << 1; } 0x4005 => { self.sweep_reload_flags |= 1 << 1; } 0x400B => { self.reload_length_counter(2, value >> 3); self.triangle_linear_reload_flag = true; } 0x400F => { self.reload_length_counter(3, value >> 3); self.envelope_start_flags |= 1 << 2; } 0x4010 => { self.dmc_irq_enabled = (value & 0x80) != 0; if !self.dmc_irq_enabled { self.dmc_irq_pending = false; } } 0x4011 => { self.dmc_output_level = value & 0x7F; } 0x4012 => { self.dmc_current_addr = self.dmc_sample_start_addr(); } _ => {} } } 0x4014 => { self.io[0x14] = value; } 0x4015 => { self.io[(addr as usize) - 0x4000] = value; self.channel_enable_mask = value & 0x1F; self.dmc_irq_pending = false; for chan in 0..4usize { if (self.channel_enable_mask & (1 << chan)) == 0 { self.length_counters[chan] = 0; } } if (self.channel_enable_mask & 0x10) == 0 { self.dmc_bytes_remaining = 0; self.dmc_cycle_counter = 0; self.dmc_dma_request = false; } else if self.dmc_bytes_remaining == 0 { self.dmc_bytes_remaining = self.dmc_sample_length_bytes(); self.dmc_cycle_counter = self.dmc_byte_period(); self.dmc_current_addr = self.dmc_sample_start_addr(); if !self.dmc_sample_buffer_valid { self.dmc_dma_request = true; } } } 0x4017 => { self.io[(addr as usize) - 0x4000] = value; self.pending_frame_mode_5step = (value & 0x80) != 0; self.pending_frame_irq_inhibit = (value & 0x40) != 0; self.frame_reset_delay = if self.cpu_cycle_parity { 4 } else { 3 }; self.frame_reset_pending = true; if self.pending_frame_irq_inhibit { self.frame_irq_pending = false; } } _ => {} } } pub fn clock_cpu_cycle(&mut self) { let mut skip_frame_counter_clock = false; if self.frame_reset_pending { self.frame_reset_delay = self.frame_reset_delay.saturating_sub(1); if self.frame_reset_delay == 0 { self.frame_reset_pending = false; self.frame_mode_5step = self.pending_frame_mode_5step; self.frame_irq_inhibit = self.pending_frame_irq_inhibit; self.frame_cycle = 0; if self.frame_mode_5step { self.clock_quarter_frame(); self.clock_half_frame(); } skip_frame_counter_clock = true; } } if !skip_frame_counter_clock { self.clock_frame_counter(); } self.clock_dmc(); self.cpu_cycle_parity = !self.cpu_cycle_parity; } pub fn poll_irq(&self) -> bool { self.dmc_irq_pending || (self.frame_irq_pending && !self.frame_irq_inhibit) } pub fn take_dmc_dma_request(&mut self) -> Option { if self.dmc_dma_request { Some(self.dmc_current_addr) } else { None } } pub fn provide_dmc_dma_byte(&mut self, byte: u8) { if !self.dmc_dma_request { return; } self.dmc_dma_request = false; self.dmc_sample_buffer = byte; self.dmc_sample_buffer_valid = true; self.dmc_current_addr = if self.dmc_current_addr == 0xFFFF { 0x8000 } else { self.dmc_current_addr.wrapping_add(1) }; if self.dmc_bytes_remaining > 0 { self.dmc_bytes_remaining -= 1; } if self.dmc_bytes_remaining == 0 { if (self.io[0x10] & 0x40) != 0 { self.dmc_bytes_remaining = self.dmc_sample_length_bytes(); self.dmc_current_addr = self.dmc_sample_start_addr(); self.dmc_dma_request = true; } else if self.dmc_irq_enabled { self.dmc_irq_pending = true; } } } pub fn save_state_tail(&self, out: &mut Vec) { out.extend_from_slice(&self.frame_cycle.to_le_bytes()); out.push(u8::from(self.frame_mode_5step)); out.push(u8::from(self.frame_irq_inhibit)); out.push(u8::from(self.frame_irq_pending)); out.push(self.channel_enable_mask); out.extend_from_slice(&self.length_counters); out.extend_from_slice(&self.dmc_bytes_remaining.to_le_bytes()); out.push(u8::from(self.dmc_irq_enabled)); out.push(u8::from(self.dmc_irq_pending)); out.extend_from_slice(&self.dmc_cycle_counter.to_le_bytes()); out.extend_from_slice(&self.dmc_current_addr.to_le_bytes()); out.push(self.dmc_sample_buffer); out.push(u8::from(self.dmc_sample_buffer_valid)); out.push(self.dmc_shift_reg); out.push(self.dmc_bits_remaining); out.push(u8::from(self.dmc_silence)); out.push(self.dmc_output_level); out.push(u8::from(self.dmc_dma_request)); out.extend_from_slice(&self.envelope_divider); out.extend_from_slice(&self.envelope_decay); out.push(self.envelope_start_flags); out.push(self.triangle_linear_counter); out.push(u8::from(self.triangle_linear_reload_flag)); out.extend_from_slice(&self.sweep_divider); out.push(self.sweep_reload_flags); out.push(u8::from(self.cpu_cycle_parity)); out.push(u8::from(self.frame_reset_pending)); out.push(self.frame_reset_delay); out.push(u8::from(self.pending_frame_mode_5step)); out.push(u8::from(self.pending_frame_irq_inhibit)); } pub fn load_state_tail(&mut self, state: ApuStateTail) { self.frame_cycle = state.frame_cycle; self.frame_mode_5step = state.frame_mode_5step; self.frame_irq_inhibit = state.frame_irq_inhibit; self.frame_irq_pending = state.frame_irq_pending; self.channel_enable_mask = state.channel_enable_mask; self.length_counters = state.length_counters; self.dmc_bytes_remaining = state.dmc_bytes_remaining; self.dmc_irq_enabled = state.dmc_irq_enabled; self.dmc_irq_pending = state.dmc_irq_pending; self.dmc_cycle_counter = state.dmc_cycle_counter; self.dmc_current_addr = state.dmc_current_addr; self.dmc_sample_buffer = state.dmc_sample_buffer; self.dmc_sample_buffer_valid = state.dmc_sample_buffer_valid; self.dmc_shift_reg = state.dmc_shift_reg; self.dmc_bits_remaining = state.dmc_bits_remaining.max(1); self.dmc_silence = state.dmc_silence; self.dmc_output_level = state.dmc_output_level & 0x7F; self.dmc_dma_request = state.dmc_dma_request; self.envelope_divider = state.envelope_divider; self.envelope_decay = state.envelope_decay; self.envelope_start_flags = state.envelope_start_flags & 0x07; self.triangle_linear_counter = state.triangle_linear_counter & 0x7F; self.triangle_linear_reload_flag = state.triangle_linear_reload_flag; self.sweep_divider = state.sweep_divider; self.sweep_reload_flags = state.sweep_reload_flags & 0x03; self.cpu_cycle_parity = state.cpu_cycle_parity; self.frame_reset_pending = state.frame_reset_pending; self.frame_reset_delay = state.frame_reset_delay; self.pending_frame_mode_5step = state.pending_frame_mode_5step; self.pending_frame_irq_inhibit = state.pending_frame_irq_inhibit; } }