feat(memory): retain IWRAM lane effects across DMA

Update existing Thumb IWRAM continuation lanes from successful DMA reads and writes before the resumed PC+4 snapshot. Keep channel halfword duplication separate from actual lane accesses. Preserve atomic errors, cold-entry diagnostics, IRQ invalidation, and nominal timing.

Add original DMA matrices and resume regressions; update prior invalidation expectations. Record pinned ares and jgenesis evidence and explicit limits for cross-state history, general open bus, and sub-instruction arbitration.

Validated on Darwin arm64: 939 workspace and 870 core/demo tests per debug/release profile, 39 Python tests, native ROM windows, ten window smoke modes, fmt, clippy, and rustdoc. Public ARM/Thumb/memory/BIOS reports remain identical to the previous results.
This commit is contained in:
0xrsydn 2026-10-06 02:34:58 +07:00
commit 663b25e5d1
15 changed files with 449 additions and 45 deletions

View file

@ -67,7 +67,8 @@ struct CpuAccess {
/// Protected BIOS reads separately retain the snapshot from BIOS execution.
/// Sequential Thumb IWRAM accesses retain addressed bus lanes transactionally.
/// Successful refills into Thumb IWRAM sample the target pair to establish history.
/// DMA channels retain transfer data separately; CPU bus handoff remains unmodeled.
/// DMA accesses drive existing Thumb IWRAM continuation lanes before the next fetch.
/// Channel data remains separate; general and sub-instruction CPU bus handoff is unmodeled.
/// A full fetch pipeline and ARM-target refill history are not modeled.
pub struct Memory {
external_ram: Vec<u8>,
@ -329,8 +330,13 @@ impl Memory {
AccessWidth::Byte => unreachable!("DMA is never byte-wide"),
}
.map_err(map_error)?;
// DMA-to-CPU bus ordering is not modeled. Never reuse pre-DMA IWRAM history.
self.invalidate_cpu_bus_history();
// DMA runs before the resumed CPU fetch. Only actual IWRAM accesses drive
// its local lanes, in read/write order. Channel halfword duplication is not
// a full IWRAM bus write. Failed units never reach these history commits.
self.iwram_bus
.dma_access(transfer.source, transfer.width, data_latch);
self.iwram_bus
.dma_access(transfer.destination, transfer.width, value);
// Keep this channel active during its cycles: another edge must not
// queue a second block while the current block is still transferring.
self.advance_cycles(timing.total());