feat(apu): add timer/sequencer/LFSR fields for channel output tracking
This commit is contained in:
@@ -34,6 +34,12 @@ impl Apu {
|
|||||||
frame_reset_delay: 0,
|
frame_reset_delay: 0,
|
||||||
pending_frame_mode_5step: false,
|
pending_frame_mode_5step: false,
|
||||||
pending_frame_irq_inhibit: 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(self.frame_reset_delay);
|
||||||
out.push(u8::from(self.pending_frame_mode_5step));
|
out.push(u8::from(self.pending_frame_mode_5step));
|
||||||
out.push(u8::from(self.pending_frame_irq_inhibit));
|
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) {
|
pub fn load_state_tail(&mut self, state: ApuStateTail) {
|
||||||
@@ -260,5 +273,11 @@ impl Apu {
|
|||||||
self.frame_reset_delay = state.frame_reset_delay;
|
self.frame_reset_delay = state.frame_reset_delay;
|
||||||
self.pending_frame_mode_5step = state.pending_frame_mode_5step;
|
self.pending_frame_mode_5step = state.pending_frame_mode_5step;
|
||||||
self.pending_frame_irq_inhibit = state.pending_frame_irq_inhibit;
|
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 };
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -48,6 +48,15 @@ pub struct Apu {
|
|||||||
pub(crate) frame_reset_delay: u8,
|
pub(crate) frame_reset_delay: u8,
|
||||||
pub(crate) pending_frame_mode_5step: bool,
|
pub(crate) pending_frame_mode_5step: bool,
|
||||||
pub(crate) pending_frame_irq_inhibit: 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 {
|
pub struct ApuStateTail {
|
||||||
@@ -81,6 +90,12 @@ pub struct ApuStateTail {
|
|||||||
pub frame_reset_delay: u8,
|
pub frame_reset_delay: u8,
|
||||||
pub pending_frame_mode_5step: bool,
|
pub pending_frame_mode_5step: bool,
|
||||||
pub pending_frame_irq_inhibit: 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 {
|
impl Default for Apu {
|
||||||
|
|||||||
@@ -112,6 +112,31 @@ impl NativeBus {
|
|||||||
let frame_reset_delay = sio::take_u8(data, &mut cursor, BUS_STATE_CTX)?;
|
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_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 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 {
|
self.apu.load_state_tail(ApuStateTail {
|
||||||
frame_cycle,
|
frame_cycle,
|
||||||
frame_mode_5step,
|
frame_mode_5step,
|
||||||
@@ -143,6 +168,12 @@ impl NativeBus {
|
|||||||
frame_reset_delay,
|
frame_reset_delay,
|
||||||
pending_frame_mode_5step,
|
pending_frame_mode_5step,
|
||||||
pending_frame_irq_inhibit,
|
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_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)?;
|
let mapper_state = sio::take_exact(data, &mut cursor, mapper_len, BUS_STATE_CTX)?;
|
||||||
|
|||||||
@@ -1,6 +1,6 @@
|
|||||||
pub const FRAME_WIDTH: usize = 256;
|
pub const FRAME_WIDTH: usize = 256;
|
||||||
pub const FRAME_HEIGHT: usize = 240;
|
pub const FRAME_HEIGHT: usize = 240;
|
||||||
pub const FRAME_RGBA_BYTES: usize = FRAME_WIDTH * FRAME_HEIGHT * 4;
|
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";
|
pub(crate) const SAVE_STATE_MAGIC: &[u8; 8] = b"NESRT001";
|
||||||
|
|||||||
Reference in New Issue
Block a user