feat(memory): retain aligned Thumb BIOS prefetch words
Snapshot the full word at (executing PC+4)&~3 for Thumb BIOS execution, using strict image reads and incoming instruction state. Keep ARM PC+8 open bus separate and unchanged. Commit snapshots only after successful steps; preserve diagnostic behavior for unavailable lookahead. Add original regression coverage for both code alignments, load widths and lanes, signed and unaligned values, processor modes, sequential fetches, ARM/Thumb transitions, SWI, RAM-backed POP exits, BIOS boundaries, failure retention, and nominal device progression. Record source evidence and explicit pipeline/hardware-validation limits. Darwin arm64 validation: 878 workspace tests and 809 core/demo tests in each of debug and release; 39 Python tests; native ROM window tests in both profiles; ten window/graphics smoke modes; fmt, clippy, and rustdoc. Public ARM, Thumb, memory, and BIOS reports match prior passing reports and match between profiles. No physical-hardware validation claimed.
This commit is contained in:
parent
efdc288db6
commit
cb4670c245
12 changed files with 427 additions and 25 deletions
|
|
@ -7,6 +7,9 @@ use crate::{
|
||||||
timing::StepTiming,
|
timing::StepTiming,
|
||||||
};
|
};
|
||||||
|
|
||||||
|
#[path = "thumb_bios_access_tests.rs"]
|
||||||
|
mod thumb;
|
||||||
|
|
||||||
const ENTRY: u32 = 0x100;
|
const ENTRY: u32 = 0x100;
|
||||||
const DATA: u32 = 0xf233_80a5;
|
const DATA: u32 = 0xf233_80a5;
|
||||||
const OTHER: u32 = 0x1234_5678;
|
const OTHER: u32 = 0x1234_5678;
|
||||||
|
|
@ -222,8 +225,8 @@ fn diagnostic_steps_keep_previous_bios_history_but_skipped_instructions_refresh_
|
||||||
}
|
}
|
||||||
|
|
||||||
#[test]
|
#[test]
|
||||||
fn thumb_bios_execution_and_missing_lookahead_invalidate_unsupported_history() {
|
fn missing_arm_or_thumb_lookahead_invalidates_unsupported_history() {
|
||||||
for (pc, thumb) in [(0x200, true), (0x3ff8, false), (0x3ffc, false)] {
|
for (pc, thumb) in [(0x3ffc, true), (0x3ff8, false), (0x3ffc, false)] {
|
||||||
let mut bios = image();
|
let mut bios = image();
|
||||||
word(&mut bios, pc, if thumb { 0x4720 } else { BX_R4 }); // Thumb BX r4 or ARM BX r4
|
word(&mut bios, pc, if thumb { 0x4720 } else { BX_R4 }); // Thumb BX r4 or ARM BX r4
|
||||||
let mut memory = bus(LDR, bios);
|
let mut memory = bus(LDR, bios);
|
||||||
|
|
|
||||||
287
crates/gba-core/src/cpu/thumb_bios_access_tests.rs
Normal file
287
crates/gba-core/src/cpu/thumb_bios_access_tests.rs
Normal file
|
|
@ -0,0 +1,287 @@
|
||||||
|
//! Thumb BIOS fetches drive a complete aligned word, unlike 16-bit ROM open bus.
|
||||||
|
use super::*;
|
||||||
|
|
||||||
|
const THUMB_ENTRY: u32 = 0x300;
|
||||||
|
const BX_R5: u16 = 0x4728;
|
||||||
|
|
||||||
|
fn half(bytes: &mut [u8], address: u32, value: u16) {
|
||||||
|
bytes[address as usize..address as usize + 2].copy_from_slice(&value.to_le_bytes());
|
||||||
|
}
|
||||||
|
|
||||||
|
fn thumb_image(pc: u32, instruction: u16) -> Vec<u8> {
|
||||||
|
let mut bios = image();
|
||||||
|
half(&mut bios, pc, instruction);
|
||||||
|
if pc < 0x3ffc {
|
||||||
|
word(&mut bios, (pc + 4) & !3, OTHER);
|
||||||
|
}
|
||||||
|
bios
|
||||||
|
}
|
||||||
|
|
||||||
|
fn enter_thumb(memory: &mut Memory, pc: u32, target_thumb: bool) -> Cpu {
|
||||||
|
let mut cpu = Cpu::new(ENTRY);
|
||||||
|
cpu.registers[4] = pc | 1;
|
||||||
|
cpu.registers[5] = ROM_START | u32::from(target_thumb);
|
||||||
|
cpu.step(memory).unwrap(); // Real ARM BX into Thumb BIOS, seed ARM history.
|
||||||
|
assert_eq!(cpu.pc(), pc);
|
||||||
|
assert_eq!(cpu.instruction_set(), InstructionSet::Thumb);
|
||||||
|
cpu
|
||||||
|
}
|
||||||
|
|
||||||
|
// Inspect via an independent CPU load without changing the BIOS CPU's state.
|
||||||
|
fn protected_word(memory: &mut Memory) -> u32 {
|
||||||
|
memory.write32(0x0200_0000, LDR).unwrap();
|
||||||
|
let mut probe = Cpu::new(0x0200_0000);
|
||||||
|
probe.step(memory).unwrap();
|
||||||
|
probe.registers[1]
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn thumb_exit_retains_both_word_lanes_for_each_code_alignment_and_load_width() {
|
||||||
|
for offset in [0, 2] {
|
||||||
|
let pc = THUMB_ENTRY + offset;
|
||||||
|
for (arm, thumb, expected) in [
|
||||||
|
(
|
||||||
|
LDR,
|
||||||
|
0x6801,
|
||||||
|
[0xf233_80a5, 0xa5f2_3380, 0x80a5_f233, 0x3380_a5f2],
|
||||||
|
),
|
||||||
|
(0xe5d0_1000, 0x7801, [0xa5, 0x80, 0x33, 0xf2]),
|
||||||
|
(
|
||||||
|
0xe1d0_10b0,
|
||||||
|
0x8801,
|
||||||
|
[0x80a5, 0xa500_0080, 0xf233, 0x3300_00f2],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
0xe1d0_10d0,
|
||||||
|
0x5681,
|
||||||
|
[0xffff_ffa5, 0xffff_ff80, 0x33, 0xffff_fff2],
|
||||||
|
),
|
||||||
|
(
|
||||||
|
0xe1d0_10f0,
|
||||||
|
0x5e81,
|
||||||
|
[0xffff_80a5, 0xffff_ff80, 0xffff_f233, 0xffff_fff2],
|
||||||
|
),
|
||||||
|
] {
|
||||||
|
for target_thumb in [false, true] {
|
||||||
|
for address in [0, 0x100, 0x3ffc] {
|
||||||
|
for (lane, value) in expected.into_iter().enumerate() {
|
||||||
|
let mut bios = thumb_image(pc, BX_R5);
|
||||||
|
word(&mut bios, ENTRY + 8, OTHER); // Distinct incoming ARM history.
|
||||||
|
word(&mut bios, THUMB_ENTRY + 4, DATA);
|
||||||
|
// Poison PC+8, and PC+6's next word for the unaligned case.
|
||||||
|
word(&mut bios, THUMB_ENTRY + 8, 0xdead_beef);
|
||||||
|
let mut memory = bus(if target_thumb { thumb } else { arm }, bios);
|
||||||
|
let mut cpu = enter_thumb(&mut memory, pc, target_thumb);
|
||||||
|
cpu.step(&mut memory).unwrap();
|
||||||
|
assert_eq!(cpu.pc(), ROM_START);
|
||||||
|
cpu.registers[0] = address + lane as u32;
|
||||||
|
cpu.flags.carry = true;
|
||||||
|
let mut after = cpu.clone();
|
||||||
|
after.registers[1] = value;
|
||||||
|
after.registers[15] += if target_thumb { 2 } else { 4 };
|
||||||
|
let timing = cpu.step_timed(&mut memory).unwrap();
|
||||||
|
assert_eq!(cpu, after, "pc={pc:#x} lane={lane} thumb={target_thumb}");
|
||||||
|
assert_eq!(timing.data_cycles, 1);
|
||||||
|
assert_eq!(timing.internal_cycles, 1);
|
||||||
|
assert_eq!(memory.cycles(), 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn sequential_thumb_steps_sample_the_aligned_fetch_word_without_extra_cycles() {
|
||||||
|
let mut bios = thumb_image(THUMB_ENTRY, 0x46c0); // MOV r8,r8
|
||||||
|
half(&mut bios, THUMB_ENTRY + 2, 0x46c0);
|
||||||
|
half(&mut bios, THUMB_ENTRY + 4, BX_R5);
|
||||||
|
// At PC+4 and PC+6: the exit instruction plus a distinct high halfword.
|
||||||
|
let first = (OTHER & 0xffff_0000) | u32::from(BX_R5);
|
||||||
|
word(&mut bios, THUMB_ENTRY + 8, DATA);
|
||||||
|
let mut memory = bus(LDR, bios);
|
||||||
|
let mut cpu = enter_thumb(&mut memory, THUMB_ENTRY, false);
|
||||||
|
assert_eq!(protected_word(&mut memory), DATA); // Incoming ARM prefetch.
|
||||||
|
for expected in [first, first, DATA] {
|
||||||
|
let timing = cpu.step_timed(&mut memory).unwrap();
|
||||||
|
assert_eq!(timing.data_cycles, 0);
|
||||||
|
assert_eq!(protected_word(&mut memory), expected);
|
||||||
|
assert_eq!(memory.cycles(), 0);
|
||||||
|
}
|
||||||
|
assert_eq!(cpu.pc(), ROM_START);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn thumb_to_arm_bios_transition_uses_incoming_state_then_refreshes_arm_history() {
|
||||||
|
for offset in [0, 2] {
|
||||||
|
let mut bios = thumb_image(THUMB_ENTRY + offset, BX_R5);
|
||||||
|
word(&mut bios, 0x208, DATA);
|
||||||
|
let mut memory = bus(LDR, bios);
|
||||||
|
let mut cpu = enter_thumb(&mut memory, THUMB_ENTRY + offset, false);
|
||||||
|
cpu.registers[5] = 0x200; // Existing ARM BX r4, now with DATA at PC+8.
|
||||||
|
cpu.registers[4] = ROM_START;
|
||||||
|
cpu.step(&mut memory).unwrap();
|
||||||
|
assert_eq!(cpu.instruction_set(), InstructionSet::Arm);
|
||||||
|
assert_eq!(protected_word(&mut memory), OTHER);
|
||||||
|
cpu.step(&mut memory).unwrap();
|
||||||
|
assert_eq!(cpu.pc(), ROM_START);
|
||||||
|
assert_eq!(protected_word(&mut memory), DATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn thumb_bios_protection_remains_pc_based_in_every_processor_mode() {
|
||||||
|
for mode in [
|
||||||
|
Mode::User,
|
||||||
|
Mode::System,
|
||||||
|
Mode::Fiq,
|
||||||
|
Mode::Irq,
|
||||||
|
Mode::Supervisor,
|
||||||
|
Mode::Abort,
|
||||||
|
Mode::Undefined,
|
||||||
|
] {
|
||||||
|
for offset in [0, 2] {
|
||||||
|
let pc = THUMB_ENTRY + offset;
|
||||||
|
let mut bios = thumb_image(pc, 0x6801); // LDR r1,[r0] inside BIOS.
|
||||||
|
half(&mut bios, pc + 2, BX_R5);
|
||||||
|
let mut memory = bus(LDR, bios);
|
||||||
|
let mut cpu = enter_thumb(&mut memory, pc, false);
|
||||||
|
cpu.apply_status(0xb000_00e0 | mode as u32, mode);
|
||||||
|
cpu.registers[0] = ENTRY + 8;
|
||||||
|
cpu.step(&mut memory).unwrap();
|
||||||
|
assert_eq!(cpu.registers[1], DATA); // Raw BIOS data, not retained OTHER.
|
||||||
|
assert_eq!(cpu.cpsr(), 0xb000_00e0 | mode as u32);
|
||||||
|
cpu.step(&mut memory).unwrap(); // BX to ARM ROM.
|
||||||
|
let expected = memory.read32((pc + 6) & !3).unwrap();
|
||||||
|
cpu.registers[0] = ENTRY + 8;
|
||||||
|
cpu.step(&mut memory).unwrap();
|
||||||
|
assert_eq!(cpu.registers[1], expected);
|
||||||
|
assert_eq!(cpu.cpsr(), 0xb000_00c0 | mode as u32);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn thumb_pop_pc_retains_prefetch_not_stack_data_and_stays_thumb() {
|
||||||
|
for offset in [0, 2] {
|
||||||
|
let mut memory = bus(0x6801, thumb_image(THUMB_ENTRY + offset, 0xbd00)); // POP {pc}
|
||||||
|
let mut cpu = enter_thumb(&mut memory, THUMB_ENTRY + offset, true);
|
||||||
|
cpu.registers[13] = 0x0300_0100;
|
||||||
|
memory.write32(0x0300_0100, ROM_START).unwrap();
|
||||||
|
let timing = cpu.step_timed(&mut memory).unwrap();
|
||||||
|
assert_eq!(timing.data_cycles, 1);
|
||||||
|
assert_eq!(cpu.registers[13], 0x0300_0104);
|
||||||
|
assert_eq!(cpu.instruction_set(), InstructionSet::Thumb);
|
||||||
|
assert_eq!(cpu.pc(), ROM_START);
|
||||||
|
cpu.step(&mut memory).unwrap();
|
||||||
|
assert_eq!(cpu.registers[1], OTHER);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn thumb_swi_enters_arm_and_the_next_bios_instruction_replaces_thumb_history() {
|
||||||
|
for offset in [0, 2] {
|
||||||
|
let mut bios = thumb_image(THUMB_ENTRY + offset, 0xdf00); // SWI #0
|
||||||
|
word(&mut bios, 8, 0xe12f_ff15); // BX r5
|
||||||
|
word(&mut bios, 16, DATA);
|
||||||
|
let mut memory = bus(LDR, bios);
|
||||||
|
let mut cpu = enter_thumb(&mut memory, THUMB_ENTRY + offset, false);
|
||||||
|
cpu.step(&mut memory).unwrap();
|
||||||
|
assert_eq!(cpu.pc(), 8);
|
||||||
|
assert_eq!(cpu.mode(), Mode::Supervisor);
|
||||||
|
assert_eq!(cpu.instruction_set(), InstructionSet::Arm);
|
||||||
|
assert_eq!(protected_word(&mut memory), OTHER);
|
||||||
|
cpu.step(&mut memory).unwrap();
|
||||||
|
assert_eq!(cpu.pc(), ROM_START);
|
||||||
|
cpu.step(&mut memory).unwrap();
|
||||||
|
assert_eq!(cpu.registers[1], DATA);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn last_complete_thumb_fetch_word_is_valid_at_both_alignments() {
|
||||||
|
for pc in [0x3ff8, 0x3ffa] {
|
||||||
|
let mut memory = bus(LDR, thumb_image(pc, BX_R5));
|
||||||
|
let mut cpu = enter_thumb(&mut memory, pc, false);
|
||||||
|
cpu.step(&mut memory).unwrap();
|
||||||
|
cpu.step(&mut memory).unwrap();
|
||||||
|
assert_eq!(cpu.registers[1], OTHER);
|
||||||
|
}
|
||||||
|
for pc in [0x3ffc, 0x3ffe] {
|
||||||
|
let mut memory = bus(LDR, thumb_image(pc, BX_R5));
|
||||||
|
let mut cpu = enter_thumb(&mut memory, pc, false);
|
||||||
|
cpu.step(&mut memory).unwrap(); // Missing lookahead does not fail BX itself.
|
||||||
|
assert_eq!(cpu.pc(), ROM_START);
|
||||||
|
let before = cpu.clone();
|
||||||
|
assert_eq!(
|
||||||
|
cpu.step(&mut memory),
|
||||||
|
Err(CpuError::Memory(MemoryError::Unmapped(0)))
|
||||||
|
);
|
||||||
|
assert_eq!(cpu, before);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn failed_thumb_steps_keep_previous_history_and_clear_access_context() {
|
||||||
|
for instruction in [0xde00, 0x6801] {
|
||||||
|
// Undefined instruction / failing LDR.
|
||||||
|
let mut memory = bus(LDR, thumb_image(THUMB_ENTRY, instruction));
|
||||||
|
let mut cpu = enter_thumb(&mut memory, THUMB_ENTRY, false);
|
||||||
|
cpu.registers[0] = 0x0e00_0000;
|
||||||
|
let before = cpu.clone();
|
||||||
|
assert!(cpu.step_timed(&mut memory).is_err());
|
||||||
|
assert_eq!(cpu, before);
|
||||||
|
assert_eq!(memory.cycles(), 0);
|
||||||
|
assert_eq!(memory.read32(THUMB_ENTRY + 4).unwrap(), OTHER); // Raw host read.
|
||||||
|
assert_eq!(protected_word(&mut memory), DATA); // Previous ARM history retained.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn outside_thumb_execution_keeps_history_but_does_not_enable_general_open_bus() {
|
||||||
|
let mut memory = bus(0x6801, thumb_image(THUMB_ENTRY, BX_R5));
|
||||||
|
let mut cpu = enter_thumb(&mut memory, THUMB_ENTRY, true);
|
||||||
|
cpu.step(&mut memory).unwrap();
|
||||||
|
assert_eq!(protected_word(&mut memory), OTHER);
|
||||||
|
cpu.registers[0] = 0x4000;
|
||||||
|
let before = cpu.clone();
|
||||||
|
assert_eq!(
|
||||||
|
cpu.step(&mut memory),
|
||||||
|
Err(CpuError::Memory(MemoryError::Unmapped(0x4000)))
|
||||||
|
);
|
||||||
|
assert_eq!(cpu, before);
|
||||||
|
assert_eq!(memory.read32(0x4000), Err(MemoryError::Unmapped(0x4000)));
|
||||||
|
assert_eq!(protected_word(&mut memory), OTHER);
|
||||||
|
cpu.registers[0] = 0;
|
||||||
|
cpu.step(&mut memory).unwrap();
|
||||||
|
assert_eq!(cpu.registers[1], OTHER);
|
||||||
|
}
|
||||||
|
|
||||||
|
#[test]
|
||||||
|
fn machine_steps_charge_thumb_exit_and_protected_load_without_snapshot_accesses() {
|
||||||
|
for offset in [0, 2] {
|
||||||
|
let mut memory = bus(LDR, thumb_image(THUMB_ENTRY + offset, BX_R5));
|
||||||
|
let cpu = enter_thumb(&mut memory, THUMB_ENTRY + offset, false);
|
||||||
|
memory.write32(TIMER_BASE, 0x0080_0000).unwrap();
|
||||||
|
let mut machine = Machine::new(cpu, memory);
|
||||||
|
assert_eq!(machine.step().unwrap(), StepKind::Instruction);
|
||||||
|
let cycles = machine.cycles();
|
||||||
|
assert_eq!(machine.last_timing().data_cycles, 0);
|
||||||
|
assert_eq!(machine.step().unwrap(), StepKind::Instruction);
|
||||||
|
assert_eq!(machine.cpu().registers()[1], OTHER);
|
||||||
|
assert_eq!(
|
||||||
|
machine.last_timing(),
|
||||||
|
StepTiming {
|
||||||
|
code_cycles: 8,
|
||||||
|
data_cycles: 1,
|
||||||
|
internal_cycles: 1,
|
||||||
|
idle_cycles: 0,
|
||||||
|
}
|
||||||
|
);
|
||||||
|
assert_eq!(machine.cycles(), cycles + 10);
|
||||||
|
assert_eq!(
|
||||||
|
u64::from(machine.memory().read16(TIMER_BASE).unwrap()),
|
||||||
|
machine.cycles()
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -57,12 +57,13 @@ fn vram_index(address: u32) -> usize {
|
||||||
struct CpuAccess {
|
struct CpuAccess {
|
||||||
pc: u32,
|
pc: u32,
|
||||||
arm_prefetch: Option<u32>,
|
arm_prefetch: Option<u32>,
|
||||||
|
bios_prefetch: Option<u32>,
|
||||||
}
|
}
|
||||||
|
|
||||||
/// Work/video/palette RAM, OAM, cartridge ROM, optional BIOS, and supported I/O.
|
/// Work/video/palette RAM, OAM, cartridge ROM, optional BIOS, and supported I/O.
|
||||||
/// ARM data reads from unused memory return a PC+8 snapshot when available.
|
/// ARM data reads from unused memory return a PC+8 snapshot when available.
|
||||||
/// CPU data reads of BIOS from outside BIOS use its retained ARM PC+8 snapshot.
|
/// Protected BIOS reads retain an ARM PC+8 or aligned Thumb PC+4 word snapshot.
|
||||||
/// Thumb BIOS prefetch, general Thumb open bus, and DMA bus latches are not modeled.
|
/// General Thumb open bus, a full fetch pipeline, and DMA bus latches are not modeled.
|
||||||
pub struct Memory {
|
pub struct Memory {
|
||||||
external_ram: Vec<u8>,
|
external_ram: Vec<u8>,
|
||||||
internal_ram: Vec<u8>,
|
internal_ram: Vec<u8>,
|
||||||
|
|
@ -333,17 +334,31 @@ impl Memory {
|
||||||
// Sample before execution, without data timing or open-bus recursion.
|
// Sample before execution, without data timing or open-bus recursion.
|
||||||
// Missing lookahead bytes must not fail an instruction that never uses them.
|
// Missing lookahead bytes must not fail an instruction that never uses them.
|
||||||
let arm_prefetch = match instruction_set {
|
let arm_prefetch = match instruction_set {
|
||||||
InstructionSet::Arm => self.snapshot_arm_prefetch(pc),
|
InstructionSet::Arm => self.snapshot_mapped_word(pc.wrapping_add(8)),
|
||||||
InstructionSet::Thumb => None,
|
InstructionSet::Thumb => None,
|
||||||
};
|
};
|
||||||
self.cpu_access = Some(CpuAccess { pc, arm_prefetch });
|
let bios_prefetch = if pc < BIOS_SIZE as u32 {
|
||||||
|
match instruction_set {
|
||||||
|
InstructionSet::Arm => arm_prefetch,
|
||||||
|
// BIOS drives a full 32-bit word for a Thumb halfword fetch.
|
||||||
|
// Preserve both lanes of the aligned PC+4 word, not a repeated halfword.
|
||||||
|
InstructionSet::Thumb => self.snapshot_mapped_word((pc + 4) & !3),
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
None
|
||||||
|
};
|
||||||
|
self.cpu_access = Some(CpuAccess {
|
||||||
|
pc,
|
||||||
|
arm_prefetch,
|
||||||
|
bios_prefetch,
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
fn snapshot_arm_prefetch(&self, pc: u32) -> Option<u32> {
|
fn snapshot_mapped_word(&self, address: u32) -> Option<u32> {
|
||||||
let mut bytes = [0; 4];
|
let mut bytes = [0; 4];
|
||||||
for (offset, byte) in bytes.iter_mut().enumerate() {
|
for (offset, byte) in bytes.iter_mut().enumerate() {
|
||||||
*byte = self
|
*byte = self
|
||||||
.read_mapped_byte(pc.wrapping_add(8 + offset as u32))
|
.read_mapped_byte(address.wrapping_add(offset as u32))
|
||||||
.ok()?;
|
.ok()?;
|
||||||
}
|
}
|
||||||
Some(u32::from_le_bytes(bytes))
|
Some(u32::from_le_bytes(bytes))
|
||||||
|
|
@ -353,8 +368,8 @@ impl Memory {
|
||||||
if let Some(access) = self.cpu_access.take() {
|
if let Some(access) = self.cpu_access.take() {
|
||||||
if succeeded && access.pc < BIOS_SIZE as u32 {
|
if succeeded && access.pc < BIOS_SIZE as u32 {
|
||||||
// Commit only successful instruction snapshots under our diagnostic policy.
|
// Commit only successful instruction snapshots under our diagnostic policy.
|
||||||
// Thumb BIOS execution or missing lookahead invalidates the known value.
|
// Missing lookahead invalidates history rather than exposing stale bytes.
|
||||||
self.bios_prefetch = access.arm_prefetch;
|
self.bios_prefetch = access.bios_prefetch;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -28,6 +28,7 @@ The repository is a Cargo workspace with three crates:
|
||||||
| `crates/gba-core/src/cpu/exception.rs` | Reset state, exception entry, and interrupt sampling |
|
| `crates/gba-core/src/cpu/exception.rs` | Reset state, exception entry, and interrupt sampling |
|
||||||
| `crates/gba-core/src/cpu/load_alias_tests.rs` | Original single-load alias regressions for indexing, banks, widths, alignment, I/O, timing, and diagnostics |
|
| `crates/gba-core/src/cpu/load_alias_tests.rs` | Original single-load alias regressions for indexing, banks, widths, alignment, I/O, timing, and diagnostics |
|
||||||
| `crates/gba-core/src/cpu/bios_access_tests.rs` | Original CPU BIOS protection regressions for retained history, modes, widths, exception returns, and diagnostics |
|
| `crates/gba-core/src/cpu/bios_access_tests.rs` | Original CPU BIOS protection regressions for retained history, modes, widths, exception returns, and diagnostics |
|
||||||
|
| `crates/gba-core/src/cpu/thumb_bios_access_tests.rs` | Thumb BIOS aligned-word snapshots, transitions, load lanes, boundaries, diagnostics, and nominal timing |
|
||||||
| `crates/gba-core/src/cpu/compare_psr_tests.rs` | Original Rd=15 test/compare status restoration, flags, bank, and timing regressions |
|
| `crates/gba-core/src/cpu/compare_psr_tests.rs` | Original Rd=15 test/compare status restoration, flags, bank, and timing regressions |
|
||||||
| `crates/gba-core/src/cpu/status_tests.rs`, `crates/gba-core/src/cpu/exception_tests.rs` | Status, banking, exception, and return tests |
|
| `crates/gba-core/src/cpu/status_tests.rs`, `crates/gba-core/src/cpu/exception_tests.rs` | Status, banking, exception, and return tests |
|
||||||
| `crates/gba-core/src/cpu/thumb_tests.rs` | Thumb instructions, state switching, and decoder checks |
|
| `crates/gba-core/src/cpu/thumb_tests.rs` | Thumb instructions, state switching, and decoder checks |
|
||||||
|
|
|
||||||
|
|
@ -97,13 +97,15 @@ While executing inside BIOS, ARM and Thumb code can read the mapped image direct
|
||||||
While executing outside BIOS, both instruction sets read byte lanes from retained BIOS prefetch data instead.
|
While executing outside BIOS, both instruction sets read byte lanes from retained BIOS prefetch data instead.
|
||||||
Normal word rotation, odd-halfword behavior, sign extension, and load timing still apply.
|
Normal word rotation, odd-halfword behavior, sign extension, and load timing still apply.
|
||||||
|
|
||||||
The current interpreter implements a bounded ARM prefetch model:
|
The current interpreter implements bounded ARM and Thumb BIOS prefetch snapshots:
|
||||||
|
|
||||||
- Before an ARM BIOS instruction executes, sample its mapped PC+8 word without charging another data access.
|
- Before an ARM BIOS instruction executes, sample its mapped PC+8 word without charging another data access.
|
||||||
|
- Before a Thumb BIOS instruction executes, sample the full mapped word at `(PC+4) & ~3`.
|
||||||
|
BIOS drives a 32-bit word even for a halfword fetch; both distinct lanes are retained.
|
||||||
- After successful execution, retain that snapshot for later protected reads. Skipped conditional instructions count as successful execution.
|
- After successful execution, retain that snapshot for later protected reads. Skipped conditional instructions count as successful execution.
|
||||||
- Instructions outside BIOS do not replace the retained value. Re-entering ARM BIOS code refreshes it.
|
- Instructions outside BIOS do not replace the retained value. Re-entering ARM or Thumb BIOS code refreshes it.
|
||||||
- Diagnostic failures preserve the previous value and clear the CPU access context.
|
- Diagnostic failures preserve the previous value and clear the CPU access context.
|
||||||
- Successful Thumb BIOS execution or unavailable PC+8 bytes invalidate the known value. Their fetch history is not yet modeled.
|
- Unavailable lookahead bytes invalidate the known value after a successful BIOS instruction.
|
||||||
- A protected read without known history returns `MemoryError::Unmapped` at the requested bus address, rather than exposing raw BIOS bytes.
|
- A protected read without known history returns `MemoryError::Unmapped` at the requested bus address, rather than exposing raw BIOS bytes.
|
||||||
|
|
||||||
The initial history is unknown. Direct cartridge startup with `Cpu::new` does not manufacture boot prefetch data.
|
The initial history is unknown. Direct cartridge startup with `Cpu::new` does not manufacture boot prefetch data.
|
||||||
|
|
@ -129,7 +131,10 @@ Instruction fetches remain strict mapped reads outside the CPU data-access conte
|
||||||
BIOS writes remain read-only diagnostics. DMA BIOS sources retain their existing unsupported-source diagnostic.
|
BIOS writes remain read-only diagnostics. DMA BIOS sources retain their existing unsupported-source diagnostic.
|
||||||
Missing BIOS images remain unmapped. The unused-memory ARM open-bus snapshot is separate from this retained BIOS value.
|
Missing BIOS images remain unmapped. The unused-memory ARM open-bus snapshot is separate from this retained BIOS value.
|
||||||
|
|
||||||
This models neither a persistent fetch pipeline nor exact bus history across Thumb BIOS code and DMA.
|
The Thumb rule follows documented BIOS bus lanes and a source comparison with NanoBoyAdvance.
|
||||||
|
See [Thumb BIOS prefetch research](../research/thumb-bios-prefetch.md) for evidence, emulator differences, and test scope.
|
||||||
|
This models neither a persistent fetch pipeline nor exact refill, BIOS data-access, or DMA bus history.
|
||||||
|
The boundary diagnostic policy and precise bus timing remain hardware-unverified.
|
||||||
Original regressions cover synthetic images, boot, exception returns, access widths, alignment, modes, and diagnostics.
|
Original regressions cover synthetic images, boot, exception returns, access widths, alignment, modes, and diagnostics.
|
||||||
The [public BIOS ROM](../public-bios-tests.md) passes its unchanged protected-read assertions in debug and release builds.
|
The [public BIOS ROM](../public-bios-tests.md) passes its unchanged protected-read assertions in debug and release builds.
|
||||||
This result covers these firmware boundaries, not full BIOS service compatibility.
|
This result covers these firmware boundaries, not full BIOS service compatibility.
|
||||||
|
|
|
||||||
|
|
@ -98,7 +98,7 @@ Host inspection, ROM-suite memory assertions, instruction fetches, and DMA do no
|
||||||
Limits remain explicit:
|
Limits remain explicit:
|
||||||
|
|
||||||
- Thumb open bus depends on instruction region, alignment, and prior bus data; it remains unsupported.
|
- Thumb open bus depends on instruction region, alignment, and prior bus data; it remains unsupported.
|
||||||
- [BIOS-protected reads](bios.md#cpu-bios-read-protection) use separate retained ARM BIOS snapshots.
|
- [BIOS-protected reads](bios.md#cpu-bios-read-protection) use separate retained ARM PC+8 or aligned Thumb PC+4 word snapshots.
|
||||||
- Unused/write-only I/O reads, DMA latches, and disabled-RAM reads are not modeled here.
|
- Unused/write-only I/O reads, DMA latches, and disabled-RAM reads are not modeled here.
|
||||||
- Missing BIOS, truncated ROM, unsupported I/O, and save-memory accesses retain their existing diagnostics.
|
- Missing BIOS, truncated ROM, unsupported I/O, and save-memory accesses retain their existing diagnostics.
|
||||||
- Unused-memory writes remain diagnostics rather than ignored hardware writes. Swaps cannot silently discard their write.
|
- Unused-memory writes remain diagnostics rather than ignored hardware writes. Swaps cannot silently discard their write.
|
||||||
|
|
|
||||||
|
|
@ -90,7 +90,8 @@ See the [root-cause research](research/bios-readback.md) and [CPU BIOS read-prot
|
||||||
|
|
||||||
The ARM, Thumb, and memory suites remain separate passing results.
|
The ARM, Thumb, and memory suites remain separate passing results.
|
||||||
This BIOS result covers boundary readback values, not complete BIOS services, exact fetch history, or commercial-game compatibility.
|
This BIOS result covers boundary readback values, not complete BIOS services, exact fetch history, or commercial-game compatibility.
|
||||||
Thumb BIOS execution, full pipeline behavior, and exact firmware timing remain unverified.
|
Separate original tests cover bounded [Thumb BIOS snapshots](research/thumb-bios-prefetch.md); this public ROM does not validate that path.
|
||||||
|
Full pipeline behavior, physical-hardware Thumb readback, and exact firmware timing remain unverified.
|
||||||
|
|
||||||
## References
|
## References
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -95,6 +95,15 @@ BIOS protected-read root-cause research:
|
||||||
|
|
||||||
See [the root-cause analysis and implementation decision](research/bios-readback.md).
|
See [the root-cause analysis and implementation decision](research/bios-readback.md).
|
||||||
|
|
||||||
|
Thumb BIOS fetch research:
|
||||||
|
|
||||||
|
- [NanoBoyAdvance bus at revision 55b5cf0a](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/bus/bus.cc), for full aligned BIOS latch updates on halfword reads.
|
||||||
|
- [NanoBoyAdvance ARM7TDMI loop at the same revision](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/arm/arm7tdmi.hh), for Thumb PC+4 fetch order.
|
||||||
|
- [mGBA CPU loop](https://github.com/mgba-emu/mgba/blob/master/src/arm/arm.c), compared with its region-exit retention path; not evidence of exact full-word Thumb BIOS readback.
|
||||||
|
- GBATEK's BIOS/OAM Thumb lane table above supports aligned-word sampling, but describes general unused-memory reads.
|
||||||
|
|
||||||
|
See [Thumb BIOS prefetch snapshots](research/thumb-bios-prefetch.md) for the inference, implementation limits, and original regressions.
|
||||||
|
|
||||||
Use [GBATEK](https://problemkaputt.de/gbatek.htm) and ARM7TDMI documentation for further hardware work.
|
Use [GBATEK](https://problemkaputt.de/gbatek.htm) and ARM7TDMI documentation for further hardware work.
|
||||||
Only use game ROMs that you may lawfully use. Do not commit game ROMs, BIOS files, or game assets.
|
Only use game ROMs that you may lawfully use. Do not commit game ROMs, BIOS files, or game assets.
|
||||||
The ignore file excludes `roms/`, common game ROM extensions, and save files.
|
The ignore file excludes `roms/`, common game ROM extensions, and save files.
|
||||||
|
|
|
||||||
|
|
@ -93,6 +93,7 @@ Workspace tests, preparation tests, lint checks, rustdoc, native ROM windows, an
|
||||||
- Keep synthetic caller-supplied images image-derived, including images with different exit words.
|
- Keep synthetic caller-supplied images image-derived, including images with different exit words.
|
||||||
- Rerun public ARM, Thumb, and memory suites and native Darwin regressions.
|
- Rerun public ARM, Thumb, and memory suites and native Darwin regressions.
|
||||||
|
|
||||||
This change does not implement a persistent instruction pipeline, Thumb BIOS fetch history, or exact firmware timing.
|
The firmware-layout fix did not implement a persistent instruction pipeline, Thumb BIOS fetch history, or exact firmware timing.
|
||||||
|
A subsequent [Thumb BIOS snapshot change](thumb-bios-prefetch.md) adds bounded aligned-word history, with separate tests and explicit limits.
|
||||||
It does not prove complete BIOS compatibility or commercial-game support.
|
It does not prove complete BIOS compatibility or commercial-game support.
|
||||||
The public BIOS ROM checks these protected-read values; it is not a comprehensive BIOS service suite.
|
The public BIOS ROM checks these protected-read values; it is not a comprehensive BIOS service suite.
|
||||||
|
|
|
||||||
75
docs/research/thumb-bios-prefetch.md
Normal file
75
docs/research/thumb-bios-prefetch.md
Normal file
|
|
@ -0,0 +1,75 @@
|
||||||
|
# Thumb BIOS prefetch snapshots
|
||||||
|
|
||||||
|
## Decision
|
||||||
|
|
||||||
|
Retain the full mapped word at `(executing PC + 4) & !3` after a successful Thumb BIOS instruction.
|
||||||
|
Keep ARM's existing PC+8 rule. Keep this BIOS history separate from general unused-memory open bus.
|
||||||
|
|
||||||
|
BIOS has a 32-bit bus. A Thumb instruction fetch selects a halfword, but the BIOS bus drives both word lanes.
|
||||||
|
This gives the following snapshot for an instruction at address P:
|
||||||
|
|
||||||
|
| Executing address | Low halfword | High halfword |
|
||||||
|
| --- | --- | --- |
|
||||||
|
| P aligned to four bytes | BIOS[P+4] | BIOS[P+6] |
|
||||||
|
| P aligned to two bytes only | BIOS[P+2] | BIOS[P+4] |
|
||||||
|
|
||||||
|
The two halfwords need not match. Do not repeat the fetched halfword, zero-extend it, or read an unaligned word at P+4.
|
||||||
|
Do not use ARM's PC+8 offset for Thumb execution.
|
||||||
|
|
||||||
|
## Evidence and confidence
|
||||||
|
|
||||||
|
[GBATEK](https://problemkaputt.de/gbatek-gba-unpredictable-things.htm) documents two related behaviors:
|
||||||
|
|
||||||
|
- Protected BIOS reads return the most recently fetched BIOS opcode.
|
||||||
|
- Thumb open bus while executing in BIOS exposes the two aligned word lanes shown above.
|
||||||
|
|
||||||
|
Its Thumb table describes unused-memory reads, not a dedicated Thumb protected-BIOS test.
|
||||||
|
The retained-word rule combines that bus-width evidence with the documented BIOS latch behavior.
|
||||||
|
We have not measured it on physical hardware.
|
||||||
|
|
||||||
|
[NanoBoyAdvance's bus implementation](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/bus/bus.cc) corroborates the full-word latch.
|
||||||
|
`ReadBIOS` aligns a permitted address down to four bytes and updates its BIOS latch with a full word.
|
||||||
|
It then selects the requested lanes. A halfword access does not reduce the retained latch to 16 bits.
|
||||||
|
Its [ARM7TDMI execution loop](https://github.com/nba-emu/NanoBoyAdvance/blob/55b5cf0ae3d929582ac5bfd486558173502b8354/src/nba/src/arm/arm7tdmi.hh) fetches the Thumb PC+4 halfword before dispatching the instruction.
|
||||||
|
This is a source comparison, not a differential emulator run. No implementation code was copied.
|
||||||
|
|
||||||
|
[mGBA's memory implementation](https://github.com/mgba-emu/mgba/blob/master/src/gba/memory.c) was also reviewed.
|
||||||
|
Its region-exit path saves `cpu->prefetch[1]`, while its [Thumb execution loop](https://github.com/mgba-emu/mgba/blob/master/src/arm/arm.c) fetches a halfword.
|
||||||
|
That path does not independently establish both retained word lanes. Its general Thumb BIOS open-bus path also notes an alignment approximation.
|
||||||
|
Do not treat agreement between all emulators, or exact hardware conformance, as established.
|
||||||
|
|
||||||
|
## Implementation scope
|
||||||
|
|
||||||
|
`Memory::begin_cpu_access` samples before execution, using the incoming instruction state and strict mapped bytes.
|
||||||
|
`end_cpu_access` commits BIOS history only after success. Failed instructions preserve previous history.
|
||||||
|
Thumb execution outside BIOS neither initializes nor replaces BIOS history.
|
||||||
|
Host reads remain raw inspection. CPU data loads outside BIOS select lanes from retained history as before.
|
||||||
|
Snapshots add no data accesses or nominal cycles.
|
||||||
|
|
||||||
|
The snapshot does not create a persistent instruction pipeline.
|
||||||
|
It does not model refill-time latches, BIOS data-access ordering within an instruction, DMA bus transitions, or general Thumb open bus.
|
||||||
|
At BIOS boundaries, unavailable lookahead invalidates history under the existing diagnostic policy.
|
||||||
|
A branch can still succeed; a later protected read reports unknown history rather than exposing stale bytes.
|
||||||
|
This boundary policy is conservative, not a hardware claim.
|
||||||
|
|
||||||
|
## Regression coverage
|
||||||
|
|
||||||
|
Original tests in `crates/gba-core/src/cpu/thumb_bios_access_tests.rs` cover:
|
||||||
|
|
||||||
|
- Both Thumb instruction alignments and both output instruction states.
|
||||||
|
- Full words, byte lanes, halfwords, sign extension, rotation, and all processor modes.
|
||||||
|
- Sequential history, ARM/Thumb transitions, SWI entry, and POP-to-PC exits with RAM stack data.
|
||||||
|
- Raw reads inside BIOS and protected reads outside it.
|
||||||
|
- The last complete fetch word, unavailable lookahead, and failure retention.
|
||||||
|
- Separate host reads, unchanged general Thumb open-bus diagnostics, nominal costs, and device progression.
|
||||||
|
|
||||||
|
## Validation result
|
||||||
|
|
||||||
|
The Thumb boundary-read regressions failed on the old history-invalidation path and pass with aligned-word snapshots.
|
||||||
|
Darwin arm64 workspace and core/demo tests pass in debug and release builds.
|
||||||
|
Preparation tests, formatting, lint checks, rustdoc, native ROM windows, and graphics smoke tests also pass.
|
||||||
|
The pinned public ARM, Thumb, memory, and BIOS reports match their previous passing reports exactly.
|
||||||
|
Debug and release reports also match each other for the same fixture paths.
|
||||||
|
|
||||||
|
The pinned public BIOS ROM uses ARM firmware paths. Its unchanged pass is regression evidence, not independent Thumb BIOS coverage.
|
||||||
|
Full pipeline timing and commercial-game compatibility remain outside this change.
|
||||||
|
|
@ -43,7 +43,7 @@ Update this file when a feature lands or a limit is removed.
|
||||||
- CPU/device stepping with nominal ARM7 instruction costs and memory wait states.
|
- CPU/device stepping with nominal ARM7 instruction costs and memory wait states.
|
||||||
- Game Pak wait-state control (`WAITCNT`) for all three ROM windows.
|
- Game Pak wait-state control (`WAITCNT`) for all three ROM windows.
|
||||||
- Sequential/non-sequential data accesses, branch refill costs, and variable multiply timing.
|
- Sequential/non-sequential data accesses, branch refill costs, and variable multiply timing.
|
||||||
- Optional caller-supplied 16 KiB BIOS mapping, with CPU read protection based on retained ARM BIOS PC+8 snapshots.
|
- Optional caller-supplied 16 KiB BIOS mapping, with CPU read protection based on retained ARM PC+8 or aligned Thumb PC+4 words.
|
||||||
- Optional original BIOS replacement: minimal boot, SoftReset, selective RegisterRamReset, IRQ dispatch, interrupt waits, memory copy/fill, integer/fixed-point arithmetic, affine matrices, bit unpacking, LZ77/run-length/Huffman decompression, and differential filters.
|
- Optional original BIOS replacement: minimal boot, SoftReset, selective RegisterRamReset, IRQ dispatch, interrupt waits, memory copy/fill, integer/fixed-point arithmetic, affine matrices, bit unpacking, LZ77/run-length/Huffman decompression, and differential filters.
|
||||||
- All 16 ARM data-processing operations, with immediate and shifted-register operands.
|
- All 16 ARM data-processing operations, with immediate and shifted-register operands.
|
||||||
- Logical operations: `AND`, `EOR`, `TST`, `TEQ`, `ORR`, `MOV`, `BIC`, and `MVN`.
|
- Logical operations: `AND`, `EOR`, `TST`, `TEQ`, `ORR`, `MOV`, `BIC`, and `MVN`.
|
||||||
|
|
@ -94,8 +94,10 @@ The reserved ARM condition code `0xF` and Thumb condition code `0xE` return erro
|
||||||
ARMv5+ extensions such as `BLX`, `BKPT`, and Thumb-2 instructions are not supported.
|
ARMv5+ extensions such as `BLX`, `BKPT`, and Thumb-2 instructions are not supported.
|
||||||
The ARMv4T high-register format requires at least one high register; low-to-low forms in that format return errors.
|
The ARMv4T high-register format requires at least one high register; low-to-low forms in that format return errors.
|
||||||
`LDRT`/`STRT` single-transfer variants and invalid register combinations return errors.
|
`LDRT`/`STRT` single-transfer variants and invalid register combinations return errors.
|
||||||
CPU BIOS reads are protected by the executing address using a bounded ARM prefetch snapshot.
|
CPU BIOS reads are protected by the executing address using bounded ARM and Thumb prefetch snapshots.
|
||||||
Thumb BIOS execution and missing lookahead invalidate that history; protected reads with unknown history remain diagnostics.
|
Thumb BIOS execution retains the full word at `(PC+4) & ~3`, not a repeated halfword.
|
||||||
|
Missing lookahead invalidates that history; protected reads with unknown history remain diagnostics.
|
||||||
|
Exact refill and BIOS data-access ordering remain unmodeled. The Thumb rule has source-based, not physical-hardware validation.
|
||||||
This is not a full fetch pipeline. The original BIOS reproduces documented boundary readback words, not all firmware side effects.
|
This is not a full fetch pipeline. The original BIOS reproduces documented boundary readback words, not all firmware side effects.
|
||||||
Multiply flags N/Z are implemented. Unspecified multiply C/V outputs are preserved deterministically, not hardware-verified.
|
Multiply flags N/Z are implemented. Unspecified multiply C/V outputs are preserved deterministically, not hardware-verified.
|
||||||
Instruction and memory errors are development diagnostics, not emulated CPU exceptions.
|
Instruction and memory errors are development diagnostics, not emulated CPU exceptions.
|
||||||
|
|
@ -129,7 +131,7 @@ The runner requires known completion addresses and has no debug-port protocol, s
|
||||||
|
|
||||||
ARM data reads from unused address ranges use a bounded [PC+8 open-bus snapshot](hardware/cpu.md#arm-unused-memory-data-reads).
|
ARM data reads from unused address ranges use a bounded [PC+8 open-bus snapshot](hardware/cpu.md#arm-unused-memory-data-reads).
|
||||||
This is not a fetch pipeline. General Thumb, DMA, and unused/write-only I/O open bus remain unmodeled.
|
This is not a fetch pipeline. General Thumb, DMA, and unused/write-only I/O open bus remain unmodeled.
|
||||||
BIOS protection uses separate retained ARM BIOS history as described in [BIOS behavior](hardware/bios.md#cpu-bios-read-protection).
|
BIOS protection uses separate retained BIOS fetch snapshots as described in [BIOS behavior](hardware/bios.md#cpu-bios-read-protection).
|
||||||
Host inspection and instruction fetches remain strict. Other unmapped reads return errors.
|
Host inspection and instruction fetches remain strict. Other unmapped reads return errors.
|
||||||
Reads beyond the supplied cartridge bytes also return errors.
|
Reads beyond the supplied cartridge bytes also return errors.
|
||||||
Direct memory-bus halfword and word accesses require alignment.
|
Direct memory-bus halfword and word accesses require alignment.
|
||||||
|
|
@ -138,9 +140,9 @@ Writes to cartridge addresses return errors instead of modeling cartridge hardwa
|
||||||
|
|
||||||
## Next steps
|
## Next steps
|
||||||
|
|
||||||
1. Extend BIOS prefetch history to Thumb execution and validate broader fetch-history behavior with independent tests.
|
1. Extend general open-bus support with documented Thumb region/alignment rules and explicit limits for history-dependent cases.
|
||||||
|
Validate BIOS snapshots with independent hardware tests before claiming exact refill or data-access history.
|
||||||
The desktop runner still uses only the original BIOS. Keep protected reads image-derived for all supplied images.
|
The desktop runner still uses only the original BIOS. Keep protected reads image-derived for all supplied images.
|
||||||
Extend general open-bus support with verified Thumb region/alignment rules and bus history.
|
|
||||||
2. Refine nominal timing with a fetch pipeline, Game Pak prefetch, per-access device updates, and verified timer/IRQ delays.
|
2. Refine nominal timing with a fetch pipeline, Game Pak prefetch, per-access device updates, and verified timer/IRQ delays.
|
||||||
3. Validate keypad retrigger behavior with hardware tests and add remaining device registers; extend BIOS reset coverage as sound/serial support becomes available.
|
3. Validate keypad retrigger behavior with hardware tests and add remaining device registers; extend BIOS reset coverage as sound/serial support becomes available.
|
||||||
Validate STOP entry/wake edges and add external wake sources and remaining DMA device modes.
|
Validate STOP entry/wake edges and add external wake sources and remaining DMA device modes.
|
||||||
|
|
|
||||||
|
|
@ -219,9 +219,12 @@ Status tests cover all modes, flag combinations, interrupt masks, field selectio
|
||||||
Exception tests cover vectors, ARM/Thumb return addresses, nested interrupts, mask priority, and return-state restoration.
|
Exception tests cover vectors, ARM/Thumb return addresses, nested interrupts, mask priority, and return-state restoration.
|
||||||
User-bank transfers and failed returns are checked for unintended changes to active and hidden registers.
|
User-bank transfers and failed returns are checked for unintended changes to active and hidden registers.
|
||||||
Integration tests execute original vector handlers and check optional BIOS mapping boundaries and write protection.
|
Integration tests execute original vector handlers and check optional BIOS mapping boundaries and write protection.
|
||||||
Original BIOS-access regressions check retained ARM PC+8 data for ARM/Thumb callers, all processor modes, load widths, lanes, and rotation.
|
Original BIOS-access regressions check retained ARM PC+8 and aligned Thumb PC+4 words for ARM/Thumb callers.
|
||||||
|
They cover all processor modes, load widths, lanes, rotation, both Thumb code alignments, and distinct halfwords.
|
||||||
They verify direct reads while executing inside BIOS, re-entry, SWI/IRQ returns, host-inspection isolation, and original-boot provenance.
|
They verify direct reads while executing inside BIOS, re-entry, SWI/IRQ returns, host-inspection isolation, and original-boot provenance.
|
||||||
Unknown history, Thumb BIOS execution, missing lookahead, and diagnostic-step retention have explicit checks.
|
Thumb tests cover sequential snapshots, instruction-state transitions, SWI entry, and RAM-backed POP exits.
|
||||||
|
Unknown history, last valid fetch words, missing lookahead, and diagnostic-step retention have explicit checks.
|
||||||
|
General Thumb unused-memory reads remain diagnostics; supporting Thumb BIOS snapshots does not enable general open bus.
|
||||||
Protected loads keep normal nominal costs and advance devices only through machine stepping.
|
Protected loads keep normal nominal costs and advance devices only through machine stepping.
|
||||||
Firmware readback regressions separately verify documented boot, SoftReset, returning SWI, IRQ callback, and IRQ return words.
|
Firmware readback regressions separately verify documented boot, SoftReset, returning SWI, IRQ callback, and IRQ return words.
|
||||||
They execute ARM/Thumb loads across widths and lanes, check caller state, and trace actual exits to their image data.
|
They execute ARM/Thumb loads across widths and lanes, check caller state, and trace actual exits to their image data.
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue