mmu: add address cache to mem slow path. better fastmmu hashtable.

mmu: add address cache to Read/WriteMemNoEx
fastmmu: ignore 1k pages. optimize hashtable
get rid of NO_MMU
This commit is contained in:
Flyinghead 2021-05-14 19:03:57 +02:00
parent 23f483cd01
commit 1464c02903
21 changed files with 222 additions and 484 deletions

View File

@ -617,7 +617,6 @@ target_sources(${PROJECT_NAME} PRIVATE
core/hw/sh4/modules/intc.cpp
core/hw/sh4/modules/mmu.cpp
core/hw/sh4/modules/mmu.h
core/hw/sh4/modules/mmu_impl.h
core/hw/sh4/modules/modules.h
core/hw/sh4/modules/rtc.cpp
core/hw/sh4/modules/serial.cpp

View File

@ -115,7 +115,6 @@
*/
//#define NO_MMU
//#define STRICT_MODE
#ifndef STRICT_MODE
#define FAST_MMU

View File

@ -217,7 +217,6 @@ static u32 vmem32_paddr_to_offset(u32 address)
static u32 vmem32_map_mmu(u32 address, bool write)
{
#ifndef NO_MMU
u32 pa;
const TLB_Entry *entry;
u32 rc = mmu_full_lookup<false>(address, &entry, pa);
@ -307,9 +306,7 @@ static u32 vmem32_map_mmu(u32 address, bool write)
return MMU_ERROR_NONE;
}
#else
u32 rc = MMU_ERROR_PROTECTED;
#endif
return rc;
}
@ -335,7 +332,7 @@ static u32 vmem32_map_address(u32 address, bool write)
return VMEM32_ERROR_NOT_MAPPED;
}
#if !defined(NO_MMU) && defined(HOST_64BIT_CPU)
#if defined(HOST_64BIT_CPU)
// returns:
// 0 if the fault address isn't handled by the mmu
// 1 if the fault was handled and the access should be reattempted

View File

@ -53,59 +53,53 @@ static DynarecCodeEntryPtr DYNACALL bm_GetCode(u32 addr)
// This returns an executable address
DynarecCodeEntryPtr DYNACALL bm_GetCodeByVAddr(u32 addr)
{
#ifndef NO_MMU
if (!mmu_enabled())
#endif
return bm_GetCode(addr);
#ifndef NO_MMU
else
if (addr & 1)
{
if (addr & 1)
switch (addr)
{
switch (addr)
{
#ifdef USE_WINCE_HACK
case 0xfffffde7: // GetTickCount
// This should make this syscall faster
r[0] = sh4_sched_now64() * 1000 / SH4_MAIN_CLOCK;
next_pc = pr;
break;
case 0xfffffde7: // GetTickCount
// This should make this syscall faster
r[0] = sh4_sched_now64() * 1000 / SH4_MAIN_CLOCK;
next_pc = pr;
break;
case 0xfffffd05: // QueryPerformanceCounter(u64 *)
case 0xfffffd05: // QueryPerformanceCounter(u64 *)
{
u32 paddr;
if (mmu_data_translation<MMU_TT_DWRITE, u64>(r[4], paddr) == MMU_ERROR_NONE)
{
u32 paddr;
if (mmu_data_translation<MMU_TT_DWRITE, u64>(r[4], paddr) == MMU_ERROR_NONE)
{
_vmem_WriteMem64(paddr, sh4_sched_now64() >> 4);
r[0] = 1;
next_pc = pr;
}
else
{
Do_Exception(addr, 0xE0, 0x100);
}
_vmem_WriteMem64(paddr, sh4_sched_now64() >> 4);
r[0] = 1;
next_pc = pr;
}
else
{
Do_Exception(addr, 0xE0, 0x100);
}
break;
#endif
default:
Do_Exception(addr, 0xE0, 0x100);
break;
}
addr = next_pc;
}
u32 paddr;
u32 rv = mmu_instruction_translation(addr, paddr);
if (rv != MMU_ERROR_NONE)
{
DoMMUException(addr, rv, MMU_TT_IREAD);
mmu_instruction_translation(next_pc, paddr);
}
return bm_GetCode(paddr);
}
break;
#endif
default:
Do_Exception(addr, 0xE0, 0x100);
break;
}
addr = next_pc;
}
u32 paddr;
u32 rv = mmu_instruction_translation(addr, paddr);
if (rv != MMU_ERROR_NONE)
{
DoMMUException(addr, rv, MMU_TT_IREAD);
mmu_instruction_translation(next_pc, paddr);
}
return bm_GetCode(paddr);
}
// addr must be a physical address
@ -691,20 +685,16 @@ void print_blocks()
{
gcode=op->guest_offs;
u32 rpc=blk->vaddr+gcode;
#ifndef NO_MMU
try {
#endif
u16 op=IReadMem16(rpc);
char temp[128];
OpDesc[op]->Disassemble(temp,rpc,op);
fprintf(f,"//g: %04X %s\n", op, temp);
#ifndef NO_MMU
} catch (SH4ThrownException& ex) {
fprintf(f,"//g: ???? (page fault)\n");
}
#endif
}
std::string s = op->dissasm();

View File

@ -169,18 +169,14 @@ bool RuntimeBlockInfo::Setup(u32 rpc,fpscr_t rfpu_cfg)
oplist.clear();
#if !defined(NO_MMU)
try {
#endif
if (!dec_DecodeBlock(this, SH4_TIMESLICE / 2))
return false;
#if !defined(NO_MMU)
}
catch (SH4ThrownException& ex) {
Do_Exception(rpc, ex.expEvn, ex.callVect);
return false;
}
#endif
SetProtectedFlags();
AnalyseBlock(this);

View File

