Implemented MMU Demand Paging

* Emulated correct behaviour of DSI and ISI exceptions
* Added memory exception checks
* Added fast TLB cache implementation (written by booto)
* Added "Enable MMU" option in the game properties
* Renamed old TLBHack to "MMU speed hack"

Thanks to booto (PowerPC) and nash (testing) who spent many weeks of their time helping me make this work.  Also thanks to shuffle2 for finding and converting the map file of the original target.

There are two options for MMU emulation found under the game properties.  "Enable MMU" is the accurate emulation option.  "MMU speed hack" is the old TLBHack renamed. Most games will work with one or the other.  Some games can work with both options ticked.

Only the JIT recompiler and Interpreter work with the MMU code.  JITIL is not supported (too hard for me to add).

The speed optimised code still needs a lot more work and is disabled for now.

Fixes issue 2546
Fixes issue 2583
Fixes issue 2704



git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@5994 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
skidau 2010-07-29 12:17:47 +00:00
parent b70f134c88
commit 9ff5e836eb
34 changed files with 1025 additions and 646 deletions

View File

@ -290,6 +290,7 @@ void SConfig::LoadSettings()
ini.Get("Core", "WiiKeyboard", &m_WiiKeyboard, false);
ini.Get("Core", "RunCompareServer", &m_LocalCoreStartupParameter.bRunCompareServer, false);
ini.Get("Core", "RunCompareClient", &m_LocalCoreStartupParameter.bRunCompareClient, false);
ini.Get("Core", "MMU", &m_LocalCoreStartupParameter.bMMU, false);
ini.Get("Core", "TLBHack", &m_LocalCoreStartupParameter.iTLBHack, 0);
ini.Get("Core", "FrameLimit", &m_Framelimit, 1); // auto frame limit by default
ini.Get("Core", "UseFPS", &b_UseFPS, false); // use vps as default

View File

@ -55,7 +55,7 @@ void Console_Submit(const char *cmd)
{
#if MAX_LOGLEVEL >= INFO_LEVEL
u32 EA =
Memory::TranslatePageAddress(addr, Memory::FLAG_NO_EXCEPTION);
Memory::TranslateAddress(addr, Memory::FLAG_NO_EXCEPTION);
INFO_LOG(CONSOLE, "EA 0x%08x to 0x%08x", addr, EA);
#endif
}

View File

@ -183,6 +183,15 @@ bool Init()
SCoreStartupParameter &_CoreParameter = SConfig::GetInstance().m_LocalCoreStartupParameter;
g_CoreStartupParameter = _CoreParameter;
// TODO: Reenable JIT instructions
if (g_CoreStartupParameter.bMMU)
{
g_CoreStartupParameter.bJITLoadStoreOff = true;
g_CoreStartupParameter.bJITLoadStorePairedOff = true;
g_CoreStartupParameter.bJITLoadStoreFloatingOff = true;
}
// FIXME DEBUG_LOG(BOOT, dump_params());
Host_SetWaitCursor(true);

View File

@ -32,7 +32,7 @@
SCoreStartupParameter::SCoreStartupParameter()
: hInstance(0), hMainWindow(0),
bJITNoBlockCache(false), bJITBlockLinking(false),
bJITNoBlockCache(false), bJITBlockLinking(true),
bJITOff(false),
bJITLoadStoreOff(false), bJITLoadStorelXzOff(false),
bJITLoadStorelwzOff(false), bJITLoadStorelbzxOff(false),
@ -48,7 +48,7 @@ SCoreStartupParameter::SCoreStartupParameter()
bEnableCheats(false),
bRunCompareServer(false), bRunCompareClient(false),
iTLBHack(0), SelectedLanguage(0),
bWii(false),
bWii(false), bMMU(false),
bConfirmStop(false), bHideCursor(false),
bAutoHideCursor(false), bUsePanicHandlers(true),
iRenderWindowXPos(0), iRenderWindowYPos(0),
@ -72,6 +72,7 @@ void SCoreStartupParameter::LoadDefaults()
bEnableFPRF = false;
bWii = false;
SelectedLanguage = 0;
bMMU = false;
iTLBHack = 0;
iPosX = 100;

View File

@ -77,6 +77,7 @@ struct SCoreStartupParameter
bool bRunCompareServer;
bool bRunCompareClient;
bool bMMU;
int iTLBHack;
int SelectedLanguage;

View File

@ -58,6 +58,7 @@ namespace Memory
/* Enable the Translation Lookaside Buffer functions. TLBHack = 1 in Dolphin.ini or a
<GameID>.ini file will set this to true */
bool bFakeVMEM = false;
bool bMMU = false;
// ==============
@ -348,6 +349,7 @@ bool Init()
{
bool wii = SConfig::GetInstance().m_LocalCoreStartupParameter.bWii;
bFakeVMEM = SConfig::GetInstance().m_LocalCoreStartupParameter.iTLBHack == 1;
bMMU = SConfig::GetInstance().m_LocalCoreStartupParameter.bMMU;
u32 flags = 0;
if (wii) flags |= MV_WII_ONLY;
@ -416,28 +418,37 @@ u32 Read_Instruction(const u32 em_address)
return inst.hex;
}
u32 Read_Opcode_JIT(const u32 _Address)
u32 Read_Opcode_JIT(u32 _Address)
{
#ifdef FAST_ICACHE
if ((_Address & ~JIT_ICACHE_MASK) != 0x80000000 && (_Address & ~JIT_ICACHE_MASK) != 0x00000000 &&
(_Address & ~JIT_ICACHE_MASK) != 0x7e000000 && // TLB area
(_Address & ~JIT_ICACHEEX_MASK) != 0x90000000 && (_Address & ~JIT_ICACHEEX_MASK) != 0x10000000)
if (bMMU && !bFakeVMEM && (_Address >> 28) == 0x7)
{
PanicAlert("iCacheJIT: Reading Opcode from %x. Please report.", _Address);
return 0;
_Address = Memory::TranslateAddress(_Address, Memory::XCheckTLBFlag::FLAG_OPCODE);
if (_Address == 0)
{
return 0;
}
}
u32 inst = Read_Opcode(_Address);
u32 inst = PowerPC::ppcState.iCache.ReadInstruction(_Address);
#else
u32 inst = Memory::ReadUnchecked_U32(_Address);
#endif
// if a crash occured after that message
// that means that we've compiled outdated code from the cache instead of memory
// that means that we have compiled outdated code from the cache instead of memory
// this could happen if a game forgot to icbi the new code
#if defined(_DEBUG) || defined(DEBUGFAST)
u32 inst_mem = Memory::ReadUnchecked_U32(_Address);
if (inst_mem != inst)
ERROR_LOG(POWERPC, "JIT: compiling outdated code. addr=%x, mem=%x, cache=%x", _Address, inst_mem, inst);
inst = Read_Opcode_JIT_LC(_Address);
if (inst_mem != inst)
{
ERROR_LOG(POWERPC, "JIT: self-modifying code detected. addr=%x, mem=%x, cache=%x", _Address, inst_mem, inst);
PanicAlert("JIT: self-modifying code detected. addr=%x, mem=%x, cache=%x", _Address, inst_mem, inst);
Write_Opcode_JIT(_Address, inst_mem);
}
#endif
return inst;
}
@ -451,6 +462,7 @@ u32 Read_Opcode_JIT_LC(const u32 _Address)
(_Address & ~JIT_ICACHEEX_MASK) != 0x90000000 && (_Address & ~JIT_ICACHEEX_MASK) != 0x10000000)
{
PanicAlert("iCacheJIT: Reading Opcode from %x. Please report.", _Address);
ERROR_LOG(MEMMAP, "iCacheJIT: Reading Opcode from %x. Please report.", _Address);
return 0;
}
u8* iCache;
@ -505,132 +517,14 @@ void Write_Opcode_JIT(const u32 _Address, const u32 _Value)
#endif
}
// =======================================================
/* Functions to detect and trace memory read/write errors. Turn of JIT LoadStore to
make it work, and add a return 0 at the beginning of CheckDTLB to avoid closing
Dolphin just as it starts to get interesting. You can also try to change
the TitleID write in IOCTL_ES_GETTITLEID to 0x00000000, otherwise it will never even
get to making the bad dev/di request.
I'm currently at (---, 8021347c) : Write32: Program wrote [0x7fd5d340] to [0x933e00f8],
0x8021347c seems to write the out buffer to a 0x933e.... address before it is copies
to the 0x133e.... address for the Ioctlv. But why does it generate this bad address
when it made a good one 120 milliseconds earlier?
*/
// -------------
bool ValidMemory(const u32 _Address)
void GenerateISIException_JIT(u32 _EffectiveAddress)
{
switch (_Address >> 24)
{
case 0x00:
case 0x01:
case 0x80:
case 0x81:
case 0xC0:
case 0xC1:
return true;
GenerateISIException(_EffectiveAddress);
case 0x10:
case 0x11:
case 0x12:
case 0x13:
case 0x90:
case 0x91:
case 0x92:
case 0x93:
case 0xD0:
case 0xD1:
case 0xD2:
case 0xD3:
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bWii)
return true;
else
return false;
case 0x7e:
case 0x7f:
if (bFakeVMEM)
return true;
else
return false;
case 0xE0:
if (_Address < (0xE0000000 + L1_CACHE_SIZE))
return true;
else
return false;
case 0xCC:
case 0xCD:
case 0xC8:
return true;
}
return false;
// Remove the invalid instruction from the icache, forcing a recompile
Write_Opcode_JIT(_EffectiveAddress, JIT_ICACHE_INVALID_WORD);
}
void CheckForBadAddresses(u32 Address, u32 Data, bool Read, int Bits)
{
if (!ValidMemory(Address))
{
if(Read)
{
WARN_LOG(CONSOLE, "Read%i: Program tried to read [%08x] from [%08x]", Bits, Address);
}
else
{
ERROR_LOG(CONSOLE, "Write%i: Program tried to write [%08x] to [%08x]", Bits, Data, Address);
}
}
if (Address == 0)
{
if(Read)
{
WARN_LOG(CONSOLE, "Read%i: Program read [0x%08x] from [0x%08x] * * * 0 * * *", Bits, Data, Address);
}
else
{
WARN_LOG(CONSOLE, "Write%i: Program wrote [0x%08x] to [0x%08x] * * * 0 * * *", Bits, Data, Address);
}
}
// Try to figure out where the dev/di Ioctl arguments are stored (including buffer out), so we can
// find the bad one
if(
Data == 0x1090f4c0 // good out buffer right before it, for sound/smashbros_sound.brsar
|| Data == 0x10913b00 // second one
|| Data == 0x7fd5d340 // first bad out buffer
|| Data == 0x133e00f8 // the address that store the bad 0x7fd5d340, this changes every time
|| Data == 0x2a24aa // menu2\sc_title_en.pac byte size
|| (
(PC == 0x8021347c || PC == 0x801f6a20 || PC == 0x800202d0 || PC == 0x80229964
|| PC == 0x801d88bc) /* this could be interesting, because the bad out buffer 0x7fd5d340
is 0x80000000 - size = 0x7fd5d340 perhaps some function read 0x80000000, I dunno */
&& Data == 0x80000000)
)
{
if(Read)
{
ERROR_LOG(CONSOLE, "Read%i: Program read [0x%08x] from [0x%08x] * * * * * * * * * * * *", Bits, Data, Address);
}
else
{
ERROR_LOG(CONSOLE, "Write%i: Program wrote [0x%08x] to [0x%08x] * * * * * * * * * * * *", Bits,Data, Address);
}
}
}
void CheckForBadAddresses8(u32 Address, u8 Data, bool Read)
{CheckForBadAddresses(Address, (u32)Data, Read, 8);}
void CheckForBadAddresses16(u32 Address, u16 Data, bool Read)
{CheckForBadAddresses(Address, (u32)Data, Read, 16);}
void CheckForBadAddresses32(u32 Address, u32 Data, bool Read)
{CheckForBadAddresses(Address, (u32)Data, Read, 32);}
void CheckForBadAddresses64(u32 Address, u64 Data, bool Read)
{CheckForBadAddresses(Address, (u32)Data, Read, 64);}
void WriteBigEData(const u8 *_pData, const u32 _Address, const u32 _iSize)
{
memcpy(GetPointer(_Address), _pData, _iSize);
@ -709,7 +603,6 @@ void GetString(std::string& _string, const u32 em_address)
_string = stringBuffer;
}
// GetPointer must always return an address in the bottom 32 bits of address space, so that 64-bit
// programs don't have problems directly addressing any part of memory.
u8 *GetPointer(const u32 _Address)
@ -743,7 +636,10 @@ u8 *GetPointer(const u32 _Address)
case 0x7E:
case 0x7F:
return (u8*)(((char*)m_pVirtualFakeVMEM) + (_Address & RAM_MASK));
if (bFakeVMEM)
return (u8*)(((char*)m_pVirtualFakeVMEM) + (_Address & RAM_MASK));
else
return 0;
case 0xE0:
if (_Address < (0xE0000000 + L1_CACHE_SIZE))

View File

@ -51,7 +51,7 @@ namespace Memory
// so be sure to load it into a 64-bit register.
extern u8 *base;
// These are guarenteed to point to "low memory" addresses (sub-32-bit).
// These are guaranteed to point to "low memory" addresses (sub-32-bit).
extern u8 *m_pRAM;
extern u8 *m_pEXRAM;
extern u8 *m_pL1Cache;
@ -75,7 +75,7 @@ enum
#ifdef _M_IX86
MEMVIEW32_MASK = 0x3FFFFFFF,
#endif
};
};
// Init and Shutdown
bool IsInitialized();
@ -169,11 +169,15 @@ enum XCheckTLBFlag
FLAG_WRITE,
FLAG_OPCODE,
};
u32 TranslatePageAddress(u32 _Address, XCheckTLBFlag _Flag);
u32 TranslateBlockAddress(u32 _Address);
u32 TranslateAddress(u32 _Address, XCheckTLBFlag _Flag);
void InvalidateTLBEntry(u32 _Address);
void GenerateDSIException(u32 _EffectiveAdress, bool _bWrite);
void GenerateISIException(u32 _EffectiveAdress);
void GenerateISIException_JIT(u32 _EffectiveAdress);
extern u32 pagetable_base;
extern u32 pagetable_hashmask;
};
#endif

View File

