268 lines
10 KiB
Rust
268 lines
10 KiB
Rust
use crate::device;
|
|
|
|
#[derive(Copy, Clone)]
|
|
pub struct TlbLut {
|
|
pub address: u64,
|
|
pub cached: bool,
|
|
}
|
|
|
|
#[derive(Copy, Clone)]
|
|
pub struct TlbEntry {
|
|
pub mask: u64,
|
|
pub vpn2: u64,
|
|
pub region: u8,
|
|
pub g: u8,
|
|
pub asid: u8,
|
|
pub pfn_even: u64,
|
|
pub c_even: u8,
|
|
pub d_even: u8,
|
|
pub v_even: u8,
|
|
pub pfn_odd: u64,
|
|
pub c_odd: u8,
|
|
pub d_odd: u8,
|
|
pub v_odd: u8,
|
|
|
|
pub start_even: u64,
|
|
pub end_even: u64,
|
|
pub phys_even: u64,
|
|
pub start_odd: u64,
|
|
pub end_odd: u64,
|
|
pub phys_odd: u64,
|
|
}
|
|
|
|
pub fn read(device: &mut device::Device, index: u64) {
|
|
if index > 31 {
|
|
return;
|
|
}
|
|
device.cpu.cop0.regs[device::cop0::COP0_PAGEMASK_REG as usize] =
|
|
(device.cpu.cop0.tlb_entries[index as usize].mask << 13) as u64;
|
|
|
|
device.cpu.cop0.regs[device::cop0::COP0_ENTRYHI_REG as usize] =
|
|
(device.cpu.cop0.tlb_entries[index as usize].region as u64) << 62
|
|
| device.cpu.cop0.tlb_entries[index as usize].vpn2 << 13
|
|
| (device.cpu.cop0.tlb_entries[index as usize].asid) as u64;
|
|
|
|
device.cpu.cop0.regs[device::cop0::COP0_ENTRYLO0_REG as usize] =
|
|
(device.cpu.cop0.tlb_entries[index as usize].pfn_even << 6)
|
|
| (device.cpu.cop0.tlb_entries[index as usize].c_even << 3) as u64
|
|
| (device.cpu.cop0.tlb_entries[index as usize].d_even << 2) as u64
|
|
| (device.cpu.cop0.tlb_entries[index as usize].v_even << 1) as u64
|
|
| (device.cpu.cop0.tlb_entries[index as usize].g) as u64;
|
|
|
|
device.cpu.cop0.regs[device::cop0::COP0_ENTRYLO1_REG as usize] =
|
|
(device.cpu.cop0.tlb_entries[index as usize].pfn_odd << 6)
|
|
| (device.cpu.cop0.tlb_entries[index as usize].c_odd << 3) as u64
|
|
| (device.cpu.cop0.tlb_entries[index as usize].d_odd << 2) as u64
|
|
| (device.cpu.cop0.tlb_entries[index as usize].v_odd << 1) as u64
|
|
| (device.cpu.cop0.tlb_entries[index as usize].g) as u64
|
|
}
|
|
|
|
pub fn write(device: &mut device::Device, index: u64) {
|
|
if index > 31 {
|
|
return;
|
|
}
|
|
tlb_unmap(device, index);
|
|
|
|
device.cpu.cop0.tlb_entries[index as usize].g = (device.cpu.cop0.regs
|
|
[device::cop0::COP0_ENTRYLO0_REG as usize]
|
|
& device.cpu.cop0.regs[device::cop0::COP0_ENTRYLO1_REG as usize]
|
|
& 1) as u8;
|
|
|
|
device.cpu.cop0.tlb_entries[index as usize].pfn_even =
|
|
(device.cpu.cop0.regs[device::cop0::COP0_ENTRYLO0_REG as usize] >> 6) & 0xFFFFF;
|
|
device.cpu.cop0.tlb_entries[index as usize].pfn_odd =
|
|
(device.cpu.cop0.regs[device::cop0::COP0_ENTRYLO1_REG as usize] >> 6) & 0xFFFFF;
|
|
device.cpu.cop0.tlb_entries[index as usize].c_even =
|
|
((device.cpu.cop0.regs[device::cop0::COP0_ENTRYLO0_REG as usize] >> 3) & 7) as u8;
|
|
device.cpu.cop0.tlb_entries[index as usize].c_odd =
|
|
((device.cpu.cop0.regs[device::cop0::COP0_ENTRYLO1_REG as usize] >> 3) & 7) as u8;
|
|
device.cpu.cop0.tlb_entries[index as usize].d_even =
|
|
((device.cpu.cop0.regs[device::cop0::COP0_ENTRYLO0_REG as usize] >> 2) & 1) as u8;
|
|
device.cpu.cop0.tlb_entries[index as usize].d_odd =
|
|
((device.cpu.cop0.regs[device::cop0::COP0_ENTRYLO1_REG as usize] >> 2) & 1) as u8;
|
|
device.cpu.cop0.tlb_entries[index as usize].v_even =
|
|
((device.cpu.cop0.regs[device::cop0::COP0_ENTRYLO0_REG as usize] >> 1) & 1) as u8;
|
|
device.cpu.cop0.tlb_entries[index as usize].v_odd =
|
|
((device.cpu.cop0.regs[device::cop0::COP0_ENTRYLO1_REG as usize] >> 1) & 1) as u8;
|
|
device.cpu.cop0.tlb_entries[index as usize].asid =
|
|
device.cpu.cop0.regs[device::cop0::COP0_ENTRYHI_REG as usize] as u8;
|
|
|
|
device.cpu.cop0.tlb_entries[index as usize].mask =
|
|
(device.cpu.cop0.regs[device::cop0::COP0_PAGEMASK_REG as usize] >> 13) & 0xFFF;
|
|
|
|
device.cpu.cop0.tlb_entries[index as usize].vpn2 =
|
|
(device.cpu.cop0.regs[device::cop0::COP0_ENTRYHI_REG as usize] >> 13) & 0x7FFFFFF;
|
|
|
|
device.cpu.cop0.tlb_entries[index as usize].region =
|
|
(device.cpu.cop0.regs[device::cop0::COP0_ENTRYHI_REG as usize] >> 62) as u8;
|
|
|
|
device.cpu.cop0.tlb_entries[index as usize].mask &= 0b101010101010;
|
|
device.cpu.cop0.tlb_entries[index as usize].mask |=
|
|
device.cpu.cop0.tlb_entries[index as usize].mask >> 1;
|
|
|
|
device.cpu.cop0.tlb_entries[index as usize].vpn2 &=
|
|
!device.cpu.cop0.tlb_entries[index as usize].mask;
|
|
|
|
device.cpu.cop0.tlb_entries[index as usize].start_even =
|
|
(device.cpu.cop0.tlb_entries[index as usize].vpn2 << 13) & 0xFFFFFFFF;
|
|
device.cpu.cop0.tlb_entries[index as usize].end_even =
|
|
device.cpu.cop0.tlb_entries[index as usize].start_even
|
|
+ (device.cpu.cop0.tlb_entries[index as usize].mask << 12)
|
|
+ 0xFFF;
|
|
device.cpu.cop0.tlb_entries[index as usize].phys_even =
|
|
device.cpu.cop0.tlb_entries[index as usize].pfn_even << 12;
|
|
|
|
device.cpu.cop0.tlb_entries[index as usize].start_odd =
|
|
device.cpu.cop0.tlb_entries[index as usize].end_even + 1;
|
|
device.cpu.cop0.tlb_entries[index as usize].end_odd =
|
|
device.cpu.cop0.tlb_entries[index as usize].start_odd
|
|
+ (device.cpu.cop0.tlb_entries[index as usize].mask << 12)
|
|
+ 0xFFF;
|
|
device.cpu.cop0.tlb_entries[index as usize].phys_odd =
|
|
device.cpu.cop0.tlb_entries[index as usize].pfn_odd << 12;
|
|
|
|
tlb_map(device, index);
|
|
}
|
|
|
|
pub fn probe(device: &mut device::Device) {
|
|
device.cpu.cop0.regs[device::cop0::COP0_INDEX_REG as usize] = 0x80000000; // set probe failure
|
|
for (pos, e) in device.cpu.cop0.tlb_entries.iter().enumerate() {
|
|
if e.vpn2 & !e.mask
|
|
!= ((device.cpu.cop0.regs[device::cop0::COP0_ENTRYHI_REG as usize] >> 13) & 0x7FFFFFF)
|
|
& !e.mask
|
|
{
|
|
continue;
|
|
}
|
|
if e.region != (device.cpu.cop0.regs[device::cop0::COP0_ENTRYHI_REG as usize] >> 62) as u8 {
|
|
continue;
|
|
}
|
|
if e.g == 0 {
|
|
if e.asid != device.cpu.cop0.regs[device::cop0::COP0_ENTRYHI_REG as usize] as u8 {
|
|
continue;
|
|
}
|
|
}
|
|
device.cpu.cop0.regs[device::cop0::COP0_INDEX_REG as usize] = pos as u64;
|
|
break;
|
|
}
|
|
}
|
|
|
|
pub fn tlb_unmap(device: &mut device::Device, index: u64) {
|
|
let e = &mut device.cpu.cop0.tlb_entries[index as usize];
|
|
|
|
if e.v_even != 0 {
|
|
let mut i = e.start_even;
|
|
while i < e.end_even {
|
|
device.cpu.cop0.tlb_lut_r[(i >> 12) as usize].address = 0;
|
|
device.cpu.cop0.tlb_lut_r[(i >> 12) as usize].cached = false;
|
|
i += 0x1000
|
|
}
|
|
if e.d_even != 0 {
|
|
let mut i = e.start_even;
|
|
while i < e.end_even {
|
|
device.cpu.cop0.tlb_lut_w[(i >> 12) as usize].address = 0;
|
|
device.cpu.cop0.tlb_lut_w[(i >> 12) as usize].cached = false;
|
|
i += 0x1000
|
|
}
|
|
}
|
|
}
|
|
|
|
if e.v_odd != 0 {
|
|
let mut i = e.start_odd;
|
|
while i < e.end_odd {
|
|
device.cpu.cop0.tlb_lut_r[(i >> 12) as usize].address = 0;
|
|
device.cpu.cop0.tlb_lut_r[(i >> 12) as usize].cached = false;
|
|
i += 0x1000
|
|
}
|
|
if e.d_odd != 0 {
|
|
let mut i = e.start_odd;
|
|
while i < e.end_odd {
|
|
device.cpu.cop0.tlb_lut_w[(i >> 12) as usize].address = 0;
|
|
device.cpu.cop0.tlb_lut_w[(i >> 12) as usize].cached = false;
|
|
i += 0x1000
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn tlb_map(device: &mut device::Device, index: u64) {
|
|
let e = &mut device.cpu.cop0.tlb_entries[index as usize];
|
|
|
|
if e.v_even != 0 {
|
|
if e.start_even < e.end_even
|
|
&& !(e.start_even >= 0x80000000 && e.end_even < 0xC0000000)
|
|
&& e.phys_even < 0x20000000
|
|
{
|
|
let mut i = e.start_even;
|
|
while i < e.end_even {
|
|
device.cpu.cop0.tlb_lut_r[(i >> 12) as usize].address =
|
|
0x80000000 | (e.phys_even + (i - e.start_even) + 0xFFF) as u64;
|
|
device.cpu.cop0.tlb_lut_r[(i >> 12) as usize].cached = e.c_even != 2;
|
|
i += 0x1000
|
|
}
|
|
if e.d_even != 0 {
|
|
let mut i = e.start_even;
|
|
while i < e.end_even {
|
|
device.cpu.cop0.tlb_lut_w[(i >> 12) as usize].address =
|
|
0x80000000 | (e.phys_even + (i - e.start_even) + 0xFFF) as u64;
|
|
device.cpu.cop0.tlb_lut_w[(i >> 12) as usize].cached = e.c_even != 2;
|
|
i += 0x1000
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
if e.v_odd != 0 {
|
|
if e.start_odd < e.end_odd
|
|
&& !(e.start_odd >= 0x80000000 && e.end_odd < 0xC0000000)
|
|
&& e.phys_odd < 0x20000000
|
|
{
|
|
let mut i = e.start_odd;
|
|
while i < e.end_odd {
|
|
device.cpu.cop0.tlb_lut_r[(i >> 12) as usize].address =
|
|
0x80000000 | (e.phys_odd + (i - e.start_odd) + 0xFFF) as u64;
|
|
device.cpu.cop0.tlb_lut_r[(i >> 12) as usize].cached = e.c_odd != 2;
|
|
i += 0x1000
|
|
}
|
|
if e.d_odd != 0 {
|
|
let mut i = e.start_odd;
|
|
while i < e.end_odd {
|
|
device.cpu.cop0.tlb_lut_w[(i >> 12) as usize].address =
|
|
0x80000000 | (e.phys_odd + (i - e.start_odd) + 0xFFF) as u64;
|
|
device.cpu.cop0.tlb_lut_w[(i >> 12) as usize].cached = e.c_odd != 2;
|
|
i += 0x1000
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
pub fn get_physical_address(
|
|
device: &mut device::Device,
|
|
address: u64,
|
|
access_type: device::memory::AccessType,
|
|
) -> (u64, bool, bool) {
|
|
if access_type == device::memory::AccessType::Write {
|
|
if device.cpu.cop0.tlb_lut_w[(address >> 12) as usize].address != 0 {
|
|
return (
|
|
(device.cpu.cop0.tlb_lut_w[(address >> 12) as usize].address & 0xFFFFF000)
|
|
| (address & 0xFFF),
|
|
device.cpu.cop0.tlb_lut_w[(address >> 12) as usize].cached,
|
|
false,
|
|
);
|
|
}
|
|
} else {
|
|
if device.cpu.cop0.tlb_lut_r[(address >> 12) as usize].address != 0 {
|
|
return (
|
|
(device.cpu.cop0.tlb_lut_r[(address >> 12) as usize].address & 0xFFFFF000)
|
|
| (address & 0xFFF),
|
|
device.cpu.cop0.tlb_lut_r[(address >> 12) as usize].cached,
|
|
false,
|
|
);
|
|
}
|
|
}
|
|
|
|
device::exceptions::tlb_miss_exception(device, address, access_type);
|
|
|
|
return (0, false, true);
|
|
}
|