@ -46,11 +46,7 @@
#include "oslib/host_context.h"
#define CODE_SIZE (10*1024*1024)
#ifdef NO_MMU
#define TEMP_CODE_SIZE (0)
#else
#define TEMP_CODE_SIZE (1024*1024)
#endif
// When NO_RWX is enabled there's two address-spaces, one executable and
// one writtable. The emitter and most of the code in rec-* will work with

View File

@ -47,9 +47,7 @@ static void Sh4_int_Run()
try {
do
{
#if !defined(NO_MMU)
try {
#endif
do
{
u32 op = ReadNexOp();
@ -58,13 +56,10 @@ static void Sh4_int_Run()
} while (l > 0);
l += SH4_TIMESLICE;
UpdateSystem_INTC();
#if !defined(NO_MMU)
}
catch (SH4ThrownException& ex) {
} catch (const SH4ThrownException& ex) {
Do_Exception(ex.epc, ex.expEvn, ex.callVect);
l -= CPU_RATIO * 5; // an exception requires the instruction pipeline to drain, so approx 5 cycles
}
#endif
} while (sh4_int_bCpuRun);
} catch (const debugger::Stop& e) {
}
@ -83,18 +78,11 @@ static void Sh4_int_Step()
RestoreHostRoundingMode();
try {
#if !defined(NO_MMU)
try {
#endif
u32 op = ReadNexOp();
ExecuteOpcode(op);
#if !defined(NO_MMU)
}
catch (SH4ThrownException& ex) {
Do_Exception(ex.epc, ex.expEvn, ex.callVect);
l -= CPU_RATIO * 5; // an exception requires the instruction pipeline to drain, so approx 5 cycles
}
#endif
u32 op = ReadNexOp();
ExecuteOpcode(op);
} catch (const SH4ThrownException& ex) {
Do_Exception(ex.epc, ex.expEvn, ex.callVect);
l -= CPU_RATIO * 5; // an exception requires the instruction pipeline to drain, so approx 5 cycles
} catch (const debugger::Stop& e) {
}
}
@ -134,37 +122,26 @@ static bool Sh4_int_IsCpuRunning()
//TODO : Check for valid delayslot instruction
void ExecuteDelayslot()
{
#if !defined(NO_MMU)
try {
#endif
u32 op = ReadNexOp();
ExecuteOpcode(op);
#if !defined(NO_MMU)
}
catch (SH4ThrownException& ex) {
} catch (SH4ThrownException& ex) {
AdjustDelaySlotException(ex);
throw ex;
}
catch (const debugger::Stop& e) {
} catch (const debugger::Stop& e) {
next_pc -= 2; // break on previous instruction
throw e;
}
#endif
}
void ExecuteDelayslot_RTE()
{
#if !defined(NO_MMU)
try {
#endif
ExecuteDelayslot();
#if !defined(NO_MMU)
}
catch (SH4ThrownException& ex) {
} catch (const SH4ThrownException& ex) {
ERROR_LOG(INTERPRETER, "Exception in RTE delay slot");
}
#endif
}
// every SH4_TIMESLICE cycles

View File

@ -2018,18 +2018,11 @@ sh4op(i0100_nnnn_0000_1110)
UpdateINTC();
}
//Not implt
sh4op(iNotImplemented)
{
#ifndef NO_MMU
INFO_LOG(INTERPRETER, "iNimp %04X", op);
debugger::debugTrap(0x180);
SH4ThrownException ex = { next_pc - 2, 0x180, 0x100 };
SH4ThrownException ex { next_pc - 2, 0x180, 0x100 };
throw ex;
#else
cpu_iNimp(op, "Unknown opcode");
#endif
}

View File

