diff --git a/src/native_core/apu/api.rs b/src/native_core/apu/api.rs index 1d79a4f..8cfd571 100644 --- a/src/native_core/apu/api.rs +++ b/src/native_core/apu/api.rs @@ -34,6 +34,12 @@ impl Apu { frame_reset_delay: 0, pending_frame_mode_5step: false, pending_frame_irq_inhibit: false, + pulse_timer_counter: [0; 2], + pulse_duty_step: [0; 2], + triangle_timer_counter: 0, + triangle_step: 0, + noise_timer_counter: 0, + noise_lfsr: 1, // LFSR initialized to 1 per NES hardware } } @@ -227,6 +233,13 @@ impl Apu { out.push(self.frame_reset_delay); out.push(u8::from(self.pending_frame_mode_5step)); out.push(u8::from(self.pending_frame_irq_inhibit)); + out.extend_from_slice(&self.pulse_timer_counter[0].to_le_bytes()); + out.extend_from_slice(&self.pulse_timer_counter[1].to_le_bytes()); + out.extend_from_slice(&self.pulse_duty_step); + out.extend_from_slice(&self.triangle_timer_counter.to_le_bytes()); + out.push(self.triangle_step); + out.extend_from_slice(&self.noise_timer_counter.to_le_bytes()); + out.extend_from_slice(&self.noise_lfsr.to_le_bytes()); } pub fn load_state_tail(&mut self, state: ApuStateTail) { @@ -260,5 +273,11 @@ impl Apu { 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; + self.pulse_timer_counter = state.pulse_timer_counter; + self.pulse_duty_step = [state.pulse_duty_step[0] & 0x07, state.pulse_duty_step[1] & 0x07]; + self.triangle_timer_counter = state.triangle_timer_counter; + self.triangle_step = state.triangle_step & 0x1F; + self.noise_timer_counter = state.noise_timer_counter; + self.noise_lfsr = if state.noise_lfsr == 0 { 1 } else { state.noise_lfsr }; } } diff --git a/src/native_core/apu/types.rs b/src/native_core/apu/types.rs index b3d9bf3..b04b47a 100644 --- a/src/native_core/apu/types.rs +++ b/src/native_core/apu/types.rs @@ -48,6 +48,15 @@ pub struct Apu { pub(crate) frame_reset_delay: u8, pub(crate) pending_frame_mode_5step: bool, pub(crate) pending_frame_irq_inhibit: bool, + // Pulse channel timers & duty sequencers + pub(crate) pulse_timer_counter: [u16; 2], + pub(crate) pulse_duty_step: [u8; 2], + // Triangle channel timer & sequencer + pub(crate) triangle_timer_counter: u16, + pub(crate) triangle_step: u8, + // Noise channel timer & LFSR + pub(crate) noise_timer_counter: u16, + pub(crate) noise_lfsr: u16, } pub struct ApuStateTail { @@ -81,6 +90,12 @@ pub struct ApuStateTail { pub frame_reset_delay: u8, pub pending_frame_mode_5step: bool, pub pending_frame_irq_inhibit: bool, + pub pulse_timer_counter: [u16; 2], + pub pulse_duty_step: [u8; 2], + pub triangle_timer_counter: u16, + pub triangle_step: u8, + pub noise_timer_counter: u16, + pub noise_lfsr: u16, } impl Default for Apu { diff --git a/src/native_core/bus/state.rs b/src/native_core/bus/state.rs index 0848644..f38a11e 100644 --- a/src/native_core/bus/state.rs +++ b/src/native_core/bus/state.rs @@ -112,6 +112,31 @@ impl NativeBus { let frame_reset_delay = sio::take_u8(data, &mut cursor, BUS_STATE_CTX)?; let pending_frame_mode_5step = sio::take_u8(data, &mut cursor, BUS_STATE_CTX)? != 0; let pending_frame_irq_inhibit = sio::take_u8(data, &mut cursor, BUS_STATE_CTX)? != 0; + let pulse_timer_counter = [ + u16::from_le_bytes([ + sio::take_u8(data, &mut cursor, BUS_STATE_CTX)?, + sio::take_u8(data, &mut cursor, BUS_STATE_CTX)?, + ]), + u16::from_le_bytes([ + sio::take_u8(data, &mut cursor, BUS_STATE_CTX)?, + sio::take_u8(data, &mut cursor, BUS_STATE_CTX)?, + ]), + ]; + let mut pulse_duty_step = [0u8; 2]; + pulse_duty_step.copy_from_slice(sio::take_exact(data, &mut cursor, 2, BUS_STATE_CTX)?); + let triangle_timer_counter = u16::from_le_bytes([ + sio::take_u8(data, &mut cursor, BUS_STATE_CTX)?, + sio::take_u8(data, &mut cursor, BUS_STATE_CTX)?, + ]); + let triangle_step = sio::take_u8(data, &mut cursor, BUS_STATE_CTX)?; + let noise_timer_counter = u16::from_le_bytes([ + sio::take_u8(data, &mut cursor, BUS_STATE_CTX)?, + sio::take_u8(data, &mut cursor, BUS_STATE_CTX)?, + ]); + let noise_lfsr = u16::from_le_bytes([ + sio::take_u8(data, &mut cursor, BUS_STATE_CTX)?, + sio::take_u8(data, &mut cursor, BUS_STATE_CTX)?, + ]); self.apu.load_state_tail(ApuStateTail { frame_cycle, frame_mode_5step, @@ -143,6 +168,12 @@ impl NativeBus { frame_reset_delay, pending_frame_mode_5step, pending_frame_irq_inhibit, + pulse_timer_counter, + pulse_duty_step, + triangle_timer_counter, + triangle_step, + noise_timer_counter, + noise_lfsr, }); let mapper_len = sio::take_u32(data, &mut cursor, BUS_STATE_CTX)? as usize; let mapper_state = sio::take_exact(data, &mut cursor, mapper_len, BUS_STATE_CTX)?; diff --git a/src/runtime/constants.rs b/src/runtime/constants.rs index 6f5f059..e1986bd 100644 --- a/src/runtime/constants.rs +++ b/src/runtime/constants.rs @@ -1,6 +1,6 @@ pub const FRAME_WIDTH: usize = 256; pub const FRAME_HEIGHT: usize = 240; pub const FRAME_RGBA_BYTES: usize = FRAME_WIDTH * FRAME_HEIGHT * 4; -pub const SAVE_STATE_VERSION: u32 = 1; +pub const SAVE_STATE_VERSION: u32 = 2; pub(crate) const SAVE_STATE_MAGIC: &[u8; 8] = b"NESRT001";