project64/Source/Project64-core/N64System/Mips/TLB.cpp

632 lines
20 KiB
C++

#include "stdafx.h"
#include "TLB.h"
#include <Project64-core/Debugger.h>
#include <Project64-core/N64System/Mips/MemoryVirtualMem.h>
#include <Project64-core/N64System/Mips/Register.h>
#include <Project64-core/N64System/Recompiler/Recompiler.h>
#include <Project64-core/N64System/SystemGlobals.h>
CTLB::CTLB(CMipsMemoryVM & MMU, CRegisters & Reg, CRecompiler *& Recomp) :
m_MMU(MMU),
m_Reg(Reg),
m_Recomp(Recomp),
m_PrivilegeMode(PrivilegeMode_Kernel),
m_AddressSize32bit(true)
{
WriteTrace(TraceTLB, TraceDebug, "Start");
memset(m_tlb, 0, sizeof(m_tlb));
memset(m_FastTlb, 0, sizeof(m_FastTlb));
Reset(true);
WriteTrace(TraceTLB, TraceDebug, "Done");
}
CTLB::~CTLB()
{
WriteTrace(TraceTLB, TraceDebug, "Start");
WriteTrace(TraceTLB, TraceDebug, "Done");
}
void CTLB::Reset(bool InvalidateTLB)
{
uint32_t count;
for (count = 0; count < 64; count++)
{
m_FastTlb[count].ValidEntry = false;
}
if (InvalidateTLB)
{
for (count = 0; count < 32; count++)
{
m_tlb[count].EntryDefined = false;
}
}
else
{
for (count = 0; count < 32; count++)
{
SetupTLB_Entry(count, false);
}
}
COP0StatusChanged();
}
bool CTLB::AddressDefined(uint64_t VAddr, bool & Dirty)
{
Dirty = true;
MemorySegment Segment = VAddrMemorySegment(VAddr);
if (Segment == MemorySegment_Mapped)
{
for (uint32_t i = 0; i < 64; i++)
{
if (!m_FastTlb[i].GLOBAL || !m_FastTlb[i].ValidEntry || VAddr < m_FastTlb[i].VSTART || VAddr > m_FastTlb[i].VEND + 1)
{
continue;
}
if (!m_FastTlb[i].VALID)
{
return true;
}
Dirty = m_FastTlb[i].DIRTY;
return true;
}
return false;
}
return false;
}
TLB_ENTRY & CTLB::TlbEntry(int32_t Entry)
{
return m_tlb[Entry];
}
void CTLB::Probe()
{
WriteTrace(TraceTLB, TraceDebug, "Start");
m_Reg.INDEX_REGISTER = 0x80000000;
for (uint32_t i = 0; i < 32; i++)
{
if (!m_tlb[i].EntryDefined)
{
continue;
}
const COP0EntryHi & TlbEntryHiValue = m_tlb[i].EntryHi;
uint64_t Mask = ~m_tlb[i].PageMask.Mask << 13;
uint64_t TlbValueMasked = TlbEntryHiValue.Value & Mask;
uint64_t EntryHiMasked = m_Reg.ENTRYHI_REGISTER.Value & Mask;
if (TlbValueMasked != EntryHiMasked ||
TlbEntryHiValue.R() != m_Reg.ENTRYHI_REGISTER.R() ||
(m_tlb[i].EntryLo0.GLOBAL == 0 || m_tlb[i].EntryLo1.GLOBAL == 0) && TlbEntryHiValue.ASID() != m_Reg.ENTRYHI_REGISTER.ASID())
{
continue;
}
m_Reg.INDEX_REGISTER = i;
uint32_t FastIndx = i << 1;
m_FastTlb[FastIndx].Probed = true;
m_FastTlb[FastIndx + 1].Probed = true;
break;
}
WriteTrace(TraceTLB, TraceDebug, "Done");
}
void CTLB::ReadEntry()
{
uint32_t Index = m_Reg.INDEX_REGISTER & 0x1F;
m_Reg.PAGE_MASK_REGISTER.Value = m_tlb[Index].PageMask.Value;
m_Reg.ENTRYHI_REGISTER.Value = (m_tlb[Index].EntryHi.Value & ~m_tlb[Index].PageMask.Value);
m_Reg.ENTRYLO0_REGISTER.Value = m_tlb[Index].EntryLo0.Value;
m_Reg.ENTRYLO1_REGISTER.Value = m_tlb[Index].EntryLo1.Value;
}
void CTLB::WriteEntry(uint32_t Index, bool Random)
{
WriteTrace(TraceTLB, TraceDebug, "%02d %d %llX %llX %llX %llX", Index, Random, m_Reg.PAGE_MASK_REGISTER, m_Reg.ENTRYHI_REGISTER, m_Reg.ENTRYLO0_REGISTER, m_Reg.ENTRYLO1_REGISTER);
// Check to see if entry is unmapping itself
if (m_tlb[Index].EntryDefined)
{
uint32_t FastIndx = Index << 1;
if (m_Reg.m_PROGRAM_COUNTER >= m_FastTlb[FastIndx].VSTART &&
m_Reg.m_PROGRAM_COUNTER < m_FastTlb[FastIndx].VEND &&
m_FastTlb[FastIndx].ValidEntry && m_FastTlb[FastIndx].VALID)
{
WriteTrace(TraceTLB, TraceDebug, "Ignored PC: %X VAddr Start: %llX VEND: %llX", m_Reg.m_PROGRAM_COUNTER, m_FastTlb[FastIndx].VSTART, m_FastTlb[FastIndx].VEND);
return;
}
if (m_Reg.m_PROGRAM_COUNTER >= m_FastTlb[FastIndx + 1].VSTART &&
m_Reg.m_PROGRAM_COUNTER < m_FastTlb[FastIndx + 1].VEND &&
m_FastTlb[FastIndx + 1].ValidEntry && m_FastTlb[FastIndx + 1].VALID)
{
WriteTrace(TraceTLB, TraceDebug, "Ignored PC: %X VAddr Start: %X VEND: %X", m_Reg.m_PROGRAM_COUNTER, m_FastTlb[FastIndx + 1].VSTART, m_FastTlb[FastIndx + 1].VEND);
return;
}
}
// Reset old addresses
if (m_tlb[Index].EntryDefined)
{
for (uint32_t FastIndx = Index << 1; FastIndx <= (Index << 1) + 1; FastIndx++)
{
if (!m_FastTlb[FastIndx].ValidEntry)
{
continue;
}
if (!m_FastTlb[FastIndx].VALID)
{
continue;
}
if (m_tlb[Index].PageMask.Value == m_Reg.PAGE_MASK_REGISTER.Value &&
m_tlb[Index].EntryHi.Value == m_Reg.ENTRYHI_REGISTER.Value)
{
if (FastIndx == (Index << 1) && m_tlb[Index].EntryLo0.Value == m_Reg.ENTRYLO0_REGISTER.Value)
{
continue;
}
if (FastIndx != (Index << 1) && m_tlb[Index].EntryLo1.Value == m_Reg.ENTRYLO1_REGISTER.Value)
{
continue;
}
}
TLB_Unmaped(m_FastTlb[FastIndx].VSTART, m_FastTlb[FastIndx].Length);
}
}
// Fill in m_tlb entry
bool Gloabl = m_Reg.ENTRYLO0_REGISTER.GLOBAL & m_Reg.ENTRYLO1_REGISTER.GLOBAL;
m_tlb[Index].PageMask.Value = m_Reg.PAGE_MASK_REGISTER.Value & 0x01554000;
m_tlb[Index].PageMask.Value |= m_tlb[Index].PageMask.Value >> 1;
m_tlb[Index].EntryHi = m_Reg.ENTRYHI_REGISTER;
m_tlb[Index].EntryLo0 = m_Reg.ENTRYLO0_REGISTER;
m_tlb[Index].EntryLo0.PFN = m_Reg.ENTRYLO0_REGISTER.PFN & 0xFFFFF;
m_tlb[Index].EntryLo0.GLOBAL = Gloabl;
m_tlb[Index].EntryLo1 = m_Reg.ENTRYLO1_REGISTER;
m_tlb[Index].EntryLo1.PFN = m_Reg.ENTRYLO1_REGISTER.PFN & 0xFFFFF;
m_tlb[Index].EntryLo1.GLOBAL = Gloabl;
m_tlb[Index].EntryDefined = true;
SetupTLB_Entry(Index, Random);
if (g_Debugger != nullptr)
{
g_Debugger->TLBChanged();
}
}
void CTLB::COP0StatusChanged(void)
{
m_PrivilegeMode = m_Reg.STATUS_REGISTER.PrivilegeMode;
switch (m_PrivilegeMode)
{
case PrivilegeMode_Kernel:
m_AddressSize32bit = m_Reg.STATUS_REGISTER.KernelExtendedAddressing == 0;
break;
case PrivilegeMode_Supervisor:
m_AddressSize32bit = m_Reg.STATUS_REGISTER.SupervisorExtendedAddressing == 0;
break;
case PrivilegeMode_User:
m_AddressSize32bit = m_Reg.STATUS_REGISTER.UserExtendedAddressing == 0;
break;
}
}
void CTLB::SetupTLB_Entry(uint32_t Index, bool Random)
{
// Fix up fast TLB entries
if (!m_tlb[Index].EntryDefined)
{
return;
}
uint32_t FastIndx = Index << 1;
if (m_FastTlb[FastIndx].VALID)
{
TLB_Unmaped(m_FastTlb[FastIndx].VSTART, m_FastTlb[FastIndx].Length);
}
m_FastTlb[FastIndx].Length = (uint32_t)((m_tlb[Index].PageMask.Mask << 12) + 0xFFF);
m_FastTlb[FastIndx].Region = (uint8_t)m_tlb[Index].EntryHi.R();
m_FastTlb[FastIndx].VSTART = (uint32_t)(m_tlb[Index].EntryHi.VPN2() << 13);
m_FastTlb[FastIndx].VEND = m_FastTlb[FastIndx].VSTART + m_FastTlb[FastIndx].Length;
m_FastTlb[FastIndx].PHYSSTART = (uint32_t)(m_tlb[Index].EntryLo0.PFN << 12);
m_FastTlb[FastIndx].PHYSEND = m_FastTlb[FastIndx].PHYSSTART + m_FastTlb[FastIndx].Length;
m_FastTlb[FastIndx].VALID = m_tlb[Index].EntryLo0.V != 0;
m_FastTlb[FastIndx].DIRTY = m_tlb[Index].EntryLo0.D != 0;
m_FastTlb[FastIndx].GLOBAL = m_tlb[Index].EntryLo0.GLOBAL & m_tlb[Index].EntryLo1.GLOBAL;
m_FastTlb[FastIndx].ValidEntry = false;
m_FastTlb[FastIndx].Random = Random;
m_FastTlb[FastIndx].Probed = false;
FastIndx = (Index << 1) + 1;
if (m_FastTlb[FastIndx].VALID)
{
TLB_Unmaped(m_FastTlb[FastIndx].VSTART, m_FastTlb[FastIndx].Length);
}
m_FastTlb[FastIndx].Length = (uint32_t)((m_tlb[Index].PageMask.Mask << 12) + 0xFFF);
m_FastTlb[FastIndx].Region = (uint8_t)m_tlb[Index].EntryHi.R();
m_FastTlb[FastIndx].VSTART = (uint32_t)(m_tlb[Index].EntryHi.VPN2() << 13) + (m_FastTlb[FastIndx].Length + 1);
m_FastTlb[FastIndx].VEND = m_FastTlb[FastIndx].VSTART + m_FastTlb[FastIndx].Length;
m_FastTlb[FastIndx].PHYSSTART = (uint32_t)m_tlb[Index].EntryLo1.PFN << 12;
m_FastTlb[FastIndx].PHYSEND = m_FastTlb[FastIndx].PHYSSTART + m_FastTlb[FastIndx].Length;
m_FastTlb[FastIndx].VALID = m_tlb[Index].EntryLo1.V != 0;
m_FastTlb[FastIndx].DIRTY = m_tlb[Index].EntryLo1.D != 0;
m_FastTlb[FastIndx].GLOBAL = m_tlb[Index].EntryLo0.GLOBAL & m_tlb[Index].EntryLo1.GLOBAL;
m_FastTlb[FastIndx].ValidEntry = false;
m_FastTlb[FastIndx].Random = Random;
m_FastTlb[FastIndx].Probed = false;
// Test both entries to see if they are valid
for (FastIndx = Index << 1; FastIndx <= (Index << 1) + 1; FastIndx++)
{
if (!m_FastTlb[FastIndx].VALID)
{
m_FastTlb[FastIndx].ValidEntry = true;
continue;
}
if ((m_FastTlb[FastIndx].VSTART & 0x80000000) == 0 && m_FastTlb[FastIndx].Region != 0)
{
continue;
}
if ((m_FastTlb[FastIndx].VSTART & 0x80000000) != 0 && m_FastTlb[FastIndx].Region != 3)
{
continue;
}
if (m_FastTlb[FastIndx].VEND <= m_FastTlb[FastIndx].VSTART)
{
continue;
}
if (m_FastTlb[FastIndx].VSTART >= 0x80000000 && m_FastTlb[FastIndx].VEND <= 0xBFFFFFFF)
{
continue;
}
if (m_FastTlb[FastIndx].PHYSEND > 0x1FFFFFFF)
{
continue;
}
// Map the new m_tlb entry for reading and writing
m_FastTlb[FastIndx].ValidEntry = true;
m_MMU.TLB_Mapped(m_FastTlb[FastIndx].VSTART, m_FastTlb[FastIndx].Length, m_FastTlb[FastIndx].PHYSSTART, !m_FastTlb[FastIndx].DIRTY);
}
}
void CTLB::TLB_Unmaped(uint32_t VAddr, uint32_t Len)
{
m_MMU.TLB_Unmaped(VAddr, Len);
if (m_Recomp && bSMM_TLB())
{
m_Recomp->ClearRecompCode_Virt((uint32_t)VAddr, Len, CRecompiler::Remove_TLB);
}
}
bool CTLB::VAddrToPAddr(uint64_t VAddr, uint32_t & PAddr, bool & MemoryUnused)
{
MemoryUnused = false;
if (b32BitCore() && (uint64_t)((int32_t)VAddr) != VAddr)
{
MemoryUnused = true;
return false;
}
MemorySegment Segment = VAddrMemorySegment(VAddr);
if (Segment == MemorySegment_Mapped)
{
for (uint32_t i = 0; i < 32; i++)
{
if (m_tlb[i].EntryLo0.GLOBAL == 0)
{
continue;
}
if ((VAddr & 0xE000000000000000) != ((uint64_t)m_tlb[i].EntryHi.R() << 62))
{
continue;
}
uint64_t PageMask = (m_tlb[i].PageMask.Mask << 12);
uint64_t AddressMaskHi = ~(PageMask | 0x1fff) & 0xFFFFFFFFFF;
uint64_t AddressRegion = ((uint64_t)m_tlb[i].EntryHi.VPN2() << 13);
if ((VAddr & AddressMaskHi) != AddressRegion)
{
continue;
}
uint64_t AddressSelect = ((m_tlb[i].PageMask.Mask << 12) | 0xfff) + 1;
COP0EntryLo EntryLo = (VAddr & AddressSelect) != 0 ? m_tlb[i].EntryLo1 : m_tlb[i].EntryLo0;
PAddr = (uint32_t)((EntryLo.PFN << 12) + (VAddr & (PageMask | 0x1fff)));
return true;
}
return false;
}
if (Segment == MemorySegment_Unused)
{
MemoryUnused = true;
return false;
}
if (Segment == MemorySegment_Direct32 || Segment == MemorySegment_Cached32)
{
PAddr = VAddr & 0x7FFFFFFF;
return true;
}
return false;
}
MemorySegment CTLB::VAddrMemorySegment(uint64_t VAddr)
{
if (m_AddressSize32bit)
{
if ((uint64_t)((int32_t)VAddr) != VAddr)
{
return MemorySegment_Unused;
}
uint32_t VAddr32 = (uint32_t)VAddr;
if (m_PrivilegeMode == PrivilegeMode_Kernel)
{
if (VAddr32 <= 0x7fffffff) //kuseg
{
return MemorySegment_Mapped;
}
if (VAddr32 <= 0x9fffffff) //kseg0
{
return MemorySegment_Cached32;
}
if (VAddr32 <= 0xbfffffff) //kseg1
{
return MemorySegment_Direct32;
}
if (VAddr32 <= 0xdfffffff) //ksseg
{
return MemorySegment_Mapped;
}
if (VAddr32 <= 0xffffffff) //kseg3
{
return MemorySegment_Mapped;
}
}
else if (m_PrivilegeMode == PrivilegeMode_Supervisor)
{
if (VAddr32 <= 0x7fffffff) //suseg
{
return MemorySegment_Mapped;
}
if (VAddr32 <= 0xbfffffff)
{
return MemorySegment_Unused;
}
if (VAddr32 <= 0xdfffffff) //sseg
{
return MemorySegment_Mapped;
}
}
else if (m_PrivilegeMode == PrivilegeMode_User)
{
if (VAddr32 <= 0x7fffffff) //useg
{
return MemorySegment_Mapped;
}
}
return MemorySegment_Unused;
}
if (m_PrivilegeMode == PrivilegeMode_Kernel)
{
if (VAddr <= 0x000000ffffffffffull) //xkuseg
{
return MemorySegment_Mapped;
}
if (VAddr <= 0x3fffffffffffffffull)
{
return MemorySegment_Unused;
}
if (VAddr <= 0x400000ffffffffffull) //xksseg
{
return MemorySegment_Mapped;
}
if (VAddr <= 0x7fffffffffffffffull)
{
return MemorySegment_Unused;
}
if (VAddr <= 0x80000000ffffffffull) //xkphys*
{
return MemorySegment_Cached32;
}
if (VAddr <= 0x87ffffffffffffffull)
{
return MemorySegment_Unused;
}
if (VAddr <= 0x88000000ffffffffull)
{
return MemorySegment_Cached32; //xkphys*
}
if (VAddr <= 0x8fffffffffffffffull)
{
return MemorySegment_Unused;
}
if (VAddr <= 0x90000000ffffffffull) //xkphys*
{
return MemorySegment_Direct32;
}
if (VAddr <= 0x97ffffffffffffffull)
{
return MemorySegment_Unused;
}
if (VAddr <= 0x98000000ffffffffull) //xkphys*
{
return MemorySegment_Cached32;
}
if (VAddr <= 0x9fffffffffffffffull)
{
return MemorySegment_Unused;
}
if (VAddr <= 0xa0000000ffffffffull) //xkphys*
{
return MemorySegment_Cached32;
}
if (VAddr <= 0xa7ffffffffffffffull)
{
return MemorySegment_Unused;
}
if (VAddr <= 0xa8000000ffffffffull) //xkphys*
{
return MemorySegment_Cached32;
}
if (VAddr <= 0xafffffffffffffffull)
{
return MemorySegment_Unused;
}
if (VAddr <= 0xb0000000ffffffffull) //xkphys*
{
return MemorySegment_Cached32;
}
if (VAddr <= 0xb7ffffffffffffffull)
{
return MemorySegment_Unused;
}
if (VAddr <= 0xb8000000ffffffffull) //xkphys*
{
return MemorySegment_Cached32;
}
if (VAddr <= 0xbfffffffffffffffull)
{
return MemorySegment_Unused;
}
if (VAddr <= 0xc00000ff7fffffffull) //xkseg
{
return MemorySegment_Mapped;
}
if (VAddr <= 0xffffffff7fffffffull)
{
return MemorySegment_Unused;
}
if (VAddr <= 0xffffffff9fffffffull) //ckseg0
{
return MemorySegment_Cached32;
}
if (VAddr <= 0xffffffffbfffffffull) //ckseg1
{
return MemorySegment_Direct32;
}
if (VAddr <= 0xffffffffdfffffffull) //ckseg2
{
return MemorySegment_Mapped;
}
if (VAddr <= 0xffffffffffffffffull) //ckseg3
{
return MemorySegment_Mapped;
}
}
else if (m_PrivilegeMode == PrivilegeMode_Kernel)
{
if (VAddr <= 0x000000ffffffffffull) //xsuseg
{
return MemorySegment_Mapped;
}
if (VAddr <= 0x3fffffffffffffffull)
{
return MemorySegment_Unused;
}
if (VAddr <= 0x400000ffffffffffull) //xsseg
{
return MemorySegment_Mapped;
}
if (VAddr <= 0xffffffffbfffffffull)
{
return MemorySegment_Unused;
}
if (VAddr <= 0xffffffffdfffffffull) //csseg
{
return MemorySegment_Mapped;
}
if (VAddr <= 0xffffffffffffffffull)
{
return MemorySegment_Unused;
}
}
else if (m_PrivilegeMode == PrivilegeMode_User)
{
if (VAddr <= 0x000000ffffffffffull)
{
return MemorySegment_Mapped; //xuseg
}
}
return MemorySegment_Unused;
}
bool CTLB::PAddrToVAddr(uint32_t PAddr, uint32_t & VAddr, uint32_t & Index)
{
for (int i = Index; i < 64; i++)
{
if (m_FastTlb[i].ValidEntry == false)
{
continue;
}
if (PAddr >= m_FastTlb[i].PHYSSTART && PAddr < m_FastTlb[i].PHYSEND)
{
VAddr = (uint32_t)(m_FastTlb[i].VSTART + (PAddr - m_FastTlb[i].PHYSSTART));
Index = i + 1;
return true;
}
}
return false;
}
void CTLB::RecordDifference(CLog & LogFile, const CTLB & rTLB)
{
for (int i = 0, n = sizeof(m_tlb) / sizeof(m_tlb[0]); i < n; i++)
{
if (m_tlb[i].EntryDefined != rTLB.m_tlb[i].EntryDefined)
{
LogFile.LogF("TLB[%d] Defined: %s %s\r\n", i, m_tlb[i].EntryDefined ? "Yes" : "No", rTLB.m_tlb[i].EntryDefined ? "Yes" : "No");
continue;
}
if (!m_tlb[i].EntryDefined)
{
continue;
}
if (m_tlb[i].PageMask.Value != rTLB.m_tlb[i].PageMask.Value)
{
LogFile.LogF("TLB[%d] PageMask: %X %X\r\n", i, m_tlb[i].PageMask.Value, rTLB.m_tlb[i].PageMask.Value);
}
if (m_tlb[i].EntryHi.Value != rTLB.m_tlb[i].EntryHi.Value)
{
LogFile.LogF("TLB[%d] EntryHi: %X %X\r\n", i, m_tlb[i].EntryHi.Value, rTLB.m_tlb[i].EntryHi.Value);
}
if (m_tlb[i].EntryLo0.Value != rTLB.m_tlb[i].EntryLo0.Value)
{
LogFile.LogF("TLB[%d] EntryLo0: %X %X\r\n", i, m_tlb[i].EntryLo0.Value, rTLB.m_tlb[i].EntryLo0.Value);
}
if (m_tlb[i].EntryLo1.Value != rTLB.m_tlb[i].EntryLo1.Value)
{
LogFile.LogF("TLB[%d] EntryLo1: %X %X\r\n", i, m_tlb[i].EntryLo1.Value, rTLB.m_tlb[i].EntryLo1.Value);
}
}
}
bool CTLB::operator==(const CTLB & rTLB) const
{
const size_t n = sizeof(m_tlb) / sizeof(m_tlb[0]);
for (size_t i = 0; i < n; i++)
{
if (m_tlb[i].EntryDefined != rTLB.m_tlb[i].EntryDefined)
{
return false;
}
if (!m_tlb[i].EntryDefined)
{
continue;
}
if (m_tlb[i].PageMask.Value != rTLB.m_tlb[i].PageMask.Value ||
m_tlb[i].EntryHi.Value != rTLB.m_tlb[i].EntryHi.Value ||
m_tlb[i].EntryLo0.Value != rTLB.m_tlb[i].EntryLo0.Value ||
m_tlb[i].EntryLo1.Value != rTLB.m_tlb[i].EntryLo1.Value)
{
return false;
}
}
return true;
}
bool CTLB::operator!=(const CTLB & rTLB) const
{
return !(*this == rTLB);
}