@ -26,7 +26,6 @@
#include "hw/mem/_vmem.h"
#include "mmu_impl.h"
#include "ccn.h"
#include "hw/sh4/sh4_mem.h"
@ -38,7 +37,7 @@ extern u32 sq_remap[64];
#include "wince.h"
const TLB_Entry *lru_entry = NULL;
static TLB_Entry const *lru_entry;
static u32 lru_mask;
static u32 lru_address;
@ -46,22 +45,25 @@ struct TLB_LinkedEntry {
TLB_Entry entry;
TLB_LinkedEntry *next_entry;
};
#define NBUCKETS 65536
#define NBUCKETS 4096
static TLB_LinkedEntry full_table[65536];
static u32 full_table_size;
static TLB_LinkedEntry *entry_buckets[NBUCKETS];
static u16 bucket_index(u32 address, int size)
static u16 bucket_index(u32 address, int size, u32 asid)
{
return ((address >> 16) ^ ((address & 0xFC00) | size)) & (NBUCKETS - 1);
return ((address >> 20) ^ (address >> 12) ^ (address | asid | (size << 8))) & (NBUCKETS - 1);
}
static void cache_entry(const TLB_Entry &entry)
{
if (entry.Data.SZ0 == 0 && entry.Data.SZ1 == 0)
return;
verify(full_table_size < ARRAY_SIZE(full_table));
u16 bucket = bucket_index(entry.Address.VPN << 10, entry.Data.SZ1 * 2 + entry.Data.SZ0);
full_table[full_table_size].entry = entry;
u16 bucket = bucket_index(entry.Address.VPN << 10, entry.Data.SZ1 * 2 + entry.Data.SZ0, entry.Address.ASID);
full_table[full_table_size].next_entry = entry_buckets[bucket];
entry_buckets[bucket] = &full_table[full_table_size];
full_table_size++;
@ -80,7 +82,7 @@ bool find_entry_by_page_size(u32 address, const TLB_Entry **ret_entry)
size == 2 ? 6 :
size == 3 ? 10 : 0;
u32 vpn = (address >> (10 + shift)) << shift;
u16 bucket = bucket_index(vpn << 10, size);
u16 bucket = bucket_index(vpn << 10, size, CCN_PTEH.ASID);
TLB_LinkedEntry *pEntry = entry_buckets[bucket];
while (pEntry != NULL)
{
@ -109,9 +111,6 @@ static bool find_entry(u32 address, const TLB_Entry **ret_entry)
// 1m
if (find_entry_by_page_size<3>(address, ret_entry))
return true;
// 1k
if (find_entry_by_page_size<0>(address, ret_entry))
return true;
return false;
}
@ -184,11 +183,13 @@ bool UTLB_Sync(u32 entry)
TLB_Entry& tlb_entry = UTLB[entry];
u32 sz = tlb_entry.Data.SZ1 * 2 + tlb_entry.Data.SZ0;
tlb_entry.Address.VPN &= mmu_mask[sz] >> 10;
tlb_entry.Data.PPN &= mmu_mask[sz] >> 10;
lru_entry = &tlb_entry;
lru_mask = mmu_mask[sz];
lru_address = (tlb_entry.Address.VPN << 10) & lru_mask;
lru_address = tlb_entry.Address.VPN << 10;
tlb_entry.Address.VPN = lru_address >> 10;
cache_entry(tlb_entry);
if (!mmu_enabled() && (tlb_entry.Address.VPN & (0xFC000000 >> 10)) == (0xE0000000 >> 10))
@ -217,44 +218,47 @@ u32 mmu_full_lookup(u32 va, const TLB_Entry** tlb_entry_ret, u32& rv)
/*|| (sr.MD == 1 && CCN_MMUCR.SV == 1)*/)) // SV=1 not handled
{
//VPN->PPN | low bits
// TODO mask off PPN when updating TLB to avoid doing it at look up time
rv = ((lru_entry->Data.PPN << 10) & lru_mask) | (va & (~lru_mask));
*tlb_entry_ret = lru_entry;
rv = (lru_entry->Data.PPN << 10) | (va & ~lru_mask);
if (tlb_entry_ret != nullptr)
*tlb_entry_ret = lru_entry;
return MMU_ERROR_NONE;
}
}
const TLB_Entry *localEntry;
if (tlb_entry_ret == nullptr)
tlb_entry_ret = &localEntry;
if (find_entry(va, tlb_entry_ret))
{
u32 mask = mmu_mask[(*tlb_entry_ret)->Data.SZ1 * 2 + (*tlb_entry_ret)->Data.SZ0];
rv = (((*tlb_entry_ret)->Data.PPN << 10) & mask) | (va & (~mask));
rv = ((*tlb_entry_ret)->Data.PPN << 10) | (va & ~mask);
lru_entry = *tlb_entry_ret;
lru_mask = mask;
lru_address = ((*tlb_entry_ret)->Address.VPN << 10);
return MMU_ERROR_NONE;
}
#ifdef USE_WINCE_HACK
// WinCE hack
TLB_Entry entry;
TLB_Entry& entry = UTLB[CCN_MMUCR.URC];
if (wince_resolve_address(va, entry))
{
CCN_PTEL.reg_data = entry.Data.reg_data;
CCN_PTEA.reg_data = entry.Assistance.reg_data;
CCN_PTEH.reg_data = entry.Address.reg_data;
UTLB[CCN_MMUCR.URC] = entry;
*tlb_entry_ret = &UTLB[CCN_MMUCR.URC];
lru_entry = *tlb_entry_ret;
lru_entry = *tlb_entry_ret = &entry;
u32 sz = lru_entry->Data.SZ1 * 2 + lru_entry->Data.SZ0;
u32 sz = entry.Data.SZ1 * 2 + entry.Data.SZ0;
lru_mask = mmu_mask[sz];
lru_address = va & lru_mask;
lru_address = va & mmu_mask[sz];
entry.Data.PPN &= mmu_mask[sz] >> 10;
rv = ((lru_entry->Data.PPN << 10) & lru_mask) | (va & (~lru_mask));
rv = (entry.Data.PPN << 10) | (va & ~mmu_mask[sz]);
cache_entry(*lru_entry);
cache_entry(entry);
return MMU_ERROR_NONE;
}
@ -267,10 +271,7 @@ template u32 mmu_full_lookup<false>(u32 va, const TLB_Entry** tlb_entry_ret, u32
template<u32 translation_type>
u32 mmu_full_SQ(u32 va, u32& rv)
{
//Address=Dest&0xFFFFFFE0;
const TLB_Entry *entry;
u32 lookup = mmu_full_lookup(va, &entry, rv);
u32 lookup = mmu_full_lookup(va, nullptr, rv);
if (lookup != MMU_ERROR_NONE)
return lookup;
@ -285,18 +286,10 @@ template u32 mmu_full_SQ<MMU_TT_DWRITE>(u32 va, u32& rv);
template<u32 translation_type, typename T>
u32 mmu_data_translation(u32 va, u32& rv)
{
if (va & (sizeof(T) - 1))
if (fast_reg_lut[va >> 29] != 0)
{
return MMU_ERROR_BADADDR;
}
if (translation_type == MMU_TT_DWRITE)
{
if ((va & 0xFC000000) == 0xE0000000)
{
rv = va; //SQ writes are not translated, only write backs are.
return MMU_ERROR_NONE;
}
rv = va;
return MMU_ERROR_NONE;
}
if ((va & 0xFC000000) == 0x7C000000)
@ -306,14 +299,7 @@ u32 mmu_data_translation(u32 va, u32& rv)
return MMU_ERROR_NONE;
}
if (fast_reg_lut[va >> 29] != 0)
{
rv = va;
return MMU_ERROR_NONE;
}
const TLB_Entry *entry;
u32 lookup = mmu_full_lookup(va, &entry, rv);
u32 lookup = mmu_full_lookup(va, nullptr, rv);
if (lookup == MMU_ERROR_NONE && (rv & 0x1C000000) == 0x1C000000)
// map 1C000000-1FFFFFFF to P4 memory-mapped registers
rv |= 0xF0000000;
@ -342,7 +328,9 @@ template u32 mmu_data_translation<MMU_TT_DWRITE, u64>(u32 va, u32& rv);
void mmu_flush_table()
{
lru_entry = NULL;
lru_entry = nullptr;
flush_cache();
lastVAddr[0] = 1;
lastVAddr[1] = 1;
}
#endif // FAST_MMU

View File

@ -13,60 +13,14 @@ u32 ITLB_LRU_USE[64];
// Used when FullMMU is off
u32 sq_remap[64];
#if defined(NO_MMU)
//Sync memory mapping to MMU , suspend compiled blocks if needed.entry is a UTLB entry # , -1 is for full sync
bool UTLB_Sync(u32 entry)
{
if ((UTLB[entry].Address.VPN & (0xFC000000 >> 10)) == (0xE0000000 >> 10))
{
u32 vpn_sq = ((UTLB[entry].Address.VPN & 0x7FFFF) >> 10) & 0x3F;//upper bits are always known [0xE0/E1/E2/E3]
sq_remap[vpn_sq] = UTLB[entry].Data.PPN << 10;
INFO_LOG(SH4, "SQ remap %d : 0x%X to 0x%X", entry, UTLB[entry].Address.VPN << 10, UTLB[entry].Data.PPN << 10);
}
else
{
INFO_LOG(SH4, "MEM remap %d : 0x%X to 0x%X", 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
void ITLB_Sync(u32 entry)
{
INFO_LOG(SH4, "ITLB MEM remap %d : 0x%X to 0x%X",entry,ITLB[entry].Address.VPN<<10,ITLB[entry].Data.PPN<<10);
}
void mmu_set_state()
{
}
void MMU_init()
{
}
void MMU_reset()
{
memset(UTLB,0,sizeof(UTLB));
memset(ITLB,0,sizeof(ITLB));
}
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"
@ -75,9 +29,6 @@ defining NO_MMU disables the full mmu emulation
#include "hw/mem/_vmem.h"
template<bool internal = false>
u32 mmu_full_lookup(u32 va, u32& idx, u32& rv);
//#define TRACE_WINCE_SYSCALLS
#ifdef TRACE_WINCE_SYSCALLS
@ -88,31 +39,14 @@ u32 unresolved_unicode_string;
#define printf_mmu(...) DEBUG_LOG(SH4, __VA_ARGS__)
extern 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
};
extern 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] =
constexpr u32 ITLB_LRU_OR[4] =
{
0x00,//000xxx
0x20,//1xx00x
0x14,//x1x1x0
0x0B,//xx1x11
};
const u32 ITLB_LRU_AND[4] =
constexpr u32 ITLB_LRU_AND[4] =
{
0x07,//000xxx
0x39,//1xx00x
@ -120,6 +54,10 @@ const u32 ITLB_LRU_AND[4] =
0x3F,//xx1x11
};
u32 lastVAddr[2] { 1, 1 };
u32 lastPAddr[2];
u8 lastIdx;
#ifndef FAST_MMU
//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)
@ -149,19 +87,10 @@ void ITLB_Sync(u32 entry)
}
#endif
static void RaiseException(u32 expEvnt, u32 callVect) {
#if !defined(NO_MMU)
debugger::debugTrap(expEvnt); // FIXME CCN_TEA and CCN_PTEH have been updated already
SH4ThrownException ex = { next_pc - 2, expEvnt, callVect };
throw ex;
#else
msgboxf("Can't raise exceptions yet", MBX_ICONERROR);
#endif
}
void mmu_raise_exception(u32 mmu_error, u32 address, u32 am)
template<typename F>
static void mmuException(u32 mmu_error, u32 address, u32 am, F raise)
{
printf_mmu("mmu_raise_exception -> pc = 0x%X : ", next_pc);
printf_mmu("MMU exception -> pc = 0x%X : ", next_pc);
CCN_TEA = address;
CCN_PTEH.VPN = address >> 10;
@ -169,18 +98,18 @@ void mmu_raise_exception(u32 mmu_error, u32 address, u32 am)
{
//No error
case MMU_ERROR_NONE:
die("Error : mmu_raise_exception(MMU_ERROR_NONE)");
break;
die("Error: mmu_error == MMU_ERROR_NONE)");
return;
//TLB miss
case MMU_ERROR_TLB_MISS:
printf_mmu("MMU_ERROR_UTLB_MISS 0x%X, handled", address);
if (am == MMU_TT_DWRITE) //WTLBMISS - Write Data TLB Miss Exception
RaiseException(0x60, 0x400);
raise(0x60, 0x400);
else if (am == MMU_TT_DREAD) //RTLBMISS - Read Data TLB Miss Exception
RaiseException(0x40, 0x400);
raise(0x40, 0x400);
else //ITLBMISS - Instruction TLB Miss Exception
RaiseException(0x40, 0x400);
raise(0x40, 0x400);
return;
//TLB Multihit
@ -192,11 +121,11 @@ void mmu_raise_exception(u32 mmu_error, u32 address, u32 am)
case MMU_ERROR_PROTECTED:
printf_mmu("MMU_ERROR_PROTECTED 0x%X, handled", address);
if (am == MMU_TT_DWRITE) //WRITEPROT - Write Data TLB Protection Violation Exception
RaiseException(0xC0, 0x100);
raise(0xC0, 0x100);
else if (am == MMU_TT_DREAD) //READPROT - Data TLB Protection Violation Exception
RaiseException(0xA0, 0x100);
raise(0xA0, 0x100);
else //READPROT - Instr TLB Protection Violation Exception
RaiseException(0xA0, 0x100);
raise(0xA0, 0x100);
return;
//Mem is write protected , firstwrite
@ -204,7 +133,7 @@ void mmu_raise_exception(u32 mmu_error, u32 address, u32 am)
printf_mmu("MMU_ERROR_FIRSTWRITE");
verify(am == MMU_TT_DWRITE);
//FIRSTWRITE - Initial Page Write Exception
RaiseException(0x80, 0x100);
raise(0x80, 0x100);
return;
//data read/write missasligned
@ -212,12 +141,12 @@ void mmu_raise_exception(u32 mmu_error, u32 address, u32 am)
if (am == MMU_TT_DWRITE) //WADDERR - Write Data Address Error
{
printf_mmu("MMU_ERROR_BADADDR(dw) 0x%X", address);
RaiseException(0x100, 0x100);
raise(0x100, 0x100);
}
else if (am == MMU_TT_DREAD) //RADDERR - Read Data Address Error
{
printf_mmu("MMU_ERROR_BADADDR(dr) 0x%X", address);
RaiseException(0xE0, 0x100);
raise(0xE0, 0x100);
}
else //IADDERR - Instruction Address Error
{
@ -225,7 +154,7 @@ void mmu_raise_exception(u32 mmu_error, u32 address, u32 am)
if (!print_wince_syscall(address))
#endif
printf_mmu("MMU_ERROR_BADADDR(i) 0x%X", address);
RaiseException(0xE0, 0x100);
raise(0xE0, 0x100);
}
return;
@ -234,99 +163,28 @@ void mmu_raise_exception(u32 mmu_error, u32 address, u32 am)
INFO_LOG(SH4, "MMU_ERROR_EXECPROT 0x%X", address);
//EXECPROT - Instruction TLB Protection Violation Exception
RaiseException(0xA0, 0x100);
raise(0xA0, 0x100);
return;
}
die("Unknown mmu_error");
}
void mmu_raise_exception(u32 mmu_error, u32 address, u32 am)
{
mmuException(mmu_error, address, am, [](u32 event, u32 vector) {
debugger::debugTrap(event); // FIXME CCN_TEA and CCN_PTEH have been updated already
SH4ThrownException ex { next_pc - 2, event, vector };
throw ex;
});
}
void DoMMUException(u32 address, u32 mmu_error, u32 access_type)
{
printf_mmu("DoMMUException -> pc = 0x%X : %d ", next_pc, access_type);
CCN_TEA = address;
CCN_PTEH.VPN = address >> 10;
switch (mmu_error)
{
//No error
case MMU_ERROR_NONE:
die("Error : mmu_raise_exception(MMU_ERROR_NONE)");
break;
//TLB miss
case MMU_ERROR_TLB_MISS:
printf_mmu("MMU_ERROR_UTLB_MISS 0x%X, handled", address);
if (access_type == MMU_TT_DWRITE) //WTLBMISS - Write Data TLB Miss Exception
Do_Exception(next_pc, 0x60, 0x400);
else if (access_type == MMU_TT_DREAD) //RTLBMISS - Read Data TLB Miss Exception
Do_Exception(next_pc, 0x40, 0x400);
else //ITLBMISS - Instruction TLB Miss Exception
Do_Exception(next_pc, 0x40, 0x400);
return;
break;
//TLB Multihit
case MMU_ERROR_TLB_MHIT:
INFO_LOG(SH4, "MMU_ERROR_TLB_MHIT @ 0x%X", address);
break;
//Mem is read/write protected (depends on translation type)
case MMU_ERROR_PROTECTED:
printf_mmu("MMU_ERROR_PROTECTED 0x%X, handled", address);
if (access_type == MMU_TT_DWRITE) //WRITEPROT - Write Data TLB Protection Violation Exception
Do_Exception(next_pc, 0xC0, 0x100);
else if (access_type == MMU_TT_DREAD) //READPROT - Data TLB Protection Violation Exception
Do_Exception(next_pc, 0xA0, 0x100);
else
{
verify(false);
}
return;
break;
//Mem is write protected , firstwrite
case MMU_ERROR_FIRSTWRITE:
printf_mmu("MMU_ERROR_FIRSTWRITE");
verify(access_type == MMU_TT_DWRITE);
//FIRSTWRITE - Initial Page Write Exception
Do_Exception(next_pc, 0x80, 0x100);
return;
break;
//data read/write missasligned
case MMU_ERROR_BADADDR:
if (access_type == MMU_TT_DWRITE) //WADDERR - Write Data Address Error
Do_Exception(next_pc, 0x100, 0x100);
else if (access_type == MMU_TT_DREAD) //RADDERR - Read Data Address Error
Do_Exception(next_pc, 0xE0, 0x100);
else //IADDERR - Instruction Address Error
{
#ifdef TRACE_WINCE_SYSCALLS
if (!print_wince_syscall(address))
#endif
printf_mmu("MMU_ERROR_BADADDR(i) 0x%X", address);
Do_Exception(next_pc, 0xE0, 0x100);
return;
}
printf_mmu("MMU_ERROR_BADADDR(d) 0x%X, handled", address);
return;
break;
//Can't Execute
case MMU_ERROR_EXECPROT:
INFO_LOG(SH4, "MMU_ERROR_EXECPROT 0x%X", address);
//EXECPROT - Instruction TLB Protection Violation Exception
Do_Exception(next_pc, 0xA0, 0x100);
return;
break;
}
die("Unknown mmu_error");
mmuException(mmu_error, address, access_type, [](u32 event, u32 vector) {
Do_Exception(next_pc, event, vector);
});
}
bool mmu_match(u32 va, CCN_PTEH_type Address, CCN_PTEL_type Data)
@ -396,10 +254,9 @@ u32 mmu_full_lookup(u32 va, const TLB_Entry** tlb_entry_ret, u32& rv)
return MMU_ERROR_NONE;
}
#endif
//Simple QACR translation for mmu (when AT is off)
u32 mmu_QACR_SQ(u32 va)
static u32 mmu_QACR_SQ(u32 va)
{
u32 QACR;
@ -412,7 +269,6 @@ u32 mmu_QACR_SQ(u32 va)
return QACR + va;
}
#ifndef FAST_MMU
template<u32 translation_type>
u32 mmu_full_SQ(u32 va, u32& rv)
{
@ -709,6 +565,8 @@ void mmu_flush_table()
for (u32 i = 0; i < 64; i++)
UTLB[i].Data.V = 0;
lastVAddr[0] = 1;
lastVAddr[1] = 1;
}
#endif
@ -773,4 +631,3 @@ bool mmu_TranslateSQW(u32 adr, u32* out)
return true;
}
#endif

