mirror of
https://github.com/cemu-project/Cemu.git
synced 2025-07-02 21:11:17 +12:00
* Implement GDB stub debugger Can be enabled by using the "--enable-gdbstub" option (and the debugger GUI, although that's untested) which'll pause any game you launch at start-up. Will start at port 1337 although it'll eventually be user-editable. The code is a bit weirdly sorted and also just needs a general cleanup, so expect that eventually too. And uses egyptian braces but formatting was easier to do at the end, so that's also something to do. It has been tested to work with IDA Pro, Clion and the standalone interface for now, but I plan on writing some instructions in the PR to follow for people who want to use this. Memory breakpoints aren't possible yet, only execution breakpoints. This code was aimed to be decoupled from the existing debugger to be able to be ported to the Wii U for an equal debugging experience. That's also why it uses the Cafe OS's thread sleep and resuming functions whenever possible instead of using recompiler/interpreter controls. * Add memory writing and floating point registers support * Reformat code a bit * Format code to adhere to Cemu's coding style * Rework GDB Stub settings in GUI * Small styling fixes * Rework execution breakpoints Should work better in some edge cases now. But this should also allow for adding access breakpoints since it's now more separated. * Implement access breakpoints * Fix some issues with breakpoints * Fix includes for Linux * Fix unnecessary include * Tweaks for Linux compatibility * Use std::thread instead of std::jthread to fix MacOS support * Enable GDB read/write breakpoints on x86 only * Fix compilation for GCC compilers at least The thread type varies on some platforms, so supporting this is hell... but let's get it to compile on MacOS first. * Disable them for MacOS due to lack of ptrace --------- Co-authored-by: Exzap <13877693+Exzap@users.noreply.github.com>
1232 lines
31 KiB
C++
1232 lines
31 KiB
C++
#include "PPCInterpreterInternal.h"
|
|
#include "PPCInterpreterHelper.h"
|
|
#include "Cafe/HW/Espresso/Debugger/Debugger.h"
|
|
#include "Cafe/HW/Espresso/Debugger/GDBStub.h"
|
|
|
|
class PPCItpCafeOSUsermode
|
|
{
|
|
public:
|
|
static const bool allowSupervisorMode = false;
|
|
static const bool allowDSI = false;
|
|
|
|
inline static uint32 memory_readCodeU32(PPCInterpreter_t* hCPU, uint32 address)
|
|
{
|
|
return _swapEndianU32(*(uint32*)(memory_base + address));
|
|
}
|
|
|
|
inline static void ppcMem_writeDataDouble(PPCInterpreter_t* hCPU, uint32 address, double vf)
|
|
{
|
|
uint64 v = *(uint64*)&vf;
|
|
uint32 v1 = v & 0xFFFFFFFF;
|
|
uint32 v2 = v >> 32;
|
|
uint8* ptr = memory_getPointerFromVirtualOffset(address);
|
|
*(uint32*)(ptr + 4) = CPU_swapEndianU32(v1);
|
|
*(uint32*)(ptr + 0) = CPU_swapEndianU32(v2);
|
|
}
|
|
|
|
inline static void ppcMem_writeDataU64(PPCInterpreter_t* hCPU, uint32 address, uint64 v)
|
|
{
|
|
*(uint64*)(memory_getPointerFromVirtualOffset(address)) = CPU_swapEndianU64(v);
|
|
}
|
|
|
|
inline static void ppcMem_writeDataU32(PPCInterpreter_t* hCPU, uint32 address, uint32 v)
|
|
{
|
|
*(uint32*)(memory_getPointerFromVirtualOffset(address)) = CPU_swapEndianU32(v);
|
|
}
|
|
|
|
inline static void ppcMem_writeDataU16(PPCInterpreter_t* hCPU, uint32 address, uint16 v)
|
|
{
|
|
*(uint16*)(memory_getPointerFromVirtualOffset(address)) = CPU_swapEndianU16(v);
|
|
}
|
|
|
|
inline static void ppcMem_writeDataU8(PPCInterpreter_t* hCPU, uint32 address, uint8 v)
|
|
{
|
|
*(uint8*)(memory_getPointerFromVirtualOffset(address)) = v;
|
|
}
|
|
|
|
inline static double ppcMem_readDataDouble(PPCInterpreter_t* hCPU, uint32 address)
|
|
{
|
|
uint32 v[2];
|
|
v[1] = *(uint32*)(memory_getPointerFromVirtualOffset(address));
|
|
v[0] = *(uint32*)(memory_getPointerFromVirtualOffset(address) + 4);
|
|
v[0] = CPU_swapEndianU32(v[0]);
|
|
v[1] = CPU_swapEndianU32(v[1]);
|
|
return *(double*)v;
|
|
}
|
|
|
|
inline static float ppcMem_readDataFloat(PPCInterpreter_t* hCPU, uint32 address)
|
|
{
|
|
uint32 v = *(uint32*)(memory_getPointerFromVirtualOffset(address));
|
|
v = CPU_swapEndianU32(v);
|
|
return *(float*)&v;
|
|
}
|
|
|
|
inline static uint64 ppcMem_readDataU64(PPCInterpreter_t* hCPU, uint32 address)
|
|
{
|
|
uint64 v = *(uint64*)(memory_getPointerFromVirtualOffset(address));
|
|
return CPU_swapEndianU64(v);
|
|
}
|
|
|
|
inline static uint32 ppcMem_readDataU32(PPCInterpreter_t* hCPU, uint32 address)
|
|
{
|
|
uint32 v = *(uint32*)(memory_getPointerFromVirtualOffset(address));
|
|
return CPU_swapEndianU32(v);
|
|
}
|
|
|
|
inline static uint16 ppcMem_readDataU16(PPCInterpreter_t* hCPU, uint32 address)
|
|
{
|
|
uint16 v = *(uint16*)(memory_getPointerFromVirtualOffset(address));
|
|
return CPU_swapEndianU16(v);
|
|
}
|
|
|
|
inline static uint8 ppcMem_readDataU8(PPCInterpreter_t* hCPU, uint32 address)
|
|
{
|
|
return *(uint8*)(memory_getPointerFromVirtualOffset(address));
|
|
}
|
|
|
|
inline static uint64 ppcMem_readDataFloatEx(PPCInterpreter_t* hCPU, uint32 addr)
|
|
{
|
|
return ConvertToDoubleNoFTZ(_swapEndianU32(*(uint32*)(memory_base + addr)));
|
|
}
|
|
|
|
inline static void ppcMem_writeDataFloatEx(PPCInterpreter_t* hCPU, uint32 addr, uint64 value)
|
|
{
|
|
*(uint32*)(memory_base + addr) = _swapEndianU32(ConvertToSingleNoFTZ(value));
|
|
}
|
|
|
|
inline static uint64 getTB(PPCInterpreter_t* hCPU)
|
|
{
|
|
return PPCInterpreter_getMainCoreCycleCounter();
|
|
}
|
|
};
|
|
|
|
uint32 debug_lastTranslatedHit;
|
|
|
|
void generateDSIException(PPCInterpreter_t* hCPU, uint32 dataAddress)
|
|
{
|
|
// todo - check if we are already inside an interrupt handler (in which case the DSI exception is queued and not executed immediately?)
|
|
|
|
// set flag to cancel current instruction
|
|
hCPU->memoryException = true;
|
|
|
|
hCPU->sprExtended.srr0 = hCPU->instructionPointer;
|
|
hCPU->sprExtended.srr1 = hCPU->sprExtended.msr & 0x87C0FFFF;
|
|
|
|
hCPU->sprExtended.dar = dataAddress;
|
|
|
|
hCPU->sprExtended.msr &= ~0x04EF36;
|
|
|
|
hCPU->instructionPointer = 0xFFF00300;
|
|
|
|
|
|
uint32 dsisr = 0;
|
|
dsisr |= (1<<(31-1)); // set if no TLB/BAT match found
|
|
|
|
hCPU->sprExtended.dsisr = dsisr;
|
|
|
|
}
|
|
|
|
class PPCItpSupervisorWithMMU
|
|
{
|
|
public:
|
|
static const bool allowSupervisorMode = true;
|
|
static const bool allowDSI = true;
|
|
|
|
inline static uint32 ppcMem_translateVirtualDataToPhysicalAddr(PPCInterpreter_t* hCPU, uint32 vAddr)
|
|
{
|
|
// check if address translation is disabled for data accesses
|
|
if (GET_MSR_BIT(MSR_DR) == 0)
|
|
{
|
|
return vAddr;
|
|
}
|
|
|
|
#ifdef CEMU_DEBUG_ASSERT
|
|
if (hCPU->memoryException)
|
|
assert_dbg(); // should not be set anymore
|
|
#endif
|
|
|
|
// how to determine if BAT is valid:
|
|
// BAT_entry_valid = (Vs & ~MSR[PR]) | (Vp & MSR[PR]) (The entry has separate enable flags for usermode and supervisor mode)
|
|
for (sint32 i = 0; i < 8; i++)
|
|
{
|
|
// upper
|
|
uint32 batU = hCPU->sprExtended.dbatU[i];
|
|
uint32 BEPI = ((batU >> 17) & 0x7FFF) << 17;
|
|
uint32 Vp = (batU >> 0) & 1;
|
|
uint32 Vs = (batU >> 1) & 1;
|
|
uint32 BL = (((batU >> 2) & 0x7FF) ^ 0x7FF) << 17;
|
|
BL |= 0xF0000000;
|
|
if (Vs == 0)
|
|
continue; // todo - check if in supervisor/usermode
|
|
// lower
|
|
uint32 batL = hCPU->sprExtended.dbatL[i];
|
|
uint32 PP = (batL >> 0) & 3;
|
|
uint32 WIMG = (batL >> 3) & 0xF;
|
|
uint32 BRPN = ((batL >> 17) & 0x7FFF) << 17;
|
|
|
|
// check for match
|
|
if ((vAddr&BL) == BEPI)
|
|
{
|
|
// match
|
|
vAddr = (vAddr&~BL) | (BRPN&BL);
|
|
debug_lastTranslatedHit = vAddr;
|
|
return vAddr;
|
|
}
|
|
}
|
|
|
|
// no match
|
|
debug_lastTranslatedHit = 0xFFFFFFFF;
|
|
|
|
// find segment
|
|
uint32 segmentIndex = (vAddr>>28);
|
|
//uint32 pageIndex = (vAddr >> 12) & 0xFFFF; // for 4KB pages
|
|
// uint32 byteOffset = vAddr & 0xFFF; // for 4KB pages
|
|
uint32 pageIndex = (vAddr >> 17) & 0x7FF; // for 128KB pages
|
|
uint32 byteOffset = vAddr & 0x1FFFF;
|
|
uint32 srValue = hCPU->sprExtended.sr[segmentIndex];
|
|
|
|
uint8 sr_ks = (srValue >> 30) & 1; // supervisor
|
|
uint8 sr_kp = (srValue >> 29) & 1; // user mode
|
|
uint8 sr_n = (srValue >> 28) & 1; // no-execute
|
|
uint32 sr_vsid = (srValue & 0xFFFFFF);
|
|
//uint32 vpn = pageIndex | (sr_vsid << 16); // 40bit virtual page number
|
|
|
|
|
|
// look up in page table
|
|
//uint32 lookupHash = (sr_vsid ^ pageIndex) & 0x7FFFF; // not correct for 4KB pages? sr_vsid must be shifted?
|
|
//uint32 lookupHash = (sr_vsid ^ pageIndex) & 0x7FFFF;
|
|
//uint32 lookupHash = ((sr_vsid>>8) ^ pageIndex) & 0x7FFFF;
|
|
uint32 lookupHash = ((sr_vsid >> 0) ^ pageIndex) & 0x7FFFF;
|
|
|
|
//lookupHash ^= 0x7FFFF;
|
|
|
|
uint32 pageTableAddr = hCPU->sprExtended.sdr1&0xFFFF0000;
|
|
uint32 pageTableMask = hCPU->sprExtended.sdr1&0x1FF;
|
|
|
|
for (uint32 ch = 0; ch < 2; ch++)
|
|
{
|
|
uint32 ptegSelectLow = (lookupHash & 0x3FF);
|
|
uint32 maskOR = (lookupHash >> 10) & pageTableMask;
|
|
|
|
uint32* pteg = (uint32*)(memory_base + (pageTableAddr | (maskOR << 16)) + ptegSelectLow * 64);
|
|
for (sint32 t = 0; t < 8; t++)
|
|
{
|
|
uint32 w0 = _swapEndianU32(pteg[0]);
|
|
uint32 w1 = _swapEndianU32(pteg[1]);
|
|
pteg += 2;
|
|
if ((w0 & 0x80000000) == 0)
|
|
continue; // entry not valid
|
|
|
|
uint32 abPageIndex = (w0 >> 0) & 0x3F;
|
|
uint8 h = (w0 >> 6) & 1;
|
|
uint32 ptegVSID = (w0 >> 7) & 0xFFFFFF;
|
|
|
|
if (abPageIndex == (pageIndex >> 5) && ptegVSID == sr_vsid && h == ch)
|
|
{
|
|
if (ch == 1)
|
|
assert_dbg();
|
|
// match
|
|
uint32 ptegPhysicalPage = (w1 >> 12) & 0xFFFFF;
|
|
// replace page (128KB)
|
|
vAddr = (vAddr & ~0xFFFE0000) | (ptegPhysicalPage << 12);
|
|
return vAddr;
|
|
|
|
}
|
|
}
|
|
// calculate hash 2
|
|
lookupHash = ~lookupHash;
|
|
}
|
|
|
|
forceLogDebug_printf("DSI exception at 0x%08x LR 0x%08x DataAddress %08x", hCPU->instructionPointer, hCPU->spr.LR, vAddr);
|
|
|
|
generateDSIException(hCPU, vAddr);
|
|
|
|
// todo: Check hash func 1
|
|
// todo: Check protection bits
|
|
// todo: Check supervisor/usermode bits
|
|
|
|
|
|
// also use this function in all the mem stuff below
|
|
|
|
// note: bat has higher priority than TLB
|
|
|
|
// since iterating the bats and page table is too slow, we need to pre-process the data somehow.
|
|
|
|
return vAddr;
|
|
}
|
|
|
|
inline static uint32 ppcMem_translateVirtualCodeToPhysicalAddr(PPCInterpreter_t* hCPU, uint32 vAddr)
|
|
{
|
|
// check if address translation is disabled for instruction accesses
|
|
if (GET_MSR_BIT(MSR_IR) == 0)
|
|
{
|
|
return vAddr;
|
|
}
|
|
|
|
// how to determine if BAT is valid:
|
|
// BAT_entry_valid = (Vs & ~MSR[PR]) | (Vp & MSR[PR]) (The entry has separate enable flags for usermode and supervisor mode)
|
|
for (sint32 i = 0; i < 8; i++)
|
|
{
|
|
// upper
|
|
uint32 batU = hCPU->sprExtended.ibatU[i];
|
|
uint32 BEPI = ((batU >> 17) & 0x7FFF) << 17;
|
|
uint32 Vp = (batU >> 0) & 1;
|
|
uint32 Vs = (batU >> 1) & 1;
|
|
uint32 BL = (((batU >> 2) & 0x7FF) ^ 0x7FF) << 17;
|
|
BL |= 0xF0000000;
|
|
if (Vs == 0)
|
|
continue; // todo - check if in supervisor/usermode
|
|
// lower
|
|
uint32 batL = hCPU->sprExtended.ibatL[i];
|
|
uint32 PP = (batL >> 0) & 3;
|
|
uint32 WIMG = (batL >> 3) & 0xF;
|
|
uint32 BRPN = ((batL >> 17) & 0x7FFF) << 17;
|
|
|
|
// check for match
|
|
if ((vAddr&BL) == BEPI)
|
|
{
|
|
// match
|
|
vAddr = (vAddr&~BL) | (BRPN&BL);
|
|
debug_lastTranslatedHit = vAddr;
|
|
return vAddr;
|
|
}
|
|
}
|
|
assert_dbg();
|
|
|
|
// no match
|
|
// todo - throw exception if translation is enabled?
|
|
return vAddr;
|
|
}
|
|
|
|
static uint32 memory_readCodeU32(PPCInterpreter_t* hCPU, uint32 address)
|
|
{
|
|
return _swapEndianU32(*(uint32*)(memory_base + ppcMem_translateVirtualCodeToPhysicalAddr(hCPU, address)));
|
|
}
|
|
|
|
inline static uint8* ppcMem_getDataPtr(PPCInterpreter_t* hCPU, uint32 vAddr)
|
|
{
|
|
return memory_base + ppcMem_translateVirtualDataToPhysicalAddr(hCPU, vAddr);
|
|
}
|
|
|
|
inline static void ppcMem_writeDataDouble(PPCInterpreter_t* hCPU, uint32 address, double vf)
|
|
{
|
|
uint64 v = *(uint64*)&vf;
|
|
uint32 v1 = v & 0xFFFFFFFF;
|
|
uint32 v2 = v >> 32;
|
|
uint8* ptr = ppcMem_getDataPtr(hCPU, address);
|
|
*(uint32*)(ptr + 4) = CPU_swapEndianU32(v1);
|
|
*(uint32*)(ptr + 0) = CPU_swapEndianU32(v2);
|
|
}
|
|
|
|
inline static void ppcMem_writeDataU64(PPCInterpreter_t* hCPU, uint32 address, uint64 v)
|
|
{
|
|
*(uint64*)(ppcMem_getDataPtr(hCPU, address)) = CPU_swapEndianU64(v);
|
|
}
|
|
|
|
inline static void ppcMem_writeDataU32(PPCInterpreter_t* hCPU, uint32 address, uint32 v)
|
|
{
|
|
uint32 pAddr = ppcMem_translateVirtualDataToPhysicalAddr(hCPU, address);
|
|
if (hCPU->memoryException)
|
|
return;
|
|
|
|
if (pAddr >= 0x0c000000 && pAddr < 0x0d100000)
|
|
{
|
|
cemu_assert_unimplemented();
|
|
return;
|
|
}
|
|
*(uint32*)(memory_base + pAddr) = CPU_swapEndianU32(v);
|
|
}
|
|
|
|
inline static void ppcMem_writeDataU16(PPCInterpreter_t* hCPU, uint32 address, uint16 v)
|
|
{
|
|
*(uint16*)(ppcMem_getDataPtr(hCPU, address)) = CPU_swapEndianU16(v);
|
|
}
|
|
|
|
inline static void ppcMem_writeDataU8(PPCInterpreter_t* hCPU, uint32 address, uint8 v)
|
|
{
|
|
*(uint8*)(ppcMem_getDataPtr(hCPU, address)) = v;
|
|
}
|
|
|
|
inline static double ppcMem_readDataDouble(PPCInterpreter_t* hCPU, uint32 address)
|
|
{
|
|
uint32 v[2];
|
|
v[1] = *(uint32*)(ppcMem_getDataPtr(hCPU, address));
|
|
v[0] = *(uint32*)(ppcMem_getDataPtr(hCPU, address) + 4);
|
|
v[0] = CPU_swapEndianU32(v[0]);
|
|
v[1] = CPU_swapEndianU32(v[1]);
|
|
return *(double*)v;
|
|
}
|
|
|
|
inline static float ppcMem_readDataFloat(PPCInterpreter_t* hCPU, uint32 address)
|
|
{
|
|
uint32 v = *(uint32*)(ppcMem_getDataPtr(hCPU, address));
|
|
v = CPU_swapEndianU32(v);
|
|
return *(float*)&v;
|
|
}
|
|
|
|
inline static uint64 ppcMem_readDataU64(PPCInterpreter_t* hCPU, uint32 address)
|
|
{
|
|
uint64 v = *(uint64*)(ppcMem_getDataPtr(hCPU, address));
|
|
return CPU_swapEndianU64(v);
|
|
}
|
|
|
|
inline static uint32 ppcMem_readDataU32(PPCInterpreter_t* hCPU, uint32 address)
|
|
{
|
|
uint32 pAddr = ppcMem_translateVirtualDataToPhysicalAddr(hCPU, address);
|
|
if (hCPU->memoryException)
|
|
return 0;
|
|
if (pAddr >= 0x01FFF000 && pAddr < 0x02000000)
|
|
{
|
|
debug_printf("Access u32 boot param block 0x%08x IP %08x LR %08x\n", pAddr, hCPU->instructionPointer, hCPU->spr.LR);
|
|
forceLogDebug_printf("Access u32 boot param block 0x%08x (org %08x) IP %08x LR %08x\n", pAddr, address, hCPU->instructionPointer, hCPU->spr.LR);
|
|
}
|
|
if (pAddr >= 0xFFEB73B0 && pAddr < (0xFFEB73B0+0x40C))
|
|
{
|
|
debug_printf("Access cached u32 boot param block 0x%08x IP %08x LR %08x\n", pAddr, hCPU->instructionPointer, hCPU->spr.LR);
|
|
forceLogDebug_printf("Access cached u32 boot param block 0x%08x (org %08x) IP %08x LR %08x\n", pAddr, address, hCPU->instructionPointer, hCPU->spr.LR);
|
|
}
|
|
|
|
if (pAddr >= 0x0c000000 && pAddr < 0x0d100000)
|
|
{
|
|
cemu_assert_unimplemented();
|
|
return 0;
|
|
}
|
|
uint32 v = *(uint32*)(memory_base + pAddr);
|
|
return CPU_swapEndianU32(v);
|
|
}
|
|
|
|
inline static uint16 ppcMem_readDataU16(PPCInterpreter_t* hCPU, uint32 address)
|
|
{
|
|
uint16 v = *(uint16*)(ppcMem_getDataPtr(hCPU, address));
|
|
return CPU_swapEndianU16(v);
|
|
}
|
|
|
|
inline static uint8 ppcMem_readDataU8(PPCInterpreter_t* hCPU, uint32 address)
|
|
{
|
|
uint32 pAddr = ppcMem_translateVirtualDataToPhysicalAddr(hCPU, address);
|
|
if (pAddr >= 0x0c000000 && pAddr < 0x0d100000)
|
|
{
|
|
cemu_assert_unimplemented();
|
|
return 0;
|
|
}
|
|
return *(uint8*)(memory_base + pAddr);
|
|
}
|
|
|
|
inline static uint64 ppcMem_readDataFloatEx(PPCInterpreter_t* hCPU, uint32 addr)
|
|
{
|
|
return ConvertToDoubleNoFTZ(_swapEndianU32(*(uint32*)(memory_base + addr)));
|
|
}
|
|
|
|
inline static void ppcMem_writeDataFloatEx(PPCInterpreter_t* hCPU, uint32 addr, uint64 value)
|
|
{
|
|
*(uint32*)(memory_base + addr) = _swapEndianU32(ConvertToSingleNoFTZ(value));
|
|
}
|
|
|
|
inline static uint64 getTB(PPCInterpreter_t* hCPU)
|
|
{
|
|
return hCPU->global->tb / 20ULL;
|
|
}
|
|
};
|
|
|
|
uint32 testIP[100];
|
|
uint32 testIPC = 0;
|
|
|
|
template <typename ppcItpCtrl>
|
|
class PPCInterpreterContainer
|
|
{
|
|
public:
|
|
#include "PPCInterpreterSPR.hpp"
|
|
#include "PPCInterpreterOPC.hpp"
|
|
#include "PPCInterpreterLoadStore.hpp"
|
|
#include "PPCInterpreterALU.hpp"
|
|
|
|
static void executeInstruction(PPCInterpreter_t* hCPU)
|
|
{
|
|
if constexpr(ppcItpCtrl::allowSupervisorMode)
|
|
{
|
|
hCPU->global->tb++;
|
|
}
|
|
|
|
#ifdef __DEBUG_OUTPUT_INSTRUCTION
|
|
debug_printf("%08x: ", hCPU->instructionPointer);
|
|
#endif
|
|
|
|
uint32 opcode = ppcItpCtrl::memory_readCodeU32(hCPU, hCPU->instructionPointer);
|
|
|
|
switch ((opcode >> 26))
|
|
{
|
|
case 0:
|
|
debug_printf("ZERO[NOP] | 0x%08X\n", (unsigned int)hCPU->instructionPointer);
|
|
#ifdef CEMU_DEBUG_ASSERT
|
|
assert_dbg();
|
|
while (true) std::this_thread::sleep_for(std::chrono::seconds(1));
|
|
#endif
|
|
hCPU->instructionPointer += 4;
|
|
break;
|
|
case 1: // virtual HLE
|
|
PPCInterpreter_virtualHLE(hCPU, opcode);
|
|
break;
|
|
case 4:
|
|
switch (PPC_getBits(opcode, 30, 5))
|
|
{
|
|
case 0: // subcategory compare
|
|
switch (PPC_getBits(opcode, 25, 5))
|
|
{
|
|
case 0: // Sonic All Stars Racing
|
|
PPCInterpreter_PS_CMPU0(hCPU, opcode);
|
|
break;
|
|
case 1:
|
|
PPCInterpreter_PS_CMPO0(hCPU, opcode);
|
|
break;
|
|
case 2: // Assassin's Creed 3, Sonic All Stars Racing
|
|
PPCInterpreter_PS_CMPU1(hCPU, opcode);
|
|
break;
|
|
default:
|
|
debug_printf("Unknown execute %04X as [4->0] at %08X\n", PPC_getBits(opcode, 25, 5), hCPU->instructionPointer);
|
|
cemu_assert_unimplemented();
|
|
break;
|
|
}
|
|
break;
|
|
case 6:
|
|
PPCInterpreter_PSQ_LX(hCPU, opcode);
|
|
break;
|
|
case 7:
|
|
PPCInterpreter_PSQ_STX(hCPU, opcode);
|
|
break;
|
|
case 8:
|
|
switch (PPC_getBits(opcode, 25, 5))
|
|
{
|
|
case 1:
|
|
PPCInterpreter_PS_NEG(hCPU, opcode);
|
|
break;
|
|
case 2:
|
|
PPCInterpreter_PS_MR(hCPU, opcode);
|
|
break;
|
|
case 4:
|
|
PPCInterpreter_PS_NABS(hCPU, opcode);
|
|
break;
|
|
case 8:
|
|
PPCInterpreter_PS_ABS(hCPU, opcode);
|
|
break;
|
|
default:
|
|
debug_printf("Unknown execute %04X as [4->8] at %08X\n", PPC_getBits(opcode, 25, 5), hCPU->instructionPointer);
|
|
cemu_assert_unimplemented();
|
|
break;
|
|
}
|
|
break;
|
|
case 10:
|
|
PPCInterpreter_PS_SUM0(hCPU, opcode);
|
|
break;
|
|
case 11:
|
|
PPCInterpreter_PS_SUM1(hCPU, opcode);
|
|
break;
|
|
case 12:
|
|
PPCInterpreter_PS_MULS0(hCPU, opcode);
|
|
break;
|
|
case 13:
|
|
PPCInterpreter_PS_MULS1(hCPU, opcode);
|
|
break;
|
|
case 14:
|
|
PPCInterpreter_PS_MADDS0(hCPU, opcode);
|
|
break;
|
|
case 15:
|
|
PPCInterpreter_PS_MADDS1(hCPU, opcode);
|
|
break;
|
|
case 16: // sub category - merge
|
|
switch (PPC_getBits(opcode, 25, 5))
|
|
{
|
|
case 16:
|
|
PPCInterpreter_PS_MERGE00(hCPU, opcode);
|
|
break;
|
|
case 17:
|
|
PPCInterpreter_PS_MERGE01(hCPU, opcode);
|
|
break;
|
|
case 18:
|
|
PPCInterpreter_PS_MERGE10(hCPU, opcode);
|
|
break;
|
|
case 19:
|
|
PPCInterpreter_PS_MERGE11(hCPU, opcode);
|
|
break;
|
|
default:
|
|
debug_printf("Unknown execute %04X as [4->16] at %08X\n", PPC_getBits(opcode, 25, 5), hCPU->instructionPointer);
|
|
debugBreakpoint();
|
|
break;
|
|
}
|
|
break;
|
|
case 18:
|
|
PPCInterpreter_PS_DIV(hCPU, opcode);
|
|
break;
|
|
case 20:
|
|
PPCInterpreter_PS_SUB(hCPU, opcode);
|
|
break;
|
|
case 21:
|
|
PPCInterpreter_PS_ADD(hCPU, opcode);
|
|
break;
|
|
case 22:
|
|
PPCInterpreter_DCBZL(hCPU, opcode);
|
|
break;
|
|
case 23:
|
|
PPCInterpreter_PS_SEL(hCPU, opcode);
|
|
break;
|
|
case 24:
|
|
PPCInterpreter_PS_RES(hCPU, opcode);
|
|
break;
|
|
case 25:
|
|
PPCInterpreter_PS_MUL(hCPU, opcode);
|
|
break;
|
|
case 26: // sub category with only one entry - RSQRTE
|
|
PPCInterpreter_PS_RSQRTE(hCPU, opcode);
|
|
break;
|
|
case 28:
|
|
PPCInterpreter_PS_MSUB(hCPU, opcode);
|
|
break;
|
|
case 29:
|
|
PPCInterpreter_PS_MADD(hCPU, opcode);
|
|
break;
|
|
case 30:
|
|
PPCInterpreter_PS_NMSUB(hCPU, opcode);
|
|
break;
|
|
case 31:
|
|
PPCInterpreter_PS_NMADD(hCPU, opcode);
|
|
break;
|
|
default:
|
|
debug_printf("Unknown execute %04X as [4] at %08X\n", PPC_getBits(opcode, 30, 5), hCPU->instructionPointer);
|
|
cemu_assert_unimplemented();
|
|
break;
|
|
}
|
|
break;
|
|
case 7:
|
|
PPCInterpreter_MULLI(hCPU, opcode);
|
|
break;
|
|
case 8:
|
|
PPCInterpreter_SUBFIC(hCPU, opcode);
|
|
break;
|
|
case 10:
|
|
PPCInterpreter_CMPLI(hCPU, opcode);
|
|
break;
|
|
case 11:
|
|
PPCInterpreter_CMPI(hCPU, opcode);
|
|
break;
|
|
case 12:
|
|
PPCInterpreter_ADDIC(hCPU, opcode);
|
|
break;
|
|
case 13:
|
|
PPCInterpreter_ADDIC_(hCPU, opcode);
|
|
break;
|
|
case 14:
|
|
PPCInterpreter_ADDI(hCPU, opcode);
|
|
break;
|
|
case 15:
|
|
PPCInterpreter_ADDIS(hCPU, opcode);
|
|
break;
|
|
case 16:
|
|
PPCInterpreter_BCX(hCPU, opcode);
|
|
break;
|
|
case 17:
|
|
if (PPC_getBits(opcode, 30, 1) == 1) {
|
|
PPCInterpreter_SC(hCPU, opcode);
|
|
}
|
|
else {
|
|
debug_printf("Unsupported Opcode [0x17 --> 0x0]\n");
|
|
cemu_assert_unimplemented();
|
|
}
|
|
break;
|
|
case 18:
|
|
PPCInterpreter_BX(hCPU, opcode);
|
|
break;
|
|
case 19: // opcode category
|
|
switch (PPC_getBits(opcode, 30, 10))
|
|
{
|
|
case 0:
|
|
PPCInterpreter_MCRF(hCPU, opcode);
|
|
break;
|
|
case 16:
|
|
PPCInterpreter_BCLRX(hCPU, opcode);
|
|
break;
|
|
case 33:
|
|
PPCInterpreter_CRNOR(hCPU, opcode);
|
|
break;
|
|
case 50:
|
|
PPCInterpreter_RFI(hCPU, opcode);
|
|
break;
|
|
case 129:
|
|
PPCInterpreter_CRANDC(hCPU, opcode);
|
|
break;
|
|
case 150:
|
|
PPCInterpreter_ISYNC(hCPU, opcode);
|
|
break;
|
|
case 193:
|
|
PPCInterpreter_CRXOR(hCPU, opcode);
|
|
break;
|
|
case 257:
|
|
PPCInterpreter_CRAND(hCPU, opcode);
|
|
break;
|
|
case 289:
|
|
PPCInterpreter_CREQV(hCPU, opcode);
|
|
break;
|
|
case 417:
|
|
PPCInterpreter_CRORC(hCPU, opcode);
|
|
break;
|
|
case 449:
|
|
PPCInterpreter_CROR(hCPU, opcode);
|
|
break;
|
|
case 528:
|
|
PPCInterpreter_BCCTR(hCPU, opcode);
|
|
break;
|
|
default:
|
|
debug_printf("Unknown execute %04X as [19] at %08X\n", PPC_getBits(opcode, 30, 10), hCPU->instructionPointer);
|
|
cemu_assert_unimplemented();
|
|
break;
|
|
}
|
|
break;
|
|
case 20:
|
|
PPCInterpreter_RLWIMI(hCPU, opcode);
|
|
break;
|
|
case 21:
|
|
PPCInterpreter_RLWINM(hCPU, opcode);
|
|
break;
|
|
case 23:
|
|
PPCInterpreter_RLWNM(hCPU, opcode);
|
|
break;
|
|
case 24:
|
|
PPCInterpreter_ORI(hCPU, opcode);
|
|
break;
|
|
case 25:
|
|
PPCInterpreter_ORIS(hCPU, opcode);
|
|
break;
|
|
case 26:
|
|
PPCInterpreter_XORI(hCPU, opcode);
|
|
break;
|
|
case 27:
|
|
PPCInterpreter_XORIS(hCPU, opcode);
|
|
break;
|
|
case 28:
|
|
PPCInterpreter_ANDI_(hCPU, opcode);
|
|
break;
|
|
case 29:
|
|
PPCInterpreter_ANDIS_(hCPU, opcode);
|
|
break;
|
|
case 31: // opcode category
|
|
switch (PPC_getBits(opcode, 30, 10))
|
|
{
|
|
case 0:
|
|
PPCInterpreter_CMP(hCPU, opcode);
|
|
break;
|
|
case 4:
|
|
#ifdef CEMU_DEBUG_ASSERT
|
|
debug_printf("TW instruction executed at %08x\n", hCPU->instructionPointer);
|
|
#endif
|
|
PPCInterpreter_TW(hCPU, opcode);
|
|
break;
|
|
case 8:
|
|
PPCInterpreter_SUBFC(hCPU, opcode);
|
|
break;
|
|
case 10:
|
|
PPCInterpreter_ADDC(hCPU, opcode);
|
|
break;
|
|
case 11:
|
|
PPCInterpreter_MULHWU_(hCPU, opcode);
|
|
break;
|
|
case 19:
|
|
PPCInterpreter_MFCR(hCPU, opcode);
|
|
break;
|
|
case 20:
|
|
PPCInterpreter_LWARX(hCPU, opcode);
|
|
break;
|
|
case 23:
|
|
PPCInterpreter_LWZX(hCPU, opcode);
|
|
break;
|
|
case 24:
|
|
PPCInterpreter_SLWX(hCPU, opcode);
|
|
break;
|
|
case 26:
|
|
PPCInterpreter_CNTLZW(hCPU, opcode);
|
|
break;
|
|
case 28:
|
|
PPCInterpreter_ANDX(hCPU, opcode);
|
|
break;
|
|
case 32:
|
|
PPCInterpreter_CMPL(hCPU, opcode);
|
|
break;
|
|
case 40:
|
|
PPCInterpreter_SUBF(hCPU, opcode);
|
|
break;
|
|
case 54:
|
|
PPCInterpreter_DCBST(hCPU, opcode);
|
|
break;
|
|
case 55:
|
|
PPCInterpreter_LWZXU(hCPU, opcode);
|
|
break;
|
|
case 60:
|
|
PPCInterpreter_ANDCX(hCPU, opcode);
|
|
break;
|
|
case 75:
|
|
PPCInterpreter_MULHW_(hCPU, opcode);
|
|
break;
|
|
case 83:
|
|
PPCInterpreter_MFMSR(hCPU, opcode);
|
|
break;
|
|
case 86:
|
|
PPCInterpreter_DCBF(hCPU, opcode);
|
|
break;
|
|
case 87:
|
|
PPCInterpreter_LBZX(hCPU, opcode);
|
|
break;
|
|
case 104:
|
|
PPCInterpreter_NEG(hCPU, opcode);
|
|
break;
|
|
case 119: // Sonic Lost World
|
|
PPCInterpreter_LBZXU(hCPU, opcode);
|
|
break;
|
|
case 124:
|
|
PPCInterpreter_NORX(hCPU, opcode);
|
|
break;
|
|
case 136:
|
|
PPCInterpreter_SUBFE(hCPU, opcode);
|
|
break;
|
|
case 138:
|
|
PPCInterpreter_ADDE(hCPU, opcode);
|
|
break;
|
|
case 144:
|
|
PPCInterpreter_MTCRF(hCPU, opcode);
|
|
break;
|
|
case 146:
|
|
PPCInterpreter_MTMSR(hCPU, opcode);
|
|
break;
|
|
case 150:
|
|
PPCInterpreter_STWCX(hCPU, opcode);
|
|
break;
|
|
case 151:
|
|
PPCInterpreter_STWX(hCPU, opcode);
|
|
break;
|
|
case 183:
|
|
PPCInterpreter_STWUX(hCPU, opcode);
|
|
break;
|
|
case 200:
|
|
PPCInterpreter_SUBFZE(hCPU, opcode);
|
|
break;
|
|
case 202:
|
|
PPCInterpreter_ADDZE(hCPU, opcode);
|
|
break;
|
|
case 210:
|
|
PPCInterpreter_MTSR(hCPU, opcode);
|
|
break;
|
|
case 215:
|
|
PPCInterpreter_STBX(hCPU, opcode);
|
|
break;
|
|
case 232: // Trine 2
|
|
PPCInterpreter_SUBFME(hCPU, opcode);
|
|
break;
|
|
case 234:
|
|
PPCInterpreter_ADDME(hCPU, opcode);
|
|
break;
|
|
case 235:
|
|
PPCInterpreter_MULLW(hCPU, opcode);
|
|
break;
|
|
case 247:
|
|
PPCInterpreter_STBUX(hCPU, opcode);
|
|
break;
|
|
case 266:
|
|
PPCInterpreter_ADD(hCPU, opcode);
|
|
break;
|
|
case 278:
|
|
PPCInterpreter_DCBT(hCPU, opcode);
|
|
break;
|
|
case 279:
|
|
PPCInterpreter_LHZX(hCPU, opcode);
|
|
break;
|
|
case 284:
|
|
PPCInterpreter_EQV(hCPU, opcode);
|
|
break;
|
|
case 306:
|
|
PPCInterpreter_TLBIE(hCPU, opcode);
|
|
break;
|
|
case 311: // Wii U Menu v177 (US)
|
|
PPCInterpreter_LHZUX(hCPU, opcode);
|
|
break;
|
|
case 316:
|
|
PPCInterpreter_XOR(hCPU, opcode);
|
|
break;
|
|
case 339:
|
|
PPCInterpreter_MFSPR(hCPU, opcode);
|
|
break;
|
|
case 343:
|
|
PPCInterpreter_LHAX(hCPU, opcode);
|
|
break;
|
|
case 371:
|
|
PPCInterpreter_MFTB(hCPU, opcode);
|
|
break;
|
|
case 375: // Wii U Menu v177 (US)
|
|
PPCInterpreter_LHAUX(hCPU, opcode);
|
|
break;
|
|
case 407:
|
|
PPCInterpreter_STHX(hCPU, opcode);
|
|
break;
|
|
case 412:
|
|
PPCInterpreter_ORC(hCPU, opcode);
|
|
break;
|
|
case 439:
|
|
PPCInterpreter_STHUX(hCPU, opcode);
|
|
break;
|
|
case 444:
|
|
PPCInterpreter_OR(hCPU, opcode);
|
|
break;
|
|
case 459:
|
|
PPCInterpreter_DIVWU(hCPU, opcode);
|
|
break;
|
|
case 467:
|
|
PPCInterpreter_MTSPR(hCPU, opcode);
|
|
break;
|
|
case 470:
|
|
PPCInterpreter_DCBI(hCPU, opcode);
|
|
break;
|
|
case 476:
|
|
PPCInterpreter_NANDX(hCPU, opcode);
|
|
break;
|
|
case 491:
|
|
PPCInterpreter_DIVW(hCPU, opcode);
|
|
break;
|
|
case 512:
|
|
PPCInterpreter_MCRXR(hCPU, opcode);
|
|
break;
|
|
case 520: // Affordable Space Adventures + other Unity games
|
|
PPCInterpreter_SUBFCO(hCPU, opcode);
|
|
break;
|
|
case 522:
|
|
PPCInterpreter_ADDCO(hCPU, opcode);
|
|
break;
|
|
case 534:
|
|
PPCInterpreter_LWBRX(hCPU, opcode);
|
|
break;
|
|
case 535:
|
|
PPCInterpreter_LFSX(hCPU, opcode);
|
|
break;
|
|
case 536:
|
|
PPCInterpreter_SRWX(hCPU, opcode);
|
|
break;
|
|
case 552:
|
|
PPCInterpreter_SUBFO(hCPU, opcode);
|
|
break;
|
|
case 566:
|
|
PPCInterpreter_TLBSYNC(hCPU, opcode);
|
|
break;
|
|
case 567:
|
|
PPCInterpreter_LFSUX(hCPU, opcode);
|
|
break;
|
|
case 595:
|
|
PPCInterpreter_MFSR(hCPU, opcode);
|
|
break;
|
|
case 597:
|
|
PPCInterpreter_LSWI(hCPU, opcode);
|
|
break;
|
|
case 598:
|
|
PPCInterpreter_SYNC(hCPU, opcode);
|
|
break;
|
|
case 599:
|
|
PPCInterpreter_LFDX(hCPU, opcode);
|
|
break;
|
|
case 616:
|
|
PPCInterpreter_NEGO(hCPU, opcode);
|
|
break;
|
|
case 631:
|
|
PPCInterpreter_LFDUX(hCPU, opcode);
|
|
break;
|
|
case 648: // 136 | OE
|
|
PPCInterpreter_SUBFEO(hCPU, opcode);
|
|
break;
|
|
case 650: // 138 | OE
|
|
PPCInterpreter_ADDEO(hCPU, opcode);
|
|
break;
|
|
case 662:
|
|
PPCInterpreter_STWBRX(hCPU, opcode);
|
|
break;
|
|
case 663:
|
|
PPCInterpreter_STFSX(hCPU, opcode);
|
|
break;
|
|
case 695:
|
|
PPCInterpreter_STFSUX(hCPU, opcode);
|
|
break;
|
|
case 725:
|
|
PPCInterpreter_STSWI(hCPU, opcode);
|
|
break;
|
|
case 727:
|
|
PPCInterpreter_STFDX(hCPU, opcode);
|
|
break;
|
|
case 747:
|
|
PPCInterpreter_MULLWO(hCPU, opcode);
|
|
break;
|
|
case 759:
|
|
PPCInterpreter_STFDUX(hCPU, opcode);
|
|
break;
|
|
case 778:
|
|
PPCInterpreter_ADDO(hCPU, opcode);
|
|
break;
|
|
case 790:
|
|
PPCInterpreter_LHBRX(hCPU, opcode);
|
|
break;
|
|
case 792:
|
|
PPCInterpreter_SRAW(hCPU, opcode);
|
|
break;
|
|
case 824:
|
|
PPCInterpreter_SRAWI(hCPU, opcode);
|
|
break;
|
|
case 854:
|
|
PPCInterpreter_EIEIO(hCPU, opcode);
|
|
break;
|
|
case 918:
|
|
PPCInterpreter_STHBRX(hCPU, opcode);
|
|
break;
|
|
case 922:
|
|
PPCInterpreter_EXTSH(hCPU, opcode);
|
|
break;
|
|
case 954:
|
|
PPCInterpreter_EXTSB(hCPU, opcode);
|
|
break;
|
|
case 971:
|
|
PPCInterpreter_DIVWUO(hCPU, opcode);
|
|
break;
|
|
case 982:
|
|
PPCInterpreter_ICBI(hCPU, opcode);
|
|
break;
|
|
case 983:
|
|
PPCInterpreter_STFIWX(hCPU, opcode);
|
|
break;
|
|
case 1003:
|
|
PPCInterpreter_DIVWO(hCPU, opcode);
|
|
break;
|
|
case 1014:
|
|
PPCInterpreter_DCBZ(hCPU, opcode);
|
|
break;
|
|
default:
|
|
debug_printf("Unknown execute %04X as [31] at %08X\n", PPC_getBits(opcode, 30, 10), hCPU->instructionPointer);
|
|
#ifdef CEMU_DEBUG_ASSERT
|
|
assert_dbg();
|
|
#endif
|
|
hCPU->instructionPointer += 4;
|
|
break;
|
|
}
|
|
break;
|
|
case 32:
|
|
PPCInterpreter_LWZ(hCPU, opcode);
|
|
break;
|
|
case 33:
|
|
PPCInterpreter_LWZU(hCPU, opcode);
|
|
break;
|
|
case 34:
|
|
PPCInterpreter_LBZ(hCPU, opcode);
|
|
break;
|
|
case 35:
|
|
PPCInterpreter_LBZU(hCPU, opcode);
|
|
break;
|
|
case 36:
|
|
PPCInterpreter_STW(hCPU, opcode);
|
|
break;
|
|
case 37:
|
|
PPCInterpreter_STWU(hCPU, opcode);
|
|
break;
|
|
case 38:
|
|
PPCInterpreter_STB(hCPU, opcode);
|
|
break;
|
|
case 39:
|
|
PPCInterpreter_STBU(hCPU, opcode);
|
|
break;
|
|
case 40:
|
|
PPCInterpreter_LHZ(hCPU, opcode);
|
|
break;
|
|
case 41:
|
|
PPCInterpreter_LHZU(hCPU, opcode);
|
|
break;
|
|
case 42:
|
|
PPCInterpreter_LHA(hCPU, opcode);
|
|
break;
|
|
case 43:
|
|
PPCInterpreter_LHAU(hCPU, opcode);
|
|
break;
|
|
case 44:
|
|
PPCInterpreter_STH(hCPU, opcode);
|
|
break;
|
|
case 45:
|
|
PPCInterpreter_STHU(hCPU, opcode);
|
|
break;
|
|
case 46:
|
|
PPCInterpreter_LMW(hCPU, opcode);
|
|
break;
|
|
case 47:
|
|
PPCInterpreter_STMW(hCPU, opcode);
|
|
break;
|
|
case 48:
|
|
PPCInterpreter_LFS(hCPU, opcode);
|
|
break;
|
|
case 49:
|
|
PPCInterpreter_LFSU(hCPU, opcode);
|
|
break;
|
|
case 50:
|
|
PPCInterpreter_LFD(hCPU, opcode);
|
|
break;
|
|
case 51:
|
|
PPCInterpreter_LFDU(hCPU, opcode);
|
|
break;
|
|
case 52:
|
|
PPCInterpreter_STFS(hCPU, opcode);
|
|
break;
|
|
case 53:
|
|
PPCInterpreter_STFSU(hCPU, opcode);
|
|
break;
|
|
case 54:
|
|
PPCInterpreter_STFD(hCPU, opcode);
|
|
break;
|
|
case 55:
|
|
PPCInterpreter_STFDU(hCPU, opcode);
|
|
break;
|
|
case 56:
|
|
PPCInterpreter_PSQ_L(hCPU, opcode);
|
|
break;
|
|
case 57:
|
|
PPCInterpreter_PSQ_LU(hCPU, opcode);
|
|
break;
|
|
case 59: //Opcode category
|
|
switch (PPC_getBits(opcode, 30, 5))
|
|
{
|
|
case 18:
|
|
PPCInterpreter_FDIVS(hCPU, opcode);
|
|
break;
|
|
case 20:
|
|
PPCInterpreter_FSUBS(hCPU, opcode);
|
|
break;
|
|
case 21:
|
|
PPCInterpreter_FADDS(hCPU, opcode);
|
|
break;
|
|
case 24:
|
|
PPCInterpreter_FRES(hCPU, opcode);
|
|
break;
|
|
case 25:
|
|
PPCInterpreter_FMULS(hCPU, opcode);
|
|
break;
|
|
case 28:
|
|
PPCInterpreter_FMSUBS(hCPU, opcode);
|
|
break;
|
|
case 29:
|
|
PPCInterpreter_FMADDS(hCPU, opcode);
|
|
break;
|
|
case 30:
|
|
PPCInterpreter_FNMSUBS(hCPU, opcode);
|
|
break;
|
|
case 31:
|
|
PPCInterpreter_FNMADDS(hCPU, opcode);
|
|
break;
|
|
default:
|
|
debug_printf("Unknown execute %04X as [59] at %08X\n", PPC_getBits(opcode, 30, 10), hCPU->instructionPointer);
|
|
cemu_assert_unimplemented();
|
|
break;
|
|
}
|
|
break;
|
|
case 60:
|
|
PPCInterpreter_PSQ_ST(hCPU, opcode);
|
|
break;
|
|
case 61:
|
|
PPCInterpreter_PSQ_STU(hCPU, opcode);
|
|
break;
|
|
case 63: // opcode category
|
|
switch (PPC_getBits(opcode, 30, 5))
|
|
{
|
|
case 0:
|
|
PPCInterpreter_FCMPU(hCPU, opcode);
|
|
break;
|
|
case 12:
|
|
PPCInterpreter_FRSP(hCPU, opcode);
|
|
break;
|
|
case 15:
|
|
PPCInterpreter_FCTIWZ(hCPU, opcode);
|
|
break;
|
|
case 18:
|
|
PPCInterpreter_FDIV(hCPU, opcode);
|
|
break;
|
|
case 20:
|
|
PPCInterpreter_FSUB(hCPU, opcode);
|
|
break;
|
|
case 21:
|
|
PPCInterpreter_FADD(hCPU, opcode);
|
|
break;
|
|
case 23:
|
|
PPCInterpreter_FSEL(hCPU, opcode);
|
|
break;
|
|
case 25:
|
|
PPCInterpreter_FMUL(hCPU, opcode);
|
|
break;
|
|
case 26:
|
|
PPCInterpreter_FRSQRTE(hCPU, opcode);
|
|
break;
|
|
case 28:
|
|
PPCInterpreter_FMSUB(hCPU, opcode);
|
|
break;
|
|
case 29:
|
|
PPCInterpreter_FMADD(hCPU, opcode);
|
|
break;
|
|
case 30:
|
|
PPCInterpreter_FNMSUB(hCPU, opcode);
|
|
break;
|
|
case 31:
|
|
PPCInterpreter_FNMADD(hCPU, opcode);
|
|
break;
|
|
default:
|
|
switch (PPC_getBits(opcode, 30, 10))
|
|
{
|
|
case 14:
|
|
PPCInterpreter_FCTIW(hCPU, opcode);
|
|
break;
|
|
case 32:
|
|
PPCInterpreter_FCMPO(hCPU, opcode);
|
|
break;
|
|
case 38:
|
|
PPCInterpreter_MTFSB1X(hCPU, opcode);
|
|
break;
|
|
case 40:
|
|
PPCInterpreter_FNEG(hCPU, opcode);
|
|
break;
|
|
case 72:
|
|
PPCInterpreter_FMR(hCPU, opcode);
|
|
break;
|
|
case 136: // Darksiders 2
|
|
PPCInterpreter_FNABS(hCPU, opcode);
|
|
break;
|
|
case 264:
|
|
PPCInterpreter_FABS(hCPU, opcode);
|
|
break;
|
|
case 583:
|
|
PPCInterpreter_MFFS(hCPU, opcode);
|
|
break;
|
|
case 711: // IBM documentation has this wrong as 771?
|
|
PPCInterpreter_MTFSF(hCPU, opcode);
|
|
break;
|
|
default:
|
|
debug_printf("Unknown execute %04X as [63] at %08X\n", PPC_getBits(opcode, 30, 10), hCPU->instructionPointer);
|
|
cemu_assert_unimplemented();
|
|
break;
|
|
}
|
|
}
|
|
break;
|
|
default:
|
|
debug_printf("Unknown execute %04X at %08X\n", PPC_getBits(opcode, 5, 6), (unsigned int)hCPU->instructionPointer);
|
|
cemu_assert_unimplemented();
|
|
}
|
|
}
|
|
};
|
|
|
|
// Slim interpreter, trades some features for extra performance
|
|
// Used when emulator runs in CafeOS HLE mode
|
|
// Assumes the following:
|
|
// - No MMU (linear memory with 1:1 mapping of physical to virtual)
|
|
// - No interrupts
|
|
// - Always runs in user mode
|
|
// - Paired single mode is always enabled
|
|
void PPCInterpreterSlim_executeInstruction(PPCInterpreter_t* hCPU)
|
|
{
|
|
PPCInterpreterContainer<PPCItpCafeOSUsermode>::executeInstruction(hCPU);
|
|
}
|
|
|
|
// Full interpreter, supports most PowerPC features
|
|
// Used when emulator runs in LLE mode
|
|
void PPCInterpreterFull_executeInstruction(PPCInterpreter_t* hCPU)
|
|
{
|
|
PPCInterpreterContainer<PPCItpSupervisorWithMMU>::executeInstruction(hCPU);
|
|
}
|