@ -185,10 +185,6 @@ inline void ReadFromHardware(T &_var, u32 em_address, u32 effective_address, Mem
{
_var = bswap((*(const T*)&m_pL1Cache[em_address & L1_CACHE_MASK]));
}
else if (em_address >= 0xE0000000)
{
PanicAlert("READ: Invalid address: %08x", em_address);
}
else if (bFakeVMEM && ((em_address &0xF0000000) == 0x70000000))
{
// fake VMEM
@ -197,30 +193,29 @@ inline void ReadFromHardware(T &_var, u32 em_address, u32 effective_address, Mem
else
{
// MMU
u32 tlb_addr = TranslateBlockAddress(em_address);
u32 tlb_addr = TranslateAddress(em_address, flag);
if (tlb_addr == 0)
{
tlb_addr = TranslatePageAddress(em_address, flag);
if (tlb_addr != 0)
_var = bswap((*(const T*)&m_pRAM[tlb_addr & RAM_MASK]));
else
PanicAlert("READ: Invalid address: %08x", em_address);
if (flag == FLAG_READ)
{
GenerateDSIException(em_address, false);
}
}
else
{
_var = bswap((*(const T*)&m_pRAM[tlb_addr & RAM_MASK]));
}
}
// Debugging: CheckForBadAddresses##_type(em_address, _var, true);
}
template <typename T>
inline void WriteToHardware(u32 em_address, const T data, u32 effective_address, Memory::XCheckTLBFlag flag)
{
// Debugging: CheckForBadAddresses##_type(em_address, data, false);
// First, let's check for FIFO writes, since they are probably the most common
// reason we end up in this function:
if (em_address == 0xCC008000) {
if (em_address == 0xCC008000)
{
switch (sizeof(T)) {
case 1: GPFifo::Write8((u8)data, em_address); return;
case 2: GPFifo::Write16((u16)data, em_address); return;
@ -243,22 +238,26 @@ inline void WriteToHardware(u32 em_address, const T data, u32 effective_address,
}
return;
}
else if (em_address <= 0xcc009000) {
else if (em_address <= 0xcc009000)
{
hwWrite(data, em_address);
return;
}
/* WIIMODE */
else if (((em_address & 0xFF000000) == 0xCD000000) &&
(em_address <= 0xcd009000)) {
(em_address <= 0xcd009000))
{
hwWriteWii(data,em_address);
return;
}
else if (((em_address & 0xFFF00000) == 0xCD800000) &&
(em_address <= 0xCD809000)) {
(em_address <= 0xCD809000))
{
hwWriteIOBridge(data,em_address);
return;
}
else {
else
{
ERROR_LOG(MEMMAP, "hwwrite [%08x] := %08x (PC: %08x)", em_address, data, PC);
_dbg_assert_msg_(MEMMAP,0,"Memory - Unknown HW address %08x", em_address);
}
@ -268,6 +267,11 @@ inline void WriteToHardware(u32 em_address, const T data, u32 effective_address,
((em_address & 0xF0000000) == 0x00000000))
{
*(T*)&m_pRAM[em_address & RAM_MASK] = bswap(data);
// Required for games with self modifying code (e.g. Monster House)
if (Core::g_CoreStartupParameter.bMMU)
Write_Opcode_JIT(em_address, 0x14141414);
return;
}
else if (((em_address & 0xF0000000) == 0x90000000) ||
@ -282,11 +286,6 @@ inline void WriteToHardware(u32 em_address, const T data, u32 effective_address,
*(T*)&m_pL1Cache[em_address & L1_CACHE_MASK] = bswap(data);
return;
}
else if (em_address >= 0xE0000000)
{
ERROR_LOG(MEMMAP,"WRITE: Cache address out of bounds (addr: %08x data: %08x)", em_address, data);
/* PanicAlert("WRITE: Cache address %08x out of bounds", em_address); */
}
else if (bFakeVMEM && ((em_address &0xF0000000) == 0x70000000))
{
// fake VMEM
@ -295,18 +294,18 @@ inline void WriteToHardware(u32 em_address, const T data, u32 effective_address,
else
{
// MMU
u32 tlb_addr = TranslateBlockAddress(em_address);
u32 tlb_addr = TranslateAddress(em_address, flag);
if (tlb_addr == 0)
{
tlb_addr = TranslatePageAddress(em_address, flag);
if (tlb_addr != 0)
*(T*)&m_pRAM[tlb_addr & RAM_MASK] = bswap(data);
else
PanicAlert("WRITE: Invalid address: %08x", em_address);
if (flag == FLAG_WRITE)
{
GenerateDSIException(em_address, true);
}
}
else
{
*(T*)&m_pRAM[tlb_addr & RAM_MASK] = bswap(data);
}
}
}
// =====================
@ -316,7 +315,7 @@ inline void WriteToHardware(u32 em_address, const T data, u32 effective_address,
/* These functions are primarily called by the Interpreter functions and are routed to the correct
location through ReadFromHardware and WriteToHardware */
// ----------------
u32 Read_Opcode(const u32 _Address)
u32 Read_Opcode(u32 _Address)
{
if (_Address == 0x00000000)
{
@ -325,9 +324,19 @@ u32 Read_Opcode(const u32 _Address)
return 0x00000000;
}
/*u32 _var = 0;
ReadFromHardware<u32>(_var, _Address, _Address, FLAG_OPCODE);
return _var;*/
if (Core::g_CoreStartupParameter.bMMU && (_Address >> 28) == 0x7)
{
// TODO: Check for MSR instruction address translation flag before translating
u32 tlb_addr = Memory::TranslateAddress(_Address, Memory::XCheckTLBFlag::FLAG_OPCODE);
if (tlb_addr == 0)
{
GenerateISIException(_Address);
return 0;
}
else
_Address = tlb_addr;
}
return PowerPC::ppcState.iCache.ReadInstruction(_Address);
}
@ -587,40 +596,25 @@ union UPTE2
u32 pagetable_base = 0;
u32 pagetable_hashmask = 0;
void GenerateDSIException(u32 _EffectiveAdress, bool _bWrite)
void GenerateDSIException(u32 _EffectiveAddress, bool _bWrite)
{
if (_bWrite)
PowerPC::ppcState.spr[SPR_DSISR] = PPC_EXC_DSISR_PAGE | PPC_EXC_DSISR_STORE;
else
PowerPC::ppcState.spr[SPR_DSISR] = PPC_EXC_DSISR_PAGE;
PowerPC::ppcState.spr[SPR_DAR] = _EffectiveAdress;
PowerPC::ppcState.spr[SPR_DAR] = _EffectiveAddress;
INFO_LOG(MEMMAP, "Generate DSI Exception 0x%08x", _EffectiveAdress);
PowerPC::ppcState.Exceptions |= EXCEPTION_DSI;
}
void GenerateISIException()
void GenerateISIException(u32 _EffectiveAddress)
{
// shuffle2: ISI exception doesn't modify DSISR at all, to my knowledge...
//PowerPC::ppcState.spr[SPR_DSISR] = 0x4000000; // maybe this was a typo for PPC_EXC_DSISR_PAGE?
// Address of instruction could not be translated
SRR1 = (1 << 30) | (MSR & 0x3fffff);
NPC = _EffectiveAddress;
// Instead, it modifies bits 1-4 in SRR1 depending on conditions:
// Bit 1: set if the translation of an attempted access is not found in the primary hash table entry group
// (HTEG), or in the rehashed secondary HTEG, or in the range of a IBAT register (page fault
// condition); otherwise cleared.
// Bit 2: cleared
// Bit 3: Set if the fetch access occurs to a direct-store segment (SR[T] = 1), to a noexecute
// segment (N bit set in segment descriptor), or to guarded memory
// when MSR[IR] = 1. Otherwise, cleared.
// Bit 4: Set if a memory access is not permitted by the page or IBAT protection
// mechanism, described in Chapter 7, "Memory Management" otherwise cleared.
// Only one of 1,3, or 4 may be set at a time
// For now let's just say that hash lookup failed
SRR1 = 0x10000000;
INFO_LOG(MEMMAP, "Generate ISI Exception");
PowerPC::ppcState.Exceptions |= EXCEPTION_ISI;
}
@ -650,17 +644,166 @@ void SDRUpdated()
pagetable_hashmask = ((xx<<10)|0x3ff);
}
// Page Address Translation
u32 TranslatePageAddress(u32 _Address, XCheckTLBFlag _Flag)
// TLB cache
//#define FAST_TLB_CACHE
#define TLB_SIZE 128
#define TLB_WAYS 2
#define NUM_TLBS 2
#define PAGE_SIZE 4096
#define PAGE_INDEX_SHIFT 12
#define PAGE_INDEX_MASK 0x3f
#define PAGE_TAG_SHIFT 18
#define TLB_FLAG_MOST_RECENT 0x01
#define TLB_FLAG_INVALID 0x02
typedef struct tlb_entry
{
// TLB cache
for (int i = 0; i < 16; i++) {
if ((_Address & ~0xfff) == (PowerPC::ppcState.tlb_va[(PowerPC::ppcState.tlb_last + i) & 15])) {
u32 result = PowerPC::ppcState.tlb_pa[(PowerPC::ppcState.tlb_last + i) & 15] | (_Address & 0xfff);
PowerPC::ppcState.tlb_last = i;
return result;
u32 tag;
u32 paddr;
u8 flags;
} tlb_entry;
tlb_entry tlb[NUM_TLBS][TLB_SIZE/TLB_WAYS][TLB_WAYS];
u32 LookupTLBPageAddress(const XCheckTLBFlag _Flag, const u32 vpa, u32 *paddr)
{
#ifdef FAST_TLB_CACHE
tlb_entry *tlbe = tlb[_Flag == FLAG_OPCODE][(vpa>>PAGE_INDEX_SHIFT)&PAGE_INDEX_MASK];
if(tlbe[0].tag == (vpa & ~0xfff) && !(tlbe[0].flags & TLB_FLAG_INVALID))
{
tlbe[0].flags |= TLB_FLAG_MOST_RECENT;
tlbe[1].flags &= ~TLB_FLAG_MOST_RECENT;
*paddr = tlbe[0].paddr | (vpa & 0xfff);
return 1;
}
if(tlbe[1].tag == (vpa & ~0xfff) && !(tlbe[1].flags & TLB_FLAG_INVALID))
{
tlbe[1].flags |= TLB_FLAG_MOST_RECENT;
tlbe[0].flags &= ~TLB_FLAG_MOST_RECENT;
*paddr = tlbe[1].paddr | (vpa & 0xfff);
return 1;
}
return 0;
#else
u32 _Address = vpa;
if (_Flag == FLAG_OPCODE)
{
for (int i = (PowerPC::ppcState.itlb_last); i > (PowerPC::ppcState.itlb_last - 128); i--)
{
if ((_Address & ~0xfff) == (PowerPC::ppcState.itlb_va[i & 127]))
{
u32 result = PowerPC::ppcState.itlb_pa[i & 127] | (_Address & 0xfff);
PowerPC::ppcState.itlb_last = i;
paddr = &result;
return 1;
}
}
}
else
{
for (int i = (PowerPC::ppcState.dtlb_last); i > (PowerPC::ppcState.dtlb_last - 128); i--)
{
if ((_Address & ~0xfff) == (PowerPC::ppcState.dtlb_va[i & 127]))
{
u32 result = PowerPC::ppcState.dtlb_pa[i & 127] | (_Address & 0xfff);
PowerPC::ppcState.dtlb_last = i;
paddr = &result;
return 1;
}
}
}
return 0;
#endif
}
void UpdateTLBEntry(const XCheckTLBFlag _Flag, UPTE2 PTE2, const u32 vpa)
{
#ifdef FAST_TLB_CACHE
tlb_entry *tlbe = tlb[_Flag == FLAG_OPCODE][(vpa>>PAGE_INDEX_SHIFT)&PAGE_INDEX_MASK];
if((tlbe[0].flags & TLB_FLAG_MOST_RECENT) == 0)
{
tlbe[0].flags = TLB_FLAG_MOST_RECENT;
tlbe[1].flags &= ~TLB_FLAG_MOST_RECENT;
tlbe[0].paddr = PTE2.RPN << PAGE_INDEX_SHIFT;
tlbe[0].tag = vpa & ~0xfff;
}
else
{
tlbe[1].flags = TLB_FLAG_MOST_RECENT;
tlbe[0].flags &= ~TLB_FLAG_MOST_RECENT;
tlbe[1].paddr = PTE2.RPN << PAGE_INDEX_SHIFT;
tlbe[1].tag = vpa & ~0xfff;
}
#else
if (_Flag == FLAG_OPCODE)
{
// ITLB cache
PowerPC::ppcState.itlb_last++;
PowerPC::ppcState.itlb_last &= 127;
PowerPC::ppcState.itlb_pa[PowerPC::ppcState.itlb_last] = PTE2.RPN << PAGE_INDEX_SHIFT;
PowerPC::ppcState.itlb_va[PowerPC::ppcState.itlb_last] = vpa & ~0xfff;
}
else
{
// DTLB cache
PowerPC::ppcState.dtlb_last++;
PowerPC::ppcState.dtlb_last &= 127;
PowerPC::ppcState.dtlb_pa[PowerPC::ppcState.dtlb_last] = PTE2.RPN << PAGE_INDEX_SHIFT;
PowerPC::ppcState.dtlb_va[PowerPC::ppcState.dtlb_last] = vpa & ~0xfff;
}
#endif
}
void InvalidateTLBEntry(u32 vpa)
{
#ifdef FAST_TLB_CACHE
tlb_entry *tlbe = tlb[0][(vpa>>PAGE_INDEX_SHIFT)&PAGE_INDEX_MASK];
if(tlbe[0].tag == (vpa & ~0xfff))
{
tlbe[0].flags |= TLB_FLAG_INVALID;
}
if(tlbe[1].tag == (vpa & ~0xfff))
{
tlbe[1].flags |= TLB_FLAG_INVALID;
}
tlb_entry *tlbe_i = tlb[1][(vpa>>PAGE_INDEX_SHIFT)&PAGE_INDEX_MASK];
if(tlbe_i[0].tag == (vpa & ~0xfff))
{
tlbe_i[0].flags |= TLB_FLAG_INVALID;
}
if(tlbe_i[1].tag == (vpa & ~0xfff))
{
tlbe_i[1].flags |= TLB_FLAG_INVALID;
}
#else
u32 _Address = vpa;
for (int i = 0; i < 128; i++)
{
if ((_Address & ~0xfff) == (PowerPC::ppcState.dtlb_va[(PowerPC::ppcState.dtlb_last + i) & 127]))
{
PowerPC::ppcState.dtlb_pa[(PowerPC::ppcState.dtlb_last + i) & 127] = 0;
PowerPC::ppcState.dtlb_va[(PowerPC::ppcState.dtlb_last + i) & 127] = 0;
}
if ((_Address & ~0xfff) == (PowerPC::ppcState.itlb_va[(PowerPC::ppcState.itlb_last + i) & 127]))
{
PowerPC::ppcState.itlb_pa[(PowerPC::ppcState.itlb_last + i) & 127] = 0;
PowerPC::ppcState.itlb_va[(PowerPC::ppcState.itlb_last + i) & 127] = 0;
}
}
#endif
}
// Page Address Translation
u32 TranslatePageAddress(const u32 _Address, const XCheckTLBFlag _Flag)
{
// TLB cache
u32 translatedAddress = 0;
if (LookupTLBPageAddress(_Flag, _Address, &translatedAddress))
return translatedAddress;
u32 sr = PowerPC::ppcState.sr[EA_SR(_Address)];
@ -673,7 +816,7 @@ u32 TranslatePageAddress(u32 _Address, XCheckTLBFlag _Flag)
// hash function no 1 "xor" .360
u32 hash1 = (VSID ^ page_index);
u32 pteg_addr = ((hash1 & pagetable_hashmask)<<6) | pagetable_base;
u32 pteg_addr = ((hash1 & pagetable_hashmask) << 6) | pagetable_base;
// hash1
for (int i = 0; i < 8; i++)
@ -688,11 +831,7 @@ u32 TranslatePageAddress(u32 _Address, XCheckTLBFlag _Flag)
UPTE2 PTE2;
PTE2.Hex = bswap((*(u32*)&pRAM[(pteg_addr + 4)]));
// TLB cache
PowerPC::ppcState.tlb_last++;
PowerPC::ppcState.tlb_last &= 15;
PowerPC::ppcState.tlb_pa[PowerPC::ppcState.tlb_last] = PTE2.RPN << 12;
PowerPC::ppcState.tlb_va[PowerPC::ppcState.tlb_last] = _Address & ~0xfff;
UpdateTLBEntry(_Flag, PTE2, _Address);
// set the access bits
switch (_Flag)
@ -712,7 +851,7 @@ u32 TranslatePageAddress(u32 _Address, XCheckTLBFlag _Flag)
// hash function no 2 "not" .360
hash1 = ~hash1;
pteg_addr = ((hash1 & pagetable_hashmask)<<6) | pagetable_base;
pteg_addr = ((hash1 & pagetable_hashmask) << 6) | pagetable_base;
for (int i = 0; i < 8; i++)
{
u32 pte = bswap(*(u32*)&pRAM[pteg_addr]);
@ -723,11 +862,7 @@ u32 TranslatePageAddress(u32 _Address, XCheckTLBFlag _Flag)
UPTE2 PTE2;
PTE2.Hex = bswap((*(u32*)&pRAM[(pteg_addr + 4)]));
// TLB cache
PowerPC::ppcState.tlb_last++;
PowerPC::ppcState.tlb_last &= 15;
PowerPC::ppcState.tlb_pa[PowerPC::ppcState.tlb_last] = PTE2.RPN << 12;
PowerPC::ppcState.tlb_va[PowerPC::ppcState.tlb_last] = _Address & ~0xfff;
UpdateTLBEntry(_Flag, PTE2, _Address);
switch (_Flag)
{
@ -743,27 +878,6 @@ u32 TranslatePageAddress(u32 _Address, XCheckTLBFlag _Flag)
}
pteg_addr+=8;
}
// If we got this far something went wrong and we save the exception data
switch(_Flag)
{
case FLAG_NO_EXCEPTION:
break;
case FLAG_READ:
GenerateDSIException(_Address, false);
break;
case FLAG_WRITE:
GenerateDSIException(_Address, true);
break;
case FLAG_OPCODE:
GenerateISIException();
break;
}
return 0;
}
@ -778,53 +892,78 @@ u32 TranslatePageAddress(u32 _Address, XCheckTLBFlag _Flag)
#define BAT_EA_4(v) ((v)&0xf0000000)
// Block Address Translation
u32 TranslateBlockAddress(u32 addr)
u32 TranslateBlockAddress(const u32 addr, const XCheckTLBFlag _Flag)
{
u32 result = 0;
UReg_MSR& m_MSR = ((UReg_MSR&)PowerPC::ppcState.msr);
// TODO: Check for enhanced mode before switching to Wii mode
int bats = Core::g_CoreStartupParameter.bWii?8:4;
for (int i = 0; i < bats; i++) {
u32 bl17 = ~(BATU_BL(PowerPC::ppcState.spr[SPR_DBAT0U + i * 2])<<17);
u32 addr2 = addr & (bl17 | 0xf001ffff);
u32 batu = (m_MSR.PR ? BATU_Vp : BATU_Vs);
if (_Flag != FLAG_OPCODE)
{
u32 bl17 = ~(BATU_BL(PowerPC::ppcState.spr[SPR_DBAT0U + i * 2]) << 17);
u32 addr2 = addr & (bl17 | 0xf001ffff);
u32 batu = (m_MSR.PR ? BATU_Vp : BATU_Vs);
if (BATU_BEPI(addr2) == BATU_BEPI(PowerPC::ppcState.spr[SPR_DBAT0U + i * 2])) {
// bat applies to this address
if (PowerPC::ppcState.spr[SPR_DBAT0U + i * 2] & batu) {
// bat entry valid
u32 offset = BAT_EA_OFFSET(addr);
u32 page = BAT_EA_11(addr);
page &= ~bl17;
page |= BATL_BRPN(PowerPC::ppcState.spr[SPR_DBAT0L + i * 2]);
// fixme: check access rights
result = page | offset;
return result;
if (BATU_BEPI(addr2) == BATU_BEPI(PowerPC::ppcState.spr[SPR_DBAT0U + i * 2]))
{
// bat applies to this address
if (PowerPC::ppcState.spr[SPR_DBAT0U + i * 2] & batu)
{
// bat entry valid
u32 offset = BAT_EA_OFFSET(addr);
u32 page = BAT_EA_11(addr);
page &= ~bl17;
page |= BATL_BRPN(PowerPC::ppcState.spr[SPR_DBAT0L + i * 2]);
// fixme: check access rights
result = page | offset;
return result;
}
}
}
else
{
u32 bl17 = ~(BATU_BL(PowerPC::ppcState.spr[SPR_IBAT0U + i * 2]) << 17);
u32 addr2 = addr & (bl17 | 0xf001ffff);
u32 batu = (m_MSR.PR ? BATU_Vp : BATU_Vs);
bl17 = ~(BATU_BL(PowerPC::ppcState.spr[SPR_IBAT0U + i * 2])<<17);
addr2 = addr & (bl17 | 0xf001ffff);
batu = (m_MSR.PR ? BATU_Vp : BATU_Vs);
if (BATU_BEPI(addr2) == BATU_BEPI(PowerPC::ppcState.spr[SPR_IBAT0U + i * 2])) {
// bat applies to this address
if (PowerPC::ppcState.spr[SPR_IBAT0U + i * 2] & batu) {
// bat entry valid
u32 offset = BAT_EA_OFFSET(addr);
u32 page = BAT_EA_11(addr);
page &= ~bl17;
page |= BATL_BRPN(PowerPC::ppcState.spr[SPR_IBAT0L + i * 2]);
// fixme: check access rights
result = page | offset;
return result;
if (BATU_BEPI(addr2) == BATU_BEPI(PowerPC::ppcState.spr[SPR_IBAT0U + i * 2]))
{
// bat applies to this address
if (PowerPC::ppcState.spr[SPR_IBAT0U + i * 2] & batu)
{
// bat entry valid
u32 offset = BAT_EA_OFFSET(addr);
u32 page = BAT_EA_11(addr);
page &= ~bl17;
page |= BATL_BRPN(PowerPC::ppcState.spr[SPR_IBAT0L + i * 2]);
// fixme: check access rights
result = page | offset;
return result;
}
}
}
}
return 0;
}
u32 TranslateAddress(const u32 _Address, const XCheckTLBFlag _Flag)
{
// TODO: Check for MSR data/instruction address translation flag before translating
u32 tlb_addr = TranslateBlockAddress(_Address, _Flag);
if (tlb_addr == 0)
{
tlb_addr = TranslatePageAddress(_Address, _Flag);
if (tlb_addr != 0)
{
return tlb_addr;
}
}
else
return tlb_addr;
return 0;
}
} // namespace

View File

@ -2797,6 +2797,7 @@ DEFINE_LUA_FUNCTION(emulua_loadrom, "filename")
game_ini.Get("Core", "CPUOnThread", &StartUp.bCPUThread, StartUp.bCPUThread);
game_ini.Get("Core", "SkipIdle", &StartUp.bSkipIdle, StartUp.bSkipIdle);
game_ini.Get("Core", "EnableFPRF", &StartUp.bEnableFPRF, StartUp.bEnableFPRF);
game_ini.Get("Core", "MMU", &StartUp.bMMU, StartUp.bMMU);
game_ini.Get("Core", "TLBHack", &StartUp.iTLBHack, StartUp.iTLBHack);
// Wii settings
if (StartUp.bWii)

View File

@ -82,32 +82,54 @@ void SingleStepInner(void)
static UGeckoInstruction instCode;
NPC = PC + sizeof(UGeckoInstruction);
instCode.hex = Memory::Read_Opcode(PC);
instCode.hex = Memory::Read_Opcode(PC);
UReg_MSR& msr = (UReg_MSR&)MSR;
if (msr.FP) //If FPU is enabled, just execute
m_opTable[instCode.OPCD](instCode);
else
if (instCode.hex != 0)
{
// check if we have to generate a FPU unavailable exception
if (!PPCTables::UsesFPU(instCode))
UReg_MSR& msr = (UReg_MSR&)MSR;
if (msr.FP) //If FPU is enabled, just execute
{
m_opTable[instCode.OPCD](instCode);
if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI)
{
PowerPC::CheckExceptions();
m_EndBlock = true;
}
}
else
{
PowerPC::ppcState.Exceptions |= EXCEPTION_FPU_UNAVAILABLE;
PowerPC::CheckExceptions();
m_EndBlock = true;
// check if we have to generate a FPU unavailable exception
if (!PPCTables::UsesFPU(instCode))
{
m_opTable[instCode.OPCD](instCode);
if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI)
{
PowerPC::CheckExceptions();
m_EndBlock = true;
}
}
else
{
PowerPC::ppcState.Exceptions |= EXCEPTION_FPU_UNAVAILABLE;
PowerPC::CheckExceptions();
m_EndBlock = true;
}
}
}
else
{
// Memory exception on instruction fetch
PowerPC::CheckExceptions();
m_EndBlock = true;
}
last_pc = PC;
PC = NPC;
#if defined(_DEBUG) || defined(DEBUGFAST)
if (PowerPC::ppcState.gpr[1] == 0)
{
printf("%i Corrupt stack", PowerPC::ppcState.DebugCount);
}
#if defined(_DEBUG) || defined(DEBUGFAST)
PowerPC::ppcState.DebugCount++;
#endif
patches();
@ -224,11 +246,15 @@ void Run()
void unknown_instruction(UGeckoInstruction _inst)
{
char disasm[256];
DisassembleGekko(Memory::ReadUnchecked_U32(last_pc), last_pc, disasm, 256);
printf("Last PC = %08x : %s\n", last_pc, disasm);
Dolphin_Debugger::PrintCallstack();
_dbg_assert_msg_(POWERPC, 0, "\nIntCPU: Unknown instr %08x at PC = %08x last_PC = %08x LR = %08x\n", _inst.hex, PC, last_pc, LR);
if (_inst.hex != 0)
{
char disasm[256];
DisassembleGekko(Memory::ReadUnchecked_U32(last_pc), last_pc, disasm, 256);
printf("Last PC = %08x : %s\n", last_pc, disasm);
Dolphin_Debugger::PrintCallstack();
_dbg_assert_msg_(POWERPC, 0, "\nIntCPU: Unknown instr %08x at PC = %08x last_PC = %08x LR = %08x\n", _inst.hex, PC, last_pc, LR);
}
}
} // namespace

View File

@ -58,100 +58,148 @@ u32 Helper_Get_EA_UX(const UGeckoInstruction _inst)
void lbz(UGeckoInstruction _inst)
{
m_GPR[_inst.RD] = (u32)Memory::Read_U8(Helper_Get_EA(_inst));
u32 temp = (u32)Memory::Read_U8(Helper_Get_EA(_inst));
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
m_GPR[_inst.RD] = temp;
}
void lbzu(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_U(_inst);
m_GPR[_inst.RD] = (u32)Memory::Read_U8(uAddress);
m_GPR[_inst.RA] = uAddress;
u32 temp = (u32)Memory::Read_U8(uAddress);
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RD] = temp;
m_GPR[_inst.RA] = uAddress;
}
}
void lfd(UGeckoInstruction _inst)
{
riPS0(_inst.FD) = Memory::Read_U64(Helper_Get_EA(_inst));
u64 temp = Memory::Read_U64(Helper_Get_EA(_inst));
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
riPS0(_inst.FD) = temp;
}
void lfdu(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_U(_inst);
riPS0(_inst.FD) = Memory::Read_U64(uAddress);
m_GPR[_inst.RA] = uAddress;
u64 temp = Memory::Read_U64(uAddress);
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
riPS0(_inst.FD) = temp;
m_GPR[_inst.RA] = uAddress;
}
}
void lfdux(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_UX(_inst);
riPS0(_inst.FD) = Memory::Read_U64(uAddress);
m_GPR[_inst.RA] = uAddress;
u64 temp = Memory::Read_U64(uAddress);
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
riPS0(_inst.FD) = temp;
m_GPR[_inst.RA] = uAddress;
}
}
void lfdx(UGeckoInstruction _inst)
{
riPS0(_inst.FD) = Memory::Read_U64(Helper_Get_EA_X(_inst));
u64 temp = Memory::Read_U64(Helper_Get_EA_X(_inst));
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
riPS0(_inst.FD) = temp;
}
void lfs(UGeckoInstruction _inst)
{
u32 uTemp = Memory::Read_U32(Helper_Get_EA(_inst));
double value = *(float*)&uTemp;
rPS0(_inst.FD) = value;
rPS1(_inst.FD) = value;
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
double value = *(float*)&uTemp;
rPS0(_inst.FD) = value;
rPS1(_inst.FD) = value;
}
}
void lfsu(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_U(_inst);
u32 uTemp = Memory::Read_U32(uAddress);
double value = *(float*)&uTemp;
rPS0(_inst.FD) = value;
rPS1(_inst.FD) = value;
m_GPR[_inst.RA] = uAddress;
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
double value = *(float*)&uTemp;
rPS0(_inst.FD) = value;
rPS1(_inst.FD) = value;
m_GPR[_inst.RA] = uAddress;
}
}
void lfsux(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_UX(_inst);
u32 uTemp = Memory::Read_U32(uAddress);
double value = *(float*)&uTemp;
rPS0(_inst.FD) = value;
rPS1(_inst.FD) = value;
m_GPR[_inst.RA] = uAddress;
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
double value = *(float*)&uTemp;
rPS0(_inst.FD) = value;
rPS1(_inst.FD) = value;
m_GPR[_inst.RA] = uAddress;
}
}
void lfsx(UGeckoInstruction _inst)
{
u32 uTemp = Memory::Read_U32(Helper_Get_EA_X(_inst));
double value = *(float*)&uTemp;
rPS0(_inst.FD) = value;
rPS1(_inst.FD) = value;
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
double value = *(float*)&uTemp;
rPS0(_inst.FD) = value;
rPS1(_inst.FD) = value;
}
}
void lha(UGeckoInstruction _inst)
{
m_GPR[_inst.RD] = (u32)(s32)(s16)Memory::Read_U16(Helper_Get_EA(_inst));
u32 temp = (u32)(s32)(s16)Memory::Read_U16(Helper_Get_EA(_inst));
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RD] = temp;
}
}
void lhau(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_U(_inst);
m_GPR[_inst.RD] = (u32)(s32)(s16)Memory::Read_U16(uAddress);
m_GPR[_inst.RA] = uAddress;
u32 temp = (u32)(s32)(s16)Memory::Read_U16(uAddress);
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RD] = temp;
m_GPR[_inst.RA] = uAddress;
}
}
void lhz(UGeckoInstruction _inst)
{
m_GPR[_inst.RD] = (u32)(u16)Memory::Read_U16(Helper_Get_EA(_inst));
u32 temp = (u32)(u16)Memory::Read_U16(Helper_Get_EA(_inst));
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RD] = temp;
}
}
void lhzu(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_U(_inst);
m_GPR[_inst.RD] = (u32)(u16)Memory::Read_U16(uAddress);
m_GPR[_inst.RA] = uAddress;
u32 temp = (u32)(u16)Memory::Read_U16(uAddress);
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RD] = temp;
m_GPR[_inst.RA] = uAddress;
}
}
// FIXME: lmw should do a total rollback if a DSI occurs
void lmw(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA(_inst);
@ -160,14 +208,18 @@ void lmw(UGeckoInstruction _inst)
u32 TempReg = Memory::Read_U32(uAddress);
if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI)
{
PanicAlert("DSI exception in lmv.");
PanicAlert("DSI exception in lmw");
NOTICE_LOG(POWERPC, "DSI exception in lmw");
return;
}
m_GPR[iReg] = TempReg;
else
{
m_GPR[iReg] = TempReg;
}
}
}
// FIXME: stmw should do a total rollback if a DSI occurs
void stmw(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA(_inst);
@ -175,14 +227,22 @@ void stmw(UGeckoInstruction _inst)
{
Memory::Write_U32(m_GPR[iReg], uAddress);
if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI)
{
PanicAlert("DSI exception in stmw");
NOTICE_LOG(POWERPC, "DSI exception in stmw");
return;
}
}
}
void lwz(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA(_inst);
m_GPR[_inst.RD] = Memory::Read_U32(uAddress);
u32 temp = Memory::Read_U32(uAddress);
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RD] = temp;
}
// hack to detect SelectThread loop
// should probably run a pass through memory instead before execution
@ -204,8 +264,12 @@ void lwz(UGeckoInstruction _inst)
void lwzu(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_U(_inst);
m_GPR[_inst.RD] = Memory::Read_U32(uAddress);
m_GPR[_inst.RA] = uAddress;
u32 temp = Memory::Read_U32(uAddress);
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RD] = temp;
m_GPR[_inst.RA] = uAddress;
}
}
void stb(UGeckoInstruction _inst)
@ -217,7 +281,10 @@ void stbu(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_U(_inst);
Memory::Write_U8((u8)m_GPR[_inst.RS], uAddress);
m_GPR[_inst.RA] = uAddress;
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RA] = uAddress;
}
}
void stfd(UGeckoInstruction _inst)
@ -229,7 +296,10 @@ void stfdu(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_U(_inst);
Memory::Write_U64(riPS0(_inst.FS), uAddress);
m_GPR[_inst.RA] = uAddress;
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RA] = uAddress;
}
}
void stfs(UGeckoInstruction _inst)
@ -244,7 +314,10 @@ void stfsu(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_U(_inst);
Memory::Write_U32(ConvertToSingle(riPS0(_inst.FS)), uAddress);
m_GPR[_inst.RA] = uAddress;
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RA] = uAddress;
}
}
void sth(UGeckoInstruction _inst)
@ -256,7 +329,10 @@ void sthu(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_U(_inst);
Memory::Write_U16((u16)m_GPR[_inst.RS], uAddress);
m_GPR[_inst.RA] = uAddress;
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RA] = uAddress;
}
}
void stw(UGeckoInstruction _inst)
@ -268,7 +344,10 @@ void stwu(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_U(_inst);
Memory::Write_U32(m_GPR[_inst.RS], uAddress);
m_GPR[_inst.RA] = uAddress;
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RA] = uAddress;
}
}
void dcba(UGeckoInstruction _inst)
@ -315,7 +394,8 @@ void dcbtst(UGeckoInstruction _inst)
void dcbz(UGeckoInstruction _inst)
{
// HACK but works... we think
Memory::Memset(Helper_Get_EA_X(_inst) & (~31), 0, 32);
if (!Core::g_CoreStartupParameter.bMMU)
Memory::Memset(Helper_Get_EA_X(_inst) & (~31), 0, 32); // Breaks Rogue Leader, fixes Super Mario Sunshine
}
// eciwx/ecowx technically should access the specified device
@ -330,7 +410,9 @@ void eciwx(UGeckoInstruction _inst)
EA = b + m_GPR[_inst.RB];
if (!(PowerPC::ppcState.spr[SPR_EAR] & 0x80000000))
{
PowerPC::ppcState.Exceptions |= EXCEPTION_DSI;
}
if (EA & 3)
PowerPC::ppcState.Exceptions |= EXCEPTION_ALIGNMENT;
@ -350,7 +432,9 @@ void ecowx(UGeckoInstruction _inst)
EA = b + m_GPR[_inst.RB];
if (!(PowerPC::ppcState.spr[SPR_EAR] & 0x80000000))
{
PowerPC::ppcState.Exceptions |= EXCEPTION_DSI;
}
if (EA & 3)
PowerPC::ppcState.Exceptions |= EXCEPTION_ALIGNMENT;
@ -378,42 +462,70 @@ void icbi(UGeckoInstruction _inst)
void lbzux(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_UX(_inst);
m_GPR[_inst.RD] = (u32)Memory::Read_U8(uAddress);
m_GPR[_inst.RA] = uAddress;
u32 temp = (u32)Memory::Read_U8(uAddress);
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RD] = temp;
m_GPR[_inst.RA] = uAddress;
}
}
void lbzx(UGeckoInstruction _inst)
{
m_GPR[_inst.RD] = (u32)Memory::Read_U8(Helper_Get_EA_X(_inst));
u32 temp = (u32)Memory::Read_U8(Helper_Get_EA_X(_inst));
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RD] = temp;
}
}
void lhaux(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_UX(_inst);
m_GPR[_inst.RD] = (s32)(s16)Memory::Read_U16(uAddress);
m_GPR[_inst.RA] = uAddress;
s32 temp = (s32)(s16)Memory::Read_U16(uAddress);
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RD] = temp;
m_GPR[_inst.RA] = uAddress;
}
}
void lhax(UGeckoInstruction _inst)
{
m_GPR[_inst.RD] = (s32)(s16)Memory::Read_U16(Helper_Get_EA_X(_inst));
s32 temp = (s32)(s16)Memory::Read_U16(Helper_Get_EA_X(_inst));
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RD] = temp;
}
}
void lhbrx(UGeckoInstruction _inst)
{
m_GPR[_inst.RD] = (u32)Common::swap16(Memory::Read_U16(Helper_Get_EA_X(_inst)));
u32 temp = (u32)Common::swap16(Memory::Read_U16(Helper_Get_EA_X(_inst)));
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RD] = temp;
}
}
void lhzux(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_UX(_inst);
m_GPR[_inst.RD] = (u32)Memory::Read_U16(uAddress);
m_GPR[_inst.RA] = uAddress;
u32 temp = (u32)Memory::Read_U16(uAddress);
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RD] = temp;
m_GPR[_inst.RA] = uAddress;
}
}
void lhzx(UGeckoInstruction _inst)
{
m_GPR[_inst.RD] = (u32)Memory::Read_U16(Helper_Get_EA_X(_inst));
u32 temp = (u32)Memory::Read_U16(Helper_Get_EA_X(_inst));
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RD] = temp;
}
}
void lswx(UGeckoInstruction _inst)
@ -426,27 +538,42 @@ void lswx(UGeckoInstruction _inst)
void lwbrx(UGeckoInstruction _inst)
{
m_GPR[_inst.RD] = Common::swap32(Memory::Read_U32(Helper_Get_EA_X(_inst)));
u32 temp = Common::swap32(Memory::Read_U32(Helper_Get_EA_X(_inst)));
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RD] = temp;
}
}
void lwzux(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_UX(_inst);
m_GPR[_inst.RD] = Memory::Read_U32(uAddress);
m_GPR[_inst.RA] = uAddress;
u32 temp = Memory::Read_U32(uAddress);
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RD] = temp;
m_GPR[_inst.RA] = uAddress;
}
}
void lwzx(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_X(_inst);
m_GPR[_inst.RD] = Memory::Read_U32(uAddress);
u32 temp = Memory::Read_U32(uAddress);
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RD] = temp;
}
}
void stbux(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_UX(_inst);
Memory::Write_U8((u8)m_GPR[_inst.RS], uAddress);
m_GPR[_inst.RA] = uAddress;
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RA] = uAddress;
}
}
void stbx(UGeckoInstruction _inst)
@ -458,7 +585,10 @@ void stfdux(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_UX(_inst);
Memory::Write_U64(riPS0(_inst.FS), uAddress);
m_GPR[_inst.RA] = uAddress;
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RA] = uAddress;
}
}
void stfdx(UGeckoInstruction _inst)
@ -482,7 +612,10 @@ void stfsux(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_UX(_inst);
Memory::Write_U32(ConvertToSingle(riPS0(_inst.FS)), uAddress);
m_GPR[_inst.RA] = uAddress;
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RA] = uAddress;
}
}
void stfsx(UGeckoInstruction _inst)
@ -499,7 +632,10 @@ void sthux(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_UX(_inst);
Memory::Write_U16((u16)m_GPR[_inst.RS], uAddress);
m_GPR[_inst.RA] = uAddress;
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RA] = uAddress;
}
}
void sthx(UGeckoInstruction _inst)
@ -509,7 +645,7 @@ void sthx(UGeckoInstruction _inst)
// __________________________________________________________________________________________________
// lswi - bizarro string instruction
//
// FIXME: Should rollback if a DSI occurs
void lswi(UGeckoInstruction _inst)
{
u32 EA;
@ -555,7 +691,7 @@ void lswi(UGeckoInstruction _inst)
// todo : optimize ?
// __________________________________________________________________________________________________
// stswi - bizarro string instruction
//
// FIXME: Should rollback if a DSI occurs
void stswi(UGeckoInstruction _inst)
{
u32 EA;
@ -581,7 +717,9 @@ void stswi(UGeckoInstruction _inst)
}
Memory::Write_U8((m_GPR[r] >> (24 - i)) & 0xFF, EA);
if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI)
{
return;
}
i += 8;
if (i == 32)
@ -612,10 +750,13 @@ void stwbrx(UGeckoInstruction _inst)
void lwarx(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_X(_inst);
m_GPR[_inst.RD] = Memory::Read_U32(uAddress);
g_bReserve = true;
g_reserveAddr = uAddress;
u32 temp = Memory::Read_U32(uAddress);
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RD] = temp;
g_bReserve = true;
g_reserveAddr = uAddress;
}
}
void stwcxd(UGeckoInstruction _inst)
@ -626,9 +767,12 @@ void stwcxd(UGeckoInstruction _inst)
uAddress = Helper_Get_EA_X(_inst);
if (uAddress == g_reserveAddr) {
Memory::Write_U32(m_GPR[_inst.RS], uAddress);
g_bReserve = false;
SetCRField(0, 2 | GetXER_SO());
return;
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
g_bReserve = false;
SetCRField(0, 2 | GetXER_SO());
return;
}
}
}
@ -639,7 +783,10 @@ void stwux(UGeckoInstruction _inst)
{
u32 uAddress = Helper_Get_EA_UX(_inst);
Memory::Write_U32(m_GPR[_inst.RS], uAddress);
m_GPR[_inst.RA] = uAddress;
if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI))
{
m_GPR[_inst.RA] = uAddress;
}
}
void stwx(UGeckoInstruction _inst)
@ -665,14 +812,7 @@ void tlbie(UGeckoInstruction _inst)
{
// Invalidate TLB entry
u32 _Address = m_GPR[_inst.RB];
for (int i = 0; i < 16; i++)
{
if ((_Address & ~0xfff) == (PowerPC::ppcState.tlb_va[(PowerPC::ppcState.tlb_last + i) & 15]))
{
PowerPC::ppcState.tlb_pa[(PowerPC::ppcState.tlb_last + i) & 15] = 0;
PowerPC::ppcState.tlb_va[(PowerPC::ppcState.tlb_last + i) & 15] = 0;
}
}
Memory::InvalidateTLBEntry(_Address);
}
void tlbsync(UGeckoInstruction _inst)

