use crate::core::cpu::CpuCore;
use crate::core::memory::{AddressBus, BusFaultKind};
use super::{MmuFault, MmuFaultKind, MmuResult};
fn buserr(address: u32) -> MmuFault {
MmuFault {
kind: MmuFaultKind::BusError,
address,
}
}
fn access_fault(address: u32) -> MmuFault {
MmuFault {
kind: MmuFaultKind::AccessLevelViolation,
address,
}
}
fn config_fault(address: u32) -> MmuFault {
MmuFault {
kind: MmuFaultKind::ConfigurationError,
address,
}
}
fn read_u32_phys<B: AddressBus>(bus: &mut B, addr: u32) -> MmuResult<u32> {
bus.try_read_long(addr).map_err(|f| {
if matches!(f.kind, BusFaultKind::BusError) {
buserr(f.address)
} else {
buserr(addr)
}
})
}
pub fn translate<B: AddressBus>(
cpu: &mut CpuCore,
bus: &mut B,
logical: u32,
write: bool,
supervisor: bool,
instruction: bool,
) -> MmuResult<u32> {
if !cpu.pmmu_enabled || !cpu.has_pmmu {
return Ok(logical);
}
if cpu.exception_processing {
return Ok(logical);
}
if let Some(phys) = super::ttr::check_transparent_translation(cpu, logical, write, instruction)
{
return Ok(phys);
}
let use_srp = (cpu.mmu_tc & 0x0200_0000) != 0 && supervisor;
let (root_aptr, root_limit) = if use_srp {
(cpu.mmu_srp_aptr, cpu.mmu_srp_limit)
} else {
(cpu.mmu_crp_aptr, cpu.mmu_crp_limit)
};
let is = (cpu.mmu_tc >> 16) & 0xF;
let abits = (cpu.mmu_tc >> 12) & 0xF;
let bbits = (cpu.mmu_tc >> 8) & 0xF;
let cbits = (cpu.mmu_tc >> 4) & 0xF;
let addr_in = logical;
#[inline]
fn top_index(addr: u32, left_shift: u32, bits: u32) -> u32 {
if bits == 0 {
return 0;
}
let rshift = 32u32.saturating_sub(bits);
addr.wrapping_shl(left_shift) >> rshift
}
#[inline]
fn low_bits(addr: u32, shift: u32) -> u32 {
if shift >= 32 {
0
} else {
addr.wrapping_shl(shift) >> shift
}
}
let mut tofs = top_index(addr_in, is, abits);
let mut tbl_entry: u32;
let tamode: u32;
match root_limit & 3 {
0 => return Err(config_fault(logical)),
1 => return Err(config_fault(logical)), 2 => {
tofs = tofs.wrapping_mul(4);
let e = read_u32_phys(bus, tofs.wrapping_add(root_aptr & 0xFFFF_FFFC))?;
tbl_entry = e;
tamode = e & 3;
}
3 => {
tofs = tofs.wrapping_mul(8);
let hi = read_u32_phys(bus, tofs.wrapping_add(root_aptr & 0xFFFF_FFFC))?;
let lo = read_u32_phys(
bus,
tofs.wrapping_add(root_aptr & 0xFFFF_FFFC).wrapping_add(4),
)?;
tamode = hi & 3;
tbl_entry = lo;
}
_ => unreachable!(),
}
tofs = top_index(addr_in, is + abits, bbits);
let mut tptr = tbl_entry & 0xFFFF_FFF0;
let tbmode: u32;
match tamode {
0 => return Err(access_fault(logical)),
1 => {
let base = tbl_entry & 0xFFFF_FF00;
let shift = is + abits;
let addr_out = low_bits(addr_in, shift).wrapping_add(base);
return Ok(addr_out);
}
2 => {
tofs = tofs.wrapping_mul(4);
tbl_entry = read_u32_phys(bus, tofs.wrapping_add(tptr))?;
tbmode = tbl_entry & 3;
}
3 => {
tofs = tofs.wrapping_mul(8);
let hi = read_u32_phys(bus, tofs.wrapping_add(tptr))?;
let lo = read_u32_phys(bus, tofs.wrapping_add(tptr).wrapping_add(4))?;
tbmode = hi & 3;
tbl_entry = lo;
}
_ => return Err(access_fault(logical)),
}
tofs = top_index(addr_in, is + abits + bbits, cbits);
tptr = tbl_entry & 0xFFFF_FFF0;
let tcmode: u32;
match tbmode {
0 => return Err(access_fault(logical)),
1 => {
let base = tbl_entry & 0xFFFF_FF00;
let shift = is + abits + bbits;
let addr_out = low_bits(addr_in, shift).wrapping_add(base);
return Ok(addr_out);
}
2 => {
tofs = tofs.wrapping_mul(4);
tbl_entry = read_u32_phys(bus, tofs.wrapping_add(tptr))?;
tcmode = tbl_entry & 3;
}
3 => {
tofs = tofs.wrapping_mul(8);
let hi = read_u32_phys(bus, tofs.wrapping_add(tptr))?;
let lo = read_u32_phys(bus, tofs.wrapping_add(tptr).wrapping_add(4))?;
tcmode = hi & 3;
tbl_entry = lo;
}
_ => return Err(access_fault(logical)),
}
match tcmode {
1 => {
let base = tbl_entry & 0xFFFF_FF00;
let shift = is + abits + bbits + cbits;
Ok(low_bits(addr_in, shift).wrapping_add(base))
}
_ => Err(access_fault(logical)),
}
}