2026-01-12 05:18:12 -05:00
// SPDX-FileCopyrightText: 2002-2026 PCSX2 Dev Team
2024-07-30 13:42:36 +02:00
// SPDX-License-Identifier: GPL-3.0+
2009-02-09 21:15:56 +00:00
#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 ];
};
struct CacheTag
{
2024-01-08 22:31:11 +10:00
uptr rawValue ;
2024-06-24 11:36:28 -04:00
// You are able to configure a TLB entry with non-existant physical address without causing a bus error.
// When this happens, the cache still fills with the data and when it gets evicted the data is lost.
// We don't emulate memory access on a logic level, so we need to ensure that we don't try to load/store to a non-existant physical address.
// This fixes the Find My Own Way demo.
2025-06-12 23:16:13 -05:00
2020-10-17 04:31:26 -05:00
// The lower parts of a cache tags structure is as follows:
// 31 - 12: The physical address cache tag.
2025-01-04 12:01:19 -05:00
// 11: Used by PCSX2 to indicate if the physical address is valid.
// 10 - 7: Unused.
2020-10-17 04:31:26 -05:00
// 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 ,
2025-01-04 12:01:19 -05:00
ALL_FLAGS = 0x7FF ,
ALL_BITS = 0xFFF
2020-10-17 04:31:26 -05:00
};
int flags () const
{
return rawValue & ALL_FLAGS ;
}
2024-06-25 11:36:12 -04:00
bool isValid () const { return rawValue & VALID_FLAG ; }
bool isDirty () const { return rawValue & DIRTY_FLAG ; }
bool lrf () const { return rawValue & LRF_FLAG ; }
2020-10-17 04:31:26 -05:00
bool isLocked () const { return rawValue & LOCK_FLAG ; }
bool isDirtyAndValid () const
{
return ( rawValue & ( DIRTY_FLAG | VALID_FLAG )) == ( DIRTY_FLAG | VALID_FLAG );
}
2024-06-25 11:36:12 -04:00
void setValid () { rawValue |= VALID_FLAG ; }
void setDirty () { rawValue |= DIRTY_FLAG ; }
2020-10-17 04:31:26 -05:00
void setLocked () { rawValue |= LOCK_FLAG ; }
2024-06-25 11:36:12 -04:00
void clearValid () { rawValue &= ~ VALID_FLAG ; }
void clearDirty () { rawValue &= ~ DIRTY_FLAG ; }
2020-10-17 04:31:26 -05:00
void clearLocked () { rawValue &= ~ LOCK_FLAG ; }
void toggleLRF () { rawValue ^= LRF_FLAG ; }
2025-01-04 12:01:19 -05:00
uptr addr () const { return rawValue & ~ ALL_BITS ; }
2020-10-17 04:31:26 -05:00
void setAddr ( uptr addr )
{
2025-01-04 12:01:19 -05:00
rawValue &= ALL_BITS ;
rawValue |= ( addr & ~ ALL_BITS );
2020-10-17 04:31:26 -05:00
}
bool matches ( uptr other ) const
{
2025-01-04 12:01:19 -05:00
return isValid () && addr () == ( other & ~ ALL_BITS );
2020-10-17 04:31:26 -05:00
}
void clear ()
{
rawValue &= LRF_FLAG ;
}
2025-01-04 12:01:19 -05:00
constexpr bool isValidPFN () const
{
return rawValue & 0x800 ;
}
constexpr void setValidPFN ( bool valid )
{
if ( valid )
rawValue |= 0x800 ;
else
rawValue &= ~ 0x800 ;
}
2020-10-17 04:31:26 -05:00
};
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 );
2025-01-04 12:01:19 -05:00
if ( tag . isValidPFN ())
2024-06-24 11:36:28 -04:00
* reinterpret_cast < CacheData *> ( target ) = data ;
2020-10-17 04:31:26 -05:00
tag . clearDirty ();
}
void load ( uptr ppf )
{
pxAssertMsg ( ! tag . isDirtyAndValid (), "Loaded a value into cache without writing back the old one!" );
tag . setAddr ( ppf );
2025-01-04 12:01:19 -05:00
if ( ! tag . isValidPFN ())
2024-06-24 11:36:28 -04:00
{
// Reading from invalid physical addresses seems to return 0 on hardware
std :: memset ( & data , 0 , sizeof ( data ));
}
else
{
std :: memcpy ( & data , reinterpret_cast < void *> ( ppf & ~ 0x3FULL ), sizeof ( data ));
}
2020-10-17 04:31:26 -05:00
tag . setValid ();
tag . clearDirty ();
}
void clear ()
{
tag . clear ();
2024-01-08 22:31:11 +10:00
std :: memset ( & data , 0 , sizeof ( data ));
2020-10-17 04:31:26 -05:00
}
};
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 )
{
2024-06-25 11:36:12 -04:00
return { sets [ idx ]. tags [ way ], sets [ idx ]. data [ way ], idx };
2020-10-17 04:31:26 -05:00
}
};
2024-01-08 22:31:11 +10:00
static Cache cache = {};
2024-06-25 11:36:12 -04:00
} // namespace
2020-10-17 04:31:26 -05:00
void resetCache ()
{
2023-06-16 21:25:52 +10:00
std :: memset ( & cache , 0 , sizeof ( cache ));
2020-10-17 04:31:26 -05:00
}
2024-12-08 14:05:03 -05:00
void writebackCache ()
{
for ( int i = 0 ; i < 64 ; i ++ )
{
for ( int j = 0 ; j < 2 ; j ++ )
{
cache . lineAt ( i , j ). writeBackIfNeeded ();
}
}
}
2020-10-17 04:31:26 -05:00
static bool findInCache ( const CacheSet & set , uptr ppf , int * way )
{
2024-06-25 11:36:12 -04:00
auto check = [ & ]( int checkWay ) -> bool {
2020-10-17 04:31:26 -05:00
if ( ! set . tags [ checkWay ]. matches ( ppf ))
return false ;
* way = checkWay ;
return true ;
};
return check ( 0 ) || check ( 1 );
}
2024-06-24 11:36:28 -04:00
static int getFreeCache ( u32 mem , int * way , bool validPFN )
2020-10-17 04:31:26 -05:00
{
const int setIdx = cache . setIdxFor ( mem );
CacheSet & set = cache . sets [ setIdx ];
VTLBVirtual vmv = vtlbdata . vmap [ mem >> VTLB_PAGE_BITS ];
2024-06-24 11:36:28 -04:00
* way = set . tags [ 0 ]. lrf () ^ set . tags [ 1 ]. lrf ();
if ( validPFN )
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
2024-06-24 11:36:28 -04:00
[[unlikely]]
if (( cpuRegs . CP0 . n . Config & 0x10000 ) == 0 )
2020-10-17 04:31:26 -05:00
CACHE_LOG ( "Cache off!" );
if ( findInCache ( set , ppf , way ))
2009-02-09 21:15:56 +00:00
{
2024-06-24 11:36:28 -04:00
[[unlikely]]
2020-10-17 04:31:26 -05:00
if ( set . tags [ * way ]. isLocked ())
2024-06-24 11:36:28 -04:00
{
// Check the other way
if ( set . tags [ * way ^ 1 ]. isLocked ())
{
Console . Error ( "CACHE: SECOND WAY IS LOCKED." , setIdx , * way );
}
else
{
// Force the unlocked way
* way ^= 1 ;
}
}
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 ();
2024-06-24 11:36:28 -04:00
[[unlikely]]
if ( set . tags [ newWay ]. isLocked ())
{
// If the new way is locked, we force the unlocked way, ignoring the lrf bits.
newWay = newWay ^ 1 ;
[[unlikely]]
if ( set . tags [ newWay ]. isLocked ())
{
Console . Warning ( "CACHE: SECOND WAY IS LOCKED." , setIdx , * way );
}
}
2020-10-17 04:31:26 -05:00
* way = newWay ;
2011-02-17 21:27:24 +00:00
2024-06-24 11:36:28 -04:00
CacheLine line = cache . lineAt ( setIdx , newWay );
2020-10-17 04:31:26 -05:00
line . writeBackIfNeeded ();
2025-01-04 12:01:19 -05:00
line . tag . setValidPFN ( validPFN );
2020-10-17 04:31:26 -05:00
line . load ( ppf );
line . tag . toggleLRF ();
}
return setIdx ;
}
2021-09-20 01:10:59 -05:00
template < bool Write , int Bytes >
2024-06-24 11:36:28 -04:00
void * prepareCacheAccess ( u32 mem , int * way , int * idx , bool validPFN = true )
2021-09-20 01:10:59 -05:00
{
* way = 0 ;
2024-06-24 11:36:28 -04:00
* idx = getFreeCache ( mem , way , validPFN );
2021-09-20 01:10:59 -05:00
CacheLine line = cache . lineAt ( * idx , * way );
if ( Write )
line . tag . setDirty ();
u32 aligned = mem & ~ ( Bytes - 1 );
return & line . data . bytes [ aligned & 0x3f ];
}
2020-10-17 04:31:26 -05:00
template < typename Int >
2024-06-24 11:36:28 -04:00
void writeCache ( u32 mem , Int value , bool validPFN )
2020-10-17 04:31:26 -05:00
{
2021-09-20 01:10:59 -05:00
int way , idx ;
2024-06-24 11:36:28 -04:00
void * addr = prepareCacheAccess < true , sizeof ( Int ) > ( mem , & way , & idx , validPFN );
2020-10-17 04:31:26 -05:00
CACHE_LOG ( "writeCache%d %8.8x adding to %d, way %d, value %llx" , 8 * sizeof ( value ), mem , idx , way , value );
2021-09-20 01:10:59 -05:00
* reinterpret_cast < Int *> ( addr ) = value ;
2009-02-09 21:15:56 +00:00
}
2024-06-24 11:36:28 -04:00
void writeCache8 ( u32 mem , u8 value , bool validPFN )
2019-07-13 17:49:51 -07:00
{
2024-06-24 11:36:28 -04:00
writeCache < u8 > ( mem , value , validPFN );
2009-02-09 21:15:56 +00:00
}
2024-06-24 11:36:28 -04:00
void writeCache16 ( u32 mem , u16 value , bool validPFN )
2019-07-13 17:49:51 -07:00
{
2024-06-24 11:36:28 -04:00
writeCache < u16 > ( mem , value , validPFN );
2009-02-09 21:15:56 +00:00
}
2024-06-24 11:36:28 -04:00
void writeCache32 ( u32 mem , u32 value , bool validPFN )
2019-07-13 17:49:51 -07:00
{
2024-06-24 11:36:28 -04:00
writeCache < u32 > ( mem , value , validPFN );
2009-02-09 21:15:56 +00:00
}
2024-06-24 11:36:28 -04:00
void writeCache64 ( u32 mem , const u64 value , bool validPFN )
2019-07-13 17:49:51 -07:00
{
2024-06-24 11:36:28 -04:00
writeCache < u64 > ( mem , value , validPFN );
2009-02-09 21:15:56 +00:00
}
2024-06-24 11:36:28 -04:00
void writeCache128 ( u32 mem , const mem128_t * value , bool validPFN )
2019-07-13 17:49:51 -07:00
{
2021-09-20 01:10:59 -05:00
int way , idx ;
2024-06-24 11:36:28 -04:00
void * addr = prepareCacheAccess < true , sizeof ( mem128_t ) > ( mem , & way , & idx , validPFN );
2019-07-13 17:49:51 -07:00
2021-09-20 01:10:59 -05:00
CACHE_LOG ( "writeCache128 %8.8x adding to %d, way %x, lo %llx, hi %llx" , mem , idx , way , value -> lo , value -> hi );
* reinterpret_cast < mem128_t *> ( addr ) = * value ;
2009-02-09 21:15:56 +00:00
}
2020-10-17 04:31:26 -05:00
template < typename Int >
2024-06-24 11:36:28 -04:00
Int readCache ( u32 mem , bool validPFN )
2020-10-17 04:31:26 -05:00
{
2021-09-20 01:10:59 -05:00
int way , idx ;
2024-06-24 11:36:28 -04:00
void * addr = prepareCacheAccess < false , sizeof ( Int ) > ( mem , & way , & idx , validPFN );
2020-10-17 04:31:26 -05:00
2021-09-20 01:10:59 -05:00
Int value = * reinterpret_cast < Int *> ( addr );
2020-10-17 04:31:26 -05:00
CACHE_LOG ( "readCache%d %8.8x from %d, way %d, value %llx" , 8 * sizeof ( value ), mem , idx , way , value );
return value ;
}
2024-06-24 11:36:28 -04:00
u8 readCache8 ( u32 mem , bool validPFN )
2019-07-13 17:49:51 -07:00
{
2024-06-24 11:36:28 -04:00
return readCache < u8 > ( mem , validPFN );
2009-02-09 21:15:56 +00:00
}
2024-06-24 11:36:28 -04:00
u16 readCache16 ( u32 mem , bool validPFN )
2019-07-13 17:49:51 -07:00
{
2024-06-24 11:36:28 -04:00
return readCache < u16 > ( mem , validPFN );
2011-02-17 21:27:24 +00:00
}
2024-06-24 11:36:28 -04:00
u32 readCache32 ( u32 mem , bool validPFN )
2019-07-13 17:49:51 -07:00
{
2024-06-24 11:36:28 -04:00
return readCache < u32 > ( mem , validPFN );
2011-02-17 21:27:24 +00:00
}
2024-06-24 11:36:28 -04:00
u64 readCache64 ( u32 mem , bool validPFN )
2019-07-13 17:49:51 -07:00
{
2024-06-24 11:36:28 -04:00
return readCache < u64 > ( mem , validPFN );
2021-09-20 01:10:59 -05:00
}
2024-06-24 11:36:28 -04:00
RETURNS_R128 readCache128 ( u32 mem , bool validPFN )
2021-09-20 01:10:59 -05:00
{
int way , idx ;
2024-06-24 11:36:28 -04:00
void * addr = prepareCacheAccess < false , sizeof ( mem128_t ) > ( mem , & way , & idx , validPFN );
2021-09-20 01:10:59 -05:00
r128 value = r128_load ( addr );
u64 * vptr = reinterpret_cast < u64 *> ( & value );
CACHE_LOG ( "readCache128 %8.8x from %d, way %d, lo %llx, hi %llx" , mem , idx , way , vptr [ 0 ], vptr [ 1 ]);
return value ;
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
}
2024-06-25 11:36:12 -04:00
namespace R5900
2011-02-17 21:27:24 +00:00
{
2024-06-25 11:36:12 -04:00
namespace Interpreter
2011-02-17 21:27:24 +00:00
{
2024-06-25 11:36:12 -04:00
namespace OpcodeImpl
{
extern int Dcache ;
void CACHE ()
2009-02-09 21:15:56 +00:00
{
2024-06-25 11:36:12 -04:00
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_);
2019-07-13 17:49:51 -07:00
2024-06-25 11:36:12 -04:00
switch ( _Rt_ )
{
case 0x1a : //DHIN (Data Cache Hit Invalidate)
doCacheHitOp ( addr , "DHIN" , []( CacheLine line ) {
line . clear ();
});
break ;
2019-07-13 17:49:51 -07:00
2024-06-25 11:36:12 -04:00
case 0x18 : //DHWBIN (Data Cache Hit WriteBack with Invalidate)
doCacheHitOp ( addr , "DHWBIN" , []( CacheLine line ) {
line . writeBackIfNeeded ();
line . clear ();
});
break ;
2019-07-13 17:49:51 -07:00
2024-06-25 11:36:12 -04:00
case 0x1c : //DHWOIN (Data Cache Hit WriteBack Without Invalidate)
doCacheHitOp ( addr , "DHWOIN" , []( CacheLine line ) {
line . writeBackIfNeeded ();
});
break ;
2009-02-09 21:15:56 +00:00
2024-06-25 11:36:12 -04:00
case 0x16 : //DXIN (Data Cache Index Invalidate)
{
const int index = cache . setIdxFor ( addr );
const int way = addr & 0x1 ;
CacheLine line = cache . lineAt ( index , way );
2010-04-25 00:31:27 +00:00
2024-06-25 11:36:12 -04:00
CACHE_LOG ( "CACHE DXIN addr %x, index %d, way %d, flag %x" , addr , index , way , line . tag . flags ());
2019-07-13 17:49:51 -07:00
2024-06-25 11:36:12 -04:00
line . clear ();
break ;
}
2019-07-13 17:49:51 -07:00
2024-06-25 11:36:12 -04:00
case 0x11 : //DXLDT (Data Cache Load Data into TagLo)
{
const int index = cache . setIdxFor ( addr );
const int way = addr & 0x1 ;
CacheLine line = cache . lineAt ( index , way );
2019-07-13 17:49:51 -07:00
2024-06-25 11:36:12 -04:00
cpuRegs . CP0 . n . TagLo = * reinterpret_cast < u32 *> ( & line . data . bytes [ addr & 0x3C ]);
2019-07-13 17:49:51 -07:00
2024-06-25 11:36:12 -04:00
CACHE_LOG ( "CACHE DXLDT addr %x, index %d, way %d, DATA %x OP %x" , addr , index , way , cpuRegs . CP0 . n . TagLo , cpuRegs . code );
break ;
}
2016-06-04 12:57:14 +01:00
2024-06-25 11:36:12 -04:00
case 0x10 : //DXLTG (Data Cache Load Tag into TagLo)
{
const int index = ( addr >> 6 ) & 0x3F ;
const int way = addr & 0x1 ;
CacheLine line = cache . lineAt ( index , way );
2019-07-13 17:49:51 -07:00
2024-06-25 11:36:12 -04: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 ();
2016-06-04 12:57:14 +01:00
2024-06-25 11:36:12 -04:00
// Our tags don't contain PS2 paddrs (instead they contain x86 addrs)
cpuRegs . CP0 . n . TagLo = line . tag . flags ();
2019-07-13 17:49:51 -07:00
2024-06-25 11:36:12 -04: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" );
break ;
}
2009-02-09 21:15:56 +00:00
2024-06-25 11:36:12 -04:00
case 0x13 : //DXSDT (Data Cache Store 32bits from TagLo)
{
const int index = ( addr >> 6 ) & 0x3F ;
const int way = addr & 0x1 ;
CacheLine line = cache . lineAt ( index , way );
2009-02-09 21:15:56 +00:00
2024-06-25 11:36:12 -04:00
* reinterpret_cast < u32 *> ( & line . data . bytes [ addr & 0x3C ]) = cpuRegs . CP0 . n . TagLo ;
2019-07-13 17:49:51 -07:00
2024-06-25 11:36:12 -04:00
CACHE_LOG ( "CACHE DXSDT addr %x, index %d, way %d, DATA %x OP %x" , addr , index , way , cpuRegs . CP0 . n . TagLo , cpuRegs . code );
break ;
}
2009-02-09 21:15:56 +00:00
2024-06-25 11:36:12 -04:00
case 0x12 : //DXSTG (Data Cache Store Tag from TagLo)
{
const int index = ( addr >> 6 ) & 0x3F ;
const int way = addr & 0x1 ;
CacheLine line = cache . lineAt ( index , way );
2020-10-17 04:31:26 -05:00
2026-07-26 15:14:02 +02:00
// TagLo carries a guest physical page. Our tags do not: they hold the
// host pointer the fill translated to (CacheLine::load stores `ppf`),
// which is what writeBackIfNeeded dereferences, so copying the guest
// word in raw aimed a 64-byte store at an address the guest chose --
// setAddr zeroes the top 32 bits, so somewhere below 4 GiB: an
// emulator crash normally, or a write into whatever happened to be
// mapped there. Translate it the way a fill does instead, through the
// KSEG0 alias of the physical page (Memory.cpp maps 0x80000000 onto
// physical 0), so the write-back lands at the physical address the
// guest named, and take isValidPFN from the same translation so the
// two cannot disagree. A tag that does not resolve to plain memory --
// an MMIO handler page, or a physical address that does not exist --
// is marked unbacked; the line still caches and reports its flags,
// and loses its data on eviction (see the comment on CacheTag).
2026-08-14 21:50:24 -07:00
//
// The lookup goes through the physical map, not through the KSEG0
// alias of the page: KSEG0 is only 512 MB wide, so routing a
// physical page through it meant masking the tag to 29 bits, and
// every page at or above 0x20000000 then folded into the low half
// of the map and resolved to whatever lives there. A page past the
// end of the map folded onto ordinary RAM and the write-back went
// into it. vtlb_GetPhyPtr covers the whole 1 GB physical map and
// answers null both for a handler page and for an address off the
// end of it.
2026-07-26 15:14:02 +02:00
const u32 pageTag = cpuRegs . CP0 . n . TagLo & ~ static_cast < u32 > ( CacheTag :: ALL_BITS );
2026-08-14 21:50:24 -07:00
void * const host = vtlb_GetPhyPtr ( pageTag );
const bool backed = host != nullptr ;
2026-07-26 15:14:02 +02:00
line . tag . setValidPFN ( backed );
2026-08-14 21:50:24 -07:00
line . tag . setAddr ( backed ? reinterpret_cast < uptr > ( host ) : static_cast < uptr > ( pageTag ));
2024-06-25 11:36:12 -04:00
line . tag . rawValue &= ~ CacheTag :: ALL_FLAGS ;
line . tag . rawValue |= ( cpuRegs . CP0 . n . TagLo & CacheTag :: ALL_FLAGS );
2019-07-13 17:49:51 -07:00
2024-06-25 11:36:12 -04:00
CACHE_LOG ( "CACHE DXSTG addr %x, index %d, way %d, DATA %x OP %x" , addr , index , way , cpuRegs . CP0 . n . TagLo , cpuRegs . code );
break ;
}
2011-02-17 21:27:24 +00:00
2024-06-25 11:36:12 -04:00
case 0x14 : //DXWBIN (Data Cache Index WriteBack Invalidate)
{
const int index = ( addr >> 6 ) & 0x3F ;
const int way = addr & 0x1 ;
CacheLine line = cache . lineAt ( index , way );
2019-07-13 17:49:51 -07:00
2024-06-25 11:36:12 -04: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 ();
break ;
}
2019-07-13 17:49:51 -07:00
2024-06-25 11:36:12 -04: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
2024-06-25 11:36:12 -04:00
case 0xC : //BFH (BTAC Flush)
{
//Not Implemented as we do not cache Branch Target Addresses.
break ;
}
2009-02-09 21:15:56 +00:00
2024-06-25 11:36:12 -04:00
default :
DevCon . Warning ( "Cache mode %x not implemented" , _Rt_ );
break ;
}
}
} // end namespace OpcodeImpl
} // namespace Interpreter
} // namespace R5900