View File

@ -153,7 +153,6 @@ float Helper_Dequantize(const u32 _Addr, const EQuantizeType _quantizeType,
fResult = 0;
break;
}
return fResult;
}
@ -170,12 +169,23 @@ void psq_l(UGeckoInstruction _inst)
if (_inst.W == 0)
{
rPS0(_inst.RS) = Helper_Dequantize(EA, ldType, ldScale);
rPS1(_inst.RS) = Helper_Dequantize(EA+c, ldType, ldScale);
float ps0 = Helper_Dequantize(EA, ldType, ldScale);
float ps1 = Helper_Dequantize(EA+c, ldType, ldScale);
if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI)
{
return;
}
rPS0(_inst.RS) = ps0;
rPS1(_inst.RS) = ps1;
}
else
{
rPS0(_inst.RS) = Helper_Dequantize(EA, ldType, ldScale);
float ps0 = Helper_Dequantize(EA, ldType, ldScale);
if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI)
{
return;
}
rPS0(_inst.RS) = ps0;
rPS1(_inst.RS) = 1.0f;
}
}
@ -193,12 +203,23 @@ void psq_lu(UGeckoInstruction _inst)
if (_inst.W == 0)
{
rPS0(_inst.RS) = Helper_Dequantize( EA, ldType, ldScale );
rPS1(_inst.RS) = Helper_Dequantize( EA+c, ldType, ldScale );
float ps0 = Helper_Dequantize( EA, ldType, ldScale );
float ps1 = Helper_Dequantize( EA+c, ldType, ldScale );
if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI)
{
return;
}
rPS0(_inst.RS) = ps0;
rPS1(_inst.RS) = ps1;
}
else
{
rPS0(_inst.RS) = Helper_Dequantize( EA, ldType, ldScale );
float ps0 = Helper_Dequantize( EA, ldType, ldScale );
if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI)
{
return;
}
rPS0(_inst.RS) = ps0;
rPS1(_inst.RS) = 1.0f;
}
m_GPR[_inst.RA] = EA;
@ -246,6 +267,10 @@ void psq_stu(UGeckoInstruction _inst)
{
Helper_Quantize(EA, rPS0(_inst.RS), stType, stScale);
}
if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI)
{
return;
}
m_GPR[_inst.RA] = EA;
}
@ -262,13 +287,29 @@ void psq_lx(UGeckoInstruction _inst)
if (_inst.Wx == 0)
{
rPS0(_inst.RS) = Helper_Dequantize( EA, ldType, ldScale );
rPS1(_inst.RS) = Helper_Dequantize( EA+c, ldType, ldScale );
float ps0 = Helper_Dequantize( EA, ldType, ldScale );
float ps1 = Helper_Dequantize( EA+c, ldType, ldScale );
if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI)
{
return;
}
rPS0(_inst.RS) = ps0;
rPS1(_inst.RS) = ps1;
}
else
{
rPS0(_inst.RS) = Helper_Dequantize( EA, ldType, ldScale );
rPS1(_inst.RS) = 1.0f;
float ps0 = Helper_Dequantize( EA, ldType, ldScale );
float ps1 = 1.0f;
if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI)
{
return;
}
rPS0(_inst.RS) = ps0;
rPS1(_inst.RS) = ps1;
}
}
@ -307,12 +348,23 @@ void psq_lux(UGeckoInstruction _inst)
if (_inst.Wx == 0)
{
rPS0(_inst.RS) = Helper_Dequantize( EA, ldType, ldScale );
rPS1(_inst.RS) = Helper_Dequantize( EA+c, ldType, ldScale );
float ps0 = Helper_Dequantize( EA, ldType, ldScale );
float ps1 = Helper_Dequantize( EA+c, ldType, ldScale );
if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI)
{
return;
}
rPS0(_inst.RS) = ps0;
rPS1(_inst.RS) = ps1;
}
else
{
rPS0(_inst.RS) = Helper_Dequantize( EA, ldType, ldScale );
float ps0 = Helper_Dequantize( EA, ldType, ldScale );
if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI)
{
return;
}
rPS0(_inst.RS) = ps0;
rPS1(_inst.RS) = 1.0f;
}
m_GPR[_inst.RA] = EA;
@ -338,6 +390,10 @@ void psq_stux(UGeckoInstruction _inst)
{
Helper_Quantize(EA, rPS0(_inst.RS), stType, stScale);
}
if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI)
{
return;
}
m_GPR[_inst.RA] = EA;
} // namespace=======

