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

@ -155,8 +155,8 @@ impl Machine {
/// Sample IRQ before executing the next instruction. GBA devices do not
/// generate FIQ. IRQ entry consumes a separate step and does not clear IF.
/// CPU/DMA work uses nominal costs; HALT advances exactly to the next device event.
/// CPU writes take effect before this bulk device update. Within-instruction
/// register timing and IRQ synchronization delays are not modeled. Opcode
/// Timer data accesses use staged bus-completion phases. Other device writes
/// precede their bulk clock update. IRQ synchronization delays are not modeled. Opcode
/// prefetch follows ordered nominal CPU/DMA costs, not per-cycle arbitration.
/// CPU/DMA diagnostics leave CPU, devices, and the clock unchanged for this step.
/// Row-capture diagnostics are deferred to Memory::present_frame instead.
@ -178,15 +178,21 @@ impl Machine {
self.last_timing = timing;
return Ok(StepKind::HaltIdle);
}
let (kind, timing) = if let Some(timing) = self.cpu.take_irq_timed(&mut self.memory) {
(StepKind::IrqEntry, timing)
self.memory.begin_timer_step();
let result = if let Some(timing) = self.cpu.take_irq_timed(&mut self.memory) {
Ok((StepKind::IrqEntry, timing))
} else {
(
StepKind::Instruction,
self.cpu
.step_timed(&mut self.memory)
.map_err(MachineError::Cpu)?,
)
self.cpu
.step_timed(&mut self.memory)
.map(|timing| (StepKind::Instruction, timing))
.map_err(MachineError::Cpu)
};
let (kind, timing) = match result {
Ok(result) => result,
Err(error) => {
self.memory.discard_timer_step();
return Err(error);
}
};
self.memory.complete_cpu_step(timing.total());
self.last_timing = timing;