fix(timers): apply machine accesses at bus completion

This commit is contained in:
0xrsydn 2026-10-06 21:44:24 +07:00
commit bfecc87e84
20 changed files with 765 additions and 137 deletions

View file

@ -0,0 +1,55 @@
//! Staged timer clocks for one machine instruction, IRQ entry, or DMA unit.
//! Other devices remain on the instruction-boundary scheduler.
use super::{Io, Timer, IF, TIMER_BASE};
pub(super) const TIMER_IRQ_MASK: u16 = 0x78;
#[derive(Debug, Clone, Copy, PartialEq, Eq)]
pub(crate) struct TimerStep {
pub(super) timers: [Timer; 4],
pub(super) pending: u16,
elapsed: u32,
}
impl TimerStep {
pub(super) fn new(io: &Io) -> Self {
Self {
timers: io.timers,
pending: io.pending & TIMER_IRQ_MASK,
elapsed: 0,
}
}
pub(crate) fn advance_to(&mut self, elapsed: u32) {
assert!(elapsed >= self.elapsed, "timer step time must be monotonic");
self.pending |= Io::advance_timer_bank(&mut self.timers, elapsed - self.elapsed);
self.elapsed = elapsed;
}
/// All lanes of a bus access observe the same staged time.
pub(crate) fn read8(&self, address: u32, committed: u8) -> Option<u8> {
match address {
TIMER_BASE..=0x0400_010f => {
Some(self.timers[((address - TIMER_BASE) / 4) as usize].read8(address))
}
IF => Some((committed & !(TIMER_IRQ_MASK as u8)) | self.pending as u8),
_ => None,
}
}
/// Return true only when the staged state owns the entire register byte.
pub(crate) fn write8(&mut self, address: u32, value: u8) -> bool {
match address {
TIMER_BASE..=0x0400_010f => {
let index = ((address - TIMER_BASE) / 4) as usize;
self.timers[index].write8(address, value, index);
true
}
IF => {
self.pending &= !u16::from(value);
false // The normal I/O path must also acknowledge non-timer sources.
}
_ => false,
}
}
}