sh4/mmu: Import the old mmu implementation from nullDC.
Reicast doesn't support exceptions yet, so this isn't of much use now, and is intended mostly as documentation. nullDC used some call stack hooking magic to handle exceptions, which was never generic and clean enough to be worth the effort to port to Reicast.
This commit is contained in:
parent
053ea61137
commit
dcd77326cc
|
@ -258,3 +258,17 @@ void Sh4_int_Term()
|
||||||
Sh4_int_Stop();
|
Sh4_int_Stop();
|
||||||
printf("Sh4 Term\n");
|
printf("Sh4 Term\n");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
bool sh4_exept_raised;
|
||||||
|
void sh4_int_RaiseExeption(u32 ExeptionCode, u32 VectorAddress)
|
||||||
|
{
|
||||||
|
sh4_exept_raised = true;
|
||||||
|
|
||||||
|
sh4_ex_ExeptionCode = ExeptionCode;
|
||||||
|
sh4_ex_VectorAddress = VectorAddress;
|
||||||
|
|
||||||
|
//save reg context
|
||||||
|
SaveSh4Regs(&sh4_ex_SRC);
|
||||||
|
}
|
||||||
|
*/
|
|
@ -1265,7 +1265,8 @@ INLINE void DYNACALL do_sqw(u32 Dest)
|
||||||
//Translate the SQ addresses as needed
|
//Translate the SQ addresses as needed
|
||||||
if (mmu_on)
|
if (mmu_on)
|
||||||
{
|
{
|
||||||
Address=mmu_TranslateSQW(Dest);
|
if (!mmu_TranslateSQW(Dest, &Address))
|
||||||
|
return;
|
||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
|
|
|
@ -7,15 +7,17 @@
|
||||||
|
|
||||||
#include "hw/mem/_vmem.h"
|
#include "hw/mem/_vmem.h"
|
||||||
|
|
||||||
//SQ fast remap , mainly hackish , assumes 1MB pages
|
|
||||||
//max 64MB can be remapped on SQ
|
|
||||||
u32 sq_remap[64];
|
|
||||||
|
|
||||||
TLB_Entry UTLB[64];
|
TLB_Entry UTLB[64];
|
||||||
TLB_Entry ITLB[4];
|
TLB_Entry ITLB[4];
|
||||||
|
|
||||||
|
#if defined(NO_MMU)
|
||||||
|
//SQ fast remap , mainly hackish , assumes 1MB pages
|
||||||
|
//max 64MB can be remapped on SQ
|
||||||
|
u32 sq_remap[64];
|
||||||
|
|
||||||
//Sync memory mapping to MMU , suspend compiled blocks if needed.entry is a UTLB entry # , -1 is for full sync
|
//Sync memory mapping to MMU , suspend compiled blocks if needed.entry is a UTLB entry # , -1 is for full sync
|
||||||
void UTLB_Sync(u32 entry)
|
bool UTLB_Sync(u32 entry)
|
||||||
{
|
{
|
||||||
if ((UTLB[entry].Address.VPN & (0xFC000000 >> 10)) == (0xE0000000 >> 10))
|
if ((UTLB[entry].Address.VPN & (0xFC000000 >> 10)) == (0xE0000000 >> 10))
|
||||||
{
|
{
|
||||||
|
@ -27,6 +29,8 @@ void UTLB_Sync(u32 entry)
|
||||||
{
|
{
|
||||||
printf("MEM remap %d : 0x%X to 0x%X\n", entry, UTLB[entry].Address.VPN << 10, UTLB[entry].Data.PPN << 10);
|
printf("MEM remap %d : 0x%X to 0x%X\n", entry, UTLB[entry].Address.VPN << 10, UTLB[entry].Data.PPN << 10);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
}
|
}
|
||||||
//Sync memory mapping to MMU, suspend compiled blocks if needed.entry is a ITLB entry # , -1 is for full sync
|
//Sync memory mapping to MMU, suspend compiled blocks if needed.entry is a ITLB entry # , -1 is for full sync
|
||||||
void ITLB_Sync(u32 entry)
|
void ITLB_Sync(u32 entry)
|
||||||
|
@ -48,3 +52,661 @@ void MMU_reset()
|
||||||
void MMU_term()
|
void MMU_term()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
#else
|
||||||
|
/*
|
||||||
|
MMU support code
|
||||||
|
This is mostly hacked-on as the core was never meant to have mmu support
|
||||||
|
|
||||||
|
There are two modes, one with 'full' mmu emulation (for wince/bleem/wtfever)
|
||||||
|
and a fast-hack mode for 1mb sqremaps (for katana)
|
||||||
|
|
||||||
|
defining NO_MMU disables the full mmu emulation
|
||||||
|
*/
|
||||||
|
#include "mmu.h"
|
||||||
|
#include "mmu_impl.h"
|
||||||
|
#include "hw/sh4/sh4_if.h"
|
||||||
|
#include "ccn.h"
|
||||||
|
#include "hw/sh4/sh4_interrupts.h"
|
||||||
|
#include "hw/sh4/sh4_if.h"
|
||||||
|
|
||||||
|
#include "hw/mem/_vmem.h"
|
||||||
|
|
||||||
|
#define printf_mmu
|
||||||
|
#define printf_win32 log
|
||||||
|
|
||||||
|
//SQ fast remap , mailny hackish , assumes 1 mb pages
|
||||||
|
//max 64 mb can be remapped on SQ
|
||||||
|
|
||||||
|
const u32 mmu_mask[4] =
|
||||||
|
{
|
||||||
|
((0xFFFFFFFF) >> 10) << 10, //1 kb page
|
||||||
|
((0xFFFFFFFF) >> 12) << 12, //4 kb page
|
||||||
|
((0xFFFFFFFF) >> 16) << 16, //64 kb page
|
||||||
|
((0xFFFFFFFF) >> 20) << 20 //1 MB page
|
||||||
|
};
|
||||||
|
|
||||||
|
const u32 fast_reg_lut[8] =
|
||||||
|
{
|
||||||
|
0, 0, 0, 0 //P0-U0
|
||||||
|
, 1 //P1
|
||||||
|
, 1 //P2
|
||||||
|
, 0 //P3
|
||||||
|
, 1 //P4
|
||||||
|
};
|
||||||
|
|
||||||
|
const u32 ITLB_LRU_OR[4] =
|
||||||
|
{
|
||||||
|
0x00,//000xxx
|
||||||
|
0x20,//1xx00x
|
||||||
|
0x14,//x1x1x0
|
||||||
|
0x0B,//xx1x11
|
||||||
|
};
|
||||||
|
const u32 ITLB_LRU_AND[4] =
|
||||||
|
{
|
||||||
|
0x07,//000xxx
|
||||||
|
0x39,//1xx00x
|
||||||
|
0x3E,//x1x1x0
|
||||||
|
0x3F,//xx1x11
|
||||||
|
};
|
||||||
|
u32 ITLB_LRU_USE[64];
|
||||||
|
|
||||||
|
//sync mem mapping to mmu , suspend compiled blocks if needed.entry is a UTLB entry # , -1 is for full sync
|
||||||
|
bool UTLB_Sync(u32 entry)
|
||||||
|
{
|
||||||
|
printf_mmu("UTLB MEM remap %d : 0x%X to 0x%X : %d\n", entry, UTLB[entry].Address.VPN << 10, UTLB[entry].Data.PPN << 10, UTLB[entry].Data.V);
|
||||||
|
if (UTLB[entry].Data.V == 0)
|
||||||
|
return true;
|
||||||
|
|
||||||
|
if ((UTLB[entry].Address.VPN & (0xFC000000 >> 10)) == (0xE0000000 >> 10))
|
||||||
|
{
|
||||||
|
#ifdef NO_MMU
|
||||||
|
u32 vpn_sq = ((UTLB[entry].Address.VPN & (0x3FFFFFF >> 10)) >> 10) & 0x3F;//upper bits are allways known [0xE0/E1/E2/E3]
|
||||||
|
sq_remap[vpn_sq] = UTLB[entry].Data.PPN << 10;
|
||||||
|
log("SQ remap %d : 0x%X to 0x%X\n", entry, UTLB[entry].Address.VPN << 10, UTLB[entry].Data.PPN << 10);
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
#ifdef NO_MMU
|
||||||
|
if ((UTLB[entry].Address.VPN&(0x1FFFFFFF >> 10)) == (UTLB[entry].Data.PPN&(0x1FFFFFFF >> 10)))
|
||||||
|
{
|
||||||
|
log("Static remap %d : 0x%X to 0x%X\n", entry, UTLB[entry].Address.VPN << 10, UTLB[entry].Data.PPN << 10);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
log("Dynamic remap %d : 0x%X to 0x%X\n", entry, UTLB[entry].Address.VPN << 10, UTLB[entry].Data.PPN << 10);
|
||||||
|
#endif
|
||||||
|
return false;//log("MEM remap %d : 0x%X to 0x%X\n",entry,UTLB[entry].Address.VPN<<10,UTLB[entry].Data.PPN<<10);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
//sync mem mapping to mmu , suspend compiled blocks if needed.entry is a ITLB entry # , -1 is for full sync
|
||||||
|
void ITLB_Sync(u32 entry)
|
||||||
|
{
|
||||||
|
printf_mmu("ITLB MEM remap %d : 0x%X to 0x%X : %d\n", entry, ITLB[entry].Address.VPN << 10, ITLB[entry].Data.PPN << 10, ITLB[entry].Data.V);
|
||||||
|
}
|
||||||
|
|
||||||
|
void RaiseException(u32 a, u32 b) {
|
||||||
|
msgboxf("Can't raise exceptions yet", MBX_ICONERROR);
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 mmu_error_TT;
|
||||||
|
void mmu_raise_exeption(u32 mmu_error, u32 address, u32 am)
|
||||||
|
{
|
||||||
|
printf_mmu("mmu_raise_exeption -> pc = 0x%X : ", next_pc);
|
||||||
|
CCN_TEA = address;
|
||||||
|
CCN_PTEH.VPN = address >> 10;
|
||||||
|
|
||||||
|
//save translation type error :)
|
||||||
|
mmu_error_TT = am;
|
||||||
|
|
||||||
|
switch (mmu_error)
|
||||||
|
{
|
||||||
|
//No error
|
||||||
|
case MMU_ERROR_NONE:
|
||||||
|
printf("Error : mmu_raise_exeption(MMU_ERROR_NONE)\n");
|
||||||
|
getc(stdin);
|
||||||
|
break;
|
||||||
|
|
||||||
|
//TLB miss
|
||||||
|
case MMU_ERROR_TLB_MISS:
|
||||||
|
printf_mmu("MMU_ERROR_UTLB_MISS 0x%X, handled\n", address);
|
||||||
|
if (am == MMU_TT_DWRITE) //WTLBMISS - Write Data TLB Miss Exception
|
||||||
|
RaiseException(0x60, 0x400);
|
||||||
|
else if (am == MMU_TT_DREAD) //RTLBMISS - Read Data TLB Miss Exception
|
||||||
|
RaiseException(0x40, 0x400);
|
||||||
|
else //ITLBMISS - Instruction TLB Miss Exception
|
||||||
|
RaiseException(0x40, 0x400);
|
||||||
|
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
|
||||||
|
//TLB Multyhit
|
||||||
|
case MMU_ERROR_TLB_MHIT:
|
||||||
|
printf("MMU_ERROR_TLB_MHIT @ 0x%X\n", address);
|
||||||
|
break;
|
||||||
|
|
||||||
|
//Mem is read/write protected (depends on translation type)
|
||||||
|
case MMU_ERROR_PROTECTED:
|
||||||
|
printf_mmu("MMU_ERROR_PROTECTED 0x%X, handled\n", address);
|
||||||
|
if (am == MMU_TT_DWRITE) //WRITEPROT - Write Data TLB Protection Violation Exception
|
||||||
|
RaiseException(0xC0, 0x100);
|
||||||
|
else if (am == MMU_TT_DREAD) //READPROT - Data TLB Protection Violation Exception
|
||||||
|
RaiseException(0xA0, 0x100);
|
||||||
|
else
|
||||||
|
{
|
||||||
|
verify(false);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
|
||||||
|
//Mem is write protected , firstwrite
|
||||||
|
case MMU_ERROR_FIRSTWRITE:
|
||||||
|
printf_mmu("MMU_ERROR_FIRSTWRITE\n");
|
||||||
|
verify(am == MMU_TT_DWRITE);
|
||||||
|
//FIRSTWRITE - Initial Page Write Exception
|
||||||
|
RaiseException(0x80, 0x100);
|
||||||
|
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
|
||||||
|
//data read/write missasligned
|
||||||
|
case MMU_ERROR_BADADDR:
|
||||||
|
if (am == MMU_TT_DWRITE) //WADDERR - Write Data Address Error
|
||||||
|
RaiseException(0x100, 0x100);
|
||||||
|
else if (am == MMU_TT_DREAD) //RADDERR - Read Data Address Error
|
||||||
|
RaiseException(0xE0, 0x100);
|
||||||
|
else //IADDERR - Instruction Address Error
|
||||||
|
{
|
||||||
|
printf("MMU_ERROR_BADADDR(i) 0x%X\n", address);
|
||||||
|
RaiseException(0xE0, 0x100);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
printf_mmu("MMU_ERROR_BADADDR(d) 0x%X, handled\n", address);
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
|
||||||
|
//Can't Execute
|
||||||
|
case MMU_ERROR_EXECPROT:
|
||||||
|
printf("MMU_ERROR_EXECPROT 0x%X\n", address);
|
||||||
|
|
||||||
|
//EXECPROT - Instruction TLB Protection Violation Exception
|
||||||
|
RaiseException(0xA0, 0x100);
|
||||||
|
return;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
__debugbreak();
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mmu_match(u32 va, CCN_PTEH_type Address, CCN_PTEL_type Data)
|
||||||
|
{
|
||||||
|
if (Data.V == 0)
|
||||||
|
return false;
|
||||||
|
|
||||||
|
u32 sz = Data.SZ1 * 2 + Data.SZ0;
|
||||||
|
u32 mask = mmu_mask[sz];
|
||||||
|
|
||||||
|
if ((((Address.VPN << 10)&mask) == (va&mask)))
|
||||||
|
{
|
||||||
|
bool asid_match = (Data.SH == 0) && ((sr.MD == 0) || (CCN_MMUCR.SV == 0));
|
||||||
|
|
||||||
|
if ((asid_match == false) || (Address.ASID == CCN_PTEH.ASID))
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
//Do a full lookup on the UTLB entry's
|
||||||
|
u32 mmu_full_lookup(u32 va, u32& idx, u32& rv)
|
||||||
|
{
|
||||||
|
CCN_MMUCR.URC++;
|
||||||
|
if (CCN_MMUCR.URB == CCN_MMUCR.URC)
|
||||||
|
CCN_MMUCR.URC = 0;
|
||||||
|
|
||||||
|
|
||||||
|
u32 entry = 0;
|
||||||
|
u32 nom = 0;
|
||||||
|
|
||||||
|
|
||||||
|
for (u32 i = 0; i<64; i++)
|
||||||
|
{
|
||||||
|
//verify(sz!=0);
|
||||||
|
if (mmu_match(va, UTLB[i].Address, UTLB[i].Data))
|
||||||
|
{
|
||||||
|
entry = i;
|
||||||
|
nom++;
|
||||||
|
u32 sz = UTLB[i].Data.SZ1 * 2 + UTLB[i].Data.SZ0;
|
||||||
|
u32 mask = mmu_mask[sz];
|
||||||
|
//VPN->PPN | low bits
|
||||||
|
rv = ((UTLB[i].Data.PPN << 10)&mask) | (va&(~mask));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (nom != 1)
|
||||||
|
{
|
||||||
|
if (nom)
|
||||||
|
{
|
||||||
|
return MMU_ERROR_TLB_MHIT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return MMU_ERROR_TLB_MISS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
idx = entry;
|
||||||
|
|
||||||
|
return MMU_ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Simple QACR translation for mmu (when AT is off)
|
||||||
|
u32 mmu_QACR_SQ(u32 va)
|
||||||
|
{
|
||||||
|
u32 QACR;
|
||||||
|
|
||||||
|
//default to sq 0
|
||||||
|
QACR = CCN_QACR_TR[0];
|
||||||
|
//sq1 ? if so use QACR1
|
||||||
|
if (va & 0x20)
|
||||||
|
QACR = CCN_QACR_TR[1];
|
||||||
|
va &= ~0x1f;
|
||||||
|
return QACR + va;
|
||||||
|
}
|
||||||
|
template<u32 translation_type>
|
||||||
|
u32 mmu_full_SQ(u32 va, u32& rv)
|
||||||
|
{
|
||||||
|
|
||||||
|
if ((va & 3) || (CCN_MMUCR.SQMD == 1 && sr.MD == 0))
|
||||||
|
{
|
||||||
|
//here, or after ?
|
||||||
|
return MMU_ERROR_BADADDR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (CCN_MMUCR.AT)
|
||||||
|
{
|
||||||
|
//Address=Dest&0xFFFFFFE0;
|
||||||
|
|
||||||
|
u32 entry;
|
||||||
|
u32 lookup = mmu_full_lookup(va, entry, rv);
|
||||||
|
|
||||||
|
rv &= ~31;//lower 5 bits are forced to 0
|
||||||
|
|
||||||
|
if (lookup != MMU_ERROR_NONE)
|
||||||
|
return lookup;
|
||||||
|
|
||||||
|
u32 md = UTLB[entry].Data.PR >> 1;
|
||||||
|
|
||||||
|
//Priv mode protection
|
||||||
|
if ((md == 0) && sr.MD == 0)
|
||||||
|
{
|
||||||
|
return MMU_ERROR_PROTECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
//Write Protection (Lock or FW)
|
||||||
|
if (translation_type == MMU_TT_DWRITE)
|
||||||
|
{
|
||||||
|
if ((UTLB[entry].Data.PR & 1) == 0)
|
||||||
|
return MMU_ERROR_PROTECTED;
|
||||||
|
else if (UTLB[entry].Data.D == 0)
|
||||||
|
return MMU_ERROR_FIRSTWRITE;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
rv = mmu_QACR_SQ(va);
|
||||||
|
}
|
||||||
|
return MMU_ERROR_NONE;
|
||||||
|
}
|
||||||
|
template<u32 translation_type>
|
||||||
|
u32 mmu_data_translation(u32 va, u32& rv)
|
||||||
|
{
|
||||||
|
//*opt notice* this could be only checked for writes, as reads are invalid
|
||||||
|
if ((va & 0xFC000000) == 0xE0000000)
|
||||||
|
{
|
||||||
|
u32 lookup = mmu_full_SQ<translation_type>(va, rv);
|
||||||
|
if (lookup != MMU_ERROR_NONE)
|
||||||
|
return lookup;
|
||||||
|
rv = va; //SQ writes are not translated, only write backs are.
|
||||||
|
return MMU_ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((sr.MD == 0) && (va & 0x80000000) != 0)
|
||||||
|
{
|
||||||
|
//if on kernel, and not SQ addr -> error
|
||||||
|
return MMU_ERROR_BADADDR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (sr.MD == 1 && ((va & 0xFC000000) == 0x7C000000))
|
||||||
|
{
|
||||||
|
rv = va;
|
||||||
|
return MMU_ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((CCN_MMUCR.AT == 0) || (fast_reg_lut[va >> 29] != 0))
|
||||||
|
{
|
||||||
|
rv = va;
|
||||||
|
return MMU_ERROR_NONE;
|
||||||
|
}
|
||||||
|
/*
|
||||||
|
if ( CCN_CCR.ORA && ((va&0xFC000000)==0x7C000000))
|
||||||
|
{
|
||||||
|
verify(false);
|
||||||
|
return va;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
u32 entry;
|
||||||
|
u32 lookup = mmu_full_lookup(va, entry, rv);
|
||||||
|
|
||||||
|
if (lookup != MMU_ERROR_NONE)
|
||||||
|
return lookup;
|
||||||
|
|
||||||
|
u32 md = UTLB[entry].Data.PR >> 1;
|
||||||
|
|
||||||
|
//0X & User mode-> protection violation
|
||||||
|
//Priv mode protection
|
||||||
|
if ((md == 0) && sr.MD == 0)
|
||||||
|
{
|
||||||
|
return MMU_ERROR_PROTECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
//X0 -> read olny
|
||||||
|
//X1 -> read/write , can be FW
|
||||||
|
|
||||||
|
//Write Protection (Lock or FW)
|
||||||
|
if (translation_type == MMU_TT_DWRITE)
|
||||||
|
{
|
||||||
|
if ((UTLB[entry].Data.PR & 1) == 0)
|
||||||
|
return MMU_ERROR_PROTECTED;
|
||||||
|
else if (UTLB[entry].Data.D == 0)
|
||||||
|
return MMU_ERROR_FIRSTWRITE;
|
||||||
|
}
|
||||||
|
return MMU_ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 mmu_instruction_translation(u32 va, u32& rv)
|
||||||
|
{
|
||||||
|
if ((sr.MD == 0) && (va & 0x80000000) != 0)
|
||||||
|
{
|
||||||
|
//if SQ disabled , or if if SQ on but out of SQ mem then BAD ADDR ;)
|
||||||
|
if (va >= 0xE0000000)
|
||||||
|
return MMU_ERROR_BADADDR;
|
||||||
|
}
|
||||||
|
|
||||||
|
if ((CCN_MMUCR.AT == 0) || (fast_reg_lut[va >> 29] != 0))
|
||||||
|
{
|
||||||
|
rv = va;
|
||||||
|
return MMU_ERROR_NONE;
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mmach = false;
|
||||||
|
retry_ITLB_Match:
|
||||||
|
u32 entry = 4;
|
||||||
|
u32 nom = 0;
|
||||||
|
for (u32 i = 0; i<4; i++)
|
||||||
|
{
|
||||||
|
if (ITLB[i].Data.V == 0)
|
||||||
|
continue;
|
||||||
|
u32 sz = ITLB[i].Data.SZ1 * 2 + ITLB[i].Data.SZ0;
|
||||||
|
u32 mask = mmu_mask[sz];
|
||||||
|
|
||||||
|
if ((((ITLB[i].Address.VPN << 10)&mask) == (va&mask)))
|
||||||
|
{
|
||||||
|
bool asid_match = (ITLB[i].Data.SH == 0) && ((sr.MD == 0) || (CCN_MMUCR.SV == 0));
|
||||||
|
|
||||||
|
if ((asid_match == false) || (ITLB[i].Address.ASID == CCN_PTEH.ASID))
|
||||||
|
{
|
||||||
|
//verify(sz!=0);
|
||||||
|
entry = i;
|
||||||
|
nom++;
|
||||||
|
//VPN->PPN | low bits
|
||||||
|
rv = ((ITLB[i].Data.PPN << 10)&mask) | (va&(~mask));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (entry == 4)
|
||||||
|
{
|
||||||
|
verify(mmach == false);
|
||||||
|
u32 lookup = mmu_full_lookup(va, entry, rv);
|
||||||
|
|
||||||
|
if (lookup != MMU_ERROR_NONE)
|
||||||
|
return lookup;
|
||||||
|
u32 replace_index = ITLB_LRU_USE[CCN_MMUCR.LRUI];
|
||||||
|
verify(replace_index != 0xFFFFFFFF);
|
||||||
|
ITLB[replace_index] = UTLB[entry];
|
||||||
|
entry = replace_index;
|
||||||
|
ITLB_Sync(entry);
|
||||||
|
mmach = true;
|
||||||
|
goto retry_ITLB_Match;
|
||||||
|
}
|
||||||
|
else if (nom != 1)
|
||||||
|
{
|
||||||
|
if (nom)
|
||||||
|
{
|
||||||
|
return MMU_ERROR_TLB_MHIT;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
return MMU_ERROR_TLB_MISS;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
CCN_MMUCR.LRUI &= ITLB_LRU_AND[entry];
|
||||||
|
CCN_MMUCR.LRUI |= ITLB_LRU_OR[entry];
|
||||||
|
|
||||||
|
u32 md = ITLB[entry].Data.PR >> 1;
|
||||||
|
|
||||||
|
//0X & User mode-> protection violation
|
||||||
|
//Priv mode protection
|
||||||
|
if ((md == 0) && sr.MD == 0)
|
||||||
|
{
|
||||||
|
return MMU_ERROR_PROTECTED;
|
||||||
|
}
|
||||||
|
|
||||||
|
return MMU_ERROR_NONE;
|
||||||
|
}
|
||||||
|
void MMU_init()
|
||||||
|
{
|
||||||
|
memset(ITLB_LRU_USE, 0xFF, sizeof(ITLB_LRU_USE));
|
||||||
|
for (u32 e = 0; e<4; e++)
|
||||||
|
{
|
||||||
|
u32 match_key = ((~ITLB_LRU_AND[e]) & 0x3F);
|
||||||
|
u32 match_mask = match_key | ITLB_LRU_OR[e];
|
||||||
|
for (u32 i = 0; i<64; i++)
|
||||||
|
{
|
||||||
|
if ((i & match_mask) == match_key)
|
||||||
|
{
|
||||||
|
verify(ITLB_LRU_USE[i] == 0xFFFFFFFF);
|
||||||
|
ITLB_LRU_USE[i] = e;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
void MMU_reset()
|
||||||
|
{
|
||||||
|
memset(UTLB, 0, sizeof(UTLB));
|
||||||
|
memset(ITLB, 0, sizeof(ITLB));
|
||||||
|
}
|
||||||
|
|
||||||
|
void MMU_term()
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
u8 DYNACALL mmu_ReadMem8(u32 adr)
|
||||||
|
{
|
||||||
|
u32 addr;
|
||||||
|
u32 tv = mmu_data_translation<MMU_TT_DREAD>(adr, addr);
|
||||||
|
if (tv == 0)
|
||||||
|
return _vmem_ReadMem8(addr);
|
||||||
|
else
|
||||||
|
mmu_raise_exeption(tv, adr, MMU_TT_DREAD);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u16 DYNACALL mmu_ReadMem16(u32 adr)
|
||||||
|
{
|
||||||
|
if (adr & 1)
|
||||||
|
{
|
||||||
|
mmu_raise_exeption(MMU_ERROR_BADADDR, adr, MMU_TT_DREAD);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
u32 addr;
|
||||||
|
u32 tv = mmu_data_translation<MMU_TT_DREAD>(adr, addr);
|
||||||
|
if (tv == 0)
|
||||||
|
return _vmem_ReadMem16(addr);
|
||||||
|
else
|
||||||
|
mmu_raise_exeption(tv, adr, MMU_TT_DREAD);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
u16 DYNACALL mmu_IReadMem16(u32 adr)
|
||||||
|
{
|
||||||
|
if (adr & 1)
|
||||||
|
{
|
||||||
|
mmu_raise_exeption(MMU_ERROR_BADADDR, adr, MMU_TT_IREAD);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
u32 addr;
|
||||||
|
u32 tv = mmu_instruction_translation(adr, addr);
|
||||||
|
if (tv == 0)
|
||||||
|
return _vmem_ReadMem16(addr);
|
||||||
|
else
|
||||||
|
mmu_raise_exeption(tv, adr, MMU_TT_IREAD);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
u32 DYNACALL mmu_ReadMem32(u32 adr)
|
||||||
|
{
|
||||||
|
if (adr & 3)
|
||||||
|
{
|
||||||
|
mmu_raise_exeption(MMU_ERROR_BADADDR, adr, MMU_TT_DREAD);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
u32 addr;
|
||||||
|
u32 tv = mmu_data_translation<MMU_TT_DREAD>(adr, addr);
|
||||||
|
if (tv == 0)
|
||||||
|
return _vmem_ReadMem32(addr);
|
||||||
|
else
|
||||||
|
mmu_raise_exeption(tv, adr, MMU_TT_DREAD);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
u64 DYNACALL mmu_ReadMem64(u32 adr)
|
||||||
|
{
|
||||||
|
if (adr & 7)
|
||||||
|
{
|
||||||
|
mmu_raise_exeption(MMU_ERROR_BADADDR, adr, MMU_TT_DREAD);
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
u32 addr;
|
||||||
|
u32 tv = mmu_data_translation<MMU_TT_DREAD>(adr, addr);
|
||||||
|
if (tv == 0)
|
||||||
|
{
|
||||||
|
return _vmem_ReadMem64(addr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mmu_raise_exeption(tv, adr, MMU_TT_DREAD);
|
||||||
|
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
void DYNACALL mmu_WriteMem8(u32 adr, u8 data)
|
||||||
|
{
|
||||||
|
u32 addr;
|
||||||
|
u32 tv = mmu_data_translation<MMU_TT_DWRITE>(adr, addr);
|
||||||
|
if (tv == 0)
|
||||||
|
{
|
||||||
|
_vmem_WriteMem8(addr, data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mmu_raise_exeption(tv, adr, MMU_TT_DWRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
void DYNACALL mmu_WriteMem16(u32 adr, u16 data)
|
||||||
|
{
|
||||||
|
if (adr & 1)
|
||||||
|
{
|
||||||
|
mmu_raise_exeption(MMU_ERROR_BADADDR, adr, MMU_TT_DWRITE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
u32 addr;
|
||||||
|
u32 tv = mmu_data_translation<MMU_TT_DWRITE>(adr, addr);
|
||||||
|
if (tv == 0)
|
||||||
|
{
|
||||||
|
_vmem_WriteMem16(addr, data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mmu_raise_exeption(tv, adr, MMU_TT_DWRITE);
|
||||||
|
}
|
||||||
|
void DYNACALL mmu_WriteMem32(u32 adr, u32 data)
|
||||||
|
{
|
||||||
|
if (adr & 3)
|
||||||
|
{
|
||||||
|
mmu_raise_exeption(MMU_ERROR_BADADDR, adr, MMU_TT_DWRITE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
u32 addr;
|
||||||
|
u32 tv = mmu_data_translation<MMU_TT_DWRITE>(adr, addr);
|
||||||
|
if (tv == 0)
|
||||||
|
{
|
||||||
|
_vmem_WriteMem32(addr, data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mmu_raise_exeption(tv, adr, MMU_TT_DWRITE);
|
||||||
|
}
|
||||||
|
void DYNACALL mmu_WriteMem64(u32 adr, u64 data)
|
||||||
|
{
|
||||||
|
if (adr & 7)
|
||||||
|
{
|
||||||
|
mmu_raise_exeption(MMU_ERROR_BADADDR, adr, MMU_TT_DWRITE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
u32 addr;
|
||||||
|
u32 tv = mmu_data_translation<MMU_TT_DWRITE>(adr, addr);
|
||||||
|
if (tv == 0)
|
||||||
|
{
|
||||||
|
_vmem_WriteMem64(addr, data);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
mmu_raise_exeption(tv, adr, MMU_TT_DWRITE);
|
||||||
|
}
|
||||||
|
|
||||||
|
bool mmu_TranslateSQW(u32 adr, u32* out)
|
||||||
|
{
|
||||||
|
#ifndef NO_MMU
|
||||||
|
|
||||||
|
u32 addr;
|
||||||
|
u32 tv = mmu_full_SQ<MMU_TT_DREAD>(adr, addr);
|
||||||
|
if (tv != 0)
|
||||||
|
{
|
||||||
|
mmu_raise_exeption(tv, adr, MMU_TT_DREAD);
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
*out = addr;
|
||||||
|
#else
|
||||||
|
//This will olny work for 1 mb pages .. hopefully nothing else is used
|
||||||
|
//*FIXME* to work for all page sizes ?
|
||||||
|
|
||||||
|
if (CCN_MMUCR.AT == 0)
|
||||||
|
{ //simple translation
|
||||||
|
*out = mmu_QACR_SQ(adr);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{ //remap table
|
||||||
|
*out = sq_remap[(adr >> 20) & 0x3F] | (adr & 0xFFFE0);
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#endif
|
|
@ -13,7 +13,25 @@ extern TLB_Entry ITLB[4];
|
||||||
extern u32 sq_remap[64];
|
extern u32 sq_remap[64];
|
||||||
|
|
||||||
//These are working only for SQ remaps on ndce
|
//These are working only for SQ remaps on ndce
|
||||||
void UTLB_Sync(u32 entry);
|
bool UTLB_Sync(u32 entry);
|
||||||
void ITLB_Sync(u32 entry);
|
void ITLB_Sync(u32 entry);
|
||||||
|
|
||||||
#define mmu_TranslateSQW(adr) (sq_remap[(adr>>20)&0x3F] | (adr & 0xFFFE0))
|
#if defined(NO_MMU)
|
||||||
|
bool inline mmu_TranslateSQW(u32 addr, u32* mapped) {
|
||||||
|
*mapped = sq_remap[(addr>>20)&0x3F] | (addr & 0xFFFE0);
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
#else
|
||||||
|
u8 DYNACALL mmu_ReadMem8(u32 addr);
|
||||||
|
u16 DYNACALL mmu_ReadMem16(u32 addr);
|
||||||
|
u16 DYNACALL mmu_IReadMem16(u32 addr);
|
||||||
|
u32 DYNACALL mmu_ReadMem32(u32 addr);
|
||||||
|
u64 DYNACALL mmu_ReadMem64(u32 addr);
|
||||||
|
|
||||||
|
void DYNACALL mmu_WriteMem8(u32 addr, u8 data);
|
||||||
|
void DYNACALL mmu_WriteMem16(u32 addr, u16 data);
|
||||||
|
void DYNACALL mmu_WriteMem32(u32 addr, u32 data);
|
||||||
|
void DYNACALL mmu_WriteMem64(u32 addr, u64 data);
|
||||||
|
|
||||||
|
bool mmu_TranslateSQW(u32 addr, u32* mapped);
|
||||||
|
#endif
|
||||||
|
|
|
@ -0,0 +1,37 @@
|
||||||
|
#pragma once
|
||||||
|
#include "types.h"
|
||||||
|
#include "ccn.h"
|
||||||
|
#include "mmu.h"
|
||||||
|
|
||||||
|
|
||||||
|
//Do a full lookup on the UTLB entry's
|
||||||
|
//Return Values
|
||||||
|
//Translation was sucessfull , rv contains return
|
||||||
|
#define MMU_ERROR_NONE 0
|
||||||
|
//TLB miss
|
||||||
|
#define MMU_ERROR_TLB_MISS 1
|
||||||
|
//TLB Multyhit
|
||||||
|
#define MMU_ERROR_TLB_MHIT 2
|
||||||
|
//Mem is read/write protected (depends on translation type)
|
||||||
|
#define MMU_ERROR_PROTECTED 3
|
||||||
|
//Mem is write protected , firstwrite
|
||||||
|
#define MMU_ERROR_FIRSTWRITE 4
|
||||||
|
//data-Opcode read/write missasligned
|
||||||
|
#define MMU_ERROR_BADADDR 5
|
||||||
|
//Can't Execute
|
||||||
|
#define MMU_ERROR_EXECPROT 6
|
||||||
|
|
||||||
|
//Translation Types
|
||||||
|
//Opcode read
|
||||||
|
#define MMU_TT_IREAD 0
|
||||||
|
//Data write
|
||||||
|
#define MMU_TT_DWRITE 1
|
||||||
|
//Data write
|
||||||
|
#define MMU_TT_DREAD 2
|
||||||
|
//Do an mmu lookup for va , returns translation status , if MMU_ERROR_NONE , rv is set to translated index
|
||||||
|
|
||||||
|
extern u32 mmu_error_TT;
|
||||||
|
|
||||||
|
void MMU_Init();
|
||||||
|
void MMU_Reset(bool Manual);
|
||||||
|
void MMU_Term();
|
|
@ -172,6 +172,7 @@ bool Do_Exeption(u32 epc, u32 expEvn, u32 CallVect)
|
||||||
|
|
||||||
next_pc = vbr + CallVect;
|
next_pc = vbr + CallVect;
|
||||||
|
|
||||||
|
printf("RaiseException: from %08X , pc errh %08X\n", spc, epc);
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -21,6 +21,7 @@ extern VArray2 mem_b;
|
||||||
#define WriteMem64 _vmem_WriteMem64
|
#define WriteMem64 _vmem_WriteMem64
|
||||||
//#define WriteMem64(addr,reg) { _vmem_WriteMem32(addr,((u32*)reg)[0]);_vmem_WriteMem32((addr)+4, ((u32*)reg)[1]); }
|
//#define WriteMem64(addr,reg) { _vmem_WriteMem32(addr,((u32*)reg)[0]);_vmem_WriteMem32((addr)+4, ((u32*)reg)[1]); }
|
||||||
#else
|
#else
|
||||||
|
#include "modules/mmu.h"
|
||||||
#define ReadMem8 mmu_ReadMem8
|
#define ReadMem8 mmu_ReadMem8
|
||||||
#define ReadMem16 mmu_ReadMem16
|
#define ReadMem16 mmu_ReadMem16
|
||||||
#define IReadMem16 mmu_IReadMem16
|
#define IReadMem16 mmu_IReadMem16
|
||||||
|
|
|
@ -288,8 +288,8 @@
|
||||||
<ClInclude Include="..\core\hw\sh4\interpr\sh4_opcodes.h" />
|
<ClInclude Include="..\core\hw\sh4\interpr\sh4_opcodes.h" />
|
||||||
<ClInclude Include="..\core\hw\sh4\modules\ccn.h" />
|
<ClInclude Include="..\core\hw\sh4\modules\ccn.h" />
|
||||||
<ClInclude Include="..\core\hw\sh4\modules\dmac.h" />
|
<ClInclude Include="..\core\hw\sh4\modules\dmac.h" />
|
||||||
<ClInclude Include="..\core\hw\sh4\modules\intc.h" />
|
|
||||||
<ClInclude Include="..\core\hw\sh4\modules\mmu.h" />
|
<ClInclude Include="..\core\hw\sh4\modules\mmu.h" />
|
||||||
|
<ClInclude Include="..\core\hw\sh4\modules\mmu_impl.h" />
|
||||||
<ClInclude Include="..\core\hw\sh4\modules\modules.h" />
|
<ClInclude Include="..\core\hw\sh4\modules\modules.h" />
|
||||||
<ClInclude Include="..\core\hw\sh4\modules\tmu.h" />
|
<ClInclude Include="..\core\hw\sh4\modules\tmu.h" />
|
||||||
<ClInclude Include="..\core\hw\sh4\sh4_if.h" />
|
<ClInclude Include="..\core\hw\sh4\sh4_if.h" />
|
||||||
|
|
|
@ -775,9 +775,6 @@
|
||||||
<ClInclude Include="..\core\hw\sh4\modules\dmac.h">
|
<ClInclude Include="..\core\hw\sh4\modules\dmac.h">
|
||||||
<Filter>hw\sh4\modules</Filter>
|
<Filter>hw\sh4\modules</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
<ClInclude Include="..\core\hw\sh4\modules\intc.h">
|
|
||||||
<Filter>hw\sh4\modules</Filter>
|
|
||||||
</ClInclude>
|
|
||||||
<ClInclude Include="..\core\hw\sh4\modules\mmu.h">
|
<ClInclude Include="..\core\hw\sh4\modules\mmu.h">
|
||||||
<Filter>hw\sh4\modules</Filter>
|
<Filter>hw\sh4\modules</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
@ -946,6 +943,9 @@
|
||||||
<ClInclude Include="..\core\cfg\ini.h">
|
<ClInclude Include="..\core\cfg\ini.h">
|
||||||
<Filter>cfg</Filter>
|
<Filter>cfg</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\core\hw\sh4\modules\mmu_impl.h">
|
||||||
|
<Filter>hw\sh4\modules</Filter>
|
||||||
|
</ClInclude>
|
||||||
</ItemGroup>
|
</ItemGroup>
|
||||||
<ItemGroup>
|
<ItemGroup>
|
||||||
<None Include="..\core\deps\zlib\Makefile">
|
<None Include="..\core\deps\zlib\Makefile">
|
||||||
|
|
Loading…
Reference in New Issue