View File

@ -63,40 +63,40 @@ static GekkoOPTemplate primarytable[] =
{28, Interpreter::andi_rc, {"andi_rc", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_SET_CR0}},
{29, Interpreter::andis_rc, {"andis_rc", OPTYPE_INTEGER, FL_OUT_A | FL_IN_S | FL_SET_CR0}},
{32, Interpreter::lwz, {"lwz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
{33, Interpreter::lwzu, {"lwzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
{34, Interpreter::lbz, {"lbz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
{35, Interpreter::lbzu, {"lbzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
{40, Interpreter::lhz, {"lhz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
{41, Interpreter::lhzu, {"lhzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
{32, Interpreter::lwz, {"lwz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A | FL_LOADSTORE}},
{33, Interpreter::lwzu, {"lwzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_LOADSTORE}},
{34, Interpreter::lbz, {"lbz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A | FL_LOADSTORE}},
{35, Interpreter::lbzu, {"lbzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_LOADSTORE}},
{40, Interpreter::lhz, {"lhz", OPTYPE_LOAD, FL_OUT_D | FL_IN_A | FL_LOADSTORE}},
{41, Interpreter::lhzu, {"lhzu", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_LOADSTORE}},
{42, Interpreter::lha, {"lha", OPTYPE_LOAD, FL_OUT_D | FL_IN_A}},
{43, Interpreter::lhau, {"lhau", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A}},
{42, Interpreter::lha, {"lha", OPTYPE_LOAD, FL_OUT_D | FL_IN_A | FL_LOADSTORE}},
{43, Interpreter::lhau, {"lhau", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_LOADSTORE}},
{44, Interpreter::sth, {"sth", OPTYPE_STORE, FL_IN_A | FL_IN_S}},
{45, Interpreter::sthu, {"sthu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}},
{36, Interpreter::stw, {"stw", OPTYPE_STORE, FL_IN_A | FL_IN_S}},
{37, Interpreter::stwu, {"stwu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}},
{38, Interpreter::stb, {"stb", OPTYPE_STORE, FL_IN_A | FL_IN_S}},
{39, Interpreter::stbu, {"stbu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S}},
{44, Interpreter::sth, {"sth", OPTYPE_STORE, FL_IN_A | FL_IN_S | FL_LOADSTORE}},
{45, Interpreter::sthu, {"sthu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S | FL_LOADSTORE}},
{36, Interpreter::stw, {"stw", OPTYPE_STORE, FL_IN_A | FL_IN_S | FL_LOADSTORE}},
{37, Interpreter::stwu, {"stwu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S | FL_LOADSTORE}},
{38, Interpreter::stb, {"stb", OPTYPE_STORE, FL_IN_A | FL_IN_S | FL_LOADSTORE}},
{39, Interpreter::stbu, {"stbu", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_S | FL_LOADSTORE}},
{46, Interpreter::lmw, {"lmw", OPTYPE_SYSTEM, FL_EVIL, 10}},
{47, Interpreter::stmw, {"stmw", OPTYPE_SYSTEM, FL_EVIL, 10}},
{46, Interpreter::lmw, {"lmw", OPTYPE_SYSTEM, FL_EVIL | FL_LOADSTORE, 10}},
{47, Interpreter::stmw, {"stmw", OPTYPE_SYSTEM, FL_EVIL | FL_LOADSTORE, 10}},
{48, Interpreter::lfs, {"lfs", OPTYPE_LOADFP, FL_IN_A | FL_USE_FPU}},
{49, Interpreter::lfsu, {"lfsu", OPTYPE_LOADFP, FL_OUT_A | FL_IN_A | FL_USE_FPU}},
{50, Interpreter::lfd, {"lfd", OPTYPE_LOADFP, FL_IN_A | FL_USE_FPU}},
{51, Interpreter::lfdu, {"lfdu", OPTYPE_LOADFP, FL_OUT_A | FL_IN_A | FL_USE_FPU}},
{48, Interpreter::lfs, {"lfs", OPTYPE_LOADFP, FL_IN_A | FL_USE_FPU | FL_LOADSTORE}},
{49, Interpreter::lfsu, {"lfsu", OPTYPE_LOADFP, FL_OUT_A | FL_IN_A | FL_USE_FPU | FL_LOADSTORE}},
{50, Interpreter::lfd, {"lfd", OPTYPE_LOADFP, FL_IN_A | FL_USE_FPU | FL_LOADSTORE}},
{51, Interpreter::lfdu, {"lfdu", OPTYPE_LOADFP, FL_OUT_A | FL_IN_A | FL_USE_FPU | FL_LOADSTORE}},
{52, Interpreter::stfs, {"stfs", OPTYPE_STOREFP, FL_IN_A | FL_USE_FPU}},
{53, Interpreter::stfsu, {"stfsu", OPTYPE_STOREFP, FL_OUT_A | FL_IN_A | FL_USE_FPU}},
{54, Interpreter::stfd, {"stfd", OPTYPE_STOREFP, FL_IN_A | FL_USE_FPU}},
{55, Interpreter::stfdu, {"stfdu", OPTYPE_STOREFP, FL_OUT_A | FL_IN_A | FL_USE_FPU}},
{52, Interpreter::stfs, {"stfs", OPTYPE_STOREFP, FL_IN_A | FL_USE_FPU | FL_LOADSTORE}},
{53, Interpreter::stfsu, {"stfsu", OPTYPE_STOREFP, FL_OUT_A | FL_IN_A | FL_USE_FPU | FL_LOADSTORE}},
{54, Interpreter::stfd, {"stfd", OPTYPE_STOREFP, FL_IN_A | FL_USE_FPU | FL_LOADSTORE}},
{55, Interpreter::stfdu, {"stfdu", OPTYPE_STOREFP, FL_OUT_A | FL_IN_A | FL_USE_FPU | FL_LOADSTORE}},
{56, Interpreter::psq_l, {"psq_l", OPTYPE_PS, FL_IN_A | FL_USE_FPU}},
{57, Interpreter::psq_lu, {"psq_lu", OPTYPE_PS, FL_OUT_A | FL_IN_A | FL_USE_FPU}},
{60, Interpreter::psq_st, {"psq_st", OPTYPE_PS, FL_IN_A | FL_USE_FPU}},
{61, Interpreter::psq_stu, {"psq_stu", OPTYPE_PS, FL_OUT_A | FL_IN_A | FL_USE_FPU}},
{56, Interpreter::psq_l, {"psq_l", OPTYPE_PS, FL_IN_A | FL_USE_FPU | FL_LOADSTORE}},
{57, Interpreter::psq_lu, {"psq_lu", OPTYPE_PS, FL_OUT_A | FL_IN_A | FL_USE_FPU | FL_LOADSTORE}},
{60, Interpreter::psq_st, {"psq_st", OPTYPE_PS, FL_IN_A | FL_USE_FPU | FL_LOADSTORE}},
{61, Interpreter::psq_stu, {"psq_stu", OPTYPE_PS, FL_OUT_A | FL_IN_A | FL_USE_FPU | FL_LOADSTORE}},
//missing: 0, 5, 6, 9, 22, 30, 62, 58
{0, Interpreter::unknown_instruction, {"unknown_instruction", OPTYPE_UNKNOWN, 0}},
@ -151,10 +151,10 @@ static GekkoOPTemplate table4_2[] =
static GekkoOPTemplate table4_3[] =
{
{6, Interpreter::psq_lx, {"psq_lx", OPTYPE_PS, FL_USE_FPU}},
{7, Interpreter::psq_stx, {"psq_stx", OPTYPE_PS, FL_USE_FPU}},
{38, Interpreter::psq_lux, {"psq_lux", OPTYPE_PS, FL_USE_FPU}},
{39, Interpreter::psq_stux, {"psq_stux", OPTYPE_PS, FL_USE_FPU}},
{6, Interpreter::psq_lx, {"psq_lx", OPTYPE_PS, FL_USE_FPU | FL_LOADSTORE}},
{7, Interpreter::psq_stx, {"psq_stx", OPTYPE_PS, FL_USE_FPU | FL_LOADSTORE}},
{38, Interpreter::psq_lux, {"psq_lux", OPTYPE_PS, FL_USE_FPU | FL_LOADSTORE}},
{39, Interpreter::psq_stux, {"psq_stux", OPTYPE_PS, FL_USE_FPU | FL_LOADSTORE}},
};
static GekkoOPTemplate table19[] =
@ -207,63 +207,63 @@ static GekkoOPTemplate table31[] =
{1014, Interpreter::dcbz, {"dcbz", OPTYPE_DCACHE, 0, 4}},
//load word
{23, Interpreter::lwzx, {"lwzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
{55, Interpreter::lwzux, {"lwzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
{23, Interpreter::lwzx, {"lwzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B | FL_LOADSTORE}},
{55, Interpreter::lwzux, {"lwzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B | FL_LOADSTORE}},
//load halfword
{279, Interpreter::lhzx, {"lhzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
{311, Interpreter::lhzux, {"lhzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
{279, Interpreter::lhzx, {"lhzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B | FL_LOADSTORE}},
{311, Interpreter::lhzux, {"lhzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B | FL_LOADSTORE}},
//load halfword signextend
{343, Interpreter::lhax, {"lhax", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
{375, Interpreter::lhaux, {"lhaux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
{343, Interpreter::lhax, {"lhax", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B | FL_LOADSTORE}},
{375, Interpreter::lhaux, {"lhaux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B | FL_LOADSTORE}},
//load byte
{87, Interpreter::lbzx, {"lbzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
{119, Interpreter::lbzux, {"lbzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B}},
{87, Interpreter::lbzx, {"lbzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B | FL_LOADSTORE}},
{119, Interpreter::lbzux, {"lbzux", OPTYPE_LOAD, FL_OUT_D | FL_OUT_A | FL_IN_A | FL_IN_B | FL_LOADSTORE}},
//load byte reverse
{534, Interpreter::lwbrx, {"lwbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
{790, Interpreter::lhbrx, {"lhbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}},
{534, Interpreter::lwbrx, {"lwbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B | FL_LOADSTORE}},
{790, Interpreter::lhbrx, {"lhbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B | FL_LOADSTORE}},
// Conditional load/store (Wii SMP)
{150, Interpreter::stwcxd, {"stwcxd", OPTYPE_STORE, FL_EVIL | FL_SET_CR0}},
{20, Interpreter::lwarx, {"lwarx", OPTYPE_LOAD, FL_EVIL | FL_OUT_D | FL_IN_A0B | FL_SET_CR0}},
{150, Interpreter::stwcxd, {"stwcxd", OPTYPE_STORE, FL_EVIL | FL_SET_CR0 | FL_LOADSTORE}},
{20, Interpreter::lwarx, {"lwarx", OPTYPE_LOAD, FL_EVIL | FL_OUT_D | FL_IN_A0B | FL_SET_CR0 | FL_LOADSTORE}},
//load string (Inst these)
{533, Interpreter::lswx, {"lswx", OPTYPE_LOAD, FL_EVIL | FL_IN_A | FL_OUT_D}},
{597, Interpreter::lswi, {"lswi", OPTYPE_LOAD, FL_EVIL | FL_IN_AB | FL_OUT_D}},
{533, Interpreter::lswx, {"lswx", OPTYPE_LOAD, FL_EVIL | FL_IN_A | FL_OUT_D | FL_LOADSTORE}},
{597, Interpreter::lswi, {"lswi", OPTYPE_LOAD, FL_EVIL | FL_IN_AB | FL_OUT_D | FL_LOADSTORE}},
//store word
{151, Interpreter::stwx, {"stwx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
{183, Interpreter::stwux, {"stwux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
{151, Interpreter::stwx, {"stwx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B | FL_LOADSTORE}},
{183, Interpreter::stwux, {"stwux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B | FL_LOADSTORE}},
//store halfword
{407, Interpreter::sthx, {"sthx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
{439, Interpreter::sthux, {"sthux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
{407, Interpreter::sthx, {"sthx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B | FL_LOADSTORE}},
{439, Interpreter::sthux, {"sthux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B | FL_LOADSTORE}},
//store byte
{215, Interpreter::stbx, {"stbx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
{247, Interpreter::stbux, {"stbux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B}},
{215, Interpreter::stbx, {"stbx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B | FL_LOADSTORE}},
{247, Interpreter::stbux, {"stbux", OPTYPE_STORE, FL_OUT_A | FL_IN_A | FL_IN_B | FL_LOADSTORE}},
//store bytereverse
{662, Interpreter::stwbrx, {"stwbrx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}},
{918, Interpreter::sthbrx, {"sthbrx", OPTYPE_STORE, FL_IN_A | FL_IN_B}},
{662, Interpreter::stwbrx, {"stwbrx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B | FL_LOADSTORE}},
{918, Interpreter::sthbrx, {"sthbrx", OPTYPE_STORE, FL_IN_A | FL_IN_B | FL_LOADSTORE}},
{661, Interpreter::stswx, {"stswx", OPTYPE_STORE, FL_EVIL}},
{725, Interpreter::stswi, {"stswi", OPTYPE_STORE, FL_EVIL}},
{661, Interpreter::stswx, {"stswx", OPTYPE_STORE, FL_EVIL | FL_LOADSTORE}},
{725, Interpreter::stswi, {"stswi", OPTYPE_STORE, FL_EVIL | FL_LOADSTORE}},
// fp load/store
{535, Interpreter::lfsx, {"lfsx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B | FL_USE_FPU}},
{567, Interpreter::lfsux, {"lfsux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B | FL_USE_FPU}},
{599, Interpreter::lfdx, {"lfdx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B | FL_USE_FPU}},
{631, Interpreter::lfdux, {"lfdux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B | FL_USE_FPU}},
{535, Interpreter::lfsx, {"lfsx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B | FL_USE_FPU | FL_LOADSTORE}},
{567, Interpreter::lfsux, {"lfsux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B | FL_USE_FPU | FL_LOADSTORE}},
{599, Interpreter::lfdx, {"lfdx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B | FL_USE_FPU | FL_LOADSTORE}},
{631, Interpreter::lfdux, {"lfdux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B | FL_USE_FPU | FL_LOADSTORE}},
{663, Interpreter::stfsx, {"stfsx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B | FL_USE_FPU}},
{695, Interpreter::stfsux, {"stfsux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B | FL_USE_FPU}},
{727, Interpreter::stfdx, {"stfdx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B | FL_USE_FPU}},
{759, Interpreter::stfdux, {"stfdux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B | FL_USE_FPU}},
{983, Interpreter::stfiwx, {"stfiwx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B | FL_USE_FPU}},
{663, Interpreter::stfsx, {"stfsx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B | FL_USE_FPU | FL_LOADSTORE}},
{695, Interpreter::stfsux, {"stfsux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B | FL_USE_FPU | FL_LOADSTORE}},
{727, Interpreter::stfdx, {"stfdx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B | FL_USE_FPU | FL_LOADSTORE}},
{759, Interpreter::stfdux, {"stfdux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B | FL_USE_FPU | FL_LOADSTORE}},
{983, Interpreter::stfiwx, {"stfiwx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B | FL_USE_FPU | FL_LOADSTORE}},
{19, Interpreter::mfcr, {"mfcr", OPTYPE_SYSTEM, FL_OUT_D}},
{83, Interpreter::mfmsr, {"mfmsr", OPTYPE_SYSTEM, FL_OUT_D}},

View File

@ -175,17 +175,22 @@ void Jit64::Init()
where this cause problems, so I'm enabling this by default, since I seem to get perhaps as much as 20% more
fps with this option enabled. If you suspect that this option cause problems you can also disable it from the
debugging window. */
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bEnableDebugging)
if (Core::g_CoreStartupParameter.bEnableDebugging)
{
jo.enableBlocklink = false;
SConfig::GetInstance().m_LocalCoreStartupParameter.bSkipIdle = false;
Core::g_CoreStartupParameter.bSkipIdle = false;
}
else
{
jo.enableBlocklink = true;
if (!Core::g_CoreStartupParameter.bJITBlockLinking)
{
jo.enableBlocklink = false;
}
else
jo.enableBlocklink = !Core::g_CoreStartupParameter.bMMU;
}
#ifdef _M_X64
jo.enableFastMem = SConfig::GetInstance().m_LocalCoreStartupParameter.bUseFastMem;
jo.enableFastMem = Core::g_CoreStartupParameter.bUseFastMem;
#else
jo.enableFastMem = false;
#endif
@ -194,16 +199,11 @@ void Jit64::Init()
jo.optimizeGatherPipe = true;
jo.fastInterrupts = false;
jo.accurateSinglePrecision = true;
js.memcheck = Core::g_CoreStartupParameter.bMMU;
gpr.SetEmitter(this);
fpr.SetEmitter(this);
if (Core::g_CoreStartupParameter.bJITBlockLinking)
{
jo.enableBlocklink = false;
SuccessAlert("Your game was started without JIT Block Linking");
}
trampolines.Init();
AllocCodeSpace(CODE_SIZE);
@ -218,7 +218,6 @@ void Jit64::ClearCache()
ClearCodeSpace();
}
void Jit64::Shutdown()
{
FreeCodeSpace();
@ -349,11 +348,10 @@ void Jit64::WriteRfiExitDestInEAX()
JMP(asm_routines.testExceptions, true);
}
void Jit64::WriteExceptionExit(u32 exception)
void Jit64::WriteExceptionExit()
{
Cleanup();
OR(32, M(&PowerPC::ppcState.Exceptions), Imm32(exception));
MOV(32, M(&PC), Imm32(js.compilerPC + 4));
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
JMP(asm_routines.testExceptions, true);
}
@ -361,7 +359,6 @@ void STACKALIGN Jit64::Run()
{
CompiledCode pExecAddr = (CompiledCode)asm_routines.enterCode;
pExecAddr();
//Will return when PowerPC::state changes
}
void Jit64::SingleStep()
@ -370,7 +367,7 @@ void Jit64::SingleStep()
pExecAddr();
}
void Jit64::Trace(PPCAnalyst::CodeBuffer *code_buf, u32 em_address)
void Jit64::Trace()
{
char regs[500] = "";
char fregs[750] = "";
@ -392,11 +389,11 @@ void Jit64::Trace(PPCAnalyst::CodeBuffer *code_buf, u32 em_address)
strncat(fregs, reg, 750);
}
#endif
const PPCAnalyst::CodeOp &op = code_buf->codebuffer[0];
char ppcInst[256];
DisassembleGekko(op.inst.hex, em_address, ppcInst, 256);
NOTICE_LOG(DYNA_REC, "JIT64 PC: %08x SRR0: %08x SRR1: %08x CRfast: %02x%02x%02x%02x%02x%02x%02x%02x FPSCR: %08x MSR: %08x LR: %08x %s %s %08x %s", PC, SRR0, SRR1, PowerPC::ppcState.cr_fast[0], PowerPC::ppcState.cr_fast[1], PowerPC::ppcState.cr_fast[2], PowerPC::ppcState.cr_fast[3], PowerPC::ppcState.cr_fast[4], PowerPC::ppcState.cr_fast[5], PowerPC::ppcState.cr_fast[6], PowerPC::ppcState.cr_fast[7], PowerPC::ppcState.fpscr, PowerPC::ppcState.msr, PowerPC::ppcState.spr[8], regs, fregs, op.inst.hex, ppcInst);
NOTICE_LOG(DYNA_REC, "JIT64 PC: %08x SRR0: %08x SRR1: %08x CRfast: %02x%02x%02x%02x%02x%02x%02x%02x FPSCR: %08x MSR: %08x LR: %08x %s %s",
PC, SRR0, SRR1, PowerPC::ppcState.cr_fast[0], PowerPC::ppcState.cr_fast[1], PowerPC::ppcState.cr_fast[2], PowerPC::ppcState.cr_fast[3],
PowerPC::ppcState.cr_fast[4], PowerPC::ppcState.cr_fast[5], PowerPC::ppcState.cr_fast[6], PowerPC::ppcState.cr_fast[7], PowerPC::ppcState.fpscr,
PowerPC::ppcState.msr, PowerPC::ppcState.spr[8], regs, fregs);
}
void STACKALIGN Jit64::Jit(u32 em_address)
@ -410,21 +407,36 @@ void STACKALIGN Jit64::Jit(u32 em_address)
blocks.FinalizeBlock(block_num, jo.enableBlocklink, DoJit(em_address, &code_buffer, b));
}
const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBlock *b)
{
int blockSize = code_buf->GetSize();
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bEnableDebugging)
// Memory exception on instruction fetch
bool memory_exception = false;
// A broken block is a block that does not end in a branch
bool broken_block = false;
if (Core::g_CoreStartupParameter.bEnableDebugging)
{
// Comment out the following to disable breakpoints (speed-up)
blockSize = 1;
Trace(code_buf, em_address);
broken_block = true;
Trace();
}
if (em_address == 0)
PanicAlert("ERROR : Trying to compile at 0. LR=%08x", LR);
if (Core::g_CoreStartupParameter.bMMU && (em_address >> 28) == 0x7)
{
if (!Memory::TranslateAddress(em_address, Memory::XCheckTLBFlag::FLAG_OPCODE))
{
// Memory exception occurred during instruction fetch
memory_exception = true;
}
}
int size = 0;
js.isLastInstruction = false;
js.blockStart = em_address;
@ -435,7 +447,12 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
//Analyze the block, collect all instructions it is made of (including inlining,
//if that is enabled), reorder instructions for optimal performance, and join joinable instructions.
u32 nextPC = PPCAnalyst::Flatten(em_address, &size, &js.st, &js.gpa, &js.fpa, code_buf, blockSize);
u32 nextPC = em_address;
if (!memory_exception)
{
// If there is a memory exception inside a block (broken_block==true), compile up to that instruction.
nextPC = PPCAnalyst::Flatten(em_address, &size, &js.st, &js.gpa, &js.fpa, broken_block, code_buf, blockSize);
}
PPCAnalyst::CodeOp *ops = code_buf->codebuffer;
@ -465,16 +482,6 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
SetJumpTarget(b1);
}
if (false && jo.fastInterrupts)
{
// This does NOT yet work.
TEST(32, M(&PowerPC::ppcState.Exceptions), Imm32(0xFFFFFFFF));
FixupBranch b1 = J_CC(CC_Z);
MOV(32, M(&PC), Imm32(js.blockStart));
JMP(asm_routines.testExceptions, true);
SetJumpTarget(b1);
}
// Conditionally add profiling code.
if (Profiler::g_ProfileBlocks) {
ADD(32, M(&b->runCount), Imm8(1));
@ -498,18 +505,22 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
gpr.Start(js.gpa);
fpr.Start(js.fpa);
js.downcountAmount = js.st.numCycles;
if (!SConfig::GetInstance().m_LocalCoreStartupParameter.bEnableDebugging)
js.downcountAmount = js.st.numCycles + PatchEngine::GetSpeedhackCycles(em_address);
js.downcountAmount = 0;
if (!Core::g_CoreStartupParameter.bEnableDebugging)
js.downcountAmount += PatchEngine::GetSpeedhackCycles(em_address);
js.skipnext = false;
js.blockSize = size;
js.compilerPC = nextPC;
// Translate instructions
for (int i = 0; i < (int)size; i++)
{
js.compilerPC = ops[i].address;
js.op = &ops[i];
js.instructionNumber = i;
const GekkoOPInfo *opinfo = GetOpInfo(ops[i].inst);
js.downcountAmount += (opinfo->numCyclesMinusOne + 1);
if (i == (int)size - 1)
{
// WARNING - cmp->branch merging will screw this up.
@ -539,8 +550,40 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
}
if (!ops[i].skip)
{
if (js.memcheck && (opinfo->flags & FL_LOADSTORE))
{
// If a memory exception occurs, the exception handler will read
// from PC. Update PC with the latest value in case that happens.
MOV(32, M(&PC), Imm32(ops[i].address));
}
if (js.memcheck && (opinfo->flags & FL_USE_FPU))
{
//This instruction uses FPU - needs to add FP exception bailout
TEST(32, M(&PowerPC::ppcState.msr), Imm32(1 << 13)); // Test FP enabled bit
FixupBranch b1 = J_CC(CC_NZ);
// If a FPU exception occurs, the exception handler will read
// from PC. Update PC with the latest value in case that happens.
MOV(32, M(&PC), Imm32(ops[i].address));
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
JMP(asm_routines.fpException, true);
SetJumpTarget(b1);
}
Jit64Tables::CompileInstruction(ops[i]);
if (js.memcheck && (opinfo->flags & FL_LOADSTORE))
{
TEST(32, M(&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_DSI));
FixupBranch noMemException = J_CC(CC_Z);
WriteExceptionExit();
SetJumpTarget(noMemException);
}
}
#if defined(_DEBUG) || defined(DEBUGFAST)
if (gpr.SanityCheck() || fpr.SanityCheck())
{
@ -558,7 +601,14 @@ const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
if (js.cancel)
break;
}
if (SConfig::GetInstance().m_LocalCoreStartupParameter.bEnableDebugging)
if (memory_exception)
{
ABI_CallFunctionC(reinterpret_cast<void *>(&Memory::GenerateISIException_JIT), js.compilerPC);
WriteExceptionExit();
}
if (broken_block)
{
gpr.Flush(FLUSH_ALL);
fpr.Flush(FLUSH_ALL);

View File

@ -52,6 +52,16 @@
Core::g_CoreStartupParameter.bJIT##type##Off) \
{Default(inst); return;}
#define MEMCHECK_START \
FixupBranch memException; \
if (js.memcheck) \
{ TEST(32, M(&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_DSI)); \
memException = J_CC(CC_NZ); }
#define MEMCHECK_END \
if (js.memcheck) \
SetJumpTarget(memException);
class Jit64 : public JitBase
{
private:
@ -69,6 +79,8 @@ private:
int block_flags;
bool isLastInstruction;
bool memcheck;
bool broken_block;
int fifoBytesThisBlock;
@ -105,12 +117,12 @@ public:
JitBlockCache *GetBlockCache() { return &blocks; }
void NotifyBreakpoint(u32 em_address, bool set);
void Trace(PPCAnalyst::CodeBuffer *code_buffer, u32 em_address);
void Trace();
void ClearCache();
const u8 *GetDispatcher() {
return asm_routines.dispatcher; // asm_routines.dispatcher
return asm_routines.dispatcher;
}
const CommonAsmRoutines *GetAsmRoutines() {
return &asm_routines;
@ -132,7 +144,7 @@ public:
void WriteExit(u32 destination, int exit_num);
void WriteExitDestInEAX(int exit_num);
void WriteExceptionExit(u32 exception);
void WriteExceptionExit();
void WriteRfiExitDestInEAX();
void WriteCallInterpreter(UGeckoInstruction _inst);
void Cleanup();

View File

@ -389,8 +389,6 @@ void CompileInstruction(PPCAnalyst::CodeOp & op)
#endif
info->compileCount++;
info->lastUse = jit->js.compilerPC;
} else {
PanicAlert("Tried to compile illegal (or unknown) instruction %08x, at %08x", op.inst.hex, jit->js.compilerPC);
}
}

View File

@ -129,8 +129,6 @@ void Jit64AsmRoutineManager::Generate()
//FP blocks test for FPU available, jump here if false
fpException = AlignCode4();
MOV(32, R(EAX), M(&PC));
MOV(32, M(&NPC), R(EAX));
OR(32, M(&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_FPU_UNAVAILABLE));
ABI_CallFunction(reinterpret_cast<void *>(&PowerPC::CheckExceptions));
MOV(32, R(EAX), M(&NPC));

View File

@ -47,7 +47,9 @@ void Jit64::sc(UGeckoInstruction inst)
gpr.Flush(FLUSH_ALL);
fpr.Flush(FLUSH_ALL);
WriteExceptionExit(EXCEPTION_SYSCALL);
MOV(32, M(&PC), Imm32(js.compilerPC + 4));
OR(32, M(&PowerPC::ppcState.Exceptions), Imm32(EXCEPTION_SYSCALL));
WriteExceptionExit();
}
void Jit64::rfi(UGeckoInstruction inst)

View File

@ -238,7 +238,8 @@ void Jit64::cmpXX(UGeckoInstruction inst)
}
}
if (!merge_branch) {
if (!merge_branch)
{
// Keep the normal code separate for clarity.
CMP(32, gpr.R(a), comparand);
@ -256,6 +257,7 @@ void Jit64::cmpXX(UGeckoInstruction inst)
// TODO: If we ever care about SO, borrow a trick from
// http://maws.mameworld.info/maws/mamesrc/src/emu/cpu/powerpc/drc_ops.c : bt, adc
} else {
js.downcountAmount++;
int test_bit = 8 >> (js.next_inst.BI & 3);
bool condition = (js.next_inst.BO & BO_BRANCH_IF_TRUE) ? false : true;
CMP(32, gpr.R(a), comparand);

View File

@ -357,13 +357,13 @@ void JitIL::SingleStep()
pExecAddr();
}
void JitIL::Trace(PPCAnalyst::CodeBuffer *code_buf, u32 em_address)
void JitIL::Trace()
{
char regs[500] = "";
char fregs[750] = "";
#ifdef JIT_LOG_GPR
for (unsigned int i = 0; i < 32; i++)
for (int i = 0; i < 32; i++)
{
char reg[50];
sprintf(reg, "r%02d: %08x ", i, PowerPC::ppcState.gpr[i]);
@ -372,18 +372,18 @@ void JitIL::Trace(PPCAnalyst::CodeBuffer *code_buf, u32 em_address)
#endif
#ifdef JIT_LOG_FPR
for (unsigned int i = 0; i < 32; i++)
for (int i = 0; i < 32; i++)
{
char reg[50];
sprintf(reg, "f%02d: %016x ", i, riPS0(i));
strncat(fregs, reg, 750);
}
#endif
const PPCAnalyst::CodeOp &op = code_buf->codebuffer[0];
char ppcInst[256];
DisassembleGekko(op.inst.hex, em_address, ppcInst, 256);
NOTICE_LOG(DYNA_REC, "JITIL PC: %08x SRR0: %08x SRR1: %08x CRfast: %02x%02x%02x%02x%02x%02x%02x%02x FPSCR: %08x MSR: %08x LR: %08x %s %s %08x %s", PC, SRR0, SRR1, PowerPC::ppcState.cr_fast[0], PowerPC::ppcState.cr_fast[1], PowerPC::ppcState.cr_fast[2], PowerPC::ppcState.cr_fast[3], PowerPC::ppcState.cr_fast[4], PowerPC::ppcState.cr_fast[5], PowerPC::ppcState.cr_fast[6], PowerPC::ppcState.cr_fast[7], PowerPC::ppcState.fpscr, PowerPC::ppcState.msr, PowerPC::ppcState.spr[8], regs, fregs, op.inst.hex, ppcInst);
NOTICE_LOG(DYNA_REC, "JITIL PC: %08x SRR0: %08x SRR1: %08x CRfast: %02x%02x%02x%02x%02x%02x%02x%02x FPSCR: %08x MSR: %08x LR: %08x %s %s",
PC, SRR0, SRR1, PowerPC::ppcState.cr_fast[0], PowerPC::ppcState.cr_fast[1], PowerPC::ppcState.cr_fast[2], PowerPC::ppcState.cr_fast[3],
PowerPC::ppcState.cr_fast[4], PowerPC::ppcState.cr_fast[5], PowerPC::ppcState.cr_fast[6], PowerPC::ppcState.cr_fast[7], PowerPC::ppcState.fpscr,
PowerPC::ppcState.msr, PowerPC::ppcState.spr[8], regs, fregs);
}
void STACKALIGN JitIL::Jit(u32 em_address)
@ -401,11 +401,14 @@ const u8* JitIL::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
{
int blockSize = code_buf->GetSize();
// A broken block is a block that does not end in a branch
bool broken_block = false;
if (Core::g_CoreStartupParameter.bEnableDebugging)
{
// Comment out the following to disable breakpoints (speed-up)
blockSize = 1;
Trace(code_buf, em_address);
Trace();
}
if (em_address == 0)
@ -420,7 +423,7 @@ const u8* JitIL::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buf, JitBloc
//Analyze the block, collect all instructions it is made of (including inlining,
//if that is enabled), reorder instructions for optimal performance, and join joinable instructions.
b->exitAddress[0] = PPCAnalyst::Flatten(em_address, &size, &js.st, &js.gpa, &js.fpa, code_buf, blockSize);
b->exitAddress[0] = PPCAnalyst::Flatten(em_address, &size, &js.st, &js.gpa, &js.fpa, broken_block, code_buf, blockSize);
PPCAnalyst::CodeOp *ops = code_buf->codebuffer;
const u8 *start = AlignCode4(); //TODO: Test if this or AlignCode16 make a difference from GetCodePtr

View File

@ -85,7 +85,7 @@ public:
const u8* DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buffer, JitBlock *b);
void NotifyBreakpoint(u32 em_address, bool set);
void Trace(PPCAnalyst::CodeBuffer *code_buffer, u32 em_address);
void Trace();
void ClearCache();
const u8 *GetDispatcher() {

View File

@ -64,6 +64,8 @@ protected:
bool isLastInstruction;
bool forceUnsafeLoad;
bool memcheck;
bool broken_block;
int fifoBytesThisBlock;

View File

@ -135,7 +135,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
links_to.clear();
block_map.clear();
num_blocks = 0;
memset(blockCodePointers, 0, sizeof(u8*)*MAX_NUM_BLOCKS);
memset(blockCodePointers, 0, sizeof(u8*)*MAX_NUM_BLOCKS);
}
/*void JitBlockCache::DestroyBlocksWithFlag(BlockFlag death_flag)
@ -360,7 +360,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
}
b.invalid = true;
#ifdef JIT_UNLIMITED_ICACHE
Memory::Write_Opcode_JIT(b.originalAddress, b.originalFirstOpcode);
Memory::Write_Opcode_JIT(b.originalAddress, b.originalFirstOpcode?b.originalFirstOpcode:JIT_ICACHE_INVALID_WORD);
#else
if (Memory::ReadFast32(b.originalAddress) == block_num)
Memory::WriteUnchecked_U32(b.originalFirstOpcode, b.originalAddress);
@ -399,7 +399,7 @@ bool JitBlock::ContainsAddress(u32 em_address)
#ifdef JIT_UNLIMITED_ICACHE
// invalidate iCache.
// icbi can be called with any address, so we sholud check
// icbi can be called with any address, so we should check
if ((address & ~JIT_ICACHE_MASK) != 0x80000000 && (address & ~JIT_ICACHE_MASK) != 0x00000000 &&
(address & ~JIT_ICACHE_MASK) != 0x7e000000 && // TLB area
(address & ~JIT_ICACHEEX_MASK) != 0x90000000 && (address & ~JIT_ICACHEEX_MASK) != 0x10000000)

View File

@ -91,7 +91,10 @@ class JitBlockCache
public:
JitBlockCache() :
blockCodePointers(0), blocks(0), num_blocks(0),
iCache(0), iCacheEx(0), iCacheVMEM(0), MAX_NUM_BLOCKS(0) { }
#ifdef JIT_UNLIMITED_ICACHE
iCache(0), iCacheEx(0), iCacheVMEM(0),
#endif
MAX_NUM_BLOCKS(0) { }
int AllocateBlock(u32 em_address);
void FinalizeBlock(int block_num, bool block_link, const u8 *code_ptr);

View File

@ -285,7 +285,7 @@ bool CanSwapAdjacentOps(const CodeOp &a, const CodeOp &b)
// Does not yet perform inlining - although there are plans for that.
// Returns the exit address of the next PC
u32 Flatten(u32 address, int *realsize, BlockStats *st, BlockRegStats *gpa, BlockRegStats *fpa, CodeBuffer *buffer, int blockSize)
u32 Flatten(u32 address, int *realsize, BlockStats *st, BlockRegStats *gpa, BlockRegStats *fpa, bool &broken_block, CodeBuffer *buffer, int blockSize)
{
memset(st, 0, sizeof(st));
@ -296,6 +296,7 @@ u32 Flatten(u32 address, int *realsize, BlockStats *st, BlockRegStats *gpa, Bloc
gpa->any = true;
fpa->any = false;
for (int i = 0; i < 32; i++)
{
gpa->firstRead[i] = -1;
@ -324,154 +325,159 @@ u32 Flatten(u32 address, int *realsize, BlockStats *st, BlockRegStats *gpa, Bloc
UGeckoInstruction inst = Memory::Read_Opcode_JIT(code[i].address);
_assert_msg_(POWERPC, inst.hex != 0, "Zero Op - Error flattening %08x op %08x", address + i*4, inst.hex);
code[i].inst = inst;
code[i].branchTo = -1;
code[i].branchToIndex = -1;
code[i].skip = false;
GekkoOPInfo *opinfo = GetOpInfo(inst);
code[i].opinfo = opinfo;
if (opinfo)
numCycles += opinfo->numCyclesMinusOne + 1;
_assert_msg_(POWERPC, opinfo != 0, "Invalid Op - Error flattening %08x op %08x", address + i*4, inst.hex);
code[i].wantsCR0 = false;
code[i].wantsCR1 = false;
code[i].wantsPS1 = false;
int flags = opinfo->flags;
if (flags & FL_USE_FPU)
fpa->any = true;
if (flags & FL_TIMER)
gpa->anyTimer = true;
// Does the instruction output CR0?
if (flags & FL_RC_BIT)
code[i].outputCR0 = inst.hex & 1; //todo fix
else if ((flags & FL_SET_CRn) && inst.CRFD == 0)
code[i].outputCR0 = true;
else
code[i].outputCR0 = (flags & FL_SET_CR0) ? true : false;
// Does the instruction output CR1?
if (flags & FL_RC_BIT_F)
code[i].outputCR1 = inst.hex & 1; //todo fix
else if ((flags & FL_SET_CRn) && inst.CRFD == 1)
code[i].outputCR1 = true;
else
code[i].outputCR1 = (flags & FL_SET_CR1) ? true : false;
int numOut = 0;
int numIn = 0;
if (flags & FL_OUT_A)
if (inst.hex != 0)
{
code[i].regsOut[numOut++] = inst.RA;
gpa->SetOutputRegister(inst.RA, i);
}
if (flags & FL_OUT_D)
{
code[i].regsOut[numOut++] = inst.RD;
gpa->SetOutputRegister(inst.RD, i);
}
if (flags & FL_OUT_S)
{
code[i].regsOut[numOut++] = inst.RS;
gpa->SetOutputRegister(inst.RS, i);
}
if ((flags & FL_IN_A) || ((flags & FL_IN_A0) && inst.RA != 0))
{
code[i].regsIn[numIn++] = inst.RA;
gpa->SetInputRegister(inst.RA, i);
}
if (flags & FL_IN_B)
{
code[i].regsIn[numIn++] = inst.RB;
gpa->SetInputRegister(inst.RB, i);
}
if (flags & FL_IN_C)
{
code[i].regsIn[numIn++] = inst.RC;
gpa->SetInputRegister(inst.RC, i);
}
if (flags & FL_IN_S)
{
code[i].regsIn[numIn++] = inst.RS;
gpa->SetInputRegister(inst.RS, i);
}
code[i].inst = inst;
code[i].branchTo = -1;
code[i].branchToIndex = -1;
code[i].skip = false;
GekkoOPInfo *opinfo = GetOpInfo(inst);
code[i].opinfo = opinfo;
if (opinfo)
numCycles += opinfo->numCyclesMinusOne + 1;
_assert_msg_(POWERPC, opinfo != 0, "Invalid Op - Error flattening %08x op %08x", address + i*4, inst.hex);
// Set remaining register slots as unused (-1)
for (int j = numIn; j < 3; j++)
code[i].regsIn[j] = -1;
for (int j = numOut; j < 2; j++)
code[i].regsOut[j] = -1;
for (int j = 0; j < 3; j++)
code[i].fregsIn[j] = -1;
code[i].fregOut = -1;
code[i].wantsCR0 = false;
code[i].wantsCR1 = false;
code[i].wantsPS1 = false;
switch (opinfo->type)
{
case OPTYPE_INTEGER:
case OPTYPE_LOAD:
case OPTYPE_STORE:
break;
case OPTYPE_FPU:
break;
case OPTYPE_LOADFP:
break;
case OPTYPE_BRANCH:
if (code[i].inst.hex == 0x4e800020)
{
// For analysis purposes, we can assume that blr eats flags.
int flags = opinfo->flags;
if (flags & FL_USE_FPU)
fpa->any = true;
if (flags & FL_TIMER)
gpa->anyTimer = true;
// Does the instruction output CR0?
if (flags & FL_RC_BIT)
code[i].outputCR0 = inst.hex & 1; //todo fix
else if ((flags & FL_SET_CRn) && inst.CRFD == 0)
code[i].outputCR0 = true;
code[i].outputCR1 = true;
}
break;
case OPTYPE_SYSTEM:
case OPTYPE_SYSTEMFP:
numSystemInstructions++;
break;
}
bool follow = false;
u32 destination;
if (inst.OPCD == 18 && blockSize > 1)
{
//Is bx - should we inline? yes!
if (inst.AA)
destination = SignExt26(inst.LI << 2);
else
destination = address + SignExt26(inst.LI << 2);
if (destination != blockstart)
follow = true;
}
if (follow)
numFollows++;
if (numFollows > 1)
follow = false;
follow = false;
if (!follow)
{
if (opinfo->flags & FL_ENDBLOCK) //right now we stop early
code[i].outputCR0 = (flags & FL_SET_CR0) ? true : false;
// Does the instruction output CR1?
if (flags & FL_RC_BIT_F)
code[i].outputCR1 = inst.hex & 1; //todo fix
else if ((flags & FL_SET_CRn) && inst.CRFD == 1)
code[i].outputCR1 = true;
else
code[i].outputCR1 = (flags & FL_SET_CR1) ? true : false;
int numOut = 0;
int numIn = 0;
if (flags & FL_OUT_A)
{
foundExit = true;
code[i].regsOut[numOut++] = inst.RA;
gpa->SetOutputRegister(inst.RA, i);
}
if (flags & FL_OUT_D)
{
code[i].regsOut[numOut++] = inst.RD;
gpa->SetOutputRegister(inst.RD, i);
}
if (flags & FL_OUT_S)
{
code[i].regsOut[numOut++] = inst.RS;
gpa->SetOutputRegister(inst.RS, i);
}
if ((flags & FL_IN_A) || ((flags & FL_IN_A0) && inst.RA != 0))
{
code[i].regsIn[numIn++] = inst.RA;
gpa->SetInputRegister(inst.RA, i);
}
if (flags & FL_IN_B)
{
code[i].regsIn[numIn++] = inst.RB;
gpa->SetInputRegister(inst.RB, i);
}
if (flags & FL_IN_C)
{
code[i].regsIn[numIn++] = inst.RC;
gpa->SetInputRegister(inst.RC, i);
}
if (flags & FL_IN_S)
{
code[i].regsIn[numIn++] = inst.RS;
gpa->SetInputRegister(inst.RS, i);
}
// Set remaining register slots as unused (-1)
for (int j = numIn; j < 3; j++)
code[i].regsIn[j] = -1;
for (int j = numOut; j < 2; j++)
code[i].regsOut[j] = -1;
for (int j = 0; j < 3; j++)
code[i].fregsIn[j] = -1;
code[i].fregOut = -1;
switch (opinfo->type)
{
case OPTYPE_INTEGER:
case OPTYPE_LOAD:
case OPTYPE_STORE:
case OPTYPE_LOADFP:
case OPTYPE_STOREFP:
break;
case OPTYPE_FPU:
break;
case OPTYPE_BRANCH:
if (code[i].inst.hex == 0x4e800020)
{
// For analysis purposes, we can assume that blr eats flags.
code[i].outputCR0 = true;
code[i].outputCR1 = true;
}
break;
case OPTYPE_SYSTEM:
case OPTYPE_SYSTEMFP:
numSystemInstructions++;
break;
}
address += 4;
bool follow = false;
u32 destination;
if (inst.OPCD == 18 && blockSize > 1)
{
//Is bx - should we inline? yes!
if (inst.AA)
destination = SignExt26(inst.LI << 2);
else
destination = address + SignExt26(inst.LI << 2);
if (destination != blockstart)
follow = true;
}
if (follow)
numFollows++;
if (numFollows > 1)
follow = false;
follow = false;
if (!follow)
{
if (opinfo->flags & FL_ENDBLOCK) //right now we stop early
{
foundExit = true;
break;
}
address += 4;
}
else
{
code[i].skip = true;
address = destination;
}
}
else
{
code[i].skip = true;
address = destination;
// Memory exception occurred
break;
}
}
if (!foundExit && blockSize > 1)
NOTICE_LOG(POWERPC, "Analyzer ERROR - Function %08x too big, size is 0x%08x", blockstart, address-blockstart);
}
st->numCycles = numCycles;
// Instruction Reordering Pass
if (blockSize > 1)
if (num_inst > 2)
{
// Bubble down compares towards branches, so that they can be merged.
// -2: -1 for the pair, -1 for not swapping with the final instruction which is probably the branch.
@ -493,6 +499,13 @@ u32 Flatten(u32 address, int *realsize, BlockStats *st, BlockRegStats *gpa, Bloc
}
}
}
if (!foundExit && num_inst > 1)
{
// A broken block is a block that does not end in a branch
broken_block = true;
}
// Scan for CR0 dependency
// assume next block wants CR0 to be safe
bool wantsCR0 = true;

View File

@ -108,7 +108,7 @@ public:
};
u32 Flatten(u32 address, int *realsize, BlockStats *st, BlockRegStats *gpa, BlockRegStats *fpa, CodeBuffer *buffer, int blockSize);
u32 Flatten(u32 address, int *realsize, BlockStats *st, BlockRegStats *gpa, BlockRegStats *fpa, bool &broken_block, CodeBuffer *buffer, int blockSize);
void LogFunctionCall(u32 addr);
void FindFunctions(u32 startAddr, u32 endAddr, PPCSymbolDB *func_db);
bool AnalyzeFunction(u32 startAddr, Symbol &func, int max_size = 0);

View File

@ -99,7 +99,7 @@ namespace PowerPC
u32 InstructionCache::ReadInstruction(u32 addr)
{
if (!HID0.ICE) // instuction cache is disabled
if (!HID0.ICE) // instruction cache is disabled
return Memory::ReadUnchecked_U32(addr);
u32 set = (addr >> 5) & 0x7f;
u32 tag = addr >> 12;

View File

@ -49,6 +49,7 @@ enum
FL_CHECKEXCEPTIONS = (1<<16),
FL_EVIL = (1<<17),
FL_USE_FPU = (1<<18),
FL_LOADSTORE = (1<<19),
};
enum

View File

@ -267,7 +267,6 @@ void CheckExceptions()
{
SRR0 = NPC;
//GenerateISIException() sets up SRR1
SRR1 |= MSR & 0x87C0FFFF;
MSR |= (MSR >> 17) & 1;
MSR &= ~0x04EF36;
NPC = 0x80000400;
@ -306,6 +305,10 @@ void CheckExceptions()
SRR1 = MSR & 0x87C0FFFF;
MSR |= (MSR >> 17) & 1;
MSR &= ~0x04EF36;
// TODO: Verify whether the code below is correct
//SRR1 = MSR & 0x0000FFFF;
//MSR |= (MSR >> 16) & 1;
//MSR &= ~0x04EF32;
NPC = 0x80000800;
INFO_LOG(POWERPC, "EXCEPTION_FPU_UNAVAILABLE");

View File

@ -66,11 +66,14 @@ struct GC_ALIGNED64(PowerPCState)
// also for power management, but we don't care about that.
u32 spr[1024];
u32 tlb_last;
u32 tlb_va[16];
u32 tlb_pa[16];
u32 dtlb_last;
u32 dtlb_va[128];
u32 dtlb_pa[128];
u32 itlb_last;
u32 itlb_va[128];
u32 itlb_pa[128];
InstructionCache iCache;
};

View File

@ -176,7 +176,8 @@ void CJitWindow::Compare(u32 em_address)
PPCAnalyst::BlockStats st;
PPCAnalyst::BlockRegStats gpa;
PPCAnalyst::BlockRegStats fpa;
if (PPCAnalyst::Flatten(ppc_addr, &size, &st, &gpa, &fpa, &code_buffer, size) != 0xffffffff)
bool broken_block = false;
if (PPCAnalyst::Flatten(ppc_addr, &size, &st, &gpa, &fpa, broken_block, &code_buffer, size) != 0xffffffff)
{
sptr = (char*)xDis;
for (int i = 0; i < size; i++)

View File

@ -107,6 +107,7 @@ bool BootCore(const std::string& _rFilename)
game_ini.Get("Core", "SkipIdle", &StartUp.bSkipIdle, StartUp.bSkipIdle);
game_ini.Get("Core", "EnableFPRF", &StartUp.bEnableFPRF, StartUp.bEnableFPRF);
game_ini.Get("Core", "TLBHack", &StartUp.iTLBHack, StartUp.iTLBHack);
game_ini.Get("Core", "MMU", &StartUp.bMMU, StartUp.bMMU);
// Wii settings
if (StartUp.bWii)
{

View File

@ -290,7 +290,8 @@ void CISOProperties::CreateGUIControls(bool IsWad)
sbCoreOverrides = new wxStaticBoxSizer(wxVERTICAL, m_GameConfig, _("Core"));
CPUThread = new wxCheckBox(m_GameConfig, ID_USEDUALCORE, _("Enable Dual Core"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator);
SkipIdle = new wxCheckBox(m_GameConfig, ID_IDLESKIP, _("Enable Idle Skipping"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator);
TLBHack = new wxCheckBox(m_GameConfig, ID_TLBHACK, _("TLB Hack"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator);
MMU = new wxCheckBox(m_GameConfig, ID_MMU, _("Enable MMU"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator);
TLBHack = new wxCheckBox(m_GameConfig, ID_TLBHACK, _("MMU Speed Hack"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator);
// Wii Console
sbWiiOverrides = new wxStaticBoxSizer(wxVERTICAL, m_GameConfig, _("Wii Console"));
EnableProgressiveScan = new wxCheckBox(m_GameConfig, ID_ENABLEPROGRESSIVESCAN, _("Enable Progressive Scan"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator);
@ -346,6 +347,7 @@ void CISOProperties::CreateGUIControls(bool IsWad)
sbGameConfig->Add(OverrideText, 0, wxEXPAND|wxALL, 5);
sbCoreOverrides->Add(CPUThread, 0, wxEXPAND|wxLEFT, 5);
sbCoreOverrides->Add(SkipIdle, 0, wxEXPAND|wxLEFT, 5);
sbCoreOverrides->Add(MMU, 0, wxEXPAND|wxLEFT, 5);
sbCoreOverrides->Add(TLBHack, 0, wxEXPAND|wxLEFT, 5);
sbWiiOverrides->Add(EnableProgressiveScan, 0, wxEXPAND|wxLEFT, 5);
sbWiiOverrides->Add(EnableWideScreen, 0, wxEXPAND|wxLEFT, 5);
@ -809,6 +811,11 @@ void CISOProperties::LoadGameConfig()
else
SkipIdle->Set3StateValue(wxCHK_UNDETERMINED);
if (GameIni.Get("Core", "MMU", &bTemp))
MMU->Set3StateValue((wxCheckBoxState)bTemp);
else
MMU->Set3StateValue(wxCHK_UNDETERMINED);
if (GameIni.Get("Core", "TLBHack", &bTemp))
TLBHack->Set3StateValue((wxCheckBoxState)bTemp);
else
@ -890,6 +897,11 @@ bool CISOProperties::SaveGameConfig()
else
GameIni.Set("Core", "SkipIdle", SkipIdle->Get3StateValue());
if (MMU->Get3StateValue() == wxCHK_UNDETERMINED)
GameIni.DeleteKey("Core", "MMU");
else
GameIni.Set("Core", "MMU", MMU->Get3StateValue());
if (TLBHack->Get3StateValue() == wxCHK_UNDETERMINED)
GameIni.DeleteKey("Core", "TLBHack");
else

View File

@ -84,7 +84,7 @@ class CISOProperties : public wxDialog
wxStaticText *OverrideText;
// Core
wxCheckBox *CPUThread, *SkipIdle, *TLBHack;
wxCheckBox *CPUThread, *SkipIdle, *MMU, *TLBHack;
// Wii
wxCheckBox *EnableProgressiveScan, *EnableWideScreen;
// Video
@ -164,6 +164,7 @@ class CISOProperties : public wxDialog
ID_OVERRIDE_TEXT,
ID_USEDUALCORE,
ID_IDLESKIP,
ID_MMU,
ID_TLBHACK,
ID_FORCEFILTERING,
ID_EFBCOPYDISABLE,