mirror of
https://github.com/DaedalusX64/daedalus.git
synced 2025-04-02 10:21:48 -04:00
188 lines
4.7 KiB
C++
188 lines
4.7 KiB
C++
/*
|
|
Copyright (C) 2009 StrmnNrmn
|
|
|
|
This program is free software; you can redistribute it and/or
|
|
modify it under the terms of the GNU General Public License
|
|
as published by the Free Software Foundation; either version 2
|
|
of the License, or (at your option) any later version.
|
|
|
|
This program is distributed in the hope that it will be useful,
|
|
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
GNU General Public License for more details.
|
|
|
|
You should have received a copy of the GNU General Public License
|
|
along with this program; if not, write to the Free Software
|
|
Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
|
|
|
|
*/
|
|
|
|
|
|
#include "Base/Types.h"
|
|
#include "Core/CPU.h"
|
|
#include "Core/TLB.h"
|
|
#include "Debug/DebugLog.h"
|
|
#include "Debug/DBGConsole.h"
|
|
#include "Ultra/ultra_R4300.h"
|
|
|
|
// ALIGNED_GLOBAL(TLBEntry, g_TLBs[32], CACHE_ALIGN);
|
|
alignas(CACHE_ALIGN) std::array<TLBEntry, 32> g_TLBs;
|
|
|
|
void TLBEntry::UpdateValue(u32 _pagemask, u32 _hi, u32 _pfno, u32 _pfne)
|
|
{
|
|
// From the R4300i Instruction manual:
|
|
// The G bit of the TLB is written with the logical AND of the G bits of the EntryLo0 and EntryLo1 regs
|
|
// The TLB entry is loaded with the contents of the EntryHi and EntryLo regs.
|
|
|
|
// TLB[INDEX] <- PageMask || (EntryHi AND NOT PageMask) || EntryLo1 || EntryLo0
|
|
DPF( DEBUG_TLB, "PAGEMASK: 0x%08x ENTRYHI: 0x%08x. ENTRYLO1: 0x%08x. ENTRYLO0: 0x%08x", _pagemask, _hi, _pfno, _pfne);
|
|
|
|
pagemask = _pagemask;
|
|
hi = _hi;
|
|
pfne = _pfne;
|
|
pfno = _pfno;
|
|
|
|
g = pfne & pfno & TLBLO_G;
|
|
|
|
// Build the masks:
|
|
mask = pagemask | (~TLBHI_VPN2MASK);
|
|
mask2 = mask>>1;
|
|
vpnmask = ~mask;
|
|
vpn2mask = vpnmask>>1;
|
|
|
|
addrcheck= hi & vpnmask;
|
|
|
|
pfnehi = ((pfne<<TLBLO_PFNSHIFT) & vpn2mask);
|
|
pfnohi = ((pfno<<TLBLO_PFNSHIFT) & vpn2mask);
|
|
|
|
switch (pagemask)
|
|
{
|
|
case TLBPGMASK_4K: // 4k
|
|
#ifdef DAEDALUS_PROFILE
|
|
DPF(DEBUG_TLB, " 4k Pagesize");
|
|
#endif
|
|
checkbit = 0x00001000; // bit 12
|
|
break;
|
|
case TLBPGMASK_16K: // 16k pagesize
|
|
#ifdef DAEDALUS_PROFILE
|
|
DPF(DEBUG_TLB, " 16k Pagesize");
|
|
#endif
|
|
checkbit = 0x00004000; // bit 14
|
|
break;
|
|
case TLBPGMASK_64K: // 64k pagesize
|
|
#ifdef DAEDALUS_PROFILE
|
|
DPF(DEBUG_TLB, " 64k Pagesize");
|
|
#endif
|
|
checkbit = 0x00010000; // bit 16
|
|
break;
|
|
case TLBPGMASK_256K: // 256k pagesize
|
|
#ifdef DAEDALUS_PROFILE
|
|
DPF(DEBUG_TLB, " 256k Pagesize");
|
|
#endif
|
|
checkbit = 0x00040000; // bit 18
|
|
break;
|
|
case TLBPGMASK_1M: // 1M pagesize
|
|
#ifdef DAEDALUS_PROFILE
|
|
DPF(DEBUG_TLB, " 1M Pagesize");
|
|
#endif
|
|
checkbit = 0x00100000; // bit 20
|
|
break;
|
|
case TLBPGMASK_4M: // 4M pagesize
|
|
#ifdef DAEDALUS_PROFILE
|
|
DPF(DEBUG_TLB, " 4M Pagesize");
|
|
#endif
|
|
checkbit = 0x00400000; // bit 22
|
|
break;
|
|
case TLBPGMASK_16M: // 16M pagesize
|
|
#ifdef DAEDALUS_PROFILE
|
|
DPF(DEBUG_TLB, " 16M Pagesize");
|
|
#endif
|
|
checkbit = 0x01000000; // bit 24
|
|
break;
|
|
default: // should not happen!
|
|
#ifdef DAEDALUS_PROFILE
|
|
DPF(DEBUG_TLB, " Unknown Pagesize");
|
|
#endif
|
|
checkbit = 0;
|
|
break;
|
|
}
|
|
}
|
|
|
|
void TLBEntry::Reset()
|
|
{
|
|
UpdateValue(0x00000000, 0x80000000, 0, 0);
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
inline bool TLBEntry::FindTLBEntry( u32 address, u32 * p_idx )
|
|
{
|
|
static u32 i = 0;
|
|
|
|
u8 mask = (u8)(gCPUState.CPUControl[C0_ENTRYHI]._u32 & TLBHI_PIDMASK);
|
|
for ( u32 count = 0; count < 32; count++ )
|
|
{
|
|
// Hack to check most recently reference entry first
|
|
// This gives some speedup if the matched address is near
|
|
// the end of the tlb array (32 entries)
|
|
i = (count + i) & 0x1F;
|
|
|
|
const TLBEntry & tlb = g_TLBs[i];
|
|
|
|
// Check that the VPN numbers match
|
|
if ((address & tlb.vpnmask) == tlb.addrcheck)
|
|
{
|
|
if (!tlb.g)
|
|
{
|
|
if ( (tlb.hi & TLBHI_PIDMASK) != mask )
|
|
{
|
|
// Entries ASID must match.
|
|
continue;
|
|
}
|
|
}
|
|
|
|
*p_idx = i;
|
|
|
|
return true;
|
|
}
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
//*****************************************************************************
|
|
//
|
|
//*****************************************************************************
|
|
u32 TLBEntry::Translate(u32 address, bool& missing)
|
|
{
|
|
u32 iMatched = 0;
|
|
|
|
missing = !FindTLBEntry( address, &iMatched );
|
|
if (!missing)
|
|
{
|
|
const TLBEntry & tlb = g_TLBs[iMatched];
|
|
|
|
// Check for odd/even entry
|
|
if (address & tlb.checkbit)
|
|
{
|
|
if( (tlb.pfno & TLBLO_V) != 0 )
|
|
return tlb.pfnohi | (address & tlb.mask2);
|
|
}
|
|
else
|
|
{
|
|
if( (tlb.pfne & TLBLO_V) != 0 )
|
|
return tlb.pfnehi | (address & tlb.mask2);
|
|
}
|
|
|
|
// Throw TLB Invalid exception
|
|
return 0;
|
|
}
|
|
else
|
|
{
|
|
// TLBRefill
|
|
|
|
// No valid TLB entry - throw TLB Refill Exception
|
|
return 0;
|
|
}
|
|
}
|