View File

@ -23,7 +23,7 @@
#define MMU_ERROR_PROTECTED 3
//Mem is write protected , firstwrite
#define MMU_ERROR_FIRSTWRITE 4
//data-Opcode read/write missasligned
//data-Opcode read/write misaligned
#define MMU_ERROR_BADADDR 5
//Can't Execute
#define MMU_ERROR_EXECPROT 6
@ -38,9 +38,24 @@ struct TLB_Entry
extern TLB_Entry UTLB[64];
extern TLB_Entry ITLB[4];
extern u32 sq_remap[64];
extern const u32 fast_reg_lut[8];
//These are working only for SQ remaps on ndce
constexpr u32 fast_reg_lut[8] =
{
0, 0, 0, 0 //P0-U0
, 1 //P1
, 1 //P2
, 0 //P3
, 1 //P4
};
constexpr 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
};
bool UTLB_Sync(u32 entry);
void ITLB_Sync(u32 entry);
@ -51,11 +66,7 @@ void mmu_raise_exception(u32 mmu_error, u32 address, u32 am);
static INLINE bool mmu_enabled()
{
#ifndef NO_MMU
return config::FullMMU && CCN_MMUCR.AT == 1;
#else
return false;
#endif
}
template<bool internal = false>
@ -75,83 +86,91 @@ static INLINE u32 mmu_instruction_translation(u32 va, u32& rv)
return MMU_ERROR_NONE;
}
const TLB_Entry *tlb_entry;
return mmu_full_lookup(va, &tlb_entry, rv);
return mmu_full_lookup(va, nullptr, rv);
}
#else
u32 mmu_instruction_translation(u32 va, u32& rv);
#endif
template<u32 translation_type, typename T>
extern u32 mmu_data_translation(u32 va, u32& rv);
u32 mmu_data_translation(u32 va, u32& rv);
void DoMMUException(u32 addr, u32 mmu_error, u32 access_type);
template<u32 translation_type>
bool mmu_is_translated(u32 va, u32 size)
inline static bool mmu_is_translated(u32 va, u32 size)
{
#ifndef FAST_MMU
if (va & (size - 1))
return true;
if (translation_type == MMU_TT_DWRITE)
{
if ((va & 0xFC000000) == 0xE0000000)
//SQ writes are not translated, only write backs are.
return false;
}
if ((va & 0xFC000000) == 0x7C000000)
// On-chip RAM area isn't translated
return false;
#endif
if (fast_reg_lut[va >> 29] != 0)
return false;
if ((va & 0xFC000000) == 0x7C000000)
// On-chip RAM area isn't translated
return false;
return true;
}
#if defined(NO_MMU)
bool inline mmu_TranslateSQW(u32 addr, u32* mapped) {
*mapped = sq_remap[(addr>>20)&0x3F] | (addr & 0xFFFE0);
return true;
template<typename T> T DYNACALL mmu_ReadMem(u32 adr);
u16 DYNACALL mmu_IReadMem16(u32 addr);
template<typename T> void DYNACALL mmu_WriteMem(u32 adr, T data);
bool mmu_TranslateSQW(u32 adr, u32* out);
extern u32 lastVAddr[2];
extern u32 lastPAddr[2];
extern u8 lastIdx;
template<typename T>
std::pair<T, bool> DYNACALL mmu_ReadMemNoEx(u32 adr)
{
u32 addr;
if (lastVAddr[0] == (adr & ~PAGE_MASK)) {
addr = lastPAddr[0] | (adr & PAGE_MASK);
}
void inline mmu_flush_table() {}
#else
template<typename T> T DYNACALL mmu_ReadMem(u32 adr);
u16 DYNACALL mmu_IReadMem16(u32 addr);
template<typename T> void DYNACALL mmu_WriteMem(u32 adr, T data);
bool mmu_TranslateSQW(u32 adr, u32* out);
template<typename T>
T DYNACALL mmu_ReadMemNoEx(u32 adr, u32 *exception_occurred)
else if (lastVAddr[1] == (adr & ~PAGE_MASK)) {
addr = lastPAddr[1] | (adr & PAGE_MASK);
}
else
{
u32 addr;
u32 rv = mmu_data_translation<MMU_TT_DREAD, T>(adr, addr);
if (rv != MMU_ERROR_NONE)
if (unlikely(rv != MMU_ERROR_NONE))
{
DoMMUException(adr, rv, MMU_TT_DREAD);
*exception_occurred = 1;
return 0;
}
else
{
*exception_occurred = 0;
return _vmem_readt<T, T>(addr);
return std::make_pair(0, true);
}
lastVAddr[lastIdx] = adr & ~PAGE_MASK;
lastPAddr[lastIdx] = addr & ~PAGE_MASK;
lastIdx ^= 1;
}
return std::make_pair(_vmem_readt<T, T>(addr), false);
}
template<typename T>
u32 DYNACALL mmu_WriteMemNoEx(u32 adr, T data)
template<typename T>
u32 DYNACALL mmu_WriteMemNoEx(u32 adr, T data)
{
u32 addr;
if (lastVAddr[0] == (adr & ~PAGE_MASK)) {
addr = lastPAddr[0] | (adr & PAGE_MASK);
}
else if (lastVAddr[1] == (adr & ~PAGE_MASK)) {
addr = lastPAddr[1] | (adr & PAGE_MASK);
}
else
{
u32 addr;
u32 rv = mmu_data_translation<MMU_TT_DWRITE, T>(adr, addr);
if (rv != MMU_ERROR_NONE)
u32 rv = mmu_data_translation<MMU_TT_DREAD, T>(adr, addr);
if (unlikely(rv != MMU_ERROR_NONE))
{
DoMMUException(adr, rv, MMU_TT_DWRITE);
return 1;
}
_vmem_writet<T>(addr, data);
return 0;
lastVAddr[lastIdx] = adr & ~PAGE_MASK;
lastPAddr[lastIdx] = addr & ~PAGE_MASK;
lastIdx ^= 1;
}
#endif
_vmem_writet<T>(addr, data);
return 0;
}

View File

@ -1,10 +0,0 @@
#pragma once
#include "types.h"
#include "ccn.h"
#include "mmu.h"
void MMU_Init();
void MMU_Reset(bool Manual);
void MMU_Term();
template<u32 translation_type> u32 mmu_full_SQ(u32 va, u32& rv);

View File

@ -106,15 +106,11 @@ struct SH4ThrownException {
static INLINE void RaiseFPUDisableException()
{
#if !defined(NO_MMU)
if (config::FullMMU)
{
SH4ThrownException ex = { next_pc - 2, 0x800, 0x100 };
SH4ThrownException ex { next_pc - 2, 0x800, 0x100 };
throw ex;
}
#else
msgboxf("Full MMU support needed", MBX_ICONERROR);
#endif
}
static INLINE void AdjustDelaySlotException(SH4ThrownException& ex)

View File

@ -17,7 +17,6 @@
//main system mem
VArray2 mem_b;
#ifndef NO_MMU
// Memory handlers
ReadMem8Func ReadMem8;
ReadMem16Func ReadMem16;
@ -29,7 +28,6 @@ WriteMem8Func WriteMem8;
WriteMem16Func WriteMem16;
WriteMem32Func WriteMem32;
WriteMem64Func WriteMem64;
#endif
//AREA 1
static _vmem_handler area1_32b;
@ -313,7 +311,6 @@ static bool interpreterRunning = false;
void SetMemoryHandlers()
{
#ifndef NO_MMU
#ifdef STRICT_MODE
if (config::DynarecEnabled && interpreterRunning)
{
@ -366,5 +363,4 @@ void SetMemoryHandlers()
WriteMem32 = &_vmem_WriteMem32;
WriteMem64 = &_vmem_WriteMem64;
}
#endif
}

View File

@ -8,23 +8,6 @@ extern VArray2 mem_b;
#include "hw/mem/_vmem.h"
#include "modules/mmu.h"
#ifdef NO_MMU
#define ReadMem8 _vmem_ReadMem8
#define ReadMem16 _vmem_ReadMem16
#define IReadMem16 ReadMem16
#define ReadMem32 _vmem_ReadMem32
#define ReadMem64 _vmem_ReadMem64
#define WriteMem8 _vmem_WriteMem8
#define WriteMem16 _vmem_WriteMem16
#define WriteMem32 _vmem_WriteMem32
#define WriteMem64 _vmem_WriteMem64
#ifdef STRICT_MODE
#error Strict mode requires the MMU
#endif
#else
#ifdef _MSC_VER
typedef u8 (DYNACALL *ReadMem8Func)(u32 addr);
typedef u16 (DYNACALL *ReadMem16Func)(u32 addr);
@ -58,8 +41,6 @@ extern WriteMem16Func WriteMem16;
extern WriteMem32Func WriteMem32;
extern WriteMem64Func WriteMem64;
#endif
#define ReadMem8_nommu _vmem_ReadMem8
#define ReadMem16_nommu _vmem_ReadMem16
#define IReadMem16_nommu _vmem_IReadMem16

View File

@ -315,16 +315,11 @@ void DYNACALL WriteMem_P4(u32 addr,T data)
{
if (addr&0x80)
{
#ifdef NO_MMU
INFO_LOG(SH4, "Unhandled p4 Write [Unified TLB address array, Associative Write] 0x%x = %x", addr, data);
#endif
CCN_PTEH_type t;
t.reg_data=data;
u32 va=t.VPN<<10;
#ifndef NO_MMU
for (int i=0;i<64;i++)
{
if (mmu_match(va,UTLB[i].Address,UTLB[i].Data))
@ -344,7 +339,6 @@ void DYNACALL WriteMem_P4(u32 addr,T data)
ITLB_Sync(i);
}
}
#endif
}
else
{

View File

@ -46,7 +46,7 @@ void sigill_handler(int sn, siginfo_t * si, void *segfault_ctx) {
void fault_handler (int sn, siginfo_t * si, void *segfault_ctx)
{
#if !defined(NO_MMU) && defined(HOST_64BIT_CPU)
#if defined(HOST_64BIT_CPU)
// WinCE virtual memory
#if HOST_CPU == CPU_ARM64
#define HOST_CTX_READY

View File

@ -113,31 +113,23 @@ void ngen_GetFeatures(ngen_features* dst)
template<typename T>
static T ReadMemNoEx(u32 addr, u32, u32 pc)
{
#ifndef NO_MMU
u32 ex;
T rv = mmu_ReadMemNoEx<T>(addr, &ex);
if (ex)
auto rv = mmu_ReadMemNoEx<T>(addr);
if (rv.second)
{
spc = pc;
handleException();
}
return rv;
#else
return (T)0; // not used
#endif
return rv.first;
}
template<typename T>
static void WriteMemNoEx(u32 addr, T data, u32 pc)
{
#ifndef NO_MMU
u32 ex = mmu_WriteMemNoEx<T>(addr, data);
if (ex)
if (mmu_WriteMemNoEx<T>(addr, data))
{
spc = pc;
handleException();
}
#endif
}
static void interpreter_fallback(u16 op, OpCallFP *oph, u32 pc)
@ -1648,9 +1640,9 @@ private:
u32 size = op.flags & 0x7f;
u32 addr = op.rs1._imm;
if (mmu_enabled() && mmu_is_translated<MMU_TT_DREAD>(addr, size))
if (mmu_enabled() && mmu_is_translated(addr, size))
{
if ((addr >> 12) != (block->vaddr >> 12))
if ((addr >> 12) != (block->vaddr >> 12) && ((addr >> 12) != ((block->vaddr + block->guest_opcodes * 2 - 1) >> 12)))
// When full mmu is on, only consider addresses in the same 4k page
return false;
u32 paddr;
@ -1859,7 +1851,7 @@ private:
u32 size = op.flags & 0x7f;
u32 addr = op.rs1._imm;
if (mmu_enabled() && mmu_is_translated<MMU_TT_DWRITE>(addr, size))
if (mmu_enabled() && mmu_is_translated(addr, size))
{
if ((addr >> 12) != (block->vaddr >> 12) && ((addr >> 12) != ((block->vaddr + block->guest_opcodes * 2 - 1) >> 12)))
// When full mmu is on, only consider addresses in the same 4k page

View File

@ -41,7 +41,6 @@ static void (*handleException)();
u32 mem_writes, mem_reads;
u32 mem_rewrites_w, mem_rewrites_r;
static u32 exception_raised;
static u64 jmp_rsp;
namespace MemSize {
@ -104,38 +103,28 @@ static void ngen_blockcheckfail(u32 pc) {
rdv_BlockCheckFail(pc);
}
static void handle_mem_exception(u32 exception_raised, u32 pc)
static void handle_mem_exception(u32 pc)
{
if (exception_raised)
{
spc = pc;
cycle_counter += 2; // probably more is needed but no easy way to find out
handleException();
}
spc = pc;
cycle_counter += 2; // probably more is needed but no easy way to find out
handleException();
}
template<typename T>
static T ReadMemNoEx(u32 addr, u32 pc)
{
#ifndef NO_MMU
T rv = mmu_ReadMemNoEx<T>(addr, &exception_raised);
handle_mem_exception(exception_raised, pc);
auto rv = mmu_ReadMemNoEx<T>(addr);
if (unlikely(rv.second))
handle_mem_exception(pc);
return rv;
#else
// not used
return (T)0;
#endif
return rv.first;
}
template<typename T>
static u32 WriteMemNoEx(u32 addr, T data, u32 pc)
static void WriteMemNoEx(u32 addr, T data, u32 pc)
{
#ifndef NO_MMU
u32 exception_raised = mmu_WriteMemNoEx<T>(addr, data);
handle_mem_exception(exception_raised, pc);
return exception_raised;
#endif
if (mmu_WriteMemNoEx<T>(addr, data))
handle_mem_exception(pc);
}
static void handle_sh4_exception(SH4ThrownException& ex, u32 pc)
@ -810,9 +799,9 @@ private:
return false;
u32 size = op.flags & 0x7f;
u32 addr = op.rs1._imm;
if (mmu_enabled() && mmu_is_translated<MMU_TT_DREAD>(addr, size))
if (mmu_enabled() && mmu_is_translated(addr, size))
{
if ((addr >> 12) != (block->vaddr >> 12))
if ((addr >> 12) != (block->vaddr >> 12) && ((addr >> 12) != ((block->vaddr + block->guest_opcodes * 2 - 1) >> 12)))
// When full mmu is on, only consider addresses in the same 4k page
return false;
@ -949,9 +938,9 @@ private:
return false;
u32 size = op.flags & 0x7f;
u32 addr = op.rs1._imm;
if (mmu_enabled() && mmu_is_translated<MMU_TT_DWRITE>(addr, size))
if (mmu_enabled() && mmu_is_translated(addr, size))
{
if ((addr >> 12) != (block->vaddr >> 12))
if ((addr >> 12) != (block->vaddr >> 12) && ((addr >> 12) != ((block->vaddr + block->guest_opcodes * 2 - 1) >> 12)))
// When full mmu is on, only consider addresses in the same 4k page
return false;

View File

@ -170,18 +170,9 @@ int darw_printf(const char* Text,...);
#include "log/Log.h"
#ifndef NO_MMU
#define _X_x_X_MMU_VER_STR "/mmu"
#else
#define _X_x_X_MMU_VER_STR ""
#endif
#define VER_EMUNAME "Flycast"
#define VER_FULLNAME VER_EMUNAME " git" _X_x_X_MMU_VER_STR " (built " __DATE__ "@" __TIME__ ")"
#define VER_SHORTNAME VER_EMUNAME " git" _X_x_X_MMU_VER_STR
#define VER_FULLNAME VER_EMUNAME " (built " __DATE__ "@" __TIME__ ")"
#define VER_SHORTNAME VER_EMUNAME
void os_DebugBreak();
#define dbgbreak os_DebugBreak()

View File

@ -1055,7 +1055,6 @@
AEE6279522247C2B00EC7E89 /* keyboard_device.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = keyboard_device.cpp; sourceTree = "<group>"; };
AEF25640227C441F00348550 /* vmem32.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = vmem32.cpp; sourceTree = "<group>"; };
AEF25641227C441F00348550 /* vmem32.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = vmem32.h; sourceTree = "<group>"; };
AEF25643227C442F00348550 /* mmu_impl.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = mmu_impl.h; sourceTree = "<group>"; };
AEF25644227C442F00348550 /* fastmmu.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = fastmmu.cpp; sourceTree = "<group>"; };
AEF25645227C442F00348550 /* wince.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = wince.h; sourceTree = "<group>"; };
AEF2564722886A2E00348550 /* posix_vmem.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = posix_vmem.cpp; sourceTree = "<group>"; };
@ -1835,7 +1834,6 @@
isa = PBXGroup;
children = (
AEF25644227C442F00348550 /* fastmmu.cpp */,
AEF25643227C442F00348550 /* mmu_impl.h */,
AEF25645227C442F00348550 /* wince.h */,
84B7BE221B72720100F9733F /* bsc.cpp */,
84B7BE231B72720100F9733F /* ccn.cpp */,