feat(memory): sample Thumb IWRAM refill targets

After successful refill instructions, sample the Thumb IWRAM target and target+2 halfwords to establish bus history. Share ARM/Thumb refill classification with nominal timing. Respect restored instruction state, completed data accesses, region limits, and diagnostic rollback; keep instruction execution and nominal costs unchanged.

Add original regressions for ARM/Thumb BX, Thumb branches and BL, PC writes, stack/block PC loads, status-restoring returns, real SWI/IRQ returns, both target alignments, mirrors/wrap, captured sample ordering, load lanes, failure retention, BIOS isolation, and DMA invalidation. Document official ARM7TDMI refill evidence and the distinction from a full pipeline.

Darwin arm64 validation: 917 workspace tests and 848 core/demo tests per debug/release profile; 39 preparation tests; native ROM windows in both profiles; ten graphics/window smoke modes; fmt, clippy, and rustdoc. Public ARM, Thumb, memory, and BIOS reports remain identical to prior passing results and between profiles. Cold startup, ARM-target refill history, and DMA-to-CPU ordering remain incomplete; no hardware-conformance or commercial-game claim.
This commit is contained in:
0xrsydn 2026-10-06 02:04:07 +07:00
commit 8b0f1a5ead
12 changed files with 566 additions and 35 deletions

View file

@ -1,5 +1,5 @@
//! Transactional lane history for consecutive Thumb instructions in IWRAM.
//! Unknown startup/refill/DMA history is not replaced with current memory bytes.
//! Transactional lane history for Thumb IWRAM execution and target-pair refills.
//! Unknown startup/DMA history is not replaced with guessed memory bytes.
use std::cell::Cell;
use crate::timing::AccessWidth;
@ -87,6 +87,20 @@ impl IwramBus {
}
}
pub(super) fn refill(&mut self, pc: u32, fetched: [u16; 2]) {
debug_assert!(self.pending.get().is_none());
debug_assert_eq!(pc & 1, 0);
let mut lanes = Lanes::default();
for (index, half) in fetched.into_iter().enumerate() {
lanes.drive(
pc + index as u32 * 2,
AccessWidth::Halfword,
u32::from(half),
);
}
self.committed = Some(History { next_pc: pc, lanes });
}
pub(super) fn invalidate(&mut self) {
debug_assert!(self.pending.get().is_none());
self.committed = None;