2009-09-08 12:08:10 +00:00
/* PCSX2 - PS2 Emulator for PCs
2010-05-03 14:08:02 +00:00
* Copyright (C) 2002-2010 PCSX2 Dev Team
2010-04-25 00:31:27 +00:00
*
2009-09-08 12:08:10 +00:00
* PCSX2 is free software: you can redistribute it and/or modify it under the terms
* of the GNU Lesser General Public License as published by the Free Software Found-
* ation, either version 3 of the License, or (at your option) any later version.
2009-02-09 21:15:56 +00:00
*
2009-09-08 12:08:10 +00:00
* PCSX2 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 PCSX2.
* If not, see <http://www.gnu.org/licenses/>.
2009-02-09 21:15:56 +00:00
*/
2009-09-08 12:08:10 +00:00
2009-02-09 21:15:56 +00:00
#include "PrecompiledHeader.h"
#include "Common.h"
#include "Cache.h"
2011-02-17 21:27:24 +00:00
#include "vtlb.h"
2009-07-30 23:50:39 +00:00
2011-02-17 21:27:24 +00:00
using namespace R5900 ;
using namespace vtlb_private ;
2020-10-17 04:31:26 -05:00
namespace
2019-07-13 17:49:51 -07:00
{
2020-10-17 04:31:26 -05:00
union alignas ( 64 ) CacheData
{
u8 bytes [ 64 ];
constexpr CacheData () : bytes { 0 } {}
};
struct CacheTag
{
uptr rawValue = 0 ;
CacheTag () = default ;
// The lower parts of a cache tags structure is as follows:
// 31 - 12: The physical address cache tag.
// 11 - 7: Unused.
// 6: Dirty flag.
// 5: Valid flag.
// 4: LRF flag - least recently filled flag.
// 3: Lock flag.
// 2-0: Unused.
enum Flags : decltype ( rawValue )
{
DIRTY_FLAG = 0x40 ,
VALID_FLAG = 0x20 ,
LRF_FLAG = 0x10 ,
LOCK_FLAG = 0x8 ,
ALL_FLAGS = 0xFFF
};
int flags () const
{
return rawValue & ALL_FLAGS ;
}
bool isValid () const { return rawValue & VALID_FLAG ; }
bool isDirty () const { return rawValue & DIRTY_FLAG ; }
bool lrf () const { return rawValue & LRF_FLAG ; }
bool isLocked () const { return rawValue & LOCK_FLAG ; }
bool isDirtyAndValid () const
{
return ( rawValue & ( DIRTY_FLAG | VALID_FLAG )) == ( DIRTY_FLAG | VALID_FLAG );
}
void setValid () { rawValue |= VALID_FLAG ; }
void setDirty () { rawValue |= DIRTY_FLAG ; }
void setLocked () { rawValue |= LOCK_FLAG ; }
void clearValid () { rawValue &= ~ VALID_FLAG ; }
void clearDirty () { rawValue &= ~ DIRTY_FLAG ; }
void clearLocked () { rawValue &= ~ LOCK_FLAG ; }
void toggleLRF () { rawValue ^= LRF_FLAG ; }
uptr addr () const { return rawValue & ~ ALL_FLAGS ; }
void setAddr ( uptr addr )
{
rawValue &= ALL_FLAGS ;
rawValue |= ( addr & ~ ALL_FLAGS );
}
bool matches ( uptr other ) const
{
return isValid () && addr () == ( other & ~ ALL_FLAGS );
}
void clear ()
{
rawValue &= LRF_FLAG ;
}
};
struct CacheLine
{
CacheTag & tag ;
CacheData & data ;
int set ;
uptr addr ()
{
return tag . addr () | ( set << 6 );
}
void writeBackIfNeeded ()
{
if ( ! tag . isDirtyAndValid ())
return ;
uptr target = addr ();
CACHE_LOG ( "Write back at %zx" , target );
* reinterpret_cast < CacheData *> ( target ) = data ;
tag . clearDirty ();
}
void load ( uptr ppf )
{
pxAssertMsg ( ! tag . isDirtyAndValid (), "Loaded a value into cache without writing back the old one!" );
tag . setAddr ( ppf );
data = * reinterpret_cast < CacheData *> ( ppf & ~ 0x3FULL );
tag . setValid ();
tag . clearDirty ();
}
void clear ()
{
tag . clear ();
data = CacheData ();
}
};
struct CacheSet
{
CacheTag tags [ 2 ];
CacheData data [ 2 ];
};
struct Cache
{
CacheSet sets [ 64 ];
int setIdxFor ( u32 vaddr ) const
{
return ( vaddr >> 6 ) & 0x3F ;
}
CacheLine lineAt ( int idx , int way )
{
return { sets [ idx ]. tags [ way ], sets [ idx ]. data [ way ], idx };
}
};
static Cache cache ;
}
void resetCache ()
{
memzero ( cache );
}
static bool findInCache ( const CacheSet & set , uptr ppf , int * way )
{
auto check = [ & ]( int checkWay ) -> bool
{
if ( ! set . tags [ checkWay ]. matches ( ppf ))
return false ;
* way = checkWay ;
return true ;
};
return check ( 0 ) || check ( 1 );
}
static int getFreeCache ( u32 mem , int * way )
{
const int setIdx = cache . setIdxFor ( mem );
CacheSet & set = cache . sets [ setIdx ];
VTLBVirtual vmv = vtlbdata . vmap [ mem >> VTLB_PAGE_BITS ];
pxAssertMsg ( ! vmv . isHandler ( mem ), "Cache currently only supports non-handler addresses!" );
2020-08-19 03:37:23 -05:00
uptr ppf = vmv . assumePtr ( mem );
2009-02-09 21:15:56 +00:00
2020-10-17 04:31:26 -05:00
if (( cpuRegs . CP0 . n . Config & 0x10000 ) == 0 )
CACHE_LOG ( "Cache off!" );
if ( findInCache ( set , ppf , way ))
2009-02-09 21:15:56 +00:00
{
2020-10-17 04:31:26 -05:00
if ( set . tags [ * way ]. isLocked ())
CACHE_LOG ( "Index %x Way %x Locked!!" , setIdx , * way );
2009-07-30 23:50:39 +00:00
}
2011-02-17 21:27:24 +00:00
else
2020-10-17 04:31:26 -05:00
{
int newWay = set . tags [ 0 ]. lrf () ^ set . tags [ 1 ]. lrf ();
* way = newWay ;
CacheLine line = cache . lineAt ( setIdx , newWay );
2011-02-17 21:27:24 +00:00
2020-10-17 04:31:26 -05:00
line . writeBackIfNeeded ();
line . load ( ppf );
line . tag . toggleLRF ();
}
return setIdx ;
}
template < typename Int >
void writeCache ( u32 mem , Int value )
{
int way = 0 ;
const int idx = getFreeCache ( mem , & way );
CACHE_LOG ( "writeCache%d %8.8x adding to %d, way %d, value %llx" , 8 * sizeof ( value ), mem , idx , way , value );
CacheLine line = cache . lineAt ( idx , way );
line . tag . setDirty (); // Set dirty bit for writes;
u32 aligned = mem & ~ ( sizeof ( value ) - 1 );
* reinterpret_cast < Int *> ( & line . data . bytes [ aligned & 0x3f ]) = value ;
2009-02-09 21:15:56 +00:00
}
2019-07-13 17:49:51 -07:00
void writeCache8 ( u32 mem , u8 value )
{
2020-10-17 04:31:26 -05:00
writeCache < u8 > ( mem , value );
2009-02-09 21:15:56 +00:00
}
2019-07-13 17:49:51 -07:00
void writeCache16 ( u32 mem , u16 value )
{
2020-10-17 04:31:26 -05:00
writeCache < u16 > ( mem , value );
2009-02-09 21:15:56 +00:00
}
2019-07-13 17:49:51 -07:00
void writeCache32 ( u32 mem , u32 value )
{
2020-10-17 04:31:26 -05:00
writeCache < u32 > ( mem , value );
2009-02-09 21:15:56 +00:00
}
2019-07-13 17:49:51 -07:00
void writeCache64 ( u32 mem , const u64 value )
{
2020-10-17 04:31:26 -05:00
writeCache < u64 > ( mem , value );
2009-02-09 21:15:56 +00:00
}
2019-07-13 17:49:51 -07:00
void writeCache128 ( u32 mem , const mem128_t * value )
{
2020-10-17 04:31:26 -05:00
int way = 0 ;
const int idx = getFreeCache ( mem , & way );
2019-07-13 17:49:51 -07:00
2020-10-17 04:31:26 -05:00
CACHE_LOG ( "writeCache128 %8.8x adding to %d, way %x, lo %x, hi %x" , mem , idx , way , value -> lo , value -> hi );
CacheLine line = cache . lineAt ( idx , way );
line . tag . setDirty (); // Set dirty bit for writes;
u32 aligned = mem & ~ 0xF ;
* reinterpret_cast < mem128_t *> ( & line . data . bytes [ aligned & 0x3f ]) = * value ;
2009-02-09 21:15:56 +00:00
}
2020-10-17 04:31:26 -05:00
template < typename Int >
Int readCache ( u32 mem )
{
int way = 0 ;
const int idx = getFreeCache ( mem , & way );
CacheLine line = cache . lineAt ( idx , way );
u32 aligned = mem & ~ ( sizeof ( Int ) - 1 );
Int value = * reinterpret_cast < Int *> ( & line . data . bytes [ aligned & 0x3f ]);
CACHE_LOG ( "readCache%d %8.8x from %d, way %d, value %llx" , 8 * sizeof ( value ), mem , idx , way , value );
return value ;
}
2019-07-13 17:49:51 -07:00
u8 readCache8 ( u32 mem )
{
2020-10-17 04:31:26 -05:00
return readCache < u8 > ( mem );
2009-02-09 21:15:56 +00:00
}
2019-07-13 17:49:51 -07:00
u16 readCache16 ( u32 mem )
{
2020-10-17 04:31:26 -05:00
return readCache < u16 > ( mem );
2011-02-17 21:27:24 +00:00
}
2019-07-13 17:49:51 -07:00
u32 readCache32 ( u32 mem )
{
2020-10-17 04:31:26 -05:00
return readCache < u32 > ( mem );
2011-02-17 21:27:24 +00:00
}
2019-07-13 17:49:51 -07:00
u64 readCache64 ( u32 mem )
{
2020-10-17 04:31:26 -05:00
return readCache < u64 > ( mem );
2019-07-13 17:49:51 -07:00
}
2020-10-17 04:31:26 -05:00
template < typename Op >
void doCacheHitOp ( u32 addr , const char * name , Op op )
2019-07-13 17:49:51 -07:00
{
2020-10-17 04:31:26 -05:00
const int index = cache . setIdxFor ( addr );
CacheSet & set = cache . sets [ index ];
VTLBVirtual vmv = vtlbdata . vmap [ addr >> VTLB_PAGE_BITS ];
uptr ppf = vmv . assumePtr ( addr );
int way ;
2019-07-13 17:49:51 -07:00
2020-10-17 04:31:26 -05:00
if ( ! findInCache ( set , ppf , & way ))
{
CACHE_LOG ( "CACHE %s NO HIT addr %x, index %d, tag0 %zx tag1 %zx" , name , addr , index , set . tags [ 0 ]. rawValue , set . tags [ 1 ]. rawValue );
return ;
}
2019-07-13 17:49:51 -07:00
2020-10-17 04:31:26 -05:00
CACHE_LOG ( "CACHE %s addr %x, index %d, way %d, flags %x OP %x" , name , addr , index , way , set . tags [ way ]. flags (), cpuRegs . code );
2019-07-13 17:49:51 -07:00
2020-10-17 04:31:26 -05:00
op ( cache . lineAt ( index , way ));
2011-02-17 21:27:24 +00:00
}
namespace R5900 {
namespace Interpreter
{
2009-02-09 21:15:56 +00:00
namespace OpcodeImpl
{
extern int Dcache ;
2019-07-13 17:49:51 -07:00
void CACHE ()
{
u32 addr = cpuRegs . GPR . r [ _Rs_ ]. UL [ 0 ] + _Imm_ ;
// CACHE_LOG("cpuRegs.GPR.r[_Rs_].UL[0] = %x, IMM = %x RT = %x", cpuRegs.GPR.r[_Rs_].UL[0], _Imm_, _Rt_);
2011-02-17 21:27:24 +00:00
switch ( _Rt_ )
{
case 0x1a : //DHIN (Data Cache Hit Invalidate)
2020-10-17 04:31:26 -05:00
doCacheHitOp ( addr , "DHIN" , []( CacheLine line )
2009-02-09 21:15:56 +00:00
{
2020-10-17 04:31:26 -05:00
line . clear ();
});
2009-02-09 21:15:56 +00:00
break ;
2019-07-13 17:49:51 -07:00
2011-02-17 21:27:24 +00:00
case 0x18 : //DHWBIN (Data Cache Hit WriteBack with Invalidate)
2020-10-17 04:31:26 -05:00
doCacheHitOp ( addr , "DHWBIN" , []( CacheLine line )
2009-02-09 21:15:56 +00:00
{
2020-10-17 04:31:26 -05:00
line . writeBackIfNeeded ();
line . clear ();
});
2009-02-09 21:15:56 +00:00
break ;
2019-07-13 17:49:51 -07:00
2011-02-17 21:27:24 +00:00
case 0x1c : //DHWOIN (Data Cache Hit WriteBack Without Invalidate)
2020-10-17 04:31:26 -05:00
doCacheHitOp ( addr , "DHWOIN" , []( CacheLine line )
2009-02-09 21:15:56 +00:00
{
2020-10-17 04:31:26 -05:00
line . writeBackIfNeeded ();
});
2009-02-09 21:15:56 +00:00
break ;
2019-07-13 17:49:51 -07:00
2011-02-17 21:27:24 +00:00
case 0x16 : //DXIN (Data Cache Index Invalidate)
2009-02-09 21:15:56 +00:00
{
2020-10-17 04:31:26 -05:00
const int index = cache . setIdxFor ( addr );
2019-07-13 17:49:51 -07:00
const int way = addr & 0x1 ;
2020-10-17 04:31:26 -05:00
CacheLine line = cache . lineAt ( index , way );
2009-02-09 21:15:56 +00:00
2020-10-17 04:31:26 -05:00
CACHE_LOG ( "CACHE DXIN addr %x, index %d, way %d, flag %x" , addr , index , way , line . tag . flags ());
2010-04-25 00:31:27 +00:00
2020-10-17 04:31:26 -05:00
line . clear ();
2009-02-09 21:15:56 +00:00
break ;
}
2019-07-13 17:49:51 -07:00
case 0x11 : //DXLDT (Data Cache Load Data into TagLo)
{
2020-10-17 04:31:26 -05:00
const int index = cache . setIdxFor ( addr );
2019-07-13 17:49:51 -07:00
const int way = addr & 0x1 ;
2020-10-17 04:31:26 -05:00
CacheLine line = cache . lineAt ( index , way );
2019-07-13 17:49:51 -07:00
2020-10-17 04:31:26 -05:00
cpuRegs . CP0 . n . TagLo = * reinterpret_cast < u32 *> ( & line . data . bytes [ addr & 0x3C ]);
2019-07-13 17:49:51 -07:00
2020-10-17 04:31:26 -05:00
CACHE_LOG ( "CACHE DXLDT addr %x, index %d, way %d, DATA %x OP %x" , addr , index , way , cpuRegs . CP0 . n . TagLo , cpuRegs . code );
2019-07-13 17:49:51 -07:00
break ;
}
2011-02-17 21:27:24 +00:00
case 0x10 : //DXLTG (Data Cache Load Tag into TagLo)
2009-02-09 21:15:56 +00:00
{
2019-07-13 17:49:51 -07:00
const int index = ( addr >> 6 ) & 0x3F ;
const int way = addr & 0x1 ;
2020-10-17 04:31:26 -05:00
CacheLine line = cache . lineAt ( index , way );
2016-06-04 12:57:14 +01:00
2020-10-17 04:31:26 -05:00
// DXLTG demands that SYNC.L is called before this command, which forces the cache to write back, so presumably games are checking the cache has updated the memory
// For speed, we will do it here.
line . writeBackIfNeeded ();
2019-07-13 17:49:51 -07:00
2020-10-17 04:31:26 -05:00
// Our tags don't contain PS2 paddrs (instead they contain x86 addrs)
cpuRegs . CP0 . n . TagLo = line . tag . flags ();
2016-06-04 12:57:14 +01:00
2020-10-17 04:31:26 -05:00
CACHE_LOG ( "CACHE DXLTG addr %x, index %d, way %d, DATA %x OP %x " , addr , index , way , cpuRegs . CP0 . n . TagLo , cpuRegs . code );
CACHE_LOG ( "WARNING: DXLTG emulation supports flags only, things could break" );
2009-02-09 21:15:56 +00:00
break ;
}
2019-07-13 17:49:51 -07:00
2011-02-17 21:27:24 +00:00
case 0x13 : //DXSDT (Data Cache Store 32bits from TagLo)
2009-02-09 21:15:56 +00:00
{
2019-07-13 17:49:51 -07:00
const int index = ( addr >> 6 ) & 0x3F ;
const int way = addr & 0x1 ;
2020-10-17 04:31:26 -05:00
CacheLine line = cache . lineAt ( index , way );
2009-02-09 21:15:56 +00:00
2020-10-17 04:31:26 -05:00
* reinterpret_cast < u32 *> ( & line . data . bytes [ addr & 0x3C ]) = cpuRegs . CP0 . n . TagLo ;
2009-02-09 21:15:56 +00:00
2020-10-17 04:31:26 -05:00
CACHE_LOG ( "CACHE DXSDT addr %x, index %d, way %d, DATA %x OP %x" , addr , index , way , cpuRegs . CP0 . n . TagLo , cpuRegs . code );
2009-02-09 21:15:56 +00:00
break ;
}
2019-07-13 17:49:51 -07:00
2011-02-17 21:27:24 +00:00
case 0x12 : //DXSTG (Data Cache Store Tag from TagLo)
2009-02-09 21:15:56 +00:00
{
2019-07-13 17:49:51 -07:00
const int index = ( addr >> 6 ) & 0x3F ;
const int way = addr & 0x1 ;
2020-10-17 04:31:26 -05:00
CacheLine line = cache . lineAt ( index , way );
2009-02-09 21:15:56 +00:00
2020-10-17 04:31:26 -05:00
line . tag . rawValue &= ~ CacheTag :: ALL_FLAGS ;
line . tag . rawValue |= ( cpuRegs . CP0 . n . TagLo & CacheTag :: ALL_FLAGS );
CACHE_LOG ( "CACHE DXSTG addr %x, index %d, way %d, DATA %x OP %x" , addr , index , way , cpuRegs . CP0 . n . TagLo , cpuRegs . code );
CACHE_LOG ( "WARNING: DXSTG emulation supports flags only, things will probably break" );
2011-02-17 21:27:24 +00:00
break ;
}
2019-07-13 17:49:51 -07:00
2011-02-17 21:27:24 +00:00
case 0x14 : //DXWBIN (Data Cache Index WriteBack Invalidate)
{
2019-07-13 17:49:51 -07:00
const int index = ( addr >> 6 ) & 0x3F ;
const int way = addr & 0x1 ;
2020-10-17 04:31:26 -05:00
CacheLine line = cache . lineAt ( index , way );
2011-02-17 21:27:24 +00:00
2020-10-17 04:31:26 -05:00
CACHE_LOG ( "CACHE DXWBIN addr %x, index %d, way %d, flags %x paddr %zx" , addr , index , way , line . tag . flags (), line . addr ());
line . writeBackIfNeeded ();
line . clear ();
2009-02-09 21:15:56 +00:00
break ;
}
2019-07-13 17:49:51 -07:00
2011-02-17 21:27:24 +00:00
case 0x7 : //IXIN (Instruction Cache Index Invalidate)
{
//Not Implemented as we do not have instruction cache
break ;
}
2019-07-13 17:49:51 -07:00
2011-02-17 21:27:24 +00:00
case 0xC : //BFH (BTAC Flush)
{
//Not Implemented as we do not cache Branch Target Addresses.
break ;
}
2019-07-13 17:49:51 -07:00
2011-02-17 21:27:24 +00:00
default :
2019-07-13 17:49:51 -07:00
DevCon . Warning ( "Cache mode %x not implemented" , _Rt_ );
2011-02-17 21:27:24 +00:00
break ;
2009-02-09 21:15:56 +00:00
}
}
} // end namespace OpcodeImpl
2016-06-03 12:00:55 +05:30
}}