diff --git a/Source/Core/Common/Src/ABI.cpp b/Source/Core/Common/Src/ABI.cpp index 2e29e2bc6c..26059494f2 100644 --- a/Source/Core/Common/Src/ABI.cpp +++ b/Source/Core/Common/Src/ABI.cpp @@ -1,279 +1,279 @@ -#include "Common.h" -#include "x64Emitter.h" -#include "ABI.h" - -using namespace Gen; - -// Shared code between Win64 and Unix64 -// ==================================== - -// Sets up a __cdecl function. -void ABI_EmitPrologue(int maxCallParams) -{ -#ifdef _M_IX86 - // Don't really need to do anything -#elif defined(_M_X64) -#if _WIN32 - int stacksize = ((maxCallParams + 1) & ~1)*8 + 8; - // Set up a stack frame so that we can call functions - // TODO: use maxCallParams - SUB(64, R(RSP), Imm8(stacksize)); -#endif -#else -#error Arch not supported -#endif -} -void ABI_EmitEpilogue(int maxCallParams) -{ -#ifdef _M_IX86 - RET(); -#elif defined(_M_X64) -#ifdef _WIN32 - int stacksize = ((maxCallParams+1)&~1)*8 + 8; - ADD(64, R(RSP), Imm8(stacksize)); -#endif - RET(); -#else -#error Arch not supported -#endif -} - -#ifdef _M_IX86 // All32 - -// Shared code between Win32 and Unix32 -// ==================================== - -void ABI_CallFunctionC(void *func, u32 param1) { - ABI_AlignStack(1 * 4); - PUSH(32, Imm32(param1)); - CALL(func); - ABI_RestoreStack(1 * 4); -} - -void ABI_CallFunctionCC(void *func, u32 param1, u32 param2) { - ABI_AlignStack(2 * 4); - PUSH(32, Imm32(param2)); - PUSH(32, Imm32(param1)); - CALL(func); - ABI_RestoreStack(2 * 4); -} - -// Pass a register as a paremeter. -void ABI_CallFunctionR(void *func, X64Reg reg1) { - ABI_AlignStack(1 * 4); - PUSH(32, R(reg1)); - CALL(func); - ABI_RestoreStack(1 * 4); -} - -void ABI_CallFunctionRR(void *func, Gen::X64Reg reg1, Gen::X64Reg reg2) -{ - ABI_AlignStack(2 * 4); - PUSH(32, R(reg2)); - PUSH(32, R(reg1)); - CALL(func); - ABI_RestoreStack(2 * 4); -} - -void ABI_CallFunctionAC(void *func, const Gen::OpArg &arg1, u32 param2) -{ - ABI_AlignStack(2 * 4); - PUSH(32, arg1); - PUSH(32, Imm32(param2)); - CALL(func); - ABI_RestoreStack(2 * 4); -} - -void ABI_PushAllCalleeSavedRegsAndAdjustStack() { - // Note: 4 * 4 = 16 bytes, so alignment is preserved. - PUSH(EBP); - PUSH(EBX); - PUSH(ESI); - PUSH(EDI); -} - -void ABI_PopAllCalleeSavedRegsAndAdjustStack() { - POP(EDI); - POP(ESI); - POP(EBX); - POP(EBP); -} - -unsigned int ABI_GetAlignedFrameSize(unsigned int frameSize) { - frameSize += 4; // reserve space for return address - unsigned int alignedSize = -#ifdef __GNUC__ - (frameSize + 15) & -16; -#else - frameSize; -#endif - return alignedSize; -} - - -void ABI_AlignStack(unsigned int frameSize) { -// Mac OS X requires the stack to be 16-byte aligned before every call. -// Linux requires the stack to be 16-byte aligned before calls that put SSE -// vectors on the stack, but since we do not keep track of which calls do that, -// it is effectively every call as well. -// Windows binaries compiled with MSVC do not have such a restriction, but I -// expect that GCC on Windows acts the same as GCC on Linux in this respect. -// It would be nice if someone could verify this. -#ifdef __GNUC__ - unsigned int fillSize = - ABI_GetAlignedFrameSize(frameSize) - (frameSize + 4); - if (fillSize != 0) { - SUB(32, R(ESP), Imm8(fillSize)); - } -#endif -} - -void ABI_RestoreStack(unsigned int frameSize) { - unsigned int alignedSize = ABI_GetAlignedFrameSize(frameSize); - alignedSize -= 4; // return address is POPped at end of call - if (alignedSize != 0) { - ADD(32, R(ESP), Imm8(alignedSize)); - } -} - -#else - -void ABI_CallFunctionC(void *func, u32 param1) { - MOV(32, R(ABI_PARAM1), Imm32(param1)); - CALL(func); -} - -void ABI_CallFunctionCC(void *func, u32 param1, u32 param2) { - MOV(32, R(ABI_PARAM1), Imm32(param1)); - MOV(32, R(ABI_PARAM2), Imm32(param2)); - CALL(func); -} - -// Pass a register as a paremeter. -void ABI_CallFunctionR(void *func, X64Reg reg1) { - if (reg1 != ABI_PARAM1) - MOV(32, R(ABI_PARAM1), R(reg1)); - CALL(func); -} - -// Pass a register as a paremeter. -void ABI_CallFunctionRR(void *func, X64Reg reg1, X64Reg reg2) { - if (reg1 != ABI_PARAM1) - MOV(32, R(ABI_PARAM1), R(reg1)); - if (reg2 != ABI_PARAM2) - MOV(32, R(ABI_PARAM2), R(reg2)); - CALL(func); -} - -void ABI_CallFunctionAC(void *func, const Gen::OpArg &arg1, u32 param2) -{ - if (!arg1.IsSimpleReg(ABI_PARAM1)) - MOV(32, R(ABI_PARAM1), arg1); - MOV(32, R(ABI_PARAM2), Imm32(param2)); - CALL(func); -} - -unsigned int ABI_GetAlignedFrameSize(unsigned int frameSize) { - return frameSize; -} - -void ABI_AlignStack(unsigned int /*frameSize*/) { -} - -void ABI_RestoreStack(unsigned int /*frameSize*/) { -} - -#ifdef _WIN32 - -// Win64 Specific Code -// ==================================== -void ABI_PushAllCalleeSavedRegsAndAdjustStack() { - //we only want to do this once - PUSH(RBX); - PUSH(RSI); - PUSH(RDI); - PUSH(RBP); - PUSH(R12); - PUSH(R13); - PUSH(R14); - PUSH(R15); - //TODO: Also preserve XMM0-3? - SUB(64, R(RSP), Imm8(0x28)); -} - -void ABI_PopAllCalleeSavedRegsAndAdjustStack() { - ADD(64, R(RSP), Imm8(0x28)); - POP(R15); - POP(R14); - POP(R13); - POP(R12); - POP(RBP); - POP(RDI); - POP(RSI); - POP(RBX); -} - -// Win64 Specific Code -// ==================================== -void ABI_PushAllCallerSavedRegsAndAdjustStack() { - PUSH(RCX); - PUSH(RDX); - PUSH(RSI); - PUSH(RDI); - PUSH(R8); - PUSH(R9); - PUSH(R10); - PUSH(R11); - //TODO: Also preserve XMM0-15? - SUB(64, R(RSP), Imm8(0x28)); -} - -void ABI_PopAllCallerSavedRegsAndAdjustStack() { - ADD(64, R(RSP), Imm8(0x28)); - POP(R11); - POP(R10); - POP(R9); - POP(R8); - POP(RDI); - POP(RSI); - POP(RDX); - POP(RCX); -} - -#else -// Unix64 Specific Code -// ==================================== -void ABI_PushAllCalleeSavedRegsAndAdjustStack() { - PUSH(RBX); - PUSH(RBP); - PUSH(R12); - PUSH(R13); - PUSH(R14); - PUSH(R15); - PUSH(R15); //just to align stack. duped push/pop doesn't hurt. -} - -void ABI_PopAllCalleeSavedRegsAndAdjustStack() { - POP(R15); - POP(R15); - POP(R14); - POP(R13); - POP(R12); - POP(RBP); - POP(RBX); -} - -void ABI_PushAllCallerSavedRegsAndAdjustStack() { - INT3(); - //not yet supported -} - -void ABI_PopAllCallerSavedRegsAndAdjustStack() { - INT3(); - //not yet supported -} - -#endif - -#endif - +#include "Common.h" +#include "x64Emitter.h" +#include "ABI.h" + +using namespace Gen; + +// Shared code between Win64 and Unix64 +// ==================================== + +// Sets up a __cdecl function. +void ABI_EmitPrologue(int maxCallParams) +{ +#ifdef _M_IX86 + // Don't really need to do anything +#elif defined(_M_X64) +#if _WIN32 + int stacksize = ((maxCallParams + 1) & ~1)*8 + 8; + // Set up a stack frame so that we can call functions + // TODO: use maxCallParams + SUB(64, R(RSP), Imm8(stacksize)); +#endif +#else +#error Arch not supported +#endif +} +void ABI_EmitEpilogue(int maxCallParams) +{ +#ifdef _M_IX86 + RET(); +#elif defined(_M_X64) +#ifdef _WIN32 + int stacksize = ((maxCallParams+1)&~1)*8 + 8; + ADD(64, R(RSP), Imm8(stacksize)); +#endif + RET(); +#else +#error Arch not supported +#endif +} + +#ifdef _M_IX86 // All32 + +// Shared code between Win32 and Unix32 +// ==================================== + +void ABI_CallFunctionC(void *func, u32 param1) { + ABI_AlignStack(1 * 4); + PUSH(32, Imm32(param1)); + CALL(func); + ABI_RestoreStack(1 * 4); +} + +void ABI_CallFunctionCC(void *func, u32 param1, u32 param2) { + ABI_AlignStack(2 * 4); + PUSH(32, Imm32(param2)); + PUSH(32, Imm32(param1)); + CALL(func); + ABI_RestoreStack(2 * 4); +} + +// Pass a register as a paremeter. +void ABI_CallFunctionR(void *func, X64Reg reg1) { + ABI_AlignStack(1 * 4); + PUSH(32, R(reg1)); + CALL(func); + ABI_RestoreStack(1 * 4); +} + +void ABI_CallFunctionRR(void *func, Gen::X64Reg reg1, Gen::X64Reg reg2) +{ + ABI_AlignStack(2 * 4); + PUSH(32, R(reg2)); + PUSH(32, R(reg1)); + CALL(func); + ABI_RestoreStack(2 * 4); +} + +void ABI_CallFunctionAC(void *func, const Gen::OpArg &arg1, u32 param2) +{ + ABI_AlignStack(2 * 4); + PUSH(32, arg1); + PUSH(32, Imm32(param2)); + CALL(func); + ABI_RestoreStack(2 * 4); +} + +void ABI_PushAllCalleeSavedRegsAndAdjustStack() { + // Note: 4 * 4 = 16 bytes, so alignment is preserved. + PUSH(EBP); + PUSH(EBX); + PUSH(ESI); + PUSH(EDI); +} + +void ABI_PopAllCalleeSavedRegsAndAdjustStack() { + POP(EDI); + POP(ESI); + POP(EBX); + POP(EBP); +} + +unsigned int ABI_GetAlignedFrameSize(unsigned int frameSize) { + frameSize += 4; // reserve space for return address + unsigned int alignedSize = +#ifdef __GNUC__ + (frameSize + 15) & -16; +#else + frameSize; +#endif + return alignedSize; +} + + +void ABI_AlignStack(unsigned int frameSize) { +// Mac OS X requires the stack to be 16-byte aligned before every call. +// Linux requires the stack to be 16-byte aligned before calls that put SSE +// vectors on the stack, but since we do not keep track of which calls do that, +// it is effectively every call as well. +// Windows binaries compiled with MSVC do not have such a restriction, but I +// expect that GCC on Windows acts the same as GCC on Linux in this respect. +// It would be nice if someone could verify this. +#ifdef __GNUC__ + unsigned int fillSize = + ABI_GetAlignedFrameSize(frameSize) - (frameSize + 4); + if (fillSize != 0) { + SUB(32, R(ESP), Imm8(fillSize)); + } +#endif +} + +void ABI_RestoreStack(unsigned int frameSize) { + unsigned int alignedSize = ABI_GetAlignedFrameSize(frameSize); + alignedSize -= 4; // return address is POPped at end of call + if (alignedSize != 0) { + ADD(32, R(ESP), Imm8(alignedSize)); + } +} + +#else + +void ABI_CallFunctionC(void *func, u32 param1) { + MOV(32, R(ABI_PARAM1), Imm32(param1)); + CALL(func); +} + +void ABI_CallFunctionCC(void *func, u32 param1, u32 param2) { + MOV(32, R(ABI_PARAM1), Imm32(param1)); + MOV(32, R(ABI_PARAM2), Imm32(param2)); + CALL(func); +} + +// Pass a register as a paremeter. +void ABI_CallFunctionR(void *func, X64Reg reg1) { + if (reg1 != ABI_PARAM1) + MOV(32, R(ABI_PARAM1), R(reg1)); + CALL(func); +} + +// Pass a register as a paremeter. +void ABI_CallFunctionRR(void *func, X64Reg reg1, X64Reg reg2) { + if (reg1 != ABI_PARAM1) + MOV(32, R(ABI_PARAM1), R(reg1)); + if (reg2 != ABI_PARAM2) + MOV(32, R(ABI_PARAM2), R(reg2)); + CALL(func); +} + +void ABI_CallFunctionAC(void *func, const Gen::OpArg &arg1, u32 param2) +{ + if (!arg1.IsSimpleReg(ABI_PARAM1)) + MOV(32, R(ABI_PARAM1), arg1); + MOV(32, R(ABI_PARAM2), Imm32(param2)); + CALL(func); +} + +unsigned int ABI_GetAlignedFrameSize(unsigned int frameSize) { + return frameSize; +} + +void ABI_AlignStack(unsigned int /*frameSize*/) { +} + +void ABI_RestoreStack(unsigned int /*frameSize*/) { +} + +#ifdef _WIN32 + +// Win64 Specific Code +// ==================================== +void ABI_PushAllCalleeSavedRegsAndAdjustStack() { + //we only want to do this once + PUSH(RBX); + PUSH(RSI); + PUSH(RDI); + PUSH(RBP); + PUSH(R12); + PUSH(R13); + PUSH(R14); + PUSH(R15); + //TODO: Also preserve XMM0-3? + SUB(64, R(RSP), Imm8(0x28)); +} + +void ABI_PopAllCalleeSavedRegsAndAdjustStack() { + ADD(64, R(RSP), Imm8(0x28)); + POP(R15); + POP(R14); + POP(R13); + POP(R12); + POP(RBP); + POP(RDI); + POP(RSI); + POP(RBX); +} + +// Win64 Specific Code +// ==================================== +void ABI_PushAllCallerSavedRegsAndAdjustStack() { + PUSH(RCX); + PUSH(RDX); + PUSH(RSI); + PUSH(RDI); + PUSH(R8); + PUSH(R9); + PUSH(R10); + PUSH(R11); + //TODO: Also preserve XMM0-15? + SUB(64, R(RSP), Imm8(0x28)); +} + +void ABI_PopAllCallerSavedRegsAndAdjustStack() { + ADD(64, R(RSP), Imm8(0x28)); + POP(R11); + POP(R10); + POP(R9); + POP(R8); + POP(RDI); + POP(RSI); + POP(RDX); + POP(RCX); +} + +#else +// Unix64 Specific Code +// ==================================== +void ABI_PushAllCalleeSavedRegsAndAdjustStack() { + PUSH(RBX); + PUSH(RBP); + PUSH(R12); + PUSH(R13); + PUSH(R14); + PUSH(R15); + PUSH(R15); //just to align stack. duped push/pop doesn't hurt. +} + +void ABI_PopAllCalleeSavedRegsAndAdjustStack() { + POP(R15); + POP(R15); + POP(R14); + POP(R13); + POP(R12); + POP(RBP); + POP(RBX); +} + +void ABI_PushAllCallerSavedRegsAndAdjustStack() { + INT3(); + //not yet supported +} + +void ABI_PopAllCallerSavedRegsAndAdjustStack() { + INT3(); + //not yet supported +} + +#endif + +#endif + diff --git a/Source/Core/Common/Src/CPUDetect.cpp b/Source/Core/Common/Src/CPUDetect.cpp index 062cdfbbbf..6d3a32edc2 100644 --- a/Source/Core/Common/Src/CPUDetect.cpp +++ b/Source/Core/Common/Src/CPUDetect.cpp @@ -1,200 +1,200 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include - -#ifdef _WIN32 -#define _interlockedbittestandset workaround_ms_header_bug_platform_sdk6_set -#define _interlockedbittestandreset workaround_ms_header_bug_platform_sdk6_reset -#define _interlockedbittestandset64 workaround_ms_header_bug_platform_sdk6_set64 -#define _interlockedbittestandreset64 workaround_ms_header_bug_platform_sdk6_reset64 -#include -#undef _interlockedbittestandset -#undef _interlockedbittestandreset -#undef _interlockedbittestandset64 -#undef _interlockedbittestandreset64 -#else - -//#include -#include - -static inline void do_cpuid(unsigned int *eax, unsigned int *ebx, - unsigned int *ecx, unsigned int *edx) -{ - #ifdef _LP64 - __asm__("cpuid" - : "=a" (*eax), - "=b" (*ebx), - "=c" (*ecx), - "=d" (*edx) - : "a" (*eax) - ); - #else - // Note: EBX is reserved on Mac OS X and in PIC on Linux, so it has to be - // restored at the end of the asm block. - __asm__( - "pushl %%ebx;" - "cpuid;" - "movl %%ebx,%1;" - "popl %%ebx;" - : "=a" (*eax), - "=r" (*ebx), - "=c" (*ecx), - "=d" (*edx) - : "a" (*eax) - ); - #endif -} - -void __cpuid(int info[4], int x) -{ - unsigned int eax = x, ebx = 0, ecx = 0, edx = 0; - do_cpuid(&eax, &ebx, &ecx, &edx); - info[0] = eax; - info[1] = ebx; - info[2] = ecx; - info[3] = edx; -} - -#endif - -#include "Common.h" -#include "CPUDetect.h" -#include "StringUtil.h" - -CPUInfo cpu_info; - -void CPUInfo::Detect() -{ - memset(this, 0, sizeof(*this)); -#ifdef _M_IX86 - Mode64bit = false; -#elif defined (_M_X64) - Mode64bit = true; - OS64bit = true; -#endif - num_cores = 1; - -#ifdef _WIN32 -#ifdef _M_IX86 - BOOL f64 = FALSE; - OS64bit = IsWow64Process(GetCurrentProcess(), &f64) && f64; -#endif -#endif - - // Set obvious defaults, for extra safety - if (Mode64bit) - { - bSSE = true; - bSSE2 = true; - bLongMode = true; - } - - // Assume CPU supports the CPUID instruction. Those that don't can barely boot modern OS:es anyway. - int cpu_id[4]; - memset(cpu_string, 0, sizeof(cpu_string)); - - // Detect CPU's CPUID capabilities, and grab cpu string - __cpuid(cpu_id, 0x00000000); - u32 max_std_fn = cpu_id[0]; // EAX - *((int *)cpu_string) = cpu_id[1]; - *((int *)(cpu_string + 4)) = cpu_id[3]; - *((int *)(cpu_string + 8)) = cpu_id[2]; - __cpuid(cpu_id, 0x80000000); - u32 max_ex_fn = cpu_id[0]; - if (!strcmp(cpu_string, "GenuineIntel")) - vendor = VENDOR_INTEL; - else if (!strcmp(cpu_string, "AuthenticAMD")) - vendor = VENDOR_AMD; - else - vendor = VENDOR_OTHER; - - // Set reasonable default brand string even if brand string not available. - strcpy(brand_string, cpu_string); - - // Detect family and other misc stuff. - bool HTT = false; - int logical_cpu_count = 1; - if (max_std_fn >= 1) { - __cpuid(cpu_id, 0x00000001); - logical_cpu_count = (cpu_id[1] >> 16) & 0xFF; - if ((cpu_id[3] >> 28) & 1) { - // wtf, we get here on my core 2 - HTT = true; - } - if ((cpu_id[3] >> 25) & 1) bSSE = true; - if ((cpu_id[3] >> 26) & 1) bSSE2 = true; - if (cpu_id[2] & 1) bSSE3 = true; - if ((cpu_id[2] >> 9) & 1) bSSSE3 = true; - if ((cpu_id[2] >> 19) & 1) bSSE4_1 = true; - if ((cpu_id[2] >> 20) & 1) bSSE4_2 = true; - } - if (max_ex_fn >= 0x80000004) { - // Extract brand string - __cpuid(cpu_id, 0x80000002); - memcpy(brand_string, cpu_id, sizeof(cpu_id)); - __cpuid(cpu_id, 0x80000003); - memcpy(brand_string + 16, cpu_id, sizeof(cpu_id)); - __cpuid(cpu_id, 0x80000004); - memcpy(brand_string + 32, cpu_id, sizeof(cpu_id)); - } - if (max_ex_fn >= 0x80000001) { - // Check for more features. - __cpuid(cpu_id, 0x80000001); - bool cmp_legacy = false; - if (cpu_id[2] & 1) bLAHFSAHF64 = true; - if (cpu_id[2] & 2) cmp_legacy = true; //wtf is this? - if ((cpu_id[3] >> 29) & 1) bLongMode = true; - } - if (max_ex_fn >= 0x80000008) { - // Get number of cores. This is a bit complicated. Following AMD manual here. - __cpuid(cpu_id, 0x80000008); - int apic_id_core_id_size = (cpu_id[2] >> 12) & 0xF; - if (apic_id_core_id_size == 0) { - // Use what AMD calls the "legacy method" to determine # of cores. - if (HTT) { - num_cores = logical_cpu_count; - } else { - num_cores = 1; - } - } else { - // Use AMD's new method. - num_cores = (cpu_id[2] & 0xFF) + 1; - } - } else { - // Wild guess - if (logical_cpu_count) - num_cores = logical_cpu_count; - } -} - -std::string CPUInfo::Summarize() -{ - std::string sum; - if (num_cores == 1) - sum = StringFromFormat("%s, %i core, ", cpu_string, num_cores); - else - sum = StringFromFormat("%s, %i cores, ", cpu_string, num_cores); - if (bSSE) sum += "SSE"; - if (bSSE2) sum += ", SSE2"; - if (bSSE3) sum += ", SSE3"; - if (bSSSE3) sum += ", SSSE3"; - if (bSSE4_1) sum += ", SSE4.1"; - if (bSSE4_2) sum += ", SSE4.2"; - if (bLongMode) sum += ", 64-bit support"; - return sum; -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#ifdef _WIN32 +#define _interlockedbittestandset workaround_ms_header_bug_platform_sdk6_set +#define _interlockedbittestandreset workaround_ms_header_bug_platform_sdk6_reset +#define _interlockedbittestandset64 workaround_ms_header_bug_platform_sdk6_set64 +#define _interlockedbittestandreset64 workaround_ms_header_bug_platform_sdk6_reset64 +#include +#undef _interlockedbittestandset +#undef _interlockedbittestandreset +#undef _interlockedbittestandset64 +#undef _interlockedbittestandreset64 +#else + +//#include +#include + +static inline void do_cpuid(unsigned int *eax, unsigned int *ebx, + unsigned int *ecx, unsigned int *edx) +{ + #ifdef _LP64 + __asm__("cpuid" + : "=a" (*eax), + "=b" (*ebx), + "=c" (*ecx), + "=d" (*edx) + : "a" (*eax) + ); + #else + // Note: EBX is reserved on Mac OS X and in PIC on Linux, so it has to be + // restored at the end of the asm block. + __asm__( + "pushl %%ebx;" + "cpuid;" + "movl %%ebx,%1;" + "popl %%ebx;" + : "=a" (*eax), + "=r" (*ebx), + "=c" (*ecx), + "=d" (*edx) + : "a" (*eax) + ); + #endif +} + +void __cpuid(int info[4], int x) +{ + unsigned int eax = x, ebx = 0, ecx = 0, edx = 0; + do_cpuid(&eax, &ebx, &ecx, &edx); + info[0] = eax; + info[1] = ebx; + info[2] = ecx; + info[3] = edx; +} + +#endif + +#include "Common.h" +#include "CPUDetect.h" +#include "StringUtil.h" + +CPUInfo cpu_info; + +void CPUInfo::Detect() +{ + memset(this, 0, sizeof(*this)); +#ifdef _M_IX86 + Mode64bit = false; +#elif defined (_M_X64) + Mode64bit = true; + OS64bit = true; +#endif + num_cores = 1; + +#ifdef _WIN32 +#ifdef _M_IX86 + BOOL f64 = FALSE; + OS64bit = IsWow64Process(GetCurrentProcess(), &f64) && f64; +#endif +#endif + + // Set obvious defaults, for extra safety + if (Mode64bit) + { + bSSE = true; + bSSE2 = true; + bLongMode = true; + } + + // Assume CPU supports the CPUID instruction. Those that don't can barely boot modern OS:es anyway. + int cpu_id[4]; + memset(cpu_string, 0, sizeof(cpu_string)); + + // Detect CPU's CPUID capabilities, and grab cpu string + __cpuid(cpu_id, 0x00000000); + u32 max_std_fn = cpu_id[0]; // EAX + *((int *)cpu_string) = cpu_id[1]; + *((int *)(cpu_string + 4)) = cpu_id[3]; + *((int *)(cpu_string + 8)) = cpu_id[2]; + __cpuid(cpu_id, 0x80000000); + u32 max_ex_fn = cpu_id[0]; + if (!strcmp(cpu_string, "GenuineIntel")) + vendor = VENDOR_INTEL; + else if (!strcmp(cpu_string, "AuthenticAMD")) + vendor = VENDOR_AMD; + else + vendor = VENDOR_OTHER; + + // Set reasonable default brand string even if brand string not available. + strcpy(brand_string, cpu_string); + + // Detect family and other misc stuff. + bool HTT = false; + int logical_cpu_count = 1; + if (max_std_fn >= 1) { + __cpuid(cpu_id, 0x00000001); + logical_cpu_count = (cpu_id[1] >> 16) & 0xFF; + if ((cpu_id[3] >> 28) & 1) { + // wtf, we get here on my core 2 + HTT = true; + } + if ((cpu_id[3] >> 25) & 1) bSSE = true; + if ((cpu_id[3] >> 26) & 1) bSSE2 = true; + if (cpu_id[2] & 1) bSSE3 = true; + if ((cpu_id[2] >> 9) & 1) bSSSE3 = true; + if ((cpu_id[2] >> 19) & 1) bSSE4_1 = true; + if ((cpu_id[2] >> 20) & 1) bSSE4_2 = true; + } + if (max_ex_fn >= 0x80000004) { + // Extract brand string + __cpuid(cpu_id, 0x80000002); + memcpy(brand_string, cpu_id, sizeof(cpu_id)); + __cpuid(cpu_id, 0x80000003); + memcpy(brand_string + 16, cpu_id, sizeof(cpu_id)); + __cpuid(cpu_id, 0x80000004); + memcpy(brand_string + 32, cpu_id, sizeof(cpu_id)); + } + if (max_ex_fn >= 0x80000001) { + // Check for more features. + __cpuid(cpu_id, 0x80000001); + bool cmp_legacy = false; + if (cpu_id[2] & 1) bLAHFSAHF64 = true; + if (cpu_id[2] & 2) cmp_legacy = true; //wtf is this? + if ((cpu_id[3] >> 29) & 1) bLongMode = true; + } + if (max_ex_fn >= 0x80000008) { + // Get number of cores. This is a bit complicated. Following AMD manual here. + __cpuid(cpu_id, 0x80000008); + int apic_id_core_id_size = (cpu_id[2] >> 12) & 0xF; + if (apic_id_core_id_size == 0) { + // Use what AMD calls the "legacy method" to determine # of cores. + if (HTT) { + num_cores = logical_cpu_count; + } else { + num_cores = 1; + } + } else { + // Use AMD's new method. + num_cores = (cpu_id[2] & 0xFF) + 1; + } + } else { + // Wild guess + if (logical_cpu_count) + num_cores = logical_cpu_count; + } +} + +std::string CPUInfo::Summarize() +{ + std::string sum; + if (num_cores == 1) + sum = StringFromFormat("%s, %i core, ", cpu_string, num_cores); + else + sum = StringFromFormat("%s, %i cores, ", cpu_string, num_cores); + if (bSSE) sum += "SSE"; + if (bSSE2) sum += ", SSE2"; + if (bSSE3) sum += ", SSE3"; + if (bSSSE3) sum += ", SSSE3"; + if (bSSE4_1) sum += ", SSE4.1"; + if (bSSE4_2) sum += ", SSE4.2"; + if (bLongMode) sum += ", 64-bit support"; + return sum; +} diff --git a/Source/Core/Common/Src/ChunkFile.cpp b/Source/Core/Common/Src/ChunkFile.cpp index 7771786e07..3c4614ddfc 100644 --- a/Source/Core/Common/Src/ChunkFile.cpp +++ b/Source/Core/Common/Src/ChunkFile.cpp @@ -1,22 +1,22 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "ChunkFile.h" - -#include - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "ChunkFile.h" + +#include + diff --git a/Source/Core/Common/Src/Common.cpp b/Source/Core/Common/Src/Common.cpp index 751105cbb5..fae4cb8445 100644 --- a/Source/Core/Common/Src/Common.cpp +++ b/Source/Core/Common/Src/Common.cpp @@ -1,113 +1,113 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include - -#include "Common.h" -#include "StringUtil.h" - -namespace -{ - static PanicAlertHandler panic_handler = 0; -} - -void RegisterPanicAlertHandler(PanicAlertHandler handler) -{ - panic_handler = handler; -} - - -void PanicAlert(const char* format, ...) -{ - va_list args; - va_start(args, format); - - if (panic_handler) - { - std::string msg; - StringFromFormatV(&msg, format, args); - LOG(MASTER_LOG, "PANIC: %s", msg.c_str()); - panic_handler(msg.c_str(), false); - } - else - { -#ifdef _WIN32 - std::string msg; - StringFromFormatV(&msg, format, args); - LOG(MASTER_LOG, "PANIC: %s", msg.c_str()); - MessageBox(0, msg.c_str(), "PANIC!", MB_ICONWARNING); -#elif __GNUC__ - //#error Do a messagebox! - vprintf(format, args); - printf("\n"); -// asm ("int $3") ; -#endif - } - - va_end(args); -} - - -bool PanicYesNo(const char* format, ...) -{ - va_list args; - va_start(args, format); - bool retval; -#ifdef _WIN32 - std::string msg; - StringFromFormatV(&msg, format, args); - LOG(MASTER_LOG, "PANIC: %s", msg.c_str()); - retval = IDYES == MessageBox(0, msg.c_str(), "PANIC! Continue?", MB_ICONQUESTION | MB_YESNO); -#elif __GNUC__ - //vprintf(format, args); - return(true); //#error Do a messagebox! -#endif - - va_end(args); - return(retval); -} - - -bool AskYesNo(const char* format, ...) -{ - va_list args; - va_start(args, format); - bool retval; -#ifdef _WIN32 - std::string msg; - StringFromFormatV(&msg, format, args); - LOG(MASTER_LOG, "ASK: %s", msg.c_str()); - retval = IDYES == MessageBox(0, msg.c_str(), "Dolphin", MB_ICONQUESTION | MB_YESNO); -#elif __GNUC__ - //vprintf(format, args); - return(true); //#error Do a messagebox! -#endif - - va_end(args); - return(retval); -} - -// Standard implementation of logging - simply print to standard output. -// Programs are welcome to override this. -/* - void __Log(int logNumber, const char *text, ...) - { - va_list args; - va_start(args, text); - vprintf(text, args); - va_end(args); - }*/ +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "Common.h" +#include "StringUtil.h" + +namespace +{ + static PanicAlertHandler panic_handler = 0; +} + +void RegisterPanicAlertHandler(PanicAlertHandler handler) +{ + panic_handler = handler; +} + + +void PanicAlert(const char* format, ...) +{ + va_list args; + va_start(args, format); + + if (panic_handler) + { + std::string msg; + StringFromFormatV(&msg, format, args); + LOG(MASTER_LOG, "PANIC: %s", msg.c_str()); + panic_handler(msg.c_str(), false); + } + else + { +#ifdef _WIN32 + std::string msg; + StringFromFormatV(&msg, format, args); + LOG(MASTER_LOG, "PANIC: %s", msg.c_str()); + MessageBox(0, msg.c_str(), "PANIC!", MB_ICONWARNING); +#elif __GNUC__ + //#error Do a messagebox! + vprintf(format, args); + printf("\n"); +// asm ("int $3") ; +#endif + } + + va_end(args); +} + + +bool PanicYesNo(const char* format, ...) +{ + va_list args; + va_start(args, format); + bool retval; +#ifdef _WIN32 + std::string msg; + StringFromFormatV(&msg, format, args); + LOG(MASTER_LOG, "PANIC: %s", msg.c_str()); + retval = IDYES == MessageBox(0, msg.c_str(), "PANIC! Continue?", MB_ICONQUESTION | MB_YESNO); +#elif __GNUC__ + //vprintf(format, args); + return(true); //#error Do a messagebox! +#endif + + va_end(args); + return(retval); +} + + +bool AskYesNo(const char* format, ...) +{ + va_list args; + va_start(args, format); + bool retval; +#ifdef _WIN32 + std::string msg; + StringFromFormatV(&msg, format, args); + LOG(MASTER_LOG, "ASK: %s", msg.c_str()); + retval = IDYES == MessageBox(0, msg.c_str(), "Dolphin", MB_ICONQUESTION | MB_YESNO); +#elif __GNUC__ + //vprintf(format, args); + return(true); //#error Do a messagebox! +#endif + + va_end(args); + return(retval); +} + +// Standard implementation of logging - simply print to standard output. +// Programs are welcome to override this. +/* + void __Log(int logNumber, const char *text, ...) + { + va_list args; + va_start(args, text); + vprintf(text, args); + va_end(args); + }*/ diff --git a/Source/Core/Common/Src/DriveUtil.cpp b/Source/Core/Common/Src/DriveUtil.cpp index 2190eb326d..165e0db847 100644 --- a/Source/Core/Common/Src/DriveUtil.cpp +++ b/Source/Core/Common/Src/DriveUtil.cpp @@ -1,57 +1,57 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" - -#ifdef _WIN32 -#include -#include -#endif - -void GetAllRemovableDrives(std::vector *drives) { - drives->clear(); -#ifdef _WIN32 - HANDLE hDisk; - DISK_GEOMETRY diskGeometry; - - for (int i = 'A'; i < 'Z'; i++) - { - char path[MAX_PATH]; - sprintf(path, "\\\\.\\%c:", i); - hDisk = CreateFile(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); - if (hDisk != INVALID_HANDLE_VALUE) - { - DWORD dwBytes; - DeviceIoControl(hDisk, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &diskGeometry, sizeof(DISK_GEOMETRY), &dwBytes, NULL); - // Only proceed if disk is a removable media - if (diskGeometry.MediaType == RemovableMedia) - { - if (diskGeometry.BytesPerSector == 2048) { - // Probably CD/DVD drive. - // "Remove" the "\\.\" part of the path and return it. - drives->push_back(path + 4); - } - } - } - CloseHandle(hDisk); - } -#else - // TODO - // stat("/media/cdrom") or whatever etc etc -#endif -} - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" + +#ifdef _WIN32 +#include +#include +#endif + +void GetAllRemovableDrives(std::vector *drives) { + drives->clear(); +#ifdef _WIN32 + HANDLE hDisk; + DISK_GEOMETRY diskGeometry; + + for (int i = 'A'; i < 'Z'; i++) + { + char path[MAX_PATH]; + sprintf(path, "\\\\.\\%c:", i); + hDisk = CreateFile(path, 0, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL, OPEN_EXISTING, 0, NULL); + if (hDisk != INVALID_HANDLE_VALUE) + { + DWORD dwBytes; + DeviceIoControl(hDisk, IOCTL_DISK_GET_DRIVE_GEOMETRY, NULL, 0, &diskGeometry, sizeof(DISK_GEOMETRY), &dwBytes, NULL); + // Only proceed if disk is a removable media + if (diskGeometry.MediaType == RemovableMedia) + { + if (diskGeometry.BytesPerSector == 2048) { + // Probably CD/DVD drive. + // "Remove" the "\\.\" part of the path and return it. + drives->push_back(path + 4); + } + } + } + CloseHandle(hDisk); + } +#else + // TODO + // stat("/media/cdrom") or whatever etc etc +#endif +} + diff --git a/Source/Core/Common/Src/DynamicLibrary.cpp b/Source/Core/Common/Src/DynamicLibrary.cpp index 70ffe82347..2010966e07 100644 --- a/Source/Core/Common/Src/DynamicLibrary.cpp +++ b/Source/Core/Common/Src/DynamicLibrary.cpp @@ -1,151 +1,151 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include -#ifdef _WIN32 -#include -#else -#include -#include -#endif - -#include "Common.h" -#include "StringUtil.h" -#include "DynamicLibrary.h" -#include "../../Core/Src/PowerPC/PowerPC.h" - -DynamicLibrary::DynamicLibrary() -{ - library = 0; -} - -#ifdef _WIN32 -std::string GetLastErrorAsString() -{ - LPVOID lpMsgBuf = 0; - DWORD error = GetLastError(); - FormatMessage( - FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, - NULL, - error, - MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language - (LPTSTR) &lpMsgBuf, - 0, NULL); - std::string s; - if (lpMsgBuf) - { - s = ((char *)lpMsgBuf); - LocalFree(lpMsgBuf); - } else { - s = StringFromFormat("(unknown error %08x)", error); - } - return s; -} -#endif - -// ------------------------------------------------------------------ -/* Loading means loading the dll with LoadLibrary() to get an instance to the dll. - This is done when Dolphin is started to determine which dlls are good, and - before opening the Config and Debugging windowses from Plugin.cpp and - before opening the dll for running the emulation in Video_...cpp in Core. */ -// ----------------------- -int DynamicLibrary::Load(const char* filename) -{ - if (!filename || strlen(filename) == 0) - { - LOG(MASTER_LOG, "Missing filename of dynamic library to load"); - return 0; - } - LOG(MASTER_LOG, "Trying to load library %s", filename); - - if (IsLoaded()) - { - LOG(MASTER_LOG, "Trying to load already loaded library %s", filename); - return 2; - } - -#ifdef _WIN32 - library = LoadLibrary(filename); - if (!library) { - LOG(MASTER_LOG, "Error loading DLL %s: %s", filename, GetLastErrorAsString().c_str()); - return 0; - } -#else - library = dlopen(filename, RTLD_NOW | RTLD_LOCAL); - if (!library) - { - #ifdef LOGGING - LOG(MASTER_LOG, "Error loading DLL %s: %s", filename, dlerror()); - #else - printf("Error loading DLL %s: %s", filename, dlerror()); - #endif - return false; - } -#endif - library_file = filename; - return 1; -} - - -void DynamicLibrary::Unload() -{ - if (!IsLoaded()) - { - PanicAlert("Trying to unload non-loaded library"); - return; - } - -#ifdef _WIN32 - /* TEMPORARY SOLUTION: To prevent that Dolphin hangs when a game is stopped - or when we try to close Dolphin. It's possible that it only occur when we render - to the main window. And sometimes FreeLibrary works without any problem, so - don't remove this just because it doesn't hang once. I could not find the - actual cause of it. */ - if( ! (library_file.find("OGL.") != std::string::npos) && !PowerPC::CPU_POWERDOWN) - FreeLibrary(library); -#else - dlclose(library); -#endif - library = 0; -} - - -void* DynamicLibrary::Get(const char* funcname) const -{ - void* retval; -#ifdef _WIN32 - if (!library) - { - PanicAlert("Can't find function %s - Library not loaded."); - } - retval = GetProcAddress(library, funcname); - //if (!retval) - //{ - // PanicAlert("Did not find function %s in library %s.", funcname, library_file.c_str()); - //} -#else - retval = dlsym(library, funcname); - - if (!retval) - { - printf("Symbol %s missing in %s (error: %s)\n", funcname, library_file.c_str(), dlerror()); - } -#endif - return retval; -} - - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#ifdef _WIN32 +#include +#else +#include +#include +#endif + +#include "Common.h" +#include "StringUtil.h" +#include "DynamicLibrary.h" +#include "../../Core/Src/PowerPC/PowerPC.h" + +DynamicLibrary::DynamicLibrary() +{ + library = 0; +} + +#ifdef _WIN32 +std::string GetLastErrorAsString() +{ + LPVOID lpMsgBuf = 0; + DWORD error = GetLastError(); + FormatMessage( + FORMAT_MESSAGE_ALLOCATE_BUFFER | FORMAT_MESSAGE_FROM_SYSTEM, + NULL, + error, + MAKELANGID(LANG_NEUTRAL, SUBLANG_DEFAULT), // Default language + (LPTSTR) &lpMsgBuf, + 0, NULL); + std::string s; + if (lpMsgBuf) + { + s = ((char *)lpMsgBuf); + LocalFree(lpMsgBuf); + } else { + s = StringFromFormat("(unknown error %08x)", error); + } + return s; +} +#endif + +// ------------------------------------------------------------------ +/* Loading means loading the dll with LoadLibrary() to get an instance to the dll. + This is done when Dolphin is started to determine which dlls are good, and + before opening the Config and Debugging windowses from Plugin.cpp and + before opening the dll for running the emulation in Video_...cpp in Core. */ +// ----------------------- +int DynamicLibrary::Load(const char* filename) +{ + if (!filename || strlen(filename) == 0) + { + LOG(MASTER_LOG, "Missing filename of dynamic library to load"); + return 0; + } + LOG(MASTER_LOG, "Trying to load library %s", filename); + + if (IsLoaded()) + { + LOG(MASTER_LOG, "Trying to load already loaded library %s", filename); + return 2; + } + +#ifdef _WIN32 + library = LoadLibrary(filename); + if (!library) { + LOG(MASTER_LOG, "Error loading DLL %s: %s", filename, GetLastErrorAsString().c_str()); + return 0; + } +#else + library = dlopen(filename, RTLD_NOW | RTLD_LOCAL); + if (!library) + { + #ifdef LOGGING + LOG(MASTER_LOG, "Error loading DLL %s: %s", filename, dlerror()); + #else + printf("Error loading DLL %s: %s", filename, dlerror()); + #endif + return false; + } +#endif + library_file = filename; + return 1; +} + + +void DynamicLibrary::Unload() +{ + if (!IsLoaded()) + { + PanicAlert("Trying to unload non-loaded library"); + return; + } + +#ifdef _WIN32 + /* TEMPORARY SOLUTION: To prevent that Dolphin hangs when a game is stopped + or when we try to close Dolphin. It's possible that it only occur when we render + to the main window. And sometimes FreeLibrary works without any problem, so + don't remove this just because it doesn't hang once. I could not find the + actual cause of it. */ + if( ! (library_file.find("OGL.") != std::string::npos) && !PowerPC::CPU_POWERDOWN) + FreeLibrary(library); +#else + dlclose(library); +#endif + library = 0; +} + + +void* DynamicLibrary::Get(const char* funcname) const +{ + void* retval; +#ifdef _WIN32 + if (!library) + { + PanicAlert("Can't find function %s - Library not loaded."); + } + retval = GetProcAddress(library, funcname); + //if (!retval) + //{ + // PanicAlert("Did not find function %s in library %s.", funcname, library_file.c_str()); + //} +#else + retval = dlsym(library, funcname); + + if (!retval) + { + printf("Symbol %s missing in %s (error: %s)\n", funcname, library_file.c_str(), dlerror()); + } +#endif + return retval; +} + + diff --git a/Source/Core/Common/Src/ExtendedTrace.cpp b/Source/Core/Common/Src/ExtendedTrace.cpp index f1dafa610e..2c119a1d86 100644 --- a/Source/Core/Common/Src/ExtendedTrace.cpp +++ b/Source/Core/Common/Src/ExtendedTrace.cpp @@ -1,437 +1,437 @@ -////////////////////////////////////////////////////////////////////////////////////// -// -// Written by Zoltan Csizmadia, zoltan_csizmadia@yahoo.com -// For companies(Austin,TX): If you would like to get my resume, send an email. -// -// The source is free, but if you want to use it, mention my name and e-mail address -// -// History: -// 1.0 Initial version Zoltan Csizmadia -// 1.1 WhineCube version Masken -// 1.2 Dolphin version Masken -// -////////////////////////////////////////////////////////////////////////////////////// -// -// ExtendedTrace.cpp -// - -// Include StdAfx.h, if you're using precompiled -// header through StdAfx.h -//#include "stdafx.h" - -#if defined(WIN32) - -#include -#include -#include "ExtendedTrace.h" -using namespace std; - -#include -#include - -#define BUFFERSIZE 0x200 -#pragma warning(disable:4996) - -// Unicode safe char* -> TCHAR* conversion -void PCSTR2LPTSTR( PCSTR lpszIn, LPTSTR lpszOut ) -{ -#if defined(UNICODE)||defined(_UNICODE) - ULONG index = 0; - PCSTR lpAct = lpszIn; - - for( ; ; lpAct++ ) - { - lpszOut[index++] = (TCHAR)(*lpAct); - if ( *lpAct == 0 ) - break; - } -#else - // This is trivial :) - strcpy( lpszOut, lpszIn ); -#endif -} - -// Let's figure out the path for the symbol files -// Search path= ".;%_NT_SYMBOL_PATH%;%_NT_ALTERNATE_SYMBOL_PATH%;%SYSTEMROOT%;%SYSTEMROOT%\System32;" + lpszIniPath -// Note: There is no size check for lpszSymbolPath! -static void InitSymbolPath( PSTR lpszSymbolPath, PCSTR lpszIniPath ) -{ - CHAR lpszPath[BUFFERSIZE]; - - // Creating the default path - // ".;%_NT_SYMBOL_PATH%;%_NT_ALTERNATE_SYMBOL_PATH%;%SYSTEMROOT%;%SYSTEMROOT%\System32;" - strcpy( lpszSymbolPath, "." ); - - // environment variable _NT_SYMBOL_PATH - if ( GetEnvironmentVariableA( "_NT_SYMBOL_PATH", lpszPath, BUFFERSIZE ) ) - { - strcat( lpszSymbolPath, ";" ); - strcat( lpszSymbolPath, lpszPath ); - } - - // environment variable _NT_ALTERNATE_SYMBOL_PATH - if ( GetEnvironmentVariableA( "_NT_ALTERNATE_SYMBOL_PATH", lpszPath, BUFFERSIZE ) ) - { - strcat( lpszSymbolPath, ";" ); - strcat( lpszSymbolPath, lpszPath ); - } - - // environment variable SYSTEMROOT - if ( GetEnvironmentVariableA( "SYSTEMROOT", lpszPath, BUFFERSIZE ) ) - { - strcat( lpszSymbolPath, ";" ); - strcat( lpszSymbolPath, lpszPath ); - strcat( lpszSymbolPath, ";" ); - - // SYSTEMROOT\System32 - strcat( lpszSymbolPath, lpszPath ); - strcat( lpszSymbolPath, "\\System32" ); - } - - // Add user defined path - if ( lpszIniPath != NULL ) - if ( lpszIniPath[0] != '\0' ) - { - strcat( lpszSymbolPath, ";" ); - strcat( lpszSymbolPath, lpszIniPath ); - } -} - -// Uninitialize the loaded symbol files -BOOL UninitSymInfo() { - return SymCleanup( GetCurrentProcess() ); -} - -// Initializes the symbol files -BOOL InitSymInfo( PCSTR lpszInitialSymbolPath ) -{ - CHAR lpszSymbolPath[BUFFERSIZE]; - DWORD symOptions = SymGetOptions(); - - symOptions |= SYMOPT_LOAD_LINES; - symOptions &= ~SYMOPT_UNDNAME; - SymSetOptions( symOptions ); - InitSymbolPath( lpszSymbolPath, lpszInitialSymbolPath ); - - return SymInitialize( GetCurrentProcess(), lpszSymbolPath, TRUE); -} - -// Get the module name from a given address -static BOOL GetModuleNameFromAddress( UINT address, LPTSTR lpszModule ) -{ - BOOL ret = FALSE; - IMAGEHLP_MODULE moduleInfo; - - ::ZeroMemory( &moduleInfo, sizeof(moduleInfo) ); - moduleInfo.SizeOfStruct = sizeof(moduleInfo); - - if ( SymGetModuleInfo( GetCurrentProcess(), (DWORD)address, &moduleInfo ) ) - { - // Got it! - PCSTR2LPTSTR( moduleInfo.ModuleName, lpszModule ); - ret = TRUE; - } - else - // Not found :( - _tcscpy( lpszModule, _T("?") ); - - return ret; -} - -// Get function prototype and parameter info from ip address and stack address -static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, LPTSTR lpszSymbol ) -{ - BOOL ret = FALSE; - DWORD dwDisp = 0; - DWORD dwSymSize = 10000; - TCHAR lpszUnDSymbol[BUFFERSIZE]=_T("?"); - CHAR lpszNonUnicodeUnDSymbol[BUFFERSIZE]="?"; - LPTSTR lpszParamSep = NULL; - LPTSTR lpszParsed = lpszUnDSymbol; - PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc( GMEM_FIXED, dwSymSize ); - - ::ZeroMemory( pSym, dwSymSize ); - pSym->SizeOfStruct = dwSymSize; - pSym->MaxNameLength = dwSymSize - sizeof(IMAGEHLP_SYMBOL); - - // Set the default to unknown - _tcscpy( lpszSymbol, _T("?") ); - - // Get symbol info for IP -#ifndef _M_X64 - if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, &dwDisp, pSym ) ) -#else - //makes it compile but hell im not sure if this works... - if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, (PDWORD64)&dwDisp, pSym ) ) -#endif - { - // Make the symbol readable for humans - UnDecorateSymbolName( pSym->Name, lpszNonUnicodeUnDSymbol, BUFFERSIZE, - UNDNAME_COMPLETE | - UNDNAME_NO_THISTYPE | - UNDNAME_NO_SPECIAL_SYMS | - UNDNAME_NO_MEMBER_TYPE | - UNDNAME_NO_MS_KEYWORDS | - UNDNAME_NO_ACCESS_SPECIFIERS ); - - // Symbol information is ANSI string - PCSTR2LPTSTR( lpszNonUnicodeUnDSymbol, lpszUnDSymbol ); - - // I am just smarter than the symbol file :) - if ( _tcscmp(lpszUnDSymbol, _T("_WinMain@16")) == 0 ) - _tcscpy(lpszUnDSymbol, _T("WinMain(HINSTANCE,HINSTANCE,LPCTSTR,int)")); - else - if ( _tcscmp(lpszUnDSymbol, _T("_main")) == 0 ) - _tcscpy(lpszUnDSymbol, _T("main(int,TCHAR * *)")); - else - if ( _tcscmp(lpszUnDSymbol, _T("_mainCRTStartup")) == 0 ) - _tcscpy(lpszUnDSymbol, _T("mainCRTStartup()")); - else - if ( _tcscmp(lpszUnDSymbol, _T("_wmain")) == 0 ) - _tcscpy(lpszUnDSymbol, _T("wmain(int,TCHAR * *,TCHAR * *)")); - else - if ( _tcscmp(lpszUnDSymbol, _T("_wmainCRTStartup")) == 0 ) - _tcscpy(lpszUnDSymbol, _T("wmainCRTStartup()")); - - lpszSymbol[0] = _T('\0'); - - // Let's go through the stack, and modify the function prototype, and insert the actual - // parameter values from the stack - if ( _tcsstr( lpszUnDSymbol, _T("(void)") ) == NULL && _tcsstr( lpszUnDSymbol, _T("()") ) == NULL) - { - ULONG index = 0; - for( ; ; index++ ) - { - lpszParamSep = _tcschr( lpszParsed, _T(',') ); - if ( lpszParamSep == NULL ) - break; - - *lpszParamSep = _T('\0'); - - _tcscat( lpszSymbol, lpszParsed ); - _stprintf( lpszSymbol + _tcslen(lpszSymbol), _T("=0x%08X,"), *((ULONG*)(stackAddress) + 2 + index) ); - - lpszParsed = lpszParamSep + 1; - } - - lpszParamSep = _tcschr( lpszParsed, _T(')') ); - if ( lpszParamSep != NULL ) - { - *lpszParamSep = _T('\0'); - - _tcscat( lpszSymbol, lpszParsed ); - _stprintf( lpszSymbol + _tcslen(lpszSymbol), _T("=0x%08X)"), *((ULONG*)(stackAddress) + 2 + index) ); - - lpszParsed = lpszParamSep + 1; - } - } - - _tcscat( lpszSymbol, lpszParsed ); - - ret = TRUE; - } - GlobalFree( pSym ); - - return ret; -} - -// Get source file name and line number from IP address -// The output format is: "sourcefile(linenumber)" or -// "modulename!address" or -// "address" -static BOOL GetSourceInfoFromAddress( UINT address, LPTSTR lpszSourceInfo ) -{ - BOOL ret = FALSE; - IMAGEHLP_LINE lineInfo; - DWORD dwDisp; - TCHAR lpszFileName[BUFFERSIZE] = _T(""); - TCHAR lpModuleInfo[BUFFERSIZE] = _T(""); - - _tcscpy( lpszSourceInfo, _T("?(?)") ); - - ::ZeroMemory( &lineInfo, sizeof( lineInfo ) ); - lineInfo.SizeOfStruct = sizeof( lineInfo ); - - if ( SymGetLineFromAddr( GetCurrentProcess(), address, &dwDisp, &lineInfo ) ) - { - // Got it. Let's use "sourcefile(linenumber)" format - PCSTR2LPTSTR( lineInfo.FileName, lpszFileName ); - TCHAR fname[_MAX_FNAME]; - TCHAR ext[_MAX_EXT]; - _tsplitpath(lpszFileName, NULL, NULL, fname, ext); - _stprintf( lpszSourceInfo, _T("%s%s(%d)"), fname, ext, lineInfo.LineNumber ); - ret = TRUE; - } - else - { - // There is no source file information. :( - // Let's use the "modulename!address" format - GetModuleNameFromAddress( address, lpModuleInfo ); - - if ( lpModuleInfo[0] == _T('?') || lpModuleInfo[0] == _T('\0')) - // There is no modulename information. :(( - // Let's use the "address" format - _stprintf( lpszSourceInfo, _T("0x%08X"), address ); - else - _stprintf( lpszSourceInfo, _T("%s!0x%08X"), lpModuleInfo, address ); - - ret = FALSE; - } - - return ret; -} - -void StackTrace( HANDLE hThread, LPCTSTR lpszMessage, FILE *file ) -{ - STACKFRAME callStack; - BOOL bResult; - CONTEXT context; - TCHAR symInfo[BUFFERSIZE] = _T("?"); - TCHAR srcInfo[BUFFERSIZE] = _T("?"); - HANDLE hProcess = GetCurrentProcess(); - - // If it's not this thread, let's suspend it, and resume it at the end - if ( hThread != GetCurrentThread() ) - if ( SuspendThread( hThread ) == -1 ) - { - // whaaat ?! - etfprint(file, "Call stack info failed\n"); - return; - } - - ::ZeroMemory( &context, sizeof(context) ); - context.ContextFlags = CONTEXT_FULL; - - if ( !GetThreadContext( hThread, &context ) ) - { - etfprint(file, "Call stack info failed\n"); - return; - } - - ::ZeroMemory( &callStack, sizeof(callStack) ); -#ifndef _M_X64 - callStack.AddrPC.Offset = context.Eip; - callStack.AddrStack.Offset = context.Esp; - callStack.AddrFrame.Offset = context.Ebp; -#else - callStack.AddrPC.Offset = context.Rip; - callStack.AddrStack.Offset = context.Rsp; - callStack.AddrFrame.Offset = context.Rbp; -#endif - callStack.AddrPC.Mode = AddrModeFlat; - callStack.AddrStack.Mode = AddrModeFlat; - callStack.AddrFrame.Mode = AddrModeFlat; - - etfprint(file, "Call stack info: \n"); - etfprint(file, lpszMessage); - - GetFunctionInfoFromAddresses( callStack.AddrPC.Offset, callStack.AddrFrame.Offset, symInfo ); - GetSourceInfoFromAddress( callStack.AddrPC.Offset, srcInfo ); - etfprint(file, string(" ") + srcInfo + string(" : ") + symInfo + string("\n")); - - for( ULONG index = 0; ; index++ ) - { - bResult = StackWalk( - IMAGE_FILE_MACHINE_I386, - hProcess, - hThread, - &callStack, - NULL, - NULL, - SymFunctionTableAccess, - SymGetModuleBase, - NULL); - - if ( index == 0 ) - continue; - - if( !bResult || callStack.AddrFrame.Offset == 0 ) - break; - - GetFunctionInfoFromAddresses( callStack.AddrPC.Offset, callStack.AddrFrame.Offset, symInfo ); - GetSourceInfoFromAddress( callStack.AddrPC.Offset, srcInfo ); - etfprint(file, string(" ") + srcInfo + string(" : ") + symInfo + string("\n")); - - } - - if ( hThread != GetCurrentThread() ) - ResumeThread( hThread ); -} - -void StackTrace( HANDLE hThread, LPCTSTR lpszMessage, FILE *file, DWORD eip, DWORD esp, DWORD ebp ) -{ - STACKFRAME callStack; - BOOL bResult; - TCHAR symInfo[BUFFERSIZE] = _T("?"); - TCHAR srcInfo[BUFFERSIZE] = _T("?"); - HANDLE hProcess = GetCurrentProcess(); - - // If it's not this thread, let's suspend it, and resume it at the end - if ( hThread != GetCurrentThread() ) - if ( SuspendThread( hThread ) == -1 ) - { - // whaaat ?! - etfprint(file, "Call stack info failed\n"); - return; - } - - ::ZeroMemory( &callStack, sizeof(callStack) ); - callStack.AddrPC.Offset = eip; - callStack.AddrStack.Offset = esp; - callStack.AddrFrame.Offset = ebp; - callStack.AddrPC.Mode = AddrModeFlat; - callStack.AddrStack.Mode = AddrModeFlat; - callStack.AddrFrame.Mode = AddrModeFlat; - - etfprint(file, "Call stack info: \n"); - etfprint(file, lpszMessage); - - GetFunctionInfoFromAddresses( callStack.AddrPC.Offset, callStack.AddrFrame.Offset, symInfo ); - GetSourceInfoFromAddress( callStack.AddrPC.Offset, srcInfo ); - etfprint(file, string(" ") + srcInfo + string(" : ") + symInfo + string("\n")); - - for( ULONG index = 0; ; index++ ) - { - bResult = StackWalk( - IMAGE_FILE_MACHINE_I386, - hProcess, - hThread, - &callStack, - NULL, - NULL, - SymFunctionTableAccess, - SymGetModuleBase, - NULL); - - if ( index == 0 ) - continue; - - if( !bResult || callStack.AddrFrame.Offset == 0 ) - break; - - GetFunctionInfoFromAddresses( callStack.AddrPC.Offset, callStack.AddrFrame.Offset, symInfo ); - GetSourceInfoFromAddress( callStack.AddrPC.Offset, srcInfo ); - etfprint(file, string(" ") + srcInfo + string(" : ") + symInfo + string("\n")); - - } - - if ( hThread != GetCurrentThread() ) - ResumeThread( hThread ); -} - -char g_uefbuf[2048]; - -void etfprintf(FILE *file, const char *format, ...) { - va_list ap; - va_start(ap, format); - int len = vsprintf(g_uefbuf, format, ap); - fwrite(g_uefbuf, 1, len, file); - va_end(ap); -} - -void etfprint(FILE *file, const std::string &text) { - size_t len = text.length(); - fwrite(text.data(), 1, len, file); -} - -#endif //WIN32 +////////////////////////////////////////////////////////////////////////////////////// +// +// Written by Zoltan Csizmadia, zoltan_csizmadia@yahoo.com +// For companies(Austin,TX): If you would like to get my resume, send an email. +// +// The source is free, but if you want to use it, mention my name and e-mail address +// +// History: +// 1.0 Initial version Zoltan Csizmadia +// 1.1 WhineCube version Masken +// 1.2 Dolphin version Masken +// +////////////////////////////////////////////////////////////////////////////////////// +// +// ExtendedTrace.cpp +// + +// Include StdAfx.h, if you're using precompiled +// header through StdAfx.h +//#include "stdafx.h" + +#if defined(WIN32) + +#include +#include +#include "ExtendedTrace.h" +using namespace std; + +#include +#include + +#define BUFFERSIZE 0x200 +#pragma warning(disable:4996) + +// Unicode safe char* -> TCHAR* conversion +void PCSTR2LPTSTR( PCSTR lpszIn, LPTSTR lpszOut ) +{ +#if defined(UNICODE)||defined(_UNICODE) + ULONG index = 0; + PCSTR lpAct = lpszIn; + + for( ; ; lpAct++ ) + { + lpszOut[index++] = (TCHAR)(*lpAct); + if ( *lpAct == 0 ) + break; + } +#else + // This is trivial :) + strcpy( lpszOut, lpszIn ); +#endif +} + +// Let's figure out the path for the symbol files +// Search path= ".;%_NT_SYMBOL_PATH%;%_NT_ALTERNATE_SYMBOL_PATH%;%SYSTEMROOT%;%SYSTEMROOT%\System32;" + lpszIniPath +// Note: There is no size check for lpszSymbolPath! +static void InitSymbolPath( PSTR lpszSymbolPath, PCSTR lpszIniPath ) +{ + CHAR lpszPath[BUFFERSIZE]; + + // Creating the default path + // ".;%_NT_SYMBOL_PATH%;%_NT_ALTERNATE_SYMBOL_PATH%;%SYSTEMROOT%;%SYSTEMROOT%\System32;" + strcpy( lpszSymbolPath, "." ); + + // environment variable _NT_SYMBOL_PATH + if ( GetEnvironmentVariableA( "_NT_SYMBOL_PATH", lpszPath, BUFFERSIZE ) ) + { + strcat( lpszSymbolPath, ";" ); + strcat( lpszSymbolPath, lpszPath ); + } + + // environment variable _NT_ALTERNATE_SYMBOL_PATH + if ( GetEnvironmentVariableA( "_NT_ALTERNATE_SYMBOL_PATH", lpszPath, BUFFERSIZE ) ) + { + strcat( lpszSymbolPath, ";" ); + strcat( lpszSymbolPath, lpszPath ); + } + + // environment variable SYSTEMROOT + if ( GetEnvironmentVariableA( "SYSTEMROOT", lpszPath, BUFFERSIZE ) ) + { + strcat( lpszSymbolPath, ";" ); + strcat( lpszSymbolPath, lpszPath ); + strcat( lpszSymbolPath, ";" ); + + // SYSTEMROOT\System32 + strcat( lpszSymbolPath, lpszPath ); + strcat( lpszSymbolPath, "\\System32" ); + } + + // Add user defined path + if ( lpszIniPath != NULL ) + if ( lpszIniPath[0] != '\0' ) + { + strcat( lpszSymbolPath, ";" ); + strcat( lpszSymbolPath, lpszIniPath ); + } +} + +// Uninitialize the loaded symbol files +BOOL UninitSymInfo() { + return SymCleanup( GetCurrentProcess() ); +} + +// Initializes the symbol files +BOOL InitSymInfo( PCSTR lpszInitialSymbolPath ) +{ + CHAR lpszSymbolPath[BUFFERSIZE]; + DWORD symOptions = SymGetOptions(); + + symOptions |= SYMOPT_LOAD_LINES; + symOptions &= ~SYMOPT_UNDNAME; + SymSetOptions( symOptions ); + InitSymbolPath( lpszSymbolPath, lpszInitialSymbolPath ); + + return SymInitialize( GetCurrentProcess(), lpszSymbolPath, TRUE); +} + +// Get the module name from a given address +static BOOL GetModuleNameFromAddress( UINT address, LPTSTR lpszModule ) +{ + BOOL ret = FALSE; + IMAGEHLP_MODULE moduleInfo; + + ::ZeroMemory( &moduleInfo, sizeof(moduleInfo) ); + moduleInfo.SizeOfStruct = sizeof(moduleInfo); + + if ( SymGetModuleInfo( GetCurrentProcess(), (DWORD)address, &moduleInfo ) ) + { + // Got it! + PCSTR2LPTSTR( moduleInfo.ModuleName, lpszModule ); + ret = TRUE; + } + else + // Not found :( + _tcscpy( lpszModule, _T("?") ); + + return ret; +} + +// Get function prototype and parameter info from ip address and stack address +static BOOL GetFunctionInfoFromAddresses( ULONG fnAddress, ULONG stackAddress, LPTSTR lpszSymbol ) +{ + BOOL ret = FALSE; + DWORD dwDisp = 0; + DWORD dwSymSize = 10000; + TCHAR lpszUnDSymbol[BUFFERSIZE]=_T("?"); + CHAR lpszNonUnicodeUnDSymbol[BUFFERSIZE]="?"; + LPTSTR lpszParamSep = NULL; + LPTSTR lpszParsed = lpszUnDSymbol; + PIMAGEHLP_SYMBOL pSym = (PIMAGEHLP_SYMBOL)GlobalAlloc( GMEM_FIXED, dwSymSize ); + + ::ZeroMemory( pSym, dwSymSize ); + pSym->SizeOfStruct = dwSymSize; + pSym->MaxNameLength = dwSymSize - sizeof(IMAGEHLP_SYMBOL); + + // Set the default to unknown + _tcscpy( lpszSymbol, _T("?") ); + + // Get symbol info for IP +#ifndef _M_X64 + if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, &dwDisp, pSym ) ) +#else + //makes it compile but hell im not sure if this works... + if ( SymGetSymFromAddr( GetCurrentProcess(), (ULONG)fnAddress, (PDWORD64)&dwDisp, pSym ) ) +#endif + { + // Make the symbol readable for humans + UnDecorateSymbolName( pSym->Name, lpszNonUnicodeUnDSymbol, BUFFERSIZE, + UNDNAME_COMPLETE | + UNDNAME_NO_THISTYPE | + UNDNAME_NO_SPECIAL_SYMS | + UNDNAME_NO_MEMBER_TYPE | + UNDNAME_NO_MS_KEYWORDS | + UNDNAME_NO_ACCESS_SPECIFIERS ); + + // Symbol information is ANSI string + PCSTR2LPTSTR( lpszNonUnicodeUnDSymbol, lpszUnDSymbol ); + + // I am just smarter than the symbol file :) + if ( _tcscmp(lpszUnDSymbol, _T("_WinMain@16")) == 0 ) + _tcscpy(lpszUnDSymbol, _T("WinMain(HINSTANCE,HINSTANCE,LPCTSTR,int)")); + else + if ( _tcscmp(lpszUnDSymbol, _T("_main")) == 0 ) + _tcscpy(lpszUnDSymbol, _T("main(int,TCHAR * *)")); + else + if ( _tcscmp(lpszUnDSymbol, _T("_mainCRTStartup")) == 0 ) + _tcscpy(lpszUnDSymbol, _T("mainCRTStartup()")); + else + if ( _tcscmp(lpszUnDSymbol, _T("_wmain")) == 0 ) + _tcscpy(lpszUnDSymbol, _T("wmain(int,TCHAR * *,TCHAR * *)")); + else + if ( _tcscmp(lpszUnDSymbol, _T("_wmainCRTStartup")) == 0 ) + _tcscpy(lpszUnDSymbol, _T("wmainCRTStartup()")); + + lpszSymbol[0] = _T('\0'); + + // Let's go through the stack, and modify the function prototype, and insert the actual + // parameter values from the stack + if ( _tcsstr( lpszUnDSymbol, _T("(void)") ) == NULL && _tcsstr( lpszUnDSymbol, _T("()") ) == NULL) + { + ULONG index = 0; + for( ; ; index++ ) + { + lpszParamSep = _tcschr( lpszParsed, _T(',') ); + if ( lpszParamSep == NULL ) + break; + + *lpszParamSep = _T('\0'); + + _tcscat( lpszSymbol, lpszParsed ); + _stprintf( lpszSymbol + _tcslen(lpszSymbol), _T("=0x%08X,"), *((ULONG*)(stackAddress) + 2 + index) ); + + lpszParsed = lpszParamSep + 1; + } + + lpszParamSep = _tcschr( lpszParsed, _T(')') ); + if ( lpszParamSep != NULL ) + { + *lpszParamSep = _T('\0'); + + _tcscat( lpszSymbol, lpszParsed ); + _stprintf( lpszSymbol + _tcslen(lpszSymbol), _T("=0x%08X)"), *((ULONG*)(stackAddress) + 2 + index) ); + + lpszParsed = lpszParamSep + 1; + } + } + + _tcscat( lpszSymbol, lpszParsed ); + + ret = TRUE; + } + GlobalFree( pSym ); + + return ret; +} + +// Get source file name and line number from IP address +// The output format is: "sourcefile(linenumber)" or +// "modulename!address" or +// "address" +static BOOL GetSourceInfoFromAddress( UINT address, LPTSTR lpszSourceInfo ) +{ + BOOL ret = FALSE; + IMAGEHLP_LINE lineInfo; + DWORD dwDisp; + TCHAR lpszFileName[BUFFERSIZE] = _T(""); + TCHAR lpModuleInfo[BUFFERSIZE] = _T(""); + + _tcscpy( lpszSourceInfo, _T("?(?)") ); + + ::ZeroMemory( &lineInfo, sizeof( lineInfo ) ); + lineInfo.SizeOfStruct = sizeof( lineInfo ); + + if ( SymGetLineFromAddr( GetCurrentProcess(), address, &dwDisp, &lineInfo ) ) + { + // Got it. Let's use "sourcefile(linenumber)" format + PCSTR2LPTSTR( lineInfo.FileName, lpszFileName ); + TCHAR fname[_MAX_FNAME]; + TCHAR ext[_MAX_EXT]; + _tsplitpath(lpszFileName, NULL, NULL, fname, ext); + _stprintf( lpszSourceInfo, _T("%s%s(%d)"), fname, ext, lineInfo.LineNumber ); + ret = TRUE; + } + else + { + // There is no source file information. :( + // Let's use the "modulename!address" format + GetModuleNameFromAddress( address, lpModuleInfo ); + + if ( lpModuleInfo[0] == _T('?') || lpModuleInfo[0] == _T('\0')) + // There is no modulename information. :(( + // Let's use the "address" format + _stprintf( lpszSourceInfo, _T("0x%08X"), address ); + else + _stprintf( lpszSourceInfo, _T("%s!0x%08X"), lpModuleInfo, address ); + + ret = FALSE; + } + + return ret; +} + +void StackTrace( HANDLE hThread, LPCTSTR lpszMessage, FILE *file ) +{ + STACKFRAME callStack; + BOOL bResult; + CONTEXT context; + TCHAR symInfo[BUFFERSIZE] = _T("?"); + TCHAR srcInfo[BUFFERSIZE] = _T("?"); + HANDLE hProcess = GetCurrentProcess(); + + // If it's not this thread, let's suspend it, and resume it at the end + if ( hThread != GetCurrentThread() ) + if ( SuspendThread( hThread ) == -1 ) + { + // whaaat ?! + etfprint(file, "Call stack info failed\n"); + return; + } + + ::ZeroMemory( &context, sizeof(context) ); + context.ContextFlags = CONTEXT_FULL; + + if ( !GetThreadContext( hThread, &context ) ) + { + etfprint(file, "Call stack info failed\n"); + return; + } + + ::ZeroMemory( &callStack, sizeof(callStack) ); +#ifndef _M_X64 + callStack.AddrPC.Offset = context.Eip; + callStack.AddrStack.Offset = context.Esp; + callStack.AddrFrame.Offset = context.Ebp; +#else + callStack.AddrPC.Offset = context.Rip; + callStack.AddrStack.Offset = context.Rsp; + callStack.AddrFrame.Offset = context.Rbp; +#endif + callStack.AddrPC.Mode = AddrModeFlat; + callStack.AddrStack.Mode = AddrModeFlat; + callStack.AddrFrame.Mode = AddrModeFlat; + + etfprint(file, "Call stack info: \n"); + etfprint(file, lpszMessage); + + GetFunctionInfoFromAddresses( callStack.AddrPC.Offset, callStack.AddrFrame.Offset, symInfo ); + GetSourceInfoFromAddress( callStack.AddrPC.Offset, srcInfo ); + etfprint(file, string(" ") + srcInfo + string(" : ") + symInfo + string("\n")); + + for( ULONG index = 0; ; index++ ) + { + bResult = StackWalk( + IMAGE_FILE_MACHINE_I386, + hProcess, + hThread, + &callStack, + NULL, + NULL, + SymFunctionTableAccess, + SymGetModuleBase, + NULL); + + if ( index == 0 ) + continue; + + if( !bResult || callStack.AddrFrame.Offset == 0 ) + break; + + GetFunctionInfoFromAddresses( callStack.AddrPC.Offset, callStack.AddrFrame.Offset, symInfo ); + GetSourceInfoFromAddress( callStack.AddrPC.Offset, srcInfo ); + etfprint(file, string(" ") + srcInfo + string(" : ") + symInfo + string("\n")); + + } + + if ( hThread != GetCurrentThread() ) + ResumeThread( hThread ); +} + +void StackTrace( HANDLE hThread, LPCTSTR lpszMessage, FILE *file, DWORD eip, DWORD esp, DWORD ebp ) +{ + STACKFRAME callStack; + BOOL bResult; + TCHAR symInfo[BUFFERSIZE] = _T("?"); + TCHAR srcInfo[BUFFERSIZE] = _T("?"); + HANDLE hProcess = GetCurrentProcess(); + + // If it's not this thread, let's suspend it, and resume it at the end + if ( hThread != GetCurrentThread() ) + if ( SuspendThread( hThread ) == -1 ) + { + // whaaat ?! + etfprint(file, "Call stack info failed\n"); + return; + } + + ::ZeroMemory( &callStack, sizeof(callStack) ); + callStack.AddrPC.Offset = eip; + callStack.AddrStack.Offset = esp; + callStack.AddrFrame.Offset = ebp; + callStack.AddrPC.Mode = AddrModeFlat; + callStack.AddrStack.Mode = AddrModeFlat; + callStack.AddrFrame.Mode = AddrModeFlat; + + etfprint(file, "Call stack info: \n"); + etfprint(file, lpszMessage); + + GetFunctionInfoFromAddresses( callStack.AddrPC.Offset, callStack.AddrFrame.Offset, symInfo ); + GetSourceInfoFromAddress( callStack.AddrPC.Offset, srcInfo ); + etfprint(file, string(" ") + srcInfo + string(" : ") + symInfo + string("\n")); + + for( ULONG index = 0; ; index++ ) + { + bResult = StackWalk( + IMAGE_FILE_MACHINE_I386, + hProcess, + hThread, + &callStack, + NULL, + NULL, + SymFunctionTableAccess, + SymGetModuleBase, + NULL); + + if ( index == 0 ) + continue; + + if( !bResult || callStack.AddrFrame.Offset == 0 ) + break; + + GetFunctionInfoFromAddresses( callStack.AddrPC.Offset, callStack.AddrFrame.Offset, symInfo ); + GetSourceInfoFromAddress( callStack.AddrPC.Offset, srcInfo ); + etfprint(file, string(" ") + srcInfo + string(" : ") + symInfo + string("\n")); + + } + + if ( hThread != GetCurrentThread() ) + ResumeThread( hThread ); +} + +char g_uefbuf[2048]; + +void etfprintf(FILE *file, const char *format, ...) { + va_list ap; + va_start(ap, format); + int len = vsprintf(g_uefbuf, format, ap); + fwrite(g_uefbuf, 1, len, file); + va_end(ap); +} + +void etfprint(FILE *file, const std::string &text) { + size_t len = text.length(); + fwrite(text.data(), 1, len, file); +} + +#endif //WIN32 diff --git a/Source/Core/Common/Src/FileSearch.cpp b/Source/Core/Common/Src/FileSearch.cpp index 95df8a0ba8..02f9cd7f58 100644 --- a/Source/Core/Common/Src/FileSearch.cpp +++ b/Source/Core/Common/Src/FileSearch.cpp @@ -1,119 +1,119 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#ifndef _WIN32 -#include -#include -#else -#include -#endif - -#include - -#include "FileSearch.h" - -#include "StringUtil.h" - - -CFileSearch::CFileSearch(const CFileSearch::XStringVector& _rSearchStrings, const CFileSearch::XStringVector& _rDirectories) -{ - // Reverse the loop order for speed? - for (size_t j = 0; j < _rSearchStrings.size(); j++) - { - for (size_t i = 0; i < _rDirectories.size(); i++) - { - FindFiles(_rSearchStrings[j], _rDirectories[i]); - } - } -} - - -void CFileSearch::FindFiles(const std::string& _searchString, const std::string& _strPath) -{ - std::string GCMSearchPath; - BuildCompleteFilename(GCMSearchPath, _strPath, _searchString); -#ifdef _WIN32 - WIN32_FIND_DATA findData; - HANDLE FindFirst = FindFirstFile(GCMSearchPath.c_str(), &findData); - - if (FindFirst != INVALID_HANDLE_VALUE) - { - bool bkeepLooping = true; - - while (bkeepLooping) - { - if (findData.cFileName[0] != '.') - { - std::string strFilename; - BuildCompleteFilename(strFilename, _strPath, findData.cFileName); - m_FileNames.push_back(strFilename); - } - - bkeepLooping = FindNextFile(FindFirst, &findData) ? true : false; - } - } - FindClose(FindFirst); - - -#else - size_t dot_pos = _searchString.rfind("."); - - if (dot_pos == std::string::npos) - { - return; - } - - std::string ext = _searchString.substr(dot_pos); - DIR* dir = opendir(_strPath.c_str()); - - if (!dir) - { - return; - } - - dirent* dp; - - while (true) - { - dp = readdir(dir); - - if (!dp) - { - break; - } - - std::string s(dp->d_name); - - if ( (s.size() > ext.size()) && (!strcasecmp(s.substr(s.size() - ext.size()).c_str(), ext.c_str())) ) - { - std::string full_name = _strPath + "/" + s; - m_FileNames.push_back(full_name); - } - } - - closedir(dir); -#endif -} - - -const CFileSearch::XStringVector& CFileSearch::GetFileNames() const -{ - return(m_FileNames); -} - - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#ifndef _WIN32 +#include +#include +#else +#include +#endif + +#include + +#include "FileSearch.h" + +#include "StringUtil.h" + + +CFileSearch::CFileSearch(const CFileSearch::XStringVector& _rSearchStrings, const CFileSearch::XStringVector& _rDirectories) +{ + // Reverse the loop order for speed? + for (size_t j = 0; j < _rSearchStrings.size(); j++) + { + for (size_t i = 0; i < _rDirectories.size(); i++) + { + FindFiles(_rSearchStrings[j], _rDirectories[i]); + } + } +} + + +void CFileSearch::FindFiles(const std::string& _searchString, const std::string& _strPath) +{ + std::string GCMSearchPath; + BuildCompleteFilename(GCMSearchPath, _strPath, _searchString); +#ifdef _WIN32 + WIN32_FIND_DATA findData; + HANDLE FindFirst = FindFirstFile(GCMSearchPath.c_str(), &findData); + + if (FindFirst != INVALID_HANDLE_VALUE) + { + bool bkeepLooping = true; + + while (bkeepLooping) + { + if (findData.cFileName[0] != '.') + { + std::string strFilename; + BuildCompleteFilename(strFilename, _strPath, findData.cFileName); + m_FileNames.push_back(strFilename); + } + + bkeepLooping = FindNextFile(FindFirst, &findData) ? true : false; + } + } + FindClose(FindFirst); + + +#else + size_t dot_pos = _searchString.rfind("."); + + if (dot_pos == std::string::npos) + { + return; + } + + std::string ext = _searchString.substr(dot_pos); + DIR* dir = opendir(_strPath.c_str()); + + if (!dir) + { + return; + } + + dirent* dp; + + while (true) + { + dp = readdir(dir); + + if (!dp) + { + break; + } + + std::string s(dp->d_name); + + if ( (s.size() > ext.size()) && (!strcasecmp(s.substr(s.size() - ext.size()).c_str(), ext.c_str())) ) + { + std::string full_name = _strPath + "/" + s; + m_FileNames.push_back(full_name); + } + } + + closedir(dir); +#endif +} + + +const CFileSearch::XStringVector& CFileSearch::GetFileNames() const +{ + return(m_FileNames); +} + + diff --git a/Source/Core/Common/Src/FileUtil.cpp b/Source/Core/Common/Src/FileUtil.cpp index a6f7533942..99888a99ab 100644 --- a/Source/Core/Common/Src/FileUtil.cpp +++ b/Source/Core/Common/Src/FileUtil.cpp @@ -1,496 +1,496 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "FileUtil.h" - -#ifdef _WIN32 -#include -#include // for SHGetFolderPath -#include -#include // for GetSaveFileName -#include -#include // getcwd -#else -#include -#include -#include -#include -#endif - -#include -#include - -#ifndef S_ISDIR -#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) -#endif - -namespace File -{ - - -// ======================================================= -// Remove any ending forward slashes from directory paths -// ------------- -inline void StripTailDirSlashes(std::string& fname) -{ - // Make sure it's not a blank string - if(fname.length() > 0) - { - while(fname.at(fname.length() - 1) == DIR_SEP_CHR) - fname.resize(fname.length() - 1); - } -} -// ============= - - -bool Exists(const char *filename) -{ - struct stat file_info; - - std::string copy = filename; - StripTailDirSlashes(copy); - - int result = stat(copy.c_str(), &file_info); - return (result == 0); -} - -bool IsDirectory(const char *filename) -{ - struct stat file_info; - std::string copy = filename; - StripTailDirSlashes(copy); - - int result = stat(copy.c_str(), &file_info); - if (result == 0) - return S_ISDIR(file_info.st_mode); - else - return false; -} - -bool Delete(const char *filename) -{ - if (!Exists(filename)) - return false; - if (IsDirectory(filename)) - return false; -#ifdef _WIN32 - DeleteFile(filename); -#else - unlink(filename); -#endif - return true; -} - -std::string SanitizePath(const char *filename) -{ - - std::string copy = filename; -#ifdef _WIN32 - for (size_t i = 0; i < copy.size(); i++) - if (copy[i] == '/') - copy[i] = '\\'; -#else - // Should we do the otherway around? -#endif - return copy; -} - -void Launch(const char *filename) -{ -#ifdef _WIN32 - std::string win_filename = SanitizePath(filename); - SHELLEXECUTEINFO shex = { sizeof(shex) }; - shex.fMask = SEE_MASK_NO_CONSOLE; // | SEE_MASK_ASYNC_OK; - shex.lpVerb = "open"; - shex.lpFile = win_filename.c_str(); - shex.nShow = SW_SHOWNORMAL; - ShellExecuteEx(&shex); -#else - // TODO: Insert GNOME/KDE code here. -#endif -} - -void Explore(const char *path) -{ -#ifdef _WIN32 - std::string win_path = SanitizePath(path); - SHELLEXECUTEINFO shex = { sizeof(shex) }; - shex.fMask = SEE_MASK_NO_CONSOLE; // | SEE_MASK_ASYNC_OK; - shex.lpVerb = "explore"; - shex.lpFile = win_path.c_str(); - shex.nShow = SW_SHOWNORMAL; - ShellExecuteEx(&shex); -#else - // TODO: Insert GNOME/KDE code here. -#endif -} - -// Returns true if successful, or path already exists. -bool CreateDir(const char *path) -{ -#ifdef _WIN32 - if (::CreateDirectory(path, NULL)) - return true; - DWORD error = GetLastError(); - if (error == ERROR_ALREADY_EXISTS) - { - PanicAlert("%s already exists", path); - return true; - } - PanicAlert("Error creating directory %s: %i", path, error); - return false; -#else - if (mkdir(path, 0755) == 0) - return true; - - int err = errno; - - if (err == EEXIST) - { - PanicAlert("%s already exists", path); - return true; - } - - PanicAlert("Error creating directory %s: %s", path, strerror(err)); - return false; - -#endif -} - -// Create several dirs -bool CreateDirectoryStructure(const std::string& _rFullPath) -{ - int PanicCounter = 10; - - size_t Position = 0; - while(true) - { - // Find next sub path, support both \ and / directory separators - { - size_t nextPosition = _rFullPath.find(DIR_SEP_CHR, Position); - Position = nextPosition; - - if (Position == std::string::npos) - return true; - - Position++; - } - - // Create next sub path - std::string SubPath = _rFullPath.substr(0, Position); - if (!SubPath.empty()) - { - if (!File::IsDirectory(SubPath.c_str())) - { - File::CreateDir(SubPath.c_str()); - LOG(WII_IPC_FILEIO, " CreateSubDir %s", SubPath.c_str()); - } - } - - // A safety check - PanicCounter--; - if (PanicCounter <= 0) - { - PanicAlert("CreateDirectoryStruct creates way to much dirs..."); - return false; - } - } -} - - -bool DeleteDir(const char *filename) -{ - - if (!File::IsDirectory(filename)) - return false; -#ifdef _WIN32 - return ::RemoveDirectory (filename) ? true : false; -#else - if (rmdir(filename) == 0) - return true; - - int err = errno; - - PanicAlert("Error removing directory %s",strerror(err)); - return false; -#endif -} - -bool Rename(const char *srcFilename, const char *destFilename) -{ - return (rename(srcFilename, destFilename) == 0); -} - -bool Copy(const char *srcFilename, const char *destFilename) -{ -#ifdef _WIN32 - return (CopyFile(srcFilename, destFilename, FALSE) == TRUE) ? true : false; -#else - -#define BSIZE 1024 - - int rnum, wnum, err; - char buffer[BSIZE]; - FILE *output, *input; - - if (! (input = fopen(srcFilename, "r"))) { - err = errno; - PanicAlert("Error copying from %s: %s", srcFilename, strerror(err)); - return false; - } - - if (! (output = fopen(destFilename, "w"))) { - err = errno; - PanicAlert("Error copying to %s: %s", destFilename, strerror(err)); - return false; - } - - while(! feof(input)) { - if((rnum = fread(buffer, sizeof(char), BSIZE, input)) != BSIZE) { - if(ferror(input) != 0){ - PanicAlert("can't read source file\n"); - return false; - } - } - - if((wnum = fwrite(buffer, sizeof(char), rnum, output))!= rnum){ - PanicAlert("can't write output file\n"); - return false; - } - } - - fclose(input); - fclose(output); - - return true; - /* - std::ifstream ifs(srcFilename, std::ios::binary); - std::ofstream ofs(destFilename, std::ios::binary); - - ofs << ifs.rdbuf(); - - ifs.close(); - ofs.close(); - - return true;*/ -#endif -} - -std::string GetUserDirectory() -{ -#ifdef _WIN32 - char path[MAX_PATH]; - if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, 0, path))) - { - return std::string(path); - } - return std::string(""); -#else - char *dir = getenv("HOME"); - if (!dir) - return std::string(""); - return dir; -#endif -} - -u64 GetSize(const char *filename) -{ - if(!Exists(filename)) - return 0; - - struct stat buf; - if (stat(filename, &buf) == 0) { - return buf.st_size; - } - int err = errno; - - PanicAlert("Error accessing %s: %s", filename, strerror(err)); - - return 0; -} - -#ifdef _WIN32 -static bool ReadFoundFile(const WIN32_FIND_DATA& ffd, FSTEntry& entry) -{ - // ignore files starting with a . - if(strncmp(ffd.cFileName, ".", 1) == 0) - return false; - - entry.virtualName = ffd.cFileName; - - if(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - entry.isDirectory = true; - } - else - { - entry.isDirectory = false; - entry.size = ffd.nFileSizeLow; - } - - return true; -} - -u32 ScanDirectoryTree(const std::string& _Directory, FSTEntry& parentEntry) -{ - // Find the first file in the directory. - WIN32_FIND_DATA ffd; - std::string searchName = _Directory + "\\*"; - HANDLE hFind = FindFirstFile(searchName.c_str(), &ffd); - - u32 foundEntries = 0; - - if (hFind != INVALID_HANDLE_VALUE) - { - do - { - FSTEntry entry; - - if(ReadFoundFile(ffd, entry)) - { - entry.physicalName = _Directory + "\\" + entry.virtualName; - if(entry.isDirectory) - { - u32 childEntries = ScanDirectoryTree(entry.physicalName, entry); - entry.size = childEntries; - foundEntries += childEntries; - } - - ++foundEntries; - - parentEntry.children.push_back(entry); - } - } while (FindNextFile(hFind, &ffd) != 0); - } - - FindClose(hFind); - - return foundEntries; -} -#else -u32 ScanDirectoryTree(const std::string& _Directory, FSTEntry& parentEntry) -{ - PanicAlert("Scan directory not implemanted yet\n"); - // TODO - Insert linux stuff here - return 0; -} -#endif - -bool CreateEmptyFile(const char *filename) -{ - FILE* pFile = fopen(filename, "wb"); - if (pFile == NULL) - return false; - - fclose(pFile); - return true; -} - - -bool DeleteDirRecursively(const std::string& _Directory) -{ -#ifdef _WIN32 - - bool Result = false; - - WIN32_FIND_DATA ffd; - std::string searchName = _Directory + "\\*"; - HANDLE hFind = FindFirstFile(searchName.c_str(), &ffd); - - if (hFind != INVALID_HANDLE_VALUE) - { - do - { - // check for "." and ".." - if (((ffd.cFileName[0] == '.') && (ffd.cFileName[1] == 0x00)) || - ((ffd.cFileName[0] == '.') && (ffd.cFileName[1] == '.') && (ffd.cFileName[2] == 0x00))) - continue; - - // build path - std::string newPath(_Directory); - newPath += '\\'; - newPath += ffd.cFileName; - - if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) - { - if (!File::DeleteDirRecursively(newPath)) - goto error_jmp; - } - else - { - if (!File::Delete(newPath.c_str())) - goto error_jmp; - } - - } while (FindNextFile(hFind, &ffd) != 0); - } - - if (!File::DeleteDir(_Directory.c_str())) - goto error_jmp; - - Result = true; - -error_jmp: - - FindClose(hFind); - - return Result; -#else - // taken from http://www.dreamincode.net/code/snippet2700.htm - DIR *pdir = NULL; - pdir = opendir (_Directory.c_str()); - struct dirent *pent = NULL; - - if (pdir == NULL) { - return false; - } - - char file[256]; - - int counter = 1; - - while ((pent = readdir(pdir))) { - if (counter > 2) { - for (int i = 0; i < 256; i++) file[i] = '\0'; - strcat(file, _Directory.c_str()); - if (pent == NULL) { - return false; - } - strcat(file, pent->d_name); - if (IsDirectory(file) == true) { - DeleteDir(file); - } else { - remove(file); - } - } - counter++; - } - - - return DeleteDir(_Directory.c_str()); -#endif -} - -void GetCurrentDirectory(std::string& _rDirectory) -{ - char tmpBuffer[MAX_PATH+1]; - getcwd(tmpBuffer, MAX_PATH); - _rDirectory = tmpBuffer; -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "FileUtil.h" + +#ifdef _WIN32 +#include +#include // for SHGetFolderPath +#include +#include // for GetSaveFileName +#include +#include // getcwd +#else +#include +#include +#include +#include +#endif + +#include +#include + +#ifndef S_ISDIR +#define S_ISDIR(m) (((m)&S_IFMT) == S_IFDIR) +#endif + +namespace File +{ + + +// ======================================================= +// Remove any ending forward slashes from directory paths +// ------------- +inline void StripTailDirSlashes(std::string& fname) +{ + // Make sure it's not a blank string + if(fname.length() > 0) + { + while(fname.at(fname.length() - 1) == DIR_SEP_CHR) + fname.resize(fname.length() - 1); + } +} +// ============= + + +bool Exists(const char *filename) +{ + struct stat file_info; + + std::string copy = filename; + StripTailDirSlashes(copy); + + int result = stat(copy.c_str(), &file_info); + return (result == 0); +} + +bool IsDirectory(const char *filename) +{ + struct stat file_info; + std::string copy = filename; + StripTailDirSlashes(copy); + + int result = stat(copy.c_str(), &file_info); + if (result == 0) + return S_ISDIR(file_info.st_mode); + else + return false; +} + +bool Delete(const char *filename) +{ + if (!Exists(filename)) + return false; + if (IsDirectory(filename)) + return false; +#ifdef _WIN32 + DeleteFile(filename); +#else + unlink(filename); +#endif + return true; +} + +std::string SanitizePath(const char *filename) +{ + + std::string copy = filename; +#ifdef _WIN32 + for (size_t i = 0; i < copy.size(); i++) + if (copy[i] == '/') + copy[i] = '\\'; +#else + // Should we do the otherway around? +#endif + return copy; +} + +void Launch(const char *filename) +{ +#ifdef _WIN32 + std::string win_filename = SanitizePath(filename); + SHELLEXECUTEINFO shex = { sizeof(shex) }; + shex.fMask = SEE_MASK_NO_CONSOLE; // | SEE_MASK_ASYNC_OK; + shex.lpVerb = "open"; + shex.lpFile = win_filename.c_str(); + shex.nShow = SW_SHOWNORMAL; + ShellExecuteEx(&shex); +#else + // TODO: Insert GNOME/KDE code here. +#endif +} + +void Explore(const char *path) +{ +#ifdef _WIN32 + std::string win_path = SanitizePath(path); + SHELLEXECUTEINFO shex = { sizeof(shex) }; + shex.fMask = SEE_MASK_NO_CONSOLE; // | SEE_MASK_ASYNC_OK; + shex.lpVerb = "explore"; + shex.lpFile = win_path.c_str(); + shex.nShow = SW_SHOWNORMAL; + ShellExecuteEx(&shex); +#else + // TODO: Insert GNOME/KDE code here. +#endif +} + +// Returns true if successful, or path already exists. +bool CreateDir(const char *path) +{ +#ifdef _WIN32 + if (::CreateDirectory(path, NULL)) + return true; + DWORD error = GetLastError(); + if (error == ERROR_ALREADY_EXISTS) + { + PanicAlert("%s already exists", path); + return true; + } + PanicAlert("Error creating directory %s: %i", path, error); + return false; +#else + if (mkdir(path, 0755) == 0) + return true; + + int err = errno; + + if (err == EEXIST) + { + PanicAlert("%s already exists", path); + return true; + } + + PanicAlert("Error creating directory %s: %s", path, strerror(err)); + return false; + +#endif +} + +// Create several dirs +bool CreateDirectoryStructure(const std::string& _rFullPath) +{ + int PanicCounter = 10; + + size_t Position = 0; + while(true) + { + // Find next sub path, support both \ and / directory separators + { + size_t nextPosition = _rFullPath.find(DIR_SEP_CHR, Position); + Position = nextPosition; + + if (Position == std::string::npos) + return true; + + Position++; + } + + // Create next sub path + std::string SubPath = _rFullPath.substr(0, Position); + if (!SubPath.empty()) + { + if (!File::IsDirectory(SubPath.c_str())) + { + File::CreateDir(SubPath.c_str()); + LOG(WII_IPC_FILEIO, " CreateSubDir %s", SubPath.c_str()); + } + } + + // A safety check + PanicCounter--; + if (PanicCounter <= 0) + { + PanicAlert("CreateDirectoryStruct creates way to much dirs..."); + return false; + } + } +} + + +bool DeleteDir(const char *filename) +{ + + if (!File::IsDirectory(filename)) + return false; +#ifdef _WIN32 + return ::RemoveDirectory (filename) ? true : false; +#else + if (rmdir(filename) == 0) + return true; + + int err = errno; + + PanicAlert("Error removing directory %s",strerror(err)); + return false; +#endif +} + +bool Rename(const char *srcFilename, const char *destFilename) +{ + return (rename(srcFilename, destFilename) == 0); +} + +bool Copy(const char *srcFilename, const char *destFilename) +{ +#ifdef _WIN32 + return (CopyFile(srcFilename, destFilename, FALSE) == TRUE) ? true : false; +#else + +#define BSIZE 1024 + + int rnum, wnum, err; + char buffer[BSIZE]; + FILE *output, *input; + + if (! (input = fopen(srcFilename, "r"))) { + err = errno; + PanicAlert("Error copying from %s: %s", srcFilename, strerror(err)); + return false; + } + + if (! (output = fopen(destFilename, "w"))) { + err = errno; + PanicAlert("Error copying to %s: %s", destFilename, strerror(err)); + return false; + } + + while(! feof(input)) { + if((rnum = fread(buffer, sizeof(char), BSIZE, input)) != BSIZE) { + if(ferror(input) != 0){ + PanicAlert("can't read source file\n"); + return false; + } + } + + if((wnum = fwrite(buffer, sizeof(char), rnum, output))!= rnum){ + PanicAlert("can't write output file\n"); + return false; + } + } + + fclose(input); + fclose(output); + + return true; + /* + std::ifstream ifs(srcFilename, std::ios::binary); + std::ofstream ofs(destFilename, std::ios::binary); + + ofs << ifs.rdbuf(); + + ifs.close(); + ofs.close(); + + return true;*/ +#endif +} + +std::string GetUserDirectory() +{ +#ifdef _WIN32 + char path[MAX_PATH]; + if (SUCCEEDED(SHGetFolderPath(NULL, CSIDL_COMMON_APPDATA, NULL, 0, path))) + { + return std::string(path); + } + return std::string(""); +#else + char *dir = getenv("HOME"); + if (!dir) + return std::string(""); + return dir; +#endif +} + +u64 GetSize(const char *filename) +{ + if(!Exists(filename)) + return 0; + + struct stat buf; + if (stat(filename, &buf) == 0) { + return buf.st_size; + } + int err = errno; + + PanicAlert("Error accessing %s: %s", filename, strerror(err)); + + return 0; +} + +#ifdef _WIN32 +static bool ReadFoundFile(const WIN32_FIND_DATA& ffd, FSTEntry& entry) +{ + // ignore files starting with a . + if(strncmp(ffd.cFileName, ".", 1) == 0) + return false; + + entry.virtualName = ffd.cFileName; + + if(ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + entry.isDirectory = true; + } + else + { + entry.isDirectory = false; + entry.size = ffd.nFileSizeLow; + } + + return true; +} + +u32 ScanDirectoryTree(const std::string& _Directory, FSTEntry& parentEntry) +{ + // Find the first file in the directory. + WIN32_FIND_DATA ffd; + std::string searchName = _Directory + "\\*"; + HANDLE hFind = FindFirstFile(searchName.c_str(), &ffd); + + u32 foundEntries = 0; + + if (hFind != INVALID_HANDLE_VALUE) + { + do + { + FSTEntry entry; + + if(ReadFoundFile(ffd, entry)) + { + entry.physicalName = _Directory + "\\" + entry.virtualName; + if(entry.isDirectory) + { + u32 childEntries = ScanDirectoryTree(entry.physicalName, entry); + entry.size = childEntries; + foundEntries += childEntries; + } + + ++foundEntries; + + parentEntry.children.push_back(entry); + } + } while (FindNextFile(hFind, &ffd) != 0); + } + + FindClose(hFind); + + return foundEntries; +} +#else +u32 ScanDirectoryTree(const std::string& _Directory, FSTEntry& parentEntry) +{ + PanicAlert("Scan directory not implemanted yet\n"); + // TODO - Insert linux stuff here + return 0; +} +#endif + +bool CreateEmptyFile(const char *filename) +{ + FILE* pFile = fopen(filename, "wb"); + if (pFile == NULL) + return false; + + fclose(pFile); + return true; +} + + +bool DeleteDirRecursively(const std::string& _Directory) +{ +#ifdef _WIN32 + + bool Result = false; + + WIN32_FIND_DATA ffd; + std::string searchName = _Directory + "\\*"; + HANDLE hFind = FindFirstFile(searchName.c_str(), &ffd); + + if (hFind != INVALID_HANDLE_VALUE) + { + do + { + // check for "." and ".." + if (((ffd.cFileName[0] == '.') && (ffd.cFileName[1] == 0x00)) || + ((ffd.cFileName[0] == '.') && (ffd.cFileName[1] == '.') && (ffd.cFileName[2] == 0x00))) + continue; + + // build path + std::string newPath(_Directory); + newPath += '\\'; + newPath += ffd.cFileName; + + if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) + { + if (!File::DeleteDirRecursively(newPath)) + goto error_jmp; + } + else + { + if (!File::Delete(newPath.c_str())) + goto error_jmp; + } + + } while (FindNextFile(hFind, &ffd) != 0); + } + + if (!File::DeleteDir(_Directory.c_str())) + goto error_jmp; + + Result = true; + +error_jmp: + + FindClose(hFind); + + return Result; +#else + // taken from http://www.dreamincode.net/code/snippet2700.htm + DIR *pdir = NULL; + pdir = opendir (_Directory.c_str()); + struct dirent *pent = NULL; + + if (pdir == NULL) { + return false; + } + + char file[256]; + + int counter = 1; + + while ((pent = readdir(pdir))) { + if (counter > 2) { + for (int i = 0; i < 256; i++) file[i] = '\0'; + strcat(file, _Directory.c_str()); + if (pent == NULL) { + return false; + } + strcat(file, pent->d_name); + if (IsDirectory(file) == true) { + DeleteDir(file); + } else { + remove(file); + } + } + counter++; + } + + + return DeleteDir(_Directory.c_str()); +#endif +} + +void GetCurrentDirectory(std::string& _rDirectory) +{ + char tmpBuffer[MAX_PATH+1]; + getcwd(tmpBuffer, MAX_PATH); + _rDirectory = tmpBuffer; +} + +} // namespace diff --git a/Source/Core/Common/Src/Hash.cpp b/Source/Core/Common/Src/Hash.cpp index b892c9e04e..3d28c57832 100644 --- a/Source/Core/Common/Src/Hash.cpp +++ b/Source/Core/Common/Src/Hash.cpp @@ -1,136 +1,136 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Hash.h" - -// uint32_t -// WARNING - may read one more byte! -// Implementation from Wikipedia. -u32 HashFletcher(const u8* data_u8, size_t length) -{ - const u16* data = (const u16*)data_u8; /* Pointer to the data to be summed */ - size_t len = (length + 1) / 2; /* Length in 16-bit words */ - u32 sum1 = 0xffff, sum2 = 0xffff; - - while (len) - { - size_t tlen = len > 360 ? 360 : len; - len -= tlen; - - do { - sum1 += *data++; - sum2 += sum1; - } - while (--tlen); - - sum1 = (sum1 & 0xffff) + (sum1 >> 16); - sum2 = (sum2 & 0xffff) + (sum2 >> 16); - } - - /* Second reduction step to reduce sums to 16 bits */ - sum1 = (sum1 & 0xffff) + (sum1 >> 16); - sum2 = (sum2 & 0xffff) + (sum2 >> 16); - return(sum2 << 16 | sum1); -} - - -// Implementation from Wikipedia -// Slightly slower than Fletcher above, but slighly more reliable. -#define MOD_ADLER 65521 -// data: Pointer to the data to be summed; len is in bytes -u32 HashAdler32(const u8* data, size_t len) -{ - u32 a = 1, b = 0; - - while (len) - { - size_t tlen = len > 5550 ? 5550 : len; - len -= tlen; - - do - { - a += *data++; - b += a; - } - while (--tlen); - - a = (a & 0xffff) + (a >> 16) * (65536 - MOD_ADLER); - b = (b & 0xffff) + (b >> 16) * (65536 - MOD_ADLER); - } - - // It can be shown that a <= 0x1013a here, so a single subtract will do. - if (a >= MOD_ADLER) - { - a -= MOD_ADLER; - } - - // It can be shown that b can reach 0xfff87 here. - b = (b & 0xffff) + (b >> 16) * (65536 - MOD_ADLER); - - if (b >= MOD_ADLER) - { - b -= MOD_ADLER; - } - - return((b << 16) | a); -} - - -// Another fast and decent hash -u32 HashFNV(const u8* ptr, int length) -{ - u32 hash = 0x811c9dc5; - - for (int i = 0; i < length; i++) - { - hash *= 1677761; - hash ^= ptr[i]; - } - - return(hash); -} - - -// Another fast and decent hash -u32 HashFNV1(const u8* ptr, int length) -{ - u32 hash = 0x811c9dc5; - - for (int i = 0; i < length; i++) - { - hash *= 1677761; - hash ^= ptr[i]; - } - - return(hash); -} - - -// Stupid hash - but can't go back now :) -// Don't use for new things. At least it's reasonably fast. -u32 HashEctor(const u8* ptr, int length) -{ - u32 crc = 0; - - for (int i = 0; i < length; i++) - { - crc ^= ptr[i]; - crc = (crc << 3) | (crc >> 29); - } - - return(crc); -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Hash.h" + +// uint32_t +// WARNING - may read one more byte! +// Implementation from Wikipedia. +u32 HashFletcher(const u8* data_u8, size_t length) +{ + const u16* data = (const u16*)data_u8; /* Pointer to the data to be summed */ + size_t len = (length + 1) / 2; /* Length in 16-bit words */ + u32 sum1 = 0xffff, sum2 = 0xffff; + + while (len) + { + size_t tlen = len > 360 ? 360 : len; + len -= tlen; + + do { + sum1 += *data++; + sum2 += sum1; + } + while (--tlen); + + sum1 = (sum1 & 0xffff) + (sum1 >> 16); + sum2 = (sum2 & 0xffff) + (sum2 >> 16); + } + + /* Second reduction step to reduce sums to 16 bits */ + sum1 = (sum1 & 0xffff) + (sum1 >> 16); + sum2 = (sum2 & 0xffff) + (sum2 >> 16); + return(sum2 << 16 | sum1); +} + + +// Implementation from Wikipedia +// Slightly slower than Fletcher above, but slighly more reliable. +#define MOD_ADLER 65521 +// data: Pointer to the data to be summed; len is in bytes +u32 HashAdler32(const u8* data, size_t len) +{ + u32 a = 1, b = 0; + + while (len) + { + size_t tlen = len > 5550 ? 5550 : len; + len -= tlen; + + do + { + a += *data++; + b += a; + } + while (--tlen); + + a = (a & 0xffff) + (a >> 16) * (65536 - MOD_ADLER); + b = (b & 0xffff) + (b >> 16) * (65536 - MOD_ADLER); + } + + // It can be shown that a <= 0x1013a here, so a single subtract will do. + if (a >= MOD_ADLER) + { + a -= MOD_ADLER; + } + + // It can be shown that b can reach 0xfff87 here. + b = (b & 0xffff) + (b >> 16) * (65536 - MOD_ADLER); + + if (b >= MOD_ADLER) + { + b -= MOD_ADLER; + } + + return((b << 16) | a); +} + + +// Another fast and decent hash +u32 HashFNV(const u8* ptr, int length) +{ + u32 hash = 0x811c9dc5; + + for (int i = 0; i < length; i++) + { + hash *= 1677761; + hash ^= ptr[i]; + } + + return(hash); +} + + +// Another fast and decent hash +u32 HashFNV1(const u8* ptr, int length) +{ + u32 hash = 0x811c9dc5; + + for (int i = 0; i < length; i++) + { + hash *= 1677761; + hash ^= ptr[i]; + } + + return(hash); +} + + +// Stupid hash - but can't go back now :) +// Don't use for new things. At least it's reasonably fast. +u32 HashEctor(const u8* ptr, int length) +{ + u32 crc = 0; + + for (int i = 0; i < length; i++) + { + crc ^= ptr[i]; + crc = (crc << 3) | (crc >> 29); + } + + return(crc); +} diff --git a/Source/Core/Common/Src/IniFile.cpp b/Source/Core/Common/Src/IniFile.cpp index 27114e6d3c..4c5b1e2ba7 100644 --- a/Source/Core/Common/Src/IniFile.cpp +++ b/Source/Core/Common/Src/IniFile.cpp @@ -1,469 +1,469 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ -// see IniFile.h - -#include -#include - -#include -#include -#include -#include -#include - -#include "StringUtil.h" -#include "IniFile.h" - -IniFile::IniFile() -{} - - -IniFile::~IniFile() -{} - - -Section::Section() - : lines(), name(""), comment("") {} - - -Section::Section(const std::string& _name) - : lines(), name(_name), comment("") {} - - -Section::Section(const Section& other) -{ - name = other.name; - comment = other.comment; - lines = other.lines; -} - -const Section* IniFile::GetSection(const char* sectionName) const -{ - for (std::vector
::const_iterator iter = sections.begin(); iter != sections.end(); ++iter) - if (!strcmp(iter->name.c_str(), sectionName)) - return (&(*iter)); - return 0; -} - -Section* IniFile::GetSection(const char* sectionName) -{ - for (std::vector
::iterator iter = sections.begin(); iter != sections.end(); ++iter) - if (!strcmp(iter->name.c_str(), sectionName)) - return (&(*iter)); - return 0; -} - -Section* IniFile::GetOrCreateSection(const char* sectionName) -{ - Section* section = GetSection(sectionName); - - if (!section) - { - sections.push_back(Section(sectionName)); - section = §ions[sections.size() - 1]; - } - - return(section); -} - - -bool IniFile::DeleteSection(const char* sectionName) -{ - Section* s = GetSection(sectionName); - - if (!s) - { - return false; - } - - for (std::vector
::iterator iter = sections.begin(); iter != sections.end(); ++iter) - { - if (&(*iter) == s) - { - sections.erase(iter); - return true; - } - } - - return false; -} - - -void IniFile::ParseLine(const std::string& line, std::string* keyOut, std::string* valueOut, std::string* commentOut) const -{ - // allow many types of commenting - // These MUST be signed! Do not change to size_t - int firstEquals = (int)line.find("=", 0); - int firstCommentChar = (int)line.find(";", 0); - - if (firstCommentChar < 0){firstCommentChar = (int)line.find("#", firstEquals > 0 ? firstEquals : 0);} - - if (firstCommentChar < 0){firstCommentChar = (int)line.find("//", firstEquals > 0 ? firstEquals : 0);} - - // allow preserval of spacing before comment - if (firstCommentChar > 0) - { - while (line[firstCommentChar - 1] == ' ' || line[firstCommentChar - 1] == 9) // 9 == tab - { - firstCommentChar--; - } - } - - if ((firstEquals >= 0) && ((firstCommentChar < 0) || (firstEquals < firstCommentChar))) - { - // Yes, a valid line! - *keyOut = StripSpaces(line.substr(0, firstEquals)); - - if (commentOut) - { - *commentOut = firstCommentChar > 0 ? line.substr(firstCommentChar) : std::string(""); - } - - if (valueOut) - { - *valueOut = StripQuotes(StripSpaces(line.substr(firstEquals + 1, firstCommentChar - firstEquals - 1))); - } - } -} - - -std::string* IniFile::GetLine(Section* section, const char* key, std::string* valueOut, std::string* commentOut) -{ - for (std::vector::iterator iter = section->lines.begin(); iter != section->lines.end(); ++iter) - { - std::string& line = *iter; - std::string lineKey; - ParseLine(line, &lineKey, valueOut, commentOut); - - if (!stricmp(lineKey.c_str(), key)) - { - return &line; - } - } - - return 0; -} - - -void IniFile::Set(const char* sectionName, const char* key, const char* newValue) -{ - Section* section = GetOrCreateSection(sectionName); - std::string value, comment; - std::string* line = GetLine(section, key, &value, &comment); - - if (line) - { - // Change the value - keep the key and comment - *line = StripSpaces(key) + " = " + newValue + comment; - } - else - { - // The key did not already exist in this section - let's add it. - section->lines.push_back(std::string(key) + " = " + newValue); - } -} - - -void IniFile::Set(const char* sectionName, const char* key, u32 newValue) -{ - Set(sectionName, key, StringFromFormat("0x%08x", newValue).c_str()); -} - - -void IniFile::Set(const char* sectionName, const char* key, int newValue) -{ - Set(sectionName, key, StringFromInt(newValue).c_str()); -} - - -void IniFile::Set(const char* sectionName, const char* key, bool newValue) -{ - Set(sectionName, key, StringFromBool(newValue).c_str()); -} - - -void IniFile::SetLines(const char* sectionName, const std::vector &lines) -{ - Section* section = GetOrCreateSection(sectionName); - section->lines.clear(); - - for (std::vector::const_iterator iter = lines.begin(); iter != lines.end(); ++iter) - { - section->lines.push_back(*iter); - } -} -bool IniFile::Get(const char* sectionName, const char* key, std::string* value, const char* defaultValue) -{ - Section* section = GetSection(sectionName); - - if (!section) - { - if (defaultValue) - { - *value = defaultValue; - } - - return false; - } - - std::string* line = GetLine(section, key, value, 0); - - if (!line) - { - if (defaultValue) - { - *value = defaultValue; - } - - return false; - } - - return true; -} - - -bool IniFile::Get(const char* sectionName, const char* key, int* value, int defaultValue) -{ - std::string temp; - bool retval = Get(sectionName, key, &temp, 0); - - if (retval && TryParseInt(temp.c_str(), value)) - { - return true; - } - - *value = defaultValue; - return false; -} - - -bool IniFile::Get(const char* sectionName, const char* key, u32* value, u32 defaultValue) -{ - std::string temp; - bool retval = Get(sectionName, key, &temp, 0); - - if (retval && TryParseUInt(temp.c_str(), value)) - { - return true; - } - - *value = defaultValue; - return false; -} - - -bool IniFile::Get(const char* sectionName, const char* key, bool* value, bool defaultValue) -{ - std::string temp; - bool retval = Get(sectionName, key, &temp, 0); - - if (retval && TryParseBool(temp.c_str(), value)) - { - return true; - } - - *value = defaultValue; - return false; -} - - -bool IniFile::DeleteKey(const char* sectionName, const char* key) -{ - Section* section = GetSection(sectionName); - - if (!section) - { - return false; - } - - std::string* line = GetLine(section, key, 0, 0); - - for (std::vector::iterator liter = section->lines.begin(); liter != section->lines.end(); ++liter) - { - if (line == &(*liter)) - { - section->lines.erase(liter); - return true; - } - } - - return false; //shouldn't happen -} - - -bool IniFile::Load(const char* filename) -{ - sections.clear(); - sections.push_back(Section("")); - //first section consists of the comments before the first real section - - std::ifstream in; - in.open(filename, std::ios::in); - - if (in.fail()) - { - return false; - } - - while (!in.eof()) - { - char templine[512]; - in.getline(templine, 512); - std::string line = templine; - - if (in.eof()) - { - break; - } - - if (line.size() > 0) - { - if (line[0] == '[') - { - size_t endpos = line.find("]"); - - if (endpos != std::string::npos) - { - // New section! - std::string sub = line.substr(1, endpos - 1); - sections.push_back(Section(sub)); - - if (endpos + 1 < line.size()) - { - sections[sections.size() - 1].comment = line.substr(endpos + 1); - } - } - } - else - { - sections[sections.size() - 1].lines.push_back(line); - } - } - } - - in.close(); - return true; -} - - -bool IniFile::Save(const char* filename) -{ - std::ofstream out; - out.open(filename, std::ios::out); - - if (out.fail()) - { - return false; - } - - for (std::vector
::const_iterator iter = sections.begin(); iter != sections.end(); ++iter) - { - const Section& section = *iter; - - if (section.name != "") - { - out << "[" << section.name << "]" << section.comment << std::endl; - } - - for (std::vector::const_iterator liter = section.lines.begin(); liter != section.lines.end(); ++liter) - { - std::string s = *liter; - out << s << std::endl; - } - } - - out.close(); - return true; -} - - -bool IniFile::GetKeys(const char* sectionName, std::vector& keys) const -{ - const Section* section = GetSection(sectionName); - - if (!section) - { - return false; - } - - keys.clear(); - - for (std::vector::const_iterator liter = section->lines.begin(); liter != section->lines.end(); ++liter) - { - std::string key; - ParseLine(*liter, &key, 0, 0); - keys.push_back(key); - } - - return true; -} - - -bool IniFile::GetLines(const char* sectionName, std::vector& lines) const -{ - const Section* section = GetSection(sectionName); - if (!section) - return false; - - lines.clear(); - for (std::vector::const_iterator iter = section->lines.begin(); iter != section->lines.end(); ++iter) - { - std::string line = StripSpaces(*iter); - int commentPos = (int)line.find('#'); - if (commentPos == 0) - { - continue; - } - - if (commentPos != (int)std::string::npos) - { - line = StripSpaces(line.substr(0, commentPos)); - } - - lines.push_back(line); - } - - return true; -} - - -void IniFile::SortSections() -{ - std::sort(sections.begin(), sections.end()); -} - - -/* - int main() - { - IniFile ini; - ini.Load("my.ini"); - ini.Set("Hej", "A", "amaskdfl"); - ini.Set("Mossa", "A", "amaskdfl"); - ini.Set("Aissa", "A", "amaskdfl"); - //ini.Read("my.ini"); - std::string x; - ini.Get("Hej", "B", &x, "boo"); - ini.DeleteKey("Mossa", "A"); - ini.DeleteSection("Mossa"); - ini.SortSections(); - ini.Save("my.ini"); - //UpdateVars(ini); - return 0; - } - */ - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +// see IniFile.h + +#include +#include + +#include +#include +#include +#include +#include + +#include "StringUtil.h" +#include "IniFile.h" + +IniFile::IniFile() +{} + + +IniFile::~IniFile() +{} + + +Section::Section() + : lines(), name(""), comment("") {} + + +Section::Section(const std::string& _name) + : lines(), name(_name), comment("") {} + + +Section::Section(const Section& other) +{ + name = other.name; + comment = other.comment; + lines = other.lines; +} + +const Section* IniFile::GetSection(const char* sectionName) const +{ + for (std::vector
::const_iterator iter = sections.begin(); iter != sections.end(); ++iter) + if (!strcmp(iter->name.c_str(), sectionName)) + return (&(*iter)); + return 0; +} + +Section* IniFile::GetSection(const char* sectionName) +{ + for (std::vector
::iterator iter = sections.begin(); iter != sections.end(); ++iter) + if (!strcmp(iter->name.c_str(), sectionName)) + return (&(*iter)); + return 0; +} + +Section* IniFile::GetOrCreateSection(const char* sectionName) +{ + Section* section = GetSection(sectionName); + + if (!section) + { + sections.push_back(Section(sectionName)); + section = §ions[sections.size() - 1]; + } + + return(section); +} + + +bool IniFile::DeleteSection(const char* sectionName) +{ + Section* s = GetSection(sectionName); + + if (!s) + { + return false; + } + + for (std::vector
::iterator iter = sections.begin(); iter != sections.end(); ++iter) + { + if (&(*iter) == s) + { + sections.erase(iter); + return true; + } + } + + return false; +} + + +void IniFile::ParseLine(const std::string& line, std::string* keyOut, std::string* valueOut, std::string* commentOut) const +{ + // allow many types of commenting + // These MUST be signed! Do not change to size_t + int firstEquals = (int)line.find("=", 0); + int firstCommentChar = (int)line.find(";", 0); + + if (firstCommentChar < 0){firstCommentChar = (int)line.find("#", firstEquals > 0 ? firstEquals : 0);} + + if (firstCommentChar < 0){firstCommentChar = (int)line.find("//", firstEquals > 0 ? firstEquals : 0);} + + // allow preserval of spacing before comment + if (firstCommentChar > 0) + { + while (line[firstCommentChar - 1] == ' ' || line[firstCommentChar - 1] == 9) // 9 == tab + { + firstCommentChar--; + } + } + + if ((firstEquals >= 0) && ((firstCommentChar < 0) || (firstEquals < firstCommentChar))) + { + // Yes, a valid line! + *keyOut = StripSpaces(line.substr(0, firstEquals)); + + if (commentOut) + { + *commentOut = firstCommentChar > 0 ? line.substr(firstCommentChar) : std::string(""); + } + + if (valueOut) + { + *valueOut = StripQuotes(StripSpaces(line.substr(firstEquals + 1, firstCommentChar - firstEquals - 1))); + } + } +} + + +std::string* IniFile::GetLine(Section* section, const char* key, std::string* valueOut, std::string* commentOut) +{ + for (std::vector::iterator iter = section->lines.begin(); iter != section->lines.end(); ++iter) + { + std::string& line = *iter; + std::string lineKey; + ParseLine(line, &lineKey, valueOut, commentOut); + + if (!stricmp(lineKey.c_str(), key)) + { + return &line; + } + } + + return 0; +} + + +void IniFile::Set(const char* sectionName, const char* key, const char* newValue) +{ + Section* section = GetOrCreateSection(sectionName); + std::string value, comment; + std::string* line = GetLine(section, key, &value, &comment); + + if (line) + { + // Change the value - keep the key and comment + *line = StripSpaces(key) + " = " + newValue + comment; + } + else + { + // The key did not already exist in this section - let's add it. + section->lines.push_back(std::string(key) + " = " + newValue); + } +} + + +void IniFile::Set(const char* sectionName, const char* key, u32 newValue) +{ + Set(sectionName, key, StringFromFormat("0x%08x", newValue).c_str()); +} + + +void IniFile::Set(const char* sectionName, const char* key, int newValue) +{ + Set(sectionName, key, StringFromInt(newValue).c_str()); +} + + +void IniFile::Set(const char* sectionName, const char* key, bool newValue) +{ + Set(sectionName, key, StringFromBool(newValue).c_str()); +} + + +void IniFile::SetLines(const char* sectionName, const std::vector &lines) +{ + Section* section = GetOrCreateSection(sectionName); + section->lines.clear(); + + for (std::vector::const_iterator iter = lines.begin(); iter != lines.end(); ++iter) + { + section->lines.push_back(*iter); + } +} +bool IniFile::Get(const char* sectionName, const char* key, std::string* value, const char* defaultValue) +{ + Section* section = GetSection(sectionName); + + if (!section) + { + if (defaultValue) + { + *value = defaultValue; + } + + return false; + } + + std::string* line = GetLine(section, key, value, 0); + + if (!line) + { + if (defaultValue) + { + *value = defaultValue; + } + + return false; + } + + return true; +} + + +bool IniFile::Get(const char* sectionName, const char* key, int* value, int defaultValue) +{ + std::string temp; + bool retval = Get(sectionName, key, &temp, 0); + + if (retval && TryParseInt(temp.c_str(), value)) + { + return true; + } + + *value = defaultValue; + return false; +} + + +bool IniFile::Get(const char* sectionName, const char* key, u32* value, u32 defaultValue) +{ + std::string temp; + bool retval = Get(sectionName, key, &temp, 0); + + if (retval && TryParseUInt(temp.c_str(), value)) + { + return true; + } + + *value = defaultValue; + return false; +} + + +bool IniFile::Get(const char* sectionName, const char* key, bool* value, bool defaultValue) +{ + std::string temp; + bool retval = Get(sectionName, key, &temp, 0); + + if (retval && TryParseBool(temp.c_str(), value)) + { + return true; + } + + *value = defaultValue; + return false; +} + + +bool IniFile::DeleteKey(const char* sectionName, const char* key) +{ + Section* section = GetSection(sectionName); + + if (!section) + { + return false; + } + + std::string* line = GetLine(section, key, 0, 0); + + for (std::vector::iterator liter = section->lines.begin(); liter != section->lines.end(); ++liter) + { + if (line == &(*liter)) + { + section->lines.erase(liter); + return true; + } + } + + return false; //shouldn't happen +} + + +bool IniFile::Load(const char* filename) +{ + sections.clear(); + sections.push_back(Section("")); + //first section consists of the comments before the first real section + + std::ifstream in; + in.open(filename, std::ios::in); + + if (in.fail()) + { + return false; + } + + while (!in.eof()) + { + char templine[512]; + in.getline(templine, 512); + std::string line = templine; + + if (in.eof()) + { + break; + } + + if (line.size() > 0) + { + if (line[0] == '[') + { + size_t endpos = line.find("]"); + + if (endpos != std::string::npos) + { + // New section! + std::string sub = line.substr(1, endpos - 1); + sections.push_back(Section(sub)); + + if (endpos + 1 < line.size()) + { + sections[sections.size() - 1].comment = line.substr(endpos + 1); + } + } + } + else + { + sections[sections.size() - 1].lines.push_back(line); + } + } + } + + in.close(); + return true; +} + + +bool IniFile::Save(const char* filename) +{ + std::ofstream out; + out.open(filename, std::ios::out); + + if (out.fail()) + { + return false; + } + + for (std::vector
::const_iterator iter = sections.begin(); iter != sections.end(); ++iter) + { + const Section& section = *iter; + + if (section.name != "") + { + out << "[" << section.name << "]" << section.comment << std::endl; + } + + for (std::vector::const_iterator liter = section.lines.begin(); liter != section.lines.end(); ++liter) + { + std::string s = *liter; + out << s << std::endl; + } + } + + out.close(); + return true; +} + + +bool IniFile::GetKeys(const char* sectionName, std::vector& keys) const +{ + const Section* section = GetSection(sectionName); + + if (!section) + { + return false; + } + + keys.clear(); + + for (std::vector::const_iterator liter = section->lines.begin(); liter != section->lines.end(); ++liter) + { + std::string key; + ParseLine(*liter, &key, 0, 0); + keys.push_back(key); + } + + return true; +} + + +bool IniFile::GetLines(const char* sectionName, std::vector& lines) const +{ + const Section* section = GetSection(sectionName); + if (!section) + return false; + + lines.clear(); + for (std::vector::const_iterator iter = section->lines.begin(); iter != section->lines.end(); ++iter) + { + std::string line = StripSpaces(*iter); + int commentPos = (int)line.find('#'); + if (commentPos == 0) + { + continue; + } + + if (commentPos != (int)std::string::npos) + { + line = StripSpaces(line.substr(0, commentPos)); + } + + lines.push_back(line); + } + + return true; +} + + +void IniFile::SortSections() +{ + std::sort(sections.begin(), sections.end()); +} + + +/* + int main() + { + IniFile ini; + ini.Load("my.ini"); + ini.Set("Hej", "A", "amaskdfl"); + ini.Set("Mossa", "A", "amaskdfl"); + ini.Set("Aissa", "A", "amaskdfl"); + //ini.Read("my.ini"); + std::string x; + ini.Get("Hej", "B", &x, "boo"); + ini.DeleteKey("Mossa", "A"); + ini.DeleteSection("Mossa"); + ini.SortSections(); + ini.Save("my.ini"); + //UpdateVars(ini); + return 0; + } + */ + diff --git a/Source/Core/Common/Src/MappedFile.cpp b/Source/Core/Common/Src/MappedFile.cpp index 29b86285c0..fb211a34b2 100644 --- a/Source/Core/Common/Src/MappedFile.cpp +++ b/Source/Core/Common/Src/MappedFile.cpp @@ -1,233 +1,233 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#ifdef _WIN32 -#include -#else -#include -#include -#include -#include -#endif - -#include "Common.h" -#include "MappedFile.h" - -namespace Common -{ -class CMappedFile - : public IMappedFile -{ - public: - - CMappedFile(void); - ~CMappedFile(void); - bool Open(const char* _szFilename); - bool IsOpen(void); - void Close(void); - u64 GetSize(void); - u8* Lock(u64 _offset, u64 _size); - void Unlock(u8* ptr); - - - private: - - u64 size; - - typedef std::mapLockmap; - Lockmap lockMap; -#ifdef _WIN32 - HANDLE hFile; - HANDLE hFileMapping; -#elif POSIX - int fd; - typedef std::mapSizemap; - Sizemap sizeMap; -#endif - - int granularity; -}; - - -CMappedFile::CMappedFile() -{ -#ifdef _WIN32 - hFile = INVALID_HANDLE_VALUE; - SYSTEM_INFO info; - GetSystemInfo(&info); - granularity = (int)info.dwAllocationGranularity; -#elif POSIX - fd = -1; - granularity = getpagesize(); //sysconf(_SC_PAGE_SIZE); -#endif -} - - -CMappedFile::~CMappedFile() -{ - Close(); -} - - -bool CMappedFile::Open(const char* filename) -{ - Close(); -#ifdef _WIN32 - hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); - - if (hFile == INVALID_HANDLE_VALUE) - { - return(false); - } - - hFileMapping = CreateFileMapping(hFile, 0, PAGE_READONLY, 0, 0, NULL); - - if (hFileMapping == NULL) - { - CloseHandle(hFile); - hFile = 0; - return(false); - } - - u32 high = 0; - u32 low = GetFileSize(hFile, (LPDWORD)&high); - size = (u64)low | ((u64)high << 32); -#elif POSIX - fd = open(filename, O_RDONLY); - size = lseek(fd, 0, SEEK_END); - lseek(fd, 0, SEEK_SET); -#endif - - return(true); -} - - -bool CMappedFile::IsOpen() -{ -#ifdef _WIN32 - return(hFile != INVALID_HANDLE_VALUE); - -#elif POSIX - return(fd != -1); -#endif - -} - - -u64 CMappedFile::GetSize() -{ - return(size); -} - - -void CMappedFile::Close() -{ -#ifdef _WIN32 - if (hFile != INVALID_HANDLE_VALUE) - { - CloseHandle(hFileMapping); - CloseHandle(hFile); - lockMap.clear(); - hFile = INVALID_HANDLE_VALUE; - } - -#elif POSIX - if (fd != -1) - { - lockMap.clear(); - sizeMap.clear(); - close(fd); - } - - fd = -1; -#endif -} - - -u8* CMappedFile::Lock(u64 offset, u64 _size) -{ -#ifdef _WIN32 - if (hFile != INVALID_HANDLE_VALUE) -#elif POSIX - if (fd != -1) -#endif - { - u64 realOffset = offset & ~(granularity - 1); - s64 difference = offset - realOffset; - u64 fake_size = (difference + _size + granularity - 1) & ~(granularity - 1); -#ifdef _WIN32 - u8* realPtr = (u8*)MapViewOfFile(hFileMapping, FILE_MAP_READ, (DWORD)(realOffset >> 32), (DWORD)realOffset, (SIZE_T)(_size)); - - if (realPtr == NULL) - { - return(NULL); - } - -#elif POSIX - // TODO - u8* realPtr = (u8*)mmap(0, fake_size, PROT_READ, MAP_PRIVATE, fd, (off_t)realOffset); - - if (!realPtr) - { - PanicAlert("Map Failed"); - exit(0); - } -#endif - - u8* fakePtr = realPtr + difference; - //add to map - lockMap[fakePtr] = realPtr; -#ifndef _WIN32 - sizeMap[fakePtr] = _size + difference; -#endif - return(fakePtr); - } - else - { - return(0); - } -} - - -void CMappedFile::Unlock(u8* ptr) -{ - if (ptr != 0) - { - Lockmap::iterator iter = lockMap.find(ptr); - - if (iter != lockMap.end()) - { -#ifdef _WIN32 - UnmapViewOfFile((*iter).second); -#else - munmap((*iter).second, sizeMap[ptr]); -#endif - lockMap.erase(iter); - } - else - { - PanicAlert("CMappedFile : Unlock failed"); - } - } -} - - -IMappedFile* IMappedFile::CreateMappedFileDEPRECATED(void) -{ - return(new CMappedFile); -} -} // end of namespace Common +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifdef _WIN32 +#include +#else +#include +#include +#include +#include +#endif + +#include "Common.h" +#include "MappedFile.h" + +namespace Common +{ +class CMappedFile + : public IMappedFile +{ + public: + + CMappedFile(void); + ~CMappedFile(void); + bool Open(const char* _szFilename); + bool IsOpen(void); + void Close(void); + u64 GetSize(void); + u8* Lock(u64 _offset, u64 _size); + void Unlock(u8* ptr); + + + private: + + u64 size; + + typedef std::mapLockmap; + Lockmap lockMap; +#ifdef _WIN32 + HANDLE hFile; + HANDLE hFileMapping; +#elif POSIX + int fd; + typedef std::mapSizemap; + Sizemap sizeMap; +#endif + + int granularity; +}; + + +CMappedFile::CMappedFile() +{ +#ifdef _WIN32 + hFile = INVALID_HANDLE_VALUE; + SYSTEM_INFO info; + GetSystemInfo(&info); + granularity = (int)info.dwAllocationGranularity; +#elif POSIX + fd = -1; + granularity = getpagesize(); //sysconf(_SC_PAGE_SIZE); +#endif +} + + +CMappedFile::~CMappedFile() +{ + Close(); +} + + +bool CMappedFile::Open(const char* filename) +{ + Close(); +#ifdef _WIN32 + hFile = CreateFile(filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, FILE_ATTRIBUTE_NORMAL, 0); + + if (hFile == INVALID_HANDLE_VALUE) + { + return(false); + } + + hFileMapping = CreateFileMapping(hFile, 0, PAGE_READONLY, 0, 0, NULL); + + if (hFileMapping == NULL) + { + CloseHandle(hFile); + hFile = 0; + return(false); + } + + u32 high = 0; + u32 low = GetFileSize(hFile, (LPDWORD)&high); + size = (u64)low | ((u64)high << 32); +#elif POSIX + fd = open(filename, O_RDONLY); + size = lseek(fd, 0, SEEK_END); + lseek(fd, 0, SEEK_SET); +#endif + + return(true); +} + + +bool CMappedFile::IsOpen() +{ +#ifdef _WIN32 + return(hFile != INVALID_HANDLE_VALUE); + +#elif POSIX + return(fd != -1); +#endif + +} + + +u64 CMappedFile::GetSize() +{ + return(size); +} + + +void CMappedFile::Close() +{ +#ifdef _WIN32 + if (hFile != INVALID_HANDLE_VALUE) + { + CloseHandle(hFileMapping); + CloseHandle(hFile); + lockMap.clear(); + hFile = INVALID_HANDLE_VALUE; + } + +#elif POSIX + if (fd != -1) + { + lockMap.clear(); + sizeMap.clear(); + close(fd); + } + + fd = -1; +#endif +} + + +u8* CMappedFile::Lock(u64 offset, u64 _size) +{ +#ifdef _WIN32 + if (hFile != INVALID_HANDLE_VALUE) +#elif POSIX + if (fd != -1) +#endif + { + u64 realOffset = offset & ~(granularity - 1); + s64 difference = offset - realOffset; + u64 fake_size = (difference + _size + granularity - 1) & ~(granularity - 1); +#ifdef _WIN32 + u8* realPtr = (u8*)MapViewOfFile(hFileMapping, FILE_MAP_READ, (DWORD)(realOffset >> 32), (DWORD)realOffset, (SIZE_T)(_size)); + + if (realPtr == NULL) + { + return(NULL); + } + +#elif POSIX + // TODO + u8* realPtr = (u8*)mmap(0, fake_size, PROT_READ, MAP_PRIVATE, fd, (off_t)realOffset); + + if (!realPtr) + { + PanicAlert("Map Failed"); + exit(0); + } +#endif + + u8* fakePtr = realPtr + difference; + //add to map + lockMap[fakePtr] = realPtr; +#ifndef _WIN32 + sizeMap[fakePtr] = _size + difference; +#endif + return(fakePtr); + } + else + { + return(0); + } +} + + +void CMappedFile::Unlock(u8* ptr) +{ + if (ptr != 0) + { + Lockmap::iterator iter = lockMap.find(ptr); + + if (iter != lockMap.end()) + { +#ifdef _WIN32 + UnmapViewOfFile((*iter).second); +#else + munmap((*iter).second, sizeMap[ptr]); +#endif + lockMap.erase(iter); + } + else + { + PanicAlert("CMappedFile : Unlock failed"); + } + } +} + + +IMappedFile* IMappedFile::CreateMappedFileDEPRECATED(void) +{ + return(new CMappedFile); +} +} // end of namespace Common diff --git a/Source/Core/Common/Src/MathUtil.cpp b/Source/Core/Common/Src/MathUtil.cpp index 830590ad39..4c2e6a0fc1 100644 --- a/Source/Core/Common/Src/MathUtil.cpp +++ b/Source/Core/Common/Src/MathUtil.cpp @@ -1,44 +1,44 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include - -#include "Common.h" -#include "MathUtil.h" - -static u32 saved_sse_state = _mm_getcsr(); -static const u32 default_sse_state = _mm_getcsr(); - - -void LoadDefaultSSEState() -{ - _mm_setcsr(default_sse_state); -} - - -void LoadSSEState() -{ - _mm_setcsr(saved_sse_state); -} - - -void SaveSSEState() -{ - saved_sse_state = _mm_getcsr(); -} - - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "Common.h" +#include "MathUtil.h" + +static u32 saved_sse_state = _mm_getcsr(); +static const u32 default_sse_state = _mm_getcsr(); + + +void LoadDefaultSSEState() +{ + _mm_setcsr(default_sse_state); +} + + +void LoadSSEState() +{ + _mm_setcsr(saved_sse_state); +} + + +void SaveSSEState() +{ + saved_sse_state = _mm_getcsr(); +} + + diff --git a/Source/Core/Common/Src/MemArena.cpp b/Source/Core/Common/Src/MemArena.cpp index 6232d156c3..531660ceda 100644 --- a/Source/Core/Common/Src/MemArena.cpp +++ b/Source/Core/Common/Src/MemArena.cpp @@ -1,144 +1,144 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "MemArena.h" - -#ifdef _WIN32 -#include -#else -#include -#include -#include -#include -#include -#include -#endif - -#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) -#define MAP_ANONYMOUS MAP_ANON -#endif - - -const char* ram_temp_file = "/tmp/gc_mem.tmp"; - -void MemArena::GrabLowMemSpace(size_t size) -{ -#ifdef _WIN32 - hMemoryMapping = CreateFileMapping(NULL, NULL, PAGE_READWRITE, 0, (DWORD)(size), _T("All GC Memory")); -#else - mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; - fd = open(ram_temp_file, O_RDWR | O_CREAT, mode); - ftruncate(fd, size); - return; -#endif -} - - -void MemArena::ReleaseSpace() -{ -#ifdef _WIN32 - CloseHandle(hMemoryMapping); - hMemoryMapping = 0; -#else - close(fd); - unlink(ram_temp_file); -#endif -} - - -void* MemArena::CreateView(s64 offset, size_t size, bool ensure_low_mem) -{ -#ifdef _WIN32 - return(MapViewOfFile(hMemoryMapping, FILE_MAP_ALL_ACCESS, 0, (DWORD)((u64)offset), size)); - -#else - void* ptr = mmap(0, size, - PROT_READ | PROT_WRITE, - MAP_SHARED -#ifdef __x86_64__ - | (ensure_low_mem ? MAP_32BIT : 0) -#endif - , fd, offset); - - if (!ptr) - { - PanicAlert("Failed to create view"); - } - - return(ptr); -#endif - -} - - -void* MemArena::CreateViewAt(s64 offset, size_t size, void* base) -{ -#ifdef _WIN32 - return(MapViewOfFileEx(hMemoryMapping, FILE_MAP_ALL_ACCESS, 0, (DWORD)((u64)offset), size, base)); - -#else - return(mmap(base, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, offset)); -#endif - -} - - -void MemArena::ReleaseView(void* view, size_t size) -{ -#ifdef _WIN32 - UnmapViewOfFile(view); -#else - munmap(view, size); -#endif -} - - -u8* MemArena::Find4GBBase() -{ -#ifdef _M_X64 -#ifdef _WIN32 - // 64 bit - u8* base = (u8*)VirtualAlloc(0, 0xE1000000, MEM_RESERVE, PAGE_READWRITE); - VirtualFree(base, 0, MEM_RELEASE); - return base; -#else - // Very precarious - mmap cannot return an error when trying to map already used pages. - // This makes the Windows approach above unusable on Linux, so we will simply pray... - return reinterpret_cast(0x2300000000ULL); -#endif - -#else - // 32 bit -#ifdef _WIN32 - // The highest thing in any 1GB section of memory space is the locked cache. We only need to fit it. - u8* base = (u8*)VirtualAlloc(0, 0x31000000, MEM_RESERVE, PAGE_READWRITE); - if (base) { - VirtualFree(base, 0, MEM_RELEASE); - } - return base; -#else - void* base = mmap(0, 0x31000000, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0); - if (base == MAP_FAILED) { - PanicAlert("Failed to map 1 GB of memory space: %s", strerror(errno)); - return 0; - } - munmap(base, 0x31000000); - return static_cast(base); -#endif -#endif -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "MemArena.h" + +#ifdef _WIN32 +#include +#else +#include +#include +#include +#include +#include +#include +#endif + +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +#define MAP_ANONYMOUS MAP_ANON +#endif + + +const char* ram_temp_file = "/tmp/gc_mem.tmp"; + +void MemArena::GrabLowMemSpace(size_t size) +{ +#ifdef _WIN32 + hMemoryMapping = CreateFileMapping(NULL, NULL, PAGE_READWRITE, 0, (DWORD)(size), _T("All GC Memory")); +#else + mode_t mode = S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH; + fd = open(ram_temp_file, O_RDWR | O_CREAT, mode); + ftruncate(fd, size); + return; +#endif +} + + +void MemArena::ReleaseSpace() +{ +#ifdef _WIN32 + CloseHandle(hMemoryMapping); + hMemoryMapping = 0; +#else + close(fd); + unlink(ram_temp_file); +#endif +} + + +void* MemArena::CreateView(s64 offset, size_t size, bool ensure_low_mem) +{ +#ifdef _WIN32 + return(MapViewOfFile(hMemoryMapping, FILE_MAP_ALL_ACCESS, 0, (DWORD)((u64)offset), size)); + +#else + void* ptr = mmap(0, size, + PROT_READ | PROT_WRITE, + MAP_SHARED +#ifdef __x86_64__ + | (ensure_low_mem ? MAP_32BIT : 0) +#endif + , fd, offset); + + if (!ptr) + { + PanicAlert("Failed to create view"); + } + + return(ptr); +#endif + +} + + +void* MemArena::CreateViewAt(s64 offset, size_t size, void* base) +{ +#ifdef _WIN32 + return(MapViewOfFileEx(hMemoryMapping, FILE_MAP_ALL_ACCESS, 0, (DWORD)((u64)offset), size, base)); + +#else + return(mmap(base, size, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_FIXED, fd, offset)); +#endif + +} + + +void MemArena::ReleaseView(void* view, size_t size) +{ +#ifdef _WIN32 + UnmapViewOfFile(view); +#else + munmap(view, size); +#endif +} + + +u8* MemArena::Find4GBBase() +{ +#ifdef _M_X64 +#ifdef _WIN32 + // 64 bit + u8* base = (u8*)VirtualAlloc(0, 0xE1000000, MEM_RESERVE, PAGE_READWRITE); + VirtualFree(base, 0, MEM_RELEASE); + return base; +#else + // Very precarious - mmap cannot return an error when trying to map already used pages. + // This makes the Windows approach above unusable on Linux, so we will simply pray... + return reinterpret_cast(0x2300000000ULL); +#endif + +#else + // 32 bit +#ifdef _WIN32 + // The highest thing in any 1GB section of memory space is the locked cache. We only need to fit it. + u8* base = (u8*)VirtualAlloc(0, 0x31000000, MEM_RESERVE, PAGE_READWRITE); + if (base) { + VirtualFree(base, 0, MEM_RELEASE); + } + return base; +#else + void* base = mmap(0, 0x31000000, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, 0, 0); + if (base == MAP_FAILED) { + PanicAlert("Failed to map 1 GB of memory space: %s", strerror(errno)); + return 0; + } + munmap(base, 0x31000000); + return static_cast(base); +#endif +#endif +} diff --git a/Source/Core/Common/Src/MemoryUtil.cpp b/Source/Core/Common/Src/MemoryUtil.cpp index 0099158872..10cf448f06 100644 --- a/Source/Core/Common/Src/MemoryUtil.cpp +++ b/Source/Core/Common/Src/MemoryUtil.cpp @@ -1,135 +1,135 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "MemoryUtil.h" - -#ifdef _WIN32 -#include -#elif __GNUC__ -#include -#include -#include -#endif - -#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) -#define MAP_ANONYMOUS MAP_ANON -#endif - -// MacOSX does not support MAP_VARIABLE -#ifndef MAP_VARIABLE -#define MAP_VARIABLE 0 -#endif - -// This is purposedely not a full wrapper for virtualalloc/mmap, but it -// provides exactly the primitive operations that Dolphin needs. - -void* AllocateExecutableMemory(int size, bool low) -{ -#ifdef _WIN32 - void* ptr = VirtualAlloc(0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); - - if ((u64)ptr >= 0x80000000) - { - PanicAlert("Executable memory ended up above 2GB!"); - // If this happens, we have to implement a free ram search scheme. ector knows how. - } - - return(ptr); - -#else - void* retval = mmap(0, size, PROT_READ | PROT_WRITE | PROT_EXEC, - MAP_ANONYMOUS | MAP_PRIVATE -#ifdef __x86_64__ - | (low ? MAP_32BIT : 0) -#endif - , -1, 0); // | MAP_FIXED - // printf("Mapped executable memory at %p (size %i)\n", retval, size); - - if (!retval) - { - PanicAlert("Failed to allocate executable memory, errno=%i", errno); - } - - return(retval); -#endif - -} - - -void* AllocateMemoryPages(int size) -{ -#ifdef _WIN32 - void* ptr = VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE); - - if (!ptr) - { - PanicAlert("Failed to allocate raw memory"); - } - - return(ptr); - -#else - void* retval = mmap(0, size, PROT_READ | PROT_WRITE, - MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); // | MAP_FIXED - // printf("Mapped memory at %p (size %i)\n", retval, size); - - if (!retval) - { - PanicAlert("Failed to allocate raw memory, errno=%i", errno); - } - - return(retval); -#endif - -} - - -void FreeMemoryPages(void* ptr, int size) -{ -#ifdef _WIN32 - if (ptr) - { - VirtualFree(ptr, 0, MEM_RELEASE); - ptr = NULL; - } -#else - munmap(ptr, size); -#endif -} - - -void WriteProtectMemory(void* ptr, int size, bool allowExecute) -{ -#ifdef _WIN32 - VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, 0); -#else - mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ); -#endif -} - - -void UnWriteProtectMemory(void* ptr, int size, bool allowExecute) -{ -#ifdef _WIN32 - VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READONLY, 0); -#else - mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ); -#endif -} - - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "MemoryUtil.h" + +#ifdef _WIN32 +#include +#elif __GNUC__ +#include +#include +#include +#endif + +#if !defined(MAP_ANONYMOUS) && defined(MAP_ANON) +#define MAP_ANONYMOUS MAP_ANON +#endif + +// MacOSX does not support MAP_VARIABLE +#ifndef MAP_VARIABLE +#define MAP_VARIABLE 0 +#endif + +// This is purposedely not a full wrapper for virtualalloc/mmap, but it +// provides exactly the primitive operations that Dolphin needs. + +void* AllocateExecutableMemory(int size, bool low) +{ +#ifdef _WIN32 + void* ptr = VirtualAlloc(0, size, MEM_COMMIT, PAGE_EXECUTE_READWRITE); + + if ((u64)ptr >= 0x80000000) + { + PanicAlert("Executable memory ended up above 2GB!"); + // If this happens, we have to implement a free ram search scheme. ector knows how. + } + + return(ptr); + +#else + void* retval = mmap(0, size, PROT_READ | PROT_WRITE | PROT_EXEC, + MAP_ANONYMOUS | MAP_PRIVATE +#ifdef __x86_64__ + | (low ? MAP_32BIT : 0) +#endif + , -1, 0); // | MAP_FIXED + // printf("Mapped executable memory at %p (size %i)\n", retval, size); + + if (!retval) + { + PanicAlert("Failed to allocate executable memory, errno=%i", errno); + } + + return(retval); +#endif + +} + + +void* AllocateMemoryPages(int size) +{ +#ifdef _WIN32 + void* ptr = VirtualAlloc(0, size, MEM_COMMIT, PAGE_READWRITE); + + if (!ptr) + { + PanicAlert("Failed to allocate raw memory"); + } + + return(ptr); + +#else + void* retval = mmap(0, size, PROT_READ | PROT_WRITE, + MAP_ANONYMOUS | MAP_PRIVATE, -1, 0); // | MAP_FIXED + // printf("Mapped memory at %p (size %i)\n", retval, size); + + if (!retval) + { + PanicAlert("Failed to allocate raw memory, errno=%i", errno); + } + + return(retval); +#endif + +} + + +void FreeMemoryPages(void* ptr, int size) +{ +#ifdef _WIN32 + if (ptr) + { + VirtualFree(ptr, 0, MEM_RELEASE); + ptr = NULL; + } +#else + munmap(ptr, size); +#endif +} + + +void WriteProtectMemory(void* ptr, int size, bool allowExecute) +{ +#ifdef _WIN32 + VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READ : PAGE_READONLY, 0); +#else + mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_EXEC) : PROT_READ); +#endif +} + + +void UnWriteProtectMemory(void* ptr, int size, bool allowExecute) +{ +#ifdef _WIN32 + VirtualProtect(ptr, size, allowExecute ? PAGE_EXECUTE_READWRITE : PAGE_READONLY, 0); +#else + mprotect(ptr, size, allowExecute ? (PROT_READ | PROT_WRITE | PROT_EXEC) : PROT_WRITE | PROT_READ); +#endif +} + + diff --git a/Source/Core/Common/Src/Plugin.cpp b/Source/Core/Common/Src/Plugin.cpp index 6a157f8b03..c804cf80e7 100644 --- a/Source/Core/Common/Src/Plugin.cpp +++ b/Source/Core/Common/Src/Plugin.cpp @@ -1,100 +1,100 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - - - -// ======================================================= -// File description -// ------------- -/* This file is a simpler version of Plugin_...cpp found in Core. This file only loads - the config and debugging windowses and works with all plugins. */ -// ============= - - -#include "Plugin.h" - -namespace Common -{ -DynamicLibrary CPlugin::m_hInstLib; - -void(__cdecl * CPlugin::m_GetDllInfo) (PLUGIN_INFO * _PluginInfo) = 0; -//void(__cdecl * CPlugin::m_DllAbout) (HWND _hParent) = 0; -void(__cdecl * CPlugin::m_DllConfig) (HWND _hParent) = 0; -void(__cdecl * CPlugin::m_DllDebugger) (HWND _hParent, bool Show) = 0; - -void -CPlugin::Release(void) -{ - m_GetDllInfo = 0; - //m_DllAbout = 0; - m_DllConfig = 0; - m_DllDebugger = 0; - - m_hInstLib.Unload(); -} - -bool -CPlugin::Load(const char* _szName) -{ - if (m_hInstLib.Load(_szName)) - { - m_GetDllInfo = (void (__cdecl*)(PLUGIN_INFO*)) m_hInstLib.Get("GetDllInfo"); - m_DllConfig = (void (__cdecl*)(HWND)) m_hInstLib.Get("DllConfig"); - m_DllDebugger = (void (__cdecl*)(HWND, bool)) m_hInstLib.Get("DllDebugger"); - return(true); - } - - return(false); -} - - -bool CPlugin::GetInfo(PLUGIN_INFO& _pluginInfo) -{ - if (m_GetDllInfo != 0) - { - m_GetDllInfo(&_pluginInfo); - return(true); - } - - return(false); -} - - -void CPlugin::Config(HWND _hwnd) -{ - if (m_DllConfig != 0) - { - m_DllConfig(_hwnd); - } -} - -//void CPlugin::About(HWND _hwnd) -//{ -// if (m_DllAbout != 0) -// { -// m_DllAbout(_hwnd); -// } -//} - -void CPlugin::Debug(HWND _hwnd, bool Show) -{ - if (m_DllDebugger != 0) - { - m_DllDebugger(_hwnd, Show); - } -} -} // end of namespace Common +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + + + +// ======================================================= +// File description +// ------------- +/* This file is a simpler version of Plugin_...cpp found in Core. This file only loads + the config and debugging windowses and works with all plugins. */ +// ============= + + +#include "Plugin.h" + +namespace Common +{ +DynamicLibrary CPlugin::m_hInstLib; + +void(__cdecl * CPlugin::m_GetDllInfo) (PLUGIN_INFO * _PluginInfo) = 0; +//void(__cdecl * CPlugin::m_DllAbout) (HWND _hParent) = 0; +void(__cdecl * CPlugin::m_DllConfig) (HWND _hParent) = 0; +void(__cdecl * CPlugin::m_DllDebugger) (HWND _hParent, bool Show) = 0; + +void +CPlugin::Release(void) +{ + m_GetDllInfo = 0; + //m_DllAbout = 0; + m_DllConfig = 0; + m_DllDebugger = 0; + + m_hInstLib.Unload(); +} + +bool +CPlugin::Load(const char* _szName) +{ + if (m_hInstLib.Load(_szName)) + { + m_GetDllInfo = (void (__cdecl*)(PLUGIN_INFO*)) m_hInstLib.Get("GetDllInfo"); + m_DllConfig = (void (__cdecl*)(HWND)) m_hInstLib.Get("DllConfig"); + m_DllDebugger = (void (__cdecl*)(HWND, bool)) m_hInstLib.Get("DllDebugger"); + return(true); + } + + return(false); +} + + +bool CPlugin::GetInfo(PLUGIN_INFO& _pluginInfo) +{ + if (m_GetDllInfo != 0) + { + m_GetDllInfo(&_pluginInfo); + return(true); + } + + return(false); +} + + +void CPlugin::Config(HWND _hwnd) +{ + if (m_DllConfig != 0) + { + m_DllConfig(_hwnd); + } +} + +//void CPlugin::About(HWND _hwnd) +//{ +// if (m_DllAbout != 0) +// { +// m_DllAbout(_hwnd); +// } +//} + +void CPlugin::Debug(HWND _hwnd, bool Show) +{ + if (m_DllDebugger != 0) + { + m_DllDebugger(_hwnd, Show); + } +} +} // end of namespace Common diff --git a/Source/Core/Common/Src/StringUtil.cpp b/Source/Core/Common/Src/StringUtil.cpp index 205ad61e72..597e5fc620 100644 --- a/Source/Core/Common/Src/StringUtil.cpp +++ b/Source/Core/Common/Src/StringUtil.cpp @@ -1,396 +1,396 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include -#include - -#include "StringUtil.h" -#include "TestFramework.h" - -// faster than sscanf -bool AsciiToHex(const char* _szValue, u32& result) -{ - u32 value = 0; - size_t finish = strlen(_szValue); - - if (finish > 8) - finish = 8; // Max 32-bit values are supported. - - for (size_t count = 0; count < finish; count++) - { - value <<= 4; - switch (_szValue[count]) - { - case '0': break; - case '1': value += 1; break; - case '2': value += 2; break; - case '3': value += 3; break; - case '4': value += 4; break; - case '5': value += 5; break; - case '6': value += 6; break; - case '7': value += 7; break; - case '8': value += 8; break; - case '9': value += 9; break; - case 'A': - case 'a': value += 10; break; - case 'B': - case 'b': value += 11; break; - case 'C': - case 'c': value += 12; break; - case 'D': - case 'd': value += 13; break; - case 'E': - case 'e': value += 14; break; - case 'F': - case 'f': value += 15; break; - default: - return false; - break; - } - } - - result = value; - return (true); -} - -bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list args) -{ - int writtenCount = vsnprintf(out, outsize, format, args); - - if (writtenCount > 0 && writtenCount < outsize) - { - out[writtenCount] = '\0'; - return true; - } - else - { - out[outsize - 1] = '\0'; - return false; - } -} - - -// Expensive! -void StringFromFormatV(std::string* out, const char* format, va_list args) -{ - int writtenCount = -1; - size_t newSize = strlen(format) + 16; - char* buf = 0; - - while (writtenCount < 0) - { - delete [] buf; - buf = new char[newSize + 1]; - writtenCount = vsnprintf(buf, newSize, format, args); - if (writtenCount > (int)newSize) - writtenCount = -1; - // ARGH! vsnprintf does no longer return -1 on truncation in newer libc! - // WORKAROUND! let's fake the old behaviour (even though it's less efficient). - // TODO: figure out why the fix causes an invalid read in strlen called from vsnprintf :( -// if (writtenCount >= (int)newSize) -// writtenCount = -1; - newSize *= 2; - } - - buf[writtenCount] = '\0'; - *out = buf; - delete[] buf; -} - - -std::string StringFromFormat(const char* format, ...) -{ - std::string temp; - va_list args; - va_start(args, format); - StringFromFormatV(&temp, format, args); - va_end(args); - return(temp); -} - - -void ToStringFromFormat(std::string* out, const char* format, ...) -{ - va_list args; - va_start(args, format); - StringFromFormatV(out, format, args); - va_end(args); -} - - -// Turns " hej " into "hej". Also handles tabs. -std::string StripSpaces(const std::string &str) -{ - std::string s = str; - int i; - for (i = 0; i < (int)s.size(); i++) - { - if ((s[i] != ' ') && (s[i] != 9)) - { - break; - } - } - - s = s.substr(i); - - for (i = (int)s.size() - 1; i > 0; i--) - { - if ((s[i] != ' ') && (s[i] != 9)) - { - break; - } - } - - return s.substr(0, i + 1); -} - - -// "\"hello\"" is turned to "hello" -// This one assumes that the string has already been space stripped in both -// ends, as done by StripSpaces above, for example. -std::string StripQuotes(const std::string& s) -{ - if ((s[0] == '\"') && (s[s.size() - 1] == '\"')) - return s.substr(1, s.size() - 2); - else - return s; -} - -// "\"hello\"" is turned to "hello" -// This one assumes that the string has already been space stripped in both -// ends, as done by StripSpaces above, for example. -std::string StripNewline(const std::string& s) -{ - if (!s.size()) - return s; - else if (s[s.size() - 1] == '\n') - return s.substr(0, s.size() - 1); - else - return s; -} - -bool TryParseInt(const char* str, int* outVal) -{ - const char* s = str; - int value = 0; - bool negativ = false; - - if (*s == '-') - { - negativ = true; - s++; - } - - while (*s) - { - char c = *s++; - - if ((c < '0') || (c > '9')) - { - return false; - } - - value = value * 10 + (c - '0'); - } - if (negativ) - value = -value; - - *outVal = value; - return true; -} - - -bool TryParseBool(const char* str, bool* output) -{ - if ((str[0] == '1') || !strcmp(str, "true") || !strcmp(str, "True") || !strcmp(str, "TRUE")) - { - *output = true; - return true; - } - else if (str[0] == '0' || !strcmp(str, "false") || !strcmp(str, "False") || !strcmp(str, "FALSE")) - { - *output = false; - return true; - } - return false; -} - -std::string StringFromInt(int value) -{ - char temp[16]; - sprintf(temp, "%i", value); - return std::string(temp); -} - -std::string StringFromBool(bool value) -{ - return value ? "True" : "False"; -} - -#ifdef _WIN32 -bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension) -{ - char drive[_MAX_DRIVE]; - char dir[_MAX_DIR]; - char fname[_MAX_FNAME]; - char ext[_MAX_EXT]; - - if (_splitpath_s(full_path.c_str(), drive, _MAX_DRIVE, dir, _MAX_DIR, fname, _MAX_FNAME, ext, _MAX_EXT) == 0) - { - if (_pPath) - { - *_pPath = std::string(drive) + std::string(dir); - } - - if (_pFilename != 0) - { - *_pFilename = fname; - } - - if (_pExtension != 0) - { - *_pExtension = ext; - } - - return true; - } - - return false; -} - - -#else -bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension) -{ - size_t last_slash = full_path.rfind('/'); - - if (last_slash == std::string::npos) - { - return false; - } - - size_t last_dot = full_path.rfind('.'); - - if ((last_dot == std::string::npos) || (last_dot < last_slash)) - { - return false; - } - - if (_pPath) - { - *_pPath = full_path.substr(0, last_slash + 1); - } - - if (_pFilename) - { - *_pFilename = full_path.substr(last_slash + 1, last_dot - (last_slash + 1)); - } - - if (_pExtension) - { - *_pExtension = full_path.substr(last_dot + 1); - _pExtension->insert(0, "."); - } - else if (_pFilename) - { - *_pFilename += full_path.substr(last_dot); - } - - return true; -} - - -#endif - -void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path, const std::string& _Filename) -{ - _CompleteFilename = _Path; - - // check for seperator - if (_CompleteFilename[_CompleteFilename.size() - 1] != '\\') - { - _CompleteFilename += "\\"; - } - - // add the filename - _CompleteFilename += _Filename; -} - - -void SplitString(const std::string& str, const std::string& delim, std::vector& output) -{ - output.clear(); - - size_t offset = 0; - size_t delimIndex = 0; - - delimIndex = str.find(delim, offset); - - while (delimIndex != std::string::npos) - { - output.push_back(str.substr(offset, delimIndex - offset)); - offset += delimIndex - offset + delim.length(); - delimIndex = str.find(delim, offset); - } - - output.push_back(str.substr(offset)); -} - - -bool TryParseUInt(const std::string& str, u32* output) -{ - if (!strcmp(str.substr(0, 2).c_str(), "0x") || !strcmp(str.substr(0, 2).c_str(), "0X")) - return sscanf(str.c_str() + 2, "%x", output) > 0; - else - return sscanf(str.c_str(), "%d", output) > 0; -} - - -int ChooseStringFrom(const char* str, const char* * items) -{ - int i = 0; - while (items[i] != 0) - { - if (!strcmp(str, items[i])) - return i; - i++; - } - return -1; -} - - -// Thousand separator. Turns 12345678 into 12,345,678. -std::string ThS(int a, bool b) -{ - char cbuf[20]; - - // determine treatment of signed or unsigned - if(b) sprintf(cbuf, "%u", a); else sprintf(cbuf, "%i", a); - - - std::string sbuf = cbuf; - for (u32 i = 0; i < sbuf.length(); ++i) - { - if((i & 3) == 3) - { - sbuf.insert(sbuf.length() - i, ","); - } - } - return sbuf; -} - - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include + +#include "StringUtil.h" +#include "TestFramework.h" + +// faster than sscanf +bool AsciiToHex(const char* _szValue, u32& result) +{ + u32 value = 0; + size_t finish = strlen(_szValue); + + if (finish > 8) + finish = 8; // Max 32-bit values are supported. + + for (size_t count = 0; count < finish; count++) + { + value <<= 4; + switch (_szValue[count]) + { + case '0': break; + case '1': value += 1; break; + case '2': value += 2; break; + case '3': value += 3; break; + case '4': value += 4; break; + case '5': value += 5; break; + case '6': value += 6; break; + case '7': value += 7; break; + case '8': value += 8; break; + case '9': value += 9; break; + case 'A': + case 'a': value += 10; break; + case 'B': + case 'b': value += 11; break; + case 'C': + case 'c': value += 12; break; + case 'D': + case 'd': value += 13; break; + case 'E': + case 'e': value += 14; break; + case 'F': + case 'f': value += 15; break; + default: + return false; + break; + } + } + + result = value; + return (true); +} + +bool CharArrayFromFormatV(char* out, int outsize, const char* format, va_list args) +{ + int writtenCount = vsnprintf(out, outsize, format, args); + + if (writtenCount > 0 && writtenCount < outsize) + { + out[writtenCount] = '\0'; + return true; + } + else + { + out[outsize - 1] = '\0'; + return false; + } +} + + +// Expensive! +void StringFromFormatV(std::string* out, const char* format, va_list args) +{ + int writtenCount = -1; + size_t newSize = strlen(format) + 16; + char* buf = 0; + + while (writtenCount < 0) + { + delete [] buf; + buf = new char[newSize + 1]; + writtenCount = vsnprintf(buf, newSize, format, args); + if (writtenCount > (int)newSize) + writtenCount = -1; + // ARGH! vsnprintf does no longer return -1 on truncation in newer libc! + // WORKAROUND! let's fake the old behaviour (even though it's less efficient). + // TODO: figure out why the fix causes an invalid read in strlen called from vsnprintf :( +// if (writtenCount >= (int)newSize) +// writtenCount = -1; + newSize *= 2; + } + + buf[writtenCount] = '\0'; + *out = buf; + delete[] buf; +} + + +std::string StringFromFormat(const char* format, ...) +{ + std::string temp; + va_list args; + va_start(args, format); + StringFromFormatV(&temp, format, args); + va_end(args); + return(temp); +} + + +void ToStringFromFormat(std::string* out, const char* format, ...) +{ + va_list args; + va_start(args, format); + StringFromFormatV(out, format, args); + va_end(args); +} + + +// Turns " hej " into "hej". Also handles tabs. +std::string StripSpaces(const std::string &str) +{ + std::string s = str; + int i; + for (i = 0; i < (int)s.size(); i++) + { + if ((s[i] != ' ') && (s[i] != 9)) + { + break; + } + } + + s = s.substr(i); + + for (i = (int)s.size() - 1; i > 0; i--) + { + if ((s[i] != ' ') && (s[i] != 9)) + { + break; + } + } + + return s.substr(0, i + 1); +} + + +// "\"hello\"" is turned to "hello" +// This one assumes that the string has already been space stripped in both +// ends, as done by StripSpaces above, for example. +std::string StripQuotes(const std::string& s) +{ + if ((s[0] == '\"') && (s[s.size() - 1] == '\"')) + return s.substr(1, s.size() - 2); + else + return s; +} + +// "\"hello\"" is turned to "hello" +// This one assumes that the string has already been space stripped in both +// ends, as done by StripSpaces above, for example. +std::string StripNewline(const std::string& s) +{ + if (!s.size()) + return s; + else if (s[s.size() - 1] == '\n') + return s.substr(0, s.size() - 1); + else + return s; +} + +bool TryParseInt(const char* str, int* outVal) +{ + const char* s = str; + int value = 0; + bool negativ = false; + + if (*s == '-') + { + negativ = true; + s++; + } + + while (*s) + { + char c = *s++; + + if ((c < '0') || (c > '9')) + { + return false; + } + + value = value * 10 + (c - '0'); + } + if (negativ) + value = -value; + + *outVal = value; + return true; +} + + +bool TryParseBool(const char* str, bool* output) +{ + if ((str[0] == '1') || !strcmp(str, "true") || !strcmp(str, "True") || !strcmp(str, "TRUE")) + { + *output = true; + return true; + } + else if (str[0] == '0' || !strcmp(str, "false") || !strcmp(str, "False") || !strcmp(str, "FALSE")) + { + *output = false; + return true; + } + return false; +} + +std::string StringFromInt(int value) +{ + char temp[16]; + sprintf(temp, "%i", value); + return std::string(temp); +} + +std::string StringFromBool(bool value) +{ + return value ? "True" : "False"; +} + +#ifdef _WIN32 +bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension) +{ + char drive[_MAX_DRIVE]; + char dir[_MAX_DIR]; + char fname[_MAX_FNAME]; + char ext[_MAX_EXT]; + + if (_splitpath_s(full_path.c_str(), drive, _MAX_DRIVE, dir, _MAX_DIR, fname, _MAX_FNAME, ext, _MAX_EXT) == 0) + { + if (_pPath) + { + *_pPath = std::string(drive) + std::string(dir); + } + + if (_pFilename != 0) + { + *_pFilename = fname; + } + + if (_pExtension != 0) + { + *_pExtension = ext; + } + + return true; + } + + return false; +} + + +#else +bool SplitPath(const std::string& full_path, std::string* _pPath, std::string* _pFilename, std::string* _pExtension) +{ + size_t last_slash = full_path.rfind('/'); + + if (last_slash == std::string::npos) + { + return false; + } + + size_t last_dot = full_path.rfind('.'); + + if ((last_dot == std::string::npos) || (last_dot < last_slash)) + { + return false; + } + + if (_pPath) + { + *_pPath = full_path.substr(0, last_slash + 1); + } + + if (_pFilename) + { + *_pFilename = full_path.substr(last_slash + 1, last_dot - (last_slash + 1)); + } + + if (_pExtension) + { + *_pExtension = full_path.substr(last_dot + 1); + _pExtension->insert(0, "."); + } + else if (_pFilename) + { + *_pFilename += full_path.substr(last_dot); + } + + return true; +} + + +#endif + +void BuildCompleteFilename(std::string& _CompleteFilename, const std::string& _Path, const std::string& _Filename) +{ + _CompleteFilename = _Path; + + // check for seperator + if (_CompleteFilename[_CompleteFilename.size() - 1] != '\\') + { + _CompleteFilename += "\\"; + } + + // add the filename + _CompleteFilename += _Filename; +} + + +void SplitString(const std::string& str, const std::string& delim, std::vector& output) +{ + output.clear(); + + size_t offset = 0; + size_t delimIndex = 0; + + delimIndex = str.find(delim, offset); + + while (delimIndex != std::string::npos) + { + output.push_back(str.substr(offset, delimIndex - offset)); + offset += delimIndex - offset + delim.length(); + delimIndex = str.find(delim, offset); + } + + output.push_back(str.substr(offset)); +} + + +bool TryParseUInt(const std::string& str, u32* output) +{ + if (!strcmp(str.substr(0, 2).c_str(), "0x") || !strcmp(str.substr(0, 2).c_str(), "0X")) + return sscanf(str.c_str() + 2, "%x", output) > 0; + else + return sscanf(str.c_str(), "%d", output) > 0; +} + + +int ChooseStringFrom(const char* str, const char* * items) +{ + int i = 0; + while (items[i] != 0) + { + if (!strcmp(str, items[i])) + return i; + i++; + } + return -1; +} + + +// Thousand separator. Turns 12345678 into 12,345,678. +std::string ThS(int a, bool b) +{ + char cbuf[20]; + + // determine treatment of signed or unsigned + if(b) sprintf(cbuf, "%u", a); else sprintf(cbuf, "%i", a); + + + std::string sbuf = cbuf; + for (u32 i = 0; i < sbuf.length(); ++i) + { + if((i & 3) == 3) + { + sbuf.insert(sbuf.length() - i, ","); + } + } + return sbuf; +} + + diff --git a/Source/Core/Common/Src/TestFramework.cpp b/Source/Core/Common/Src/TestFramework.cpp index 623e96b35a..d805db5996 100644 --- a/Source/Core/Common/Src/TestFramework.cpp +++ b/Source/Core/Common/Src/TestFramework.cpp @@ -1,38 +1,38 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "TestFramework.h" - -namespace __test -{ -int numTests; -int numTestsFailed; -} - - -int GetNumTests() -{ - return(__test::numTests); -} - - -int GetNumTestsFailed() -{ - return(__test::numTestsFailed); -} - - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "TestFramework.h" + +namespace __test +{ +int numTests; +int numTestsFailed; +} + + +int GetNumTests() +{ + return(__test::numTests); +} + + +int GetNumTestsFailed() +{ + return(__test::numTestsFailed); +} + + diff --git a/Source/Core/Common/Src/Thread.cpp b/Source/Core/Common/Src/Thread.cpp index ab045a808e..5af43cca41 100644 --- a/Source/Core/Common/Src/Thread.cpp +++ b/Source/Core/Common/Src/Thread.cpp @@ -1,404 +1,404 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" - -#ifdef _WIN32 -#include -#elif __GNUC__ -#include -#include -#else -#error unsupported platform -#endif - -#include "Thread.h" - -namespace Common -{ -#ifdef _WIN32 -CriticalSection::CriticalSection(int spincount) -{ - if (spincount) - { - InitializeCriticalSectionAndSpinCount(§ion, spincount); - } - else - { - InitializeCriticalSection(§ion); - } -} - - -CriticalSection::~CriticalSection() -{ - DeleteCriticalSection(§ion); -} - - -void CriticalSection::Enter() -{ - EnterCriticalSection(§ion); -} - - -bool CriticalSection::TryEnter() -{ - return(TryEnterCriticalSection(§ion) ? true : false); -} - - -void CriticalSection::Leave() -{ - LeaveCriticalSection(§ion); -} - - -Thread::Thread(ThreadFunc function, void* arg) - : m_hThread(NULL), m_threadId(0) -{ - m_hThread = CreateThread( - 0, // Security attributes - 0, // Stack size - function, - arg, - 0, - &m_threadId); -} - - -Thread::~Thread() -{ - WaitForDeath(); -} - - -void Thread::WaitForDeath() -{ - if (m_hThread) - { - WaitForSingleObject(m_hThread, INFINITE); - CloseHandle(m_hThread); - m_hThread = NULL; - } -} - - -void Thread::SetAffinity(int mask) -{ - SetThreadAffinityMask(m_hThread, mask); -} - - -void Thread::SetCurrentThreadAffinity(int mask) -{ - SetThreadAffinityMask(GetCurrentThread(), mask); -} - - -Event::Event() -{ - m_hEvent = 0; -} - - -void Event::Init() -{ - m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); -} - - -void Event::Shutdown() -{ - CloseHandle(m_hEvent); - m_hEvent = 0; -} - - -void Event::Set() -{ - SetEvent(m_hEvent); -} - - -void Event::Wait() -{ - WaitForSingleObject(m_hEvent, INFINITE); -} - - -void SleepCurrentThread(int ms) -{ - Sleep(ms); -} - - -typedef struct tagTHREADNAME_INFO -{ - DWORD dwType; // must be 0x1000 - LPCSTR szName; // pointer to name (in user addr space) - DWORD dwThreadID; // thread ID (-1=caller thread) - DWORD dwFlags; // reserved for future use, must be zero -} THREADNAME_INFO; -// Usage: SetThreadName (-1, "MainThread"); -// -// Sets the debugger-visible name of the current thread. -// Uses undocumented (actually, it is now documented) trick. -// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp - -void SetCurrentThreadName(const TCHAR* szThreadName) -{ - THREADNAME_INFO info; - info.dwType = 0x1000; -#ifdef UNICODE - //TODO: Find the proper way to do this. - char tname[256]; - unsigned int i; - - for (i = 0; i < _tcslen(szThreadName); i++) - { - tname[i] = (char)szThreadName[i]; //poor man's unicode->ansi, TODO: fix - } - - tname[i] = 0; - info.szName = tname; -#else - info.szName = szThreadName; -#endif - - info.dwThreadID = -1; //dwThreadID; - info.dwFlags = 0; - __try - { - RaiseException(0x406D1388, 0, sizeof(info) / sizeof(DWORD), (ULONG_PTR*)&info); - } - __except(EXCEPTION_CONTINUE_EXECUTION) - {} -} -// TODO: check if ever inline -LONG SyncInterlockedIncrement(LONG *Dest) -{ - return InterlockedIncrement(Dest); -} - -LONG SyncInterlockedExchangeAdd(LONG *Dest, LONG Val) -{ - return InterlockedExchangeAdd(Dest, Val); -} - -LONG SyncInterlockedExchange(LONG *Dest, LONG Val) -{ - return InterlockedExchange(Dest, Val); -} - -#elif __GNUC__ -CriticalSection::CriticalSection(int spincount_unused) -{ - pthread_mutex_init(&mutex, 0); -} - - -CriticalSection::~CriticalSection() -{ - pthread_mutex_destroy(&mutex); -} - - -void CriticalSection::Enter() -{ - pthread_mutex_lock(&mutex); -} - - -bool CriticalSection::TryEnter() -{ - return(!pthread_mutex_trylock(&mutex)); -} - - -void CriticalSection::Leave() -{ - pthread_mutex_unlock(&mutex); -} - - -Thread::Thread(ThreadFunc function, void* arg) - : thread_id(0) -{ - pthread_attr_t attr; - pthread_attr_init(&attr); - pthread_attr_setstacksize(&attr, 1024 * 1024); - pthread_create(&thread_id, &attr, function, arg); -} - - -Thread::~Thread() -{ - WaitForDeath(); -} - - -void Thread::WaitForDeath() -{ - if (thread_id) - { - void* exit_status; - pthread_join(thread_id, &exit_status); - if (exit_status) - fprintf(stderr, "error %d joining thread\n", *(int *)exit_status); - thread_id = 0; - } -} - - -void Thread::SetAffinity(int mask) -{ - // This is non-standard -#ifdef __linux__ - cpu_set_t cpu_set; - CPU_ZERO(&cpu_set); - - for (unsigned int i = 0; i < sizeof(mask) * 8; i++) - { - if ((mask >> i) & 1){CPU_SET(i, &cpu_set);} - } - - pthread_setaffinity_np(thread_id, sizeof(cpu_set), &cpu_set); -#endif -} - - -void Thread::SetCurrentThreadAffinity(int mask) -{ -#ifdef __linux__ - cpu_set_t cpu_set; - CPU_ZERO(&cpu_set); - - for (size_t i = 0; i < sizeof(mask) * 8; i++) - { - if ((mask >> i) & 1){CPU_SET(i, &cpu_set);} - } - - pthread_setaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set); -#endif -} - - -void SleepCurrentThread(int ms) -{ - usleep(1000 * ms); -} - - -void SetCurrentThreadName(const TCHAR* szThreadName) -{ - // noop -} - - -Event::Event() -{ - is_set_ = false; -} - - -void Event::Init() -{ - pthread_cond_init(&event_, 0); - pthread_mutex_init(&mutex_, 0); -} - - -void Event::Shutdown() -{ - pthread_mutex_destroy(&mutex_); - pthread_cond_destroy(&event_); -} - - -void Event::Set() -{ - pthread_mutex_lock(&mutex_); - - if (!is_set_) - { - is_set_ = true; - pthread_cond_signal(&event_); - } - - pthread_mutex_unlock(&mutex_); -} - - -void Event::Wait() -{ - pthread_mutex_lock(&mutex_); - - while (!is_set_) - { - pthread_cond_wait(&event_, &mutex_); - } - - is_set_ = false; - pthread_mutex_unlock(&mutex_); -} - -LONG SyncInterlockedIncrement(LONG *Dest) -{ -#if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__)) - return __sync_add_and_fetch(Dest, 1); -#else - register int result; - __asm__ __volatile__("lock; xadd %0,%1" - : "=r" (result), "=m" (*Dest) - : "0" (1), "m" (*Dest) - : "memory"); - return result; -#endif -} - -LONG SyncInterlockedExchangeAdd(LONG *Dest, LONG Val) -{ -#if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__)) - return __sync_add_and_fetch(Dest, Val); -#else - register int result; - __asm__ __volatile__("lock; xadd %0,%1" - : "=r" (result), "=m" (*Dest) - : "0" (Val), "m" (*Dest) - : "memory"); - return result; -#endif -} - -LONG SyncInterlockedExchange(LONG *Dest, LONG Val) -{ -#if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__)) - return __sync_lock_test_and_set(Dest, Val); -#else - register int result; - __asm__ __volatile__("lock; xchg %0,%1" - : "=r" (result), "=m" (*Dest) - : "0" (Val), "m" (*Dest) - : "memory"); - return result; -#endif -} - -#endif - -} // end of namespace Common +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" + +#ifdef _WIN32 +#include +#elif __GNUC__ +#include +#include +#else +#error unsupported platform +#endif + +#include "Thread.h" + +namespace Common +{ +#ifdef _WIN32 +CriticalSection::CriticalSection(int spincount) +{ + if (spincount) + { + InitializeCriticalSectionAndSpinCount(§ion, spincount); + } + else + { + InitializeCriticalSection(§ion); + } +} + + +CriticalSection::~CriticalSection() +{ + DeleteCriticalSection(§ion); +} + + +void CriticalSection::Enter() +{ + EnterCriticalSection(§ion); +} + + +bool CriticalSection::TryEnter() +{ + return(TryEnterCriticalSection(§ion) ? true : false); +} + + +void CriticalSection::Leave() +{ + LeaveCriticalSection(§ion); +} + + +Thread::Thread(ThreadFunc function, void* arg) + : m_hThread(NULL), m_threadId(0) +{ + m_hThread = CreateThread( + 0, // Security attributes + 0, // Stack size + function, + arg, + 0, + &m_threadId); +} + + +Thread::~Thread() +{ + WaitForDeath(); +} + + +void Thread::WaitForDeath() +{ + if (m_hThread) + { + WaitForSingleObject(m_hThread, INFINITE); + CloseHandle(m_hThread); + m_hThread = NULL; + } +} + + +void Thread::SetAffinity(int mask) +{ + SetThreadAffinityMask(m_hThread, mask); +} + + +void Thread::SetCurrentThreadAffinity(int mask) +{ + SetThreadAffinityMask(GetCurrentThread(), mask); +} + + +Event::Event() +{ + m_hEvent = 0; +} + + +void Event::Init() +{ + m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL); +} + + +void Event::Shutdown() +{ + CloseHandle(m_hEvent); + m_hEvent = 0; +} + + +void Event::Set() +{ + SetEvent(m_hEvent); +} + + +void Event::Wait() +{ + WaitForSingleObject(m_hEvent, INFINITE); +} + + +void SleepCurrentThread(int ms) +{ + Sleep(ms); +} + + +typedef struct tagTHREADNAME_INFO +{ + DWORD dwType; // must be 0x1000 + LPCSTR szName; // pointer to name (in user addr space) + DWORD dwThreadID; // thread ID (-1=caller thread) + DWORD dwFlags; // reserved for future use, must be zero +} THREADNAME_INFO; +// Usage: SetThreadName (-1, "MainThread"); +// +// Sets the debugger-visible name of the current thread. +// Uses undocumented (actually, it is now documented) trick. +// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp + +void SetCurrentThreadName(const TCHAR* szThreadName) +{ + THREADNAME_INFO info; + info.dwType = 0x1000; +#ifdef UNICODE + //TODO: Find the proper way to do this. + char tname[256]; + unsigned int i; + + for (i = 0; i < _tcslen(szThreadName); i++) + { + tname[i] = (char)szThreadName[i]; //poor man's unicode->ansi, TODO: fix + } + + tname[i] = 0; + info.szName = tname; +#else + info.szName = szThreadName; +#endif + + info.dwThreadID = -1; //dwThreadID; + info.dwFlags = 0; + __try + { + RaiseException(0x406D1388, 0, sizeof(info) / sizeof(DWORD), (ULONG_PTR*)&info); + } + __except(EXCEPTION_CONTINUE_EXECUTION) + {} +} +// TODO: check if ever inline +LONG SyncInterlockedIncrement(LONG *Dest) +{ + return InterlockedIncrement(Dest); +} + +LONG SyncInterlockedExchangeAdd(LONG *Dest, LONG Val) +{ + return InterlockedExchangeAdd(Dest, Val); +} + +LONG SyncInterlockedExchange(LONG *Dest, LONG Val) +{ + return InterlockedExchange(Dest, Val); +} + +#elif __GNUC__ +CriticalSection::CriticalSection(int spincount_unused) +{ + pthread_mutex_init(&mutex, 0); +} + + +CriticalSection::~CriticalSection() +{ + pthread_mutex_destroy(&mutex); +} + + +void CriticalSection::Enter() +{ + pthread_mutex_lock(&mutex); +} + + +bool CriticalSection::TryEnter() +{ + return(!pthread_mutex_trylock(&mutex)); +} + + +void CriticalSection::Leave() +{ + pthread_mutex_unlock(&mutex); +} + + +Thread::Thread(ThreadFunc function, void* arg) + : thread_id(0) +{ + pthread_attr_t attr; + pthread_attr_init(&attr); + pthread_attr_setstacksize(&attr, 1024 * 1024); + pthread_create(&thread_id, &attr, function, arg); +} + + +Thread::~Thread() +{ + WaitForDeath(); +} + + +void Thread::WaitForDeath() +{ + if (thread_id) + { + void* exit_status; + pthread_join(thread_id, &exit_status); + if (exit_status) + fprintf(stderr, "error %d joining thread\n", *(int *)exit_status); + thread_id = 0; + } +} + + +void Thread::SetAffinity(int mask) +{ + // This is non-standard +#ifdef __linux__ + cpu_set_t cpu_set; + CPU_ZERO(&cpu_set); + + for (unsigned int i = 0; i < sizeof(mask) * 8; i++) + { + if ((mask >> i) & 1){CPU_SET(i, &cpu_set);} + } + + pthread_setaffinity_np(thread_id, sizeof(cpu_set), &cpu_set); +#endif +} + + +void Thread::SetCurrentThreadAffinity(int mask) +{ +#ifdef __linux__ + cpu_set_t cpu_set; + CPU_ZERO(&cpu_set); + + for (size_t i = 0; i < sizeof(mask) * 8; i++) + { + if ((mask >> i) & 1){CPU_SET(i, &cpu_set);} + } + + pthread_setaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set); +#endif +} + + +void SleepCurrentThread(int ms) +{ + usleep(1000 * ms); +} + + +void SetCurrentThreadName(const TCHAR* szThreadName) +{ + // noop +} + + +Event::Event() +{ + is_set_ = false; +} + + +void Event::Init() +{ + pthread_cond_init(&event_, 0); + pthread_mutex_init(&mutex_, 0); +} + + +void Event::Shutdown() +{ + pthread_mutex_destroy(&mutex_); + pthread_cond_destroy(&event_); +} + + +void Event::Set() +{ + pthread_mutex_lock(&mutex_); + + if (!is_set_) + { + is_set_ = true; + pthread_cond_signal(&event_); + } + + pthread_mutex_unlock(&mutex_); +} + + +void Event::Wait() +{ + pthread_mutex_lock(&mutex_); + + while (!is_set_) + { + pthread_cond_wait(&event_, &mutex_); + } + + is_set_ = false; + pthread_mutex_unlock(&mutex_); +} + +LONG SyncInterlockedIncrement(LONG *Dest) +{ +#if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__)) + return __sync_add_and_fetch(Dest, 1); +#else + register int result; + __asm__ __volatile__("lock; xadd %0,%1" + : "=r" (result), "=m" (*Dest) + : "0" (1), "m" (*Dest) + : "memory"); + return result; +#endif +} + +LONG SyncInterlockedExchangeAdd(LONG *Dest, LONG Val) +{ +#if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__)) + return __sync_add_and_fetch(Dest, Val); +#else + register int result; + __asm__ __volatile__("lock; xadd %0,%1" + : "=r" (result), "=m" (*Dest) + : "0" (Val), "m" (*Dest) + : "memory"); + return result; +#endif +} + +LONG SyncInterlockedExchange(LONG *Dest, LONG Val) +{ +#if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__)) + return __sync_lock_test_and_set(Dest, Val); +#else + register int result; + __asm__ __volatile__("lock; xchg %0,%1" + : "=r" (result), "=m" (*Dest) + : "0" (Val), "m" (*Dest) + : "memory"); + return result; +#endif +} + +#endif + +} // end of namespace Common diff --git a/Source/Core/Common/Src/Thunk.cpp b/Source/Core/Common/Src/Thunk.cpp index 73ad098090..a5ba2c9b8d 100644 --- a/Source/Core/Common/Src/Thunk.cpp +++ b/Source/Core/Common/Src/Thunk.cpp @@ -1,153 +1,153 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include - -#include "Common.h" -#include "Thunk.h" -#include "x64Emitter.h" -#include "MemoryUtil.h" -#include "ABI.h" - -using namespace Gen; - -#define THUNK_ARENA_SIZE 1024*1024*1 - -namespace { -static std::map thunks; -u8 GC_ALIGNED32(saved_fp_state[16 * 4 * 4]); -u8 GC_ALIGNED32(saved_gpr_state[16 * 8]); - -static u8 *thunk_memory; -static u8 *thunk_code; -static const u8 *save_regs; -static const u8 *load_regs; -static u16 saved_mxcsr; -} - -void Thunk_Init() -{ - thunk_memory = (u8 *)AllocateExecutableMemory(THUNK_ARENA_SIZE); - thunk_code = thunk_memory; - - GenContext ctx(&thunk_code); - save_regs = GetCodePtr(); - for (int i = 2; i < ABI_GetNumXMMRegs(); i++) - MOVAPS(M(saved_fp_state + i * 16), (X64Reg)(XMM0 + i)); - STMXCSR(M(&saved_mxcsr)); -#ifdef _M_X64 - MOV(64, M(saved_gpr_state + 0 ), R(RCX)); - MOV(64, M(saved_gpr_state + 8 ), R(RDX)); - MOV(64, M(saved_gpr_state + 16), R(R8) ); - MOV(64, M(saved_gpr_state + 24), R(R9) ); - MOV(64, M(saved_gpr_state + 32), R(R10)); - MOV(64, M(saved_gpr_state + 40), R(R11)); -#ifndef _WIN32 - MOV(64, M(saved_gpr_state + 48), R(RSI)); - MOV(64, M(saved_gpr_state + 56), R(RDI)); -#endif - MOV(64, M(saved_gpr_state + 64), R(RBX)); -#else - MOV(32, M(saved_gpr_state + 0 ), R(RCX)); - MOV(32, M(saved_gpr_state + 4 ), R(RDX)); -#endif - RET(); - load_regs = GetCodePtr(); - LDMXCSR(M(&saved_mxcsr)); - for (int i = 2; i < ABI_GetNumXMMRegs(); i++) - MOVAPS((X64Reg)(XMM0 + i), M(saved_fp_state + i * 16)); -#ifdef _M_X64 - MOV(64, R(RCX), M(saved_gpr_state + 0 )); - MOV(64, R(RDX), M(saved_gpr_state + 8 )); - MOV(64, R(R8) , M(saved_gpr_state + 16)); - MOV(64, R(R9) , M(saved_gpr_state + 24)); - MOV(64, R(R10), M(saved_gpr_state + 32)); - MOV(64, R(R11), M(saved_gpr_state + 40)); -#ifndef _WIN32 - MOV(64, R(RSI), M(saved_gpr_state + 48)); - MOV(64, R(RDI), M(saved_gpr_state + 56)); -#endif - MOV(64, R(RBX), M(saved_gpr_state + 64)); -#else - MOV(32, R(RCX), M(saved_gpr_state + 0 )); - MOV(32, R(RDX), M(saved_gpr_state + 4 )); -#endif - RET(); -} - -void Thunk_Reset() -{ - thunks.clear(); - thunk_code = thunk_memory; -} - -void Thunk_Shutdown() -{ - Thunk_Reset(); - FreeMemoryPages(thunk_memory, THUNK_ARENA_SIZE); - thunk_memory = 0; - thunk_code = 0; -} - -void *ProtectFunction(void *function, int num_params) -{ - std::map::iterator iter; - iter = thunks.find(function); - if (iter != thunks.end()) - return (void *)iter->second; - - if (!thunk_memory) - PanicAlert("Trying to protect functions before the emu is started. Bad bad bad."); - - GenContext gen(&thunk_code); - const u8 *call_point = GetCodePtr(); - // Make sure to align stack. -#ifdef _M_X64 -#ifdef _WIN32 - SUB(64, R(ESP), Imm8(0x28)); -#else - SUB(64, R(ESP), Imm8(0x8)); -#endif - CALL((void*)save_regs); - CALL((void*)function); - CALL((void*)load_regs); -#ifdef _WIN32 - ADD(64, R(ESP), Imm8(0x28)); -#else - ADD(64, R(ESP), Imm8(0x8)); -#endif - RET(); -#else - CALL((void*)save_regs); - // Since parameters are in the previous stack frame, not in registers, this takes some - // trickery : we simply re-push the parameters. might not be optimal, but that doesn't really - // matter. - ABI_AlignStack(num_params * 4); - unsigned int alignedSize = ABI_GetAlignedFrameSize(num_params * 4); - for (int i = 0; i < num_params; i++) { - // ESP is changing, so we do not need i - PUSH(32, MDisp(ESP, alignedSize - 4)); - } - CALL(function); - ABI_RestoreStack(num_params * 4); - CALL((void*)load_regs); - RET(); -#endif - - thunks[function] = call_point; - return (void *)call_point; -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "Common.h" +#include "Thunk.h" +#include "x64Emitter.h" +#include "MemoryUtil.h" +#include "ABI.h" + +using namespace Gen; + +#define THUNK_ARENA_SIZE 1024*1024*1 + +namespace { +static std::map thunks; +u8 GC_ALIGNED32(saved_fp_state[16 * 4 * 4]); +u8 GC_ALIGNED32(saved_gpr_state[16 * 8]); + +static u8 *thunk_memory; +static u8 *thunk_code; +static const u8 *save_regs; +static const u8 *load_regs; +static u16 saved_mxcsr; +} + +void Thunk_Init() +{ + thunk_memory = (u8 *)AllocateExecutableMemory(THUNK_ARENA_SIZE); + thunk_code = thunk_memory; + + GenContext ctx(&thunk_code); + save_regs = GetCodePtr(); + for (int i = 2; i < ABI_GetNumXMMRegs(); i++) + MOVAPS(M(saved_fp_state + i * 16), (X64Reg)(XMM0 + i)); + STMXCSR(M(&saved_mxcsr)); +#ifdef _M_X64 + MOV(64, M(saved_gpr_state + 0 ), R(RCX)); + MOV(64, M(saved_gpr_state + 8 ), R(RDX)); + MOV(64, M(saved_gpr_state + 16), R(R8) ); + MOV(64, M(saved_gpr_state + 24), R(R9) ); + MOV(64, M(saved_gpr_state + 32), R(R10)); + MOV(64, M(saved_gpr_state + 40), R(R11)); +#ifndef _WIN32 + MOV(64, M(saved_gpr_state + 48), R(RSI)); + MOV(64, M(saved_gpr_state + 56), R(RDI)); +#endif + MOV(64, M(saved_gpr_state + 64), R(RBX)); +#else + MOV(32, M(saved_gpr_state + 0 ), R(RCX)); + MOV(32, M(saved_gpr_state + 4 ), R(RDX)); +#endif + RET(); + load_regs = GetCodePtr(); + LDMXCSR(M(&saved_mxcsr)); + for (int i = 2; i < ABI_GetNumXMMRegs(); i++) + MOVAPS((X64Reg)(XMM0 + i), M(saved_fp_state + i * 16)); +#ifdef _M_X64 + MOV(64, R(RCX), M(saved_gpr_state + 0 )); + MOV(64, R(RDX), M(saved_gpr_state + 8 )); + MOV(64, R(R8) , M(saved_gpr_state + 16)); + MOV(64, R(R9) , M(saved_gpr_state + 24)); + MOV(64, R(R10), M(saved_gpr_state + 32)); + MOV(64, R(R11), M(saved_gpr_state + 40)); +#ifndef _WIN32 + MOV(64, R(RSI), M(saved_gpr_state + 48)); + MOV(64, R(RDI), M(saved_gpr_state + 56)); +#endif + MOV(64, R(RBX), M(saved_gpr_state + 64)); +#else + MOV(32, R(RCX), M(saved_gpr_state + 0 )); + MOV(32, R(RDX), M(saved_gpr_state + 4 )); +#endif + RET(); +} + +void Thunk_Reset() +{ + thunks.clear(); + thunk_code = thunk_memory; +} + +void Thunk_Shutdown() +{ + Thunk_Reset(); + FreeMemoryPages(thunk_memory, THUNK_ARENA_SIZE); + thunk_memory = 0; + thunk_code = 0; +} + +void *ProtectFunction(void *function, int num_params) +{ + std::map::iterator iter; + iter = thunks.find(function); + if (iter != thunks.end()) + return (void *)iter->second; + + if (!thunk_memory) + PanicAlert("Trying to protect functions before the emu is started. Bad bad bad."); + + GenContext gen(&thunk_code); + const u8 *call_point = GetCodePtr(); + // Make sure to align stack. +#ifdef _M_X64 +#ifdef _WIN32 + SUB(64, R(ESP), Imm8(0x28)); +#else + SUB(64, R(ESP), Imm8(0x8)); +#endif + CALL((void*)save_regs); + CALL((void*)function); + CALL((void*)load_regs); +#ifdef _WIN32 + ADD(64, R(ESP), Imm8(0x28)); +#else + ADD(64, R(ESP), Imm8(0x8)); +#endif + RET(); +#else + CALL((void*)save_regs); + // Since parameters are in the previous stack frame, not in registers, this takes some + // trickery : we simply re-push the parameters. might not be optimal, but that doesn't really + // matter. + ABI_AlignStack(num_params * 4); + unsigned int alignedSize = ABI_GetAlignedFrameSize(num_params * 4); + for (int i = 0; i < num_params; i++) { + // ESP is changing, so we do not need i + PUSH(32, MDisp(ESP, alignedSize - 4)); + } + CALL(function); + ABI_RestoreStack(num_params * 4); + CALL((void*)load_regs); + RET(); +#endif + + thunks[function] = call_point; + return (void *)call_point; +} diff --git a/Source/Core/Common/Src/Timer.cpp b/Source/Core/Common/Src/Timer.cpp index 359c5f9039..152d664416 100644 --- a/Source/Core/Common/Src/Timer.cpp +++ b/Source/Core/Common/Src/Timer.cpp @@ -1,99 +1,99 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#ifdef _WIN32 -#include -#include -#endif - -#include - -#include "Common.h" -#include "Timer.h" - -#ifdef __GNUC__ -#include - -u32 timeGetTime() -{ - struct timeb t; - ftime(&t); - return((u32)(t.time * 1000 + t.millitm)); -} - - -#endif - - -namespace Common -{ -Timer::Timer(void) - : m_LastTime(0) -{ - Update(); - -#ifdef _WIN32 - QueryPerformanceFrequency((LARGE_INTEGER*)&m_frequency); -#endif -} - - -void Timer::Update(void) -{ - m_LastTime = timeGetTime(); - //TODO(ector) - QPF -} - - -s64 Timer::GetTimeDifference(void) -{ - return(timeGetTime() - m_LastTime); -} - - -void Timer::IncreaseResolution() -{ -#ifdef _WIN32 - timeBeginPeriod(1); -#endif -} - - -void Timer::RestoreResolution() -{ -#ifdef _WIN32 - timeEndPeriod(1); -#endif -} - - -#ifdef __GNUC__ -void _time64(u64* t) -{ - *t = 0; //TODO -} -#endif - - - -u64 Timer::GetTimeSinceJan1970(void) -{ - time_t ltime; - time(<ime); - return((u64)ltime); -} -} // end of namespace Common +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifdef _WIN32 +#include +#include +#endif + +#include + +#include "Common.h" +#include "Timer.h" + +#ifdef __GNUC__ +#include + +u32 timeGetTime() +{ + struct timeb t; + ftime(&t); + return((u32)(t.time * 1000 + t.millitm)); +} + + +#endif + + +namespace Common +{ +Timer::Timer(void) + : m_LastTime(0) +{ + Update(); + +#ifdef _WIN32 + QueryPerformanceFrequency((LARGE_INTEGER*)&m_frequency); +#endif +} + + +void Timer::Update(void) +{ + m_LastTime = timeGetTime(); + //TODO(ector) - QPF +} + + +s64 Timer::GetTimeDifference(void) +{ + return(timeGetTime() - m_LastTime); +} + + +void Timer::IncreaseResolution() +{ +#ifdef _WIN32 + timeBeginPeriod(1); +#endif +} + + +void Timer::RestoreResolution() +{ +#ifdef _WIN32 + timeEndPeriod(1); +#endif +} + + +#ifdef __GNUC__ +void _time64(u64* t) +{ + *t = 0; //TODO +} +#endif + + + +u64 Timer::GetTimeSinceJan1970(void) +{ + time_t ltime; + time(<ime); + return((u64)ltime); +} +} // end of namespace Common diff --git a/Source/Core/Common/Src/WaveFile.cpp b/Source/Core/Common/Src/WaveFile.cpp index 7cbeafa05a..be18ef8b73 100644 --- a/Source/Core/Common/Src/WaveFile.cpp +++ b/Source/Core/Common/Src/WaveFile.cpp @@ -1,123 +1,123 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "WaveFile.h" - -enum {BUF_SIZE = 32*1024}; - -WaveFileWriter::WaveFileWriter() -{ - conv_buffer = 0; - skip_silence = false; -} - -WaveFileWriter::~WaveFileWriter() -{ - delete [] conv_buffer; - Stop(); -} - -bool WaveFileWriter::Start(const char *filename) -{ - if (!conv_buffer) - conv_buffer = new short[BUF_SIZE]; - - if (file) - return false; - file = fopen(filename, "wb"); - if (!file) - return false; - - Write4("RIFF"); - Write(100 * 1000 * 1000); // write big value in case the file gets truncated - Write4("WAVE"); - Write4("fmt "); - Write(16); // size of fmt block - Write(0x00020001); //two channels, uncompressed - const u32 sample_rate = 32000; - Write(sample_rate); - Write(sample_rate * 2 * 2); //two channels, 16bit - Write(0x00100004); - Write4("data"); - Write(100 * 1000 * 1000 - 32); - // We are now at offset 44 - if (ftell(file) != 44) - PanicAlert("wrong offset: %i", ftell(file)); - - return true; -} - -void WaveFileWriter::Stop() -{ - if (!file) - return; - // u32 file_size = (u32)ftell(file); - fseek(file, 4, SEEK_SET); - Write(audio_size + 36); - fseek(file, 40, SEEK_SET); - Write(audio_size); - fclose(file); - file = 0; -} - -void WaveFileWriter::Write(u32 value) -{ - fwrite(&value, 4, 1, file); -} - -void WaveFileWriter::Write4(const char *ptr) -{ - fwrite(ptr, 4, 1, file); -} - -void WaveFileWriter::AddStereoSamples(const short *sample_data, int count) -{ - if (!file) - PanicAlert("WaveFileWriter - file not open."); - if (skip_silence) { - bool all_zero = true; - for (int i = 0; i < count * 2; i++) - if (sample_data[i]) - all_zero = false; - if (all_zero) - return; - } - fwrite(sample_data, count * 4, 1, file); - audio_size += count * 4; -} - -void WaveFileWriter::AddStereoSamplesBE(const short *sample_data, int count) -{ - if (!file) - PanicAlert("WaveFileWriter - file not open."); - if (count > BUF_SIZE * 2) - PanicAlert("WaveFileWriter - buffer too small (count = %i).", count); - if (skip_silence) { - bool all_zero = true; - for (int i = 0; i < count * 2; i++) - if (sample_data[i]) - all_zero = false; - if (all_zero) - return; - } - for (int i = 0; i < count * 2; i++) { - conv_buffer[i] = Common::swap16((u16)sample_data[i]); - } - fwrite(conv_buffer, count * 4, 1, file); - audio_size += count * 4; -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "WaveFile.h" + +enum {BUF_SIZE = 32*1024}; + +WaveFileWriter::WaveFileWriter() +{ + conv_buffer = 0; + skip_silence = false; +} + +WaveFileWriter::~WaveFileWriter() +{ + delete [] conv_buffer; + Stop(); +} + +bool WaveFileWriter::Start(const char *filename) +{ + if (!conv_buffer) + conv_buffer = new short[BUF_SIZE]; + + if (file) + return false; + file = fopen(filename, "wb"); + if (!file) + return false; + + Write4("RIFF"); + Write(100 * 1000 * 1000); // write big value in case the file gets truncated + Write4("WAVE"); + Write4("fmt "); + Write(16); // size of fmt block + Write(0x00020001); //two channels, uncompressed + const u32 sample_rate = 32000; + Write(sample_rate); + Write(sample_rate * 2 * 2); //two channels, 16bit + Write(0x00100004); + Write4("data"); + Write(100 * 1000 * 1000 - 32); + // We are now at offset 44 + if (ftell(file) != 44) + PanicAlert("wrong offset: %i", ftell(file)); + + return true; +} + +void WaveFileWriter::Stop() +{ + if (!file) + return; + // u32 file_size = (u32)ftell(file); + fseek(file, 4, SEEK_SET); + Write(audio_size + 36); + fseek(file, 40, SEEK_SET); + Write(audio_size); + fclose(file); + file = 0; +} + +void WaveFileWriter::Write(u32 value) +{ + fwrite(&value, 4, 1, file); +} + +void WaveFileWriter::Write4(const char *ptr) +{ + fwrite(ptr, 4, 1, file); +} + +void WaveFileWriter::AddStereoSamples(const short *sample_data, int count) +{ + if (!file) + PanicAlert("WaveFileWriter - file not open."); + if (skip_silence) { + bool all_zero = true; + for (int i = 0; i < count * 2; i++) + if (sample_data[i]) + all_zero = false; + if (all_zero) + return; + } + fwrite(sample_data, count * 4, 1, file); + audio_size += count * 4; +} + +void WaveFileWriter::AddStereoSamplesBE(const short *sample_data, int count) +{ + if (!file) + PanicAlert("WaveFileWriter - file not open."); + if (count > BUF_SIZE * 2) + PanicAlert("WaveFileWriter - buffer too small (count = %i).", count); + if (skip_silence) { + bool all_zero = true; + for (int i = 0; i < count * 2; i++) + if (sample_data[i]) + all_zero = false; + if (all_zero) + return; + } + for (int i = 0; i < count * 2; i++) { + conv_buffer[i] = Common::swap16((u16)sample_data[i]); + } + fwrite(conv_buffer, count * 4, 1, file); + audio_size += count * 4; +} diff --git a/Source/Core/Common/Src/stdafx.cpp b/Source/Core/Common/Src/stdafx.cpp index 428bdfaf8b..1dc71df3a9 100644 --- a/Source/Core/Common/Src/stdafx.cpp +++ b/Source/Core/Common/Src/stdafx.cpp @@ -1,18 +1,18 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "stdafx.h" +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "stdafx.h" diff --git a/Source/Core/Common/Src/x64Analyzer.cpp b/Source/Core/Common/Src/x64Analyzer.cpp index 00fb299c84..7c11f7c1f5 100644 --- a/Source/Core/Common/Src/x64Analyzer.cpp +++ b/Source/Core/Common/Src/x64Analyzer.cpp @@ -1,232 +1,232 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ -#include "x64Analyzer.h" - -bool DisassembleMov(const unsigned char *codePtr, InstructionInfo &info, int accessType) -{ - unsigned const char *startCodePtr = codePtr; - u8 rex = 0; - u8 codeByte = 0; - u8 codeByte2 = 0; - - //Check for regular prefix - info.operandSize = 4; - info.zeroExtend = false; - info.signExtend = false; - info.hasImmediate = false; - info.isMemoryWrite = false; - - int addressSize = 8; - u8 modRMbyte = 0; - u8 sibByte = 0; - bool hasModRM = false; - bool hasSIBbyte = false; - bool hasDisplacement = false; - - int displacementSize = 0; - - if (*codePtr == 0x66) - { - info.operandSize = 2; - codePtr++; - } - else if (*codePtr == 0x67) - { - addressSize = 4; - codePtr++; - } - - //Check for REX prefix - if ((*codePtr & 0xF0) == 0x40) - { - rex = *codePtr; - if (rex & 8) //REX.W - { - info.operandSize = 8; - } - codePtr++; - } - - codeByte = *codePtr++; - - // Skip two-byte opcode byte - bool twoByte = false; - if(codeByte == 0x0F) - { - twoByte = true; - codeByte2 = *codePtr++; - } - - if (!twoByte) - { - if ((codeByte & 0xF0) == 0x80 || - ((codeByte & 0xF8) == 0xC0 && (codeByte & 0x0E) != 0x02)) - { - modRMbyte = *codePtr++; - hasModRM = true; - } - } - else - { - if (((codeByte2 & 0xF0) == 0x00 && (codeByte2 & 0x0F) >= 0x04 && (codeByte2 & 0x0D) != 0x0D) || - (codeByte2 & 0xF0) == 0x30 || - codeByte2 == 0x77 || - (codeByte2 & 0xF0) == 0x80 || - ((codeByte2 & 0xF0) == 0xA0 && (codeByte2 & 0x07) <= 0x02) || - (codeByte2 & 0xF8) == 0xC8) - { - // No mod R/M byte - } - else - { - modRMbyte = *codePtr++; - hasModRM = true; - } - } - - if (hasModRM) - { - ModRM mrm(modRMbyte, rex); - info.regOperandReg = mrm.reg; - if (mrm.mod < 3) - { - if (mrm.rm == 4) - { - //SIB byte - sibByte = *codePtr++; - info.scaledReg = (sibByte >> 3) & 7; - info.otherReg = (sibByte & 7); - if (rex & 2) info.scaledReg += 8; - if (rex & 1) info.otherReg += 8; - hasSIBbyte = true; - } - else - { - //info.scaledReg = - } - } - if (mrm.mod == 1 || mrm.mod == 2) - { - hasDisplacement = true; - if (mrm.mod == 1) - displacementSize = 1; - else - displacementSize = 4; - } - } - - if (displacementSize == 1) - info.displacement = (s32)(s8)*codePtr; - else - info.displacement = *((s32 *)codePtr); - codePtr += displacementSize; - - - if (accessType == 1) - { - info.isMemoryWrite = true; - //Write access - switch (codeByte) - { - case 0xC6: //move 8-bit immediate - { - info.hasImmediate = true; - info.immediate = *codePtr; - codePtr++; //move past immediate - } - break; - - case 0xC7: //move 16 or 32-bit immediate, easiest case for writes - { - if (info.operandSize == 2) - { - info.hasImmediate = true; - info.immediate = *(u16*)codePtr; - codePtr += 2; - } - else if (info.operandSize == 4) - { - info.hasImmediate = true; - info.immediate = *(u32*)codePtr; - codePtr += 4; - } - else if (info.operandSize == 8) - { - info.zeroExtend = true; - info.immediate = *(u32*)codePtr; - codePtr += 4; - } - } - break; - case 0x89: //move reg to memory - break; - - default: - PanicAlert("Unhandled disasm case in write handler!\n\nPlease implement or avoid."); - return false; - } - } - else - { - // Memory read - - //mov eax, dword ptr [rax] == 8b 00 - switch (codeByte) - { - case 0x0F: - switch (codeByte2) - { - case 0xB6: //movzx on byte - info.zeroExtend = true; - info.operandSize = 1; - break; - case 0xB7: //movzx on short - info.zeroExtend = true; - info.operandSize = 2; - break; - case 0xBE: //movsx on byte - info.signExtend = true; - info.operandSize = 1; - break; - case 0xBF: - info.signExtend = true; - info.operandSize = 2; - break; - default: - return false; - } - break; - case 0x8a: - if (info.operandSize == 4) - { - info.operandSize = 1; - break; - } - else - return false; - case 0x8b: - break; //it's OK don't need to do anything - default: - return false; - } - } - info.instructionSize = (int)(codePtr - startCodePtr); - return true; -} - - - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +#include "x64Analyzer.h" + +bool DisassembleMov(const unsigned char *codePtr, InstructionInfo &info, int accessType) +{ + unsigned const char *startCodePtr = codePtr; + u8 rex = 0; + u8 codeByte = 0; + u8 codeByte2 = 0; + + //Check for regular prefix + info.operandSize = 4; + info.zeroExtend = false; + info.signExtend = false; + info.hasImmediate = false; + info.isMemoryWrite = false; + + int addressSize = 8; + u8 modRMbyte = 0; + u8 sibByte = 0; + bool hasModRM = false; + bool hasSIBbyte = false; + bool hasDisplacement = false; + + int displacementSize = 0; + + if (*codePtr == 0x66) + { + info.operandSize = 2; + codePtr++; + } + else if (*codePtr == 0x67) + { + addressSize = 4; + codePtr++; + } + + //Check for REX prefix + if ((*codePtr & 0xF0) == 0x40) + { + rex = *codePtr; + if (rex & 8) //REX.W + { + info.operandSize = 8; + } + codePtr++; + } + + codeByte = *codePtr++; + + // Skip two-byte opcode byte + bool twoByte = false; + if(codeByte == 0x0F) + { + twoByte = true; + codeByte2 = *codePtr++; + } + + if (!twoByte) + { + if ((codeByte & 0xF0) == 0x80 || + ((codeByte & 0xF8) == 0xC0 && (codeByte & 0x0E) != 0x02)) + { + modRMbyte = *codePtr++; + hasModRM = true; + } + } + else + { + if (((codeByte2 & 0xF0) == 0x00 && (codeByte2 & 0x0F) >= 0x04 && (codeByte2 & 0x0D) != 0x0D) || + (codeByte2 & 0xF0) == 0x30 || + codeByte2 == 0x77 || + (codeByte2 & 0xF0) == 0x80 || + ((codeByte2 & 0xF0) == 0xA0 && (codeByte2 & 0x07) <= 0x02) || + (codeByte2 & 0xF8) == 0xC8) + { + // No mod R/M byte + } + else + { + modRMbyte = *codePtr++; + hasModRM = true; + } + } + + if (hasModRM) + { + ModRM mrm(modRMbyte, rex); + info.regOperandReg = mrm.reg; + if (mrm.mod < 3) + { + if (mrm.rm == 4) + { + //SIB byte + sibByte = *codePtr++; + info.scaledReg = (sibByte >> 3) & 7; + info.otherReg = (sibByte & 7); + if (rex & 2) info.scaledReg += 8; + if (rex & 1) info.otherReg += 8; + hasSIBbyte = true; + } + else + { + //info.scaledReg = + } + } + if (mrm.mod == 1 || mrm.mod == 2) + { + hasDisplacement = true; + if (mrm.mod == 1) + displacementSize = 1; + else + displacementSize = 4; + } + } + + if (displacementSize == 1) + info.displacement = (s32)(s8)*codePtr; + else + info.displacement = *((s32 *)codePtr); + codePtr += displacementSize; + + + if (accessType == 1) + { + info.isMemoryWrite = true; + //Write access + switch (codeByte) + { + case 0xC6: //move 8-bit immediate + { + info.hasImmediate = true; + info.immediate = *codePtr; + codePtr++; //move past immediate + } + break; + + case 0xC7: //move 16 or 32-bit immediate, easiest case for writes + { + if (info.operandSize == 2) + { + info.hasImmediate = true; + info.immediate = *(u16*)codePtr; + codePtr += 2; + } + else if (info.operandSize == 4) + { + info.hasImmediate = true; + info.immediate = *(u32*)codePtr; + codePtr += 4; + } + else if (info.operandSize == 8) + { + info.zeroExtend = true; + info.immediate = *(u32*)codePtr; + codePtr += 4; + } + } + break; + case 0x89: //move reg to memory + break; + + default: + PanicAlert("Unhandled disasm case in write handler!\n\nPlease implement or avoid."); + return false; + } + } + else + { + // Memory read + + //mov eax, dword ptr [rax] == 8b 00 + switch (codeByte) + { + case 0x0F: + switch (codeByte2) + { + case 0xB6: //movzx on byte + info.zeroExtend = true; + info.operandSize = 1; + break; + case 0xB7: //movzx on short + info.zeroExtend = true; + info.operandSize = 2; + break; + case 0xBE: //movsx on byte + info.signExtend = true; + info.operandSize = 1; + break; + case 0xBF: + info.signExtend = true; + info.operandSize = 2; + break; + default: + return false; + } + break; + case 0x8a: + if (info.operandSize == 4) + { + info.operandSize = 1; + break; + } + else + return false; + case 0x8b: + break; //it's OK don't need to do anything + default: + return false; + } + } + info.instructionSize = (int)(codePtr - startCodePtr); + return true; +} + + + diff --git a/Source/Core/Common/Src/x64Emitter.cpp b/Source/Core/Common/Src/x64Emitter.cpp index e3adb2e54b..64ca290ca0 100644 --- a/Source/Core/Common/Src/x64Emitter.cpp +++ b/Source/Core/Common/Src/x64Emitter.cpp @@ -1,1499 +1,1499 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ -#include "Common.h" -#include "x64Emitter.h" -#include "ABI.h" -#include "CPUDetect.h" - -namespace Gen -{ - static u8 *code; - static bool enableBranchHints = false; - - void SetCodePtr(u8 *ptr) - { - code = ptr; - } - - const u8 *GetCodePtr() - { - return code; - } - - u8 *GetWritableCodePtr() - { - return code; - } - - void ReserveCodeSpace(int bytes) - { - for (int i = 0; i < bytes; i++) - *code++ = 0xCC; - } - - const u8 *AlignCode4() - { - int c = int((u64)code & 3); - if (c) - ReserveCodeSpace(4-c); - return code; - } - - const u8 *AlignCode16() - { - int c = int((u64)code & 15); - if (c) - ReserveCodeSpace(16-c); - return code; - } - - const u8 *AlignCodePage() - { - int c = int((u64)code & 4095); - if (c) - ReserveCodeSpace(4096-c); - return code; - } - - inline void Write8(u8 value) - { - *code++ = value; - } - - inline void Write16(u16 value) - { - *(u16*)code = value; - code += 2; - } - - inline void Write32(u32 value) - { - *(u32*)code = value; - code += 4; - } - - inline void Write64(u64 value) - { - *(u64*)code = value; - code += 8; - } - - void WriteModRM( int mod, int rm, int reg ) - { - Write8((u8)((mod << 6) | ((rm & 7) << 3) | (reg & 7))); - } - - void WriteSIB(int scale, int index, int base) - { - Write8((u8)((scale << 6) | ((index & 7) << 3) | (base & 7))); - } - - void OpArg::WriteRex(bool op64, int customOp) const - { -#ifdef _M_X64 - u8 op = 0x40; - if (customOp == -1) customOp = operandReg; - if (op64) op |= 8; - if (customOp >> 3) op |= 4; - if (indexReg >> 3) op |= 2; - if (offsetOrBaseReg >> 3) op |= 1; //TODO investigate if this is dangerous - if (op != 0x40) - Write8(op); -#else - _dbg_assert_(DYNA_REC, (operandReg >> 3) == 0); - _dbg_assert_(DYNA_REC, (indexReg >> 3) == 0); - _dbg_assert_(DYNA_REC, (offsetOrBaseReg >> 3) == 0); -#endif - } - - void OpArg::WriteRest(int extraBytes, X64Reg _operandReg) const - { - if (_operandReg == 0xff) - _operandReg = (X64Reg)this->operandReg; - int mod = 0; - int ireg = indexReg; - bool SIB = false; - int _offsetOrBaseReg = this->offsetOrBaseReg; - - if (scale == SCALE_RIP) //Also, on 32-bit, just an immediate address - { - // Oh, RIP addressing. - _offsetOrBaseReg = 5; - WriteModRM(0, _operandReg&7, 5); - //TODO : add some checks -#ifdef _M_X64 - u64 ripAddr = (u64)code + 4 + extraBytes; - s32 offs = (s32)((s64)offset - (s64)ripAddr); - Write32((u32)offs); -#else - Write32((u32)offset); -#endif - return; - } - - if (scale == 0) - { - // Oh, no memory, Just a reg. - mod = 3; //11 - } - else if (scale >= 1) - { - //Ah good, no scaling. - if (scale == SCALE_ATREG && !((_offsetOrBaseReg&7) == 4 || (_offsetOrBaseReg&7) == 5)) - { - //Okay, we're good. No SIB necessary. - int ioff = (int)offset; - if (ioff == 0) - { - mod = 0; - } - else if (ioff<-128 || ioff>127) - { - mod = 2; //32-bit displacement - } - else - { - mod = 1; //8-bit displacement - } - } - else //if (scale != SCALE_ATREG) - { - if ((_offsetOrBaseReg & 7) == 4) //this would occupy the SIB encoding :( - { - //So we have to fake it with SIB encoding :( - SIB = true; - } - - if (scale >= SCALE_1 && scale < SCALE_ATREG) - { - SIB = true; - } - - if (scale == SCALE_ATREG && _offsetOrBaseReg == 4) - { - SIB = true; - ireg = 4; - } - - //Okay, we're fine. Just disp encoding. - //We need displacement. Which size? - int ioff = (int)(s64)offset; - if (ioff < -128 || ioff > 127) - { - mod = 2; //32-bit displacement - } - else - { - mod = 1; //8-bit displacement - } - } - } - - // Okay. Time to do the actual writing - // ModRM byte: - int oreg = _offsetOrBaseReg; - if (SIB) - oreg = 4; - - // TODO(ector): WTF is this if about? I don't remember writing it :-) - //if (RIP) - // oreg = 5; - - WriteModRM(mod, _operandReg&7, oreg&7); - - if (SIB) - { - //SIB byte - int ss; - switch (scale) - { - case 0: _offsetOrBaseReg = 4; ss = 0; break; //RSP - case 1: ss = 0; break; - case 2: ss = 1; break; - case 4: ss = 2; break; - case 8: ss = 3; break; - case SCALE_ATREG: ss = 0; break; - default: _assert_msg_(DYNA_REC, 0, "Invalid scale for SIB byte"); ss = 0; break; - } - Write8((u8)((ss << 6) | ((ireg&7)<<3) | (_offsetOrBaseReg&7))); - } - - if (mod == 1) //8-bit disp - { - Write8((u8)(s8)(s32)offset); - } - else if (mod == 2) //32-bit disp - { - Write32((u32)offset); - } - } - - - // W = operand extended width (1 if 64-bit) - // R = register# upper bit - // X = scale amnt upper bit - // B = base register# upper bit - void Rex(int w, int r, int x, int b) - { - w = w ? 1 : 0; - r = r ? 1 : 0; - x = x ? 1 : 0; - b = b ? 1 : 0; - u8 rx = (u8)(0x40 | (w << 3) | (r << 2) | (x << 1) | (b)); - if (rx != 0x40) - Write8(rx); - } - - void JMP(const u8 *addr, bool force5Bytes) - { - u64 fn = (u64)addr; - if (!force5Bytes) - { - s32 distance = (s32)(fn - ((u64)code + 2)); //TODO - sanity check - //8 bits will do - Write8(0xEB); - Write8((u8)(s8)distance); - } - else - { - s32 distance = (s32)(fn - ((u64)code + 5)); //TODO - sanity check - Write8(0xE9); - Write32((u32)(s32)distance); - } - } - - void JMPptr(const OpArg &arg2) - { - OpArg arg = arg2; - if (arg.IsImm()) _assert_msg_(DYNA_REC, 0, "JMPptr - Imm argument"); - arg.operandReg = 4; - arg.WriteRex(false); - Write8(0xFF); - arg.WriteRest(); - } - - //Can be used to trap other processors, before overwriting their code - // not used in dolphin - void JMPself() - { - Write8(0xEB); - Write8(0xFE); - } - - void CALLptr(OpArg arg) - { - if (arg.IsImm()) _assert_msg_(DYNA_REC, 0, "CALLptr - Imm argument"); - arg.operandReg = 2; - arg.WriteRex(false); - Write8(0xFF); - arg.WriteRest(); - } - - void CALL(void *fnptr) - { - u64 distance = u64(fnptr) - (u64(code) + 5); - if (distance >= 0x0000000080000000ULL - && distance < 0xFFFFFFFF80000000ULL) { - PanicAlert("CALL out of range (%p calls %p)", code, fnptr); - } - Write8(0xE8); - Write32(u32(distance)); - } - - FixupBranch J(bool force5bytes) - { - FixupBranch branch; - branch.type = force5bytes ? 1 : 0; - branch.ptr = code + (force5bytes ? 5 : 2); - if (!force5bytes) - { - //8 bits will do - Write8(0xEB); - Write8(0); - } - else - { - Write8(0xE9); - Write32(0); - } - return branch; - } - - // These are to be used with Jcc only. - // Found in intel manual 2A - // These do not really make a difference for any current X86 CPU, - // but are provided here for future use - void HINT_NOT_TAKEN() { if (enableBranchHints) Write8(0x2E); } - void HINT_TAKEN() { if (enableBranchHints) Write8(0x3E); } - - FixupBranch J_CC(CCFlags conditionCode, bool force5bytes) - { - FixupBranch branch; - branch.type = force5bytes ? 1 : 0; - branch.ptr = code + (force5bytes ? 5 : 2); - if (!force5bytes) - { - //8 bits will do - Write8(0x70 + conditionCode); - Write8(0); - } - else - { - Write8(0x0F); - Write8(0x80 + conditionCode); - Write32(0); - } - return branch; - } - - void J_CC(CCFlags conditionCode, const u8 * addr, bool force5Bytes) - { - u64 fn = (u64)addr; - if (!force5Bytes) - { - s32 distance = (s32)(fn - ((u64)code + 2)); //TODO - sanity check - //8 bits will do - Write8(0x70 + conditionCode); - Write8((u8)(s8)distance); - } - else - { - s32 distance = (s32)(fn - ((u64)code + 6)); //TODO - sanity check - Write8(0x0F); - Write8(0x80 + conditionCode); - Write32((u32)(s32)distance); - } - } - - void SetJumpTarget(const FixupBranch &branch) - { - if (branch.type == 0) - { - branch.ptr[-1] = (u8)(s8)(code - branch.ptr); - } - else if (branch.type == 1) - { - ((s32*)branch.ptr)[-1] = (s32)(code - branch.ptr); - } - } - -/* - void INC(int bits, OpArg arg) - { - if (arg.IsImm()) _assert_msg_(DYNA_REC, 0, "INC - Imm argument"); - arg.operandReg = 0; - if (bits == 16) {Write8(0x66);} - arg.WriteRex(bits == 64); - Write8(bits == 8 ? 0xFE : 0xFF); - arg.WriteRest(); - } - void DEC(int bits, OpArg arg) - { - if (arg.IsImm()) _assert_msg_(DYNA_REC, 0, "DEC - Imm argument"); - arg.operandReg = 1; - if (bits == 16) {Write8(0x66);} - arg.WriteRex(bits == 64); - Write8(bits == 8 ? 0xFE : 0xFF); - arg.WriteRest(); - } -*/ - - //Single byte opcodes - //There is no PUSHAD/POPAD in 64-bit mode. - void INT3() {Write8(0xCC);} - void RET() {Write8(0xC3);} - void RET_FAST() {Write8(0xF3); Write8(0xC3);} //two-byte return (rep ret) - recommended by AMD optimization manual for the case of jumping to a ret - - void NOP(int count) - { - // TODO: look up the fastest nop sleds for various sizes - int i; - switch (count) { - case 1: - Write8(0x90); - break; - case 2: - Write8(0x66); - Write8(0x90); - break; - default: - for (i = 0; i < count; i++) { - Write8(0x90); - } - break; - } - } - - void PAUSE() {Write8(0xF3); NOP();} //use in tight spinloops for energy saving on some cpu - void CLC() {Write8(0xF8);} //clear carry - void CMC() {Write8(0xF5);} //flip carry - void STC() {Write8(0xF9);} //set carry - - //TODO: xchg ah, al ??? - void XCHG_AHAL() - { - Write8(0x86); - Write8(0xe0); - // alt. 86 c4 - } - - //These two can not be executed on early Intel 64-bit CPU:s, only on AMD! - void LAHF() {Write8(0x9F);} - void SAHF() {Write8(0x9E);} - - void PUSHF() {Write8(0x9C);} - void POPF() {Write8(0x9D);} - - void LFENCE() {Write8(0x0F); Write8(0xAE); Write8(0xE8);} - void MFENCE() {Write8(0x0F); Write8(0xAE); Write8(0xF0);} - void SFENCE() {Write8(0x0F); Write8(0xAE); Write8(0xF8);} - - void WriteSimple1Byte(int bits, u8 byte, X64Reg reg) - { - if (bits == 16) {Write8(0x66);} - Rex(bits == 64, 0, 0, (int)reg >> 3); - Write8(byte + ((int)reg & 7)); - } - - void WriteSimple2Byte(int bits, u8 byte1, u8 byte2, X64Reg reg) - { - if (bits == 16) {Write8(0x66);} - Rex(bits==64, 0, 0, (int)reg >> 3); - Write8(byte1); - Write8(byte2 + ((int)reg & 7)); - } - - void CWD(int bits) - { - if (bits == 16) {Write8(0x66);} - Rex(bits == 64, 0, 0, 0); - Write8(0x99); - } - - void CBW(int bits) - { - if (bits == 8) {Write8(0x66);} - Rex(bits == 32, 0, 0, 0); - Write8(0x98); - } - - //Simple opcodes - - - //push/pop do not need wide to be 64-bit - void PUSH(X64Reg reg) {WriteSimple1Byte(32, 0x50, reg);} - void POP(X64Reg reg) {WriteSimple1Byte(32, 0x58, reg);} - - void PUSH(int bits, const OpArg ®) - { - if (reg.IsSimpleReg()) - PUSH(reg.GetSimpleReg()); - else if (reg.IsImm()) - { - switch (reg.GetImmBits()) - { - case 8: - Write8(0x6A); - Write8((u8)(s8)reg.offset); - break; - case 16: - Write8(0x66); - Write8(0x68); - Write16((u16)(s16)(s32)reg.offset); - break; - case 32: - Write8(0x68); - Write32((u32)reg.offset); - break; - default: - _assert_msg_(DYNA_REC, 0, "PUSH - Bad imm bits"); - break; - } - } - else - { - //INT3(); - if (bits == 16) - Write8(0x66); - reg.WriteRex(bits == 64); - Write8(0xFF); - reg.WriteRest(0,(X64Reg)6); - } - } - - void POP(int /*bits*/, const OpArg ®) - { - if (reg.IsSimpleReg()) - POP(reg.GetSimpleReg()); - else - INT3(); - } - - void BSWAP(int bits, X64Reg reg) - { - if (bits >= 32) - { - WriteSimple2Byte(bits, 0x0F, 0xC8, reg); - } - else if (bits == 16) - { - //fake 16-bit bswap, TODO replace with xchg ah, al where appropriate - WriteSimple2Byte(false, 0x0F, 0xC8, reg); - SHR(32, R(reg), Imm8(16)); - } - else if (bits == 8) - { - - } - else - { - _assert_msg_(DYNA_REC, 0, "BSWAP - Wrong number of bits"); - } - } - - // Undefined opcode - reserved - // If we ever need a way to always cause a non-breakpoint hard exception... - void UD2() - { - Write8(0x0F); - Write8(0x0B); - } - - void PREFETCH(PrefetchLevel level, OpArg arg) - { - if (arg.IsImm()) _assert_msg_(DYNA_REC, 0, "PREFETCH - Imm argument");; - arg.operandReg = (u8)level; - arg.WriteRex(false); - Write8(0x0F); - Write8(0x18); - arg.WriteRest(); - } - - void SETcc(CCFlags flag, OpArg dest) - { - if (dest.IsImm()) _assert_msg_(DYNA_REC, 0, "SETcc - Imm argument"); - dest.operandReg = 0; - dest.WriteRex(false); - Write8(0x0F); - Write8(0x90 + (u8)flag); - dest.WriteRest(); - } - - void CMOVcc(int bits, X64Reg dest, OpArg src, CCFlags flag) - { - if (src.IsImm()) _assert_msg_(DYNA_REC, 0, "CMOVcc - Imm argument"); - src.operandReg = dest; - src.WriteRex(bits == 64); - Write8(0x0F); - Write8(0x40 + (u8)flag); - src.WriteRest(); - } - - void WriteMulDivType(int bits, OpArg src, int ext) - { - if (src.IsImm()) _assert_msg_(DYNA_REC, 0, "WriteMulDivType - Imm argument"); - src.operandReg = ext; - if (bits == 16) Write8(0x66); - src.WriteRex(bits == 64); - if (bits == 8) - { - Write8(0xF6); - } - else - { - Write8(0xF7); - } - src.WriteRest(); - } - - void MUL(int bits, OpArg src) {WriteMulDivType(bits, src, 4);} - void DIV(int bits, OpArg src) {WriteMulDivType(bits, src, 6);} - void IMUL(int bits, OpArg src) {WriteMulDivType(bits, src, 5);} - void IDIV(int bits, OpArg src) {WriteMulDivType(bits, src, 7);} - void NEG(int bits, OpArg src) {WriteMulDivType(bits, src, 3);} - void NOT(int bits, OpArg src) {WriteMulDivType(bits, src, 2);} - - void WriteBitSearchType(int bits, X64Reg dest, OpArg src, u8 byte2) - { - if (src.IsImm()) _assert_msg_(DYNA_REC, 0, "WriteBitSearchType - Imm argument"); - src.operandReg = (u8)dest; - if (bits == 16) Write8(0x66); - src.WriteRex(bits == 64); - Write8(0x0F); - Write8(byte2); - src.WriteRest(); - } - - void MOVNTI(int bits, OpArg dest, X64Reg src) - { - if (bits <= 16) _assert_msg_(DYNA_REC, 0, "MOVNTI - bits<=16"); - WriteBitSearchType(bits, src, dest, 0xC3); - } - - void BSF(int bits, X64Reg dest, OpArg src) {WriteBitSearchType(bits,dest,src,0xBC);} //bottom bit to top bit - void BSR(int bits, X64Reg dest, OpArg src) {WriteBitSearchType(bits,dest,src,0xBD);} //top bit to bottom bit - - void MOVSX(int dbits, int sbits, X64Reg dest, OpArg src) - { - if (src.IsImm()) _assert_msg_(DYNA_REC, 0, "MOVSX - Imm argument"); - if (dbits == sbits) { - MOV(dbits, R(dest), src); - return; - } - src.operandReg = (u8)dest; - if (dbits == 16) Write8(0x66); - src.WriteRex(dbits == 64); - if (sbits == 8) - { - Write8(0x0F); - Write8(0xBE); - } - else if (sbits == 16) - { - Write8(0x0F); - Write8(0xBF); - } - else if (sbits == 32 && dbits == 64) - { - Write8(0x63); - } - else - { - Crash(); - } - src.WriteRest(); - } - - void MOVZX(int dbits, int sbits, X64Reg dest, OpArg src) - { - if (src.IsImm()) _assert_msg_(DYNA_REC, 0, "MOVZX - Imm argument"); - if (dbits == sbits) { - MOV(dbits, R(dest), src); - return; - } - src.operandReg = (u8)dest; - if (dbits == 16) Write8(0x66); - src.WriteRex(dbits == 64); - if (sbits == 8) - { - Write8(0x0F); - Write8(0xB6); - } - else if (sbits == 16) - { - Write8(0x0F); - Write8(0xB7); - } - else - { - Crash(); - } - src.WriteRest(); - } - - - void LEA(int bits, X64Reg dest, OpArg src) - { - if (src.IsImm()) _assert_msg_(DYNA_REC, 0, "LEA - Imm argument"); - src.operandReg = (u8)dest; - if (bits == 16) Write8(0x66); //TODO: performance warning - src.WriteRex(bits == 64); - Write8(0x8D); - src.WriteRest(); - } - - //shift can be either imm8 or cl - void WriteShift(int bits, OpArg dest, OpArg &shift, int ext) - { - bool writeImm = false; - if (dest.IsImm()) - { - _assert_msg_(DYNA_REC, 0, "WriteShift - can't shift imms"); - } - if ((shift.IsSimpleReg() && shift.GetSimpleReg() != ECX) || (shift.IsImm() && shift.GetImmBits() != 8)) - { - _assert_msg_(DYNA_REC, 0, "WriteShift - illegal argument"); - } - dest.operandReg = ext; - if (bits == 16) Write8(0x66); - dest.WriteRex(bits == 64); - if (shift.GetImmBits() == 8) - { - //ok an imm - u8 imm = (u8)shift.offset; - if (imm == 1) - { - Write8(bits == 8 ? 0xD0 : 0xD1); - } - else - { - writeImm = true; - Write8(bits == 8 ? 0xC0 : 0xC1); - } - } - else - { - Write8(bits == 8 ? 0xD2 : 0xD3); - } - dest.WriteRest(writeImm ? 1 : 0); - if (writeImm) - Write8((u8)shift.offset); - } - - // large rotates and shift are slower on intel than amd - // intel likes to rotate by 1, and the op is smaller too - void ROL(int bits, OpArg dest, OpArg shift) {WriteShift(bits, dest, shift, 0);} - void ROR(int bits, OpArg dest, OpArg shift) {WriteShift(bits, dest, shift, 1);} - void RCL(int bits, OpArg dest, OpArg shift) {WriteShift(bits, dest, shift, 2);} - void RCR(int bits, OpArg dest, OpArg shift) {WriteShift(bits, dest, shift, 3);} - void SHL(int bits, OpArg dest, OpArg shift) {WriteShift(bits, dest, shift, 4);} - void SHR(int bits, OpArg dest, OpArg shift) {WriteShift(bits, dest, shift, 5);} - void SAR(int bits, OpArg dest, OpArg shift) {WriteShift(bits, dest, shift, 7);} - - void OpArg::WriteSingleByteOp(u8 op, X64Reg _operandReg, int bits) - { - if (bits == 16) - Write8(0x66); - - this->operandReg = (u8)_operandReg; - WriteRex(bits == 64); - Write8(op); - WriteRest(); - } - - //todo : add eax special casing - struct NormalOpDef - { - u8 toRm8, toRm32, fromRm8, fromRm32, imm8, imm32, simm8, ext; - }; - - const NormalOpDef nops[11] = - { - {0x00, 0x01, 0x02, 0x03, 0x80, 0x81, 0x83, 0}, //ADD - {0x10, 0x11, 0x12, 0x13, 0x80, 0x81, 0x83, 2}, //ADC - - {0x28, 0x29, 0x2A, 0x2B, 0x80, 0x81, 0x83, 5}, //SUB - {0x18, 0x19, 0x1A, 0x1B, 0x80, 0x81, 0x83, 3}, //SBB - - {0x20, 0x21, 0x22, 0x23, 0x80, 0x81, 0x83, 4}, //AND - {0x08, 0x09, 0x0A, 0x0B, 0x80, 0x81, 0x83, 1}, //OR - - {0x30, 0x31, 0x32, 0x33, 0x80, 0x81, 0x83, 6}, //XOR - {0x88, 0x89, 0x8A, 0x8B, 0xC6, 0xC7, 0xCC, 0}, //MOV - - {0x84, 0x85, 0x84, 0x85, 0xF6, 0xF7, 0xCC, 0}, //TEST (to == from) - {0x38, 0x39, 0x3A, 0x3B, 0x80, 0x81, 0x83, 7}, //CMP - - {0x86, 0x87, 0x86, 0x87, 0xCC, 0xCC, 0xCC, 7}, //XCHG - }; - - //operand can either be immediate or register - void OpArg::WriteNormalOp(bool toRM, NormalOp op, const OpArg &operand, int bits) const - { - X64Reg _operandReg = (X64Reg)this->operandReg; - if (IsImm()) - { - _assert_msg_(DYNA_REC, 0, "WriteNormalOp - Imm argument, wrong order"); - } - - if (bits == 16) - Write8(0x66); - - int immToWrite = 0; - - if (operand.IsImm()) - { - _operandReg = (X64Reg)0; - WriteRex(bits == 64); - - if (!toRM) - { - _assert_msg_(DYNA_REC, 0, "WriteNormalOp - Writing to Imm (!toRM)"); - } - - if (operand.scale == SCALE_IMM8 && bits == 8) - { - Write8(nops[op].imm8); - _assert_msg_(DYNA_REC, code[-1] != 0xCC, "ARGH1"); - immToWrite = 8; - } - else if ((operand.scale == SCALE_IMM16 && bits == 16) || - (operand.scale == SCALE_IMM32 && bits == 32) || - (operand.scale == SCALE_IMM32 && bits == 64)) - { - Write8(nops[op].imm32); - _assert_msg_(DYNA_REC, code[-1] != 0xCC, "ARGH2"); - immToWrite = 32; - } - else if ((operand.scale == SCALE_IMM8 && bits == 16) || - (operand.scale == SCALE_IMM8 && bits == 32) || - (operand.scale == SCALE_IMM8 && bits == 64)) - { - Write8(nops[op].simm8); - _assert_msg_(DYNA_REC, code[-1] != 0xCC, "ARGH3"); - immToWrite = 8; - } - else if (operand.scale == SCALE_IMM64 && bits == 64) - { - if (op == nrmMOV) - { - Write8(0xB8 + (offsetOrBaseReg & 7)); - Write64((u64)operand.offset); - return; - } - _assert_msg_(DYNA_REC, 0, "WriteNormalOp - Only MOV can take 64-bit imm"); - } - else - { - _assert_msg_(DYNA_REC, 0, "WriteNormalOp - Unhandled case"); - } - _operandReg = (X64Reg)nops[op].ext; //pass extension in REG of ModRM - } - else - { - _operandReg = (X64Reg)operand.offsetOrBaseReg; - WriteRex(bits == 64, _operandReg); - // mem/reg or reg/reg op - if (toRM) - { - Write8(bits == 8 ? nops[op].toRm8 : nops[op].toRm32); - _assert_msg_(DYNA_REC, code[-1] != 0xCC, "ARGH4"); - } - else - { - Write8(bits == 8 ? nops[op].fromRm8 : nops[op].fromRm32); - _assert_msg_(DYNA_REC, code[-1] != 0xCC, "ARGH5"); - } - } - WriteRest(immToWrite>>3, _operandReg); - switch (immToWrite) - { - case 0: - break; - case 8: - Write8((u8)operand.offset); - break; - case 32: - Write32((u32)operand.offset); - break; - default: - _assert_msg_(DYNA_REC, 0, "WriteNormalOp - Unhandled case"); - } - } - - void WriteNormalOp(int bits, NormalOp op, const OpArg &a1, const OpArg &a2) - { - if (a1.IsImm()) - { - //Booh! Can't write to an imm - _assert_msg_(DYNA_REC, 0, "WriteNormalOp - a1 cannot be imm"); - return; - } - if (a2.IsImm()) - { - a1.WriteNormalOp(true, op, a2, bits); - } - else - { - if (a1.IsSimpleReg()) - { - a2.WriteNormalOp(false, op, a1, bits); - } - else - { - a1.WriteNormalOp(true, op, a2, bits); - } - } - } - - void ADD (int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(bits, nrmADD, a1, a2);} - void ADC (int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(bits, nrmADC, a1, a2);} - void SUB (int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(bits, nrmSUB, a1, a2);} - void SBB (int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(bits, nrmSBB, a1, a2);} - void AND (int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(bits, nrmAND, a1, a2);} - void OR (int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(bits, nrmOR , a1, a2);} - void XOR (int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(bits, nrmXOR, a1, a2);} - void MOV (int bits, const OpArg &a1, const OpArg &a2) - { - _assert_msg_(DYNA_REC, !a1.IsSimpleReg() || !a2.IsSimpleReg() || a1.GetSimpleReg() != a2.GetSimpleReg(), "Redundant MOV @ %p", - code); - WriteNormalOp(bits, nrmMOV, a1, a2); - } - void TEST(int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(bits, nrmTEST, a1, a2);} - void CMP (int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(bits, nrmCMP, a1, a2);} - void XCHG(int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(bits, nrmXCHG, a1, a2);} - - void IMUL(int bits, X64Reg regOp, OpArg a1, OpArg a2) - { - if (bits == 8) { - _assert_msg_(DYNA_REC, 0, "IMUL - illegal bit size!"); - return; - } - if (a1.IsImm()) { - _assert_msg_(DYNA_REC, 0, "IMUL - second arg cannot be imm!"); - return; - } - if (!a2.IsImm()) - { - _assert_msg_(DYNA_REC, 0, "IMUL - third arg must be imm!"); - return; - } - - if (bits == 16) - Write8(0x66); - a1.WriteRex(bits == 64, regOp); - - if (a2.GetImmBits() == 8) { - Write8(0x6B); - a1.WriteRest(1, regOp); - Write8((u8)a2.offset); - } else { - Write8(0x69); - if (a2.GetImmBits() == 16 && bits == 16) { - a1.WriteRest(2, regOp); - Write16((u16)a2.offset); - } else if (a2.GetImmBits() == 32 && - (bits == 32 || bits == 64)) { - a1.WriteRest(4, regOp); - Write32((u32)a2.offset); - } else { - _assert_msg_(DYNA_REC, 0, "IMUL - unhandled case!"); - } - } - } - - void IMUL(int bits, X64Reg regOp, OpArg a) - { - if (bits == 8) { - _assert_msg_(DYNA_REC, 0, "IMUL - illegal bit size!"); - return; - } - if (a.IsImm()) - { - IMUL(bits, regOp, R(regOp), a) ; - return ; - } - - if (bits == 16) - Write8(0x66); - a.WriteRex(bits == 64, regOp); - Write8(0x0F); - Write8(0xAF); - a.WriteRest(0, regOp); - } - - - void WriteSSEOp(int size, u8 sseOp, bool packed, X64Reg regOp, OpArg arg, int extrabytes = 0) - { - if (size == 64 && packed) - Write8(0x66); //this time, override goes upwards - if (!packed) - Write8(size == 64 ? 0xF2 : 0xF3); - arg.operandReg = regOp; - arg.WriteRex(false); - Write8(0x0F); - Write8(sseOp); - arg.WriteRest(extrabytes); - } - - void MOVD_xmm(X64Reg dest, const OpArg &arg) {WriteSSEOp(64, 0x6E, true, dest, arg, 0);} - - void MOVQ_xmm(X64Reg dest, OpArg arg) { -#ifdef _M_X64 - // Alternate encoding - // This does not display correctly in MSVC's debugger, it thinks it's a MOVD - arg.operandReg = dest; - Write8(0x66); - arg.WriteRex(true); - Write8(0x0f); - Write8(0x6E); - arg.WriteRest(0); -#else - arg.operandReg = dest; - Write8(0xF3); - Write8(0x0f); - Write8(0x7E); - arg.WriteRest(0); -#endif - } - - void MOVD_xmm(const OpArg &arg, X64Reg src) {WriteSSEOp(64, 0x7E, true, src, arg, 0);} - void MOVQ_xmm(OpArg arg, X64Reg src) { - if (src > 7) - { - // Alternate encoding - // This does not display correctly in MSVC's debugger, it thinks it's a MOVD - arg.operandReg = src; - Write8(0x66); - arg.WriteRex(true); - Write8(0x0f); - Write8(0x7E); - arg.WriteRest(0); - } else { - // INT3(); - arg.operandReg = src; - arg.WriteRex(false); - Write8(0x66); - Write8(0x0f); - Write8(0xD6); - arg.WriteRest(0); - } - } - - - - void WriteMXCSR(OpArg arg, int ext) - { - if (arg.IsImm() || arg.IsSimpleReg()) - _assert_msg_(DYNA_REC, 0, "MXCSR - invalid operand"); - - arg.operandReg = ext; - arg.WriteRex(false); - Write8(0x0F); - Write8(0xAE); - arg.WriteRest(); - } - - enum NormalSSEOps - { - sseCMP = 0xC2, - sseADD = 0x58, //ADD - sseSUB = 0x5C, //SUB - sseAND = 0x54, //AND - sseANDN = 0x55, //ANDN - sseOR = 0x56, - sseXOR = 0x57, - sseMUL = 0x59, //MUL, - sseDIV = 0x5E, //DIV - sseMIN = 0x5D, //MIN - sseMAX = 0x5F, //MAX - sseCOMIS = 0x2F, //COMIS - sseUCOMIS = 0x2E, //UCOMIS - sseSQRT = 0x51, //SQRT - sseRSQRT = 0x52, //RSQRT (NO DOUBLE PRECISION!!!) - sseMOVAPfromRM = 0x28, //MOVAP from RM - sseMOVAPtoRM = 0x29, //MOVAP to RM - sseMOVUPfromRM = 0x10, //MOVUP from RM - sseMOVUPtoRM = 0x11, //MOVUP to RM - sseMASKMOVDQU = 0xF7, - sseLDDQU = 0xF0, - sseSHUF = 0xC6, - sseMOVNTDQ = 0xE7, - sseMOVNTP = 0x2B, - }; - - void STMXCSR(OpArg memloc) {WriteMXCSR(memloc, 3);} - void LDMXCSR(OpArg memloc) {WriteMXCSR(memloc, 2);} - - void MOVNTDQ(OpArg arg, X64Reg regOp) {WriteSSEOp(64, sseMOVNTDQ, true, regOp, arg);} - void MOVNTPS(OpArg arg, X64Reg regOp) {WriteSSEOp(32, sseMOVNTP, true, regOp, arg);} - void MOVNTPD(OpArg arg, X64Reg regOp) {WriteSSEOp(64, sseMOVNTP, true, regOp, arg);} - - void ADDSS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseADD, false, regOp, arg);} - void ADDSD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseADD, false, regOp, arg);} - void SUBSS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseSUB, false, regOp, arg);} - void SUBSD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseSUB, false, regOp, arg);} - void CMPSS(X64Reg regOp, OpArg arg, u8 compare) {WriteSSEOp(32, sseCMP, false, regOp, arg,1); Write8(compare);} - void CMPSD(X64Reg regOp, OpArg arg, u8 compare) {WriteSSEOp(64, sseCMP, false, regOp, arg,1); Write8(compare);} - void MULSS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseMUL, false, regOp, arg);} - void MULSD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseMUL, false, regOp, arg);} - void DIVSS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseDIV, false, regOp, arg);} - void DIVSD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseDIV, false, regOp, arg);} - void MINSS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseMIN, false, regOp, arg);} - void MINSD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseMIN, false, regOp, arg);} - void MAXSS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseMAX, false, regOp, arg);} - void MAXSD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseMAX, false, regOp, arg);} - void SQRTSS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseSQRT, false, regOp, arg);} - void SQRTSD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseSQRT, false, regOp, arg);} - void RSQRTSS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseRSQRT, false, regOp, arg);} - void RSQRTSD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseRSQRT, false, regOp, arg);} - - void ADDPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseADD, true, regOp, arg);} - void ADDPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseADD, true, regOp, arg);} - void SUBPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseSUB, true, regOp, arg);} - void SUBPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseSUB, true, regOp, arg);} - void CMPPS(X64Reg regOp, OpArg arg, u8 compare) {WriteSSEOp(32, sseCMP, true, regOp, arg,1); Write8(compare);} - void CMPPD(X64Reg regOp, OpArg arg, u8 compare) {WriteSSEOp(64, sseCMP, true, regOp, arg,1); Write8(compare);} - void ANDPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseAND, true, regOp, arg);} - void ANDPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseAND, true, regOp, arg);} - void ANDNPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseANDN, true, regOp, arg);} - void ANDNPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseANDN, true, regOp, arg);} - void ORPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseOR, true, regOp, arg);} - void ORPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseOR, true, regOp, arg);} - void XORPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseXOR, true, regOp, arg);} - void XORPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseXOR, true, regOp, arg);} - void MULPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseMUL, true, regOp, arg);} - void MULPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseMUL, true, regOp, arg);} - void DIVPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseDIV, true, regOp, arg);} - void DIVPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseDIV, true, regOp, arg);} - void MINPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseMIN, true, regOp, arg);} - void MINPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseMIN, true, regOp, arg);} - void MAXPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseMAX, true, regOp, arg);} - void MAXPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseMAX, true, regOp, arg);} - void SQRTPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseSQRT, true, regOp, arg);} - void SQRTPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseSQRT, true, regOp, arg);} - void RSQRTPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseRSQRT, true, regOp, arg);} - void RSQRTPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseRSQRT, true, regOp, arg);} - void SHUFPS(X64Reg regOp, OpArg arg, u8 shuffle) {WriteSSEOp(32, sseSHUF, true, regOp, arg,1); Write8(shuffle);} - void SHUFPD(X64Reg regOp, OpArg arg, u8 shuffle) {WriteSSEOp(64, sseSHUF, true, regOp, arg,1); Write8(shuffle);} - - void COMISS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseCOMIS, true, regOp, arg);} //weird that these should be packed - void COMISD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseCOMIS, true, regOp, arg);} //ordered - void UCOMISS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseUCOMIS, true, regOp, arg);} //unordered - void UCOMISD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseUCOMIS, true, regOp, arg);} - - void MOVAPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseMOVAPfromRM, true, regOp, arg);} - void MOVAPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseMOVAPfromRM, true, regOp, arg);} - void MOVAPS(OpArg arg, X64Reg regOp) {WriteSSEOp(32, sseMOVAPtoRM, true, regOp, arg);} - void MOVAPD(OpArg arg, X64Reg regOp) {WriteSSEOp(64, sseMOVAPtoRM, true, regOp, arg);} - - void MOVUPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseMOVUPfromRM, true, regOp, arg);} - void MOVUPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseMOVUPfromRM, true, regOp, arg);} - void MOVUPS(OpArg arg, X64Reg regOp) {WriteSSEOp(32, sseMOVUPtoRM, true, regOp, arg);} - void MOVUPD(OpArg arg, X64Reg regOp) {WriteSSEOp(64, sseMOVUPtoRM, true, regOp, arg);} - - void MOVSS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseMOVUPfromRM, false, regOp, arg);} - void MOVSD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseMOVUPfromRM, false, regOp, arg);} - void MOVSS(OpArg arg, X64Reg regOp) {WriteSSEOp(32, sseMOVUPtoRM, false, regOp, arg);} - void MOVSD(OpArg arg, X64Reg regOp) {WriteSSEOp(64, sseMOVUPtoRM, false, regOp, arg);} - - void CVTPS2PD(X64Reg regOp, OpArg arg) {WriteSSEOp(32, 0x5A, true, regOp, arg);} - void CVTPD2PS(X64Reg regOp, OpArg arg) {WriteSSEOp(64, 0x5A, true, regOp, arg);} - - void CVTSD2SS(X64Reg regOp, OpArg arg) {WriteSSEOp(64, 0x5A, false, regOp, arg);} - void CVTSS2SD(X64Reg regOp, OpArg arg) {WriteSSEOp(32, 0x5A, false, regOp, arg);} - void CVTSD2SI(X64Reg regOp, OpArg arg) {WriteSSEOp(32, 0xF2, false, regOp, arg);} - - void CVTDQ2PD(X64Reg regOp, OpArg arg) {WriteSSEOp(32, 0xE6, false, regOp, arg);} - void CVTDQ2PS(X64Reg regOp, const OpArg &arg) {WriteSSEOp(32, 0x5B, true, regOp, arg);} - void CVTPD2DQ(X64Reg regOp, OpArg arg) {WriteSSEOp(64, 0xE6, false, regOp, arg);} - - void MASKMOVDQU(X64Reg dest, X64Reg src) {WriteSSEOp(64, sseMASKMOVDQU, true, dest, R(src));} - - void MOVMSKPS(X64Reg dest, OpArg arg) {WriteSSEOp(32, 0x50, true, dest, arg);} - void MOVMSKPD(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x50, true, dest, arg);} - - void LDDQU(X64Reg dest, OpArg arg) {WriteSSEOp(64, sseLDDQU, false, dest, arg);} // For integer data only - - void UNPCKLPD(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x14, true, dest, arg);} - void UNPCKHPD(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x15, true, dest, arg);} - - void MOVDDUP(X64Reg regOp, OpArg arg) - { - if (cpu_info.bSSE3) - { - WriteSSEOp(64, 0x12, false, regOp, arg); //SSE3 movddup - } - else - { - // Simulate this instruction with SSE2 instructions - if (!arg.IsSimpleReg(regOp)) - MOVQ_xmm(regOp, arg); - UNPCKLPD(regOp, R(regOp)); - } - } - - //There are a few more left - - // Also some integer instrucitons are missing - void PACKSSDW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x6B, true, dest, arg);} - void PACKSSWB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x63, true, dest, arg);} - //void PACKUSDW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x66, true, dest, arg);} // WRONG - void PACKUSWB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x67, true, dest, arg);} - - void PUNPCKLBW(X64Reg dest, const OpArg &arg) {WriteSSEOp(64, 0x60, true, dest, arg);} - void PUNPCKLWD(X64Reg dest, const OpArg &arg) {WriteSSEOp(64, 0x61, true, dest, arg);} - void PUNPCKLDQ(X64Reg dest, const OpArg &arg) {WriteSSEOp(64, 0x62, true, dest, arg);} - //void PUNPCKLQDQ(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x60, true, dest, arg);} - - // WARNING not REX compatible - void PSRAW(X64Reg reg, int shift) { - if (reg > 7) - PanicAlert("The PSRAW-emitter does not support regs above 7"); - Write8(0x66); - Write8(0x0f); - Write8(0x71); - Write8(0xE0 | reg); - Write8(shift); - } - - // WARNING not REX compatible - void PSRAD(X64Reg reg, int shift) { - if (reg > 7) - PanicAlert("The PSRAD-emitter does not support regs above 7"); - Write8(0x66); - Write8(0x0f); - Write8(0x72); - Write8(0xE0 | reg); - Write8(shift); - } - - void PSHUFB(X64Reg dest, OpArg arg) { - if (!cpu_info.bSSSE3) { - PanicAlert("Trying to use PSHUFB on a system that doesn't support it. Bad programmer."); - } - Write8(0x66); - arg.operandReg = dest; - arg.WriteRex(false); - Write8(0x0f); - Write8(0x38); - Write8(0x00); - arg.WriteRest(0); - } - - void PAND(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xDB, true, dest, arg);} - void PANDN(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xDF, true, dest, arg);} - void PXOR(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xEF, true, dest, arg);} - void POR(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xEB, true, dest, arg);} - - void PADDB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xFC, true, dest, arg);} - void PADDW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xFD, true, dest, arg);} - void PADDD(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xFE, true, dest, arg);} - void PADDQ(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xD4, true, dest, arg);} - - void PADDSB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xEC, true, dest, arg);} - void PADDSW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xED, true, dest, arg);} - void PADDUSB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xDC, true, dest, arg);} - void PADDUSW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xDD, true, dest, arg);} - - void PSUBB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xF8, true, dest, arg);} - void PSUBW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xF9, true, dest, arg);} - void PSUBD(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xFA, true, dest, arg);} - void PSUBQ(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xDB, true, dest, arg);} - - void PSUBSB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xE8, true, dest, arg);} - void PSUBSW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xE9, true, dest, arg);} - void PSUBUSB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xD8, true, dest, arg);} - void PSUBUSW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xD9, true, dest, arg);} - - void PAVGB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xE0, true, dest, arg);} - void PAVGW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xE3, true, dest, arg);} - - void PCMPEQB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x74, true, dest, arg);} - void PCMPEQW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x75, true, dest, arg);} - void PCMPEQD(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x76, true, dest, arg);} - - void PCMPGTB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x64, true, dest, arg);} - void PCMPGTW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x65, true, dest, arg);} - void PCMPGTD(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x66, true, dest, arg);} - - void PEXTRW(X64Reg dest, OpArg arg, u8 subreg) {WriteSSEOp(64, 0x64, true, dest, arg); Write8(subreg);} - void PINSRW(X64Reg dest, OpArg arg, u8 subreg) {WriteSSEOp(64, 0x64, true, dest, arg); Write8(subreg);} - - void PMADDWD(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xF5, true, dest, arg); } - void PSADBW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xF6, true, dest, arg);} - - void PMAXSW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xEE, true, dest, arg); } - void PMAXUB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xDE, true, dest, arg); } - void PMINSW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xEA, true, dest, arg); } - void PMINUB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xDA, true, dest, arg); } - - void PMOVMSKB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xD7, true, dest, arg); } - - // Prefixes - - void LOCK() { Write8(0xF0); } - void REP() { Write8(0xF3); } - void REPNE(){ Write8(0xF2); } - - void FWAIT() - { - Write8(0x9B); - } - - void RTDSC() { Write8(0x0F); Write8(0x31); } - -// helper routines for setting pointers -void CallCdeclFunction3(void* fnptr, u32 arg0, u32 arg1, u32 arg2) -{ - using namespace Gen; -#ifdef _M_X64 - -#ifdef _MSC_VER - MOV(32, R(RCX), Imm32(arg0)); - MOV(32, R(RDX), Imm32(arg1)); - MOV(32, R(R8), Imm32(arg2)); - CALL(fnptr); -#else - MOV(32, R(RDI), Imm32(arg0)); - MOV(32, R(RSI), Imm32(arg1)); - MOV(32, R(RDX), Imm32(arg2)); - CALL(fnptr); -#endif - -#else - ABI_AlignStack(3 * 4); - PUSH(32, Imm32(arg2)); - PUSH(32, Imm32(arg1)); - PUSH(32, Imm32(arg0)); - CALL(fnptr); -#ifdef _WIN32 - // don't inc stack -#else - ABI_RestoreStack(3 * 4); -#endif -#endif -} - -void CallCdeclFunction4(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3) -{ - using namespace Gen; -#ifdef _M_X64 - -#ifdef _MSC_VER - MOV(32, R(RCX), Imm32(arg0)); - MOV(32, R(RDX), Imm32(arg1)); - MOV(32, R(R8), Imm32(arg2)); - MOV(32, R(R9), Imm32(arg3)); - CALL(fnptr); -#else - MOV(32, R(RDI), Imm32(arg0)); - MOV(32, R(RSI), Imm32(arg1)); - MOV(32, R(RDX), Imm32(arg2)); - MOV(32, R(RCX), Imm32(arg3)); - CALL(fnptr); -#endif - -#else - ABI_AlignStack(4 * 4); - PUSH(32, Imm32(arg3)); - PUSH(32, Imm32(arg2)); - PUSH(32, Imm32(arg1)); - PUSH(32, Imm32(arg0)); - CALL(fnptr); -#ifdef _WIN32 - // don't inc stack -#else - ABI_RestoreStack(4 * 4); -#endif -#endif -} - -void CallCdeclFunction5(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) -{ - using namespace Gen; -#ifdef _M_X64 - -#ifdef _MSC_VER - MOV(32, R(RCX), Imm32(arg0)); - MOV(32, R(RDX), Imm32(arg1)); - MOV(32, R(R8), Imm32(arg2)); - MOV(32, R(R9), Imm32(arg3)); - MOV(32, MDisp(RSP, 0x20), Imm32(arg4)); - CALL(fnptr); -#else - MOV(32, R(RDI), Imm32(arg0)); - MOV(32, R(RSI), Imm32(arg1)); - MOV(32, R(RDX), Imm32(arg2)); - MOV(32, R(RCX), Imm32(arg3)); - MOV(32, R(R8), Imm32(arg4)); - CALL(fnptr); -#endif - -#else - ABI_AlignStack(5 * 4); - PUSH(32, Imm32(arg4)); - PUSH(32, Imm32(arg3)); - PUSH(32, Imm32(arg2)); - PUSH(32, Imm32(arg1)); - PUSH(32, Imm32(arg0)); - CALL(fnptr); -#ifdef _WIN32 - // don't inc stack -#else - ABI_RestoreStack(5 * 4); -#endif -#endif -} - -void CallCdeclFunction6(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4, u32 arg5) -{ - using namespace Gen; -#ifdef _M_X64 - -#ifdef _MSC_VER - MOV(32, R(RCX), Imm32(arg0)); - MOV(32, R(RDX), Imm32(arg1)); - MOV(32, R(R8), Imm32(arg2)); - MOV(32, R(R9), Imm32(arg3)); - MOV(32, MDisp(RSP, 0x20), Imm32(arg4)); - MOV(32, MDisp(RSP, 0x28), Imm32(arg5)); - CALL(fnptr); -#else - MOV(32, R(RDI), Imm32(arg0)); - MOV(32, R(RSI), Imm32(arg1)); - MOV(32, R(RDX), Imm32(arg2)); - MOV(32, R(RCX), Imm32(arg3)); - MOV(32, R(R8), Imm32(arg4)); - MOV(32, R(R9), Imm32(arg5)); - CALL(fnptr); -#endif - -#else - ABI_AlignStack(6 * 4); - PUSH(32, Imm32(arg5)); - PUSH(32, Imm32(arg4)); - PUSH(32, Imm32(arg3)); - PUSH(32, Imm32(arg2)); - PUSH(32, Imm32(arg1)); - PUSH(32, Imm32(arg0)); - CALL(fnptr); -#ifdef _WIN32 - // don't inc stack -#else - ABI_RestoreStack(6 * 4); -#endif -#endif -} - -#ifdef _M_X64 - -// See header -void ___CallCdeclImport3(void* impptr, u32 arg0, u32 arg1, u32 arg2) { - MOV(32, R(RCX), Imm32(arg0)); - MOV(32, R(RDX), Imm32(arg1)); - MOV(32, R(R8), Imm32(arg2)); - CALLptr(M(impptr)); -} -void ___CallCdeclImport4(void* impptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3) { - MOV(32, R(RCX), Imm32(arg0)); - MOV(32, R(RDX), Imm32(arg1)); - MOV(32, R(R8), Imm32(arg2)); - MOV(32, R(R9), Imm32(arg3)); - CALLptr(M(impptr)); -} -void ___CallCdeclImport5(void* impptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) { - MOV(32, R(RCX), Imm32(arg0)); - MOV(32, R(RDX), Imm32(arg1)); - MOV(32, R(R8), Imm32(arg2)); - MOV(32, R(R9), Imm32(arg3)); - MOV(32, MDisp(RSP, 0x20), Imm32(arg4)); - CALLptr(M(impptr)); -} -void ___CallCdeclImport6(void* impptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4, u32 arg5) { - MOV(32, R(RCX), Imm32(arg0)); - MOV(32, R(RDX), Imm32(arg1)); - MOV(32, R(R8), Imm32(arg2)); - MOV(32, R(R9), Imm32(arg3)); - MOV(32, MDisp(RSP, 0x20), Imm32(arg4)); - MOV(32, MDisp(RSP, 0x28), Imm32(arg5)); - CALLptr(M(impptr)); -} - -#endif - -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +#include "Common.h" +#include "x64Emitter.h" +#include "ABI.h" +#include "CPUDetect.h" + +namespace Gen +{ + static u8 *code; + static bool enableBranchHints = false; + + void SetCodePtr(u8 *ptr) + { + code = ptr; + } + + const u8 *GetCodePtr() + { + return code; + } + + u8 *GetWritableCodePtr() + { + return code; + } + + void ReserveCodeSpace(int bytes) + { + for (int i = 0; i < bytes; i++) + *code++ = 0xCC; + } + + const u8 *AlignCode4() + { + int c = int((u64)code & 3); + if (c) + ReserveCodeSpace(4-c); + return code; + } + + const u8 *AlignCode16() + { + int c = int((u64)code & 15); + if (c) + ReserveCodeSpace(16-c); + return code; + } + + const u8 *AlignCodePage() + { + int c = int((u64)code & 4095); + if (c) + ReserveCodeSpace(4096-c); + return code; + } + + inline void Write8(u8 value) + { + *code++ = value; + } + + inline void Write16(u16 value) + { + *(u16*)code = value; + code += 2; + } + + inline void Write32(u32 value) + { + *(u32*)code = value; + code += 4; + } + + inline void Write64(u64 value) + { + *(u64*)code = value; + code += 8; + } + + void WriteModRM( int mod, int rm, int reg ) + { + Write8((u8)((mod << 6) | ((rm & 7) << 3) | (reg & 7))); + } + + void WriteSIB(int scale, int index, int base) + { + Write8((u8)((scale << 6) | ((index & 7) << 3) | (base & 7))); + } + + void OpArg::WriteRex(bool op64, int customOp) const + { +#ifdef _M_X64 + u8 op = 0x40; + if (customOp == -1) customOp = operandReg; + if (op64) op |= 8; + if (customOp >> 3) op |= 4; + if (indexReg >> 3) op |= 2; + if (offsetOrBaseReg >> 3) op |= 1; //TODO investigate if this is dangerous + if (op != 0x40) + Write8(op); +#else + _dbg_assert_(DYNA_REC, (operandReg >> 3) == 0); + _dbg_assert_(DYNA_REC, (indexReg >> 3) == 0); + _dbg_assert_(DYNA_REC, (offsetOrBaseReg >> 3) == 0); +#endif + } + + void OpArg::WriteRest(int extraBytes, X64Reg _operandReg) const + { + if (_operandReg == 0xff) + _operandReg = (X64Reg)this->operandReg; + int mod = 0; + int ireg = indexReg; + bool SIB = false; + int _offsetOrBaseReg = this->offsetOrBaseReg; + + if (scale == SCALE_RIP) //Also, on 32-bit, just an immediate address + { + // Oh, RIP addressing. + _offsetOrBaseReg = 5; + WriteModRM(0, _operandReg&7, 5); + //TODO : add some checks +#ifdef _M_X64 + u64 ripAddr = (u64)code + 4 + extraBytes; + s32 offs = (s32)((s64)offset - (s64)ripAddr); + Write32((u32)offs); +#else + Write32((u32)offset); +#endif + return; + } + + if (scale == 0) + { + // Oh, no memory, Just a reg. + mod = 3; //11 + } + else if (scale >= 1) + { + //Ah good, no scaling. + if (scale == SCALE_ATREG && !((_offsetOrBaseReg&7) == 4 || (_offsetOrBaseReg&7) == 5)) + { + //Okay, we're good. No SIB necessary. + int ioff = (int)offset; + if (ioff == 0) + { + mod = 0; + } + else if (ioff<-128 || ioff>127) + { + mod = 2; //32-bit displacement + } + else + { + mod = 1; //8-bit displacement + } + } + else //if (scale != SCALE_ATREG) + { + if ((_offsetOrBaseReg & 7) == 4) //this would occupy the SIB encoding :( + { + //So we have to fake it with SIB encoding :( + SIB = true; + } + + if (scale >= SCALE_1 && scale < SCALE_ATREG) + { + SIB = true; + } + + if (scale == SCALE_ATREG && _offsetOrBaseReg == 4) + { + SIB = true; + ireg = 4; + } + + //Okay, we're fine. Just disp encoding. + //We need displacement. Which size? + int ioff = (int)(s64)offset; + if (ioff < -128 || ioff > 127) + { + mod = 2; //32-bit displacement + } + else + { + mod = 1; //8-bit displacement + } + } + } + + // Okay. Time to do the actual writing + // ModRM byte: + int oreg = _offsetOrBaseReg; + if (SIB) + oreg = 4; + + // TODO(ector): WTF is this if about? I don't remember writing it :-) + //if (RIP) + // oreg = 5; + + WriteModRM(mod, _operandReg&7, oreg&7); + + if (SIB) + { + //SIB byte + int ss; + switch (scale) + { + case 0: _offsetOrBaseReg = 4; ss = 0; break; //RSP + case 1: ss = 0; break; + case 2: ss = 1; break; + case 4: ss = 2; break; + case 8: ss = 3; break; + case SCALE_ATREG: ss = 0; break; + default: _assert_msg_(DYNA_REC, 0, "Invalid scale for SIB byte"); ss = 0; break; + } + Write8((u8)((ss << 6) | ((ireg&7)<<3) | (_offsetOrBaseReg&7))); + } + + if (mod == 1) //8-bit disp + { + Write8((u8)(s8)(s32)offset); + } + else if (mod == 2) //32-bit disp + { + Write32((u32)offset); + } + } + + + // W = operand extended width (1 if 64-bit) + // R = register# upper bit + // X = scale amnt upper bit + // B = base register# upper bit + void Rex(int w, int r, int x, int b) + { + w = w ? 1 : 0; + r = r ? 1 : 0; + x = x ? 1 : 0; + b = b ? 1 : 0; + u8 rx = (u8)(0x40 | (w << 3) | (r << 2) | (x << 1) | (b)); + if (rx != 0x40) + Write8(rx); + } + + void JMP(const u8 *addr, bool force5Bytes) + { + u64 fn = (u64)addr; + if (!force5Bytes) + { + s32 distance = (s32)(fn - ((u64)code + 2)); //TODO - sanity check + //8 bits will do + Write8(0xEB); + Write8((u8)(s8)distance); + } + else + { + s32 distance = (s32)(fn - ((u64)code + 5)); //TODO - sanity check + Write8(0xE9); + Write32((u32)(s32)distance); + } + } + + void JMPptr(const OpArg &arg2) + { + OpArg arg = arg2; + if (arg.IsImm()) _assert_msg_(DYNA_REC, 0, "JMPptr - Imm argument"); + arg.operandReg = 4; + arg.WriteRex(false); + Write8(0xFF); + arg.WriteRest(); + } + + //Can be used to trap other processors, before overwriting their code + // not used in dolphin + void JMPself() + { + Write8(0xEB); + Write8(0xFE); + } + + void CALLptr(OpArg arg) + { + if (arg.IsImm()) _assert_msg_(DYNA_REC, 0, "CALLptr - Imm argument"); + arg.operandReg = 2; + arg.WriteRex(false); + Write8(0xFF); + arg.WriteRest(); + } + + void CALL(void *fnptr) + { + u64 distance = u64(fnptr) - (u64(code) + 5); + if (distance >= 0x0000000080000000ULL + && distance < 0xFFFFFFFF80000000ULL) { + PanicAlert("CALL out of range (%p calls %p)", code, fnptr); + } + Write8(0xE8); + Write32(u32(distance)); + } + + FixupBranch J(bool force5bytes) + { + FixupBranch branch; + branch.type = force5bytes ? 1 : 0; + branch.ptr = code + (force5bytes ? 5 : 2); + if (!force5bytes) + { + //8 bits will do + Write8(0xEB); + Write8(0); + } + else + { + Write8(0xE9); + Write32(0); + } + return branch; + } + + // These are to be used with Jcc only. + // Found in intel manual 2A + // These do not really make a difference for any current X86 CPU, + // but are provided here for future use + void HINT_NOT_TAKEN() { if (enableBranchHints) Write8(0x2E); } + void HINT_TAKEN() { if (enableBranchHints) Write8(0x3E); } + + FixupBranch J_CC(CCFlags conditionCode, bool force5bytes) + { + FixupBranch branch; + branch.type = force5bytes ? 1 : 0; + branch.ptr = code + (force5bytes ? 5 : 2); + if (!force5bytes) + { + //8 bits will do + Write8(0x70 + conditionCode); + Write8(0); + } + else + { + Write8(0x0F); + Write8(0x80 + conditionCode); + Write32(0); + } + return branch; + } + + void J_CC(CCFlags conditionCode, const u8 * addr, bool force5Bytes) + { + u64 fn = (u64)addr; + if (!force5Bytes) + { + s32 distance = (s32)(fn - ((u64)code + 2)); //TODO - sanity check + //8 bits will do + Write8(0x70 + conditionCode); + Write8((u8)(s8)distance); + } + else + { + s32 distance = (s32)(fn - ((u64)code + 6)); //TODO - sanity check + Write8(0x0F); + Write8(0x80 + conditionCode); + Write32((u32)(s32)distance); + } + } + + void SetJumpTarget(const FixupBranch &branch) + { + if (branch.type == 0) + { + branch.ptr[-1] = (u8)(s8)(code - branch.ptr); + } + else if (branch.type == 1) + { + ((s32*)branch.ptr)[-1] = (s32)(code - branch.ptr); + } + } + +/* + void INC(int bits, OpArg arg) + { + if (arg.IsImm()) _assert_msg_(DYNA_REC, 0, "INC - Imm argument"); + arg.operandReg = 0; + if (bits == 16) {Write8(0x66);} + arg.WriteRex(bits == 64); + Write8(bits == 8 ? 0xFE : 0xFF); + arg.WriteRest(); + } + void DEC(int bits, OpArg arg) + { + if (arg.IsImm()) _assert_msg_(DYNA_REC, 0, "DEC - Imm argument"); + arg.operandReg = 1; + if (bits == 16) {Write8(0x66);} + arg.WriteRex(bits == 64); + Write8(bits == 8 ? 0xFE : 0xFF); + arg.WriteRest(); + } +*/ + + //Single byte opcodes + //There is no PUSHAD/POPAD in 64-bit mode. + void INT3() {Write8(0xCC);} + void RET() {Write8(0xC3);} + void RET_FAST() {Write8(0xF3); Write8(0xC3);} //two-byte return (rep ret) - recommended by AMD optimization manual for the case of jumping to a ret + + void NOP(int count) + { + // TODO: look up the fastest nop sleds for various sizes + int i; + switch (count) { + case 1: + Write8(0x90); + break; + case 2: + Write8(0x66); + Write8(0x90); + break; + default: + for (i = 0; i < count; i++) { + Write8(0x90); + } + break; + } + } + + void PAUSE() {Write8(0xF3); NOP();} //use in tight spinloops for energy saving on some cpu + void CLC() {Write8(0xF8);} //clear carry + void CMC() {Write8(0xF5);} //flip carry + void STC() {Write8(0xF9);} //set carry + + //TODO: xchg ah, al ??? + void XCHG_AHAL() + { + Write8(0x86); + Write8(0xe0); + // alt. 86 c4 + } + + //These two can not be executed on early Intel 64-bit CPU:s, only on AMD! + void LAHF() {Write8(0x9F);} + void SAHF() {Write8(0x9E);} + + void PUSHF() {Write8(0x9C);} + void POPF() {Write8(0x9D);} + + void LFENCE() {Write8(0x0F); Write8(0xAE); Write8(0xE8);} + void MFENCE() {Write8(0x0F); Write8(0xAE); Write8(0xF0);} + void SFENCE() {Write8(0x0F); Write8(0xAE); Write8(0xF8);} + + void WriteSimple1Byte(int bits, u8 byte, X64Reg reg) + { + if (bits == 16) {Write8(0x66);} + Rex(bits == 64, 0, 0, (int)reg >> 3); + Write8(byte + ((int)reg & 7)); + } + + void WriteSimple2Byte(int bits, u8 byte1, u8 byte2, X64Reg reg) + { + if (bits == 16) {Write8(0x66);} + Rex(bits==64, 0, 0, (int)reg >> 3); + Write8(byte1); + Write8(byte2 + ((int)reg & 7)); + } + + void CWD(int bits) + { + if (bits == 16) {Write8(0x66);} + Rex(bits == 64, 0, 0, 0); + Write8(0x99); + } + + void CBW(int bits) + { + if (bits == 8) {Write8(0x66);} + Rex(bits == 32, 0, 0, 0); + Write8(0x98); + } + + //Simple opcodes + + + //push/pop do not need wide to be 64-bit + void PUSH(X64Reg reg) {WriteSimple1Byte(32, 0x50, reg);} + void POP(X64Reg reg) {WriteSimple1Byte(32, 0x58, reg);} + + void PUSH(int bits, const OpArg ®) + { + if (reg.IsSimpleReg()) + PUSH(reg.GetSimpleReg()); + else if (reg.IsImm()) + { + switch (reg.GetImmBits()) + { + case 8: + Write8(0x6A); + Write8((u8)(s8)reg.offset); + break; + case 16: + Write8(0x66); + Write8(0x68); + Write16((u16)(s16)(s32)reg.offset); + break; + case 32: + Write8(0x68); + Write32((u32)reg.offset); + break; + default: + _assert_msg_(DYNA_REC, 0, "PUSH - Bad imm bits"); + break; + } + } + else + { + //INT3(); + if (bits == 16) + Write8(0x66); + reg.WriteRex(bits == 64); + Write8(0xFF); + reg.WriteRest(0,(X64Reg)6); + } + } + + void POP(int /*bits*/, const OpArg ®) + { + if (reg.IsSimpleReg()) + POP(reg.GetSimpleReg()); + else + INT3(); + } + + void BSWAP(int bits, X64Reg reg) + { + if (bits >= 32) + { + WriteSimple2Byte(bits, 0x0F, 0xC8, reg); + } + else if (bits == 16) + { + //fake 16-bit bswap, TODO replace with xchg ah, al where appropriate + WriteSimple2Byte(false, 0x0F, 0xC8, reg); + SHR(32, R(reg), Imm8(16)); + } + else if (bits == 8) + { + + } + else + { + _assert_msg_(DYNA_REC, 0, "BSWAP - Wrong number of bits"); + } + } + + // Undefined opcode - reserved + // If we ever need a way to always cause a non-breakpoint hard exception... + void UD2() + { + Write8(0x0F); + Write8(0x0B); + } + + void PREFETCH(PrefetchLevel level, OpArg arg) + { + if (arg.IsImm()) _assert_msg_(DYNA_REC, 0, "PREFETCH - Imm argument");; + arg.operandReg = (u8)level; + arg.WriteRex(false); + Write8(0x0F); + Write8(0x18); + arg.WriteRest(); + } + + void SETcc(CCFlags flag, OpArg dest) + { + if (dest.IsImm()) _assert_msg_(DYNA_REC, 0, "SETcc - Imm argument"); + dest.operandReg = 0; + dest.WriteRex(false); + Write8(0x0F); + Write8(0x90 + (u8)flag); + dest.WriteRest(); + } + + void CMOVcc(int bits, X64Reg dest, OpArg src, CCFlags flag) + { + if (src.IsImm()) _assert_msg_(DYNA_REC, 0, "CMOVcc - Imm argument"); + src.operandReg = dest; + src.WriteRex(bits == 64); + Write8(0x0F); + Write8(0x40 + (u8)flag); + src.WriteRest(); + } + + void WriteMulDivType(int bits, OpArg src, int ext) + { + if (src.IsImm()) _assert_msg_(DYNA_REC, 0, "WriteMulDivType - Imm argument"); + src.operandReg = ext; + if (bits == 16) Write8(0x66); + src.WriteRex(bits == 64); + if (bits == 8) + { + Write8(0xF6); + } + else + { + Write8(0xF7); + } + src.WriteRest(); + } + + void MUL(int bits, OpArg src) {WriteMulDivType(bits, src, 4);} + void DIV(int bits, OpArg src) {WriteMulDivType(bits, src, 6);} + void IMUL(int bits, OpArg src) {WriteMulDivType(bits, src, 5);} + void IDIV(int bits, OpArg src) {WriteMulDivType(bits, src, 7);} + void NEG(int bits, OpArg src) {WriteMulDivType(bits, src, 3);} + void NOT(int bits, OpArg src) {WriteMulDivType(bits, src, 2);} + + void WriteBitSearchType(int bits, X64Reg dest, OpArg src, u8 byte2) + { + if (src.IsImm()) _assert_msg_(DYNA_REC, 0, "WriteBitSearchType - Imm argument"); + src.operandReg = (u8)dest; + if (bits == 16) Write8(0x66); + src.WriteRex(bits == 64); + Write8(0x0F); + Write8(byte2); + src.WriteRest(); + } + + void MOVNTI(int bits, OpArg dest, X64Reg src) + { + if (bits <= 16) _assert_msg_(DYNA_REC, 0, "MOVNTI - bits<=16"); + WriteBitSearchType(bits, src, dest, 0xC3); + } + + void BSF(int bits, X64Reg dest, OpArg src) {WriteBitSearchType(bits,dest,src,0xBC);} //bottom bit to top bit + void BSR(int bits, X64Reg dest, OpArg src) {WriteBitSearchType(bits,dest,src,0xBD);} //top bit to bottom bit + + void MOVSX(int dbits, int sbits, X64Reg dest, OpArg src) + { + if (src.IsImm()) _assert_msg_(DYNA_REC, 0, "MOVSX - Imm argument"); + if (dbits == sbits) { + MOV(dbits, R(dest), src); + return; + } + src.operandReg = (u8)dest; + if (dbits == 16) Write8(0x66); + src.WriteRex(dbits == 64); + if (sbits == 8) + { + Write8(0x0F); + Write8(0xBE); + } + else if (sbits == 16) + { + Write8(0x0F); + Write8(0xBF); + } + else if (sbits == 32 && dbits == 64) + { + Write8(0x63); + } + else + { + Crash(); + } + src.WriteRest(); + } + + void MOVZX(int dbits, int sbits, X64Reg dest, OpArg src) + { + if (src.IsImm()) _assert_msg_(DYNA_REC, 0, "MOVZX - Imm argument"); + if (dbits == sbits) { + MOV(dbits, R(dest), src); + return; + } + src.operandReg = (u8)dest; + if (dbits == 16) Write8(0x66); + src.WriteRex(dbits == 64); + if (sbits == 8) + { + Write8(0x0F); + Write8(0xB6); + } + else if (sbits == 16) + { + Write8(0x0F); + Write8(0xB7); + } + else + { + Crash(); + } + src.WriteRest(); + } + + + void LEA(int bits, X64Reg dest, OpArg src) + { + if (src.IsImm()) _assert_msg_(DYNA_REC, 0, "LEA - Imm argument"); + src.operandReg = (u8)dest; + if (bits == 16) Write8(0x66); //TODO: performance warning + src.WriteRex(bits == 64); + Write8(0x8D); + src.WriteRest(); + } + + //shift can be either imm8 or cl + void WriteShift(int bits, OpArg dest, OpArg &shift, int ext) + { + bool writeImm = false; + if (dest.IsImm()) + { + _assert_msg_(DYNA_REC, 0, "WriteShift - can't shift imms"); + } + if ((shift.IsSimpleReg() && shift.GetSimpleReg() != ECX) || (shift.IsImm() && shift.GetImmBits() != 8)) + { + _assert_msg_(DYNA_REC, 0, "WriteShift - illegal argument"); + } + dest.operandReg = ext; + if (bits == 16) Write8(0x66); + dest.WriteRex(bits == 64); + if (shift.GetImmBits() == 8) + { + //ok an imm + u8 imm = (u8)shift.offset; + if (imm == 1) + { + Write8(bits == 8 ? 0xD0 : 0xD1); + } + else + { + writeImm = true; + Write8(bits == 8 ? 0xC0 : 0xC1); + } + } + else + { + Write8(bits == 8 ? 0xD2 : 0xD3); + } + dest.WriteRest(writeImm ? 1 : 0); + if (writeImm) + Write8((u8)shift.offset); + } + + // large rotates and shift are slower on intel than amd + // intel likes to rotate by 1, and the op is smaller too + void ROL(int bits, OpArg dest, OpArg shift) {WriteShift(bits, dest, shift, 0);} + void ROR(int bits, OpArg dest, OpArg shift) {WriteShift(bits, dest, shift, 1);} + void RCL(int bits, OpArg dest, OpArg shift) {WriteShift(bits, dest, shift, 2);} + void RCR(int bits, OpArg dest, OpArg shift) {WriteShift(bits, dest, shift, 3);} + void SHL(int bits, OpArg dest, OpArg shift) {WriteShift(bits, dest, shift, 4);} + void SHR(int bits, OpArg dest, OpArg shift) {WriteShift(bits, dest, shift, 5);} + void SAR(int bits, OpArg dest, OpArg shift) {WriteShift(bits, dest, shift, 7);} + + void OpArg::WriteSingleByteOp(u8 op, X64Reg _operandReg, int bits) + { + if (bits == 16) + Write8(0x66); + + this->operandReg = (u8)_operandReg; + WriteRex(bits == 64); + Write8(op); + WriteRest(); + } + + //todo : add eax special casing + struct NormalOpDef + { + u8 toRm8, toRm32, fromRm8, fromRm32, imm8, imm32, simm8, ext; + }; + + const NormalOpDef nops[11] = + { + {0x00, 0x01, 0x02, 0x03, 0x80, 0x81, 0x83, 0}, //ADD + {0x10, 0x11, 0x12, 0x13, 0x80, 0x81, 0x83, 2}, //ADC + + {0x28, 0x29, 0x2A, 0x2B, 0x80, 0x81, 0x83, 5}, //SUB + {0x18, 0x19, 0x1A, 0x1B, 0x80, 0x81, 0x83, 3}, //SBB + + {0x20, 0x21, 0x22, 0x23, 0x80, 0x81, 0x83, 4}, //AND + {0x08, 0x09, 0x0A, 0x0B, 0x80, 0x81, 0x83, 1}, //OR + + {0x30, 0x31, 0x32, 0x33, 0x80, 0x81, 0x83, 6}, //XOR + {0x88, 0x89, 0x8A, 0x8B, 0xC6, 0xC7, 0xCC, 0}, //MOV + + {0x84, 0x85, 0x84, 0x85, 0xF6, 0xF7, 0xCC, 0}, //TEST (to == from) + {0x38, 0x39, 0x3A, 0x3B, 0x80, 0x81, 0x83, 7}, //CMP + + {0x86, 0x87, 0x86, 0x87, 0xCC, 0xCC, 0xCC, 7}, //XCHG + }; + + //operand can either be immediate or register + void OpArg::WriteNormalOp(bool toRM, NormalOp op, const OpArg &operand, int bits) const + { + X64Reg _operandReg = (X64Reg)this->operandReg; + if (IsImm()) + { + _assert_msg_(DYNA_REC, 0, "WriteNormalOp - Imm argument, wrong order"); + } + + if (bits == 16) + Write8(0x66); + + int immToWrite = 0; + + if (operand.IsImm()) + { + _operandReg = (X64Reg)0; + WriteRex(bits == 64); + + if (!toRM) + { + _assert_msg_(DYNA_REC, 0, "WriteNormalOp - Writing to Imm (!toRM)"); + } + + if (operand.scale == SCALE_IMM8 && bits == 8) + { + Write8(nops[op].imm8); + _assert_msg_(DYNA_REC, code[-1] != 0xCC, "ARGH1"); + immToWrite = 8; + } + else if ((operand.scale == SCALE_IMM16 && bits == 16) || + (operand.scale == SCALE_IMM32 && bits == 32) || + (operand.scale == SCALE_IMM32 && bits == 64)) + { + Write8(nops[op].imm32); + _assert_msg_(DYNA_REC, code[-1] != 0xCC, "ARGH2"); + immToWrite = 32; + } + else if ((operand.scale == SCALE_IMM8 && bits == 16) || + (operand.scale == SCALE_IMM8 && bits == 32) || + (operand.scale == SCALE_IMM8 && bits == 64)) + { + Write8(nops[op].simm8); + _assert_msg_(DYNA_REC, code[-1] != 0xCC, "ARGH3"); + immToWrite = 8; + } + else if (operand.scale == SCALE_IMM64 && bits == 64) + { + if (op == nrmMOV) + { + Write8(0xB8 + (offsetOrBaseReg & 7)); + Write64((u64)operand.offset); + return; + } + _assert_msg_(DYNA_REC, 0, "WriteNormalOp - Only MOV can take 64-bit imm"); + } + else + { + _assert_msg_(DYNA_REC, 0, "WriteNormalOp - Unhandled case"); + } + _operandReg = (X64Reg)nops[op].ext; //pass extension in REG of ModRM + } + else + { + _operandReg = (X64Reg)operand.offsetOrBaseReg; + WriteRex(bits == 64, _operandReg); + // mem/reg or reg/reg op + if (toRM) + { + Write8(bits == 8 ? nops[op].toRm8 : nops[op].toRm32); + _assert_msg_(DYNA_REC, code[-1] != 0xCC, "ARGH4"); + } + else + { + Write8(bits == 8 ? nops[op].fromRm8 : nops[op].fromRm32); + _assert_msg_(DYNA_REC, code[-1] != 0xCC, "ARGH5"); + } + } + WriteRest(immToWrite>>3, _operandReg); + switch (immToWrite) + { + case 0: + break; + case 8: + Write8((u8)operand.offset); + break; + case 32: + Write32((u32)operand.offset); + break; + default: + _assert_msg_(DYNA_REC, 0, "WriteNormalOp - Unhandled case"); + } + } + + void WriteNormalOp(int bits, NormalOp op, const OpArg &a1, const OpArg &a2) + { + if (a1.IsImm()) + { + //Booh! Can't write to an imm + _assert_msg_(DYNA_REC, 0, "WriteNormalOp - a1 cannot be imm"); + return; + } + if (a2.IsImm()) + { + a1.WriteNormalOp(true, op, a2, bits); + } + else + { + if (a1.IsSimpleReg()) + { + a2.WriteNormalOp(false, op, a1, bits); + } + else + { + a1.WriteNormalOp(true, op, a2, bits); + } + } + } + + void ADD (int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(bits, nrmADD, a1, a2);} + void ADC (int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(bits, nrmADC, a1, a2);} + void SUB (int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(bits, nrmSUB, a1, a2);} + void SBB (int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(bits, nrmSBB, a1, a2);} + void AND (int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(bits, nrmAND, a1, a2);} + void OR (int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(bits, nrmOR , a1, a2);} + void XOR (int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(bits, nrmXOR, a1, a2);} + void MOV (int bits, const OpArg &a1, const OpArg &a2) + { + _assert_msg_(DYNA_REC, !a1.IsSimpleReg() || !a2.IsSimpleReg() || a1.GetSimpleReg() != a2.GetSimpleReg(), "Redundant MOV @ %p", + code); + WriteNormalOp(bits, nrmMOV, a1, a2); + } + void TEST(int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(bits, nrmTEST, a1, a2);} + void CMP (int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(bits, nrmCMP, a1, a2);} + void XCHG(int bits, const OpArg &a1, const OpArg &a2) {WriteNormalOp(bits, nrmXCHG, a1, a2);} + + void IMUL(int bits, X64Reg regOp, OpArg a1, OpArg a2) + { + if (bits == 8) { + _assert_msg_(DYNA_REC, 0, "IMUL - illegal bit size!"); + return; + } + if (a1.IsImm()) { + _assert_msg_(DYNA_REC, 0, "IMUL - second arg cannot be imm!"); + return; + } + if (!a2.IsImm()) + { + _assert_msg_(DYNA_REC, 0, "IMUL - third arg must be imm!"); + return; + } + + if (bits == 16) + Write8(0x66); + a1.WriteRex(bits == 64, regOp); + + if (a2.GetImmBits() == 8) { + Write8(0x6B); + a1.WriteRest(1, regOp); + Write8((u8)a2.offset); + } else { + Write8(0x69); + if (a2.GetImmBits() == 16 && bits == 16) { + a1.WriteRest(2, regOp); + Write16((u16)a2.offset); + } else if (a2.GetImmBits() == 32 && + (bits == 32 || bits == 64)) { + a1.WriteRest(4, regOp); + Write32((u32)a2.offset); + } else { + _assert_msg_(DYNA_REC, 0, "IMUL - unhandled case!"); + } + } + } + + void IMUL(int bits, X64Reg regOp, OpArg a) + { + if (bits == 8) { + _assert_msg_(DYNA_REC, 0, "IMUL - illegal bit size!"); + return; + } + if (a.IsImm()) + { + IMUL(bits, regOp, R(regOp), a) ; + return ; + } + + if (bits == 16) + Write8(0x66); + a.WriteRex(bits == 64, regOp); + Write8(0x0F); + Write8(0xAF); + a.WriteRest(0, regOp); + } + + + void WriteSSEOp(int size, u8 sseOp, bool packed, X64Reg regOp, OpArg arg, int extrabytes = 0) + { + if (size == 64 && packed) + Write8(0x66); //this time, override goes upwards + if (!packed) + Write8(size == 64 ? 0xF2 : 0xF3); + arg.operandReg = regOp; + arg.WriteRex(false); + Write8(0x0F); + Write8(sseOp); + arg.WriteRest(extrabytes); + } + + void MOVD_xmm(X64Reg dest, const OpArg &arg) {WriteSSEOp(64, 0x6E, true, dest, arg, 0);} + + void MOVQ_xmm(X64Reg dest, OpArg arg) { +#ifdef _M_X64 + // Alternate encoding + // This does not display correctly in MSVC's debugger, it thinks it's a MOVD + arg.operandReg = dest; + Write8(0x66); + arg.WriteRex(true); + Write8(0x0f); + Write8(0x6E); + arg.WriteRest(0); +#else + arg.operandReg = dest; + Write8(0xF3); + Write8(0x0f); + Write8(0x7E); + arg.WriteRest(0); +#endif + } + + void MOVD_xmm(const OpArg &arg, X64Reg src) {WriteSSEOp(64, 0x7E, true, src, arg, 0);} + void MOVQ_xmm(OpArg arg, X64Reg src) { + if (src > 7) + { + // Alternate encoding + // This does not display correctly in MSVC's debugger, it thinks it's a MOVD + arg.operandReg = src; + Write8(0x66); + arg.WriteRex(true); + Write8(0x0f); + Write8(0x7E); + arg.WriteRest(0); + } else { + // INT3(); + arg.operandReg = src; + arg.WriteRex(false); + Write8(0x66); + Write8(0x0f); + Write8(0xD6); + arg.WriteRest(0); + } + } + + + + void WriteMXCSR(OpArg arg, int ext) + { + if (arg.IsImm() || arg.IsSimpleReg()) + _assert_msg_(DYNA_REC, 0, "MXCSR - invalid operand"); + + arg.operandReg = ext; + arg.WriteRex(false); + Write8(0x0F); + Write8(0xAE); + arg.WriteRest(); + } + + enum NormalSSEOps + { + sseCMP = 0xC2, + sseADD = 0x58, //ADD + sseSUB = 0x5C, //SUB + sseAND = 0x54, //AND + sseANDN = 0x55, //ANDN + sseOR = 0x56, + sseXOR = 0x57, + sseMUL = 0x59, //MUL, + sseDIV = 0x5E, //DIV + sseMIN = 0x5D, //MIN + sseMAX = 0x5F, //MAX + sseCOMIS = 0x2F, //COMIS + sseUCOMIS = 0x2E, //UCOMIS + sseSQRT = 0x51, //SQRT + sseRSQRT = 0x52, //RSQRT (NO DOUBLE PRECISION!!!) + sseMOVAPfromRM = 0x28, //MOVAP from RM + sseMOVAPtoRM = 0x29, //MOVAP to RM + sseMOVUPfromRM = 0x10, //MOVUP from RM + sseMOVUPtoRM = 0x11, //MOVUP to RM + sseMASKMOVDQU = 0xF7, + sseLDDQU = 0xF0, + sseSHUF = 0xC6, + sseMOVNTDQ = 0xE7, + sseMOVNTP = 0x2B, + }; + + void STMXCSR(OpArg memloc) {WriteMXCSR(memloc, 3);} + void LDMXCSR(OpArg memloc) {WriteMXCSR(memloc, 2);} + + void MOVNTDQ(OpArg arg, X64Reg regOp) {WriteSSEOp(64, sseMOVNTDQ, true, regOp, arg);} + void MOVNTPS(OpArg arg, X64Reg regOp) {WriteSSEOp(32, sseMOVNTP, true, regOp, arg);} + void MOVNTPD(OpArg arg, X64Reg regOp) {WriteSSEOp(64, sseMOVNTP, true, regOp, arg);} + + void ADDSS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseADD, false, regOp, arg);} + void ADDSD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseADD, false, regOp, arg);} + void SUBSS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseSUB, false, regOp, arg);} + void SUBSD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseSUB, false, regOp, arg);} + void CMPSS(X64Reg regOp, OpArg arg, u8 compare) {WriteSSEOp(32, sseCMP, false, regOp, arg,1); Write8(compare);} + void CMPSD(X64Reg regOp, OpArg arg, u8 compare) {WriteSSEOp(64, sseCMP, false, regOp, arg,1); Write8(compare);} + void MULSS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseMUL, false, regOp, arg);} + void MULSD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseMUL, false, regOp, arg);} + void DIVSS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseDIV, false, regOp, arg);} + void DIVSD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseDIV, false, regOp, arg);} + void MINSS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseMIN, false, regOp, arg);} + void MINSD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseMIN, false, regOp, arg);} + void MAXSS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseMAX, false, regOp, arg);} + void MAXSD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseMAX, false, regOp, arg);} + void SQRTSS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseSQRT, false, regOp, arg);} + void SQRTSD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseSQRT, false, regOp, arg);} + void RSQRTSS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseRSQRT, false, regOp, arg);} + void RSQRTSD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseRSQRT, false, regOp, arg);} + + void ADDPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseADD, true, regOp, arg);} + void ADDPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseADD, true, regOp, arg);} + void SUBPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseSUB, true, regOp, arg);} + void SUBPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseSUB, true, regOp, arg);} + void CMPPS(X64Reg regOp, OpArg arg, u8 compare) {WriteSSEOp(32, sseCMP, true, regOp, arg,1); Write8(compare);} + void CMPPD(X64Reg regOp, OpArg arg, u8 compare) {WriteSSEOp(64, sseCMP, true, regOp, arg,1); Write8(compare);} + void ANDPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseAND, true, regOp, arg);} + void ANDPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseAND, true, regOp, arg);} + void ANDNPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseANDN, true, regOp, arg);} + void ANDNPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseANDN, true, regOp, arg);} + void ORPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseOR, true, regOp, arg);} + void ORPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseOR, true, regOp, arg);} + void XORPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseXOR, true, regOp, arg);} + void XORPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseXOR, true, regOp, arg);} + void MULPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseMUL, true, regOp, arg);} + void MULPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseMUL, true, regOp, arg);} + void DIVPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseDIV, true, regOp, arg);} + void DIVPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseDIV, true, regOp, arg);} + void MINPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseMIN, true, regOp, arg);} + void MINPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseMIN, true, regOp, arg);} + void MAXPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseMAX, true, regOp, arg);} + void MAXPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseMAX, true, regOp, arg);} + void SQRTPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseSQRT, true, regOp, arg);} + void SQRTPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseSQRT, true, regOp, arg);} + void RSQRTPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseRSQRT, true, regOp, arg);} + void RSQRTPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseRSQRT, true, regOp, arg);} + void SHUFPS(X64Reg regOp, OpArg arg, u8 shuffle) {WriteSSEOp(32, sseSHUF, true, regOp, arg,1); Write8(shuffle);} + void SHUFPD(X64Reg regOp, OpArg arg, u8 shuffle) {WriteSSEOp(64, sseSHUF, true, regOp, arg,1); Write8(shuffle);} + + void COMISS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseCOMIS, true, regOp, arg);} //weird that these should be packed + void COMISD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseCOMIS, true, regOp, arg);} //ordered + void UCOMISS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseUCOMIS, true, regOp, arg);} //unordered + void UCOMISD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseUCOMIS, true, regOp, arg);} + + void MOVAPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseMOVAPfromRM, true, regOp, arg);} + void MOVAPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseMOVAPfromRM, true, regOp, arg);} + void MOVAPS(OpArg arg, X64Reg regOp) {WriteSSEOp(32, sseMOVAPtoRM, true, regOp, arg);} + void MOVAPD(OpArg arg, X64Reg regOp) {WriteSSEOp(64, sseMOVAPtoRM, true, regOp, arg);} + + void MOVUPS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseMOVUPfromRM, true, regOp, arg);} + void MOVUPD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseMOVUPfromRM, true, regOp, arg);} + void MOVUPS(OpArg arg, X64Reg regOp) {WriteSSEOp(32, sseMOVUPtoRM, true, regOp, arg);} + void MOVUPD(OpArg arg, X64Reg regOp) {WriteSSEOp(64, sseMOVUPtoRM, true, regOp, arg);} + + void MOVSS(X64Reg regOp, OpArg arg) {WriteSSEOp(32, sseMOVUPfromRM, false, regOp, arg);} + void MOVSD(X64Reg regOp, OpArg arg) {WriteSSEOp(64, sseMOVUPfromRM, false, regOp, arg);} + void MOVSS(OpArg arg, X64Reg regOp) {WriteSSEOp(32, sseMOVUPtoRM, false, regOp, arg);} + void MOVSD(OpArg arg, X64Reg regOp) {WriteSSEOp(64, sseMOVUPtoRM, false, regOp, arg);} + + void CVTPS2PD(X64Reg regOp, OpArg arg) {WriteSSEOp(32, 0x5A, true, regOp, arg);} + void CVTPD2PS(X64Reg regOp, OpArg arg) {WriteSSEOp(64, 0x5A, true, regOp, arg);} + + void CVTSD2SS(X64Reg regOp, OpArg arg) {WriteSSEOp(64, 0x5A, false, regOp, arg);} + void CVTSS2SD(X64Reg regOp, OpArg arg) {WriteSSEOp(32, 0x5A, false, regOp, arg);} + void CVTSD2SI(X64Reg regOp, OpArg arg) {WriteSSEOp(32, 0xF2, false, regOp, arg);} + + void CVTDQ2PD(X64Reg regOp, OpArg arg) {WriteSSEOp(32, 0xE6, false, regOp, arg);} + void CVTDQ2PS(X64Reg regOp, const OpArg &arg) {WriteSSEOp(32, 0x5B, true, regOp, arg);} + void CVTPD2DQ(X64Reg regOp, OpArg arg) {WriteSSEOp(64, 0xE6, false, regOp, arg);} + + void MASKMOVDQU(X64Reg dest, X64Reg src) {WriteSSEOp(64, sseMASKMOVDQU, true, dest, R(src));} + + void MOVMSKPS(X64Reg dest, OpArg arg) {WriteSSEOp(32, 0x50, true, dest, arg);} + void MOVMSKPD(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x50, true, dest, arg);} + + void LDDQU(X64Reg dest, OpArg arg) {WriteSSEOp(64, sseLDDQU, false, dest, arg);} // For integer data only + + void UNPCKLPD(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x14, true, dest, arg);} + void UNPCKHPD(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x15, true, dest, arg);} + + void MOVDDUP(X64Reg regOp, OpArg arg) + { + if (cpu_info.bSSE3) + { + WriteSSEOp(64, 0x12, false, regOp, arg); //SSE3 movddup + } + else + { + // Simulate this instruction with SSE2 instructions + if (!arg.IsSimpleReg(regOp)) + MOVQ_xmm(regOp, arg); + UNPCKLPD(regOp, R(regOp)); + } + } + + //There are a few more left + + // Also some integer instrucitons are missing + void PACKSSDW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x6B, true, dest, arg);} + void PACKSSWB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x63, true, dest, arg);} + //void PACKUSDW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x66, true, dest, arg);} // WRONG + void PACKUSWB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x67, true, dest, arg);} + + void PUNPCKLBW(X64Reg dest, const OpArg &arg) {WriteSSEOp(64, 0x60, true, dest, arg);} + void PUNPCKLWD(X64Reg dest, const OpArg &arg) {WriteSSEOp(64, 0x61, true, dest, arg);} + void PUNPCKLDQ(X64Reg dest, const OpArg &arg) {WriteSSEOp(64, 0x62, true, dest, arg);} + //void PUNPCKLQDQ(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x60, true, dest, arg);} + + // WARNING not REX compatible + void PSRAW(X64Reg reg, int shift) { + if (reg > 7) + PanicAlert("The PSRAW-emitter does not support regs above 7"); + Write8(0x66); + Write8(0x0f); + Write8(0x71); + Write8(0xE0 | reg); + Write8(shift); + } + + // WARNING not REX compatible + void PSRAD(X64Reg reg, int shift) { + if (reg > 7) + PanicAlert("The PSRAD-emitter does not support regs above 7"); + Write8(0x66); + Write8(0x0f); + Write8(0x72); + Write8(0xE0 | reg); + Write8(shift); + } + + void PSHUFB(X64Reg dest, OpArg arg) { + if (!cpu_info.bSSSE3) { + PanicAlert("Trying to use PSHUFB on a system that doesn't support it. Bad programmer."); + } + Write8(0x66); + arg.operandReg = dest; + arg.WriteRex(false); + Write8(0x0f); + Write8(0x38); + Write8(0x00); + arg.WriteRest(0); + } + + void PAND(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xDB, true, dest, arg);} + void PANDN(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xDF, true, dest, arg);} + void PXOR(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xEF, true, dest, arg);} + void POR(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xEB, true, dest, arg);} + + void PADDB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xFC, true, dest, arg);} + void PADDW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xFD, true, dest, arg);} + void PADDD(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xFE, true, dest, arg);} + void PADDQ(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xD4, true, dest, arg);} + + void PADDSB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xEC, true, dest, arg);} + void PADDSW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xED, true, dest, arg);} + void PADDUSB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xDC, true, dest, arg);} + void PADDUSW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xDD, true, dest, arg);} + + void PSUBB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xF8, true, dest, arg);} + void PSUBW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xF9, true, dest, arg);} + void PSUBD(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xFA, true, dest, arg);} + void PSUBQ(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xDB, true, dest, arg);} + + void PSUBSB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xE8, true, dest, arg);} + void PSUBSW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xE9, true, dest, arg);} + void PSUBUSB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xD8, true, dest, arg);} + void PSUBUSW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xD9, true, dest, arg);} + + void PAVGB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xE0, true, dest, arg);} + void PAVGW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xE3, true, dest, arg);} + + void PCMPEQB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x74, true, dest, arg);} + void PCMPEQW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x75, true, dest, arg);} + void PCMPEQD(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x76, true, dest, arg);} + + void PCMPGTB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x64, true, dest, arg);} + void PCMPGTW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x65, true, dest, arg);} + void PCMPGTD(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0x66, true, dest, arg);} + + void PEXTRW(X64Reg dest, OpArg arg, u8 subreg) {WriteSSEOp(64, 0x64, true, dest, arg); Write8(subreg);} + void PINSRW(X64Reg dest, OpArg arg, u8 subreg) {WriteSSEOp(64, 0x64, true, dest, arg); Write8(subreg);} + + void PMADDWD(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xF5, true, dest, arg); } + void PSADBW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xF6, true, dest, arg);} + + void PMAXSW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xEE, true, dest, arg); } + void PMAXUB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xDE, true, dest, arg); } + void PMINSW(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xEA, true, dest, arg); } + void PMINUB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xDA, true, dest, arg); } + + void PMOVMSKB(X64Reg dest, OpArg arg) {WriteSSEOp(64, 0xD7, true, dest, arg); } + + // Prefixes + + void LOCK() { Write8(0xF0); } + void REP() { Write8(0xF3); } + void REPNE(){ Write8(0xF2); } + + void FWAIT() + { + Write8(0x9B); + } + + void RTDSC() { Write8(0x0F); Write8(0x31); } + +// helper routines for setting pointers +void CallCdeclFunction3(void* fnptr, u32 arg0, u32 arg1, u32 arg2) +{ + using namespace Gen; +#ifdef _M_X64 + +#ifdef _MSC_VER + MOV(32, R(RCX), Imm32(arg0)); + MOV(32, R(RDX), Imm32(arg1)); + MOV(32, R(R8), Imm32(arg2)); + CALL(fnptr); +#else + MOV(32, R(RDI), Imm32(arg0)); + MOV(32, R(RSI), Imm32(arg1)); + MOV(32, R(RDX), Imm32(arg2)); + CALL(fnptr); +#endif + +#else + ABI_AlignStack(3 * 4); + PUSH(32, Imm32(arg2)); + PUSH(32, Imm32(arg1)); + PUSH(32, Imm32(arg0)); + CALL(fnptr); +#ifdef _WIN32 + // don't inc stack +#else + ABI_RestoreStack(3 * 4); +#endif +#endif +} + +void CallCdeclFunction4(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3) +{ + using namespace Gen; +#ifdef _M_X64 + +#ifdef _MSC_VER + MOV(32, R(RCX), Imm32(arg0)); + MOV(32, R(RDX), Imm32(arg1)); + MOV(32, R(R8), Imm32(arg2)); + MOV(32, R(R9), Imm32(arg3)); + CALL(fnptr); +#else + MOV(32, R(RDI), Imm32(arg0)); + MOV(32, R(RSI), Imm32(arg1)); + MOV(32, R(RDX), Imm32(arg2)); + MOV(32, R(RCX), Imm32(arg3)); + CALL(fnptr); +#endif + +#else + ABI_AlignStack(4 * 4); + PUSH(32, Imm32(arg3)); + PUSH(32, Imm32(arg2)); + PUSH(32, Imm32(arg1)); + PUSH(32, Imm32(arg0)); + CALL(fnptr); +#ifdef _WIN32 + // don't inc stack +#else + ABI_RestoreStack(4 * 4); +#endif +#endif +} + +void CallCdeclFunction5(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) +{ + using namespace Gen; +#ifdef _M_X64 + +#ifdef _MSC_VER + MOV(32, R(RCX), Imm32(arg0)); + MOV(32, R(RDX), Imm32(arg1)); + MOV(32, R(R8), Imm32(arg2)); + MOV(32, R(R9), Imm32(arg3)); + MOV(32, MDisp(RSP, 0x20), Imm32(arg4)); + CALL(fnptr); +#else + MOV(32, R(RDI), Imm32(arg0)); + MOV(32, R(RSI), Imm32(arg1)); + MOV(32, R(RDX), Imm32(arg2)); + MOV(32, R(RCX), Imm32(arg3)); + MOV(32, R(R8), Imm32(arg4)); + CALL(fnptr); +#endif + +#else + ABI_AlignStack(5 * 4); + PUSH(32, Imm32(arg4)); + PUSH(32, Imm32(arg3)); + PUSH(32, Imm32(arg2)); + PUSH(32, Imm32(arg1)); + PUSH(32, Imm32(arg0)); + CALL(fnptr); +#ifdef _WIN32 + // don't inc stack +#else + ABI_RestoreStack(5 * 4); +#endif +#endif +} + +void CallCdeclFunction6(void* fnptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4, u32 arg5) +{ + using namespace Gen; +#ifdef _M_X64 + +#ifdef _MSC_VER + MOV(32, R(RCX), Imm32(arg0)); + MOV(32, R(RDX), Imm32(arg1)); + MOV(32, R(R8), Imm32(arg2)); + MOV(32, R(R9), Imm32(arg3)); + MOV(32, MDisp(RSP, 0x20), Imm32(arg4)); + MOV(32, MDisp(RSP, 0x28), Imm32(arg5)); + CALL(fnptr); +#else + MOV(32, R(RDI), Imm32(arg0)); + MOV(32, R(RSI), Imm32(arg1)); + MOV(32, R(RDX), Imm32(arg2)); + MOV(32, R(RCX), Imm32(arg3)); + MOV(32, R(R8), Imm32(arg4)); + MOV(32, R(R9), Imm32(arg5)); + CALL(fnptr); +#endif + +#else + ABI_AlignStack(6 * 4); + PUSH(32, Imm32(arg5)); + PUSH(32, Imm32(arg4)); + PUSH(32, Imm32(arg3)); + PUSH(32, Imm32(arg2)); + PUSH(32, Imm32(arg1)); + PUSH(32, Imm32(arg0)); + CALL(fnptr); +#ifdef _WIN32 + // don't inc stack +#else + ABI_RestoreStack(6 * 4); +#endif +#endif +} + +#ifdef _M_X64 + +// See header +void ___CallCdeclImport3(void* impptr, u32 arg0, u32 arg1, u32 arg2) { + MOV(32, R(RCX), Imm32(arg0)); + MOV(32, R(RDX), Imm32(arg1)); + MOV(32, R(R8), Imm32(arg2)); + CALLptr(M(impptr)); +} +void ___CallCdeclImport4(void* impptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3) { + MOV(32, R(RCX), Imm32(arg0)); + MOV(32, R(RDX), Imm32(arg1)); + MOV(32, R(R8), Imm32(arg2)); + MOV(32, R(R9), Imm32(arg3)); + CALLptr(M(impptr)); +} +void ___CallCdeclImport5(void* impptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4) { + MOV(32, R(RCX), Imm32(arg0)); + MOV(32, R(RDX), Imm32(arg1)); + MOV(32, R(R8), Imm32(arg2)); + MOV(32, R(R9), Imm32(arg3)); + MOV(32, MDisp(RSP, 0x20), Imm32(arg4)); + CALLptr(M(impptr)); +} +void ___CallCdeclImport6(void* impptr, u32 arg0, u32 arg1, u32 arg2, u32 arg3, u32 arg4, u32 arg5) { + MOV(32, R(RCX), Imm32(arg0)); + MOV(32, R(RDX), Imm32(arg1)); + MOV(32, R(R8), Imm32(arg2)); + MOV(32, R(R9), Imm32(arg3)); + MOV(32, MDisp(RSP, 0x20), Imm32(arg4)); + MOV(32, MDisp(RSP, 0x28), Imm32(arg5)); + CALLptr(M(impptr)); +} + +#endif + +} diff --git a/Source/Core/Core/Src/ARDecrypt.cpp b/Source/Core/Core/Src/ARDecrypt.cpp index 9982a72c39..5347071111 100644 --- a/Source/Core/Core/Src/ARDecrypt.cpp +++ b/Source/Core/Core/Src/ARDecrypt.cpp @@ -1,511 +1,511 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// Most of the code in this file is from: -// GCNcrypt - Gamecube AR Crypto Program -// Copyright (C) 2003-2004 Parasyte - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "ARDecrypt.h" -#include - -int total; - -// Alphanumeric filter for text<->bin conversion -const char *filter = "0123456789ABCDEFGHJKMNPQRTUVWXYZILOS"; - -u32 genseeds[0x20]; -//u8 globalvar=0; -//u8 globalvar2=0; - - -const u8 bitstringlen[0x08] = { - 0x06, 0x0A, 0x0C, 0x11, 0x11, 0x08, 0x07, 0x20, -}; - -const u8 gentable0[0x38] = { - 0x39, 0x31, 0x29, 0x21, 0x19, 0x11, 0x09, 0x01, - 0x3A, 0x32, 0x2A, 0x22, 0x1A, 0x12, 0x0A, 0x02, - 0x3B, 0x33, 0x2B, 0x23, 0x1B, 0x13, 0x0B, 0x03, - 0x3C, 0x34, 0x2C, 0x24, 0x3F, 0x37, 0x2F, 0x27, - 0x1F, 0x17, 0x0F, 0x07, 0x3E, 0x36, 0x2E, 0x26, - 0x1E, 0x16, 0x0E, 0x06, 0x3D, 0x35, 0x2D, 0x25, - 0x1D, 0x15, 0x0D, 0x05, 0x1C, 0x14, 0x0C, 0x04, -}; -const u8 gentable1[0x08] = { - 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, -}; -const u8 gentable2[0x10] = { - 0x01, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, - 0x0F, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1C, -}; -const u8 gentable3[0x30] = { - 0x0E, 0x11, 0x0B, 0x18, 0x01, 0x05, 0x03, 0x1C, - 0x0F, 0x06, 0x15, 0x0A, 0x17, 0x13, 0x0C, 0x04, - 0x1A, 0x08, 0x10, 0x07, 0x1B, 0x14, 0x0D, 0x02, - 0x29, 0x34, 0x1F, 0x25, 0x2F, 0x37, 0x1E, 0x28, - 0x33, 0x2D, 0x21, 0x30, 0x2C, 0x31, 0x27, 0x38, - 0x22, 0x35, 0x2E, 0x2A, 0x32, 0x24, 0x1D, 0x20, -}; - -const u16 crctable0[0x10] = { - 0x0000, 0x1081, 0x2102, 0x3183, 0x4204, 0x5285, 0x6306, 0x7387, - 0x8408, 0x9489, 0xA50A, 0xB58B, 0xC60C, 0xD68D, 0xE70E, 0xF78F, -}; -const u16 crctable1[0x10] = { - 0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF, - 0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7, -}; - -const u8 gensubtable[0x08] = { - 0x34, 0x1C, 0x84, 0x9E, 0xFD, 0xA4, 0xB6, 0x7B, -}; - - -const u32 table0[0x40] = { - 0x01010400, 0x00000000, 0x00010000, 0x01010404, 0x01010004, 0x00010404, 0x00000004, 0x00010000, - 0x00000400, 0x01010400, 0x01010404, 0x00000400, 0x01000404, 0x01010004, 0x01000000, 0x00000004, - 0x00000404, 0x01000400, 0x01000400, 0x00010400, 0x00010400, 0x01010000, 0x01010000, 0x01000404, - 0x00010004, 0x01000004, 0x01000004, 0x00010004, 0x00000000, 0x00000404, 0x00010404, 0x01000000, - 0x00010000, 0x01010404, 0x00000004, 0x01010000, 0x01010400, 0x01000000, 0x01000000, 0x00000400, - 0x01010004, 0x00010000, 0x00010400, 0x01000004, 0x00000400, 0x00000004, 0x01000404, 0x00010404, - 0x01010404, 0x00010004, 0x01010000, 0x01000404, 0x01000004, 0x00000404, 0x00010404, 0x01010400, - 0x00000404, 0x01000400, 0x01000400, 0x00000000, 0x00010004, 0x00010400, 0x00000000, 0x01010004, -}; -const u32 table1[0x40] = { - 0x80108020, 0x80008000, 0x00008000, 0x00108020, 0x00100000, 0x00000020, 0x80100020, 0x80008020, - 0x80000020, 0x80108020, 0x80108000, 0x80000000, 0x80008000, 0x00100000, 0x00000020, 0x80100020, - 0x00108000, 0x00100020, 0x80008020, 0x00000000, 0x80000000, 0x00008000, 0x00108020, 0x80100000, - 0x00100020, 0x80000020, 0x00000000, 0x00108000, 0x00008020, 0x80108000, 0x80100000, 0x00008020, - 0x00000000, 0x00108020, 0x80100020, 0x00100000, 0x80008020, 0x80100000, 0x80108000, 0x00008000, - 0x80100000, 0x80008000, 0x00000020, 0x80108020, 0x00108020, 0x00000020, 0x00008000, 0x80000000, - 0x00008020, 0x80108000, 0x00100000, 0x80000020, 0x00100020, 0x80008020, 0x80000020, 0x00100020, - 0x00108000, 0x00000000, 0x80008000, 0x00008020, 0x80000000, 0x80100020, 0x80108020, 0x00108000, -}; -const u32 table2[0x40] = { - 0x00000208, 0x08020200, 0x00000000, 0x08020008, 0x08000200, 0x00000000, 0x00020208, 0x08000200, - 0x00020008, 0x08000008, 0x08000008, 0x00020000, 0x08020208, 0x00020008, 0x08020000, 0x00000208, - 0x08000000, 0x00000008, 0x08020200, 0x00000200, 0x00020200, 0x08020000, 0x08020008, 0x00020208, - 0x08000208, 0x00020200, 0x00020000, 0x08000208, 0x00000008, 0x08020208, 0x00000200, 0x08000000, - 0x08020200, 0x08000000, 0x00020008, 0x00000208, 0x00020000, 0x08020200, 0x08000200, 0x00000000, - 0x00000200, 0x00020008, 0x08020208, 0x08000200, 0x08000008, 0x00000200, 0x00000000, 0x08020008, - 0x08000208, 0x00020000, 0x08000000, 0x08020208, 0x00000008, 0x00020208, 0x00020200, 0x08000008, - 0x08020000, 0x08000208, 0x00000208, 0x08020000, 0x00020208, 0x00000008, 0x08020008, 0x00020200, -}; -const u32 table3[0x40] = { - 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802080, 0x00800081, 0x00800001, 0x00002001, - 0x00000000, 0x00802000, 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00800080, 0x00800001, - 0x00000001, 0x00002000, 0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002001, 0x00002080, - 0x00800081, 0x00000001, 0x00002080, 0x00800080, 0x00002000, 0x00802080, 0x00802081, 0x00000081, - 0x00800080, 0x00800001, 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00000000, 0x00802000, - 0x00002080, 0x00800080, 0x00800081, 0x00000001, 0x00802001, 0x00002081, 0x00002081, 0x00000080, - 0x00802081, 0x00000081, 0x00000001, 0x00002000, 0x00800001, 0x00002001, 0x00802080, 0x00800081, - 0x00002001, 0x00002080, 0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002000, 0x00802080, -}; -const u32 table4[0x40] = { - 0x00000100, 0x02080100, 0x02080000, 0x42000100, 0x00080000, 0x00000100, 0x40000000, 0x02080000, - 0x40080100, 0x00080000, 0x02000100, 0x40080100, 0x42000100, 0x42080000, 0x00080100, 0x40000000, - 0x02000000, 0x40080000, 0x40080000, 0x00000000, 0x40000100, 0x42080100, 0x42080100, 0x02000100, - 0x42080000, 0x40000100, 0x00000000, 0x42000000, 0x02080100, 0x02000000, 0x42000000, 0x00080100, - 0x00080000, 0x42000100, 0x00000100, 0x02000000, 0x40000000, 0x02080000, 0x42000100, 0x40080100, - 0x02000100, 0x40000000, 0x42080000, 0x02080100, 0x40080100, 0x00000100, 0x02000000, 0x42080000, - 0x42080100, 0x00080100, 0x42000000, 0x42080100, 0x02080000, 0x00000000, 0x40080000, 0x42000000, - 0x00080100, 0x02000100, 0x40000100, 0x00080000, 0x00000000, 0x40080000, 0x02080100, 0x40000100, -}; -const u32 table5[0x40] = { - 0x20000010, 0x20400000, 0x00004000, 0x20404010, 0x20400000, 0x00000010, 0x20404010, 0x00400000, - 0x20004000, 0x00404010, 0x00400000, 0x20000010, 0x00400010, 0x20004000, 0x20000000, 0x00004010, - 0x00000000, 0x00400010, 0x20004010, 0x00004000, 0x00404000, 0x20004010, 0x00000010, 0x20400010, - 0x20400010, 0x00000000, 0x00404010, 0x20404000, 0x00004010, 0x00404000, 0x20404000, 0x20000000, - 0x20004000, 0x00000010, 0x20400010, 0x00404000, 0x20404010, 0x00400000, 0x00004010, 0x20000010, - 0x00400000, 0x20004000, 0x20000000, 0x00004010, 0x20000010, 0x20404010, 0x00404000, 0x20400000, - 0x00404010, 0x20404000, 0x00000000, 0x20400010, 0x00000010, 0x00004000, 0x20400000, 0x00404010, - 0x00004000, 0x00400010, 0x20004010, 0x00000000, 0x20404000, 0x20000000, 0x00400010, 0x20004010, -}; -const u32 table6[0x40] = { - 0x00200000, 0x04200002, 0x04000802, 0x00000000, 0x00000800, 0x04000802, 0x00200802, 0x04200800, - 0x04200802, 0x00200000, 0x00000000, 0x04000002, 0x00000002, 0x04000000, 0x04200002, 0x00000802, - 0x04000800, 0x00200802, 0x00200002, 0x04000800, 0x04000002, 0x04200000, 0x04200800, 0x00200002, - 0x04200000, 0x00000800, 0x00000802, 0x04200802, 0x00200800, 0x00000002, 0x04000000, 0x00200800, - 0x04000000, 0x00200800, 0x00200000, 0x04000802, 0x04000802, 0x04200002, 0x04200002, 0x00000002, - 0x00200002, 0x04000000, 0x04000800, 0x00200000, 0x04200800, 0x00000802, 0x00200802, 0x04200800, - 0x00000802, 0x04000002, 0x04200802, 0x04200000, 0x00200800, 0x00000000, 0x00000002, 0x04200802, - 0x00000000, 0x00200802, 0x04200000, 0x00000800, 0x04000002, 0x04000800, 0x00000800, 0x00200002, -}; -const u32 table7[0x40] = { - 0x10001040, 0x00001000, 0x00040000, 0x10041040, 0x10000000, 0x10001040, 0x00000040, 0x10000000, - 0x00040040, 0x10040000, 0x10041040, 0x00041000, 0x10041000, 0x00041040, 0x00001000, 0x00000040, - 0x10040000, 0x10000040, 0x10001000, 0x00001040, 0x00041000, 0x00040040, 0x10040040, 0x10041000, - 0x00001040, 0x00000000, 0x00000000, 0x10040040, 0x10000040, 0x10001000, 0x00041040, 0x00040000, - 0x00041040, 0x00040000, 0x10041000, 0x00001000, 0x00000040, 0x10040040, 0x00001000, 0x00041040, - 0x10001000, 0x00000040, 0x10000040, 0x10040000, 0x10040040, 0x10000000, 0x00040000, 0x10001040, - 0x00000000, 0x10041040, 0x00040040, 0x10000040, 0x10040000, 0x10001000, 0x10001040, 0x00000000, - 0x10041040, 0x00041000, 0x00041000, 0x00001040, 0x00001040, 0x00040040, 0x10000000, 0x10041000, -}; - - -void generateseeds(u32 *seeds, const u8 *seedtable, u8 doreverse) -{ - int i,j; - u32 tmp3; - u8 array0[0x38],array1[0x38],array2[0x08]; - u8 tmp,tmp2; - - i = 0; - while (i < 0x38) - { - tmp = (gentable0[i] - 1); - array0[i++] = ((u32)(0-(seedtable[tmp>>3] & gentable1[tmp&7])) >> 31); - } - - i = 0; - while (i < 0x10) - { - memset(array2,0,8); - tmp2 = gentable2[i]; - - for (j = 0; j < 0x38; j++) - { - tmp = (tmp2+j); - - if (j > 0x1B) - { - if (tmp > 0x37) tmp-=0x1C; - } - else if (tmp > 0x1B) tmp-=0x1C; - - array1[j] = array0[tmp]; - } - for (j = 0; j < 0x30; j++) - { - if (!array1[gentable3[j]-1]) continue; - tmp = (((j*0x2AAB)>>16) - (j>>0x1F)); - array2[tmp] |= (gentable1[j-(tmp*6)]>>2); - } - seeds[i<<1] = ((array2[0]<<24)|(array2[2]<<16)|(array2[4]<<8)|array2[6]); - seeds[(i<<1)+1] = ((array2[1]<<24)|(array2[3]<<16)|(array2[5]<<8)|array2[7]); - i++; - } - - if (!doreverse) - { - j = 0x1F; - for (i = 0; i < 16; i+=2) - { - tmp3 = seeds[i]; - seeds[i] = seeds[j-1]; - seeds[j-1] = tmp3; - - tmp3 = seeds[i+1]; - seeds[i+1] = seeds[j]; - seeds[j] = tmp3; - j-=2; - } - } -} - -void buildseeds() -{ - generateseeds(genseeds,gensubtable,0); -} - -void getcode(u32 *src, u32 *addr, u32 *val) -{ - *addr = Common::swap32(src[0]); - *val = Common::swap32(src[1]); -} - -void setcode(u32 *dst, u32 addr, u32 val) -{ - dst[0] = Common::swap32(addr); - dst[1] = Common::swap32(val); -} - -u16 gencrc16(u32 *codes, u16 size) -{ - u16 ret=0; - u8 tmp=0,tmp2; - int i; - - if (size > 0) - { - while (tmp < size) - { - for (i = 0; i < 4; i++) - { - tmp2 = ((codes[tmp] >> (i<<3))^ret); - ret = ((crctable0[(tmp2>>4)&0x0F]^crctable1[tmp2&0x0F])^(ret>>8)); - } - tmp++; - } - } - return ret; -} - -u8 verifycode(u32 *codes, u16 size) -{ - u16 tmp; - - tmp = gencrc16(codes,size); - return (((tmp>>12)^(tmp>>8)^(tmp>>4)^tmp)&0x0F); -} - -void unscramble1(u32 *addr, u32 *val) -{ - u32 tmp; - - *val = _rotl(*val,4); - - tmp = ((*addr^*val)&0xF0F0F0F0); - *addr ^= tmp; - *val = _rotr((*val^tmp),0x14); - - tmp = ((*addr^*val)&0xFFFF0000); - *addr ^= tmp; - *val = _rotr((*val^tmp),0x12); - - tmp = ((*addr^*val)&0x33333333); - *addr ^= tmp; - *val = _rotr((*val^tmp),6); - - tmp = ((*addr^*val)&0x00FF00FF); - *addr ^= tmp; - *val = _rotl((*val^tmp),9); - - tmp = ((*addr^*val)&0xAAAAAAAA); - *addr = _rotl((*addr^tmp),1); - *val ^= tmp; -} - -void unscramble2(u32 *addr, u32 *val) -{ - u32 tmp; - - *val = _rotr(*val,1); - - tmp = ((*addr^*val)&0xAAAAAAAA); - *val ^= tmp; - *addr = _rotr((*addr^tmp),9); - - tmp = ((*addr^*val)&0x00FF00FF); - *val ^= tmp; - *addr = _rotl((*addr^tmp),6); - - tmp = ((*addr^*val)&0x33333333); - *val ^= tmp; - *addr = _rotl((*addr^tmp),0x12); - - tmp = ((*addr^*val)&0xFFFF0000); - *val ^= tmp; - *addr = _rotl((*addr^tmp),0x14); - - tmp = ((*addr^*val)&0xF0F0F0F0); - *val ^= tmp; - *addr = _rotr((*addr^tmp),4); -} - -void decryptcode(u32 *seeds, u32 *code) -{ - u32 addr,val; - u32 tmp,tmp2; - int i=0; - - getcode(code,&addr,&val); - unscramble1(&addr,&val); - while (i < 32) - { - tmp = (_rotr(val,4)^seeds[i++]); - tmp2 = (val^seeds[i++]); - addr ^= (table6[tmp&0x3F]^table4[(tmp>>8)&0x3F]^table2[(tmp>>16)&0x3F]^table0[(tmp>>24)&0x3F]^table7[tmp2&0x3F]^table5[(tmp2>>8)&0x3F]^table3[(tmp2>>16)&0x3F]^table1[(tmp2>>24)&0x3F]); - - tmp = (_rotr(addr,4)^seeds[i++]); - tmp2 = (addr^seeds[i++]); - val ^= (table6[tmp&0x3F]^table4[(tmp>>8)&0x3F]^table2[(tmp>>16)&0x3F]^table0[(tmp>>24)&0x3F]^table7[tmp2&0x3F]^table5[(tmp2>>8)&0x3F]^table3[(tmp2>>16)&0x3F]^table1[(tmp2>>24)&0x3F]); - } - unscramble2(&addr,&val); - setcode(code,val,addr); -} - -bool getbitstring(u32 *ctrl, u32 *out, u8 len) -{ - u32 tmp=(ctrl[0]+(ctrl[1]<<2)); - - *out = 0; - while (len--) - { - if (ctrl[2] > 0x1F) - { - ctrl[2] = 0; - ctrl[1]++; - tmp = (ctrl[0]+(ctrl[1]<<2)); - } - if (ctrl[1] >= ctrl[3]) return false; - *out = ((*out<<1) | ((tmp >> (0x1F-ctrl[2])) & 1)); - ctrl[2]++; - } - return true; -} - -bool batchdecrypt(u32 *codes, u16 size) -{ - u32 tmp,*ptr=codes; - u32 tmparray[4] = { 0 },tmparray2[8] = { 0 }; - - // Not required - //if (size & 1) return 0; - //if (!size) return 0; - - tmp = (size >> 1); - while (tmp--) - { - decryptcode(genseeds,ptr); - ptr+=2; - } - - tmparray[0] = *codes; - tmparray[1] = 0; - tmparray[2] = 4; // Skip crc - tmparray[3] = size; - getbitstring(tmparray,tmparray2+1,11); // Game id - getbitstring(tmparray,tmparray2+2,17); // Code id - getbitstring(tmparray,tmparray2+3,1); // Master code - getbitstring(tmparray,tmparray2+4,1); // Unknown - getbitstring(tmparray,tmparray2+5,2); // Region - - // Grab gameid and region from the last decrypted code - // Maybe check this against dolphin's GameID? - "code is for wrong game" type msg - //gameid = tmparray2[1]; - //region = tmparray2[5]; - - tmp = codes[0]; - codes[0] &= 0x0FFFFFFF; - if ((tmp>>28) != verifycode(codes,size)) return false; - - return true; - - // Unfinished (so says Parasyte :p ) -} - -int GetVal(const char *flt, char chr) -{ - int ret; - - ret = (strchr(flt,chr) - flt); - switch (ret) - { - case 32: // 'I' - case 33: // 'L' - ret = 1; - break; - case 34: // 'O' - ret = 0; - break; - case 35: // 'S' - ret = 5; - break; - } - - return ret; -} - -int alphatobin(u32 *dst, std::vector alpha, int size) -{ - int i,j=0,k; - int ret=0,org=(size+1); - u32 bin[2]; - u8 parity; - - while (size) - { - bin[0]=0; - for (i = 0; i < 6; i++) - { - bin[0] |= (GetVal(filter,alpha[j>>1][i]) << (((5-i)*5)+2)); - } - bin[0] |= (GetVal(filter,alpha[j>>1][6]) >> 3); - dst[j++] = bin[0]; - - bin[1]=0; - for (i = 0; i < 6; i++) - { - bin[1] |= (GetVal(filter,alpha[j>>1][i+6]) << (((5-i)*5)+4)); - } - bin[1] |= (GetVal(filter,alpha[j>>1][12]) >> 1); - dst[j++] = bin[1]; - - //verify parity bit - k=0; - parity=0; - for (i = 0; i < 64; i++) - { - if (i == 32) k++; - parity ^= (bin[k] >> (i-(k<<5))); - } - if ((parity&1) != (GetVal(filter,alpha[(j-2)>>1][12])&1)) ret=(org-size); - - size--; - } - - return ret; -} - -void DecryptARCode(std::vector vCodes, std::vector &ops) -{ - // The almighty buildseeds() function!! without this, the crypto routines are useless - buildseeds(); - - u32 uCodes[1200]; - u32 i,ret; - - for(i = 0; i < vCodes.size(); ++i) - { - transform(vCodes[i].begin(), vCodes[i].end(), vCodes[i].begin(), toupper); - //PanicAlert("Encrypted AR Code\n%s", vCodes[i].c_str()); - } - - if ((ret=alphatobin(uCodes, vCodes, (int)vCodes.size()))) - { - PanicAlert("Action Replay Code Decryption Error:\nParity Check Failed\n\nCulprit Code:\n%s", vCodes[ret].c_str()); - batchdecrypt(uCodes, (u16)vCodes.size()<<1); - } - else if (!batchdecrypt(uCodes, (u16)vCodes.size()<<1)) - { - // Commented out since we just send the code anyways and hope for the best XD - //PanicAlert("Action Replay Code Decryption Error:\nCRC Check Failed\n\n" - // "First Code in Block(should be verification code):\n%s", vCodes[0].c_str()); - - for (i = 0; i < (vCodes.size()<<1); i+=2) - { - AREntry op; - op.cmd_addr = uCodes[i]; - op.value = uCodes[i+1]; - ops.push_back(op); - //PanicAlert("Decrypted AR Code without verification code:\n%08X %08X", uCodes[i], uCodes[i+1]); - } - } - else - { - // Skip passing the verification code back - for (i = 2; i < (vCodes.size()<<1); i+=2) - { - AREntry op; - op.cmd_addr = uCodes[i]; - op.value = uCodes[i+1]; - ops.push_back(op); - //PanicAlert("Decrypted AR Code:\n%08X %08X", uCodes[i], uCodes[i+1]); - } - } -} +// Copyright (C) 2003-2008 Dolphin Project. + +// Most of the code in this file is from: +// GCNcrypt - Gamecube AR Crypto Program +// Copyright (C) 2003-2004 Parasyte + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "ARDecrypt.h" +#include + +int total; + +// Alphanumeric filter for text<->bin conversion +const char *filter = "0123456789ABCDEFGHJKMNPQRTUVWXYZILOS"; + +u32 genseeds[0x20]; +//u8 globalvar=0; +//u8 globalvar2=0; + + +const u8 bitstringlen[0x08] = { + 0x06, 0x0A, 0x0C, 0x11, 0x11, 0x08, 0x07, 0x20, +}; + +const u8 gentable0[0x38] = { + 0x39, 0x31, 0x29, 0x21, 0x19, 0x11, 0x09, 0x01, + 0x3A, 0x32, 0x2A, 0x22, 0x1A, 0x12, 0x0A, 0x02, + 0x3B, 0x33, 0x2B, 0x23, 0x1B, 0x13, 0x0B, 0x03, + 0x3C, 0x34, 0x2C, 0x24, 0x3F, 0x37, 0x2F, 0x27, + 0x1F, 0x17, 0x0F, 0x07, 0x3E, 0x36, 0x2E, 0x26, + 0x1E, 0x16, 0x0E, 0x06, 0x3D, 0x35, 0x2D, 0x25, + 0x1D, 0x15, 0x0D, 0x05, 0x1C, 0x14, 0x0C, 0x04, +}; +const u8 gentable1[0x08] = { + 0x80, 0x40, 0x20, 0x10, 0x08, 0x04, 0x02, 0x01, +}; +const u8 gentable2[0x10] = { + 0x01, 0x02, 0x04, 0x06, 0x08, 0x0A, 0x0C, 0x0E, + 0x0F, 0x11, 0x13, 0x15, 0x17, 0x19, 0x1B, 0x1C, +}; +const u8 gentable3[0x30] = { + 0x0E, 0x11, 0x0B, 0x18, 0x01, 0x05, 0x03, 0x1C, + 0x0F, 0x06, 0x15, 0x0A, 0x17, 0x13, 0x0C, 0x04, + 0x1A, 0x08, 0x10, 0x07, 0x1B, 0x14, 0x0D, 0x02, + 0x29, 0x34, 0x1F, 0x25, 0x2F, 0x37, 0x1E, 0x28, + 0x33, 0x2D, 0x21, 0x30, 0x2C, 0x31, 0x27, 0x38, + 0x22, 0x35, 0x2E, 0x2A, 0x32, 0x24, 0x1D, 0x20, +}; + +const u16 crctable0[0x10] = { + 0x0000, 0x1081, 0x2102, 0x3183, 0x4204, 0x5285, 0x6306, 0x7387, + 0x8408, 0x9489, 0xA50A, 0xB58B, 0xC60C, 0xD68D, 0xE70E, 0xF78F, +}; +const u16 crctable1[0x10] = { + 0x0000, 0x1189, 0x2312, 0x329B, 0x4624, 0x57AD, 0x6536, 0x74BF, + 0x8C48, 0x9DC1, 0xAF5A, 0xBED3, 0xCA6C, 0xDBE5, 0xE97E, 0xF8F7, +}; + +const u8 gensubtable[0x08] = { + 0x34, 0x1C, 0x84, 0x9E, 0xFD, 0xA4, 0xB6, 0x7B, +}; + + +const u32 table0[0x40] = { + 0x01010400, 0x00000000, 0x00010000, 0x01010404, 0x01010004, 0x00010404, 0x00000004, 0x00010000, + 0x00000400, 0x01010400, 0x01010404, 0x00000400, 0x01000404, 0x01010004, 0x01000000, 0x00000004, + 0x00000404, 0x01000400, 0x01000400, 0x00010400, 0x00010400, 0x01010000, 0x01010000, 0x01000404, + 0x00010004, 0x01000004, 0x01000004, 0x00010004, 0x00000000, 0x00000404, 0x00010404, 0x01000000, + 0x00010000, 0x01010404, 0x00000004, 0x01010000, 0x01010400, 0x01000000, 0x01000000, 0x00000400, + 0x01010004, 0x00010000, 0x00010400, 0x01000004, 0x00000400, 0x00000004, 0x01000404, 0x00010404, + 0x01010404, 0x00010004, 0x01010000, 0x01000404, 0x01000004, 0x00000404, 0x00010404, 0x01010400, + 0x00000404, 0x01000400, 0x01000400, 0x00000000, 0x00010004, 0x00010400, 0x00000000, 0x01010004, +}; +const u32 table1[0x40] = { + 0x80108020, 0x80008000, 0x00008000, 0x00108020, 0x00100000, 0x00000020, 0x80100020, 0x80008020, + 0x80000020, 0x80108020, 0x80108000, 0x80000000, 0x80008000, 0x00100000, 0x00000020, 0x80100020, + 0x00108000, 0x00100020, 0x80008020, 0x00000000, 0x80000000, 0x00008000, 0x00108020, 0x80100000, + 0x00100020, 0x80000020, 0x00000000, 0x00108000, 0x00008020, 0x80108000, 0x80100000, 0x00008020, + 0x00000000, 0x00108020, 0x80100020, 0x00100000, 0x80008020, 0x80100000, 0x80108000, 0x00008000, + 0x80100000, 0x80008000, 0x00000020, 0x80108020, 0x00108020, 0x00000020, 0x00008000, 0x80000000, + 0x00008020, 0x80108000, 0x00100000, 0x80000020, 0x00100020, 0x80008020, 0x80000020, 0x00100020, + 0x00108000, 0x00000000, 0x80008000, 0x00008020, 0x80000000, 0x80100020, 0x80108020, 0x00108000, +}; +const u32 table2[0x40] = { + 0x00000208, 0x08020200, 0x00000000, 0x08020008, 0x08000200, 0x00000000, 0x00020208, 0x08000200, + 0x00020008, 0x08000008, 0x08000008, 0x00020000, 0x08020208, 0x00020008, 0x08020000, 0x00000208, + 0x08000000, 0x00000008, 0x08020200, 0x00000200, 0x00020200, 0x08020000, 0x08020008, 0x00020208, + 0x08000208, 0x00020200, 0x00020000, 0x08000208, 0x00000008, 0x08020208, 0x00000200, 0x08000000, + 0x08020200, 0x08000000, 0x00020008, 0x00000208, 0x00020000, 0x08020200, 0x08000200, 0x00000000, + 0x00000200, 0x00020008, 0x08020208, 0x08000200, 0x08000008, 0x00000200, 0x00000000, 0x08020008, + 0x08000208, 0x00020000, 0x08000000, 0x08020208, 0x00000008, 0x00020208, 0x00020200, 0x08000008, + 0x08020000, 0x08000208, 0x00000208, 0x08020000, 0x00020208, 0x00000008, 0x08020008, 0x00020200, +}; +const u32 table3[0x40] = { + 0x00802001, 0x00002081, 0x00002081, 0x00000080, 0x00802080, 0x00800081, 0x00800001, 0x00002001, + 0x00000000, 0x00802000, 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00800080, 0x00800001, + 0x00000001, 0x00002000, 0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002001, 0x00002080, + 0x00800081, 0x00000001, 0x00002080, 0x00800080, 0x00002000, 0x00802080, 0x00802081, 0x00000081, + 0x00800080, 0x00800001, 0x00802000, 0x00802081, 0x00000081, 0x00000000, 0x00000000, 0x00802000, + 0x00002080, 0x00800080, 0x00800081, 0x00000001, 0x00802001, 0x00002081, 0x00002081, 0x00000080, + 0x00802081, 0x00000081, 0x00000001, 0x00002000, 0x00800001, 0x00002001, 0x00802080, 0x00800081, + 0x00002001, 0x00002080, 0x00800000, 0x00802001, 0x00000080, 0x00800000, 0x00002000, 0x00802080, +}; +const u32 table4[0x40] = { + 0x00000100, 0x02080100, 0x02080000, 0x42000100, 0x00080000, 0x00000100, 0x40000000, 0x02080000, + 0x40080100, 0x00080000, 0x02000100, 0x40080100, 0x42000100, 0x42080000, 0x00080100, 0x40000000, + 0x02000000, 0x40080000, 0x40080000, 0x00000000, 0x40000100, 0x42080100, 0x42080100, 0x02000100, + 0x42080000, 0x40000100, 0x00000000, 0x42000000, 0x02080100, 0x02000000, 0x42000000, 0x00080100, + 0x00080000, 0x42000100, 0x00000100, 0x02000000, 0x40000000, 0x02080000, 0x42000100, 0x40080100, + 0x02000100, 0x40000000, 0x42080000, 0x02080100, 0x40080100, 0x00000100, 0x02000000, 0x42080000, + 0x42080100, 0x00080100, 0x42000000, 0x42080100, 0x02080000, 0x00000000, 0x40080000, 0x42000000, + 0x00080100, 0x02000100, 0x40000100, 0x00080000, 0x00000000, 0x40080000, 0x02080100, 0x40000100, +}; +const u32 table5[0x40] = { + 0x20000010, 0x20400000, 0x00004000, 0x20404010, 0x20400000, 0x00000010, 0x20404010, 0x00400000, + 0x20004000, 0x00404010, 0x00400000, 0x20000010, 0x00400010, 0x20004000, 0x20000000, 0x00004010, + 0x00000000, 0x00400010, 0x20004010, 0x00004000, 0x00404000, 0x20004010, 0x00000010, 0x20400010, + 0x20400010, 0x00000000, 0x00404010, 0x20404000, 0x00004010, 0x00404000, 0x20404000, 0x20000000, + 0x20004000, 0x00000010, 0x20400010, 0x00404000, 0x20404010, 0x00400000, 0x00004010, 0x20000010, + 0x00400000, 0x20004000, 0x20000000, 0x00004010, 0x20000010, 0x20404010, 0x00404000, 0x20400000, + 0x00404010, 0x20404000, 0x00000000, 0x20400010, 0x00000010, 0x00004000, 0x20400000, 0x00404010, + 0x00004000, 0x00400010, 0x20004010, 0x00000000, 0x20404000, 0x20000000, 0x00400010, 0x20004010, +}; +const u32 table6[0x40] = { + 0x00200000, 0x04200002, 0x04000802, 0x00000000, 0x00000800, 0x04000802, 0x00200802, 0x04200800, + 0x04200802, 0x00200000, 0x00000000, 0x04000002, 0x00000002, 0x04000000, 0x04200002, 0x00000802, + 0x04000800, 0x00200802, 0x00200002, 0x04000800, 0x04000002, 0x04200000, 0x04200800, 0x00200002, + 0x04200000, 0x00000800, 0x00000802, 0x04200802, 0x00200800, 0x00000002, 0x04000000, 0x00200800, + 0x04000000, 0x00200800, 0x00200000, 0x04000802, 0x04000802, 0x04200002, 0x04200002, 0x00000002, + 0x00200002, 0x04000000, 0x04000800, 0x00200000, 0x04200800, 0x00000802, 0x00200802, 0x04200800, + 0x00000802, 0x04000002, 0x04200802, 0x04200000, 0x00200800, 0x00000000, 0x00000002, 0x04200802, + 0x00000000, 0x00200802, 0x04200000, 0x00000800, 0x04000002, 0x04000800, 0x00000800, 0x00200002, +}; +const u32 table7[0x40] = { + 0x10001040, 0x00001000, 0x00040000, 0x10041040, 0x10000000, 0x10001040, 0x00000040, 0x10000000, + 0x00040040, 0x10040000, 0x10041040, 0x00041000, 0x10041000, 0x00041040, 0x00001000, 0x00000040, + 0x10040000, 0x10000040, 0x10001000, 0x00001040, 0x00041000, 0x00040040, 0x10040040, 0x10041000, + 0x00001040, 0x00000000, 0x00000000, 0x10040040, 0x10000040, 0x10001000, 0x00041040, 0x00040000, + 0x00041040, 0x00040000, 0x10041000, 0x00001000, 0x00000040, 0x10040040, 0x00001000, 0x00041040, + 0x10001000, 0x00000040, 0x10000040, 0x10040000, 0x10040040, 0x10000000, 0x00040000, 0x10001040, + 0x00000000, 0x10041040, 0x00040040, 0x10000040, 0x10040000, 0x10001000, 0x10001040, 0x00000000, + 0x10041040, 0x00041000, 0x00041000, 0x00001040, 0x00001040, 0x00040040, 0x10000000, 0x10041000, +}; + + +void generateseeds(u32 *seeds, const u8 *seedtable, u8 doreverse) +{ + int i,j; + u32 tmp3; + u8 array0[0x38],array1[0x38],array2[0x08]; + u8 tmp,tmp2; + + i = 0; + while (i < 0x38) + { + tmp = (gentable0[i] - 1); + array0[i++] = ((u32)(0-(seedtable[tmp>>3] & gentable1[tmp&7])) >> 31); + } + + i = 0; + while (i < 0x10) + { + memset(array2,0,8); + tmp2 = gentable2[i]; + + for (j = 0; j < 0x38; j++) + { + tmp = (tmp2+j); + + if (j > 0x1B) + { + if (tmp > 0x37) tmp-=0x1C; + } + else if (tmp > 0x1B) tmp-=0x1C; + + array1[j] = array0[tmp]; + } + for (j = 0; j < 0x30; j++) + { + if (!array1[gentable3[j]-1]) continue; + tmp = (((j*0x2AAB)>>16) - (j>>0x1F)); + array2[tmp] |= (gentable1[j-(tmp*6)]>>2); + } + seeds[i<<1] = ((array2[0]<<24)|(array2[2]<<16)|(array2[4]<<8)|array2[6]); + seeds[(i<<1)+1] = ((array2[1]<<24)|(array2[3]<<16)|(array2[5]<<8)|array2[7]); + i++; + } + + if (!doreverse) + { + j = 0x1F; + for (i = 0; i < 16; i+=2) + { + tmp3 = seeds[i]; + seeds[i] = seeds[j-1]; + seeds[j-1] = tmp3; + + tmp3 = seeds[i+1]; + seeds[i+1] = seeds[j]; + seeds[j] = tmp3; + j-=2; + } + } +} + +void buildseeds() +{ + generateseeds(genseeds,gensubtable,0); +} + +void getcode(u32 *src, u32 *addr, u32 *val) +{ + *addr = Common::swap32(src[0]); + *val = Common::swap32(src[1]); +} + +void setcode(u32 *dst, u32 addr, u32 val) +{ + dst[0] = Common::swap32(addr); + dst[1] = Common::swap32(val); +} + +u16 gencrc16(u32 *codes, u16 size) +{ + u16 ret=0; + u8 tmp=0,tmp2; + int i; + + if (size > 0) + { + while (tmp < size) + { + for (i = 0; i < 4; i++) + { + tmp2 = ((codes[tmp] >> (i<<3))^ret); + ret = ((crctable0[(tmp2>>4)&0x0F]^crctable1[tmp2&0x0F])^(ret>>8)); + } + tmp++; + } + } + return ret; +} + +u8 verifycode(u32 *codes, u16 size) +{ + u16 tmp; + + tmp = gencrc16(codes,size); + return (((tmp>>12)^(tmp>>8)^(tmp>>4)^tmp)&0x0F); +} + +void unscramble1(u32 *addr, u32 *val) +{ + u32 tmp; + + *val = _rotl(*val,4); + + tmp = ((*addr^*val)&0xF0F0F0F0); + *addr ^= tmp; + *val = _rotr((*val^tmp),0x14); + + tmp = ((*addr^*val)&0xFFFF0000); + *addr ^= tmp; + *val = _rotr((*val^tmp),0x12); + + tmp = ((*addr^*val)&0x33333333); + *addr ^= tmp; + *val = _rotr((*val^tmp),6); + + tmp = ((*addr^*val)&0x00FF00FF); + *addr ^= tmp; + *val = _rotl((*val^tmp),9); + + tmp = ((*addr^*val)&0xAAAAAAAA); + *addr = _rotl((*addr^tmp),1); + *val ^= tmp; +} + +void unscramble2(u32 *addr, u32 *val) +{ + u32 tmp; + + *val = _rotr(*val,1); + + tmp = ((*addr^*val)&0xAAAAAAAA); + *val ^= tmp; + *addr = _rotr((*addr^tmp),9); + + tmp = ((*addr^*val)&0x00FF00FF); + *val ^= tmp; + *addr = _rotl((*addr^tmp),6); + + tmp = ((*addr^*val)&0x33333333); + *val ^= tmp; + *addr = _rotl((*addr^tmp),0x12); + + tmp = ((*addr^*val)&0xFFFF0000); + *val ^= tmp; + *addr = _rotl((*addr^tmp),0x14); + + tmp = ((*addr^*val)&0xF0F0F0F0); + *val ^= tmp; + *addr = _rotr((*addr^tmp),4); +} + +void decryptcode(u32 *seeds, u32 *code) +{ + u32 addr,val; + u32 tmp,tmp2; + int i=0; + + getcode(code,&addr,&val); + unscramble1(&addr,&val); + while (i < 32) + { + tmp = (_rotr(val,4)^seeds[i++]); + tmp2 = (val^seeds[i++]); + addr ^= (table6[tmp&0x3F]^table4[(tmp>>8)&0x3F]^table2[(tmp>>16)&0x3F]^table0[(tmp>>24)&0x3F]^table7[tmp2&0x3F]^table5[(tmp2>>8)&0x3F]^table3[(tmp2>>16)&0x3F]^table1[(tmp2>>24)&0x3F]); + + tmp = (_rotr(addr,4)^seeds[i++]); + tmp2 = (addr^seeds[i++]); + val ^= (table6[tmp&0x3F]^table4[(tmp>>8)&0x3F]^table2[(tmp>>16)&0x3F]^table0[(tmp>>24)&0x3F]^table7[tmp2&0x3F]^table5[(tmp2>>8)&0x3F]^table3[(tmp2>>16)&0x3F]^table1[(tmp2>>24)&0x3F]); + } + unscramble2(&addr,&val); + setcode(code,val,addr); +} + +bool getbitstring(u32 *ctrl, u32 *out, u8 len) +{ + u32 tmp=(ctrl[0]+(ctrl[1]<<2)); + + *out = 0; + while (len--) + { + if (ctrl[2] > 0x1F) + { + ctrl[2] = 0; + ctrl[1]++; + tmp = (ctrl[0]+(ctrl[1]<<2)); + } + if (ctrl[1] >= ctrl[3]) return false; + *out = ((*out<<1) | ((tmp >> (0x1F-ctrl[2])) & 1)); + ctrl[2]++; + } + return true; +} + +bool batchdecrypt(u32 *codes, u16 size) +{ + u32 tmp,*ptr=codes; + u32 tmparray[4] = { 0 },tmparray2[8] = { 0 }; + + // Not required + //if (size & 1) return 0; + //if (!size) return 0; + + tmp = (size >> 1); + while (tmp--) + { + decryptcode(genseeds,ptr); + ptr+=2; + } + + tmparray[0] = *codes; + tmparray[1] = 0; + tmparray[2] = 4; // Skip crc + tmparray[3] = size; + getbitstring(tmparray,tmparray2+1,11); // Game id + getbitstring(tmparray,tmparray2+2,17); // Code id + getbitstring(tmparray,tmparray2+3,1); // Master code + getbitstring(tmparray,tmparray2+4,1); // Unknown + getbitstring(tmparray,tmparray2+5,2); // Region + + // Grab gameid and region from the last decrypted code + // Maybe check this against dolphin's GameID? - "code is for wrong game" type msg + //gameid = tmparray2[1]; + //region = tmparray2[5]; + + tmp = codes[0]; + codes[0] &= 0x0FFFFFFF; + if ((tmp>>28) != verifycode(codes,size)) return false; + + return true; + + // Unfinished (so says Parasyte :p ) +} + +int GetVal(const char *flt, char chr) +{ + int ret; + + ret = (strchr(flt,chr) - flt); + switch (ret) + { + case 32: // 'I' + case 33: // 'L' + ret = 1; + break; + case 34: // 'O' + ret = 0; + break; + case 35: // 'S' + ret = 5; + break; + } + + return ret; +} + +int alphatobin(u32 *dst, std::vector alpha, int size) +{ + int i,j=0,k; + int ret=0,org=(size+1); + u32 bin[2]; + u8 parity; + + while (size) + { + bin[0]=0; + for (i = 0; i < 6; i++) + { + bin[0] |= (GetVal(filter,alpha[j>>1][i]) << (((5-i)*5)+2)); + } + bin[0] |= (GetVal(filter,alpha[j>>1][6]) >> 3); + dst[j++] = bin[0]; + + bin[1]=0; + for (i = 0; i < 6; i++) + { + bin[1] |= (GetVal(filter,alpha[j>>1][i+6]) << (((5-i)*5)+4)); + } + bin[1] |= (GetVal(filter,alpha[j>>1][12]) >> 1); + dst[j++] = bin[1]; + + //verify parity bit + k=0; + parity=0; + for (i = 0; i < 64; i++) + { + if (i == 32) k++; + parity ^= (bin[k] >> (i-(k<<5))); + } + if ((parity&1) != (GetVal(filter,alpha[(j-2)>>1][12])&1)) ret=(org-size); + + size--; + } + + return ret; +} + +void DecryptARCode(std::vector vCodes, std::vector &ops) +{ + // The almighty buildseeds() function!! without this, the crypto routines are useless + buildseeds(); + + u32 uCodes[1200]; + u32 i,ret; + + for(i = 0; i < vCodes.size(); ++i) + { + transform(vCodes[i].begin(), vCodes[i].end(), vCodes[i].begin(), toupper); + //PanicAlert("Encrypted AR Code\n%s", vCodes[i].c_str()); + } + + if ((ret=alphatobin(uCodes, vCodes, (int)vCodes.size()))) + { + PanicAlert("Action Replay Code Decryption Error:\nParity Check Failed\n\nCulprit Code:\n%s", vCodes[ret].c_str()); + batchdecrypt(uCodes, (u16)vCodes.size()<<1); + } + else if (!batchdecrypt(uCodes, (u16)vCodes.size()<<1)) + { + // Commented out since we just send the code anyways and hope for the best XD + //PanicAlert("Action Replay Code Decryption Error:\nCRC Check Failed\n\n" + // "First Code in Block(should be verification code):\n%s", vCodes[0].c_str()); + + for (i = 0; i < (vCodes.size()<<1); i+=2) + { + AREntry op; + op.cmd_addr = uCodes[i]; + op.value = uCodes[i+1]; + ops.push_back(op); + //PanicAlert("Decrypted AR Code without verification code:\n%08X %08X", uCodes[i], uCodes[i+1]); + } + } + else + { + // Skip passing the verification code back + for (i = 2; i < (vCodes.size()<<1); i+=2) + { + AREntry op; + op.cmd_addr = uCodes[i]; + op.value = uCodes[i+1]; + ops.push_back(op); + //PanicAlert("Decrypted AR Code:\n%08X %08X", uCodes[i], uCodes[i+1]); + } + } +} diff --git a/Source/Core/Core/Src/ActionReplay.cpp b/Source/Core/Core/Src/ActionReplay.cpp index cb737c262f..3618446f1e 100644 --- a/Source/Core/Core/Src/ActionReplay.cpp +++ b/Source/Core/Core/Src/ActionReplay.cpp @@ -1,985 +1,985 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - - -// Simple partial Action Replay code system implementation. - -// Will never be able to support some AR codes - specifically those that patch the running -// Action Replay engine itself - yes they do exist!!! - -// Action Replay actually is a small virtual machine with a limited number of commands. -// It probably is Turing complete - but what does that matter when AR codes can write -// actual PowerPC code. - -#include -#include - -#include "Common.h" -#include "StringUtil.h" -#include "HW/Memmap.h" -#include "ActionReplay.h" -#include "Core.h" -#include "ARDecrypt.h" -#include "LogManager.h" - -namespace -{ - static std::vector::const_iterator iter; - static ARCode code; - static bool b_RanOnce = false; - static std::vector arCodes; - static std::vector activeCodes; -} // namespace - -void LogInfo(const char *format, ...); -// --- Codes --- -// SubTypes (Normal 0 Codes) -bool Subtype_RamWriteAndFill(u32 addr, u32 data); -bool Subtype_WriteToPointer(u32 addr, u32 data); -bool Subtype_AddCode(u32 addr, u32 data); -bool Subtype_MasterCodeAndWriteToCCXXXXXX(); -// Zero Codes -bool ZeroCode_FillAndSlide(u32 addr_last, u32 addr, u32 data); -bool ZeroCode_MemoryCopy(u32 val_last, u32 addr, u32 data); -// Normal Codes -bool NormalCode_Type_0(u8 subtype, u32 addr, u32 data); -bool NormalCode_Type_1(u8 subtype, u32 addr, u32 data, int *count, bool *skip); -bool NormalCode_Type_2(u8 subtype, u32 addr, u32 data, int *count, bool *skip); -bool NormalCode_Type_3(u8 subtype, u32 addr, u32 data, int *count, bool *skip); -bool NormalCode_Type_4(u8 subtype, u32 addr, u32 data, int *count, bool *skip); -bool NormalCode_Type_5(u8 subtype, u32 addr, u32 data, int *count, bool *skip); -bool NormalCode_Type_6(u8 subtype, u32 addr, u32 data, int *count, bool *skip); -bool NormalCode_Type_7(u8 subtype, u32 addr, u32 data, int *count, bool *skip); - -void LoadActionReplayCodes(IniFile &ini) -{ - // Parses the Action Replay section of a game ini file. - if (!Core::GetStartupParameter().bEnableCheats) - return; // If cheats are off, do not load them - - std::vector lines; - std::vector encryptedLines; - ARCode currentCode; - arCodes.clear(); - - if (!ini.GetLines("ActionReplay", lines)) - return; // no codes found. - - for (std::vector::const_iterator it = lines.begin(); it != lines.end(); ++it) - { - std::string line = *it; - std::vector pieces; - - // Check if the line is a name of the code - if (line[0] == '+' || line[0] == '$') - { - if (currentCode.ops.size()) - { - arCodes.push_back(currentCode); - currentCode.ops.clear(); - } - if (encryptedLines.size()) - { - DecryptARCode(encryptedLines, currentCode.ops); - arCodes.push_back(currentCode); - currentCode.ops.clear(); - encryptedLines.clear(); - } - - if(line.size() > 1) - { - if (line[0] == '+') - { - currentCode.active = true; - currentCode.name = line.substr(2, line.size() - 2);; - Core::DisplayMessage("AR code active: " + currentCode.name, 5000); - } - else - { - currentCode.active = false; - currentCode.name = line.substr(1, line.size() - 1); - } - } - continue; - } - - SplitString(line, " ", pieces); - - // Check if the AR code is decrypted - if (pieces.size() == 2 && pieces[0].size() == 8 && pieces[1].size() == 8) - { - AREntry op; - bool success_addr = TryParseUInt(std::string("0x") + pieces[0], &op.cmd_addr); - bool success_val = TryParseUInt(std::string("0x") + pieces[1], &op.value); - if (!(success_addr | success_val)) { - PanicAlert("Action Replay Error: invalid AR code line: %s", line.c_str()); - if (!success_addr) PanicAlert("The address is invalid"); - if (!success_val) PanicAlert("The value is invalid"); - } - else - currentCode.ops.push_back(op); - } - else - { - SplitString(line, "-", pieces); - if (pieces.size() == 3 && pieces[0].size() == 4 && pieces[1].size() == 4 && pieces[2].size() == 5) - { - // Encrypted AR code - // Decryption is done in "blocks", so we must push blocks into a vector, - // then send to decrypt when a new block is encountered, or if it's the last block. - encryptedLines.push_back(pieces[0]+pieces[1]+pieces[2]); - } - } - } - - // Handle the last code correctly. - if (currentCode.ops.size()) - { - arCodes.push_back(currentCode); - } - if (encryptedLines.size()) - { - DecryptARCode(encryptedLines, currentCode.ops); - arCodes.push_back(currentCode); - } - - ActionReplay_UpdateActiveList(); -} - -void LogInfo(const char *format, ...) -{ - if(!b_RanOnce && IsLoggingActivated()) { - char* temp = (char*)alloca(strlen(format)+512); - va_list args; - va_start(args, format); - CharArrayFromFormatV(temp, 512, format, args); - va_end(args); - LogManager::Log(LogTypes::ACTIONREPLAY, temp); - } -} - -void ActionReplayRunAllActive() -{ - if (Core::GetStartupParameter().bEnableCheats) { - for (std::vector::iterator iter = activeCodes.begin(); iter != activeCodes.end(); ++iter) - if (iter->active) { - if(!RunActionReplayCode(*iter)) - iter->active = false; - LogInfo("\n"); - } - if(!b_RanOnce) - b_RanOnce = true; - } -} - - - -bool RunActionReplayCode(const ARCode &arcode) { - // The mechanism is slightly different than what the real AR uses, so there may be compatibility problems. - u8 cmd; - u32 addr; - u32 data; - bool doFillNSlide = false; - bool doMemoryCopy = false; - int count = 0; - bool skip = false; - bool cond = false; - u32 addr_last = 0; - u32 val_last; - - code = arcode; - - LogInfo("Code Name: %s", code.name.c_str()); - LogInfo("Number of codes: %i", code.ops.size()); - - for (iter = code.ops.begin(); iter != code.ops.end(); ++iter) - { - // If conditional mode has been set to true, then run our code execution control - if (cond) - { - // Some checks on the count value - if (count == -1 || count < -2 || count > (int)code.ops.size()) - { - LogInfo("Bad Count: %i", count); - PanicAlert("Action Replay: Bad Count: %i (%s)", count, code.name.c_str()); - return false; - } - - if (skip && count > 0) { LogInfo("Line skipped"); if (count-- == 0) cond = false; continue; } // Skip n lines - if (skip && count == -2) { LogInfo("Line skipped"); continue; } // Skip all lines - - if (!skip && count == 0) { LogInfo("Line skipped"); continue; }// Skip rest of lines - if (!skip && count > 0) count--; // execute n lines - // if -2 : execute all lines - - if(b_RanOnce) - b_RanOnce = false; - } - - cmd = iter->cmd_addr >> 24; // AR command - addr = iter->cmd_addr; // AR command with address offset - data = iter->value; - - LogInfo("--- Running Code: %08x %08x ---", addr, data); - LogInfo("Command: %08x", cmd); - - // Do Fill & Slide - if (doFillNSlide) { - doFillNSlide = false; - LogInfo("Doing Fill And Slide"); - if (!ZeroCode_FillAndSlide(addr_last, addr, data)) - return false; - continue; - } - - // Memory Copy - if (doMemoryCopy) { - doMemoryCopy = false; - LogInfo("Doing Memory Copy"); - if (!ZeroCode_MemoryCopy(val_last, addr, data)) - return false; - continue; - } - - // ActionReplay program self modification codes - if (addr >= 0x00002000 && addr < 0x00003000) { - LogInfo("This action replay simulator does not support codes that modify Action Replay itself."); - PanicAlert("This action replay simulator does not support codes that modify Action Replay itself."); - return false; - } - - // skip these weird init lines - if (iter == code.ops.begin() && cmd == 1) continue; - - // Zero codes - if (addr == 0x0) // Check if the code is a zero code - { - u8 zcode = ((data >> 29) & 0x07); - LogInfo("Doing Zero Code %08x", zcode); - switch(zcode) - { - case 0x00: // END OF CODES - LogInfo("ZCode: End Of Codes"); - return true; - case 0x02: // Normal execution of codes - // Todo: Set register 1BB4 to 0 - LogInfo("ZCode: Normal execution of codes, set register 1BB4 to 0 (zcode not supported)"); - break; - case 0x03: // Executes all codes in the same row - // Todo: Set register 1BB4 to 1 - LogInfo("ZCode: Executes all codes in the same row, Set register 1BB4 to 1 (zcode not supported)"); - PanicAlert("Zero 3 code not supported"); - return false; - case 0x04: // Fill & Slide or Memory Copy - if (((addr >> 25) & 0x03) == 0x3) { - LogInfo("ZCode: Memory Copy"); - doMemoryCopy = true; - addr_last = addr; - val_last = data; - } - else - { - LogInfo("ZCode: Fill And Slide"); - doFillNSlide = true; - addr_last = addr; - } - continue; - default: - LogInfo("ZCode: Unknown"); - PanicAlert("Zero code unknown to dolphin: %08x",zcode); - return false; - } - } - - // Normal codes - u8 type = ((addr >> 27) & 0x07); - u8 subtype = ((addr >> 30) & 0x03); - LogInfo("Doing Normal Code %08x", type); - LogInfo("Subtype: %08x", subtype); - if (type >= 1 && type <= 7) { - cond = true; - LogInfo("This Normal Code is a Conditional Code"); - } - switch(type) - { - case 0x0: - if(!NormalCode_Type_0(subtype, addr, data)) - return false; - continue; - case 0x1: - LogInfo("Type 1: If Equal"); - if(!NormalCode_Type_1(subtype, addr, data, &count, &skip)) - return false; - continue; - case 0x2: - LogInfo("Type 2: If Not Equal"); - if(!NormalCode_Type_2(subtype, addr, data, &count, &skip)) - return false; - continue; - case 0x3: - LogInfo("Type 3: If Less Than (Signed)"); - if(!NormalCode_Type_3(subtype, addr, data, &count, &skip)) - return false; - continue; - case 0x4: - LogInfo("Type 4: If Greater Than (Signed)"); - if(!NormalCode_Type_4(subtype, addr, data, &count, &skip)) - return false; - continue; - case 0x5: - LogInfo("Type 5: If Less Than (Unsigned)"); - if(!NormalCode_Type_5(subtype, addr, data, &count, &skip)) - return false; - continue; - case 0x6: - LogInfo("Type 6: If Greater Than (Unsigned)"); - if(!NormalCode_Type_6(subtype, addr, data, &count, &skip)) - return false; - continue; - case 0x7: - LogInfo("Type 7: If AND"); - if(!NormalCode_Type_7(subtype, addr, data, &count, &skip)) - return false; - continue; - default: - LogInfo("Bad Normal Code type"); - PanicAlert("Action Replay: Invalid Normal Code Type %08x (%s)", type, code.name.c_str()); - } - } - - if(b_RanOnce && cond) - b_RanOnce = true; - - return true; -} - - - -// Subtypes -bool Subtype_RamWriteAndFill(u32 addr, u32 data) -{ - u32 new_addr = ((addr & 0x7FFFFF) | 0x80000000); // real GC address - u8 size = ((addr >> 25) & 0x03); - LogInfo("Hardware Address: %08x", new_addr); - LogInfo("Size: %08x", size); - switch (size) - { - case 0x00: // Byte write - { - LogInfo("Byte Write"); - LogInfo("--------"); - u8 repeat = data >> 8; - for (int i = 0; i <= repeat; i++) { - Memory::Write_U8(data & 0xFF, new_addr + i); - LogInfo("Wrote %08x to address %08x", data & 0xFF, new_addr + i); - } - LogInfo("--------"); - break; - } - - case 0x01: // Short write - { - LogInfo("Short Write"); - LogInfo("--------"); - u16 repeat = data >> 16; - for (int i = 0; i <= repeat; i++) { - Memory::Write_U16(data & 0xFFFF, new_addr + i * 2); - LogInfo("Wrote %08x to address %08x", data & 0xFFFF, new_addr + i * 2); - } - LogInfo("--------"); - break; - } - case 0x03: //some codes use 03, but its just the same as 02... - LogInfo("The odd size 3 code (we just decided to write a U32 for this)"); - case 0x02: // Dword write - LogInfo("Dword Write"); - LogInfo("--------"); - Memory::Write_U32(data, new_addr); - LogInfo("Wrote %08x to address %08x", data, new_addr); - LogInfo("--------"); - break; - default: - LogInfo("Bad Size"); - PanicAlert("Action Replay Error: Invalid size (%08x : address = %08x) in Ram Write And Fill (%s)", size, addr, code.name.c_str()); - return false; - } - return true; -} - -bool Subtype_WriteToPointer(u32 addr, u32 data) -{ - u32 new_addr = ((addr & 0x7FFFFF) | 0x80000000); - u8 size = ((addr >> 25) & 0x03); - LogInfo("Hardware Address: %08x", new_addr); - LogInfo("Size: %08x", size); - switch (size) - { - case 0x00: // Byte write to pointer [40] - { - LogInfo("Write byte to pointer"); - LogInfo("--------"); - u32 ptr = Memory::Read_U32(new_addr); - u8 thebyte = data & 0xFF; - u32 offset = data >> 8; - LogInfo("Pointer: %08x", ptr); - LogInfo("Byte: %08x", thebyte); - LogInfo("Offset: %08x", offset); - Memory::Write_U8(thebyte, ptr + offset); - LogInfo("Wrote %08x to address %08x", thebyte, ptr + offset); - LogInfo("--------"); - break; - } - - case 0x01: // Short write to pointer [42] - { - LogInfo("Write short to pointer"); - LogInfo("--------"); - u32 ptr = Memory::Read_U32(new_addr); - u16 theshort = data & 0xFFFF; - u32 offset = (data >> 16) << 1; - LogInfo("Pointer: %08x", ptr); - LogInfo("Byte: %08x", theshort); - LogInfo("Offset: %08x", offset); - Memory::Write_U16(theshort, ptr + offset); - LogInfo("Wrote %08x to address %08x", theshort, ptr + offset); - LogInfo("--------"); - break; - } - case 0x03: - case 0x02: // Dword write to pointer [44] - LogInfo("Write dword to pointer"); - LogInfo("--------"); - Memory::Write_U32(data, Memory::Read_U32(new_addr)); - LogInfo("Wrote %08x to address %08x", data, Memory::Read_U32(new_addr)); - LogInfo("--------"); - break; - - default: - LogInfo("Bad Size"); - PanicAlert("Action Replay Error: Invalid size (%08x : address = %08x) in Write To Pointer (%s)", size, addr, code.name.c_str()); - return false; - } - return true; -} - -bool Subtype_AddCode(u32 addr, u32 data) -{ - u32 new_addr = (addr & 0x81FFFFFF); - u8 size = ((addr >> 25) & 0x03); - LogInfo("Hardware Address: %08x", new_addr); - LogInfo("Size: %08x", size); - switch (size) - { - case 0x0: // Byte add - LogInfo("Byte Add"); - LogInfo("--------"); - Memory::Write_U8(Memory::Read_U8(new_addr) + (data & 0xFF), new_addr); - LogInfo("Wrote %08x to address %08x", Memory::Read_U8(new_addr) + (data & 0xFF), new_addr); - LogInfo("--------"); - break; - case 0x1: // Short add - LogInfo("Short Add"); - LogInfo("--------"); - Memory::Write_U16(Memory::Read_U16(new_addr) + (data & 0xFFFF), new_addr); - LogInfo("Wrote %08x to address %08x", Memory::Read_U16(new_addr) + (data & 0xFFFF), new_addr); - LogInfo("--------"); - break; - case 0x2: // DWord add - LogInfo("Dword Add"); - LogInfo("--------"); - Memory::Write_U32(Memory::Read_U32(new_addr) + data, new_addr); - LogInfo("Wrote %08x to address %08x", Memory::Read_U32(new_addr) + data, new_addr); - LogInfo("--------"); - break; - case 0x3: // Float add (not working?) - { - LogInfo("Float Add"); - LogInfo("--------"); - float newval = (float)Memory::Read_U32(new_addr) + (float)data; - Memory::Write_U32((u32)newval, new_addr); - LogInfo("Wrote %08x to address %08x", (u32)newval, new_addr); - LogInfo("--------"); - break; - } - default: - LogInfo("Bad Size"); - PanicAlert("Action Replay Error: Invalid size(%08x : address = %08x) in Add Code (%s)", size, addr, code.name.c_str()); - return false; - } - return true; -} - -bool Subtype_MasterCodeAndWriteToCCXXXXXX() -{ - // code not yet implemented - TODO - PanicAlert("Action Replay Error: Master Code and Write To CCXXXXXX not implemented (%s)", code.name.c_str()); - return false; -} - - -// Zero Codes -bool ZeroCode_FillAndSlide(u32 addr_last, u32 addr, u32 data) // This needs more testing -{ - u32 new_addr = (addr_last & 0x81FFFFFF); - u8 size = ((new_addr >> 25) & 0x03); - int addr_incr; - u32 val = addr; - int val_incr; - u8 write_num = ((data & 0x78000) >> 16); // Z2 - u32 curr_addr = new_addr; - LogInfo("Current Hardware Address: %08x", new_addr); - LogInfo("Size: %08x", size); - LogInfo("Write Num: %08x", write_num); - - if (write_num < 1) { - LogInfo("Write Num is less than 1, exiting Fill and Slide call..."); - return true; - } - - if ((data >> 24) >> 3) { // z1 >> 3 - addr_incr = ((data & 0x7FFF) + 0xFFFF0000); // FFFFZ3Z4 - val_incr = (int)((data & 0x7F) + 0xFFFFFF00); // FFFFFFZ1 - } - else { - addr_incr = (data & 0x7FFF); // 0000Z3Z4 - val_incr = (int)(data & 0x7F); // 000000Z1 - } - - LogInfo("Address Increment: %08x", addr_incr); - LogInfo("Value Increment: %08x", val_incr); - - // Correct? - if (val_incr < 0) - { - curr_addr = new_addr + (addr_incr * write_num); - LogInfo("Value increment is less than 0, we need to go to the last address"); - LogInfo("Current Hardware Address Update: %08x", curr_addr); - } - - switch(size) - { - case 0x0: // Byte - LogInfo("Byte Write"); - LogInfo("--------"); - for (int i=0; i < write_num; i++) { - u8 repeat = val >> 8; - for(int j=0; j < repeat; j++) { - Memory::Write_U8(val & 0xFF, new_addr + j); - LogInfo("Write %08x to address %08x", val & 0xFF, new_addr + j); - val += val_incr; - curr_addr += addr_incr; - LogInfo("Value Update: %08x", val); - LogInfo("Current Hardware Address Update: %08x", curr_addr); - } - } - LogInfo("--------"); - break; - case 0x1: // Halfword - addr_incr >>= 1; - LogInfo ("Address increment shifted right by 1: %08x", addr_incr); - LogInfo("Short Write"); - LogInfo("--------"); - for (int i=0; i < write_num; i++) { - u8 repeat = val >> 16; - for(int j=0; j < repeat; j++) { - Memory::Write_U16(val & 0xFFFF, new_addr + j * 2); - LogInfo("Write %08x to address %08x", val & 0xFFFF, new_addr + j * 2); - val += val_incr; - curr_addr += addr_incr; - LogInfo("Value Update: %08x", val); - LogInfo("Current Hardware Address Update: %08x", curr_addr); - } - } - LogInfo("--------"); - break; - case 0x2: // Word - addr_incr >>= 2; - LogInfo ("Address increment shifted right by 2: %08x", addr_incr); - LogInfo("Word Write"); - LogInfo("--------"); - for (int i=0; i < write_num; i++) { - Memory::Write_U32(val, new_addr); - LogInfo("Write %08x to address %08x", val, new_addr); - val += val_incr; - curr_addr += addr_incr; - LogInfo("Value Update: %08x", val); - LogInfo("Current Hardware Address Update: %08x", curr_addr); - } - LogInfo("--------"); - break; - default: - LogInfo("Bad Size"); - PanicAlert("Action Replay Error: Invalid size (%08x : address = %08x) in Fill and Slide (%s)", size, new_addr, code.name.c_str()); - return false; - } - return true; -} - -bool ZeroCode_MemoryCopy(u32 val_last, u32 addr, u32 data) // Has not been tested -{ - u32 addr_dest = (val_last | 0x06000000); - u32 addr_src = ((addr & 0x7FFFFF) | 0x80000000); - u8 num_bytes = (data & 0x7FFF); - LogInfo("Dest Address: %08x", addr_dest); - LogInfo("Src Address: %08x", addr_src); - LogInfo("Size: %08x", num_bytes); - - if ((data & ~0x7FFF) == 0x0000) - { - if((data >> 24) != 0x0) - { // Memory Copy With Pointers Support - LogInfo("Memory Copy With Pointers Support"); - LogInfo("--------"); - for (int i = 0; i < 138; i++) { - Memory::Write_U8(Memory::Read_U8(addr_src + i), addr_dest + i); - LogInfo("Wrote %08x to address %08x", Memory::Read_U8(addr_src + i), addr_dest + i); - } - LogInfo("--------"); - } - else - { // Memory Copy Without Pointer Support - LogInfo("Memory Copy Without Pointers Support"); - LogInfo("--------"); - for (int i=0; i < num_bytes; i++) { - Memory::Write_U32(Memory::Read_U32(addr_src + i), addr_dest + i); - LogInfo("Wrote %08x to address %08x", Memory::Read_U32(addr_src + i), addr_dest + i); - } - LogInfo("--------"); - return true; - } - } - else - { - LogInfo("Bad Value"); - PanicAlert("Action Replay Error: Invalid value (&08x) in Memory Copy (%s)", (data & ~0x7FFF), code.name.c_str()); - return false; - } - return true; -} - -// Normal Codes -bool NormalCode_Type_0(u8 subtype, u32 addr, u32 data) -{ - switch (subtype) - { - case 0x0: // Ram write (and fill) - LogInfo("Doing Ram Write And Fill"); - if (!Subtype_RamWriteAndFill(addr, data)) - return false; - break; - case 0x1: // Write to pointer - LogInfo("Doing Write To Pointer"); - if (!Subtype_WriteToPointer(addr, data)) - return false; - break; - case 0x2: // Add code - LogInfo("Doing Add Code"); - if (!Subtype_AddCode(addr, data)) - return false; - break; - case 0x3: // Master Code & Write to CCXXXXXX - LogInfo("Doing Master Code And Write to CCXXXXXX (ncode not supported)"); - if (!Subtype_MasterCodeAndWriteToCCXXXXXX()) - return false; - break; - default: - LogInfo("Bad Subtype"); - PanicAlert("Action Replay: Normal Code 0: Invalid Subtype %08x (%s)", subtype, code.name.c_str()); - return false; - } - return true; -} -// Conditional Codes -bool NormalCode_Type_1(u8 subtype, u32 addr, u32 data, int *count, bool *skip) -{ - u8 size = (addr >> 25) & 0x03; - u32 new_addr = ((addr & 0x7FFFFF) | 0x80000000); - LogInfo("Size: %08x", size); - LogInfo("Hardware Address: %08x", new_addr); - bool con = true; - switch(size) - { - case 0x0: con = (Memory::Read_U8(new_addr) == (u8)(data & 0xFF)); break; - case 0x1: con = (Memory::Read_U16(new_addr) == (u16)(data & 0xFFFF)); break; - case 0x3: - case 0x2: con = (Memory::Read_U32(new_addr) == data); break; - default: - LogInfo("Bad Size"); - PanicAlert("Action Replay: Normal Code 1: Invalid Size %08x (%s)", size, code.name.c_str()); - return false; - } - - *skip = !con; // set skip - LogInfo("Skip set to %s", !con ? "False" : "True"); - - switch(subtype) - { - case 0x0: *count = 1; break; // 1 line - case 0x1: *count = 2; break; // 2 lines - case 0x2: *count = -2; break; // all lines - case 0x3: *count = -2; break; // While != : skip all codes ("infinite loop on the code" ?) - default: - LogInfo("Bad Subtype"); - PanicAlert("Action Replay: Normal Code 1: Invalid subtype %08x (%s)", subtype, code.name.c_str()); - return false; - } - return true; -} -bool NormalCode_Type_2(u8 subtype, u32 addr, u32 data, int *count, bool *skip) -{ - u8 size = (addr >> 25) & 0x03; - u32 new_addr = ((addr & 0x7FFFFF) | 0x80000000); - LogInfo("Size: %08x", size); - LogInfo("Hardware Address: %08x", new_addr); - bool con = true; - switch(size) - { - case 0x0: con = (Memory::Read_U8(new_addr) != (u8)(data & 0xFF)); break; - case 0x1: con = (Memory::Read_U16(new_addr) != (u16)(data & 0xFFFF)); break; - case 0x3: - case 0x2: con = (Memory::Read_U32(new_addr) != data); break; - default: - LogInfo("Bad Size"); - PanicAlert("Action Replay: Normal Code 2: Invalid Size %08x (%s)", size, code.name.c_str()); - return false; - } - - *skip = !con; // set skip - LogInfo("Skip set to %s", !con ? "False" : "True"); - - switch(subtype) - { - case 0x0: *count = 1; break; // 1 line - case 0x1: *count = 2; break; // 2 lines - case 0x2: *count = -2; break; // all lines - case 0x3: *count = -2; break; // While != : skip all codes ("infinite loop on the code" ?) - default: - LogInfo("Bad Subtype"); - PanicAlert("Action Replay: Normal Code 2: Invalid subtype %08x (%s)", subtype, code.name.c_str()); - return false; - } - return true; -} -bool NormalCode_Type_3(u8 subtype, u32 addr, u32 data, int *count, bool *skip) -{ - u8 size = (addr >> 25) & 0x03; - u32 new_addr = ((addr & 0x7FFFFF) | 0x80000000); - LogInfo("Size: %08x", size); - LogInfo("Hardware Address: %08x", new_addr); - bool con = true; - switch(size) - { - case 0x0: con = ((char)Memory::Read_U8(new_addr) < (char)(data & 0xFF)); break; - case 0x1: con = ((short)Memory::Read_U16(new_addr) < (short)(data & 0xFFFF)); break; - case 0x3: - case 0x2: con = ((int)Memory::Read_U32(new_addr) < (int)data); break; - default: - LogInfo("Bad Size"); - PanicAlert("Action Replay: Normal Code 3: Invalid Size %08x (%s)", size, code.name.c_str()); - return false; - } - - *skip = !con; // set skip - LogInfo("Skip set to %s", !con ? "False" : "True"); - - switch(subtype) - { - case 0x0: *count = 1; break; // 1 line - case 0x1: *count = 2; break; // 2 lines - case 0x2: *count = -2; break; // all lines - case 0x3: *count = -2; break; // While != : skip all codes ("infinite loop on the code" ?) - default: - LogInfo("Bad Subtype"); - PanicAlert("Action Replay: Normal Code 3: Invalid subtype %08x (%s)", subtype, code.name.c_str()); - return false; - } - return true; -} -bool NormalCode_Type_4(u8 subtype, u32 addr, u32 data, int *count, bool *skip) -{ - u8 size = (addr >> 25) & 0x03; - u32 new_addr = ((addr & 0x7FFFFF) | 0x80000000); - LogInfo("Size: %08x", size); - LogInfo("Hardware Address: %08x", new_addr); - bool con = true; - switch(size) - { - case 0x0: con = ((char)Memory::Read_U8(new_addr) > (char)(data & 0xFF)); break; - case 0x1: con = ((short)Memory::Read_U16(new_addr) > (short)(data & 0xFFFF)); break; - case 0x3: - case 0x2: con = ((int)Memory::Read_U32(new_addr) > (int)data); break; - default: - LogInfo("Bad Size"); - PanicAlert("Action Replay: Normal Code 4: Invalid Size %08x (%s)", size, code.name.c_str()); - return false; - } - - *skip = !con; // set skip - LogInfo("Skip set to %s", !con ? "False" : "True"); - - switch(subtype) - { - case 0x0: *count = 1; break; // 1 line - case 0x1: *count = 2; break; // 2 lines - case 0x2: *count = -2; break; // all lines - case 0x3: *count = -2; break; // While != : skip all codes ("infinite loop on the code" ?) - default: - LogInfo("Bad Subtype"); - PanicAlert("Action Replay: Normal Code 4: Invalid subtype %08x (%s)", subtype, code.name.c_str()); - return false; - } - return true; -} -bool NormalCode_Type_5(u8 subtype, u32 addr, u32 data, int *count, bool *skip) -{ - u8 size = (addr >> 25) & 0x03; - u32 new_addr = ((addr & 0x7FFFFF) | 0x80000000); - LogInfo("Size: %08x", size); - LogInfo("Hardware Address: %08x", new_addr); - bool con = true; - switch(size) - { - case 0x0: con = (Memory::Read_U8(new_addr) < (data & 0xFF)); break; - case 0x1: con = (Memory::Read_U16(new_addr) < (data & 0xFFFF)); break; - case 0x3: - case 0x2: con = (Memory::Read_U32(new_addr) < data); break; - default: - LogInfo("Bad Size"); - PanicAlert("Action Replay: Normal Code 5: Invalid Size %08x (%s)", size, code.name.c_str()); - return false; - } - - *skip = !con; // set skip - LogInfo("Skip set to %s", !con ? "False" : "True"); - - switch(subtype) - { - case 0x0: *count = 1; break; // 1 line - case 0x1: *count = 2; break; // 2 lines - case 0x2: *count = -2; break; // all lines - case 0x3: *count = -2; break; // While != : skip all codes ("infinite loop on the code" ?) - default: - LogInfo("Bad Subtype"); - PanicAlert("Action Replay: Normal Code 5: Invalid subtype %08x (%s)", subtype, code.name.c_str()); - return false; - } - return true; -} -bool NormalCode_Type_6(u8 subtype, u32 addr, u32 data, int *count, bool *skip) -{ - u8 size = (addr >> 25) & 0x03; - u32 new_addr = ((addr & 0x7FFFFF) | 0x80000000); - LogInfo("Size: %08x", size); - LogInfo("Hardware Address: %08x", new_addr); - bool con = true; - switch(size) - { - case 0x0: con = (Memory::Read_U8(new_addr) > (data & 0xFF)); break; - case 0x1: con = (Memory::Read_U16(new_addr) > (data & 0xFFFF)); break; - case 0x3: - case 0x2: con = (Memory::Read_U32(new_addr) > data); break; - default: - LogInfo("Bad Size"); - PanicAlert("Action Replay: Normal Code 6: Invalid Size %08x (%s)", size, code.name.c_str()); - return false; - } - - *skip = !con; // set skip - LogInfo("Skip set to %s", !con ? "False" : "True"); - - switch(subtype) - { - case 0x0: *count = 1; break; // 1 line - case 0x1: *count = 2; break; // 2 lines - case 0x2: *count = -2; break; // all lines - case 0x3: *count = -2; break; // While != : skip all codes ("infinite loop on the code" ?) - default: - LogInfo("Bad Subtype"); - PanicAlert("Action Replay: Normal Code 6: Invalid subtype %08x (%s)", subtype, code.name.c_str()); - return false; - } - return true; -} -bool NormalCode_Type_7(u8 subtype, u32 addr, u32 data, int *count, bool *skip) -{ - u8 size = (addr >> 25) & 0x03; - u32 new_addr = ((addr & 0x7FFFFF) | 0x80000000); - LogInfo("Size: %08x", size); - LogInfo("Hardware Address: %08x", new_addr); - bool con = true; - switch(size) - { - case 0x0: con = ((Memory::Read_U8(new_addr) & (data & 0xFF)) != 0); break; - case 0x1: con = ((Memory::Read_U16(new_addr) & (data & 0xFFFF)) != 0); break; - case 0x3: - case 0x2: con = ((Memory::Read_U32(new_addr) & data) != 0); break; - default: - LogInfo("Bad Size"); - PanicAlert("Action Replay: Normal Code 7: Invalid Size %08x (%s)", size, code.name.c_str()); - return false; - } - - *skip = !con; // set skip - LogInfo("Skip set to %s", !con ? "False" : "True"); - - switch(subtype) - { - case 0x0: *count = 1; break; // 1 line - case 0x1: *count = 2; break; // 2 lines - case 0x2: *count = -2; break; // all lines - case 0x3: *count = -2; break; // While != : skip all codes ("infinite loop on the code" ?) - default: - LogInfo("Bad Subtype"); - PanicAlert("Action Replay: Normal Code 7: Invalid subtype %08x (%s)", subtype, code.name.c_str()); - return false; - } - return true; -} - -size_t ActionReplay_GetCodeListSize() -{ - return arCodes.size(); -} -ARCode ActionReplay_GetARCode(size_t index) -{ - if (index > arCodes.size()) - { - PanicAlert("ActionReplay_GetARCode: Index is greater than ar code list size %i", index); - return ARCode(); - } - return arCodes[index]; -} -void ActionReplay_SetARCode_IsActive(bool active, size_t index) -{ - if (index > arCodes.size()) - { - PanicAlert("ActionReplay_SetARCode_IsActive: Index is greater than ar code list size %i", index); - return; - } - arCodes[index].active = active; - ActionReplay_UpdateActiveList(); -} -void ActionReplay_UpdateActiveList() -{ - b_RanOnce = false; - activeCodes.clear(); - for (size_t i = 0; i < arCodes.size(); i++) - { - if (arCodes[i].active) - activeCodes.push_back(arCodes[i]); - } -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + + +// Simple partial Action Replay code system implementation. + +// Will never be able to support some AR codes - specifically those that patch the running +// Action Replay engine itself - yes they do exist!!! + +// Action Replay actually is a small virtual machine with a limited number of commands. +// It probably is Turing complete - but what does that matter when AR codes can write +// actual PowerPC code. + +#include +#include + +#include "Common.h" +#include "StringUtil.h" +#include "HW/Memmap.h" +#include "ActionReplay.h" +#include "Core.h" +#include "ARDecrypt.h" +#include "LogManager.h" + +namespace +{ + static std::vector::const_iterator iter; + static ARCode code; + static bool b_RanOnce = false; + static std::vector arCodes; + static std::vector activeCodes; +} // namespace + +void LogInfo(const char *format, ...); +// --- Codes --- +// SubTypes (Normal 0 Codes) +bool Subtype_RamWriteAndFill(u32 addr, u32 data); +bool Subtype_WriteToPointer(u32 addr, u32 data); +bool Subtype_AddCode(u32 addr, u32 data); +bool Subtype_MasterCodeAndWriteToCCXXXXXX(); +// Zero Codes +bool ZeroCode_FillAndSlide(u32 addr_last, u32 addr, u32 data); +bool ZeroCode_MemoryCopy(u32 val_last, u32 addr, u32 data); +// Normal Codes +bool NormalCode_Type_0(u8 subtype, u32 addr, u32 data); +bool NormalCode_Type_1(u8 subtype, u32 addr, u32 data, int *count, bool *skip); +bool NormalCode_Type_2(u8 subtype, u32 addr, u32 data, int *count, bool *skip); +bool NormalCode_Type_3(u8 subtype, u32 addr, u32 data, int *count, bool *skip); +bool NormalCode_Type_4(u8 subtype, u32 addr, u32 data, int *count, bool *skip); +bool NormalCode_Type_5(u8 subtype, u32 addr, u32 data, int *count, bool *skip); +bool NormalCode_Type_6(u8 subtype, u32 addr, u32 data, int *count, bool *skip); +bool NormalCode_Type_7(u8 subtype, u32 addr, u32 data, int *count, bool *skip); + +void LoadActionReplayCodes(IniFile &ini) +{ + // Parses the Action Replay section of a game ini file. + if (!Core::GetStartupParameter().bEnableCheats) + return; // If cheats are off, do not load them + + std::vector lines; + std::vector encryptedLines; + ARCode currentCode; + arCodes.clear(); + + if (!ini.GetLines("ActionReplay", lines)) + return; // no codes found. + + for (std::vector::const_iterator it = lines.begin(); it != lines.end(); ++it) + { + std::string line = *it; + std::vector pieces; + + // Check if the line is a name of the code + if (line[0] == '+' || line[0] == '$') + { + if (currentCode.ops.size()) + { + arCodes.push_back(currentCode); + currentCode.ops.clear(); + } + if (encryptedLines.size()) + { + DecryptARCode(encryptedLines, currentCode.ops); + arCodes.push_back(currentCode); + currentCode.ops.clear(); + encryptedLines.clear(); + } + + if(line.size() > 1) + { + if (line[0] == '+') + { + currentCode.active = true; + currentCode.name = line.substr(2, line.size() - 2);; + Core::DisplayMessage("AR code active: " + currentCode.name, 5000); + } + else + { + currentCode.active = false; + currentCode.name = line.substr(1, line.size() - 1); + } + } + continue; + } + + SplitString(line, " ", pieces); + + // Check if the AR code is decrypted + if (pieces.size() == 2 && pieces[0].size() == 8 && pieces[1].size() == 8) + { + AREntry op; + bool success_addr = TryParseUInt(std::string("0x") + pieces[0], &op.cmd_addr); + bool success_val = TryParseUInt(std::string("0x") + pieces[1], &op.value); + if (!(success_addr | success_val)) { + PanicAlert("Action Replay Error: invalid AR code line: %s", line.c_str()); + if (!success_addr) PanicAlert("The address is invalid"); + if (!success_val) PanicAlert("The value is invalid"); + } + else + currentCode.ops.push_back(op); + } + else + { + SplitString(line, "-", pieces); + if (pieces.size() == 3 && pieces[0].size() == 4 && pieces[1].size() == 4 && pieces[2].size() == 5) + { + // Encrypted AR code + // Decryption is done in "blocks", so we must push blocks into a vector, + // then send to decrypt when a new block is encountered, or if it's the last block. + encryptedLines.push_back(pieces[0]+pieces[1]+pieces[2]); + } + } + } + + // Handle the last code correctly. + if (currentCode.ops.size()) + { + arCodes.push_back(currentCode); + } + if (encryptedLines.size()) + { + DecryptARCode(encryptedLines, currentCode.ops); + arCodes.push_back(currentCode); + } + + ActionReplay_UpdateActiveList(); +} + +void LogInfo(const char *format, ...) +{ + if(!b_RanOnce && IsLoggingActivated()) { + char* temp = (char*)alloca(strlen(format)+512); + va_list args; + va_start(args, format); + CharArrayFromFormatV(temp, 512, format, args); + va_end(args); + LogManager::Log(LogTypes::ACTIONREPLAY, temp); + } +} + +void ActionReplayRunAllActive() +{ + if (Core::GetStartupParameter().bEnableCheats) { + for (std::vector::iterator iter = activeCodes.begin(); iter != activeCodes.end(); ++iter) + if (iter->active) { + if(!RunActionReplayCode(*iter)) + iter->active = false; + LogInfo("\n"); + } + if(!b_RanOnce) + b_RanOnce = true; + } +} + + + +bool RunActionReplayCode(const ARCode &arcode) { + // The mechanism is slightly different than what the real AR uses, so there may be compatibility problems. + u8 cmd; + u32 addr; + u32 data; + bool doFillNSlide = false; + bool doMemoryCopy = false; + int count = 0; + bool skip = false; + bool cond = false; + u32 addr_last = 0; + u32 val_last; + + code = arcode; + + LogInfo("Code Name: %s", code.name.c_str()); + LogInfo("Number of codes: %i", code.ops.size()); + + for (iter = code.ops.begin(); iter != code.ops.end(); ++iter) + { + // If conditional mode has been set to true, then run our code execution control + if (cond) + { + // Some checks on the count value + if (count == -1 || count < -2 || count > (int)code.ops.size()) + { + LogInfo("Bad Count: %i", count); + PanicAlert("Action Replay: Bad Count: %i (%s)", count, code.name.c_str()); + return false; + } + + if (skip && count > 0) { LogInfo("Line skipped"); if (count-- == 0) cond = false; continue; } // Skip n lines + if (skip && count == -2) { LogInfo("Line skipped"); continue; } // Skip all lines + + if (!skip && count == 0) { LogInfo("Line skipped"); continue; }// Skip rest of lines + if (!skip && count > 0) count--; // execute n lines + // if -2 : execute all lines + + if(b_RanOnce) + b_RanOnce = false; + } + + cmd = iter->cmd_addr >> 24; // AR command + addr = iter->cmd_addr; // AR command with address offset + data = iter->value; + + LogInfo("--- Running Code: %08x %08x ---", addr, data); + LogInfo("Command: %08x", cmd); + + // Do Fill & Slide + if (doFillNSlide) { + doFillNSlide = false; + LogInfo("Doing Fill And Slide"); + if (!ZeroCode_FillAndSlide(addr_last, addr, data)) + return false; + continue; + } + + // Memory Copy + if (doMemoryCopy) { + doMemoryCopy = false; + LogInfo("Doing Memory Copy"); + if (!ZeroCode_MemoryCopy(val_last, addr, data)) + return false; + continue; + } + + // ActionReplay program self modification codes + if (addr >= 0x00002000 && addr < 0x00003000) { + LogInfo("This action replay simulator does not support codes that modify Action Replay itself."); + PanicAlert("This action replay simulator does not support codes that modify Action Replay itself."); + return false; + } + + // skip these weird init lines + if (iter == code.ops.begin() && cmd == 1) continue; + + // Zero codes + if (addr == 0x0) // Check if the code is a zero code + { + u8 zcode = ((data >> 29) & 0x07); + LogInfo("Doing Zero Code %08x", zcode); + switch(zcode) + { + case 0x00: // END OF CODES + LogInfo("ZCode: End Of Codes"); + return true; + case 0x02: // Normal execution of codes + // Todo: Set register 1BB4 to 0 + LogInfo("ZCode: Normal execution of codes, set register 1BB4 to 0 (zcode not supported)"); + break; + case 0x03: // Executes all codes in the same row + // Todo: Set register 1BB4 to 1 + LogInfo("ZCode: Executes all codes in the same row, Set register 1BB4 to 1 (zcode not supported)"); + PanicAlert("Zero 3 code not supported"); + return false; + case 0x04: // Fill & Slide or Memory Copy + if (((addr >> 25) & 0x03) == 0x3) { + LogInfo("ZCode: Memory Copy"); + doMemoryCopy = true; + addr_last = addr; + val_last = data; + } + else + { + LogInfo("ZCode: Fill And Slide"); + doFillNSlide = true; + addr_last = addr; + } + continue; + default: + LogInfo("ZCode: Unknown"); + PanicAlert("Zero code unknown to dolphin: %08x",zcode); + return false; + } + } + + // Normal codes + u8 type = ((addr >> 27) & 0x07); + u8 subtype = ((addr >> 30) & 0x03); + LogInfo("Doing Normal Code %08x", type); + LogInfo("Subtype: %08x", subtype); + if (type >= 1 && type <= 7) { + cond = true; + LogInfo("This Normal Code is a Conditional Code"); + } + switch(type) + { + case 0x0: + if(!NormalCode_Type_0(subtype, addr, data)) + return false; + continue; + case 0x1: + LogInfo("Type 1: If Equal"); + if(!NormalCode_Type_1(subtype, addr, data, &count, &skip)) + return false; + continue; + case 0x2: + LogInfo("Type 2: If Not Equal"); + if(!NormalCode_Type_2(subtype, addr, data, &count, &skip)) + return false; + continue; + case 0x3: + LogInfo("Type 3: If Less Than (Signed)"); + if(!NormalCode_Type_3(subtype, addr, data, &count, &skip)) + return false; + continue; + case 0x4: + LogInfo("Type 4: If Greater Than (Signed)"); + if(!NormalCode_Type_4(subtype, addr, data, &count, &skip)) + return false; + continue; + case 0x5: + LogInfo("Type 5: If Less Than (Unsigned)"); + if(!NormalCode_Type_5(subtype, addr, data, &count, &skip)) + return false; + continue; + case 0x6: + LogInfo("Type 6: If Greater Than (Unsigned)"); + if(!NormalCode_Type_6(subtype, addr, data, &count, &skip)) + return false; + continue; + case 0x7: + LogInfo("Type 7: If AND"); + if(!NormalCode_Type_7(subtype, addr, data, &count, &skip)) + return false; + continue; + default: + LogInfo("Bad Normal Code type"); + PanicAlert("Action Replay: Invalid Normal Code Type %08x (%s)", type, code.name.c_str()); + } + } + + if(b_RanOnce && cond) + b_RanOnce = true; + + return true; +} + + + +// Subtypes +bool Subtype_RamWriteAndFill(u32 addr, u32 data) +{ + u32 new_addr = ((addr & 0x7FFFFF) | 0x80000000); // real GC address + u8 size = ((addr >> 25) & 0x03); + LogInfo("Hardware Address: %08x", new_addr); + LogInfo("Size: %08x", size); + switch (size) + { + case 0x00: // Byte write + { + LogInfo("Byte Write"); + LogInfo("--------"); + u8 repeat = data >> 8; + for (int i = 0; i <= repeat; i++) { + Memory::Write_U8(data & 0xFF, new_addr + i); + LogInfo("Wrote %08x to address %08x", data & 0xFF, new_addr + i); + } + LogInfo("--------"); + break; + } + + case 0x01: // Short write + { + LogInfo("Short Write"); + LogInfo("--------"); + u16 repeat = data >> 16; + for (int i = 0; i <= repeat; i++) { + Memory::Write_U16(data & 0xFFFF, new_addr + i * 2); + LogInfo("Wrote %08x to address %08x", data & 0xFFFF, new_addr + i * 2); + } + LogInfo("--------"); + break; + } + case 0x03: //some codes use 03, but its just the same as 02... + LogInfo("The odd size 3 code (we just decided to write a U32 for this)"); + case 0x02: // Dword write + LogInfo("Dword Write"); + LogInfo("--------"); + Memory::Write_U32(data, new_addr); + LogInfo("Wrote %08x to address %08x", data, new_addr); + LogInfo("--------"); + break; + default: + LogInfo("Bad Size"); + PanicAlert("Action Replay Error: Invalid size (%08x : address = %08x) in Ram Write And Fill (%s)", size, addr, code.name.c_str()); + return false; + } + return true; +} + +bool Subtype_WriteToPointer(u32 addr, u32 data) +{ + u32 new_addr = ((addr & 0x7FFFFF) | 0x80000000); + u8 size = ((addr >> 25) & 0x03); + LogInfo("Hardware Address: %08x", new_addr); + LogInfo("Size: %08x", size); + switch (size) + { + case 0x00: // Byte write to pointer [40] + { + LogInfo("Write byte to pointer"); + LogInfo("--------"); + u32 ptr = Memory::Read_U32(new_addr); + u8 thebyte = data & 0xFF; + u32 offset = data >> 8; + LogInfo("Pointer: %08x", ptr); + LogInfo("Byte: %08x", thebyte); + LogInfo("Offset: %08x", offset); + Memory::Write_U8(thebyte, ptr + offset); + LogInfo("Wrote %08x to address %08x", thebyte, ptr + offset); + LogInfo("--------"); + break; + } + + case 0x01: // Short write to pointer [42] + { + LogInfo("Write short to pointer"); + LogInfo("--------"); + u32 ptr = Memory::Read_U32(new_addr); + u16 theshort = data & 0xFFFF; + u32 offset = (data >> 16) << 1; + LogInfo("Pointer: %08x", ptr); + LogInfo("Byte: %08x", theshort); + LogInfo("Offset: %08x", offset); + Memory::Write_U16(theshort, ptr + offset); + LogInfo("Wrote %08x to address %08x", theshort, ptr + offset); + LogInfo("--------"); + break; + } + case 0x03: + case 0x02: // Dword write to pointer [44] + LogInfo("Write dword to pointer"); + LogInfo("--------"); + Memory::Write_U32(data, Memory::Read_U32(new_addr)); + LogInfo("Wrote %08x to address %08x", data, Memory::Read_U32(new_addr)); + LogInfo("--------"); + break; + + default: + LogInfo("Bad Size"); + PanicAlert("Action Replay Error: Invalid size (%08x : address = %08x) in Write To Pointer (%s)", size, addr, code.name.c_str()); + return false; + } + return true; +} + +bool Subtype_AddCode(u32 addr, u32 data) +{ + u32 new_addr = (addr & 0x81FFFFFF); + u8 size = ((addr >> 25) & 0x03); + LogInfo("Hardware Address: %08x", new_addr); + LogInfo("Size: %08x", size); + switch (size) + { + case 0x0: // Byte add + LogInfo("Byte Add"); + LogInfo("--------"); + Memory::Write_U8(Memory::Read_U8(new_addr) + (data & 0xFF), new_addr); + LogInfo("Wrote %08x to address %08x", Memory::Read_U8(new_addr) + (data & 0xFF), new_addr); + LogInfo("--------"); + break; + case 0x1: // Short add + LogInfo("Short Add"); + LogInfo("--------"); + Memory::Write_U16(Memory::Read_U16(new_addr) + (data & 0xFFFF), new_addr); + LogInfo("Wrote %08x to address %08x", Memory::Read_U16(new_addr) + (data & 0xFFFF), new_addr); + LogInfo("--------"); + break; + case 0x2: // DWord add + LogInfo("Dword Add"); + LogInfo("--------"); + Memory::Write_U32(Memory::Read_U32(new_addr) + data, new_addr); + LogInfo("Wrote %08x to address %08x", Memory::Read_U32(new_addr) + data, new_addr); + LogInfo("--------"); + break; + case 0x3: // Float add (not working?) + { + LogInfo("Float Add"); + LogInfo("--------"); + float newval = (float)Memory::Read_U32(new_addr) + (float)data; + Memory::Write_U32((u32)newval, new_addr); + LogInfo("Wrote %08x to address %08x", (u32)newval, new_addr); + LogInfo("--------"); + break; + } + default: + LogInfo("Bad Size"); + PanicAlert("Action Replay Error: Invalid size(%08x : address = %08x) in Add Code (%s)", size, addr, code.name.c_str()); + return false; + } + return true; +} + +bool Subtype_MasterCodeAndWriteToCCXXXXXX() +{ + // code not yet implemented - TODO + PanicAlert("Action Replay Error: Master Code and Write To CCXXXXXX not implemented (%s)", code.name.c_str()); + return false; +} + + +// Zero Codes +bool ZeroCode_FillAndSlide(u32 addr_last, u32 addr, u32 data) // This needs more testing +{ + u32 new_addr = (addr_last & 0x81FFFFFF); + u8 size = ((new_addr >> 25) & 0x03); + int addr_incr; + u32 val = addr; + int val_incr; + u8 write_num = ((data & 0x78000) >> 16); // Z2 + u32 curr_addr = new_addr; + LogInfo("Current Hardware Address: %08x", new_addr); + LogInfo("Size: %08x", size); + LogInfo("Write Num: %08x", write_num); + + if (write_num < 1) { + LogInfo("Write Num is less than 1, exiting Fill and Slide call..."); + return true; + } + + if ((data >> 24) >> 3) { // z1 >> 3 + addr_incr = ((data & 0x7FFF) + 0xFFFF0000); // FFFFZ3Z4 + val_incr = (int)((data & 0x7F) + 0xFFFFFF00); // FFFFFFZ1 + } + else { + addr_incr = (data & 0x7FFF); // 0000Z3Z4 + val_incr = (int)(data & 0x7F); // 000000Z1 + } + + LogInfo("Address Increment: %08x", addr_incr); + LogInfo("Value Increment: %08x", val_incr); + + // Correct? + if (val_incr < 0) + { + curr_addr = new_addr + (addr_incr * write_num); + LogInfo("Value increment is less than 0, we need to go to the last address"); + LogInfo("Current Hardware Address Update: %08x", curr_addr); + } + + switch(size) + { + case 0x0: // Byte + LogInfo("Byte Write"); + LogInfo("--------"); + for (int i=0; i < write_num; i++) { + u8 repeat = val >> 8; + for(int j=0; j < repeat; j++) { + Memory::Write_U8(val & 0xFF, new_addr + j); + LogInfo("Write %08x to address %08x", val & 0xFF, new_addr + j); + val += val_incr; + curr_addr += addr_incr; + LogInfo("Value Update: %08x", val); + LogInfo("Current Hardware Address Update: %08x", curr_addr); + } + } + LogInfo("--------"); + break; + case 0x1: // Halfword + addr_incr >>= 1; + LogInfo ("Address increment shifted right by 1: %08x", addr_incr); + LogInfo("Short Write"); + LogInfo("--------"); + for (int i=0; i < write_num; i++) { + u8 repeat = val >> 16; + for(int j=0; j < repeat; j++) { + Memory::Write_U16(val & 0xFFFF, new_addr + j * 2); + LogInfo("Write %08x to address %08x", val & 0xFFFF, new_addr + j * 2); + val += val_incr; + curr_addr += addr_incr; + LogInfo("Value Update: %08x", val); + LogInfo("Current Hardware Address Update: %08x", curr_addr); + } + } + LogInfo("--------"); + break; + case 0x2: // Word + addr_incr >>= 2; + LogInfo ("Address increment shifted right by 2: %08x", addr_incr); + LogInfo("Word Write"); + LogInfo("--------"); + for (int i=0; i < write_num; i++) { + Memory::Write_U32(val, new_addr); + LogInfo("Write %08x to address %08x", val, new_addr); + val += val_incr; + curr_addr += addr_incr; + LogInfo("Value Update: %08x", val); + LogInfo("Current Hardware Address Update: %08x", curr_addr); + } + LogInfo("--------"); + break; + default: + LogInfo("Bad Size"); + PanicAlert("Action Replay Error: Invalid size (%08x : address = %08x) in Fill and Slide (%s)", size, new_addr, code.name.c_str()); + return false; + } + return true; +} + +bool ZeroCode_MemoryCopy(u32 val_last, u32 addr, u32 data) // Has not been tested +{ + u32 addr_dest = (val_last | 0x06000000); + u32 addr_src = ((addr & 0x7FFFFF) | 0x80000000); + u8 num_bytes = (data & 0x7FFF); + LogInfo("Dest Address: %08x", addr_dest); + LogInfo("Src Address: %08x", addr_src); + LogInfo("Size: %08x", num_bytes); + + if ((data & ~0x7FFF) == 0x0000) + { + if((data >> 24) != 0x0) + { // Memory Copy With Pointers Support + LogInfo("Memory Copy With Pointers Support"); + LogInfo("--------"); + for (int i = 0; i < 138; i++) { + Memory::Write_U8(Memory::Read_U8(addr_src + i), addr_dest + i); + LogInfo("Wrote %08x to address %08x", Memory::Read_U8(addr_src + i), addr_dest + i); + } + LogInfo("--------"); + } + else + { // Memory Copy Without Pointer Support + LogInfo("Memory Copy Without Pointers Support"); + LogInfo("--------"); + for (int i=0; i < num_bytes; i++) { + Memory::Write_U32(Memory::Read_U32(addr_src + i), addr_dest + i); + LogInfo("Wrote %08x to address %08x", Memory::Read_U32(addr_src + i), addr_dest + i); + } + LogInfo("--------"); + return true; + } + } + else + { + LogInfo("Bad Value"); + PanicAlert("Action Replay Error: Invalid value (&08x) in Memory Copy (%s)", (data & ~0x7FFF), code.name.c_str()); + return false; + } + return true; +} + +// Normal Codes +bool NormalCode_Type_0(u8 subtype, u32 addr, u32 data) +{ + switch (subtype) + { + case 0x0: // Ram write (and fill) + LogInfo("Doing Ram Write And Fill"); + if (!Subtype_RamWriteAndFill(addr, data)) + return false; + break; + case 0x1: // Write to pointer + LogInfo("Doing Write To Pointer"); + if (!Subtype_WriteToPointer(addr, data)) + return false; + break; + case 0x2: // Add code + LogInfo("Doing Add Code"); + if (!Subtype_AddCode(addr, data)) + return false; + break; + case 0x3: // Master Code & Write to CCXXXXXX + LogInfo("Doing Master Code And Write to CCXXXXXX (ncode not supported)"); + if (!Subtype_MasterCodeAndWriteToCCXXXXXX()) + return false; + break; + default: + LogInfo("Bad Subtype"); + PanicAlert("Action Replay: Normal Code 0: Invalid Subtype %08x (%s)", subtype, code.name.c_str()); + return false; + } + return true; +} +// Conditional Codes +bool NormalCode_Type_1(u8 subtype, u32 addr, u32 data, int *count, bool *skip) +{ + u8 size = (addr >> 25) & 0x03; + u32 new_addr = ((addr & 0x7FFFFF) | 0x80000000); + LogInfo("Size: %08x", size); + LogInfo("Hardware Address: %08x", new_addr); + bool con = true; + switch(size) + { + case 0x0: con = (Memory::Read_U8(new_addr) == (u8)(data & 0xFF)); break; + case 0x1: con = (Memory::Read_U16(new_addr) == (u16)(data & 0xFFFF)); break; + case 0x3: + case 0x2: con = (Memory::Read_U32(new_addr) == data); break; + default: + LogInfo("Bad Size"); + PanicAlert("Action Replay: Normal Code 1: Invalid Size %08x (%s)", size, code.name.c_str()); + return false; + } + + *skip = !con; // set skip + LogInfo("Skip set to %s", !con ? "False" : "True"); + + switch(subtype) + { + case 0x0: *count = 1; break; // 1 line + case 0x1: *count = 2; break; // 2 lines + case 0x2: *count = -2; break; // all lines + case 0x3: *count = -2; break; // While != : skip all codes ("infinite loop on the code" ?) + default: + LogInfo("Bad Subtype"); + PanicAlert("Action Replay: Normal Code 1: Invalid subtype %08x (%s)", subtype, code.name.c_str()); + return false; + } + return true; +} +bool NormalCode_Type_2(u8 subtype, u32 addr, u32 data, int *count, bool *skip) +{ + u8 size = (addr >> 25) & 0x03; + u32 new_addr = ((addr & 0x7FFFFF) | 0x80000000); + LogInfo("Size: %08x", size); + LogInfo("Hardware Address: %08x", new_addr); + bool con = true; + switch(size) + { + case 0x0: con = (Memory::Read_U8(new_addr) != (u8)(data & 0xFF)); break; + case 0x1: con = (Memory::Read_U16(new_addr) != (u16)(data & 0xFFFF)); break; + case 0x3: + case 0x2: con = (Memory::Read_U32(new_addr) != data); break; + default: + LogInfo("Bad Size"); + PanicAlert("Action Replay: Normal Code 2: Invalid Size %08x (%s)", size, code.name.c_str()); + return false; + } + + *skip = !con; // set skip + LogInfo("Skip set to %s", !con ? "False" : "True"); + + switch(subtype) + { + case 0x0: *count = 1; break; // 1 line + case 0x1: *count = 2; break; // 2 lines + case 0x2: *count = -2; break; // all lines + case 0x3: *count = -2; break; // While != : skip all codes ("infinite loop on the code" ?) + default: + LogInfo("Bad Subtype"); + PanicAlert("Action Replay: Normal Code 2: Invalid subtype %08x (%s)", subtype, code.name.c_str()); + return false; + } + return true; +} +bool NormalCode_Type_3(u8 subtype, u32 addr, u32 data, int *count, bool *skip) +{ + u8 size = (addr >> 25) & 0x03; + u32 new_addr = ((addr & 0x7FFFFF) | 0x80000000); + LogInfo("Size: %08x", size); + LogInfo("Hardware Address: %08x", new_addr); + bool con = true; + switch(size) + { + case 0x0: con = ((char)Memory::Read_U8(new_addr) < (char)(data & 0xFF)); break; + case 0x1: con = ((short)Memory::Read_U16(new_addr) < (short)(data & 0xFFFF)); break; + case 0x3: + case 0x2: con = ((int)Memory::Read_U32(new_addr) < (int)data); break; + default: + LogInfo("Bad Size"); + PanicAlert("Action Replay: Normal Code 3: Invalid Size %08x (%s)", size, code.name.c_str()); + return false; + } + + *skip = !con; // set skip + LogInfo("Skip set to %s", !con ? "False" : "True"); + + switch(subtype) + { + case 0x0: *count = 1; break; // 1 line + case 0x1: *count = 2; break; // 2 lines + case 0x2: *count = -2; break; // all lines + case 0x3: *count = -2; break; // While != : skip all codes ("infinite loop on the code" ?) + default: + LogInfo("Bad Subtype"); + PanicAlert("Action Replay: Normal Code 3: Invalid subtype %08x (%s)", subtype, code.name.c_str()); + return false; + } + return true; +} +bool NormalCode_Type_4(u8 subtype, u32 addr, u32 data, int *count, bool *skip) +{ + u8 size = (addr >> 25) & 0x03; + u32 new_addr = ((addr & 0x7FFFFF) | 0x80000000); + LogInfo("Size: %08x", size); + LogInfo("Hardware Address: %08x", new_addr); + bool con = true; + switch(size) + { + case 0x0: con = ((char)Memory::Read_U8(new_addr) > (char)(data & 0xFF)); break; + case 0x1: con = ((short)Memory::Read_U16(new_addr) > (short)(data & 0xFFFF)); break; + case 0x3: + case 0x2: con = ((int)Memory::Read_U32(new_addr) > (int)data); break; + default: + LogInfo("Bad Size"); + PanicAlert("Action Replay: Normal Code 4: Invalid Size %08x (%s)", size, code.name.c_str()); + return false; + } + + *skip = !con; // set skip + LogInfo("Skip set to %s", !con ? "False" : "True"); + + switch(subtype) + { + case 0x0: *count = 1; break; // 1 line + case 0x1: *count = 2; break; // 2 lines + case 0x2: *count = -2; break; // all lines + case 0x3: *count = -2; break; // While != : skip all codes ("infinite loop on the code" ?) + default: + LogInfo("Bad Subtype"); + PanicAlert("Action Replay: Normal Code 4: Invalid subtype %08x (%s)", subtype, code.name.c_str()); + return false; + } + return true; +} +bool NormalCode_Type_5(u8 subtype, u32 addr, u32 data, int *count, bool *skip) +{ + u8 size = (addr >> 25) & 0x03; + u32 new_addr = ((addr & 0x7FFFFF) | 0x80000000); + LogInfo("Size: %08x", size); + LogInfo("Hardware Address: %08x", new_addr); + bool con = true; + switch(size) + { + case 0x0: con = (Memory::Read_U8(new_addr) < (data & 0xFF)); break; + case 0x1: con = (Memory::Read_U16(new_addr) < (data & 0xFFFF)); break; + case 0x3: + case 0x2: con = (Memory::Read_U32(new_addr) < data); break; + default: + LogInfo("Bad Size"); + PanicAlert("Action Replay: Normal Code 5: Invalid Size %08x (%s)", size, code.name.c_str()); + return false; + } + + *skip = !con; // set skip + LogInfo("Skip set to %s", !con ? "False" : "True"); + + switch(subtype) + { + case 0x0: *count = 1; break; // 1 line + case 0x1: *count = 2; break; // 2 lines + case 0x2: *count = -2; break; // all lines + case 0x3: *count = -2; break; // While != : skip all codes ("infinite loop on the code" ?) + default: + LogInfo("Bad Subtype"); + PanicAlert("Action Replay: Normal Code 5: Invalid subtype %08x (%s)", subtype, code.name.c_str()); + return false; + } + return true; +} +bool NormalCode_Type_6(u8 subtype, u32 addr, u32 data, int *count, bool *skip) +{ + u8 size = (addr >> 25) & 0x03; + u32 new_addr = ((addr & 0x7FFFFF) | 0x80000000); + LogInfo("Size: %08x", size); + LogInfo("Hardware Address: %08x", new_addr); + bool con = true; + switch(size) + { + case 0x0: con = (Memory::Read_U8(new_addr) > (data & 0xFF)); break; + case 0x1: con = (Memory::Read_U16(new_addr) > (data & 0xFFFF)); break; + case 0x3: + case 0x2: con = (Memory::Read_U32(new_addr) > data); break; + default: + LogInfo("Bad Size"); + PanicAlert("Action Replay: Normal Code 6: Invalid Size %08x (%s)", size, code.name.c_str()); + return false; + } + + *skip = !con; // set skip + LogInfo("Skip set to %s", !con ? "False" : "True"); + + switch(subtype) + { + case 0x0: *count = 1; break; // 1 line + case 0x1: *count = 2; break; // 2 lines + case 0x2: *count = -2; break; // all lines + case 0x3: *count = -2; break; // While != : skip all codes ("infinite loop on the code" ?) + default: + LogInfo("Bad Subtype"); + PanicAlert("Action Replay: Normal Code 6: Invalid subtype %08x (%s)", subtype, code.name.c_str()); + return false; + } + return true; +} +bool NormalCode_Type_7(u8 subtype, u32 addr, u32 data, int *count, bool *skip) +{ + u8 size = (addr >> 25) & 0x03; + u32 new_addr = ((addr & 0x7FFFFF) | 0x80000000); + LogInfo("Size: %08x", size); + LogInfo("Hardware Address: %08x", new_addr); + bool con = true; + switch(size) + { + case 0x0: con = ((Memory::Read_U8(new_addr) & (data & 0xFF)) != 0); break; + case 0x1: con = ((Memory::Read_U16(new_addr) & (data & 0xFFFF)) != 0); break; + case 0x3: + case 0x2: con = ((Memory::Read_U32(new_addr) & data) != 0); break; + default: + LogInfo("Bad Size"); + PanicAlert("Action Replay: Normal Code 7: Invalid Size %08x (%s)", size, code.name.c_str()); + return false; + } + + *skip = !con; // set skip + LogInfo("Skip set to %s", !con ? "False" : "True"); + + switch(subtype) + { + case 0x0: *count = 1; break; // 1 line + case 0x1: *count = 2; break; // 2 lines + case 0x2: *count = -2; break; // all lines + case 0x3: *count = -2; break; // While != : skip all codes ("infinite loop on the code" ?) + default: + LogInfo("Bad Subtype"); + PanicAlert("Action Replay: Normal Code 7: Invalid subtype %08x (%s)", subtype, code.name.c_str()); + return false; + } + return true; +} + +size_t ActionReplay_GetCodeListSize() +{ + return arCodes.size(); +} +ARCode ActionReplay_GetARCode(size_t index) +{ + if (index > arCodes.size()) + { + PanicAlert("ActionReplay_GetARCode: Index is greater than ar code list size %i", index); + return ARCode(); + } + return arCodes[index]; +} +void ActionReplay_SetARCode_IsActive(bool active, size_t index) +{ + if (index > arCodes.size()) + { + PanicAlert("ActionReplay_SetARCode_IsActive: Index is greater than ar code list size %i", index); + return; + } + arCodes[index].active = active; + ActionReplay_UpdateActiveList(); +} +void ActionReplay_UpdateActiveList() +{ + b_RanOnce = false; + activeCodes.clear(); + for (size_t i = 0; i < arCodes.size(); i++) + { + if (arCodes[i].active) + activeCodes.push_back(arCodes[i]); + } +} diff --git a/Source/Core/Core/Src/Boot/Boot.cpp b/Source/Core/Core/Src/Boot/Boot.cpp index 69214aed65..a371be5f99 100644 --- a/Source/Core/Core/Src/Boot/Boot.cpp +++ b/Source/Core/Core/Src/Boot/Boot.cpp @@ -1,298 +1,298 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "StringUtil.h" -#include "FileUtil.h" - -#include "../HLE/HLE.h" - -#include "../PowerPC/PowerPC.h" -#include "../PowerPC/PPCAnalyst.h" -#include "../Core.h" -#include "../HW/HW.h" -#include "../HW/EXI_DeviceIPL.h" -#include "../HW/Memmap.h" -#include "../HW/PeripheralInterface.h" -#include "../HW/DVDInterface.h" -#include "../HW/VideoInterface.h" -#include "../HW/CPU.h" - -#include "../Debugger/Debugger_SymbolMap.h" -#include "../Debugger/Debugger_BreakPoints.h" - -#include "Boot_DOL.h" -#include "Boot.h" -#include "../Host.h" -#include "../VolumeHandler.h" -#include "../PatchEngine.h" -#include "../PowerPC/SignatureDB.h" -#include "../PowerPC/SymbolDB.h" - -#include "../MemTools.h" -#include "MappedFile.h" - -#include "VolumeCreator.h" - -void CBoot::Load_FST(bool _bIsWii) -{ - if (VolumeHandler::IsValid()) - { - // copy first 20 bytes of disc to start of Mem 1 - VolumeHandler::ReadToPtr(Memory::GetPointer(0x80000000), 0, 0x20); - - // copy of game id - Memory::Write_U32(Memory::Read_U32(0x80000000), 0x80003180); - - u32 shift = 0; - if (_bIsWii) - shift = 2; - - u32 fstOffset = VolumeHandler::Read32(0x0424) << shift; - u32 fstSize = VolumeHandler::Read32(0x0428) << shift; - u32 maxFstSize = VolumeHandler::Read32(0x042c) << shift; - - u32 arenaHigh = 0x817FFFF4 - maxFstSize; - Memory::Write_U32(arenaHigh, 0x00000034); - - // load FST - VolumeHandler::ReadToPtr(Memory::GetPointer(arenaHigh), fstOffset, fstSize); - Memory::Write_U32(arenaHigh, 0x00000038); - Memory::Write_U32(maxFstSize, 0x0000003c); - } -} - -void CBoot::UpdateDebugger_MapLoaded(const char *_gameID) -{ - Host_NotifyMapLoaded(); - Host_UpdateMemoryView(); -} - -std::string CBoot::GenerateMapFilename() -{ - /* - std::string strDriveDirectory, strFilename; - SplitPath(booted_file, &strDriveDirectory, &strFilename, NULL); - - std::string strFullfilename(strFilename + _T(".map")); - std::string strMapFilename; - BuildCompleteFilename(strMapFilename, strDriveDirectory, strFullfilename); - */ - return FULL_MAPS_DIR + Core::GetStartupParameter().GetUniqueID() + ".map"; -} - -bool CBoot::LoadMapFromFilename(const std::string &_rFilename, const char *_gameID) -{ - if (_rFilename.size() == 0) - return false; - - std::string strMapFilename = GenerateMapFilename(); - - bool success = false; - if (!g_symbolDB.LoadMap(strMapFilename.c_str())) - { - if (_gameID != NULL) - { - BuildCompleteFilename(strMapFilename, "maps", std::string(_gameID) + ".map"); - success = g_symbolDB.LoadMap(strMapFilename.c_str()); - } - } - else - { - success = true; - } - - if (success) - UpdateDebugger_MapLoaded(); - - return success; -} - -bool CBoot::Load_BIOS(const std::string& _rBiosFilename) -{ - bool bResult = false; - Common::IMappedFile* pFile = Common::IMappedFile::CreateMappedFileDEPRECATED(); - if (pFile->Open(_rBiosFilename.c_str())) - { - if (pFile->GetSize() >= 1024*1024*2) - { - u32 CopySize = (u32)pFile->GetSize() - 0x820; - u8* pData = pFile->Lock(0x820, CopySize); - Memory::WriteBigEData(pData, 0x81300000, CopySize); - pFile->Unlock(pData); - pFile->Close(); - - PC = 0x81300000; - - bResult = true; - } - } - delete pFile; - return bResult; -} - -bool CBoot::BootUp(const SCoreStartupParameter& _StartupPara) -{ - const bool bDebugIsoBootup = false; - - g_symbolDB.Clear(); - VideoInterface::PreInit(_StartupPara.bNTSC); - switch (_StartupPara.m_BootType) - { - // GCM - // =================================================================================== - case SCoreStartupParameter::BOOT_ISO: - { - DiscIO::IVolume* pVolume = DiscIO::CreateVolumeFromFilename(_StartupPara.m_strFilename); - if (pVolume == NULL) - break; - - bool isoWii = DiscIO::IsVolumeWiiDisc(pVolume); - if (isoWii != Core::GetStartupParameter().bWii) - { - PanicAlert("Warning - starting ISO in wrong console mode!"); - } - - char gameID[7]; - memcpy(gameID, pVolume->GetUniqueID().c_str(), 6); - gameID[6] = 0; - - // setup the map from ISOFile ID - VolumeHandler::SetVolumeName(_StartupPara.m_strFilename); - - DVDInterface::SetDiscInside(true); - - if (_StartupPara.bHLEBios) - { - if (!VolumeHandler::IsWii()) - EmulatedBIOS(bDebugIsoBootup); - else - { - Core::g_CoreStartupParameter.bWii = true; - EmulatedBIOS_Wii(bDebugIsoBootup); - } - } - else - { - if (!Load_BIOS(_StartupPara.m_strBios)) - { - // fails to load a BIOS so HLE it - if (!VolumeHandler::IsWii()) - EmulatedBIOS(bDebugIsoBootup); - else - { - Core::g_CoreStartupParameter.bWii = true; - EmulatedBIOS_Wii(bDebugIsoBootup); - } - } - } - if (LoadMapFromFilename(_StartupPara.m_strFilename, gameID)) - HLE::PatchFunctions(); - } - break; - - // DOL - // =================================================================================== - case SCoreStartupParameter::BOOT_DOL: - { - CDolLoader dolLoader(_StartupPara.m_strFilename.c_str()); - PC = dolLoader.GetEntryPoint(); -#ifdef _DEBUG - if (LoadMapFromFilename(_StartupPara.m_strFilename)) - HLE::PatchFunctions(); -#endif - } - break; - - // ELF - // =================================================================================== - case SCoreStartupParameter::BOOT_ELF: - { - if(!File::Exists(_StartupPara.m_strFilename.c_str())) - { - PanicAlert("The file you specified (%s) does not exists", - _StartupPara.m_strFilename.c_str()); - return false; - } - - // Check if we have gotten a Wii file or not - bool elfWii = IsElfWii(_StartupPara.m_strFilename.c_str()); - if (elfWii != Core::GetStartupParameter().bWii) - { - PanicAlert("Warning - starting ELF in wrong console mode!"); - } - - // stop apploader from running when BIOS boots - VolumeHandler::SetVolumeName(""); - - if (elfWii) - { - EmulatedBIOS_Wii(false); - } - else - { - if (!VolumeHandler::IsWii() && !_StartupPara.m_strDefaultGCM.empty()) - { - VolumeHandler::SetVolumeName(_StartupPara.m_strDefaultGCM.c_str()); - EmulatedBIOS(false); - } - } - - // load image or create virtual drive from directory - if (!_StartupPara.m_strDVDRoot.empty()) - VolumeHandler::SetVolumeDirectory(_StartupPara.m_strDVDRoot, elfWii); - else if (!_StartupPara.m_strDefaultGCM.empty()) - VolumeHandler::SetVolumeName(_StartupPara.m_strDefaultGCM); - else - VolumeHandler::SetVolumeDirectory(_StartupPara.m_strFilename, elfWii); - - DVDInterface::SetDiscInside(VolumeHandler::IsValid()); - - Load_FST(elfWii); - - Boot_ELF(_StartupPara.m_strFilename.c_str()); - UpdateDebugger_MapLoaded(); - CBreakPoints::AddAutoBreakpoints(); - } - break; - - // BIOS - // =================================================================================== - case SCoreStartupParameter::BOOT_BIOS: - { - DVDInterface::SetDiscInside(false); - if (Load_BIOS(_StartupPara.m_strBios)) - { - if (LoadMapFromFilename(_StartupPara.m_strFilename)) - HLE::PatchFunctions(); - } - else - { - return false; - } - } - break; - - default: - { - PanicAlert("Tried to load an unknown file type."); - return false; - } - } - Host_UpdateLogDisplay(); - return true; -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "StringUtil.h" +#include "FileUtil.h" + +#include "../HLE/HLE.h" + +#include "../PowerPC/PowerPC.h" +#include "../PowerPC/PPCAnalyst.h" +#include "../Core.h" +#include "../HW/HW.h" +#include "../HW/EXI_DeviceIPL.h" +#include "../HW/Memmap.h" +#include "../HW/PeripheralInterface.h" +#include "../HW/DVDInterface.h" +#include "../HW/VideoInterface.h" +#include "../HW/CPU.h" + +#include "../Debugger/Debugger_SymbolMap.h" +#include "../Debugger/Debugger_BreakPoints.h" + +#include "Boot_DOL.h" +#include "Boot.h" +#include "../Host.h" +#include "../VolumeHandler.h" +#include "../PatchEngine.h" +#include "../PowerPC/SignatureDB.h" +#include "../PowerPC/SymbolDB.h" + +#include "../MemTools.h" +#include "MappedFile.h" + +#include "VolumeCreator.h" + +void CBoot::Load_FST(bool _bIsWii) +{ + if (VolumeHandler::IsValid()) + { + // copy first 20 bytes of disc to start of Mem 1 + VolumeHandler::ReadToPtr(Memory::GetPointer(0x80000000), 0, 0x20); + + // copy of game id + Memory::Write_U32(Memory::Read_U32(0x80000000), 0x80003180); + + u32 shift = 0; + if (_bIsWii) + shift = 2; + + u32 fstOffset = VolumeHandler::Read32(0x0424) << shift; + u32 fstSize = VolumeHandler::Read32(0x0428) << shift; + u32 maxFstSize = VolumeHandler::Read32(0x042c) << shift; + + u32 arenaHigh = 0x817FFFF4 - maxFstSize; + Memory::Write_U32(arenaHigh, 0x00000034); + + // load FST + VolumeHandler::ReadToPtr(Memory::GetPointer(arenaHigh), fstOffset, fstSize); + Memory::Write_U32(arenaHigh, 0x00000038); + Memory::Write_U32(maxFstSize, 0x0000003c); + } +} + +void CBoot::UpdateDebugger_MapLoaded(const char *_gameID) +{ + Host_NotifyMapLoaded(); + Host_UpdateMemoryView(); +} + +std::string CBoot::GenerateMapFilename() +{ + /* + std::string strDriveDirectory, strFilename; + SplitPath(booted_file, &strDriveDirectory, &strFilename, NULL); + + std::string strFullfilename(strFilename + _T(".map")); + std::string strMapFilename; + BuildCompleteFilename(strMapFilename, strDriveDirectory, strFullfilename); + */ + return FULL_MAPS_DIR + Core::GetStartupParameter().GetUniqueID() + ".map"; +} + +bool CBoot::LoadMapFromFilename(const std::string &_rFilename, const char *_gameID) +{ + if (_rFilename.size() == 0) + return false; + + std::string strMapFilename = GenerateMapFilename(); + + bool success = false; + if (!g_symbolDB.LoadMap(strMapFilename.c_str())) + { + if (_gameID != NULL) + { + BuildCompleteFilename(strMapFilename, "maps", std::string(_gameID) + ".map"); + success = g_symbolDB.LoadMap(strMapFilename.c_str()); + } + } + else + { + success = true; + } + + if (success) + UpdateDebugger_MapLoaded(); + + return success; +} + +bool CBoot::Load_BIOS(const std::string& _rBiosFilename) +{ + bool bResult = false; + Common::IMappedFile* pFile = Common::IMappedFile::CreateMappedFileDEPRECATED(); + if (pFile->Open(_rBiosFilename.c_str())) + { + if (pFile->GetSize() >= 1024*1024*2) + { + u32 CopySize = (u32)pFile->GetSize() - 0x820; + u8* pData = pFile->Lock(0x820, CopySize); + Memory::WriteBigEData(pData, 0x81300000, CopySize); + pFile->Unlock(pData); + pFile->Close(); + + PC = 0x81300000; + + bResult = true; + } + } + delete pFile; + return bResult; +} + +bool CBoot::BootUp(const SCoreStartupParameter& _StartupPara) +{ + const bool bDebugIsoBootup = false; + + g_symbolDB.Clear(); + VideoInterface::PreInit(_StartupPara.bNTSC); + switch (_StartupPara.m_BootType) + { + // GCM + // =================================================================================== + case SCoreStartupParameter::BOOT_ISO: + { + DiscIO::IVolume* pVolume = DiscIO::CreateVolumeFromFilename(_StartupPara.m_strFilename); + if (pVolume == NULL) + break; + + bool isoWii = DiscIO::IsVolumeWiiDisc(pVolume); + if (isoWii != Core::GetStartupParameter().bWii) + { + PanicAlert("Warning - starting ISO in wrong console mode!"); + } + + char gameID[7]; + memcpy(gameID, pVolume->GetUniqueID().c_str(), 6); + gameID[6] = 0; + + // setup the map from ISOFile ID + VolumeHandler::SetVolumeName(_StartupPara.m_strFilename); + + DVDInterface::SetDiscInside(true); + + if (_StartupPara.bHLEBios) + { + if (!VolumeHandler::IsWii()) + EmulatedBIOS(bDebugIsoBootup); + else + { + Core::g_CoreStartupParameter.bWii = true; + EmulatedBIOS_Wii(bDebugIsoBootup); + } + } + else + { + if (!Load_BIOS(_StartupPara.m_strBios)) + { + // fails to load a BIOS so HLE it + if (!VolumeHandler::IsWii()) + EmulatedBIOS(bDebugIsoBootup); + else + { + Core::g_CoreStartupParameter.bWii = true; + EmulatedBIOS_Wii(bDebugIsoBootup); + } + } + } + if (LoadMapFromFilename(_StartupPara.m_strFilename, gameID)) + HLE::PatchFunctions(); + } + break; + + // DOL + // =================================================================================== + case SCoreStartupParameter::BOOT_DOL: + { + CDolLoader dolLoader(_StartupPara.m_strFilename.c_str()); + PC = dolLoader.GetEntryPoint(); +#ifdef _DEBUG + if (LoadMapFromFilename(_StartupPara.m_strFilename)) + HLE::PatchFunctions(); +#endif + } + break; + + // ELF + // =================================================================================== + case SCoreStartupParameter::BOOT_ELF: + { + if(!File::Exists(_StartupPara.m_strFilename.c_str())) + { + PanicAlert("The file you specified (%s) does not exists", + _StartupPara.m_strFilename.c_str()); + return false; + } + + // Check if we have gotten a Wii file or not + bool elfWii = IsElfWii(_StartupPara.m_strFilename.c_str()); + if (elfWii != Core::GetStartupParameter().bWii) + { + PanicAlert("Warning - starting ELF in wrong console mode!"); + } + + // stop apploader from running when BIOS boots + VolumeHandler::SetVolumeName(""); + + if (elfWii) + { + EmulatedBIOS_Wii(false); + } + else + { + if (!VolumeHandler::IsWii() && !_StartupPara.m_strDefaultGCM.empty()) + { + VolumeHandler::SetVolumeName(_StartupPara.m_strDefaultGCM.c_str()); + EmulatedBIOS(false); + } + } + + // load image or create virtual drive from directory + if (!_StartupPara.m_strDVDRoot.empty()) + VolumeHandler::SetVolumeDirectory(_StartupPara.m_strDVDRoot, elfWii); + else if (!_StartupPara.m_strDefaultGCM.empty()) + VolumeHandler::SetVolumeName(_StartupPara.m_strDefaultGCM); + else + VolumeHandler::SetVolumeDirectory(_StartupPara.m_strFilename, elfWii); + + DVDInterface::SetDiscInside(VolumeHandler::IsValid()); + + Load_FST(elfWii); + + Boot_ELF(_StartupPara.m_strFilename.c_str()); + UpdateDebugger_MapLoaded(); + CBreakPoints::AddAutoBreakpoints(); + } + break; + + // BIOS + // =================================================================================== + case SCoreStartupParameter::BOOT_BIOS: + { + DVDInterface::SetDiscInside(false); + if (Load_BIOS(_StartupPara.m_strBios)) + { + if (LoadMapFromFilename(_StartupPara.m_strFilename)) + HLE::PatchFunctions(); + } + else + { + return false; + } + } + break; + + default: + { + PanicAlert("Tried to load an unknown file type."); + return false; + } + } + Host_UpdateLogDisplay(); + return true; +} diff --git a/Source/Core/Core/Src/Boot/Boot_BIOSEmu.cpp b/Source/Core/Core/Src/Boot/Boot_BIOSEmu.cpp index 0ddf40006d..549a2d4c48 100644 --- a/Source/Core/Core/Src/Boot/Boot_BIOSEmu.cpp +++ b/Source/Core/Core/Src/Boot/Boot_BIOSEmu.cpp @@ -1,400 +1,400 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" - -#include "../PowerPC/PowerPC.h" -#include "../Core.h" -#include "../HW/EXI_DeviceIPL.h" -#include "../HW/Memmap.h" -#include "../HW/DVDInterface.h" -#include "../HW/CPU.h" - -#include "../Host.h" -#include "../VolumeHandler.h" -#include "../PatchEngine.h" -#include "../MemTools.h" - -#include "VolumeCreator.h" -#include "Boot.h" - -void CBoot::RunFunction(u32 _iAddr, bool _bUseDebugger) -{ - PC = _iAddr; - LR = 0x00; - - if (_bUseDebugger) - { - CCPU::Break(); - while (PC != 0x00) - CCPU::SingleStep(); - } - else - { - while (PC != 0x00) - PowerPC::SingleStep(); - } -} - -// __________________________________________________________________________________________________ -// -// BIOS HLE: -// copy the apploader to 0x81200000 -// execute the apploader -// -void CBoot::EmulatedBIOS(bool _bDebug) -{ - LOG(BOOT, "Faking GC BIOS..."); - UReg_MSR& m_MSR = ((UReg_MSR&)PowerPC::ppcState.msr); - m_MSR.FP = 1; - - Memory::Clear(); - - // ======================================================================================= - // Write necessary values - // --------------------------------------------------------------------------------------- - /* Here we write values to memory that the apploader does not take care of. Game iso info goes - to 0x80000000 according to yagcd 4.2. I'm not sure what bytes 8-10 does (version and - streaming), but I include them anyway because it seems like they are supposed to be there. */ - // --------------------------------------------------------------------------------------- - DVDInterface::DVDRead(0x00000000, 0x80000000, 10); // write boot info needed for multidisc games - - Memory::Write_U32(0x4c000064, 0x80000300); // write default DFI Handler: rfi - Memory::Write_U32(0x4c000064, 0x80000800); // write default FPU Handler: rfi - Memory::Write_U32(0x4c000064, 0x80000C00); // write default Syscall Handler: rfi - // - Memory::Write_U32(0xc2339f3d, 0x8000001C); //game disc - Memory::Write_U32(0x0D15EA5E, 0x80000020); //funny magic word for normal boot - Memory::Write_U32(0x01800000, 0x80000028); // Physical Memory Size - -// Memory::Write_U32(0x00000003, 0x8000002C); // Console type - retail - Memory::Write_U32(0x10000006, 0x8000002C); // DevKit - - Memory::Write_U32(((1 & 0x3f) << 26) | 2, 0x81300000); // HLE OSReport for Apploader - // ======================================================================================= - - - // ======================================================================================= - // Load Apploader to Memory - The apploader is hardcoded to begin at byte 9 280 on the disc, - // but it seems like the size can be variable. Compare with yagcd chap 13. - // --------------------------------------------------------------------------------------- - PowerPC::ppcState.gpr[1] = 0x816ffff0; // StackPointer - u32 iAppLoaderOffset = 0x2440; // 0x1c40 (what is 0x1c40?) - // --------------------------------------------------------------------------------------- - u32 iAppLoaderEntry = VolumeHandler::Read32(iAppLoaderOffset + 0x10); - u32 iAppLoaderSize = VolumeHandler::Read32(iAppLoaderOffset + 0x14); - if ((iAppLoaderEntry == (u32)-1) || (iAppLoaderSize == (u32)-1)) - return; - VolumeHandler::ReadToPtr(Memory::GetPointer(0x81200000), iAppLoaderOffset + 0x20, iAppLoaderSize); - // ======================================================================================= - - - //call iAppLoaderEntry - LOG(MASTER_LOG, "Call iAppLoaderEntry"); - - u32 iAppLoaderFuncAddr = 0x80003100; - PowerPC::ppcState.gpr[3] = iAppLoaderFuncAddr + 0; - PowerPC::ppcState.gpr[4] = iAppLoaderFuncAddr + 4; - PowerPC::ppcState.gpr[5] = iAppLoaderFuncAddr + 8; - RunFunction(iAppLoaderEntry, _bDebug); - u32 iAppLoaderInit = Memory::ReadUnchecked_U32(iAppLoaderFuncAddr+0); - u32 iAppLoaderMain = Memory::ReadUnchecked_U32(iAppLoaderFuncAddr+4); - u32 iAppLoaderClose = Memory::ReadUnchecked_U32(iAppLoaderFuncAddr+8); - - // iAppLoaderInit - LOG(MASTER_LOG, "Call iAppLoaderInit"); - PowerPC::ppcState.gpr[3] = 0x81300000; - RunFunction(iAppLoaderInit, _bDebug); - - - // ======================================================================================= - /* iAppLoaderMain - Here we load the apploader, the DOL (the exe) and the FST (filesystem). - To give you an idea about where the stuff is located on the disc take a look at yagcd - ch 13. */ - // --------------------------------------------------------------------------------------- - LOG(MASTER_LOG, "Call iAppLoaderMain"); - do - { - PowerPC::ppcState.gpr[3] = 0x81300004; - PowerPC::ppcState.gpr[4] = 0x81300008; - PowerPC::ppcState.gpr[5] = 0x8130000c; - - RunFunction(iAppLoaderMain, _bDebug); - - u32 iRamAddress = Memory::ReadUnchecked_U32(0x81300004); - u32 iLength = Memory::ReadUnchecked_U32(0x81300008); - u32 iDVDOffset = Memory::ReadUnchecked_U32(0x8130000c); - - LOGV(MASTER_LOG, 2, "DVDRead: offset: %08x memOffset: %08x length: %i", iDVDOffset, iRamAddress, iLength); - DVDInterface::DVDRead(iDVDOffset, iRamAddress, iLength); - - } while(PowerPC::ppcState.gpr[3] != 0x00); - // ======================================================================================= - - - // iAppLoaderClose - LOG(MASTER_LOG, "call iAppLoaderClose"); - RunFunction(iAppLoaderClose, _bDebug); - - // Load patches - std::string gameID = VolumeHandler::GetVolume()->GetUniqueID(); - PatchEngine::LoadPatches(gameID.c_str()); - PowerPC::ppcState.DebugCount = 0; - // return - PC = PowerPC::ppcState.gpr[3]; - - // --- preinit some stuff from bios --- - - // Bus Clock Speed - Memory::Write_U32(0x09a7ec80, 0x800000F8); - Memory::Write_U32(0x1cf7c580, 0x800000FC); - - // fake the VI Init of the BIOS - Memory::Write_U32(Core::g_CoreStartupParameter.bNTSC ? 0 : 1, 0x800000CC); - - // preset time - Memory::Write_U32(CEXIIPL::GetGCTime(), 0x800030D8); -} - - -// __________________________________________________________________________________________________ -// -// BIOS HLE: -// copy the apploader to 0x81200000 -// execute the apploader -// -bool CBoot::EmulatedBIOS_Wii(bool _bDebug) -{ - LOG(BOOT, "Faking Wii BIOS..."); - - FILE* pDump = fopen(FULL_WII_SYS_DIR "dump_0x0000_0x4000.bin", "rb"); - if (pDump != NULL) - { - LOG(MASTER_LOG, "Init from memory dump."); - - fread(Memory::GetMainRAMPtr(), 1, 16384, pDump); - fclose(pDump); - pDump = NULL; - } - else - { - // ======================================================= - /* Write the 256 byte setting.txt to memory. This may not be needed as - most or all games read the setting.txt file from \title\00000001\00000002\ - data\setting.txt directly after the read the SYSCONF file. The games also - read it to 0x3800, what is a little strange however is that it only reads - the first 100 bytes of it. */ - // ------------- - { - std::string filename(WII_EUR_SETTING_FILE); - if (VolumeHandler::IsValid()) - { - switch(VolumeHandler::GetVolume()->GetCountry()) - { - case DiscIO::IVolume::COUNTRY_JAP: - filename = WII_JAP_SETTING_FILE; - break; - - case DiscIO::IVolume::COUNTRY_USA: - filename = WII_USA_SETTING_FILE; - break; - - case DiscIO::IVolume::COUNTRY_EUROPE: - filename = WII_EUR_SETTING_FILE; - break; - - default: - PanicAlert("Unknown country. Wii boot process will be switched to European settings."); - filename = WII_EUR_SETTING_FILE; - break; - } - } - - FILE* pTmp = fopen(filename.c_str(), "rb"); - if (!pTmp) - { - LOG(MASTER_LOG, "Cant find setting file"); - return false; - } - - fread(Memory::GetPointer(0x3800), 256, 1, pTmp); - fclose(pTmp); - } - // ============= - - - // ======================================================= - /* Set hardcoded global variables to Wii memory. These are partly collected from - Wiibrew. These values are needed for the games to function correctly. A few - values in this region will also be placed here by the game as it boots. - They are: - - // Strange values that I don't know the meaning of, all games write these - 0x00 to 0x18: 0x029f0010 - 0x029f0033 - 0x029f0034 - 0x029f0035 - 0x029f0036 - 0x029f0037 - 0x029f0038 - 0x029f0039 // Replaces the previous 0x5d1c9ea3 magic word - - 0x80000038 Start of FST - 0x8000003c Size of FST Size - 0x80000060 Copyright code */ - // ------------- - { - DVDInterface::DVDRead(0x00000000, 0x00000000, 6); // Game Code - Memory::Write_U32(0x5d1c9ea3, 0x00000018); // Magic word it is a wii disc - Memory::Write_U32(0x0D15EA5E, 0x00000020); // Another magic word - Memory::Write_U32(0x00000001, 0x00000024); // Unknown - Memory::Write_U32(0x01800000, 0x00000028); // MEM1 size 24MB - Memory::Write_U32(0x00000023, 0x0000002c); // Production Board Model - Memory::Write_U32(0x00000000, 0x00000030); // Init - Memory::Write_U32(0x817FEC60, 0x00000034); // Init - // 38, 3C should get start, size of FST through apploader - Memory::Write_U32(0x38a00040, 0x00000060); // Exception init - Memory::Write_U32(0x8008f7b8, 0x000000e4); // Thread Init - Memory::Write_U32(0x01800000, 0x000000f0); // "Simulated memory size" (debug mode?) - Memory::Write_U32(0x8179b500, 0x000000f4); // __start - Memory::Write_U32(0x0e7be2c0, 0x000000f8); // Bus speed - Memory::Write_U32(0x2B73A840, 0x000000fc); // CPU speed - Memory::Write_U16(0x0000, 0x000030e6); // Console type - Memory::Write_U32(0x00000000, 0x000030c0); // EXI - Memory::Write_U32(0x00000000, 0x000030c4); // EXI - Memory::Write_U32(0x00000000, 0x000030dc); // Time - Memory::Write_U32(0x00000000, 0x000030d8); // Time - Memory::Write_U32(0x00000000, 0x000030f0); // Apploader - Memory::Write_U32(0x01800000, 0x00003100); // BAT - Memory::Write_U32(0x01800000, 0x00003104); // BAT - Memory::Write_U32(0x00000000, 0x0000310c); // Init - Memory::Write_U32(0x8179d500, 0x00003110); // Init - Memory::Write_U32(0x04000000, 0x00003118); // Unknown - Memory::Write_U32(0x04000000, 0x0000311c); // BAT - Memory::Write_U32(0x93400000, 0x00003120); // BAT - Memory::Write_U32(0x90000800, 0x00003124); // Init - MEM2 low - Memory::Write_U32(0x933e0000, 0x00003128); // Init - MEM2 high - Memory::Write_U32(0x933e0000, 0x00003130); // IOS MEM2 low - Memory::Write_U32(0x93400000, 0x00003134); // IOS MEM2 high - Memory::Write_U32(0x00000011, 0x00003138); // Console type - Memory::Write_U64(0x0009020400062507ULL, 0x00003140); // IOS Version - Memory::Write_U16(0x0113, 0x0000315e); // Apploader - Memory::Write_U32(0x0000FF16, 0x00003158); // DDR ram vendor code - - Memory::Write_U8(0x80, 0x0000315c); // OSInit - Memory::Write_U8(0x00, 0x00000006); // DVDInit - Memory::Write_U8(0x00, 0x00000007); // DVDInit - Memory::Write_U16(0x0000, 0x000030e0); // PADInit - - // Fake the VI Init of the BIOS - Memory::Write_U32(Core::g_CoreStartupParameter.bNTSC ? 0 : 1, 0x000000CC); - - // Clear exception handler. Why? Don't we begin with only zeroes? - for (int i = 0x3000; i <= 0x3038; i += 4) - { - Memory::Write_U32(0x00000000, 0x80000000 + i); - } - - /* This is some kind of consistency check that is compared to the 0x00 - values as the game boots. This location keep the 4 byte ID for as long - as the game is running. The 6 byte ID at 0x00 is overwritten sometime - after this check during booting. */ - VolumeHandler::ReadToPtr(Memory::GetPointer(0x3180), 0, 4); - Memory::Write_U8(0x80, 0x00003184); - } - } - - // apploader - if (VolumeHandler::IsValid() && VolumeHandler::IsWii()) - { - UReg_MSR& m_MSR = ((UReg_MSR&)PowerPC::ppcState.msr); - m_MSR.FP = 1; - - //TODO: Game iso info to 0x80000000 according to yagcd - or does apploader do this? - - Memory::Write_U32(0x4c000064, 0x80000300); // write default DFI Handler: rfi - Memory::Write_U32(0x4c000064, 0x80000800); // write default FPU Handler: rfi - Memory::Write_U32(0x4c000064, 0x80000C00); // write default Syscall Handler: rfi - - Memory::Write_U32(((1 & 0x3f) << 26) | 2, 0x81300000); // HLE OSReport for Apploader - - PowerPC::ppcState.gpr[1] = 0x816ffff0; // StackPointer - - u32 iAppLoaderOffset = 0x2440; // 0x1c40; - - // Load Apploader to Memory - u32 iAppLoaderEntry = VolumeHandler::Read32(iAppLoaderOffset + 0x10); - u32 iAppLoaderSize = VolumeHandler::Read32(iAppLoaderOffset + 0x14); - if ((iAppLoaderEntry == (u32)-1) || (iAppLoaderSize == (u32)-1)) - { - LOG(BOOT, "Invalid apploader. Probably your image is corrupted."); - return false; - } - VolumeHandler::ReadToPtr(Memory::GetPointer(0x81200000), iAppLoaderOffset + 0x20, iAppLoaderSize); - - //call iAppLoaderEntry - LOG(BOOT, "Call iAppLoaderEntry"); - - u32 iAppLoaderFuncAddr = 0x80004000; - PowerPC::ppcState.gpr[3] = iAppLoaderFuncAddr + 0; - PowerPC::ppcState.gpr[4] = iAppLoaderFuncAddr + 4; - PowerPC::ppcState.gpr[5] = iAppLoaderFuncAddr + 8; - RunFunction(iAppLoaderEntry, _bDebug); - u32 iAppLoaderInit = Memory::ReadUnchecked_U32(iAppLoaderFuncAddr+0); - u32 iAppLoaderMain = Memory::ReadUnchecked_U32(iAppLoaderFuncAddr+4); - u32 iAppLoaderClose = Memory::ReadUnchecked_U32(iAppLoaderFuncAddr+8); - - // iAppLoaderInit - LOG(BOOT, "Call iAppLoaderInit"); - PowerPC::ppcState.gpr[3] = 0x81300000; - RunFunction(iAppLoaderInit, _bDebug); - - // iAppLoaderMain - LOG(BOOT, "Call iAppLoaderMain"); - do - { - PowerPC::ppcState.gpr[3] = 0x81300004; - PowerPC::ppcState.gpr[4] = 0x81300008; - PowerPC::ppcState.gpr[5] = 0x8130000c; - - RunFunction(iAppLoaderMain, _bDebug); - - u32 iRamAddress = Memory::ReadUnchecked_U32(0x81300004); - u32 iLength = Memory::ReadUnchecked_U32(0x81300008); - u32 iDVDOffset = Memory::ReadUnchecked_U32(0x8130000c) << 2; - - LOGV(BOOT, 1, "DVDRead: offset: %08x memOffse: %08x length: %i", iDVDOffset, iRamAddress, iLength); - DVDInterface::DVDRead(iDVDOffset, iRamAddress, iLength); - } while(PowerPC::ppcState.gpr[3] != 0x00); - - // iAppLoaderClose - LOG(BOOT, "call iAppLoaderClose"); - RunFunction(iAppLoaderClose, _bDebug); - - // Load patches and run startup patches - std::string gameID = VolumeHandler::GetVolume()->GetUniqueID(); - PatchEngine::LoadPatches(gameID.c_str()); - - // return - PC = PowerPC::ppcState.gpr[3]; - } - - PowerPC::ppcState.DebugCount = 0; - - return true; -} - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" + +#include "../PowerPC/PowerPC.h" +#include "../Core.h" +#include "../HW/EXI_DeviceIPL.h" +#include "../HW/Memmap.h" +#include "../HW/DVDInterface.h" +#include "../HW/CPU.h" + +#include "../Host.h" +#include "../VolumeHandler.h" +#include "../PatchEngine.h" +#include "../MemTools.h" + +#include "VolumeCreator.h" +#include "Boot.h" + +void CBoot::RunFunction(u32 _iAddr, bool _bUseDebugger) +{ + PC = _iAddr; + LR = 0x00; + + if (_bUseDebugger) + { + CCPU::Break(); + while (PC != 0x00) + CCPU::SingleStep(); + } + else + { + while (PC != 0x00) + PowerPC::SingleStep(); + } +} + +// __________________________________________________________________________________________________ +// +// BIOS HLE: +// copy the apploader to 0x81200000 +// execute the apploader +// +void CBoot::EmulatedBIOS(bool _bDebug) +{ + LOG(BOOT, "Faking GC BIOS..."); + UReg_MSR& m_MSR = ((UReg_MSR&)PowerPC::ppcState.msr); + m_MSR.FP = 1; + + Memory::Clear(); + + // ======================================================================================= + // Write necessary values + // --------------------------------------------------------------------------------------- + /* Here we write values to memory that the apploader does not take care of. Game iso info goes + to 0x80000000 according to yagcd 4.2. I'm not sure what bytes 8-10 does (version and + streaming), but I include them anyway because it seems like they are supposed to be there. */ + // --------------------------------------------------------------------------------------- + DVDInterface::DVDRead(0x00000000, 0x80000000, 10); // write boot info needed for multidisc games + + Memory::Write_U32(0x4c000064, 0x80000300); // write default DFI Handler: rfi + Memory::Write_U32(0x4c000064, 0x80000800); // write default FPU Handler: rfi + Memory::Write_U32(0x4c000064, 0x80000C00); // write default Syscall Handler: rfi + // + Memory::Write_U32(0xc2339f3d, 0x8000001C); //game disc + Memory::Write_U32(0x0D15EA5E, 0x80000020); //funny magic word for normal boot + Memory::Write_U32(0x01800000, 0x80000028); // Physical Memory Size + +// Memory::Write_U32(0x00000003, 0x8000002C); // Console type - retail + Memory::Write_U32(0x10000006, 0x8000002C); // DevKit + + Memory::Write_U32(((1 & 0x3f) << 26) | 2, 0x81300000); // HLE OSReport for Apploader + // ======================================================================================= + + + // ======================================================================================= + // Load Apploader to Memory - The apploader is hardcoded to begin at byte 9 280 on the disc, + // but it seems like the size can be variable. Compare with yagcd chap 13. + // --------------------------------------------------------------------------------------- + PowerPC::ppcState.gpr[1] = 0x816ffff0; // StackPointer + u32 iAppLoaderOffset = 0x2440; // 0x1c40 (what is 0x1c40?) + // --------------------------------------------------------------------------------------- + u32 iAppLoaderEntry = VolumeHandler::Read32(iAppLoaderOffset + 0x10); + u32 iAppLoaderSize = VolumeHandler::Read32(iAppLoaderOffset + 0x14); + if ((iAppLoaderEntry == (u32)-1) || (iAppLoaderSize == (u32)-1)) + return; + VolumeHandler::ReadToPtr(Memory::GetPointer(0x81200000), iAppLoaderOffset + 0x20, iAppLoaderSize); + // ======================================================================================= + + + //call iAppLoaderEntry + LOG(MASTER_LOG, "Call iAppLoaderEntry"); + + u32 iAppLoaderFuncAddr = 0x80003100; + PowerPC::ppcState.gpr[3] = iAppLoaderFuncAddr + 0; + PowerPC::ppcState.gpr[4] = iAppLoaderFuncAddr + 4; + PowerPC::ppcState.gpr[5] = iAppLoaderFuncAddr + 8; + RunFunction(iAppLoaderEntry, _bDebug); + u32 iAppLoaderInit = Memory::ReadUnchecked_U32(iAppLoaderFuncAddr+0); + u32 iAppLoaderMain = Memory::ReadUnchecked_U32(iAppLoaderFuncAddr+4); + u32 iAppLoaderClose = Memory::ReadUnchecked_U32(iAppLoaderFuncAddr+8); + + // iAppLoaderInit + LOG(MASTER_LOG, "Call iAppLoaderInit"); + PowerPC::ppcState.gpr[3] = 0x81300000; + RunFunction(iAppLoaderInit, _bDebug); + + + // ======================================================================================= + /* iAppLoaderMain - Here we load the apploader, the DOL (the exe) and the FST (filesystem). + To give you an idea about where the stuff is located on the disc take a look at yagcd + ch 13. */ + // --------------------------------------------------------------------------------------- + LOG(MASTER_LOG, "Call iAppLoaderMain"); + do + { + PowerPC::ppcState.gpr[3] = 0x81300004; + PowerPC::ppcState.gpr[4] = 0x81300008; + PowerPC::ppcState.gpr[5] = 0x8130000c; + + RunFunction(iAppLoaderMain, _bDebug); + + u32 iRamAddress = Memory::ReadUnchecked_U32(0x81300004); + u32 iLength = Memory::ReadUnchecked_U32(0x81300008); + u32 iDVDOffset = Memory::ReadUnchecked_U32(0x8130000c); + + LOGV(MASTER_LOG, 2, "DVDRead: offset: %08x memOffset: %08x length: %i", iDVDOffset, iRamAddress, iLength); + DVDInterface::DVDRead(iDVDOffset, iRamAddress, iLength); + + } while(PowerPC::ppcState.gpr[3] != 0x00); + // ======================================================================================= + + + // iAppLoaderClose + LOG(MASTER_LOG, "call iAppLoaderClose"); + RunFunction(iAppLoaderClose, _bDebug); + + // Load patches + std::string gameID = VolumeHandler::GetVolume()->GetUniqueID(); + PatchEngine::LoadPatches(gameID.c_str()); + PowerPC::ppcState.DebugCount = 0; + // return + PC = PowerPC::ppcState.gpr[3]; + + // --- preinit some stuff from bios --- + + // Bus Clock Speed + Memory::Write_U32(0x09a7ec80, 0x800000F8); + Memory::Write_U32(0x1cf7c580, 0x800000FC); + + // fake the VI Init of the BIOS + Memory::Write_U32(Core::g_CoreStartupParameter.bNTSC ? 0 : 1, 0x800000CC); + + // preset time + Memory::Write_U32(CEXIIPL::GetGCTime(), 0x800030D8); +} + + +// __________________________________________________________________________________________________ +// +// BIOS HLE: +// copy the apploader to 0x81200000 +// execute the apploader +// +bool CBoot::EmulatedBIOS_Wii(bool _bDebug) +{ + LOG(BOOT, "Faking Wii BIOS..."); + + FILE* pDump = fopen(FULL_WII_SYS_DIR "dump_0x0000_0x4000.bin", "rb"); + if (pDump != NULL) + { + LOG(MASTER_LOG, "Init from memory dump."); + + fread(Memory::GetMainRAMPtr(), 1, 16384, pDump); + fclose(pDump); + pDump = NULL; + } + else + { + // ======================================================= + /* Write the 256 byte setting.txt to memory. This may not be needed as + most or all games read the setting.txt file from \title\00000001\00000002\ + data\setting.txt directly after the read the SYSCONF file. The games also + read it to 0x3800, what is a little strange however is that it only reads + the first 100 bytes of it. */ + // ------------- + { + std::string filename(WII_EUR_SETTING_FILE); + if (VolumeHandler::IsValid()) + { + switch(VolumeHandler::GetVolume()->GetCountry()) + { + case DiscIO::IVolume::COUNTRY_JAP: + filename = WII_JAP_SETTING_FILE; + break; + + case DiscIO::IVolume::COUNTRY_USA: + filename = WII_USA_SETTING_FILE; + break; + + case DiscIO::IVolume::COUNTRY_EUROPE: + filename = WII_EUR_SETTING_FILE; + break; + + default: + PanicAlert("Unknown country. Wii boot process will be switched to European settings."); + filename = WII_EUR_SETTING_FILE; + break; + } + } + + FILE* pTmp = fopen(filename.c_str(), "rb"); + if (!pTmp) + { + LOG(MASTER_LOG, "Cant find setting file"); + return false; + } + + fread(Memory::GetPointer(0x3800), 256, 1, pTmp); + fclose(pTmp); + } + // ============= + + + // ======================================================= + /* Set hardcoded global variables to Wii memory. These are partly collected from + Wiibrew. These values are needed for the games to function correctly. A few + values in this region will also be placed here by the game as it boots. + They are: + + // Strange values that I don't know the meaning of, all games write these + 0x00 to 0x18: 0x029f0010 + 0x029f0033 + 0x029f0034 + 0x029f0035 + 0x029f0036 + 0x029f0037 + 0x029f0038 + 0x029f0039 // Replaces the previous 0x5d1c9ea3 magic word + + 0x80000038 Start of FST + 0x8000003c Size of FST Size + 0x80000060 Copyright code */ + // ------------- + { + DVDInterface::DVDRead(0x00000000, 0x00000000, 6); // Game Code + Memory::Write_U32(0x5d1c9ea3, 0x00000018); // Magic word it is a wii disc + Memory::Write_U32(0x0D15EA5E, 0x00000020); // Another magic word + Memory::Write_U32(0x00000001, 0x00000024); // Unknown + Memory::Write_U32(0x01800000, 0x00000028); // MEM1 size 24MB + Memory::Write_U32(0x00000023, 0x0000002c); // Production Board Model + Memory::Write_U32(0x00000000, 0x00000030); // Init + Memory::Write_U32(0x817FEC60, 0x00000034); // Init + // 38, 3C should get start, size of FST through apploader + Memory::Write_U32(0x38a00040, 0x00000060); // Exception init + Memory::Write_U32(0x8008f7b8, 0x000000e4); // Thread Init + Memory::Write_U32(0x01800000, 0x000000f0); // "Simulated memory size" (debug mode?) + Memory::Write_U32(0x8179b500, 0x000000f4); // __start + Memory::Write_U32(0x0e7be2c0, 0x000000f8); // Bus speed + Memory::Write_U32(0x2B73A840, 0x000000fc); // CPU speed + Memory::Write_U16(0x0000, 0x000030e6); // Console type + Memory::Write_U32(0x00000000, 0x000030c0); // EXI + Memory::Write_U32(0x00000000, 0x000030c4); // EXI + Memory::Write_U32(0x00000000, 0x000030dc); // Time + Memory::Write_U32(0x00000000, 0x000030d8); // Time + Memory::Write_U32(0x00000000, 0x000030f0); // Apploader + Memory::Write_U32(0x01800000, 0x00003100); // BAT + Memory::Write_U32(0x01800000, 0x00003104); // BAT + Memory::Write_U32(0x00000000, 0x0000310c); // Init + Memory::Write_U32(0x8179d500, 0x00003110); // Init + Memory::Write_U32(0x04000000, 0x00003118); // Unknown + Memory::Write_U32(0x04000000, 0x0000311c); // BAT + Memory::Write_U32(0x93400000, 0x00003120); // BAT + Memory::Write_U32(0x90000800, 0x00003124); // Init - MEM2 low + Memory::Write_U32(0x933e0000, 0x00003128); // Init - MEM2 high + Memory::Write_U32(0x933e0000, 0x00003130); // IOS MEM2 low + Memory::Write_U32(0x93400000, 0x00003134); // IOS MEM2 high + Memory::Write_U32(0x00000011, 0x00003138); // Console type + Memory::Write_U64(0x0009020400062507ULL, 0x00003140); // IOS Version + Memory::Write_U16(0x0113, 0x0000315e); // Apploader + Memory::Write_U32(0x0000FF16, 0x00003158); // DDR ram vendor code + + Memory::Write_U8(0x80, 0x0000315c); // OSInit + Memory::Write_U8(0x00, 0x00000006); // DVDInit + Memory::Write_U8(0x00, 0x00000007); // DVDInit + Memory::Write_U16(0x0000, 0x000030e0); // PADInit + + // Fake the VI Init of the BIOS + Memory::Write_U32(Core::g_CoreStartupParameter.bNTSC ? 0 : 1, 0x000000CC); + + // Clear exception handler. Why? Don't we begin with only zeroes? + for (int i = 0x3000; i <= 0x3038; i += 4) + { + Memory::Write_U32(0x00000000, 0x80000000 + i); + } + + /* This is some kind of consistency check that is compared to the 0x00 + values as the game boots. This location keep the 4 byte ID for as long + as the game is running. The 6 byte ID at 0x00 is overwritten sometime + after this check during booting. */ + VolumeHandler::ReadToPtr(Memory::GetPointer(0x3180), 0, 4); + Memory::Write_U8(0x80, 0x00003184); + } + } + + // apploader + if (VolumeHandler::IsValid() && VolumeHandler::IsWii()) + { + UReg_MSR& m_MSR = ((UReg_MSR&)PowerPC::ppcState.msr); + m_MSR.FP = 1; + + //TODO: Game iso info to 0x80000000 according to yagcd - or does apploader do this? + + Memory::Write_U32(0x4c000064, 0x80000300); // write default DFI Handler: rfi + Memory::Write_U32(0x4c000064, 0x80000800); // write default FPU Handler: rfi + Memory::Write_U32(0x4c000064, 0x80000C00); // write default Syscall Handler: rfi + + Memory::Write_U32(((1 & 0x3f) << 26) | 2, 0x81300000); // HLE OSReport for Apploader + + PowerPC::ppcState.gpr[1] = 0x816ffff0; // StackPointer + + u32 iAppLoaderOffset = 0x2440; // 0x1c40; + + // Load Apploader to Memory + u32 iAppLoaderEntry = VolumeHandler::Read32(iAppLoaderOffset + 0x10); + u32 iAppLoaderSize = VolumeHandler::Read32(iAppLoaderOffset + 0x14); + if ((iAppLoaderEntry == (u32)-1) || (iAppLoaderSize == (u32)-1)) + { + LOG(BOOT, "Invalid apploader. Probably your image is corrupted."); + return false; + } + VolumeHandler::ReadToPtr(Memory::GetPointer(0x81200000), iAppLoaderOffset + 0x20, iAppLoaderSize); + + //call iAppLoaderEntry + LOG(BOOT, "Call iAppLoaderEntry"); + + u32 iAppLoaderFuncAddr = 0x80004000; + PowerPC::ppcState.gpr[3] = iAppLoaderFuncAddr + 0; + PowerPC::ppcState.gpr[4] = iAppLoaderFuncAddr + 4; + PowerPC::ppcState.gpr[5] = iAppLoaderFuncAddr + 8; + RunFunction(iAppLoaderEntry, _bDebug); + u32 iAppLoaderInit = Memory::ReadUnchecked_U32(iAppLoaderFuncAddr+0); + u32 iAppLoaderMain = Memory::ReadUnchecked_U32(iAppLoaderFuncAddr+4); + u32 iAppLoaderClose = Memory::ReadUnchecked_U32(iAppLoaderFuncAddr+8); + + // iAppLoaderInit + LOG(BOOT, "Call iAppLoaderInit"); + PowerPC::ppcState.gpr[3] = 0x81300000; + RunFunction(iAppLoaderInit, _bDebug); + + // iAppLoaderMain + LOG(BOOT, "Call iAppLoaderMain"); + do + { + PowerPC::ppcState.gpr[3] = 0x81300004; + PowerPC::ppcState.gpr[4] = 0x81300008; + PowerPC::ppcState.gpr[5] = 0x8130000c; + + RunFunction(iAppLoaderMain, _bDebug); + + u32 iRamAddress = Memory::ReadUnchecked_U32(0x81300004); + u32 iLength = Memory::ReadUnchecked_U32(0x81300008); + u32 iDVDOffset = Memory::ReadUnchecked_U32(0x8130000c) << 2; + + LOGV(BOOT, 1, "DVDRead: offset: %08x memOffse: %08x length: %i", iDVDOffset, iRamAddress, iLength); + DVDInterface::DVDRead(iDVDOffset, iRamAddress, iLength); + } while(PowerPC::ppcState.gpr[3] != 0x00); + + // iAppLoaderClose + LOG(BOOT, "call iAppLoaderClose"); + RunFunction(iAppLoaderClose, _bDebug); + + // Load patches and run startup patches + std::string gameID = VolumeHandler::GetVolume()->GetUniqueID(); + PatchEngine::LoadPatches(gameID.c_str()); + + // return + PC = PowerPC::ppcState.gpr[3]; + } + + PowerPC::ppcState.DebugCount = 0; + + return true; +} + diff --git a/Source/Core/Core/Src/Boot/Boot_DOL.cpp b/Source/Core/Core/Src/Boot/Boot_DOL.cpp index a8c7bd61a5..c3910912c8 100644 --- a/Source/Core/Core/Src/Boot/Boot_DOL.cpp +++ b/Source/Core/Core/Src/Boot/Boot_DOL.cpp @@ -1,80 +1,80 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Boot_DOL.h" - -#include "../HW/Memmap.h" - -CDolLoader::CDolLoader(const char* _szFilename) : m_bInit(false) -{ - // try to open file - FILE* pStream = fopen(_szFilename, "rb"); - if (pStream) - { - fread(&m_dolheader, 1, sizeof(SDolHeader), pStream); - - // swap memory - u32* p = (u32*)&m_dolheader; - for (size_t i=0; i<(sizeof(SDolHeader)>>2); i++) - p[i] = Common::swap32(p[i]); - - // load all text (code) sections - for(int i = 0; i < DOL_NUM_TEXT; i++) - { - if(m_dolheader.textOffset[i] != 0) - { - u8* pTemp = new u8[m_dolheader.textSize[i]]; - - fseek(pStream, m_dolheader.textOffset[i], SEEK_SET); - fread(pTemp, 1, m_dolheader.textSize[i], pStream); - - for (u32 num = 0; num < m_dolheader.textSize[i]; num++) - Memory::Write_U8(pTemp[num], m_dolheader.textAddress[i] + num); - - delete [] pTemp; - } - } - - // load all data sections - for(int i = 0; i < DOL_NUM_DATA; i++) - { - if(m_dolheader.dataOffset[i] != 0) - { - u8* pTemp = new u8[m_dolheader.dataSize[i]]; - - fseek(pStream, m_dolheader.dataOffset[i], SEEK_SET); - fread(pTemp, 1, m_dolheader.dataSize[i], pStream); - - for (u32 num = 0; num < m_dolheader.dataSize[i]; num++) - Memory::Write_U8(pTemp[num], m_dolheader.dataAddress[i] + num); - - delete [] pTemp; - } - } - - //TODO - we know where there is code, and where there is data - //Make use of this! - - fclose(pStream); - m_bInit = true; - } -} - -u32 CDolLoader::GetEntryPoint() -{ - return m_dolheader.entryPoint; -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Boot_DOL.h" + +#include "../HW/Memmap.h" + +CDolLoader::CDolLoader(const char* _szFilename) : m_bInit(false) +{ + // try to open file + FILE* pStream = fopen(_szFilename, "rb"); + if (pStream) + { + fread(&m_dolheader, 1, sizeof(SDolHeader), pStream); + + // swap memory + u32* p = (u32*)&m_dolheader; + for (size_t i=0; i<(sizeof(SDolHeader)>>2); i++) + p[i] = Common::swap32(p[i]); + + // load all text (code) sections + for(int i = 0; i < DOL_NUM_TEXT; i++) + { + if(m_dolheader.textOffset[i] != 0) + { + u8* pTemp = new u8[m_dolheader.textSize[i]]; + + fseek(pStream, m_dolheader.textOffset[i], SEEK_SET); + fread(pTemp, 1, m_dolheader.textSize[i], pStream); + + for (u32 num = 0; num < m_dolheader.textSize[i]; num++) + Memory::Write_U8(pTemp[num], m_dolheader.textAddress[i] + num); + + delete [] pTemp; + } + } + + // load all data sections + for(int i = 0; i < DOL_NUM_DATA; i++) + { + if(m_dolheader.dataOffset[i] != 0) + { + u8* pTemp = new u8[m_dolheader.dataSize[i]]; + + fseek(pStream, m_dolheader.dataOffset[i], SEEK_SET); + fread(pTemp, 1, m_dolheader.dataSize[i], pStream); + + for (u32 num = 0; num < m_dolheader.dataSize[i]; num++) + Memory::Write_U8(pTemp[num], m_dolheader.dataAddress[i] + num); + + delete [] pTemp; + } + } + + //TODO - we know where there is code, and where there is data + //Make use of this! + + fclose(pStream); + m_bInit = true; + } +} + +u32 CDolLoader::GetEntryPoint() +{ + return m_dolheader.entryPoint; +} diff --git a/Source/Core/Core/Src/Boot/Boot_ELF.cpp b/Source/Core/Core/Src/Boot/Boot_ELF.cpp index 53a2ae826d..ce5d6d6ffb 100644 --- a/Source/Core/Core/Src/Boot/Boot_ELF.cpp +++ b/Source/Core/Core/Src/Boot/Boot_ELF.cpp @@ -1,71 +1,71 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "../PowerPC/PowerPC.h" -#include "Boot.h" -#include "../HLE/HLE.h" -#include "Boot_ELF.h" -#include "ElfReader.h" -#include "MappedFile.h" - -bool CBoot::IsElfWii(const char *filename) -{ - /* We already check if filename existed before we called this function, so - there is no need for another check, just read the file right away */ - FILE *f = fopen(filename, "rb"); - fseek(f, 0, SEEK_END); - u64 filesize = ftell(f); - fseek(f, 0, SEEK_SET); - u8 *mem = new u8[(size_t)filesize]; - fread(mem, 1, filesize, f); - fclose(f); - - ElfReader reader(mem); - // TODO: Find a more reliable way to distinguish. - bool isWii = reader.GetEntryPoint() >= 0x80004000; - delete [] mem; - - return isWii; -} - - -bool CBoot::Boot_ELF(const char *filename) -{ - FILE *f = fopen(filename, "rb"); - fseek(f, 0, SEEK_END); - u64 filesize = ftell(f); - fseek(f, 0, SEEK_SET); - u8 *mem = new u8[(size_t)filesize]; - fread(mem, 1, filesize, f); - fclose(f); - - ElfReader reader(mem); - reader.LoadInto(0x80000000); - if (!reader.LoadSymbols()) - { - if (LoadMapFromFilename(filename)) - HLE::PatchFunctions(); - } else { - HLE::PatchFunctions(); - } - - PC = reader.GetEntryPoint(); - delete [] mem; - - return true; -} - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "../PowerPC/PowerPC.h" +#include "Boot.h" +#include "../HLE/HLE.h" +#include "Boot_ELF.h" +#include "ElfReader.h" +#include "MappedFile.h" + +bool CBoot::IsElfWii(const char *filename) +{ + /* We already check if filename existed before we called this function, so + there is no need for another check, just read the file right away */ + FILE *f = fopen(filename, "rb"); + fseek(f, 0, SEEK_END); + u64 filesize = ftell(f); + fseek(f, 0, SEEK_SET); + u8 *mem = new u8[(size_t)filesize]; + fread(mem, 1, filesize, f); + fclose(f); + + ElfReader reader(mem); + // TODO: Find a more reliable way to distinguish. + bool isWii = reader.GetEntryPoint() >= 0x80004000; + delete [] mem; + + return isWii; +} + + +bool CBoot::Boot_ELF(const char *filename) +{ + FILE *f = fopen(filename, "rb"); + fseek(f, 0, SEEK_END); + u64 filesize = ftell(f); + fseek(f, 0, SEEK_SET); + u8 *mem = new u8[(size_t)filesize]; + fread(mem, 1, filesize, f); + fclose(f); + + ElfReader reader(mem); + reader.LoadInto(0x80000000); + if (!reader.LoadSymbols()) + { + if (LoadMapFromFilename(filename)) + HLE::PatchFunctions(); + } else { + HLE::PatchFunctions(); + } + + PC = reader.GetEntryPoint(); + delete [] mem; + + return true; +} + diff --git a/Source/Core/Core/Src/Boot/ElfReader.cpp b/Source/Core/Core/Src/Boot/ElfReader.cpp index 425ec21f42..9e86a94604 100644 --- a/Source/Core/Core/Src/Boot/ElfReader.cpp +++ b/Source/Core/Core/Src/Boot/ElfReader.cpp @@ -1,258 +1,258 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include - -#include "Common.h" -#include "../Debugger/Debugger_SymbolMap.h" -#include "../HW/Memmap.h" -#include "../PowerPC/SymbolDB.h" -#include "ElfReader.h" - -void bswap(Elf32_Word &w) {w = Common::swap32(w);} -void bswap(Elf32_Half &w) {w = Common::swap16(w);} - -void byteswapHeader(Elf32_Ehdr &ELF_H) -{ - bswap(ELF_H.e_type); - bswap(ELF_H.e_machine); - bswap(ELF_H.e_ehsize); - bswap(ELF_H.e_phentsize); - bswap(ELF_H.e_phnum); - bswap(ELF_H.e_shentsize); - bswap(ELF_H.e_shnum); - bswap(ELF_H.e_shstrndx); - bswap(ELF_H.e_version); - bswap(ELF_H.e_entry); - bswap(ELF_H.e_phoff); - bswap(ELF_H.e_shoff); - bswap(ELF_H.e_flags); -} - -void byteswapSegment(Elf32_Phdr &sec) -{ - bswap(sec.p_align); - bswap(sec.p_filesz); - bswap(sec.p_flags); - bswap(sec.p_memsz); - bswap(sec.p_offset); - bswap(sec.p_paddr); - bswap(sec.p_vaddr); - bswap(sec.p_type); -} - -void byteswapSection(Elf32_Shdr &sec) -{ - bswap(sec.sh_addr); - bswap(sec.sh_addralign); - bswap(sec.sh_entsize); - bswap(sec.sh_flags); - bswap(sec.sh_info); - bswap(sec.sh_link); - bswap(sec.sh_name); - bswap(sec.sh_offset); - bswap(sec.sh_size); - bswap(sec.sh_type); -} - -ElfReader::ElfReader(void *ptr) -{ - base = (char*)ptr; - base32 = (u32 *)ptr; - header = (Elf32_Ehdr*)ptr; - byteswapHeader(*header); - - segments = (Elf32_Phdr *)(base + header->e_phoff); - sections = (Elf32_Shdr *)(base + header->e_shoff); - - for (int i = 0; i < GetNumSegments(); i++) - { - byteswapSegment(segments[i]); - } - - for (int i = 0; i < GetNumSections(); i++) - { - byteswapSection(sections[i]); - } - entryPoint = header->e_entry; -} - -const char *ElfReader::GetSectionName(int section) const -{ - if (sections[section].sh_type == SHT_NULL) - return 0; - - int nameOffset = sections[section].sh_name; - char *ptr = (char*)GetSectionDataPtr(header->e_shstrndx); - - if (ptr) - return ptr + nameOffset; - else - return 0; -} - -void addrToHiLo(u32 addr, u16 &hi, s16 &lo) -{ - lo = (addr & 0xFFFF); - u32 naddr = addr - lo; - hi = naddr>>16; - - u32 test = (hi<<16) + lo; - if (test != addr) - { - Crash(); - } -} - - -bool ElfReader::LoadInto(u32 vaddr) -{ - LOG(MASTER_LOG,"String section: %i", header->e_shstrndx); - -// sectionOffsets = new u32[GetNumSections()]; -// sectionAddrs = new u32[GetNumSections()]; - - // Should we relocate? - bRelocate = (header->e_type != ET_EXEC); - - if (bRelocate) - { - LOG(MASTER_LOG,"Relocatable module"); - entryPoint += vaddr; - } - else - { - LOG(MASTER_LOG,"Prerelocated executable"); - } - - LOG(MASTER_LOG,"%i segments:", header->e_phnum); - - // First pass : Get the bits into RAM - u32 segmentVAddr[32]; - - u32 baseAddress = bRelocate?vaddr:0; - for (int i = 0; i < header->e_phnum; i++) - { - Elf32_Phdr *p = segments + i; - - LOG(MASTER_LOG, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr, p->p_filesz, p->p_memsz); - - if (p->p_type == PT_LOAD) - { - segmentVAddr[i] = baseAddress + p->p_vaddr; - u32 writeAddr = segmentVAddr[i]; - - const u8 *src = GetSegmentPtr(i); - u8 *dst = Memory::GetPointer(writeAddr); - u32 srcSize = p->p_filesz; - u32 dstSize = p->p_memsz; - u32 *s = (u32*)src; - u32 *d = (u32*)dst; - for (int j = 0; j < (int)(srcSize + 3) / 4; j++) - { - *d++ = /*_byteswap_ulong*/(*s++); - } - if (srcSize < dstSize) - { - //memset(dst + srcSize, 0, dstSize-srcSize); //zero out bss - } - LOG(MASTER_LOG,"Loadable Segment Copied to %08x, size %08x", writeAddr, p->p_memsz); - } - } - - /* - LOG(MASTER_LOG,"%i sections:", header->e_shnum); - - for (int i=0; ish_addr + baseAddress; - sectionOffsets[i] = writeAddr - vaddr; - sectionAddrs[i] = writeAddr; - - if (s->sh_flags & SHF_ALLOC) - { - LOG(MASTER_LOG,"Data Section found: %s Sitting at %08x, size %08x", name, writeAddr, s->sh_size); - - } - else - { - LOG(MASTER_LOG,"NonData Section found: %s Ignoring (size=%08x) (flags=%08x)", name, s->sh_size, s->sh_flags); - } - } -*/ - LOG(MASTER_LOG,"Done."); - return true; -} - -SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const -{ - for (int i = firstSection; i < header->e_shnum; i++) - { - const char *secname = GetSectionName(i); - - if (secname != 0 && strcmp(name, secname) == 0) - return i; - } - return -1; -} - -bool ElfReader::LoadSymbols() -{ - bool hasSymbols = false; - SectionID sec = GetSectionByName(".symtab"); - if (sec != -1) - { - int stringSection = sections[sec].sh_link; - const char *stringBase = (const char *)GetSectionDataPtr(stringSection); - - //We have a symbol table! - Elf32_Sym *symtab = (Elf32_Sym *)(GetSectionDataPtr(sec)); - int numSymbols = sections[sec].sh_size / sizeof(Elf32_Sym); - for (int sym = 0; sym < numSymbols; sym++) - { - int size = Common::swap32(symtab[sym].st_size); - if (size == 0) - continue; - - // int bind = symtab[sym].st_info >> 4; - int type = symtab[sym].st_info & 0xF; - int sectionIndex = Common::swap16(symtab[sym].st_shndx); - int value = Common::swap32(symtab[sym].st_value); - const char *name = stringBase + Common::swap32(symtab[sym].st_name); - if (bRelocate) - value += sectionAddrs[sectionIndex]; - - int symtype = Symbol::SYMBOL_DATA; - switch (type) - { - case STT_OBJECT: - symtype = Symbol::SYMBOL_DATA; break; - case STT_FUNC: - symtype = Symbol::SYMBOL_FUNCTION; break; - default: - continue; - } - g_symbolDB.AddKnownSymbol(value, size, name, symtype); - hasSymbols = true; - } - } - g_symbolDB.Index(); - return hasSymbols; -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "Common.h" +#include "../Debugger/Debugger_SymbolMap.h" +#include "../HW/Memmap.h" +#include "../PowerPC/SymbolDB.h" +#include "ElfReader.h" + +void bswap(Elf32_Word &w) {w = Common::swap32(w);} +void bswap(Elf32_Half &w) {w = Common::swap16(w);} + +void byteswapHeader(Elf32_Ehdr &ELF_H) +{ + bswap(ELF_H.e_type); + bswap(ELF_H.e_machine); + bswap(ELF_H.e_ehsize); + bswap(ELF_H.e_phentsize); + bswap(ELF_H.e_phnum); + bswap(ELF_H.e_shentsize); + bswap(ELF_H.e_shnum); + bswap(ELF_H.e_shstrndx); + bswap(ELF_H.e_version); + bswap(ELF_H.e_entry); + bswap(ELF_H.e_phoff); + bswap(ELF_H.e_shoff); + bswap(ELF_H.e_flags); +} + +void byteswapSegment(Elf32_Phdr &sec) +{ + bswap(sec.p_align); + bswap(sec.p_filesz); + bswap(sec.p_flags); + bswap(sec.p_memsz); + bswap(sec.p_offset); + bswap(sec.p_paddr); + bswap(sec.p_vaddr); + bswap(sec.p_type); +} + +void byteswapSection(Elf32_Shdr &sec) +{ + bswap(sec.sh_addr); + bswap(sec.sh_addralign); + bswap(sec.sh_entsize); + bswap(sec.sh_flags); + bswap(sec.sh_info); + bswap(sec.sh_link); + bswap(sec.sh_name); + bswap(sec.sh_offset); + bswap(sec.sh_size); + bswap(sec.sh_type); +} + +ElfReader::ElfReader(void *ptr) +{ + base = (char*)ptr; + base32 = (u32 *)ptr; + header = (Elf32_Ehdr*)ptr; + byteswapHeader(*header); + + segments = (Elf32_Phdr *)(base + header->e_phoff); + sections = (Elf32_Shdr *)(base + header->e_shoff); + + for (int i = 0; i < GetNumSegments(); i++) + { + byteswapSegment(segments[i]); + } + + for (int i = 0; i < GetNumSections(); i++) + { + byteswapSection(sections[i]); + } + entryPoint = header->e_entry; +} + +const char *ElfReader::GetSectionName(int section) const +{ + if (sections[section].sh_type == SHT_NULL) + return 0; + + int nameOffset = sections[section].sh_name; + char *ptr = (char*)GetSectionDataPtr(header->e_shstrndx); + + if (ptr) + return ptr + nameOffset; + else + return 0; +} + +void addrToHiLo(u32 addr, u16 &hi, s16 &lo) +{ + lo = (addr & 0xFFFF); + u32 naddr = addr - lo; + hi = naddr>>16; + + u32 test = (hi<<16) + lo; + if (test != addr) + { + Crash(); + } +} + + +bool ElfReader::LoadInto(u32 vaddr) +{ + LOG(MASTER_LOG,"String section: %i", header->e_shstrndx); + +// sectionOffsets = new u32[GetNumSections()]; +// sectionAddrs = new u32[GetNumSections()]; + + // Should we relocate? + bRelocate = (header->e_type != ET_EXEC); + + if (bRelocate) + { + LOG(MASTER_LOG,"Relocatable module"); + entryPoint += vaddr; + } + else + { + LOG(MASTER_LOG,"Prerelocated executable"); + } + + LOG(MASTER_LOG,"%i segments:", header->e_phnum); + + // First pass : Get the bits into RAM + u32 segmentVAddr[32]; + + u32 baseAddress = bRelocate?vaddr:0; + for (int i = 0; i < header->e_phnum; i++) + { + Elf32_Phdr *p = segments + i; + + LOG(MASTER_LOG, "Type: %i Vaddr: %08x Filesz: %i Memsz: %i ", p->p_type, p->p_vaddr, p->p_filesz, p->p_memsz); + + if (p->p_type == PT_LOAD) + { + segmentVAddr[i] = baseAddress + p->p_vaddr; + u32 writeAddr = segmentVAddr[i]; + + const u8 *src = GetSegmentPtr(i); + u8 *dst = Memory::GetPointer(writeAddr); + u32 srcSize = p->p_filesz; + u32 dstSize = p->p_memsz; + u32 *s = (u32*)src; + u32 *d = (u32*)dst; + for (int j = 0; j < (int)(srcSize + 3) / 4; j++) + { + *d++ = /*_byteswap_ulong*/(*s++); + } + if (srcSize < dstSize) + { + //memset(dst + srcSize, 0, dstSize-srcSize); //zero out bss + } + LOG(MASTER_LOG,"Loadable Segment Copied to %08x, size %08x", writeAddr, p->p_memsz); + } + } + + /* + LOG(MASTER_LOG,"%i sections:", header->e_shnum); + + for (int i=0; ish_addr + baseAddress; + sectionOffsets[i] = writeAddr - vaddr; + sectionAddrs[i] = writeAddr; + + if (s->sh_flags & SHF_ALLOC) + { + LOG(MASTER_LOG,"Data Section found: %s Sitting at %08x, size %08x", name, writeAddr, s->sh_size); + + } + else + { + LOG(MASTER_LOG,"NonData Section found: %s Ignoring (size=%08x) (flags=%08x)", name, s->sh_size, s->sh_flags); + } + } +*/ + LOG(MASTER_LOG,"Done."); + return true; +} + +SectionID ElfReader::GetSectionByName(const char *name, int firstSection) const +{ + for (int i = firstSection; i < header->e_shnum; i++) + { + const char *secname = GetSectionName(i); + + if (secname != 0 && strcmp(name, secname) == 0) + return i; + } + return -1; +} + +bool ElfReader::LoadSymbols() +{ + bool hasSymbols = false; + SectionID sec = GetSectionByName(".symtab"); + if (sec != -1) + { + int stringSection = sections[sec].sh_link; + const char *stringBase = (const char *)GetSectionDataPtr(stringSection); + + //We have a symbol table! + Elf32_Sym *symtab = (Elf32_Sym *)(GetSectionDataPtr(sec)); + int numSymbols = sections[sec].sh_size / sizeof(Elf32_Sym); + for (int sym = 0; sym < numSymbols; sym++) + { + int size = Common::swap32(symtab[sym].st_size); + if (size == 0) + continue; + + // int bind = symtab[sym].st_info >> 4; + int type = symtab[sym].st_info & 0xF; + int sectionIndex = Common::swap16(symtab[sym].st_shndx); + int value = Common::swap32(symtab[sym].st_value); + const char *name = stringBase + Common::swap32(symtab[sym].st_name); + if (bRelocate) + value += sectionAddrs[sectionIndex]; + + int symtype = Symbol::SYMBOL_DATA; + switch (type) + { + case STT_OBJECT: + symtype = Symbol::SYMBOL_DATA; break; + case STT_FUNC: + symtype = Symbol::SYMBOL_FUNCTION; break; + default: + continue; + } + g_symbolDB.AddKnownSymbol(value, size, name, symtype); + hasSymbols = true; + } + } + g_symbolDB.Index(); + return hasSymbols; +} diff --git a/Source/Core/Core/Src/Console.cpp b/Source/Core/Core/Src/Console.cpp index c9dd42a5e4..adbbeec781 100644 --- a/Source/Core/Core/Src/Console.cpp +++ b/Source/Core/Core/Src/Console.cpp @@ -1,160 +1,160 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include -#include - -#include "Common.h" -#include "Thread.h" -#include "HW/Memmap.h" -#include "PowerPC/PPCAnalyst.h" -#include "PowerPC/PPCTables.h" -#include "CoreTiming.h" -#include "Core.h" -#include "PowerPC/Jit64/JitCache.h" -#include "PowerPC/SymbolDB.h" -#include "PowerPCDisasm.h" -#include "Console.h" - -#define CASE(x) else if (memcmp(cmd, x, 4*sizeof(TCHAR))==0) -#define CASE1(x) if (memcmp(cmd, x, 2*sizeof(TCHAR))==0) - -void Console_Submit(const char *cmd) -{ - CASE1("jits") - { -#ifdef _M_X64 - Jit64::PrintStats(); -#endif - } - CASE1("r") - { - Core::StartTrace(false); - LOG(CONSOLE, "read tracing started."); - } - CASE1("w") - { - Core::StartTrace(true); - LOG(CONSOLE, "write tracing started."); - } - CASE("trans") - { - TCHAR temp[256]; - u32 addr; - sscanf(cmd, "%s %08x", temp, &addr); - - if (addr!=0) - { -#ifdef LOGGING - u32 EA = -#endif - Memory::CheckDTLB(addr, Memory::FLAG_NO_EXCEPTION); - LOG(CONSOLE, "EA 0x%08x to 0x%08x", addr, EA); - } - else - { - LOG(CONSOLE, "Syntax: trans ADDR"); - } - } - CASE("call") - { - TCHAR temp[256]; - u32 addr; - sscanf(cmd, "%s %08x", temp, &addr); - if (addr!=0) - { - g_symbolDB.PrintCalls(addr); - } - else - { - LOG(CONSOLE, "Syntax: call ADDR"); - } - } - CASE("llac") - { - TCHAR temp[256]; - u32 addr; - sscanf(cmd, "%s %08x", temp, &addr); - if (addr!=0) - { - g_symbolDB.PrintCallers(addr); - } - else - { - LOG(CONSOLE, "Syntax: llac ADDR"); - } - } - CASE("pend") - { - CoreTiming::LogPendingEvents(); - } - CASE("dump") - { - TCHAR temp[256]; - TCHAR filename[256]; - u32 start; - u32 end; - sscanf(cmd, "%s %08x %08x %s", temp, &start, &end, filename); - - FILE *f = fopen(filename, "wb"); - for (u32 i=start; i +#include + +#include "Common.h" +#include "Thread.h" +#include "HW/Memmap.h" +#include "PowerPC/PPCAnalyst.h" +#include "PowerPC/PPCTables.h" +#include "CoreTiming.h" +#include "Core.h" +#include "PowerPC/Jit64/JitCache.h" +#include "PowerPC/SymbolDB.h" +#include "PowerPCDisasm.h" +#include "Console.h" + +#define CASE(x) else if (memcmp(cmd, x, 4*sizeof(TCHAR))==0) +#define CASE1(x) if (memcmp(cmd, x, 2*sizeof(TCHAR))==0) + +void Console_Submit(const char *cmd) +{ + CASE1("jits") + { +#ifdef _M_X64 + Jit64::PrintStats(); +#endif + } + CASE1("r") + { + Core::StartTrace(false); + LOG(CONSOLE, "read tracing started."); + } + CASE1("w") + { + Core::StartTrace(true); + LOG(CONSOLE, "write tracing started."); + } + CASE("trans") + { + TCHAR temp[256]; + u32 addr; + sscanf(cmd, "%s %08x", temp, &addr); + + if (addr!=0) + { +#ifdef LOGGING + u32 EA = +#endif + Memory::CheckDTLB(addr, Memory::FLAG_NO_EXCEPTION); + LOG(CONSOLE, "EA 0x%08x to 0x%08x", addr, EA); + } + else + { + LOG(CONSOLE, "Syntax: trans ADDR"); + } + } + CASE("call") + { + TCHAR temp[256]; + u32 addr; + sscanf(cmd, "%s %08x", temp, &addr); + if (addr!=0) + { + g_symbolDB.PrintCalls(addr); + } + else + { + LOG(CONSOLE, "Syntax: call ADDR"); + } + } + CASE("llac") + { + TCHAR temp[256]; + u32 addr; + sscanf(cmd, "%s %08x", temp, &addr); + if (addr!=0) + { + g_symbolDB.PrintCallers(addr); + } + else + { + LOG(CONSOLE, "Syntax: llac ADDR"); + } + } + CASE("pend") + { + CoreTiming::LogPendingEvents(); + } + CASE("dump") + { + TCHAR temp[256]; + TCHAR filename[256]; + u32 start; + u32 end; + sscanf(cmd, "%s %08x %08x %s", temp, &start, &end, filename); + + FILE *f = fopen(filename, "wb"); + for (u32 i=start; i -#else - -#endif - -#include "Thread.h" -#include "Timer.h" - -#include "Console.h" -#include "Core.h" -#include "CPUDetect.h" -#include "CoreTiming.h" -#include "Boot/Boot.h" -#include "PatchEngine.h" - -#include "HW/Memmap.h" -#include "HW/PeripheralInterface.h" -#include "HW/GPFifo.h" -#include "HW/CPU.h" -#include "HW/CPUCompare.h" -#include "HW/HW.h" -#include "HW/DSP.h" -#include "HW/GPFifo.h" -#include "HW/AudioInterface.h" -#include "HW/VideoInterface.h" -#include "HW/CommandProcessor.h" -#include "HW/PixelEngine.h" -#include "HW/SystemTimers.h" - -#include "PowerPC/PowerPC.h" - -#include "Plugins/Plugin_Video.h" -#include "Plugins/Plugin_PAD.h" -#include "Plugins/Plugin_DSP.h" -#include "Plugins/Plugin_Wiimote.h" - -#include "MemTools.h" -#include "Host.h" -#include "LogManager.h" - -#include "State.h" - -#ifndef _WIN32 -#define WINAPI -#endif - -// The idea behind the recent restructure is to fix various stupid problems. -// glXMakeCurrent/ wglMakeCurrent takes a context and makes it current on the current thread. -// So it's fine to init ogl on one thread, and then make it current and start blasting on another. - -namespace Core -{ -// forwarding -//void Callback_VideoRequestWindowSize(int _iWidth, int _iHeight, BOOL _bFullscreen); -void Callback_VideoLog(const TCHAR* _szMessage, BOOL _bDoBreak); -void Callback_VideoCopiedToXFB(); -void Callback_DSPLog(const TCHAR* _szMessage, int _v); -char * Callback_ISOName(void); -void Callback_DSPInterrupt(); -void Callback_PADLog(const TCHAR* _szMessage); -void Callback_WiimoteLog(const TCHAR* _szMessage, int _v); -void Callback_WiimoteInput(u16 _channelID, const void* _pData, u32 _Size); - -// For keyboard shortcuts. -void Callback_KeyPress(int key, BOOL shift, BOOL control); - -TPeekMessages Callback_PeekMessages = NULL; -TUpdateFPSDisplay g_pUpdateFPSDisplay = NULL; - -#ifdef _WIN32 -DWORD WINAPI EmuThread(void *pArg); -#else -void *EmuThread(void *pArg); -#endif -void Stop(); - -bool g_bHwInit = false; -HWND g_pWindowHandle = NULL; -Common::Thread* g_pThread = NULL; - -SCoreStartupParameter g_CoreStartupParameter; //uck - -Common::Event emuThreadGoing; - -bool PanicAlertToVideo(const char* text, bool yes_no) -{ - DisplayMessage(text, 3000); - return true; -} - -// Called from GUI thread -bool Init(const SCoreStartupParameter _CoreParameter) -{ - if (g_pThread != NULL) { - PanicAlert("ERROR: Emu Thread already running. Report this bug."); - return false; - } - - LogManager::Init(); - Host_SetWaitCursor(true); - - g_CoreStartupParameter = _CoreParameter; - - // start the thread again - _dbg_assert_(HLE, g_pThread == NULL); - - // load plugins - if (!PluginDSP::LoadPlugin(g_CoreStartupParameter.m_strDSPPlugin.c_str())) { - PanicAlert("Failed to load DSP plugin %s", g_CoreStartupParameter.m_strDSPPlugin.c_str()); - return false; - } - if (!PluginPAD::LoadPlugin(g_CoreStartupParameter.m_strPadPlugin.c_str())) { - PanicAlert("Failed to load PAD plugin %s", g_CoreStartupParameter.m_strPadPlugin.c_str()); - return false; - } - if (!PluginVideo::LoadPlugin(g_CoreStartupParameter.m_strVideoPlugin.c_str())) { - PanicAlert("Failed to load video plugin %s", g_CoreStartupParameter.m_strVideoPlugin.c_str()); - return false; - } - if (!PluginWiimote::LoadPlugin(g_CoreStartupParameter.m_strWiimotePlugin.c_str())) { - PanicAlert("Failed to load Wiimote plugin %s", g_CoreStartupParameter.m_strWiimotePlugin.c_str()); - return false; - } - - emuThreadGoing.Init(); - - g_pThread = new Common::Thread(EmuThread, (void*)&g_CoreStartupParameter); - - emuThreadGoing.Wait(); - emuThreadGoing.Shutdown(); - // all right ... here we go - Host_SetWaitCursor(false); - - DisplayMessage("CPU: " + cpu_info.Summarize(), 8000); - DisplayMessage(_CoreParameter.m_strFilename, 3000); - - //PluginVideo::DllDebugger(NULL); - - //RegisterPanicAlertHandler(PanicAlertToVideo); - - return true; -} - -void DisplayMessage(const std::string &message, int time_in_ms) -{ - PluginVideo::Video_AddMessage(message.c_str(), time_in_ms); -} - -void DisplayMessage(const char *message, int time_in_ms) -{ - PluginVideo::Video_AddMessage(message, time_in_ms); -} - - -// Called from GUI thread or VI thread -void Stop() // - Hammertime! -{ - Host_SetWaitCursor(true); - if (PowerPC::state == PowerPC::CPU_POWERDOWN) - return; - - // stop the CPU - PowerPC::state = PowerPC::CPU_POWERDOWN; - - CCPU::StepOpcode(); //kick it if it's waiting - - // The quit is to get it out of its message loop - // Should be moved inside the plugin. -#ifdef _WIN32 - PostMessage((HWND)g_pWindowHandle, WM_QUIT, 0, 0); -#else - PluginVideo::Video_Stop(); -#endif - - delete g_pThread; //Wait for emuthread to close - g_pThread = 0; - Core::StopTrace(); - LogManager::Shutdown(); - Host_SetWaitCursor(false); -} - -THREAD_RETURN CpuThread(void *pArg) -{ - Common::SetCurrentThreadName("CPU thread"); - - const SCoreStartupParameter& _CoreParameter = g_CoreStartupParameter; - if (!g_CoreStartupParameter.bUseDualCore) - { - PluginVideo::Video_Prepare(); //wglMakeCurrent - } - - if (_CoreParameter.bRunCompareServer) - { - CPUCompare::StartServer(); - PowerPC::state = PowerPC::CPU_RUNNING; - } - else if (_CoreParameter.bRunCompareClient) - { - PanicAlert("Compare Debug : Press OK when ready."); - CPUCompare::ConnectAsClient(); - } - - if (_CoreParameter.bLockThreads) - Common::Thread::SetCurrentThreadAffinity(1); //Force to first core - - if (_CoreParameter.bUseFastMem) - { -#ifdef _M_X64 - // Let's run under memory watch - EMM::InstallExceptionHandler(); -#else - PanicAlert("32-bit platforms do not support fastmem yet. Report this bug."); -#endif - } - - CCPU::Run(); - - if (_CoreParameter.bRunCompareServer || _CoreParameter.bRunCompareClient) - { - CPUCompare::Stop(); - } - return 0; -} - -void Callback_DebuggerBreak() -{ - CCPU::EnableStepping(true); -} - -THREAD_RETURN EmuThread(void *pArg) -{ - Host_BootingStarted(); - - Common::SetCurrentThreadName("Emuthread - starting"); - const SCoreStartupParameter& _CoreParameter = *(SCoreStartupParameter*)pArg; - - if (_CoreParameter.bLockThreads) - Common::Thread::SetCurrentThreadAffinity(2); //Force to second core - - LOG(OSREPORT, "Starting core = %s mode", _CoreParameter.bWii ? "Wii" : "Gamecube"); - LOG(OSREPORT, "Dualcore = %s", _CoreParameter.bUseDualCore ? "Yes" : "No"); - - HW::Init(); - - emuThreadGoing.Set(); - - // Load the VideoPlugin - SVideoInitialize VideoInitialize; - VideoInitialize.pGetMemoryPointer = Memory::GetPointer; - VideoInitialize.pSetPEToken = PixelEngine::SetToken; - VideoInitialize.pSetPEFinish = PixelEngine::SetFinish; - VideoInitialize.pWindowHandle = _CoreParameter.hMainWindow; // NULL; // filled by video_initialize - VideoInitialize.pLog = Callback_VideoLog; - VideoInitialize.pSysMessage = Host_SysMessage; - VideoInitialize.pRequestWindowSize = NULL; //Callback_VideoRequestWindowSize; - VideoInitialize.pCopiedToXFB = Callback_VideoCopiedToXFB; - VideoInitialize.pVIRegs = VideoInterface::m_UVIUnknownRegs; - VideoInitialize.pPeekMessages = NULL; - VideoInitialize.pUpdateFPSDisplay = NULL; - VideoInitialize.pCPFifo = (SCPFifoStruct*)&CommandProcessor::fifo; - VideoInitialize.pUpdateInterrupts = &(CommandProcessor::UpdateInterruptsFromVideoPlugin); - VideoInitialize.pMemoryBase = Memory::base; - VideoInitialize.pKeyPress = Callback_KeyPress; - VideoInitialize.bWii = _CoreParameter.bWii; - PluginVideo::Video_Initialize(&VideoInitialize); - - // Under linux, this is an X11 Display, not an HWND! - g_pWindowHandle = (HWND)VideoInitialize.pWindowHandle; - Callback_PeekMessages = VideoInitialize.pPeekMessages; - g_pUpdateFPSDisplay = VideoInitialize.pUpdateFPSDisplay; - - // Load and init DSPPlugin - DSPInitialize dspInit; - dspInit.hWnd = g_pWindowHandle; - dspInit.pARAM_Read_U8 = (u8 (__cdecl *)(const u32))DSP::ReadARAM; - dspInit.pGetARAMPointer = DSP::GetARAMPtr; - dspInit.pGetMemoryPointer = Memory::GetPointer; - dspInit.pLog = Callback_DSPLog; - dspInit.pName = Callback_ISOName; - dspInit.pDebuggerBreak = Callback_DebuggerBreak; - dspInit.pGenerateDSPInterrupt = Callback_DSPInterrupt; - dspInit.pGetAudioStreaming = AudioInterface::Callback_GetStreaming; - PluginDSP::DSP_Initialize(dspInit); - - // Load and Init PadPlugin - SPADInitialize PADInitialize; - PADInitialize.hWnd = g_pWindowHandle; - PADInitialize.pLog = Callback_PADLog; - PluginPAD::PAD_Initialize(PADInitialize); - - // Load and Init WiimotePlugin - SWiimoteInitialize WiimoteInitialize; - WiimoteInitialize.hWnd = g_pWindowHandle; - WiimoteInitialize.pLog = Callback_WiimoteLog; - WiimoteInitialize.pWiimoteInput = Callback_WiimoteInput; - PluginWiimote::Wiimote_Initialize(WiimoteInitialize); - - // The hardware is initialized. - g_bHwInit = true; - - // Load GCM/DOL/ELF whatever ... we boot with the interpreter core - PowerPC::SetMode(PowerPC::MODE_INTERPRETER); - CBoot::BootUp(_CoreParameter); - - if( g_pUpdateFPSDisplay != NULL ) - g_pUpdateFPSDisplay("Loading..."); - - // setup our core, but can't use dynarec if we are compare server - if (_CoreParameter.bUseJIT && (!_CoreParameter.bRunCompareServer || _CoreParameter.bRunCompareClient)) - PowerPC::SetMode(PowerPC::MODE_JIT); - else - PowerPC::SetMode(PowerPC::MODE_INTERPRETER); - - // update the window again because all stuff is initialized - Host_UpdateDisasmDialog(); - Host_UpdateMainFrame(); - Host_BootingEnded(); - - //This thread, after creating the EmuWindow, spawns a CPU thread, - //then takes over and becomes the graphics thread - - //In single core mode, the CPU thread does the graphics. In fact, the - //CPU thread should in this case also create the emuwindow... - - //Spawn the CPU thread - Common::Thread *cpuThread = NULL; - ////////////////////////////////////////////////////////////////////////// - // ENTER THE VIDEO THREAD LOOP - ////////////////////////////////////////////////////////////////////////// - - if (!Core::GetStartupParameter().bUseDualCore) - { -#ifdef _WIN32 - cpuThread = new Common::Thread(CpuThread, pArg); - //Common::SetCurrentThreadName("Idle thread"); - //TODO(ector) : investigate using GetMessage instead .. although - //then we lose the powerdown check. ... unless powerdown sends a message :P - while (PowerPC::state != PowerPC::CPU_POWERDOWN) - { - if (Callback_PeekMessages) { - Callback_PeekMessages(); - } - Common::SleepCurrentThread(20); - } -#else - // In single-core mode, the Emulation main thread is also the CPU thread - CpuThread(pArg); -#endif - } - else - { - cpuThread = new Common::Thread(CpuThread, pArg); - PluginVideo::Video_Prepare(); //wglMakeCurrent - Common::SetCurrentThreadName("Video thread"); - PluginVideo::Video_EnterLoop(); - } - - // Wait for CPU thread to exit - it should have been signaled to do so by now - if(cpuThread) - cpuThread->WaitForDeath(); - if( g_pUpdateFPSDisplay != NULL ) - g_pUpdateFPSDisplay("Stopping..."); - if(cpuThread) { - delete cpuThread; - cpuThread = NULL; - } - // Returns after game exited - - g_bHwInit = false; - - PluginPAD::PAD_Shutdown(); - PluginPAD::UnloadPlugin(); - PluginWiimote::Wiimote_Shutdown(); - PluginWiimote::UnloadPlugin(); - PluginDSP::DSP_Shutdown(); - PluginDSP::UnloadPlugin(); - PluginVideo::Video_Shutdown(); - PluginVideo::UnloadPlugin(); - - HW::Shutdown(); - - LOG(MASTER_LOG, "EmuThread exited"); - //The CPU should return when a game is stopped and cleanup should be done here, - //so we can restart the plugins (or load new ones) for the next game - if (_CoreParameter.hMainWindow == g_pWindowHandle) - Host_UpdateMainFrame(); - return 0; -} - -bool SetState(EState _State) -{ - switch(_State) - { - case CORE_UNINITIALIZED: - Stop(); - break; - - case CORE_PAUSE: - CCPU::EnableStepping(true); - break; - - case CORE_RUN: - CCPU::EnableStepping(false); - break; - - default: - return false; - } - - return true; -} - -EState GetState() -{ - if (g_bHwInit) - { - if (CCPU::IsStepping()) - return CORE_PAUSE; - else - return CORE_RUN; - } - return CORE_UNINITIALIZED; -} - -void SaveState() { - State_Save(0); -} - -void LoadState() { - State_Load(0); -} - -const SCoreStartupParameter& GetStartupParameter() -{ - return g_CoreStartupParameter; -} - -bool MakeScreenshot(const std::string &filename) -{ - bool bResult = false; - if (PluginVideo::IsLoaded()) - { - TCHAR szTmpFilename[MAX_PATH]; - strcpy(szTmpFilename, filename.c_str()); - bResult = PluginVideo::Video_Screenshot(szTmpFilename) ? true : false; - } - return bResult; -} - -void* GetWindowHandle() -{ - return g_pWindowHandle; -} - -///////////////////////////////////////////////////////////////////////////////////////////////////// -// --- Callbacks for plugins / engine --- -///////////////////////////////////////////////////////////////////////////////////////////////////// - -// __________________________________________________________________________________________________ -// Callback_VideoLog -// WARNING - THIS IS EXECUTED FROM VIDEO THREAD -void Callback_VideoLog(const TCHAR *_szMessage, BOOL _bDoBreak) -{ - LOG(VIDEO, _szMessage); -} - -// __________________________________________________________________________________________________ -// Callback_VideoCopiedToXFB -// WARNING - THIS IS EXECUTED FROM VIDEO THREAD -// We do not touch anything outside this function here -void Callback_VideoCopiedToXFB() -{ - //count FPS - static Common::Timer Timer; - static u32 frames = 0; - static u64 ticks = 0; - static u64 idleTicks = 0; - frames++; - - if (Timer.GetTimeDifference() >= 1000) - { - u64 newTicks = CoreTiming::GetTicks(); - u64 newIdleTicks = CoreTiming::GetIdleTicks(); - - s64 diff = (newTicks - ticks)/1000000; - s64 idleDiff = (newIdleTicks - idleTicks)/1000000; - - ticks = newTicks; - idleTicks = newIdleTicks; - - float t = (float)(Timer.GetTimeDifference()) / 1000.f; - char temp[256]; - sprintf(temp, "FPS:%8.2f - Core: %s | %s - Speed: %i MHz [Real: %i + IdleSkip: %i] / %i MHz", - (float)frames / t, - g_CoreStartupParameter.bUseJIT ? "JIT" : "Interpreter", - g_CoreStartupParameter.bUseDualCore ? "DC" : "SC", - (int)(diff), - (int)(diff-idleDiff), - (int)(idleDiff), - SystemTimers::GetTicksPerSecond()/1000000); - - if( g_pUpdateFPSDisplay != NULL ) - g_pUpdateFPSDisplay(temp); - - Host_UpdateStatusBar(temp); - - - frames = 0; - Timer.Update(); - } - PatchEngine::ApplyFramePatches(); - PatchEngine::ApplyARPatches(); -} - -// __________________________________________________________________________________________________ -// Callback_DSPLog -// WARNING - THIS MAY EXECUTED FROM DSP THREAD -void Callback_DSPLog(const TCHAR* _szMessage, int _v) -{ - LOGV(AUDIO, _v, _szMessage); -} - -// __________________________________________________________________________________________________ -// Callback_DSPInterrupt -// WARNING - THIS MAY EXECUTED FROM DSP THREAD -void Callback_DSPInterrupt() -{ - DSP::GenerateDSPInterruptFromPlugin(DSP::INT_DSP); -} - -// __________________________________________________________________________________________________ -// Callback_PADLog -// -void Callback_PADLog(const TCHAR* _szMessage) -{ - LOG(SERIALINTERFACE, _szMessage); -} - -// __________________________________________________________________________________________________ -// Callback_ISOName: Let the DSP plugin get the game name -// -//std::string Callback_ISOName(void) -char * Callback_ISOName(void) -{ - if(g_CoreStartupParameter.m_strName.length() > 0) - return (char *)g_CoreStartupParameter.m_strName.c_str(); - else - return (char *)""; -} - -// __________________________________________________________________________________________________ -// Called from ANY thread! -void Callback_KeyPress(int key, BOOL shift, BOOL control) -{ - // 0x70 == VK_F1 - if (key >= 0x70 && key < 0x79) { - // F-key - int slot_number = key - 0x70 + 1; - if (shift) { - State_Save(slot_number); - } else { - State_Load(slot_number); - } - } -} - -// __________________________________________________________________________________________________ -// Callback_WiimoteLog -// -void Callback_WiimoteLog(const TCHAR* _szMessage, int _v) -{ - LOGV(WII_IPC_WIIMOTE, _v, _szMessage); -} - -} // end of namespace Core +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifdef _WIN32 +#include +#else + +#endif + +#include "Thread.h" +#include "Timer.h" + +#include "Console.h" +#include "Core.h" +#include "CPUDetect.h" +#include "CoreTiming.h" +#include "Boot/Boot.h" +#include "PatchEngine.h" + +#include "HW/Memmap.h" +#include "HW/PeripheralInterface.h" +#include "HW/GPFifo.h" +#include "HW/CPU.h" +#include "HW/CPUCompare.h" +#include "HW/HW.h" +#include "HW/DSP.h" +#include "HW/GPFifo.h" +#include "HW/AudioInterface.h" +#include "HW/VideoInterface.h" +#include "HW/CommandProcessor.h" +#include "HW/PixelEngine.h" +#include "HW/SystemTimers.h" + +#include "PowerPC/PowerPC.h" + +#include "Plugins/Plugin_Video.h" +#include "Plugins/Plugin_PAD.h" +#include "Plugins/Plugin_DSP.h" +#include "Plugins/Plugin_Wiimote.h" + +#include "MemTools.h" +#include "Host.h" +#include "LogManager.h" + +#include "State.h" + +#ifndef _WIN32 +#define WINAPI +#endif + +// The idea behind the recent restructure is to fix various stupid problems. +// glXMakeCurrent/ wglMakeCurrent takes a context and makes it current on the current thread. +// So it's fine to init ogl on one thread, and then make it current and start blasting on another. + +namespace Core +{ +// forwarding +//void Callback_VideoRequestWindowSize(int _iWidth, int _iHeight, BOOL _bFullscreen); +void Callback_VideoLog(const TCHAR* _szMessage, BOOL _bDoBreak); +void Callback_VideoCopiedToXFB(); +void Callback_DSPLog(const TCHAR* _szMessage, int _v); +char * Callback_ISOName(void); +void Callback_DSPInterrupt(); +void Callback_PADLog(const TCHAR* _szMessage); +void Callback_WiimoteLog(const TCHAR* _szMessage, int _v); +void Callback_WiimoteInput(u16 _channelID, const void* _pData, u32 _Size); + +// For keyboard shortcuts. +void Callback_KeyPress(int key, BOOL shift, BOOL control); + +TPeekMessages Callback_PeekMessages = NULL; +TUpdateFPSDisplay g_pUpdateFPSDisplay = NULL; + +#ifdef _WIN32 +DWORD WINAPI EmuThread(void *pArg); +#else +void *EmuThread(void *pArg); +#endif +void Stop(); + +bool g_bHwInit = false; +HWND g_pWindowHandle = NULL; +Common::Thread* g_pThread = NULL; + +SCoreStartupParameter g_CoreStartupParameter; //uck + +Common::Event emuThreadGoing; + +bool PanicAlertToVideo(const char* text, bool yes_no) +{ + DisplayMessage(text, 3000); + return true; +} + +// Called from GUI thread +bool Init(const SCoreStartupParameter _CoreParameter) +{ + if (g_pThread != NULL) { + PanicAlert("ERROR: Emu Thread already running. Report this bug."); + return false; + } + + LogManager::Init(); + Host_SetWaitCursor(true); + + g_CoreStartupParameter = _CoreParameter; + + // start the thread again + _dbg_assert_(HLE, g_pThread == NULL); + + // load plugins + if (!PluginDSP::LoadPlugin(g_CoreStartupParameter.m_strDSPPlugin.c_str())) { + PanicAlert("Failed to load DSP plugin %s", g_CoreStartupParameter.m_strDSPPlugin.c_str()); + return false; + } + if (!PluginPAD::LoadPlugin(g_CoreStartupParameter.m_strPadPlugin.c_str())) { + PanicAlert("Failed to load PAD plugin %s", g_CoreStartupParameter.m_strPadPlugin.c_str()); + return false; + } + if (!PluginVideo::LoadPlugin(g_CoreStartupParameter.m_strVideoPlugin.c_str())) { + PanicAlert("Failed to load video plugin %s", g_CoreStartupParameter.m_strVideoPlugin.c_str()); + return false; + } + if (!PluginWiimote::LoadPlugin(g_CoreStartupParameter.m_strWiimotePlugin.c_str())) { + PanicAlert("Failed to load Wiimote plugin %s", g_CoreStartupParameter.m_strWiimotePlugin.c_str()); + return false; + } + + emuThreadGoing.Init(); + + g_pThread = new Common::Thread(EmuThread, (void*)&g_CoreStartupParameter); + + emuThreadGoing.Wait(); + emuThreadGoing.Shutdown(); + // all right ... here we go + Host_SetWaitCursor(false); + + DisplayMessage("CPU: " + cpu_info.Summarize(), 8000); + DisplayMessage(_CoreParameter.m_strFilename, 3000); + + //PluginVideo::DllDebugger(NULL); + + //RegisterPanicAlertHandler(PanicAlertToVideo); + + return true; +} + +void DisplayMessage(const std::string &message, int time_in_ms) +{ + PluginVideo::Video_AddMessage(message.c_str(), time_in_ms); +} + +void DisplayMessage(const char *message, int time_in_ms) +{ + PluginVideo::Video_AddMessage(message, time_in_ms); +} + + +// Called from GUI thread or VI thread +void Stop() // - Hammertime! +{ + Host_SetWaitCursor(true); + if (PowerPC::state == PowerPC::CPU_POWERDOWN) + return; + + // stop the CPU + PowerPC::state = PowerPC::CPU_POWERDOWN; + + CCPU::StepOpcode(); //kick it if it's waiting + + // The quit is to get it out of its message loop + // Should be moved inside the plugin. +#ifdef _WIN32 + PostMessage((HWND)g_pWindowHandle, WM_QUIT, 0, 0); +#else + PluginVideo::Video_Stop(); +#endif + + delete g_pThread; //Wait for emuthread to close + g_pThread = 0; + Core::StopTrace(); + LogManager::Shutdown(); + Host_SetWaitCursor(false); +} + +THREAD_RETURN CpuThread(void *pArg) +{ + Common::SetCurrentThreadName("CPU thread"); + + const SCoreStartupParameter& _CoreParameter = g_CoreStartupParameter; + if (!g_CoreStartupParameter.bUseDualCore) + { + PluginVideo::Video_Prepare(); //wglMakeCurrent + } + + if (_CoreParameter.bRunCompareServer) + { + CPUCompare::StartServer(); + PowerPC::state = PowerPC::CPU_RUNNING; + } + else if (_CoreParameter.bRunCompareClient) + { + PanicAlert("Compare Debug : Press OK when ready."); + CPUCompare::ConnectAsClient(); + } + + if (_CoreParameter.bLockThreads) + Common::Thread::SetCurrentThreadAffinity(1); //Force to first core + + if (_CoreParameter.bUseFastMem) + { +#ifdef _M_X64 + // Let's run under memory watch + EMM::InstallExceptionHandler(); +#else + PanicAlert("32-bit platforms do not support fastmem yet. Report this bug."); +#endif + } + + CCPU::Run(); + + if (_CoreParameter.bRunCompareServer || _CoreParameter.bRunCompareClient) + { + CPUCompare::Stop(); + } + return 0; +} + +void Callback_DebuggerBreak() +{ + CCPU::EnableStepping(true); +} + +THREAD_RETURN EmuThread(void *pArg) +{ + Host_BootingStarted(); + + Common::SetCurrentThreadName("Emuthread - starting"); + const SCoreStartupParameter& _CoreParameter = *(SCoreStartupParameter*)pArg; + + if (_CoreParameter.bLockThreads) + Common::Thread::SetCurrentThreadAffinity(2); //Force to second core + + LOG(OSREPORT, "Starting core = %s mode", _CoreParameter.bWii ? "Wii" : "Gamecube"); + LOG(OSREPORT, "Dualcore = %s", _CoreParameter.bUseDualCore ? "Yes" : "No"); + + HW::Init(); + + emuThreadGoing.Set(); + + // Load the VideoPlugin + SVideoInitialize VideoInitialize; + VideoInitialize.pGetMemoryPointer = Memory::GetPointer; + VideoInitialize.pSetPEToken = PixelEngine::SetToken; + VideoInitialize.pSetPEFinish = PixelEngine::SetFinish; + VideoInitialize.pWindowHandle = _CoreParameter.hMainWindow; // NULL; // filled by video_initialize + VideoInitialize.pLog = Callback_VideoLog; + VideoInitialize.pSysMessage = Host_SysMessage; + VideoInitialize.pRequestWindowSize = NULL; //Callback_VideoRequestWindowSize; + VideoInitialize.pCopiedToXFB = Callback_VideoCopiedToXFB; + VideoInitialize.pVIRegs = VideoInterface::m_UVIUnknownRegs; + VideoInitialize.pPeekMessages = NULL; + VideoInitialize.pUpdateFPSDisplay = NULL; + VideoInitialize.pCPFifo = (SCPFifoStruct*)&CommandProcessor::fifo; + VideoInitialize.pUpdateInterrupts = &(CommandProcessor::UpdateInterruptsFromVideoPlugin); + VideoInitialize.pMemoryBase = Memory::base; + VideoInitialize.pKeyPress = Callback_KeyPress; + VideoInitialize.bWii = _CoreParameter.bWii; + PluginVideo::Video_Initialize(&VideoInitialize); + + // Under linux, this is an X11 Display, not an HWND! + g_pWindowHandle = (HWND)VideoInitialize.pWindowHandle; + Callback_PeekMessages = VideoInitialize.pPeekMessages; + g_pUpdateFPSDisplay = VideoInitialize.pUpdateFPSDisplay; + + // Load and init DSPPlugin + DSPInitialize dspInit; + dspInit.hWnd = g_pWindowHandle; + dspInit.pARAM_Read_U8 = (u8 (__cdecl *)(const u32))DSP::ReadARAM; + dspInit.pGetARAMPointer = DSP::GetARAMPtr; + dspInit.pGetMemoryPointer = Memory::GetPointer; + dspInit.pLog = Callback_DSPLog; + dspInit.pName = Callback_ISOName; + dspInit.pDebuggerBreak = Callback_DebuggerBreak; + dspInit.pGenerateDSPInterrupt = Callback_DSPInterrupt; + dspInit.pGetAudioStreaming = AudioInterface::Callback_GetStreaming; + PluginDSP::DSP_Initialize(dspInit); + + // Load and Init PadPlugin + SPADInitialize PADInitialize; + PADInitialize.hWnd = g_pWindowHandle; + PADInitialize.pLog = Callback_PADLog; + PluginPAD::PAD_Initialize(PADInitialize); + + // Load and Init WiimotePlugin + SWiimoteInitialize WiimoteInitialize; + WiimoteInitialize.hWnd = g_pWindowHandle; + WiimoteInitialize.pLog = Callback_WiimoteLog; + WiimoteInitialize.pWiimoteInput = Callback_WiimoteInput; + PluginWiimote::Wiimote_Initialize(WiimoteInitialize); + + // The hardware is initialized. + g_bHwInit = true; + + // Load GCM/DOL/ELF whatever ... we boot with the interpreter core + PowerPC::SetMode(PowerPC::MODE_INTERPRETER); + CBoot::BootUp(_CoreParameter); + + if( g_pUpdateFPSDisplay != NULL ) + g_pUpdateFPSDisplay("Loading..."); + + // setup our core, but can't use dynarec if we are compare server + if (_CoreParameter.bUseJIT && (!_CoreParameter.bRunCompareServer || _CoreParameter.bRunCompareClient)) + PowerPC::SetMode(PowerPC::MODE_JIT); + else + PowerPC::SetMode(PowerPC::MODE_INTERPRETER); + + // update the window again because all stuff is initialized + Host_UpdateDisasmDialog(); + Host_UpdateMainFrame(); + Host_BootingEnded(); + + //This thread, after creating the EmuWindow, spawns a CPU thread, + //then takes over and becomes the graphics thread + + //In single core mode, the CPU thread does the graphics. In fact, the + //CPU thread should in this case also create the emuwindow... + + //Spawn the CPU thread + Common::Thread *cpuThread = NULL; + ////////////////////////////////////////////////////////////////////////// + // ENTER THE VIDEO THREAD LOOP + ////////////////////////////////////////////////////////////////////////// + + if (!Core::GetStartupParameter().bUseDualCore) + { +#ifdef _WIN32 + cpuThread = new Common::Thread(CpuThread, pArg); + //Common::SetCurrentThreadName("Idle thread"); + //TODO(ector) : investigate using GetMessage instead .. although + //then we lose the powerdown check. ... unless powerdown sends a message :P + while (PowerPC::state != PowerPC::CPU_POWERDOWN) + { + if (Callback_PeekMessages) { + Callback_PeekMessages(); + } + Common::SleepCurrentThread(20); + } +#else + // In single-core mode, the Emulation main thread is also the CPU thread + CpuThread(pArg); +#endif + } + else + { + cpuThread = new Common::Thread(CpuThread, pArg); + PluginVideo::Video_Prepare(); //wglMakeCurrent + Common::SetCurrentThreadName("Video thread"); + PluginVideo::Video_EnterLoop(); + } + + // Wait for CPU thread to exit - it should have been signaled to do so by now + if(cpuThread) + cpuThread->WaitForDeath(); + if( g_pUpdateFPSDisplay != NULL ) + g_pUpdateFPSDisplay("Stopping..."); + if(cpuThread) { + delete cpuThread; + cpuThread = NULL; + } + // Returns after game exited + + g_bHwInit = false; + + PluginPAD::PAD_Shutdown(); + PluginPAD::UnloadPlugin(); + PluginWiimote::Wiimote_Shutdown(); + PluginWiimote::UnloadPlugin(); + PluginDSP::DSP_Shutdown(); + PluginDSP::UnloadPlugin(); + PluginVideo::Video_Shutdown(); + PluginVideo::UnloadPlugin(); + + HW::Shutdown(); + + LOG(MASTER_LOG, "EmuThread exited"); + //The CPU should return when a game is stopped and cleanup should be done here, + //so we can restart the plugins (or load new ones) for the next game + if (_CoreParameter.hMainWindow == g_pWindowHandle) + Host_UpdateMainFrame(); + return 0; +} + +bool SetState(EState _State) +{ + switch(_State) + { + case CORE_UNINITIALIZED: + Stop(); + break; + + case CORE_PAUSE: + CCPU::EnableStepping(true); + break; + + case CORE_RUN: + CCPU::EnableStepping(false); + break; + + default: + return false; + } + + return true; +} + +EState GetState() +{ + if (g_bHwInit) + { + if (CCPU::IsStepping()) + return CORE_PAUSE; + else + return CORE_RUN; + } + return CORE_UNINITIALIZED; +} + +void SaveState() { + State_Save(0); +} + +void LoadState() { + State_Load(0); +} + +const SCoreStartupParameter& GetStartupParameter() +{ + return g_CoreStartupParameter; +} + +bool MakeScreenshot(const std::string &filename) +{ + bool bResult = false; + if (PluginVideo::IsLoaded()) + { + TCHAR szTmpFilename[MAX_PATH]; + strcpy(szTmpFilename, filename.c_str()); + bResult = PluginVideo::Video_Screenshot(szTmpFilename) ? true : false; + } + return bResult; +} + +void* GetWindowHandle() +{ + return g_pWindowHandle; +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// +// --- Callbacks for plugins / engine --- +///////////////////////////////////////////////////////////////////////////////////////////////////// + +// __________________________________________________________________________________________________ +// Callback_VideoLog +// WARNING - THIS IS EXECUTED FROM VIDEO THREAD +void Callback_VideoLog(const TCHAR *_szMessage, BOOL _bDoBreak) +{ + LOG(VIDEO, _szMessage); +} + +// __________________________________________________________________________________________________ +// Callback_VideoCopiedToXFB +// WARNING - THIS IS EXECUTED FROM VIDEO THREAD +// We do not touch anything outside this function here +void Callback_VideoCopiedToXFB() +{ + //count FPS + static Common::Timer Timer; + static u32 frames = 0; + static u64 ticks = 0; + static u64 idleTicks = 0; + frames++; + + if (Timer.GetTimeDifference() >= 1000) + { + u64 newTicks = CoreTiming::GetTicks(); + u64 newIdleTicks = CoreTiming::GetIdleTicks(); + + s64 diff = (newTicks - ticks)/1000000; + s64 idleDiff = (newIdleTicks - idleTicks)/1000000; + + ticks = newTicks; + idleTicks = newIdleTicks; + + float t = (float)(Timer.GetTimeDifference()) / 1000.f; + char temp[256]; + sprintf(temp, "FPS:%8.2f - Core: %s | %s - Speed: %i MHz [Real: %i + IdleSkip: %i] / %i MHz", + (float)frames / t, + g_CoreStartupParameter.bUseJIT ? "JIT" : "Interpreter", + g_CoreStartupParameter.bUseDualCore ? "DC" : "SC", + (int)(diff), + (int)(diff-idleDiff), + (int)(idleDiff), + SystemTimers::GetTicksPerSecond()/1000000); + + if( g_pUpdateFPSDisplay != NULL ) + g_pUpdateFPSDisplay(temp); + + Host_UpdateStatusBar(temp); + + + frames = 0; + Timer.Update(); + } + PatchEngine::ApplyFramePatches(); + PatchEngine::ApplyARPatches(); +} + +// __________________________________________________________________________________________________ +// Callback_DSPLog +// WARNING - THIS MAY EXECUTED FROM DSP THREAD +void Callback_DSPLog(const TCHAR* _szMessage, int _v) +{ + LOGV(AUDIO, _v, _szMessage); +} + +// __________________________________________________________________________________________________ +// Callback_DSPInterrupt +// WARNING - THIS MAY EXECUTED FROM DSP THREAD +void Callback_DSPInterrupt() +{ + DSP::GenerateDSPInterruptFromPlugin(DSP::INT_DSP); +} + +// __________________________________________________________________________________________________ +// Callback_PADLog +// +void Callback_PADLog(const TCHAR* _szMessage) +{ + LOG(SERIALINTERFACE, _szMessage); +} + +// __________________________________________________________________________________________________ +// Callback_ISOName: Let the DSP plugin get the game name +// +//std::string Callback_ISOName(void) +char * Callback_ISOName(void) +{ + if(g_CoreStartupParameter.m_strName.length() > 0) + return (char *)g_CoreStartupParameter.m_strName.c_str(); + else + return (char *)""; +} + +// __________________________________________________________________________________________________ +// Called from ANY thread! +void Callback_KeyPress(int key, BOOL shift, BOOL control) +{ + // 0x70 == VK_F1 + if (key >= 0x70 && key < 0x79) { + // F-key + int slot_number = key - 0x70 + 1; + if (shift) { + State_Save(slot_number); + } else { + State_Load(slot_number); + } + } +} + +// __________________________________________________________________________________________________ +// Callback_WiimoteLog +// +void Callback_WiimoteLog(const TCHAR* _szMessage, int _v) +{ + LOGV(WII_IPC_WIIMOTE, _v, _szMessage); +} + +} // end of namespace Core diff --git a/Source/Core/Core/Src/CoreParameter.cpp b/Source/Core/Core/Src/CoreParameter.cpp index 2dfef5c201..6cc4f0de73 100644 --- a/Source/Core/Core/Src/CoreParameter.cpp +++ b/Source/Core/Core/Src/CoreParameter.cpp @@ -1,160 +1,160 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "Boot/Boot.h" -#include "FileUtil.h" -#include "StringUtil.h" -#include "CoreParameter.h" -#include "VolumeCreator.h" - -SCoreStartupParameter::SCoreStartupParameter() -{ - LoadDefaults(); -} - -void SCoreStartupParameter::LoadDefaults() -{ - bEnableDebugging = false; - bUseJIT = false; - bUseDualCore = false; - bSkipIdle = false; - bRunCompareServer = false; - bLockThreads = true; - bWii = false; - SelectedLanguage = 0; - - bJITOff = false; // debugger only settings - bJITLoadStoreOff = false; - bJITLoadStoreFloatingOff = false; - bJITLoadStorePairedOff = false; - bJITFloatingPointOff = false; - bJITIntegerOff = false; - bJITPairedOff = false; - bJITSystemRegistersOff = false; -} - -bool SCoreStartupParameter::AutoSetup(EBootBios _BootBios) -{ - std::string Region(EUR_DIR); - - switch (_BootBios) - { - case BOOT_DEFAULT: - { - /* Check if the file exist, we may have gotten it from a --elf command line - that gave an incorrect file name */ - if (!File::Exists(m_strFilename.c_str())) - { - PanicAlert("The file you specified (%s) does not exists", m_strFilename.c_str()); - return false; - } - - std::string Extension; - SplitPath(m_strFilename, NULL, NULL, &Extension); - if (!strcasecmp(Extension.c_str(), ".gcm") || - !strcasecmp(Extension.c_str(), ".iso") || - !strcasecmp(Extension.c_str(), ".gcz") ) - { - m_BootType = BOOT_ISO; - DiscIO::IVolume* pVolume = DiscIO::CreateVolumeFromFilename(m_strFilename.c_str()); - if (pVolume == NULL) - { - PanicAlert("Your GCM/ISO file seems to be invalid, or not a GC/Wii ISO."); - return false; - } - m_strName = pVolume->GetName(); - m_strUniqueID = pVolume->GetUniqueID(); - bWii = DiscIO::IsVolumeWiiDisc(pVolume); - - switch (pVolume->GetCountry()) - { - case DiscIO::IVolume::COUNTRY_USA: - bNTSC = true; - Region = USA_DIR; - break; - - case DiscIO::IVolume::COUNTRY_JAP: - bNTSC = true; - Region = JAP_DIR; - break; - - case DiscIO::IVolume::COUNTRY_EUROPE: - case DiscIO::IVolume::COUNTRY_FRANCE: - bNTSC = false; - Region = EUR_DIR; - break; - - default: - PanicAlert("Your GCM/ISO file seems to be invalid (invalid country)."); - return false; - } - - delete pVolume; - } - else if (!strcasecmp(Extension.c_str(), ".elf")) - { - bWii = CBoot::IsElfWii(m_strFilename.c_str()); - Region = USA_DIR; - m_BootType = BOOT_ELF; - bNTSC = true; - } - else if (!strcasecmp(Extension.c_str(), ".dol")) - { - Region = USA_DIR; - m_BootType = BOOT_DOL; - bNTSC = true; - } - else - { - PanicAlert("Could not recognize ISO file %s", m_strFilename.c_str()); - return false; - } - } - break; - - case BOOT_BIOS_USA: - Region = USA_DIR; - m_strFilename.clear(); - bNTSC = true; - break; - - case BOOT_BIOS_JAP: - Region = JAP_DIR; - m_strFilename.clear(); - bNTSC = true; - break; - - case BOOT_BIOS_EUR: - Region = EUR_DIR; - m_strFilename.clear(); - bNTSC = false; - break; - } - - // setup paths - m_strBios = FULL_GC_SYS_DIR + Region + DIR_SEP GC_IPL; - m_strMemoryCardA = FULL_GC_USER_DIR + Region + DIR_SEP GC_MEMCARDA; - m_strMemoryCardB = FULL_GC_USER_DIR + Region + DIR_SEP GC_MEMCARDB; - m_strSRAM = GC_SRAM_FILE; - if (!File::Exists(m_strBios.c_str())) { - LOG(BOOT, "BIOS file %s not found - using HLE.", m_strBios.c_str()); - bHLEBios = true; - } - - return true; -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "Boot/Boot.h" +#include "FileUtil.h" +#include "StringUtil.h" +#include "CoreParameter.h" +#include "VolumeCreator.h" + +SCoreStartupParameter::SCoreStartupParameter() +{ + LoadDefaults(); +} + +void SCoreStartupParameter::LoadDefaults() +{ + bEnableDebugging = false; + bUseJIT = false; + bUseDualCore = false; + bSkipIdle = false; + bRunCompareServer = false; + bLockThreads = true; + bWii = false; + SelectedLanguage = 0; + + bJITOff = false; // debugger only settings + bJITLoadStoreOff = false; + bJITLoadStoreFloatingOff = false; + bJITLoadStorePairedOff = false; + bJITFloatingPointOff = false; + bJITIntegerOff = false; + bJITPairedOff = false; + bJITSystemRegistersOff = false; +} + +bool SCoreStartupParameter::AutoSetup(EBootBios _BootBios) +{ + std::string Region(EUR_DIR); + + switch (_BootBios) + { + case BOOT_DEFAULT: + { + /* Check if the file exist, we may have gotten it from a --elf command line + that gave an incorrect file name */ + if (!File::Exists(m_strFilename.c_str())) + { + PanicAlert("The file you specified (%s) does not exists", m_strFilename.c_str()); + return false; + } + + std::string Extension; + SplitPath(m_strFilename, NULL, NULL, &Extension); + if (!strcasecmp(Extension.c_str(), ".gcm") || + !strcasecmp(Extension.c_str(), ".iso") || + !strcasecmp(Extension.c_str(), ".gcz") ) + { + m_BootType = BOOT_ISO; + DiscIO::IVolume* pVolume = DiscIO::CreateVolumeFromFilename(m_strFilename.c_str()); + if (pVolume == NULL) + { + PanicAlert("Your GCM/ISO file seems to be invalid, or not a GC/Wii ISO."); + return false; + } + m_strName = pVolume->GetName(); + m_strUniqueID = pVolume->GetUniqueID(); + bWii = DiscIO::IsVolumeWiiDisc(pVolume); + + switch (pVolume->GetCountry()) + { + case DiscIO::IVolume::COUNTRY_USA: + bNTSC = true; + Region = USA_DIR; + break; + + case DiscIO::IVolume::COUNTRY_JAP: + bNTSC = true; + Region = JAP_DIR; + break; + + case DiscIO::IVolume::COUNTRY_EUROPE: + case DiscIO::IVolume::COUNTRY_FRANCE: + bNTSC = false; + Region = EUR_DIR; + break; + + default: + PanicAlert("Your GCM/ISO file seems to be invalid (invalid country)."); + return false; + } + + delete pVolume; + } + else if (!strcasecmp(Extension.c_str(), ".elf")) + { + bWii = CBoot::IsElfWii(m_strFilename.c_str()); + Region = USA_DIR; + m_BootType = BOOT_ELF; + bNTSC = true; + } + else if (!strcasecmp(Extension.c_str(), ".dol")) + { + Region = USA_DIR; + m_BootType = BOOT_DOL; + bNTSC = true; + } + else + { + PanicAlert("Could not recognize ISO file %s", m_strFilename.c_str()); + return false; + } + } + break; + + case BOOT_BIOS_USA: + Region = USA_DIR; + m_strFilename.clear(); + bNTSC = true; + break; + + case BOOT_BIOS_JAP: + Region = JAP_DIR; + m_strFilename.clear(); + bNTSC = true; + break; + + case BOOT_BIOS_EUR: + Region = EUR_DIR; + m_strFilename.clear(); + bNTSC = false; + break; + } + + // setup paths + m_strBios = FULL_GC_SYS_DIR + Region + DIR_SEP GC_IPL; + m_strMemoryCardA = FULL_GC_USER_DIR + Region + DIR_SEP GC_MEMCARDA; + m_strMemoryCardB = FULL_GC_USER_DIR + Region + DIR_SEP GC_MEMCARDB; + m_strSRAM = GC_SRAM_FILE; + if (!File::Exists(m_strBios.c_str())) { + LOG(BOOT, "BIOS file %s not found - using HLE.", m_strBios.c_str()); + bHLEBios = true; + } + + return true; +} diff --git a/Source/Core/Core/Src/CoreTiming.cpp b/Source/Core/Core/Src/CoreTiming.cpp index 1f1617a9d3..5dcec626a9 100644 --- a/Source/Core/Core/Src/CoreTiming.cpp +++ b/Source/Core/Core/Src/CoreTiming.cpp @@ -1,377 +1,377 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include - -#include "Thread.h" -#include "PowerPC/PowerPC.h" -#include "CoreTiming.h" -#include "StringUtil.h" - -// TODO(ector): Replace new/delete in this file with a simple memory pool -// Don't expect a massive speedup though. - -namespace CoreTiming -{ - -struct EventType -{ - TimedCallback callback; - const char *name; -}; - -std::vector event_types; - -struct BaseEvent -{ - s64 time; - u64 userdata; - int type; -// Event *next; -}; - -typedef LinkedListItem Event; - -// STATE_TO_SAVE (how?) -Event *first; -Event *tsFirst; - -int downcount, slicelength; -int maxSliceLength = 20000; - -s64 globalTimer; -s64 idledCycles; - -Common::CriticalSection externalEventSection; - -void (*advanceCallback)(int cyclesExecuted); - -int RegisterEvent(const char *name, TimedCallback callback) -{ - EventType type; - type.name = name; - type.callback = callback; - event_types.push_back(type); - return (int)event_types.size() - 1; -} - -void UnregisterAllEvents() -{ - if (first) - PanicAlert("Cannot unregister events with events pending"); - event_types.clear(); -} - -void Init() -{ - downcount = maxSliceLength; - slicelength = maxSliceLength; - globalTimer = 0; - idledCycles = 0; -} - -void Shutdown() -{ - ClearPendingEvents(); - UnregisterAllEvents(); -} - -void DoState(PointerWrap &p) -{ - externalEventSection.Enter(); - p.Do(downcount); - p.Do(slicelength); - p.Do(globalTimer); - p.Do(idledCycles); - // OK, here we're gonna need to specialize depending on the mode. - // Should do something generic to serialize linked lists. - switch (p.GetMode()) { - case PointerWrap::MODE_READ: - { - ClearPendingEvents(); - if (first) - PanicAlert("Clear failed."); - int more_events = 0; - Event *prev = 0; - while (true) { - p.Do(more_events); - if (!more_events) - break; - Event *ev = new Event; - if (!prev) - first = ev; - else - prev->next = ev; - p.Do(ev->time); - p.Do(ev->type); - p.Do(ev->userdata); - ev->next = 0; - prev = ev; - ev = ev->next; - } - } - break; - case PointerWrap::MODE_MEASURE: - case PointerWrap::MODE_WRITE: - { - Event *ev = first; - int more_events = 1; - while (ev) { - p.Do(more_events); - p.Do(ev->time); - p.Do(ev->type); - p.Do(ev->userdata); - ev = ev->next; - } - more_events = 0; - p.Do(more_events); - break; - } - } - externalEventSection.Leave(); -} - -u64 GetTicks() -{ - return (u64)globalTimer; -} - -u64 GetIdleTicks() -{ - return (u64)idledCycles; -} - -// This is to be called when outside threads, such as the graphics thread, wants to -// schedule things to be executed on the main thread. -void ScheduleEvent_Threadsafe(int cyclesIntoFuture, int event_type, u64 userdata) -{ - externalEventSection.Enter(); - Event *ne = new Event; - ne->time = globalTimer + cyclesIntoFuture; - ne->type = event_type; - ne->next = tsFirst; - ne->userdata = userdata; - tsFirst = ne; - externalEventSection.Leave(); -} - -void ClearPendingEvents() -{ - while (first) - { - Event *e = first->next; - delete first; - first = e; - } -} - -void AddEventToQueue(Event *ne) -{ - // Damn, this logic got complicated. Must be an easier way. - if (!first) - { - first = ne; - ne->next = 0; - } - else - { - Event *ptr = first; - Event *prev = 0; - if (ptr->time > ne->time) - { - ne->next = first; - first = ne; - } - else - { - prev = first; - ptr = first->next; - - while (ptr) - { - if (ptr->time <= ne->time) - { - prev = ptr; - ptr = ptr->next; - } - else - break; - } - - //OK, ptr points to the item AFTER our new item. Let's insert - ne->next = prev->next; - prev->next = ne; - // Done! - } - } -} - -// This must be run ONLY from within the cpu thread -// cyclesIntoFuture may be VERY inaccurate if called from anything else -// than Advance -void ScheduleEvent(int cyclesIntoFuture, int event_type, u64 userdata) -{ - Event *ne = new Event; - ne->userdata = userdata; - ne->type = event_type; - ne->time = globalTimer + cyclesIntoFuture; - - AddEventToQueue(ne); -} - -void RegisterAdvanceCallback(void (*callback)(int cyclesExecuted)) -{ - advanceCallback = callback; -} - -bool IsScheduled(int event_type) -{ - if (!first) - return false; - Event *e = first; - while (e) { - if (e->type == event_type) - return true; - e = e->next; - } - return false; -} - -void RemoveEvent(int event_type) -{ - if (!first) - return; - if (first->type == event_type) - { - Event *next = first->next; - delete first; - first = next; - } - if (!first) - return; - Event *prev = first; - Event *ptr = prev->next; - while (ptr) - { - if (ptr->type == event_type) - { - prev->next = ptr->next; - delete ptr; - ptr = prev->next; - } - else - { - prev = ptr; - ptr = ptr->next; - } - } -} - -void SetMaximumSlice(int maximumSliceLength) -{ - maxSliceLength = maximumSliceLength; -} - - -void Advance() -{ - // Move events from async queue into main queue - externalEventSection.Enter(); - while (tsFirst) - { - Event *next = tsFirst->next; - AddEventToQueue(tsFirst); - tsFirst = next; - } - externalEventSection.Leave(); - - int cyclesExecuted = slicelength - downcount; - - globalTimer += cyclesExecuted; - - while (first) - { - if (first->time <= globalTimer) - { -// LOG(GEKKO, "[Scheduler] %s (%lld, %lld) ", -// first->name ? first->name : "?", (u64)globalTimer, (u64)first->time); - event_types[first->type].callback(first->userdata, (int)(globalTimer - first->time)); - Event *next = first->next; - delete first; - first = next; - } - else - { - break; - } - } - if (!first) - { - LOG(GEKKO, "WARNING - no events in queue. Setting downcount to 10000"); - downcount += 10000; - } - else - { - slicelength = (int)(first->time - globalTimer); - if (slicelength > maxSliceLength) - slicelength = maxSliceLength; - downcount = slicelength; - } - if (advanceCallback) - advanceCallback(cyclesExecuted); -} - -void LogPendingEvents() -{ - Event *ptr = first; - while (ptr) - { - LOG(GEKKO, "PENDING: Now: %lld Pending: %lld Type: %d", globalTimer, ptr->time, ptr->type); - ptr = ptr->next; - } -} - -void Idle() -{ - LOGV(GEKKO, 3, "Idle"); - - idledCycles += downcount; - downcount = 0; - - Advance(); -} - -std::string GetScheduledEventsSummary() -{ - Event *ptr = first; - std::string text = "Scheduled events\n"; - text.reserve(1000); - while (ptr) - { - unsigned int t = ptr->type; - if (t < 0 || t >= event_types.size()) - PanicAlert("Invalid event type %i", t); - const char *name = event_types[ptr->type].name; - if (!name) - name = "[unknown]"; - text += StringFromFormat("%s : %i %08x%08x\n", event_types[ptr->type].name, ptr->time, ptr->userdata >> 32, ptr->userdata); - ptr = ptr->next; - } - return text; -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "Thread.h" +#include "PowerPC/PowerPC.h" +#include "CoreTiming.h" +#include "StringUtil.h" + +// TODO(ector): Replace new/delete in this file with a simple memory pool +// Don't expect a massive speedup though. + +namespace CoreTiming +{ + +struct EventType +{ + TimedCallback callback; + const char *name; +}; + +std::vector event_types; + +struct BaseEvent +{ + s64 time; + u64 userdata; + int type; +// Event *next; +}; + +typedef LinkedListItem Event; + +// STATE_TO_SAVE (how?) +Event *first; +Event *tsFirst; + +int downcount, slicelength; +int maxSliceLength = 20000; + +s64 globalTimer; +s64 idledCycles; + +Common::CriticalSection externalEventSection; + +void (*advanceCallback)(int cyclesExecuted); + +int RegisterEvent(const char *name, TimedCallback callback) +{ + EventType type; + type.name = name; + type.callback = callback; + event_types.push_back(type); + return (int)event_types.size() - 1; +} + +void UnregisterAllEvents() +{ + if (first) + PanicAlert("Cannot unregister events with events pending"); + event_types.clear(); +} + +void Init() +{ + downcount = maxSliceLength; + slicelength = maxSliceLength; + globalTimer = 0; + idledCycles = 0; +} + +void Shutdown() +{ + ClearPendingEvents(); + UnregisterAllEvents(); +} + +void DoState(PointerWrap &p) +{ + externalEventSection.Enter(); + p.Do(downcount); + p.Do(slicelength); + p.Do(globalTimer); + p.Do(idledCycles); + // OK, here we're gonna need to specialize depending on the mode. + // Should do something generic to serialize linked lists. + switch (p.GetMode()) { + case PointerWrap::MODE_READ: + { + ClearPendingEvents(); + if (first) + PanicAlert("Clear failed."); + int more_events = 0; + Event *prev = 0; + while (true) { + p.Do(more_events); + if (!more_events) + break; + Event *ev = new Event; + if (!prev) + first = ev; + else + prev->next = ev; + p.Do(ev->time); + p.Do(ev->type); + p.Do(ev->userdata); + ev->next = 0; + prev = ev; + ev = ev->next; + } + } + break; + case PointerWrap::MODE_MEASURE: + case PointerWrap::MODE_WRITE: + { + Event *ev = first; + int more_events = 1; + while (ev) { + p.Do(more_events); + p.Do(ev->time); + p.Do(ev->type); + p.Do(ev->userdata); + ev = ev->next; + } + more_events = 0; + p.Do(more_events); + break; + } + } + externalEventSection.Leave(); +} + +u64 GetTicks() +{ + return (u64)globalTimer; +} + +u64 GetIdleTicks() +{ + return (u64)idledCycles; +} + +// This is to be called when outside threads, such as the graphics thread, wants to +// schedule things to be executed on the main thread. +void ScheduleEvent_Threadsafe(int cyclesIntoFuture, int event_type, u64 userdata) +{ + externalEventSection.Enter(); + Event *ne = new Event; + ne->time = globalTimer + cyclesIntoFuture; + ne->type = event_type; + ne->next = tsFirst; + ne->userdata = userdata; + tsFirst = ne; + externalEventSection.Leave(); +} + +void ClearPendingEvents() +{ + while (first) + { + Event *e = first->next; + delete first; + first = e; + } +} + +void AddEventToQueue(Event *ne) +{ + // Damn, this logic got complicated. Must be an easier way. + if (!first) + { + first = ne; + ne->next = 0; + } + else + { + Event *ptr = first; + Event *prev = 0; + if (ptr->time > ne->time) + { + ne->next = first; + first = ne; + } + else + { + prev = first; + ptr = first->next; + + while (ptr) + { + if (ptr->time <= ne->time) + { + prev = ptr; + ptr = ptr->next; + } + else + break; + } + + //OK, ptr points to the item AFTER our new item. Let's insert + ne->next = prev->next; + prev->next = ne; + // Done! + } + } +} + +// This must be run ONLY from within the cpu thread +// cyclesIntoFuture may be VERY inaccurate if called from anything else +// than Advance +void ScheduleEvent(int cyclesIntoFuture, int event_type, u64 userdata) +{ + Event *ne = new Event; + ne->userdata = userdata; + ne->type = event_type; + ne->time = globalTimer + cyclesIntoFuture; + + AddEventToQueue(ne); +} + +void RegisterAdvanceCallback(void (*callback)(int cyclesExecuted)) +{ + advanceCallback = callback; +} + +bool IsScheduled(int event_type) +{ + if (!first) + return false; + Event *e = first; + while (e) { + if (e->type == event_type) + return true; + e = e->next; + } + return false; +} + +void RemoveEvent(int event_type) +{ + if (!first) + return; + if (first->type == event_type) + { + Event *next = first->next; + delete first; + first = next; + } + if (!first) + return; + Event *prev = first; + Event *ptr = prev->next; + while (ptr) + { + if (ptr->type == event_type) + { + prev->next = ptr->next; + delete ptr; + ptr = prev->next; + } + else + { + prev = ptr; + ptr = ptr->next; + } + } +} + +void SetMaximumSlice(int maximumSliceLength) +{ + maxSliceLength = maximumSliceLength; +} + + +void Advance() +{ + // Move events from async queue into main queue + externalEventSection.Enter(); + while (tsFirst) + { + Event *next = tsFirst->next; + AddEventToQueue(tsFirst); + tsFirst = next; + } + externalEventSection.Leave(); + + int cyclesExecuted = slicelength - downcount; + + globalTimer += cyclesExecuted; + + while (first) + { + if (first->time <= globalTimer) + { +// LOG(GEKKO, "[Scheduler] %s (%lld, %lld) ", +// first->name ? first->name : "?", (u64)globalTimer, (u64)first->time); + event_types[first->type].callback(first->userdata, (int)(globalTimer - first->time)); + Event *next = first->next; + delete first; + first = next; + } + else + { + break; + } + } + if (!first) + { + LOG(GEKKO, "WARNING - no events in queue. Setting downcount to 10000"); + downcount += 10000; + } + else + { + slicelength = (int)(first->time - globalTimer); + if (slicelength > maxSliceLength) + slicelength = maxSliceLength; + downcount = slicelength; + } + if (advanceCallback) + advanceCallback(cyclesExecuted); +} + +void LogPendingEvents() +{ + Event *ptr = first; + while (ptr) + { + LOG(GEKKO, "PENDING: Now: %lld Pending: %lld Type: %d", globalTimer, ptr->time, ptr->type); + ptr = ptr->next; + } +} + +void Idle() +{ + LOGV(GEKKO, 3, "Idle"); + + idledCycles += downcount; + downcount = 0; + + Advance(); +} + +std::string GetScheduledEventsSummary() +{ + Event *ptr = first; + std::string text = "Scheduled events\n"; + text.reserve(1000); + while (ptr) + { + unsigned int t = ptr->type; + if (t < 0 || t >= event_types.size()) + PanicAlert("Invalid event type %i", t); + const char *name = event_types[ptr->type].name; + if (!name) + name = "[unknown]"; + text += StringFromFormat("%s : %i %08x%08x\n", event_types[ptr->type].name, ptr->time, ptr->userdata >> 32, ptr->userdata); + ptr = ptr->next; + } + return text; +} + +} // namespace diff --git a/Source/Core/Core/Src/Debugger/Debugger_BreakPoints.cpp b/Source/Core/Core/Src/Debugger/Debugger_BreakPoints.cpp index 28595c0a6d..bdcc7ccb82 100644 --- a/Source/Core/Core/Src/Debugger/Debugger_BreakPoints.cpp +++ b/Source/Core/Core/Src/Debugger/Debugger_BreakPoints.cpp @@ -1,195 +1,195 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -// Lame slow breakpoint system -// TODO: a real one - -// -// [F|RES]: this class isn't really nice... for a better management we should use a base class for -// breakpoints and memory checks. but probably this will be slower too -// - - -#include "Common.h" -#include "../HW/CPU.h" -#include "../Host.h" -#include "../PowerPC/SymbolDB.h" -#include "Debugger_BreakPoints.h" - -CBreakPoints::TBreakPoints CBreakPoints::m_BreakPoints; -CBreakPoints::TMemChecks CBreakPoints::m_MemChecks; -u32 CBreakPoints::m_iBreakOnCount = 0; - -TMemCheck::TMemCheck() -{ - numHits = 0; -} - -void TMemCheck::Action(u32 iValue, u32 addr, bool write, int size, u32 pc) -{ - if ((write && OnWrite) || (!write && OnRead)) - { - if (Log) - { - LOG(MEMMAP,"CHK %08x %s%i at %08x (%s)", - iValue, write ? "Write" : "Read", // read or write - size*8, addr, // address - g_symbolDB.GetDescription(addr) // symbol map description - ); - } - if (Break) - CCPU::Break(); - } -} - -bool CBreakPoints::IsAddressBreakPoint(u32 _iAddress) -{ - std::vector::iterator iter; - - for (iter = m_BreakPoints.begin(); iter != m_BreakPoints.end(); ++iter) - if ((*iter).iAddress == _iAddress) - return true; - - return false; -} - -bool CBreakPoints::IsTempBreakPoint(u32 _iAddress) -{ - std::vector::iterator iter; - - for (iter = m_BreakPoints.begin(); iter != m_BreakPoints.end(); ++iter) - if ((*iter).iAddress == _iAddress && (*iter).bTemporary) - return true; - - return false; -} - -TMemCheck *CBreakPoints::GetMemCheck(u32 address) -{ - std::vector::iterator iter; - for (iter = m_MemChecks.begin(); iter != m_MemChecks.end(); ++iter) - { - if ((*iter).bRange) - { - if (address >= (*iter).StartAddress && address <= (*iter).EndAddress) - return &(*iter); - } - else - { - if ((*iter).StartAddress==address) - return &(*iter); - } - } - - //none found - return 0; -} - -void CBreakPoints::AddBreakPoint(u32 _iAddress, bool temp) -{ - if (!IsAddressBreakPoint(_iAddress)) // only add new addresses - { - TBreakPoint pt; // breakpoint settings - pt.bOn = true; - pt.bTemporary = temp; - pt.iAddress = _iAddress; - - m_BreakPoints.push_back(pt); - } -} - -void CBreakPoints::RemoveBreakPoint(u32 _iAddress) -{ - std::vector::iterator iter; - - for (iter = m_BreakPoints.begin(); iter != m_BreakPoints.end(); ++iter) - { - if ((*iter).iAddress == _iAddress) - { - m_BreakPoints.erase(iter); - break; - } - } - - Host_UpdateBreakPointView(); -} - -void CBreakPoints::ClearAllBreakPoints() -{ - m_BreakPoints.clear(); - m_MemChecks.clear(); - Host_UpdateBreakPointView(); -} - -// update breakpoint window -void CBreakPoints::UpdateBreakPointView() -{ - Host_UpdateBreakPointView(); -} - -void CBreakPoints::AddMemoryCheck(const TMemCheck& _rMemoryCheck) -{ - m_MemChecks.push_back(_rMemoryCheck); -} - -void CBreakPoints::AddAutoBreakpoints() -{ -#if defined(_DEBUG) || defined(DEBUGFAST) -#if 1 - const char *bps[] = { - "PPCHalt", - }; - - for (int i = 0; i < sizeof(bps) / sizeof(const char *); i++) - { - Symbol *symbol = g_symbolDB.GetSymbolFromName(bps[i]); - if (symbol) - AddBreakPoint(symbol->address, false); - } - Host_UpdateBreakPointView(); -#endif -#endif -} - -void CBreakPoints::DeleteElementByAddress(u32 _Address) -{ - // first check breakpoints - { - std::vector::iterator iter; - for (iter = m_BreakPoints.begin(); iter != m_BreakPoints.end(); ++iter) - { - if ((*iter).iAddress == _Address) - { - m_BreakPoints.erase(iter); - Host_UpdateBreakPointView(); - return; - } - } - } - - // second memory check checkpoint - std::vector::iterator iter; - for (iter = m_MemChecks.begin(); iter != m_MemChecks.end(); ++iter) - { - if ((*iter).StartAddress == _Address) - { - m_MemChecks.erase(iter); - Host_UpdateBreakPointView(); - return; - } - } -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// Lame slow breakpoint system +// TODO: a real one + +// +// [F|RES]: this class isn't really nice... for a better management we should use a base class for +// breakpoints and memory checks. but probably this will be slower too +// + + +#include "Common.h" +#include "../HW/CPU.h" +#include "../Host.h" +#include "../PowerPC/SymbolDB.h" +#include "Debugger_BreakPoints.h" + +CBreakPoints::TBreakPoints CBreakPoints::m_BreakPoints; +CBreakPoints::TMemChecks CBreakPoints::m_MemChecks; +u32 CBreakPoints::m_iBreakOnCount = 0; + +TMemCheck::TMemCheck() +{ + numHits = 0; +} + +void TMemCheck::Action(u32 iValue, u32 addr, bool write, int size, u32 pc) +{ + if ((write && OnWrite) || (!write && OnRead)) + { + if (Log) + { + LOG(MEMMAP,"CHK %08x %s%i at %08x (%s)", + iValue, write ? "Write" : "Read", // read or write + size*8, addr, // address + g_symbolDB.GetDescription(addr) // symbol map description + ); + } + if (Break) + CCPU::Break(); + } +} + +bool CBreakPoints::IsAddressBreakPoint(u32 _iAddress) +{ + std::vector::iterator iter; + + for (iter = m_BreakPoints.begin(); iter != m_BreakPoints.end(); ++iter) + if ((*iter).iAddress == _iAddress) + return true; + + return false; +} + +bool CBreakPoints::IsTempBreakPoint(u32 _iAddress) +{ + std::vector::iterator iter; + + for (iter = m_BreakPoints.begin(); iter != m_BreakPoints.end(); ++iter) + if ((*iter).iAddress == _iAddress && (*iter).bTemporary) + return true; + + return false; +} + +TMemCheck *CBreakPoints::GetMemCheck(u32 address) +{ + std::vector::iterator iter; + for (iter = m_MemChecks.begin(); iter != m_MemChecks.end(); ++iter) + { + if ((*iter).bRange) + { + if (address >= (*iter).StartAddress && address <= (*iter).EndAddress) + return &(*iter); + } + else + { + if ((*iter).StartAddress==address) + return &(*iter); + } + } + + //none found + return 0; +} + +void CBreakPoints::AddBreakPoint(u32 _iAddress, bool temp) +{ + if (!IsAddressBreakPoint(_iAddress)) // only add new addresses + { + TBreakPoint pt; // breakpoint settings + pt.bOn = true; + pt.bTemporary = temp; + pt.iAddress = _iAddress; + + m_BreakPoints.push_back(pt); + } +} + +void CBreakPoints::RemoveBreakPoint(u32 _iAddress) +{ + std::vector::iterator iter; + + for (iter = m_BreakPoints.begin(); iter != m_BreakPoints.end(); ++iter) + { + if ((*iter).iAddress == _iAddress) + { + m_BreakPoints.erase(iter); + break; + } + } + + Host_UpdateBreakPointView(); +} + +void CBreakPoints::ClearAllBreakPoints() +{ + m_BreakPoints.clear(); + m_MemChecks.clear(); + Host_UpdateBreakPointView(); +} + +// update breakpoint window +void CBreakPoints::UpdateBreakPointView() +{ + Host_UpdateBreakPointView(); +} + +void CBreakPoints::AddMemoryCheck(const TMemCheck& _rMemoryCheck) +{ + m_MemChecks.push_back(_rMemoryCheck); +} + +void CBreakPoints::AddAutoBreakpoints() +{ +#if defined(_DEBUG) || defined(DEBUGFAST) +#if 1 + const char *bps[] = { + "PPCHalt", + }; + + for (int i = 0; i < sizeof(bps) / sizeof(const char *); i++) + { + Symbol *symbol = g_symbolDB.GetSymbolFromName(bps[i]); + if (symbol) + AddBreakPoint(symbol->address, false); + } + Host_UpdateBreakPointView(); +#endif +#endif +} + +void CBreakPoints::DeleteElementByAddress(u32 _Address) +{ + // first check breakpoints + { + std::vector::iterator iter; + for (iter = m_BreakPoints.begin(); iter != m_BreakPoints.end(); ++iter) + { + if ((*iter).iAddress == _Address) + { + m_BreakPoints.erase(iter); + Host_UpdateBreakPointView(); + return; + } + } + } + + // second memory check checkpoint + std::vector::iterator iter; + for (iter = m_MemChecks.begin(); iter != m_MemChecks.end(); ++iter) + { + if ((*iter).StartAddress == _Address) + { + m_MemChecks.erase(iter); + Host_UpdateBreakPointView(); + return; + } + } +} diff --git a/Source/Core/Core/Src/Debugger/Debugger_SymbolMap.cpp b/Source/Core/Core/Src/Debugger/Debugger_SymbolMap.cpp index abfc9aa427..85d265cec2 100644 --- a/Source/Core/Core/Src/Debugger/Debugger_SymbolMap.cpp +++ b/Source/Core/Core/Src/Debugger/Debugger_SymbolMap.cpp @@ -1,157 +1,157 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "StringUtil.h" -#include "Debugger_SymbolMap.h" -#include "../Core.h" -#include "../HW/Memmap.h" -#include "../PowerPC/PowerPC.h" -#include "../PowerPC/PPCAnalyst.h" -#include "../PowerPC/SymbolDB.h" -#include "../../../../Externals/Bochs_disasm/PowerPCDisasm.h" - -namespace Debugger -{ - -bool GetCallstack(std::vector &output) -{ - if (Core::GetState() == Core::CORE_UNINITIALIZED) - return false; - - if (!Memory::IsRAMAddress(PowerPC::ppcState.gpr[1])) - return false; - - u32 addr = Memory::ReadUnchecked_U32(PowerPC::ppcState.gpr[1]); // SP - if (LR == 0) { - CallstackEntry entry; - entry.Name = "(error: LR=0)"; - entry.vAddress = 0x0; - output.push_back(entry); - return false; - } - int count = 1; - if (g_symbolDB.GetDescription(PowerPC::ppcState.pc) != g_symbolDB.GetDescription(LR)) - { - CallstackEntry entry; - entry.Name = StringFromFormat(" * %s [ LR = %08x ]\n", g_symbolDB.GetDescription(LR), LR); - entry.vAddress = 0x0; - count++; - } - - //walk the stack chain - while ((addr != 0xFFFFFFFF) && (addr != 0) && (count++ < 20) && (PowerPC::ppcState.gpr[1] != 0)) - { - if (!Memory::IsRAMAddress(addr + 4)) - return false; - - u32 func = Memory::ReadUnchecked_U32(addr + 4); - const char *str = g_symbolDB.GetDescription(func); - if (!str || strlen(str) == 0 || !strcmp(str, "Invalid")) - str = "(unknown)"; - - CallstackEntry entry; - entry.Name = StringFromFormat(" * %s [ addr = %08x ]\n", str, func); - entry.vAddress = func; - output.push_back(entry); - - if (!Memory::IsRAMAddress(addr)) - return false; - - addr = Memory::ReadUnchecked_U32(addr); - } - - return true; -} - -void PrintCallstack() -{ - u32 addr = Memory::ReadUnchecked_U32(PowerPC::ppcState.gpr[1]); // SP - - printf("\n == STACK TRACE - SP = %08x ==\n", PowerPC::ppcState.gpr[1]); - - if (LR == 0) { - printf(" LR = 0 - this is bad\n"); - } - int count = 1; - if (g_symbolDB.GetDescription(PowerPC::ppcState.pc) != g_symbolDB.GetDescription(LR)) - { - printf(" * %s [ LR = %08x ]\n", g_symbolDB.GetDescription(LR), LR); - count++; - } - - //walk the stack chain - while ((addr != 0xFFFFFFFF) && (addr != 0) && (count++ < 20) && (PowerPC::ppcState.gpr[1] != 0)) - { - u32 func = Memory::ReadUnchecked_U32(addr + 4); - const char *str = g_symbolDB.GetDescription(func); - if (!str || strlen(str) == 0 || !strcmp(str, "Invalid")) - str = "(unknown)"; - printf( " * %s [ addr = %08x ]\n", str, func); - addr = Memory::ReadUnchecked_U32(addr); - } -} - -void PrintCallstack(LogTypes::LOG_TYPE _Log) -{ - u32 addr = Memory::ReadUnchecked_U32(PowerPC::ppcState.gpr[1]); // SP - - __Logv(_Log, 1, "\n == STACK TRACE - SP = %08x ==\n", PowerPC::ppcState.gpr[1]); - - if (LR == 0) { - __Logv(_Log, 1, " LR = 0 - this is bad\n"); - } - int count = 1; - if (g_symbolDB.GetDescription(PowerPC::ppcState.pc) != g_symbolDB.GetDescription(LR)) - { - __Log(_Log, " * %s [ LR = %08x ]\n", g_symbolDB.GetDescription(LR), LR); - count++; - } - - //walk the stack chain - while ((addr != 0xFFFFFFFF) && (addr != 0) && (count++ < 20) && (PowerPC::ppcState.gpr[1] != 0)) - { - u32 func = Memory::ReadUnchecked_U32(addr + 4); - const char *str = g_symbolDB.GetDescription(func); - if (!str || strlen(str) == 0 || !strcmp(str, "Invalid")) - str = "(unknown)"; - __Logv(_Log, 3, " * %s [ addr = %08x ]\n", str, func); - addr = Memory::ReadUnchecked_U32(addr); - } -} - -void PrintDataBuffer(LogTypes::LOG_TYPE _Log, u8* _pData, size_t _Size, const char* _title) -{ - __Log(_Log, _title); - for (u32 j=0; j<_Size;) - { - std::string Temp; - for (int i=0; i<16; i++) - { - char Buffer[128]; - sprintf(Buffer, "%02x ", _pData[j++]); - Temp.append(Buffer); - - if (j >= _Size) - break; - } - - __Log(_Log, " Data: %s", Temp.c_str()); - } -} - -} // end of namespace Debugger +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "StringUtil.h" +#include "Debugger_SymbolMap.h" +#include "../Core.h" +#include "../HW/Memmap.h" +#include "../PowerPC/PowerPC.h" +#include "../PowerPC/PPCAnalyst.h" +#include "../PowerPC/SymbolDB.h" +#include "../../../../Externals/Bochs_disasm/PowerPCDisasm.h" + +namespace Debugger +{ + +bool GetCallstack(std::vector &output) +{ + if (Core::GetState() == Core::CORE_UNINITIALIZED) + return false; + + if (!Memory::IsRAMAddress(PowerPC::ppcState.gpr[1])) + return false; + + u32 addr = Memory::ReadUnchecked_U32(PowerPC::ppcState.gpr[1]); // SP + if (LR == 0) { + CallstackEntry entry; + entry.Name = "(error: LR=0)"; + entry.vAddress = 0x0; + output.push_back(entry); + return false; + } + int count = 1; + if (g_symbolDB.GetDescription(PowerPC::ppcState.pc) != g_symbolDB.GetDescription(LR)) + { + CallstackEntry entry; + entry.Name = StringFromFormat(" * %s [ LR = %08x ]\n", g_symbolDB.GetDescription(LR), LR); + entry.vAddress = 0x0; + count++; + } + + //walk the stack chain + while ((addr != 0xFFFFFFFF) && (addr != 0) && (count++ < 20) && (PowerPC::ppcState.gpr[1] != 0)) + { + if (!Memory::IsRAMAddress(addr + 4)) + return false; + + u32 func = Memory::ReadUnchecked_U32(addr + 4); + const char *str = g_symbolDB.GetDescription(func); + if (!str || strlen(str) == 0 || !strcmp(str, "Invalid")) + str = "(unknown)"; + + CallstackEntry entry; + entry.Name = StringFromFormat(" * %s [ addr = %08x ]\n", str, func); + entry.vAddress = func; + output.push_back(entry); + + if (!Memory::IsRAMAddress(addr)) + return false; + + addr = Memory::ReadUnchecked_U32(addr); + } + + return true; +} + +void PrintCallstack() +{ + u32 addr = Memory::ReadUnchecked_U32(PowerPC::ppcState.gpr[1]); // SP + + printf("\n == STACK TRACE - SP = %08x ==\n", PowerPC::ppcState.gpr[1]); + + if (LR == 0) { + printf(" LR = 0 - this is bad\n"); + } + int count = 1; + if (g_symbolDB.GetDescription(PowerPC::ppcState.pc) != g_symbolDB.GetDescription(LR)) + { + printf(" * %s [ LR = %08x ]\n", g_symbolDB.GetDescription(LR), LR); + count++; + } + + //walk the stack chain + while ((addr != 0xFFFFFFFF) && (addr != 0) && (count++ < 20) && (PowerPC::ppcState.gpr[1] != 0)) + { + u32 func = Memory::ReadUnchecked_U32(addr + 4); + const char *str = g_symbolDB.GetDescription(func); + if (!str || strlen(str) == 0 || !strcmp(str, "Invalid")) + str = "(unknown)"; + printf( " * %s [ addr = %08x ]\n", str, func); + addr = Memory::ReadUnchecked_U32(addr); + } +} + +void PrintCallstack(LogTypes::LOG_TYPE _Log) +{ + u32 addr = Memory::ReadUnchecked_U32(PowerPC::ppcState.gpr[1]); // SP + + __Logv(_Log, 1, "\n == STACK TRACE - SP = %08x ==\n", PowerPC::ppcState.gpr[1]); + + if (LR == 0) { + __Logv(_Log, 1, " LR = 0 - this is bad\n"); + } + int count = 1; + if (g_symbolDB.GetDescription(PowerPC::ppcState.pc) != g_symbolDB.GetDescription(LR)) + { + __Log(_Log, " * %s [ LR = %08x ]\n", g_symbolDB.GetDescription(LR), LR); + count++; + } + + //walk the stack chain + while ((addr != 0xFFFFFFFF) && (addr != 0) && (count++ < 20) && (PowerPC::ppcState.gpr[1] != 0)) + { + u32 func = Memory::ReadUnchecked_U32(addr + 4); + const char *str = g_symbolDB.GetDescription(func); + if (!str || strlen(str) == 0 || !strcmp(str, "Invalid")) + str = "(unknown)"; + __Logv(_Log, 3, " * %s [ addr = %08x ]\n", str, func); + addr = Memory::ReadUnchecked_U32(addr); + } +} + +void PrintDataBuffer(LogTypes::LOG_TYPE _Log, u8* _pData, size_t _Size, const char* _title) +{ + __Log(_Log, _title); + for (u32 j=0; j<_Size;) + { + std::string Temp; + for (int i=0; i<16; i++) + { + char Buffer[128]; + sprintf(Buffer, "%02x ", _pData[j++]); + Temp.append(Buffer); + + if (j >= _Size) + break; + } + + __Log(_Log, " Data: %s", Temp.c_str()); + } +} + +} // end of namespace Debugger diff --git a/Source/Core/Core/Src/Debugger/Dump.cpp b/Source/Core/Core/Src/Debugger/Dump.cpp index a333aa85f9..93c95fb0a7 100644 --- a/Source/Core/Core/Src/Debugger/Dump.cpp +++ b/Source/Core/Core/Src/Debugger/Dump.cpp @@ -1,87 +1,87 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ -#include - -#include "Common.h" -#include "Dump.h" - -CDump::CDump(const char* _szFilename) : - m_pData(NULL), - m_bInit(false) -{ - FILE* pStream = fopen(_szFilename, "rb"); - if (pStream != NULL) - { - fseek(pStream, 0, SEEK_END); - m_size = ftell(pStream); - fseek(pStream, 0, SEEK_SET); - - m_pData = new u8[m_size]; - - fread(m_pData, m_size, 1, pStream); - - fclose(pStream); - } -} - -CDump::~CDump(void) -{ - if (m_pData != NULL) - { - delete [] m_pData; - m_pData = NULL; - } -} - -int -CDump::GetNumberOfSteps(void) -{ - return (int)(m_size / STRUCTUR_SIZE); -} - -u32 -CDump::GetGPR(int _step, int _gpr) -{ - u32 offset = _step * STRUCTUR_SIZE; - - if (offset >= m_size) - return -1; - - return Read32(offset + OFFSET_GPR + (_gpr * 4)); -} - -u32 -CDump::GetPC(int _step) -{ - u32 offset = _step * STRUCTUR_SIZE; - - if (offset >= m_size) - return -1; - - return Read32(offset + OFFSET_PC); -} - -u32 -CDump::Read32(u32 _pos) -{ - u32 result = (m_pData[_pos+0] << 24) | - (m_pData[_pos+1] << 16) | - (m_pData[_pos+2] << 8) | - (m_pData[_pos+3] << 0); - - return result; -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +#include + +#include "Common.h" +#include "Dump.h" + +CDump::CDump(const char* _szFilename) : + m_pData(NULL), + m_bInit(false) +{ + FILE* pStream = fopen(_szFilename, "rb"); + if (pStream != NULL) + { + fseek(pStream, 0, SEEK_END); + m_size = ftell(pStream); + fseek(pStream, 0, SEEK_SET); + + m_pData = new u8[m_size]; + + fread(m_pData, m_size, 1, pStream); + + fclose(pStream); + } +} + +CDump::~CDump(void) +{ + if (m_pData != NULL) + { + delete [] m_pData; + m_pData = NULL; + } +} + +int +CDump::GetNumberOfSteps(void) +{ + return (int)(m_size / STRUCTUR_SIZE); +} + +u32 +CDump::GetGPR(int _step, int _gpr) +{ + u32 offset = _step * STRUCTUR_SIZE; + + if (offset >= m_size) + return -1; + + return Read32(offset + OFFSET_GPR + (_gpr * 4)); +} + +u32 +CDump::GetPC(int _step) +{ + u32 offset = _step * STRUCTUR_SIZE; + + if (offset >= m_size) + return -1; + + return Read32(offset + OFFSET_PC); +} + +u32 +CDump::Read32(u32 _pos) +{ + u32 result = (m_pData[_pos+0] << 24) | + (m_pData[_pos+1] << 16) | + (m_pData[_pos+2] << 8) | + (m_pData[_pos+3] << 0); + + return result; +} diff --git a/Source/Core/Core/Src/Debugger/PPCDebugInterface.cpp b/Source/Core/Core/Src/Debugger/PPCDebugInterface.cpp index 6cf594463f..dd83e6ef29 100644 --- a/Source/Core/Core/Src/Debugger/PPCDebugInterface.cpp +++ b/Source/Core/Core/Src/Debugger/PPCDebugInterface.cpp @@ -1,150 +1,150 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Debugger_BreakPoints.h" -#include "Debugger_SymbolMap.h" -#include "DebugInterface.h" -#include "PPCDebugInterface.h" -#include "PowerPCDisasm.h" -#include "../Core.h" -#include "../HW/Memmap.h" -#include "../PowerPC/PowerPC.h" -#include "../PowerPC/SymbolDB.h" - -// Not thread safe. -const char *PPCDebugInterface::disasm(unsigned int address) -{ - if (Core::GetState() != Core::CORE_UNINITIALIZED) - { - if (Memory::IsRAMAddress(address)) - { - u32 op = Memory::Read_Instruction(address); - return DisassembleGekko(op, address); - } - return "No RAM here - invalid"; - } - - static const char tmp[] = ""; - return tmp; -} - -const char *PPCDebugInterface::getRawMemoryString(unsigned int address) -{ - if (Core::GetState() != Core::CORE_UNINITIALIZED) - { - if (address < 0xE0000000) - { - static char str[256] ={0}; - if (sprintf(str,"%08X",readMemory(address))!=8) { - PanicAlert("getRawMemoryString -> WTF! ( as read somewhere;) )"); - return ":("; - } - return str; - } - return "No RAM"; - } - static const char tmp[] = ""; - return tmp; -} - -unsigned int PPCDebugInterface::readMemory(unsigned int address) -{ - return Memory::ReadUnchecked_U32(address); -} - -unsigned int PPCDebugInterface::readInstruction(unsigned int address) -{ - return Memory::Read_Instruction(address); -} - -bool PPCDebugInterface::isAlive() -{ - return Core::GetState() != Core::CORE_UNINITIALIZED; -} - -bool PPCDebugInterface::isBreakpoint(unsigned int address) -{ - return CBreakPoints::IsAddressBreakPoint(address); -} - -void PPCDebugInterface::setBreakpoint(unsigned int address) -{ - CBreakPoints::AddBreakPoint(address); -} - -void PPCDebugInterface::clearBreakpoint(unsigned int address) -{ - CBreakPoints::RemoveBreakPoint(address); -} - -void PPCDebugInterface::clearAllBreakpoints() {} - -void PPCDebugInterface::toggleBreakpoint(unsigned int address) -{ - CBreakPoints::IsAddressBreakPoint(address) ? CBreakPoints::RemoveBreakPoint(address) : CBreakPoints::AddBreakPoint(address); -} - -void PPCDebugInterface::insertBLR(unsigned int address) -{ - Memory::Write_U32(0x4e800020, address); -} - - -// ======================================================= -// Separate the blocks with colors. -// ------------- -int PPCDebugInterface::getColor(unsigned int address) -{ - if (!Memory::IsRAMAddress(address)) - return 0xeeeeee; - int colors[6] = - { - 0xd0FFFF // light cyan - ,0xFFd0d0 // light red - ,0xd8d8FF // light blue - ,0xFFd0FF // light purple - ,0xd0FFd0 // light green - ,0xFFFFd0 // light yellow - }; - Symbol *symbol = g_symbolDB.GetSymbolFromAddr(address); - if (!symbol) return 0xFFFFFF; - if (symbol->type != Symbol::SYMBOL_FUNCTION) - return 0xEEEEFF; - return colors[symbol->index % 6]; -} -// ============= - - -std::string PPCDebugInterface::getDescription(unsigned int address) -{ - return g_symbolDB.GetDescription(address); -} - -unsigned int PPCDebugInterface::getPC() -{ - return PowerPC::ppcState.pc; -} - -void PPCDebugInterface::setPC(unsigned int address) -{ - PowerPC::ppcState.pc = address; -} - -void PPCDebugInterface::runToBreakpoint() -{ - -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Debugger_BreakPoints.h" +#include "Debugger_SymbolMap.h" +#include "DebugInterface.h" +#include "PPCDebugInterface.h" +#include "PowerPCDisasm.h" +#include "../Core.h" +#include "../HW/Memmap.h" +#include "../PowerPC/PowerPC.h" +#include "../PowerPC/SymbolDB.h" + +// Not thread safe. +const char *PPCDebugInterface::disasm(unsigned int address) +{ + if (Core::GetState() != Core::CORE_UNINITIALIZED) + { + if (Memory::IsRAMAddress(address)) + { + u32 op = Memory::Read_Instruction(address); + return DisassembleGekko(op, address); + } + return "No RAM here - invalid"; + } + + static const char tmp[] = ""; + return tmp; +} + +const char *PPCDebugInterface::getRawMemoryString(unsigned int address) +{ + if (Core::GetState() != Core::CORE_UNINITIALIZED) + { + if (address < 0xE0000000) + { + static char str[256] ={0}; + if (sprintf(str,"%08X",readMemory(address))!=8) { + PanicAlert("getRawMemoryString -> WTF! ( as read somewhere;) )"); + return ":("; + } + return str; + } + return "No RAM"; + } + static const char tmp[] = ""; + return tmp; +} + +unsigned int PPCDebugInterface::readMemory(unsigned int address) +{ + return Memory::ReadUnchecked_U32(address); +} + +unsigned int PPCDebugInterface::readInstruction(unsigned int address) +{ + return Memory::Read_Instruction(address); +} + +bool PPCDebugInterface::isAlive() +{ + return Core::GetState() != Core::CORE_UNINITIALIZED; +} + +bool PPCDebugInterface::isBreakpoint(unsigned int address) +{ + return CBreakPoints::IsAddressBreakPoint(address); +} + +void PPCDebugInterface::setBreakpoint(unsigned int address) +{ + CBreakPoints::AddBreakPoint(address); +} + +void PPCDebugInterface::clearBreakpoint(unsigned int address) +{ + CBreakPoints::RemoveBreakPoint(address); +} + +void PPCDebugInterface::clearAllBreakpoints() {} + +void PPCDebugInterface::toggleBreakpoint(unsigned int address) +{ + CBreakPoints::IsAddressBreakPoint(address) ? CBreakPoints::RemoveBreakPoint(address) : CBreakPoints::AddBreakPoint(address); +} + +void PPCDebugInterface::insertBLR(unsigned int address) +{ + Memory::Write_U32(0x4e800020, address); +} + + +// ======================================================= +// Separate the blocks with colors. +// ------------- +int PPCDebugInterface::getColor(unsigned int address) +{ + if (!Memory::IsRAMAddress(address)) + return 0xeeeeee; + int colors[6] = + { + 0xd0FFFF // light cyan + ,0xFFd0d0 // light red + ,0xd8d8FF // light blue + ,0xFFd0FF // light purple + ,0xd0FFd0 // light green + ,0xFFFFd0 // light yellow + }; + Symbol *symbol = g_symbolDB.GetSymbolFromAddr(address); + if (!symbol) return 0xFFFFFF; + if (symbol->type != Symbol::SYMBOL_FUNCTION) + return 0xEEEEFF; + return colors[symbol->index % 6]; +} +// ============= + + +std::string PPCDebugInterface::getDescription(unsigned int address) +{ + return g_symbolDB.GetDescription(address); +} + +unsigned int PPCDebugInterface::getPC() +{ + return PowerPC::ppcState.pc; +} + +void PPCDebugInterface::setPC(unsigned int address) +{ + PowerPC::ppcState.pc = address; +} + +void PPCDebugInterface::runToBreakpoint() +{ + +} diff --git a/Source/Core/Core/Src/HLE/HLE.cpp b/Source/Core/Core/Src/HLE/HLE.cpp index c186a55e4d..ca3e730104 100644 --- a/Source/Core/Core/Src/HLE/HLE.cpp +++ b/Source/Core/Core/Src/HLE/HLE.cpp @@ -1,141 +1,141 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "HLE.h" - -#include "../PowerPC/PowerPC.h" -#include "../PowerPC/SymbolDB.h" -#include "../HW/Memmap.h" -#include "../Debugger/Debugger_SymbolMap.h" -#include "../Debugger/Debugger_BreakPoints.h" - -#include "HLE_OS.h" -#include "HLE_Misc.h" - -namespace HLE -{ - -using namespace PowerPC; - -typedef void (*TPatchFunction)(); - -enum -{ - HLE_RETURNTYPE_BLR = 0, - HLE_RETURNTYPE_RFI = 1, -}; - -struct SPatch -{ - char m_szPatchName[128]; - TPatchFunction PatchFunction; - int returnType; -}; - -static const SPatch OSPatches[] = -{ - { "FAKE_TO_SKIP_0", HLE_Misc::UnimplementedFunction }, - - // speedup - { "OSProtectRange", HLE_Misc::UnimplementedFunctionFalse }, - // { "THPPlayerGetState", HLE_Misc:THPPlayerGetState }, - - - // debug out is very nice ;) - { "OSReport", HLE_OS::HLE_OSReport }, - { "OSPanic", HLE_OS::HLE_OSPanic }, - { "vprintf", HLE_OS::HLE_vprintf }, - { "printf", HLE_OS::HLE_printf }, - { "puts", HLE_OS::HLE_printf }, //gcc-optimized printf? - - // wii only - { "__OSInitAudioSystem", HLE_Misc::UnimplementedFunction }, - - // Super Monkey Ball - { ".evil_vec_cosine", HLE_Misc::SMB_EvilVecCosine }, - { ".evil_normalize", HLE_Misc::SMB_EvilNormalize }, - { ".evil_vec_setlength", HLE_Misc::SMB_evil_vec_setlength }, - { "PanicAlert", HLE_Misc::PanicAlert }, - { ".sqrt_internal_needs_cr1", HLE_Misc::SMB_sqrt_internal }, - { ".rsqrt_internal_needs_cr1", HLE_Misc::SMB_rsqrt_internal }, - { ".atan2", HLE_Misc::SMB_atan2}, - - // special - // { "GXPeekZ", HLE_Misc::GXPeekZ}, - // { "GXPeekARGB", HLE_Misc::GXPeekARGB}, -}; - -static const SPatch OSBreakPoints[] = -{ - { "FAKE_TO_SKIP_0", HLE_Misc::UnimplementedFunction }, -}; - -void Patch(u32 address, const char *hle_func_name) -{ - for (u32 i = 0; i < sizeof(OSPatches) / sizeof(SPatch); i++) - { - if (!strcmp(OSPatches[i].m_szPatchName, hle_func_name)) { - u32 HLEPatchValue = (1 & 0x3f) << 26; - Memory::Write_U32(HLEPatchValue | i, address); - return; - } - } -} - -void PatchFunctions() -{ - for (u32 i = 0; i < sizeof(OSPatches) / sizeof(SPatch); i++) - { - Symbol *symbol = g_symbolDB.GetSymbolFromName(OSPatches[i].m_szPatchName); - if (symbol > 0) - { - u32 HLEPatchValue = (1 & 0x3f) << 26; - for (u32 addr = symbol->address; addr < symbol->address + symbol->size; addr += 4) - Memory::Write_U32(HLEPatchValue | i, addr); - LOG(HLE,"Patching %s %08x", OSPatches[i].m_szPatchName, symbol->address); - } - } - - for (size_t i = 1; i < sizeof(OSBreakPoints) / sizeof(SPatch); i++) - { - Symbol *symbol = g_symbolDB.GetSymbolFromName(OSPatches[i].m_szPatchName); - if (symbol > 0) - { - CBreakPoints::AddBreakPoint(symbol->address, false); - LOG(HLE,"Adding BP to %s %08x", OSBreakPoints[i].m_szPatchName, symbol->address); - } - } - - // CBreakPoints::AddBreakPoint(0x8000D3D0, false); -} - -void Execute(u32 _CurrentPC, u32 _Instruction) -{ - unsigned int FunctionIndex = _Instruction & 0xFFFFF; - if ((FunctionIndex > 0) && (FunctionIndex < (sizeof(OSPatches) / sizeof(SPatch)))) - { - OSPatches[FunctionIndex].PatchFunction(); - } - else - { - PanicAlert("HLE system tried to call an undefined HLE function %i.", FunctionIndex); - } - - // _dbg_assert_msg_(HLE,NPC == LR, "Broken HLE function (doesn't set NPC)", OSPatches[pos].m_szPatchName); -} - -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "HLE.h" + +#include "../PowerPC/PowerPC.h" +#include "../PowerPC/SymbolDB.h" +#include "../HW/Memmap.h" +#include "../Debugger/Debugger_SymbolMap.h" +#include "../Debugger/Debugger_BreakPoints.h" + +#include "HLE_OS.h" +#include "HLE_Misc.h" + +namespace HLE +{ + +using namespace PowerPC; + +typedef void (*TPatchFunction)(); + +enum +{ + HLE_RETURNTYPE_BLR = 0, + HLE_RETURNTYPE_RFI = 1, +}; + +struct SPatch +{ + char m_szPatchName[128]; + TPatchFunction PatchFunction; + int returnType; +}; + +static const SPatch OSPatches[] = +{ + { "FAKE_TO_SKIP_0", HLE_Misc::UnimplementedFunction }, + + // speedup + { "OSProtectRange", HLE_Misc::UnimplementedFunctionFalse }, + // { "THPPlayerGetState", HLE_Misc:THPPlayerGetState }, + + + // debug out is very nice ;) + { "OSReport", HLE_OS::HLE_OSReport }, + { "OSPanic", HLE_OS::HLE_OSPanic }, + { "vprintf", HLE_OS::HLE_vprintf }, + { "printf", HLE_OS::HLE_printf }, + { "puts", HLE_OS::HLE_printf }, //gcc-optimized printf? + + // wii only + { "__OSInitAudioSystem", HLE_Misc::UnimplementedFunction }, + + // Super Monkey Ball + { ".evil_vec_cosine", HLE_Misc::SMB_EvilVecCosine }, + { ".evil_normalize", HLE_Misc::SMB_EvilNormalize }, + { ".evil_vec_setlength", HLE_Misc::SMB_evil_vec_setlength }, + { "PanicAlert", HLE_Misc::PanicAlert }, + { ".sqrt_internal_needs_cr1", HLE_Misc::SMB_sqrt_internal }, + { ".rsqrt_internal_needs_cr1", HLE_Misc::SMB_rsqrt_internal }, + { ".atan2", HLE_Misc::SMB_atan2}, + + // special + // { "GXPeekZ", HLE_Misc::GXPeekZ}, + // { "GXPeekARGB", HLE_Misc::GXPeekARGB}, +}; + +static const SPatch OSBreakPoints[] = +{ + { "FAKE_TO_SKIP_0", HLE_Misc::UnimplementedFunction }, +}; + +void Patch(u32 address, const char *hle_func_name) +{ + for (u32 i = 0; i < sizeof(OSPatches) / sizeof(SPatch); i++) + { + if (!strcmp(OSPatches[i].m_szPatchName, hle_func_name)) { + u32 HLEPatchValue = (1 & 0x3f) << 26; + Memory::Write_U32(HLEPatchValue | i, address); + return; + } + } +} + +void PatchFunctions() +{ + for (u32 i = 0; i < sizeof(OSPatches) / sizeof(SPatch); i++) + { + Symbol *symbol = g_symbolDB.GetSymbolFromName(OSPatches[i].m_szPatchName); + if (symbol > 0) + { + u32 HLEPatchValue = (1 & 0x3f) << 26; + for (u32 addr = symbol->address; addr < symbol->address + symbol->size; addr += 4) + Memory::Write_U32(HLEPatchValue | i, addr); + LOG(HLE,"Patching %s %08x", OSPatches[i].m_szPatchName, symbol->address); + } + } + + for (size_t i = 1; i < sizeof(OSBreakPoints) / sizeof(SPatch); i++) + { + Symbol *symbol = g_symbolDB.GetSymbolFromName(OSPatches[i].m_szPatchName); + if (symbol > 0) + { + CBreakPoints::AddBreakPoint(symbol->address, false); + LOG(HLE,"Adding BP to %s %08x", OSBreakPoints[i].m_szPatchName, symbol->address); + } + } + + // CBreakPoints::AddBreakPoint(0x8000D3D0, false); +} + +void Execute(u32 _CurrentPC, u32 _Instruction) +{ + unsigned int FunctionIndex = _Instruction & 0xFFFFF; + if ((FunctionIndex > 0) && (FunctionIndex < (sizeof(OSPatches) / sizeof(SPatch)))) + { + OSPatches[FunctionIndex].PatchFunction(); + } + else + { + PanicAlert("HLE system tried to call an undefined HLE function %i.", FunctionIndex); + } + + // _dbg_assert_msg_(HLE,NPC == LR, "Broken HLE function (doesn't set NPC)", OSPatches[pos].m_szPatchName); +} + +} diff --git a/Source/Core/Core/Src/HLE/HLE_Misc.cpp b/Source/Core/Core/Src/HLE/HLE_Misc.cpp index 52d791d67e..825fc04ddb 100644 --- a/Source/Core/Core/Src/HLE/HLE_Misc.cpp +++ b/Source/Core/Core/Src/HLE/HLE_Misc.cpp @@ -1,161 +1,161 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include -#include "Common.h" -#include "HLE_OS.h" - -#include "../PowerPC/PowerPC.h" -#include "../HW/Memmap.h" - -namespace HLE_Misc -{ - -inline float F(u32 addr) -{ - u32 mem = Memory::ReadFast32(addr); - return *((float*)&mem); -} - -inline void FW(u32 addr, float x) -{ - u32 data = *((u32*)&x); - Memory::WriteUnchecked_U32(data, addr); -} - -void UnimplementedFunction() -{ - NPC = LR; -} - -void UnimplementedFunctionTrue() -{ - GPR(3) = 1; - NPC = LR; -} - -void UnimplementedFunctionFalse() -{ - GPR(3) = 0; - NPC = LR; -} - -void GXPeekZ() -{ - Memory::Write_U32(0xFFFFFF, GPR(5)); - NPC = LR; -} - -void GXPeekARGB() -{ - Memory::Write_U32(0xFFFFFFFF, GPR(5)); - NPC = LR; -} - -void PanicAlert() -{ - ::PanicAlert("HLE: PanicAlert %08x", LR); - NPC = LR; -} - -// .evil_vec_cosine -void SMB_EvilVecCosine() -{ - u32 r3 = GPR(3); - u32 r4 = GPR(4); - - float x1 = F(r3); - float y1 = F(r3 + 4); - float z1 = F(r3 + 8); - - float x2 = F(r4); - float y2 = F(r4 + 4); - float z2 = F(r4 + 8); - - float s1 = x1*x1 + y1*y1 + z1*z1; - float s2 = x2*x2 + y2*y2 + z2*z2; - - float dot = x1*x2 + y1*y2 + z1*z2; - - rPS0(1) = dot / sqrtf(s1 * s2); - NPC = LR; -} - -void SMB_EvilNormalize() -{ - u32 r3 = GPR(3); - float x = F(r3); - float y = F(r3 + 4); - float z = F(r3 + 8); - float inv_len = 1.0f / sqrtf(x*x + y*y + z*z); - x *= inv_len; - y *= inv_len; - z *= inv_len; - FW(r3, x); - FW(r3 + 4, y); - FW(r3 + 8, z); - NPC = LR; -} - -void SMB_evil_vec_setlength() -{ - u32 r3 = GPR(3); - u32 r4 = GPR(4); - float x = F(r3); - float y = F(r3 + 4); - float z = F(r3 + 8); - float inv_len = (float)(rPS0(1) / sqrt(x*x + y*y + z*z)); - x *= inv_len; - y *= inv_len; - z *= inv_len; - FW(r4, x); - FW(r4 + 4, y); - FW(r4 + 8, z); - NPC = LR; -} - - -void SMB_sqrt_internal() -{ - double f = sqrt(rPS0(1)); - rPS0(0) = rPS0(1); - rPS1(0) = rPS0(1); - rPS0(1) = f; - rPS1(1) = f; - NPC = LR; -} - -void SMB_rsqrt_internal() -{ - double f = 1.0 / sqrt(rPS0(1)); - rPS0(1) = f; - rPS1(1) = f; - NPC = LR; -} - -void SMB_atan2() -{ - // in: f1 = x, f2 = y - // out: r3 = angle - double angle = atan2(rPS0(1), rPS0(2)); - int angle_fixpt = (int)(angle / 3.14159 * 32767); - if (angle_fixpt < -32767) angle_fixpt = -32767; - if (angle_fixpt > 32767) angle_fixpt = 32767; - GPR(3) = angle_fixpt; - NPC = LR; -} -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include "Common.h" +#include "HLE_OS.h" + +#include "../PowerPC/PowerPC.h" +#include "../HW/Memmap.h" + +namespace HLE_Misc +{ + +inline float F(u32 addr) +{ + u32 mem = Memory::ReadFast32(addr); + return *((float*)&mem); +} + +inline void FW(u32 addr, float x) +{ + u32 data = *((u32*)&x); + Memory::WriteUnchecked_U32(data, addr); +} + +void UnimplementedFunction() +{ + NPC = LR; +} + +void UnimplementedFunctionTrue() +{ + GPR(3) = 1; + NPC = LR; +} + +void UnimplementedFunctionFalse() +{ + GPR(3) = 0; + NPC = LR; +} + +void GXPeekZ() +{ + Memory::Write_U32(0xFFFFFF, GPR(5)); + NPC = LR; +} + +void GXPeekARGB() +{ + Memory::Write_U32(0xFFFFFFFF, GPR(5)); + NPC = LR; +} + +void PanicAlert() +{ + ::PanicAlert("HLE: PanicAlert %08x", LR); + NPC = LR; +} + +// .evil_vec_cosine +void SMB_EvilVecCosine() +{ + u32 r3 = GPR(3); + u32 r4 = GPR(4); + + float x1 = F(r3); + float y1 = F(r3 + 4); + float z1 = F(r3 + 8); + + float x2 = F(r4); + float y2 = F(r4 + 4); + float z2 = F(r4 + 8); + + float s1 = x1*x1 + y1*y1 + z1*z1; + float s2 = x2*x2 + y2*y2 + z2*z2; + + float dot = x1*x2 + y1*y2 + z1*z2; + + rPS0(1) = dot / sqrtf(s1 * s2); + NPC = LR; +} + +void SMB_EvilNormalize() +{ + u32 r3 = GPR(3); + float x = F(r3); + float y = F(r3 + 4); + float z = F(r3 + 8); + float inv_len = 1.0f / sqrtf(x*x + y*y + z*z); + x *= inv_len; + y *= inv_len; + z *= inv_len; + FW(r3, x); + FW(r3 + 4, y); + FW(r3 + 8, z); + NPC = LR; +} + +void SMB_evil_vec_setlength() +{ + u32 r3 = GPR(3); + u32 r4 = GPR(4); + float x = F(r3); + float y = F(r3 + 4); + float z = F(r3 + 8); + float inv_len = (float)(rPS0(1) / sqrt(x*x + y*y + z*z)); + x *= inv_len; + y *= inv_len; + z *= inv_len; + FW(r4, x); + FW(r4 + 4, y); + FW(r4 + 8, z); + NPC = LR; +} + + +void SMB_sqrt_internal() +{ + double f = sqrt(rPS0(1)); + rPS0(0) = rPS0(1); + rPS1(0) = rPS0(1); + rPS0(1) = f; + rPS1(1) = f; + NPC = LR; +} + +void SMB_rsqrt_internal() +{ + double f = 1.0 / sqrt(rPS0(1)); + rPS0(1) = f; + rPS1(1) = f; + NPC = LR; +} + +void SMB_atan2() +{ + // in: f1 = x, f2 = y + // out: r3 = angle + double angle = atan2(rPS0(1), rPS0(2)); + int angle_fixpt = (int)(angle / 3.14159 * 32767); + if (angle_fixpt < -32767) angle_fixpt = -32767; + if (angle_fixpt > 32767) angle_fixpt = 32767; + GPR(3) = angle_fixpt; + NPC = LR; +} +} diff --git a/Source/Core/Core/Src/HLE/HLE_OS.cpp b/Source/Core/Core/Src/HLE/HLE_OS.cpp index c309e7c30d..8ad2947a95 100644 --- a/Source/Core/Core/Src/HLE/HLE_OS.cpp +++ b/Source/Core/Core/Src/HLE/HLE_OS.cpp @@ -1,148 +1,148 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "StringUtil.h" -#include - -#include "Common.h" -#include "HLE_OS.h" - -#include "../PowerPC/PowerPC.h" -#include "../HW/Memmap.h" - -namespace HLE_OS -{ - -void GetStringVA(std::string& _rOutBuffer); - -void HLE_OSPanic() -{ - std::string Error; - GetStringVA(Error); - - PanicAlert("OSPanic: %s", Error.c_str()); - LOG(OSREPORT,"(PC=%08x), OSPanic: %s", LR, Error.c_str()); - - NPC = LR; -} - -void HLE_OSReport() -{ - std::string ReportMessage; - GetStringVA(ReportMessage); - - - // PanicAlert("(PC=%08x) OSReport: %s", LR, ReportMessage.c_str()); - - LOG(OSREPORT,"(PC=%08x) OSReport: %s", LR, ReportMessage.c_str()); - NPC = LR; -} - -void HLE_vprintf() -{ - std::string ReportMessage; - GetStringVA(ReportMessage); - - LOG(OSREPORT,"(PC=%08x) VPrintf: %s", LR, ReportMessage.c_str()); - NPC = LR; - -} - -void HLE_printf() -{ - std::string ReportMessage; - GetStringVA(ReportMessage); - - LOG(OSREPORT,"(PC=%08x) Printf: %s ", LR, ReportMessage.c_str()); - NPC = LR; -} - -void GetStringVA(std::string& _rOutBuffer) -{ - _rOutBuffer = ""; - char ArgumentBuffer[256]; - u32 ParameterCounter = 4; - u32 FloatingParameterCounter = 1; - char* pString = (char*)Memory::GetPointer(GPR(3)); - if (!pString) { - //PanicAlert("Invalid GetStringVA call"); - return; - } - while(*pString) - { - if (*pString == '%') - { - char* pArgument = ArgumentBuffer; - *pArgument++ = *pString++; - while(*pString < 'A' || *pString > 'z' || *pString == 'l' || *pString == '-') - *pArgument++ = *pString++; - - *pArgument++ = *pString; - *pArgument = NULL; - - u32 Parameter; - if (ParameterCounter > 10) - { - Parameter = Memory::Read_U32(GPR(1) + 0x8 + ((ParameterCounter - 11) * 4)); - } - else - { - Parameter = GPR(ParameterCounter); - } - ParameterCounter++; - - switch(*pString) - { - case 's': - _rOutBuffer += StringFromFormat(ArgumentBuffer, (char*)Memory::GetPointer(Parameter)); - break; - - case 'd': - case 'i': - { - //u64 Double = Memory::Read_U64(Parameter); - _rOutBuffer += StringFromFormat(ArgumentBuffer, Parameter); - } - break; - - case 'f': - { - _rOutBuffer += StringFromFormat(ArgumentBuffer, - rPS0(FloatingParameterCounter)); - FloatingParameterCounter++; - ParameterCounter--; - } - break; - - - default: - _rOutBuffer += StringFromFormat(ArgumentBuffer, Parameter); - break; - } - pString++; - } - else - { - _rOutBuffer += StringFromFormat("%c", *pString); - pString++; - } - } - if(_rOutBuffer[_rOutBuffer.length() - 1] == '\n') - _rOutBuffer.resize(_rOutBuffer.length() - 1); -} - -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "StringUtil.h" +#include + +#include "Common.h" +#include "HLE_OS.h" + +#include "../PowerPC/PowerPC.h" +#include "../HW/Memmap.h" + +namespace HLE_OS +{ + +void GetStringVA(std::string& _rOutBuffer); + +void HLE_OSPanic() +{ + std::string Error; + GetStringVA(Error); + + PanicAlert("OSPanic: %s", Error.c_str()); + LOG(OSREPORT,"(PC=%08x), OSPanic: %s", LR, Error.c_str()); + + NPC = LR; +} + +void HLE_OSReport() +{ + std::string ReportMessage; + GetStringVA(ReportMessage); + + + // PanicAlert("(PC=%08x) OSReport: %s", LR, ReportMessage.c_str()); + + LOG(OSREPORT,"(PC=%08x) OSReport: %s", LR, ReportMessage.c_str()); + NPC = LR; +} + +void HLE_vprintf() +{ + std::string ReportMessage; + GetStringVA(ReportMessage); + + LOG(OSREPORT,"(PC=%08x) VPrintf: %s", LR, ReportMessage.c_str()); + NPC = LR; + +} + +void HLE_printf() +{ + std::string ReportMessage; + GetStringVA(ReportMessage); + + LOG(OSREPORT,"(PC=%08x) Printf: %s ", LR, ReportMessage.c_str()); + NPC = LR; +} + +void GetStringVA(std::string& _rOutBuffer) +{ + _rOutBuffer = ""; + char ArgumentBuffer[256]; + u32 ParameterCounter = 4; + u32 FloatingParameterCounter = 1; + char* pString = (char*)Memory::GetPointer(GPR(3)); + if (!pString) { + //PanicAlert("Invalid GetStringVA call"); + return; + } + while(*pString) + { + if (*pString == '%') + { + char* pArgument = ArgumentBuffer; + *pArgument++ = *pString++; + while(*pString < 'A' || *pString > 'z' || *pString == 'l' || *pString == '-') + *pArgument++ = *pString++; + + *pArgument++ = *pString; + *pArgument = NULL; + + u32 Parameter; + if (ParameterCounter > 10) + { + Parameter = Memory::Read_U32(GPR(1) + 0x8 + ((ParameterCounter - 11) * 4)); + } + else + { + Parameter = GPR(ParameterCounter); + } + ParameterCounter++; + + switch(*pString) + { + case 's': + _rOutBuffer += StringFromFormat(ArgumentBuffer, (char*)Memory::GetPointer(Parameter)); + break; + + case 'd': + case 'i': + { + //u64 Double = Memory::Read_U64(Parameter); + _rOutBuffer += StringFromFormat(ArgumentBuffer, Parameter); + } + break; + + case 'f': + { + _rOutBuffer += StringFromFormat(ArgumentBuffer, + rPS0(FloatingParameterCounter)); + FloatingParameterCounter++; + ParameterCounter--; + } + break; + + + default: + _rOutBuffer += StringFromFormat(ArgumentBuffer, Parameter); + break; + } + pString++; + } + else + { + _rOutBuffer += StringFromFormat("%c", *pString); + pString++; + } + } + if(_rOutBuffer[_rOutBuffer.length() - 1] == '\n') + _rOutBuffer.resize(_rOutBuffer.length() - 1); +} + +} diff --git a/Source/Core/Core/Src/HW/AudioInterface.cpp b/Source/Core/Core/Src/HW/AudioInterface.cpp index db6eaa1bf1..442fc42437 100644 --- a/Source/Core/Core/Src/HW/AudioInterface.cpp +++ b/Source/Core/Core/Src/HW/AudioInterface.cpp @@ -1,361 +1,361 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -// This file is ONLY about disc streaming. It's a bit unfortunately named. -// For the rest of the audio stuff, including the "real" AI, see DSP.cpp/h. - -// AI disc streaming is handled completely separately from the rest of the -// audio processing. In short, it simply streams audio directly from disc -// out through the speakers. - -#include "Common.h" - -#include "StreamADPCM.H" -#include "AudioInterface.h" - -#include "CPU.h" -#include "PeripheralInterface.h" -#include "DVDInterface.h" -#include "../PowerPC/PowerPC.h" -#include "../CoreTiming.h" -#include "../HW/SystemTimers.h" - -namespace AudioInterface -{ - -// internal hardware addresses -enum -{ - AI_CONTROL_REGISTER = 0x6C00, - AI_VOLUME_REGISTER = 0x6C04, - AI_SAMPLE_COUNTER = 0x6C08, - AI_INTERRUPT_TIMING = 0x6C0C, -}; - -// AI Control Register -union AICR -{ - AICR() { hex = 0;} - AICR(u32 _hex) { hex = _hex;} - struct - { - unsigned PSTAT : 1; // sample counter/playback enable - unsigned AFR : 1; // 0=32khz 1=48khz - unsigned AIINTMSK : 1; // 0=interrupt masked 1=interrupt enabled - unsigned AIINT : 1; // audio interrupt status - unsigned AIINTVLD : 1; // This bit controls whether AIINT is affected by the AIIT register - // matching AISLRCNT. Once set, AIINT will hold - unsigned SCRESET : 1; // write to reset counter - unsigned DSPFR : 1; // DSP Frequency (0=32khz 1=48khz) - unsigned :25; - }; - u32 hex; -}; - -// AI m_Volume Register -union AIVR -{ - struct - { - unsigned leftVolume : 8; - unsigned rightVolume : 8; - unsigned : 16; - }; - u32 hex; -}; - -// AudioInterface-Registers -struct SAudioRegister -{ - AICR m_Control; - AIVR m_Volume; - u32 m_SampleCounter; - u32 m_InterruptTiming; -}; - -// STATE_TO_SAVE -static SAudioRegister g_AudioRegister; -static u64 g_LastCPUTime = 0; -static int g_SampleRate = 32000; -static int g_DSPSampleRate = 32000; -static u64 g_CPUCyclesPerSample = 0xFFFFFFFFFFFULL; - -void DoState(PointerWrap &p) -{ - p.Do(g_AudioRegister); - p.Do(g_LastCPUTime); - p.Do(g_SampleRate); - p.Do(g_DSPSampleRate); - p.Do(g_CPUCyclesPerSample); -} - -void GenerateAudioInterrupt(); -void UpdateInterrupts(); -void IncreaseSampleCount(const u32 _uAmount); -void ReadStreamBlock(short* _pPCM); - -void Init() -{ - g_AudioRegister.m_SampleCounter = 0; - g_AudioRegister.m_Control.AFR = 1; -} - -void Shutdown() -{ -} - -void Read32(u32& _rReturnValue, const u32 _Address) -{ - //__AI_SRC_INIT compares CC006C08 to zero, loops if 2 - switch (_Address & 0xFFFF) - { - case AI_CONTROL_REGISTER: //0x6C00 - LOGV(AUDIO_INTERFACE, 1, "AudioInterface(R) 0x%08x", _Address); - _rReturnValue = g_AudioRegister.m_Control.hex; - - return; - - // Sample Rate (AIGetDSPSampleRate) - // 32bit state (highest bit PlayState) // AIGetStreamPlayState - case AI_VOLUME_REGISTER: //0x6C04 - LOGV(AUDIO_INTERFACE, 1, "AudioInterface(R) 0x%08x", _Address); - _rReturnValue = g_AudioRegister.m_Volume.hex; - return; - - case AI_SAMPLE_COUNTER: //0x6C08 - _rReturnValue = g_AudioRegister.m_SampleCounter; - if (g_AudioRegister.m_Control.PSTAT) - g_AudioRegister.m_SampleCounter++; // FAKE: but this is a must - return; - - case AI_INTERRUPT_TIMING: - // When sample counter reaches the value of this register, the interrupt AIINT should - // fire. - LOGV(AUDIO_INTERFACE, 1, "AudioInterface(R) 0x%08x", _Address); - _rReturnValue = g_AudioRegister.m_InterruptTiming; - return; - - default: - LOG(AUDIO_INTERFACE, "AudioInterface(R) 0x%08x", _Address); - _dbg_assert_msg_(AUDIO_INTERFACE, 0, "AudioInterface - Read from ???"); - _rReturnValue = 0; - return; - } -} - -void Write32(const u32 _Value, const u32 _Address) -{ - switch (_Address & 0xFFFF) - { - case AI_CONTROL_REGISTER: - { - AICR tmpAICtrl(_Value); - - g_AudioRegister.m_Control.AIINTMSK = tmpAICtrl.AIINTMSK; - g_AudioRegister.m_Control.AIINTVLD = tmpAICtrl.AIINTVLD; - - // Set frequency - if (tmpAICtrl.AFR != g_AudioRegister.m_Control.AFR) - { - LOG(AUDIO_INTERFACE, "Change Freq to %s", tmpAICtrl.AFR ? "48khz":"32khz"); - g_AudioRegister.m_Control.AFR = tmpAICtrl.AFR; - } - - g_SampleRate = tmpAICtrl.AFR ? 32000 : 48000; - g_DSPSampleRate = tmpAICtrl.DSPFR ? 32000 : 48000; -// PanicAlert("Sample rate %i %i", g_Aui, g_SampleRate); - - g_CPUCyclesPerSample = SystemTimers::GetTicksPerSecond() / g_SampleRate; - - // Streaming counter - if (tmpAICtrl.PSTAT != g_AudioRegister.m_Control.PSTAT) - { - LOG(AUDIO_INTERFACE, "Change StreamingCounter to %s", tmpAICtrl.PSTAT ? "startet":"stopped"); - g_AudioRegister.m_Control.PSTAT = tmpAICtrl.PSTAT; - - g_LastCPUTime = CoreTiming::GetTicks(); - } - - // AI Interrupt - if (tmpAICtrl.AIINT) - { - LOG(AUDIO_INTERFACE, "Clear AI Interrupt"); - g_AudioRegister.m_Control.AIINT = 0; - } - - // Sample Count Reset - if (tmpAICtrl.SCRESET) - { - LOGV(AUDIO_INTERFACE, 1, "Reset SampleCounter"); - g_AudioRegister.m_SampleCounter = 0; - g_AudioRegister.m_Control.SCRESET = 0; - - // set PSTAT = 0 too ? at least the reversed look like this - - g_LastCPUTime = CoreTiming::GetTicks(); - } - - g_AudioRegister.m_Control = tmpAICtrl; - - UpdateInterrupts(); - } - break; - - case AI_VOLUME_REGISTER: - g_AudioRegister.m_Volume.hex = _Value; - LOGV(AUDIO_INTERFACE, 1, "Set m_Volume: left(%i) right(%i)", g_AudioRegister.m_Volume.leftVolume, g_AudioRegister.m_Volume.rightVolume); - break; - - case AI_SAMPLE_COUNTER: - // _dbg_assert_msg_(AUDIO_INTERFACE, 0, "AudioInterface - m_SampleCounter is Read only"); - g_AudioRegister.m_SampleCounter = _Value; - break; - - case AI_INTERRUPT_TIMING: - g_AudioRegister.m_InterruptTiming = _Value; - LOG(AUDIO_INTERFACE, "Set AudioInterrupt: 0x%08x Samples", g_AudioRegister.m_InterruptTiming); - break; - - default: - PanicAlert("AudioInterface unknown write"); - _dbg_assert_msg_(AUDIO_INTERFACE,0,"AudioInterface - Write to ??? %08x", _Address); - break; - } -} - -void UpdateInterrupts() -{ - if (g_AudioRegister.m_Control.AIINT & g_AudioRegister.m_Control.AIINTMSK) - { - CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_AUDIO, true); - } - else - { - CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_AUDIO, false); - } -} - -void GenerateAudioInterrupt() -{ - g_AudioRegister.m_Control.AIINT = 1; - UpdateInterrupts(); -} - -// Callback for the disc streaming -// WARNING - called from audio thread -u32 Callback_GetStreaming(short* _pDestBuffer, u32 _numSamples) -{ - if (g_AudioRegister.m_Control.PSTAT && !CCPU::IsStepping()) - { - static int pos = 0; - static short pcm[28*2]; - const int lvolume = g_AudioRegister.m_Volume.leftVolume; - const int rvolume = g_AudioRegister.m_Volume.rightVolume; - - for (unsigned int i = 0; i < _numSamples; i++) - { - if (pos == 0) - { - ReadStreamBlock(pcm); - } - - *_pDestBuffer++ = (pcm[pos*2] * lvolume) >> 8; - *_pDestBuffer++ = (pcm[pos*2+1] * rvolume) >> 8; - - pos++; - if (pos == 28) - { - pos = 0; - } - } - } - else - { - for (unsigned int i = 0; i < _numSamples * 2; i++) - { - _pDestBuffer[i] = 0; //silence! - } - } - - return _numSamples; -} - -// WARNING - called from audio thread -void ReadStreamBlock(short *_pPCM) -{ - char tempADPCM[32]; - if (DVDInterface::DVDReadADPCM((u8*)tempADPCM, 32)) - { - NGCADPCM::DecodeBlock(_pPCM, (u8*)tempADPCM); - } - else - { - for (int j=0; j<28; j++) - { - *_pPCM++ = 0; - *_pPCM++ = 0; - } - } - - // COMMENT: - // our whole streaming code is "faked" ... so it shouldn't increase the sample counter - // streaming will never work correctly this way, but at least the program will think all is alright. - - // This call must not be done wihout going through CoreTiming's threadsafe option. - // IncreaseSampleCount(28); -} - -void IncreaseSampleCount(const u32 _iAmount) -{ - if (g_AudioRegister.m_Control.PSTAT) - { - g_AudioRegister.m_SampleCounter += _iAmount; - if (g_AudioRegister.m_Control.AIINTVLD && - (g_AudioRegister.m_SampleCounter >= g_AudioRegister.m_InterruptTiming)) - { - GenerateAudioInterrupt(); - } - } -} - -u32 GetAISampleRate() -{ - return g_SampleRate; -} - -u32 GetDSPSampleRate() -{ - return g_DSPSampleRate; -} - -void Update() -{ - // update timer - if (g_AudioRegister.m_Control.PSTAT) - { - const u64 Diff = CoreTiming::GetTicks() - g_LastCPUTime; - if (Diff > g_CPUCyclesPerSample) - { - const u32 Samples = static_cast(Diff / g_CPUCyclesPerSample); - g_LastCPUTime += Samples * g_CPUCyclesPerSample; - IncreaseSampleCount(Samples); - } - } -} - -} // end of namespace AudioInterface - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// This file is ONLY about disc streaming. It's a bit unfortunately named. +// For the rest of the audio stuff, including the "real" AI, see DSP.cpp/h. + +// AI disc streaming is handled completely separately from the rest of the +// audio processing. In short, it simply streams audio directly from disc +// out through the speakers. + +#include "Common.h" + +#include "StreamADPCM.H" +#include "AudioInterface.h" + +#include "CPU.h" +#include "PeripheralInterface.h" +#include "DVDInterface.h" +#include "../PowerPC/PowerPC.h" +#include "../CoreTiming.h" +#include "../HW/SystemTimers.h" + +namespace AudioInterface +{ + +// internal hardware addresses +enum +{ + AI_CONTROL_REGISTER = 0x6C00, + AI_VOLUME_REGISTER = 0x6C04, + AI_SAMPLE_COUNTER = 0x6C08, + AI_INTERRUPT_TIMING = 0x6C0C, +}; + +// AI Control Register +union AICR +{ + AICR() { hex = 0;} + AICR(u32 _hex) { hex = _hex;} + struct + { + unsigned PSTAT : 1; // sample counter/playback enable + unsigned AFR : 1; // 0=32khz 1=48khz + unsigned AIINTMSK : 1; // 0=interrupt masked 1=interrupt enabled + unsigned AIINT : 1; // audio interrupt status + unsigned AIINTVLD : 1; // This bit controls whether AIINT is affected by the AIIT register + // matching AISLRCNT. Once set, AIINT will hold + unsigned SCRESET : 1; // write to reset counter + unsigned DSPFR : 1; // DSP Frequency (0=32khz 1=48khz) + unsigned :25; + }; + u32 hex; +}; + +// AI m_Volume Register +union AIVR +{ + struct + { + unsigned leftVolume : 8; + unsigned rightVolume : 8; + unsigned : 16; + }; + u32 hex; +}; + +// AudioInterface-Registers +struct SAudioRegister +{ + AICR m_Control; + AIVR m_Volume; + u32 m_SampleCounter; + u32 m_InterruptTiming; +}; + +// STATE_TO_SAVE +static SAudioRegister g_AudioRegister; +static u64 g_LastCPUTime = 0; +static int g_SampleRate = 32000; +static int g_DSPSampleRate = 32000; +static u64 g_CPUCyclesPerSample = 0xFFFFFFFFFFFULL; + +void DoState(PointerWrap &p) +{ + p.Do(g_AudioRegister); + p.Do(g_LastCPUTime); + p.Do(g_SampleRate); + p.Do(g_DSPSampleRate); + p.Do(g_CPUCyclesPerSample); +} + +void GenerateAudioInterrupt(); +void UpdateInterrupts(); +void IncreaseSampleCount(const u32 _uAmount); +void ReadStreamBlock(short* _pPCM); + +void Init() +{ + g_AudioRegister.m_SampleCounter = 0; + g_AudioRegister.m_Control.AFR = 1; +} + +void Shutdown() +{ +} + +void Read32(u32& _rReturnValue, const u32 _Address) +{ + //__AI_SRC_INIT compares CC006C08 to zero, loops if 2 + switch (_Address & 0xFFFF) + { + case AI_CONTROL_REGISTER: //0x6C00 + LOGV(AUDIO_INTERFACE, 1, "AudioInterface(R) 0x%08x", _Address); + _rReturnValue = g_AudioRegister.m_Control.hex; + + return; + + // Sample Rate (AIGetDSPSampleRate) + // 32bit state (highest bit PlayState) // AIGetStreamPlayState + case AI_VOLUME_REGISTER: //0x6C04 + LOGV(AUDIO_INTERFACE, 1, "AudioInterface(R) 0x%08x", _Address); + _rReturnValue = g_AudioRegister.m_Volume.hex; + return; + + case AI_SAMPLE_COUNTER: //0x6C08 + _rReturnValue = g_AudioRegister.m_SampleCounter; + if (g_AudioRegister.m_Control.PSTAT) + g_AudioRegister.m_SampleCounter++; // FAKE: but this is a must + return; + + case AI_INTERRUPT_TIMING: + // When sample counter reaches the value of this register, the interrupt AIINT should + // fire. + LOGV(AUDIO_INTERFACE, 1, "AudioInterface(R) 0x%08x", _Address); + _rReturnValue = g_AudioRegister.m_InterruptTiming; + return; + + default: + LOG(AUDIO_INTERFACE, "AudioInterface(R) 0x%08x", _Address); + _dbg_assert_msg_(AUDIO_INTERFACE, 0, "AudioInterface - Read from ???"); + _rReturnValue = 0; + return; + } +} + +void Write32(const u32 _Value, const u32 _Address) +{ + switch (_Address & 0xFFFF) + { + case AI_CONTROL_REGISTER: + { + AICR tmpAICtrl(_Value); + + g_AudioRegister.m_Control.AIINTMSK = tmpAICtrl.AIINTMSK; + g_AudioRegister.m_Control.AIINTVLD = tmpAICtrl.AIINTVLD; + + // Set frequency + if (tmpAICtrl.AFR != g_AudioRegister.m_Control.AFR) + { + LOG(AUDIO_INTERFACE, "Change Freq to %s", tmpAICtrl.AFR ? "48khz":"32khz"); + g_AudioRegister.m_Control.AFR = tmpAICtrl.AFR; + } + + g_SampleRate = tmpAICtrl.AFR ? 32000 : 48000; + g_DSPSampleRate = tmpAICtrl.DSPFR ? 32000 : 48000; +// PanicAlert("Sample rate %i %i", g_Aui, g_SampleRate); + + g_CPUCyclesPerSample = SystemTimers::GetTicksPerSecond() / g_SampleRate; + + // Streaming counter + if (tmpAICtrl.PSTAT != g_AudioRegister.m_Control.PSTAT) + { + LOG(AUDIO_INTERFACE, "Change StreamingCounter to %s", tmpAICtrl.PSTAT ? "startet":"stopped"); + g_AudioRegister.m_Control.PSTAT = tmpAICtrl.PSTAT; + + g_LastCPUTime = CoreTiming::GetTicks(); + } + + // AI Interrupt + if (tmpAICtrl.AIINT) + { + LOG(AUDIO_INTERFACE, "Clear AI Interrupt"); + g_AudioRegister.m_Control.AIINT = 0; + } + + // Sample Count Reset + if (tmpAICtrl.SCRESET) + { + LOGV(AUDIO_INTERFACE, 1, "Reset SampleCounter"); + g_AudioRegister.m_SampleCounter = 0; + g_AudioRegister.m_Control.SCRESET = 0; + + // set PSTAT = 0 too ? at least the reversed look like this + + g_LastCPUTime = CoreTiming::GetTicks(); + } + + g_AudioRegister.m_Control = tmpAICtrl; + + UpdateInterrupts(); + } + break; + + case AI_VOLUME_REGISTER: + g_AudioRegister.m_Volume.hex = _Value; + LOGV(AUDIO_INTERFACE, 1, "Set m_Volume: left(%i) right(%i)", g_AudioRegister.m_Volume.leftVolume, g_AudioRegister.m_Volume.rightVolume); + break; + + case AI_SAMPLE_COUNTER: + // _dbg_assert_msg_(AUDIO_INTERFACE, 0, "AudioInterface - m_SampleCounter is Read only"); + g_AudioRegister.m_SampleCounter = _Value; + break; + + case AI_INTERRUPT_TIMING: + g_AudioRegister.m_InterruptTiming = _Value; + LOG(AUDIO_INTERFACE, "Set AudioInterrupt: 0x%08x Samples", g_AudioRegister.m_InterruptTiming); + break; + + default: + PanicAlert("AudioInterface unknown write"); + _dbg_assert_msg_(AUDIO_INTERFACE,0,"AudioInterface - Write to ??? %08x", _Address); + break; + } +} + +void UpdateInterrupts() +{ + if (g_AudioRegister.m_Control.AIINT & g_AudioRegister.m_Control.AIINTMSK) + { + CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_AUDIO, true); + } + else + { + CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_AUDIO, false); + } +} + +void GenerateAudioInterrupt() +{ + g_AudioRegister.m_Control.AIINT = 1; + UpdateInterrupts(); +} + +// Callback for the disc streaming +// WARNING - called from audio thread +u32 Callback_GetStreaming(short* _pDestBuffer, u32 _numSamples) +{ + if (g_AudioRegister.m_Control.PSTAT && !CCPU::IsStepping()) + { + static int pos = 0; + static short pcm[28*2]; + const int lvolume = g_AudioRegister.m_Volume.leftVolume; + const int rvolume = g_AudioRegister.m_Volume.rightVolume; + + for (unsigned int i = 0; i < _numSamples; i++) + { + if (pos == 0) + { + ReadStreamBlock(pcm); + } + + *_pDestBuffer++ = (pcm[pos*2] * lvolume) >> 8; + *_pDestBuffer++ = (pcm[pos*2+1] * rvolume) >> 8; + + pos++; + if (pos == 28) + { + pos = 0; + } + } + } + else + { + for (unsigned int i = 0; i < _numSamples * 2; i++) + { + _pDestBuffer[i] = 0; //silence! + } + } + + return _numSamples; +} + +// WARNING - called from audio thread +void ReadStreamBlock(short *_pPCM) +{ + char tempADPCM[32]; + if (DVDInterface::DVDReadADPCM((u8*)tempADPCM, 32)) + { + NGCADPCM::DecodeBlock(_pPCM, (u8*)tempADPCM); + } + else + { + for (int j=0; j<28; j++) + { + *_pPCM++ = 0; + *_pPCM++ = 0; + } + } + + // COMMENT: + // our whole streaming code is "faked" ... so it shouldn't increase the sample counter + // streaming will never work correctly this way, but at least the program will think all is alright. + + // This call must not be done wihout going through CoreTiming's threadsafe option. + // IncreaseSampleCount(28); +} + +void IncreaseSampleCount(const u32 _iAmount) +{ + if (g_AudioRegister.m_Control.PSTAT) + { + g_AudioRegister.m_SampleCounter += _iAmount; + if (g_AudioRegister.m_Control.AIINTVLD && + (g_AudioRegister.m_SampleCounter >= g_AudioRegister.m_InterruptTiming)) + { + GenerateAudioInterrupt(); + } + } +} + +u32 GetAISampleRate() +{ + return g_SampleRate; +} + +u32 GetDSPSampleRate() +{ + return g_DSPSampleRate; +} + +void Update() +{ + // update timer + if (g_AudioRegister.m_Control.PSTAT) + { + const u64 Diff = CoreTiming::GetTicks() - g_LastCPUTime; + if (Diff > g_CPUCyclesPerSample) + { + const u32 Samples = static_cast(Diff / g_CPUCyclesPerSample); + g_LastCPUTime += Samples * g_CPUCyclesPerSample; + IncreaseSampleCount(Samples); + } + } +} + +} // end of namespace AudioInterface + diff --git a/Source/Core/Core/Src/HW/CPU.cpp b/Source/Core/Core/Src/HW/CPU.cpp index 50b3e48c4d..21896a76a2 100644 --- a/Source/Core/Core/Src/HW/CPU.cpp +++ b/Source/Core/Core/Src/HW/CPU.cpp @@ -1,237 +1,237 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "Thread.h" - -#include "../PowerPC/PowerPC.h" -#include "../Host.h" -#include "../Core.h" -#include "CPU.h" -#include "CPUCompare.h" - -#include "../Debugger/Debugger_BreakPoints.h" - -using namespace PowerPC; -namespace { - static bool g_Branch; - static Common::Event m_StepEvent; - static Common::Event *m_SyncEvent; -} - -void CCPU::Init() -{ - m_StepEvent.Init(); - PowerPC::Init(); - m_SyncEvent = 0; -} - -void CCPU::Shutdown() -{ - PowerPC::Shutdown(); - m_StepEvent.Shutdown(); - m_SyncEvent = 0; -} - -void CCPU::Run() -{ - Host_UpdateDisasmDialog(); - - while (true) - { - switch(PowerPC::state) { - case CPU_RUNNING: - //1: enter a fast runloop - PowerPC::RunLoop(); - break; - - case CPU_RUNNINGDEBUG: - //1: check for cpucompare - /* - if (CPUCompare::IsEnabled() && g_Branch) - { - g_Branch = false; - CPUCompare::Sync(); - }*/ - - //2: check for breakpoint - if (CBreakPoints::IsAddressBreakPoint(PC)) - { - LOG(GEKKO, "Hit Breakpoint - %08x", PC); - EnableStepping(true); - if (CBreakPoints::IsTempBreakPoint(PC)) - CBreakPoints::RemoveBreakPoint(PC); - - Host_UpdateDisasmDialog(); - - break; - } - -/* if (!Core::g_CoreStartupParameter.bUseJIT && CBreakPoints::GetBreakCount() == PowerPC::ppcState.DebugCount) - { - LOG(GEKKO, "Hit DebugCount breakpoint - %i", PowerPC::ppcState.DebugCount); - EnableStepping(true); - break; - }*/ - - //3: do a step - PowerPC::SingleStep(); - break; - - case CPU_STEPPING: - //1: wait for step command.. - - m_StepEvent.Wait(); - if (state == CPU_POWERDOWN) - return; - - //2: check for cpu compare - if (CPUCompare::IsEnabled() && g_Branch) - { - g_Branch = false; - CPUCompare::Sync(); - } - - //3: do a step - PowerPC::SingleStep(); - - //4: update disasm dialog - if (m_SyncEvent) { - m_SyncEvent->Set(); - m_SyncEvent = 0; - } - Host_UpdateDisasmDialog(); - break; - - case CPU_POWERDOWN: - //1: Exit loop!! - return; - } - } -} - -void CCPU::Stop() -{ - PowerPC::Stop(); - m_StepEvent.Set(); -} - -bool CCPU::IsStepping() -{ - return PowerPC::state == CPU_STEPPING; -} - -void CCPU::Reset() -{ - -} - -void CCPU::StepOpcode(Common::Event *event) -{ - m_StepEvent.Set(); - if (PowerPC::state == CPU_STEPPING) - { - m_SyncEvent = event; - } -} - -void CCPU::EnableStepping(const bool _bStepping) -{ - if (_bStepping) - { - PowerPC::Pause(); - // TODO(ector): why a sleep? - Host_SetDebugMode(true); - } - else - { - Host_SetDebugMode(false); - PowerPC::Start(); - m_StepEvent.Set(); - } -} - -void CCPU::SingleStep() -{ - switch (PowerPC::state) - { - case CPU_RUNNING: - //1: enter a fast runloop - PowerPC::RunLoop(); - break; - - case CPU_RUNNINGDEBUG: - //1: check for cpucompare - if (CPUCompare::IsEnabled() && g_Branch) - { - g_Branch = false; - CPUCompare::Sync(); - } - - //2: check for breakpoint - if (CBreakPoints::IsAddressBreakPoint(PC)) - { - LOG(GEKKO, "Hit Breakpoint - %08x", PC); - EnableStepping(true); - if (CBreakPoints::IsTempBreakPoint(PC)) - CBreakPoints::RemoveBreakPoint(PC); - break; - } - - if (!Core::g_CoreStartupParameter.bUseJIT && CBreakPoints::GetBreakCount() == PowerPC::ppcState.DebugCount) - { - LOG(GEKKO, "Hit DebugCount breakpoint - %i", PowerPC::ppcState.DebugCount); - EnableStepping(true); - break; - } - - //3: do a step - PowerPC::SingleStep(); - break; - - case CPU_STEPPING: - //1: wait for step command.. - m_StepEvent.Wait(); - - //2: check for cpu compare - if (CPUCompare::IsEnabled() && g_Branch) - { - g_Branch = false; - CPUCompare::Sync(); - } - - //3: do a step - PowerPC::SingleStep(); - - //4: update disasm dialog - if (m_SyncEvent) { - m_SyncEvent->Set(); - m_SyncEvent = 0; - } - Host_UpdateDisasmDialog(); - break; - - case CPU_POWERDOWN: - //1: Exit loop!! - return; - } -} - -void CCPU::Break() -{ - EnableStepping(true); -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "Thread.h" + +#include "../PowerPC/PowerPC.h" +#include "../Host.h" +#include "../Core.h" +#include "CPU.h" +#include "CPUCompare.h" + +#include "../Debugger/Debugger_BreakPoints.h" + +using namespace PowerPC; +namespace { + static bool g_Branch; + static Common::Event m_StepEvent; + static Common::Event *m_SyncEvent; +} + +void CCPU::Init() +{ + m_StepEvent.Init(); + PowerPC::Init(); + m_SyncEvent = 0; +} + +void CCPU::Shutdown() +{ + PowerPC::Shutdown(); + m_StepEvent.Shutdown(); + m_SyncEvent = 0; +} + +void CCPU::Run() +{ + Host_UpdateDisasmDialog(); + + while (true) + { + switch(PowerPC::state) { + case CPU_RUNNING: + //1: enter a fast runloop + PowerPC::RunLoop(); + break; + + case CPU_RUNNINGDEBUG: + //1: check for cpucompare + /* + if (CPUCompare::IsEnabled() && g_Branch) + { + g_Branch = false; + CPUCompare::Sync(); + }*/ + + //2: check for breakpoint + if (CBreakPoints::IsAddressBreakPoint(PC)) + { + LOG(GEKKO, "Hit Breakpoint - %08x", PC); + EnableStepping(true); + if (CBreakPoints::IsTempBreakPoint(PC)) + CBreakPoints::RemoveBreakPoint(PC); + + Host_UpdateDisasmDialog(); + + break; + } + +/* if (!Core::g_CoreStartupParameter.bUseJIT && CBreakPoints::GetBreakCount() == PowerPC::ppcState.DebugCount) + { + LOG(GEKKO, "Hit DebugCount breakpoint - %i", PowerPC::ppcState.DebugCount); + EnableStepping(true); + break; + }*/ + + //3: do a step + PowerPC::SingleStep(); + break; + + case CPU_STEPPING: + //1: wait for step command.. + + m_StepEvent.Wait(); + if (state == CPU_POWERDOWN) + return; + + //2: check for cpu compare + if (CPUCompare::IsEnabled() && g_Branch) + { + g_Branch = false; + CPUCompare::Sync(); + } + + //3: do a step + PowerPC::SingleStep(); + + //4: update disasm dialog + if (m_SyncEvent) { + m_SyncEvent->Set(); + m_SyncEvent = 0; + } + Host_UpdateDisasmDialog(); + break; + + case CPU_POWERDOWN: + //1: Exit loop!! + return; + } + } +} + +void CCPU::Stop() +{ + PowerPC::Stop(); + m_StepEvent.Set(); +} + +bool CCPU::IsStepping() +{ + return PowerPC::state == CPU_STEPPING; +} + +void CCPU::Reset() +{ + +} + +void CCPU::StepOpcode(Common::Event *event) +{ + m_StepEvent.Set(); + if (PowerPC::state == CPU_STEPPING) + { + m_SyncEvent = event; + } +} + +void CCPU::EnableStepping(const bool _bStepping) +{ + if (_bStepping) + { + PowerPC::Pause(); + // TODO(ector): why a sleep? + Host_SetDebugMode(true); + } + else + { + Host_SetDebugMode(false); + PowerPC::Start(); + m_StepEvent.Set(); + } +} + +void CCPU::SingleStep() +{ + switch (PowerPC::state) + { + case CPU_RUNNING: + //1: enter a fast runloop + PowerPC::RunLoop(); + break; + + case CPU_RUNNINGDEBUG: + //1: check for cpucompare + if (CPUCompare::IsEnabled() && g_Branch) + { + g_Branch = false; + CPUCompare::Sync(); + } + + //2: check for breakpoint + if (CBreakPoints::IsAddressBreakPoint(PC)) + { + LOG(GEKKO, "Hit Breakpoint - %08x", PC); + EnableStepping(true); + if (CBreakPoints::IsTempBreakPoint(PC)) + CBreakPoints::RemoveBreakPoint(PC); + break; + } + + if (!Core::g_CoreStartupParameter.bUseJIT && CBreakPoints::GetBreakCount() == PowerPC::ppcState.DebugCount) + { + LOG(GEKKO, "Hit DebugCount breakpoint - %i", PowerPC::ppcState.DebugCount); + EnableStepping(true); + break; + } + + //3: do a step + PowerPC::SingleStep(); + break; + + case CPU_STEPPING: + //1: wait for step command.. + m_StepEvent.Wait(); + + //2: check for cpu compare + if (CPUCompare::IsEnabled() && g_Branch) + { + g_Branch = false; + CPUCompare::Sync(); + } + + //3: do a step + PowerPC::SingleStep(); + + //4: update disasm dialog + if (m_SyncEvent) { + m_SyncEvent->Set(); + m_SyncEvent = 0; + } + Host_UpdateDisasmDialog(); + break; + + case CPU_POWERDOWN: + //1: Exit loop!! + return; + } +} + +void CCPU::Break() +{ + EnableStepping(true); +} diff --git a/Source/Core/Core/Src/HW/CPUCompare.cpp b/Source/Core/Core/Src/HW/CPUCompare.cpp index 42b02a000f..96a85e64e5 100644 --- a/Source/Core/Core/Src/HW/CPUCompare.cpp +++ b/Source/Core/Core/Src/HW/CPUCompare.cpp @@ -1,246 +1,246 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ -#ifdef _WIN32 -#include -#endif - -#include "Common.h" -#include "../Core.h" - -#include "CPUCompare.h" -#include "../PowerPC/PowerPC.h" -#include "CommandProcessor.h" - - -#include "../Host.h" - -#ifdef _WIN32 - -namespace CPUCompare -{ - -HANDLE m_hPipe; -bool m_bIsServer; -bool m_bEnabled; -u32 m_BlockStart; - -#define PIPENAME "\\\\.\\pipe\\cpucompare" - - -int stateSize = 32*4 + 32*16 + 6*4; - - -void SetBlockStart(u32 addr) -{ - m_BlockStart = addr; -} - -void StartServer() -{ - _assert_msg_(GEKKO, Core::GetStartupParameter().bUseDualCore != true, "Don't use multithreading together with CPU-compare."); - - if (m_bEnabled) - return; - //TODO: error checking - m_hPipe = CreateNamedPipe( - PIPENAME, - PIPE_ACCESS_OUTBOUND, - PIPE_WAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, - 1, //maxinst - 0x1000, //outbufsize - 0x1000, //inbufsize - INFINITE, //timeout - 0); - _assert_msg_(GEKKO, m_hPipe != INVALID_HANDLE_VALUE, "Failed to create pipe."); - //_assert_msg_(GEKKO, 0, "Pipe %s created.", PIPENAME); - - m_bIsServer = true; - m_bEnabled = true; -} - -void ConnectAsClient() -{ - _assert_msg_(GEKKO, Core::GetStartupParameter().bUseDualCore != true, "Don't use multithreading together with CPU-compare."); - - if (m_bEnabled) - return; - - //TODO: error checking - m_hPipe = CreateFile( - PIPENAME, - GENERIC_READ, - 0, //share - NULL, - OPEN_EXISTING, - 0, - NULL); - - _assert_msg_(GEKKO, m_hPipe != INVALID_HANDLE_VALUE, "Failed to connect to pipe. %08x (2 = file not found)", GetLastError()); - - m_bEnabled = true; - m_bIsServer = false; -} - -void Stop() -{ - if (m_bEnabled) - { - if (m_bIsServer) - { - DisconnectNamedPipe(m_hPipe); - CloseHandle(m_hPipe); - } - else - { - CloseHandle(m_hPipe); //both for server and client i guess - } - m_bEnabled=false; - } -} - -int Sync() -{ - _assert_msg_(GEKKO,0,"Sync - PC = %08x", PC); - - PowerPC::PowerPCState state; - if (!m_bEnabled) - return 0; - - if (m_bIsServer) // This should be interpreter - { - //write cpu state to m_hPipe - HRESULT result; - u32 written; - // LogManager::Redraw(); - result = WriteFile(m_hPipe, &PowerPC::ppcState, stateSize, (LPDWORD)&written,FALSE); - //_assert_msg_(GEKKO, 0, "Server Wrote!"); - if (FAILED(result)) - { - _assert_msg_(GEKKO,0,"Failed to write cpu state to named pipe"); - Stop(); - } - // LogManager::Redraw(); - } - else // This should be JIT - { - u32 read; - memset(&state,0xcc,stateSize); - BOOL res = ReadFile(m_hPipe, &state, stateSize, (LPDWORD)&read, FALSE); - //_assert_msg_(GEKKO, 0, "Client got data!"); - - //read cpu state to m_hPipe and compare - //if any errors, print report - if (!res || read != stateSize) - { - _assert_msg_(GEKKO,0,"Failed to read cpu state from named pipe"); - Stop(); - } - else - { - bool difference = false; - for (int i=0; i<32; i++) - { - if (PowerPC::ppcState.gpr[i] != state.gpr[i]) - { - LOG(GEKKO, "DIFFERENCE - r%i (local %08x, remote %08x)", i, PowerPC::ppcState.gpr[i], state.gpr[i]); - difference = true; - } - } - - for (int i=0; i<32; i++) - { - for (int j=0; j<2; j++) - { - if (PowerPC::ppcState.ps[i][j] != state.ps[i][j]) - { - LOG(GEKKO, "DIFFERENCE - ps%i_%i (local %f, remote %f)", i, j, PowerPC::ppcState.ps[i][j], state.ps[i][j]); - difference = true; - } - } - } - if (PowerPC::ppcState.cr != state.cr) - { - LOG(GEKKO, "DIFFERENCE - CR (local %08x, remote %08x)", PowerPC::ppcState.cr, state.cr); - difference = true; - } - if (PowerPC::ppcState.pc != state.pc) - { - LOG(GEKKO, "DIFFERENCE - PC (local %08x, remote %08x)", PowerPC::ppcState.pc, state.pc); - difference = true; - } - //if (PowerPC::ppcState.npc != state.npc) - ///{ - // LOG(GEKKO, "DIFFERENCE - NPC (local %08x, remote %08x)", PowerPC::ppcState.npc, state.npc); - // difference = true; - //} - if (PowerPC::ppcState.msr != state.msr) - { - LOG(GEKKO, "DIFFERENCE - MSR (local %08x, remote %08x)", PowerPC::ppcState.msr, state.msr); - difference = true; - } - if (PowerPC::ppcState.fpscr != state.fpscr) - { - LOG(GEKKO, "DIFFERENCE - FPSCR (local %08x, remote %08x)", PowerPC::ppcState.fpscr, state.fpscr); - difference = true; - } - - if (difference) - { - Host_UpdateLogDisplay(); - //Also show drec compare window here - //CDynaViewDlg::Show(true); - //CDynaViewDlg::ViewAddr(m_BlockStart); - //CDynaViewDlg::Show(true); - //Sleep(INFINITE); - return false; - } - else - { - return true; - //LOG(GEKKO, "No difference!"); - } - } - } - return 0; -} - -bool IsEnabled() -{ - return m_bEnabled; -} - -bool IsServer() -{ - return m_bIsServer; -} - - -} -#else - -namespace CPUCompare -{ -void StartServer() { } -void ConnectAsClient() { } -void Stop() { } -int Sync() { return 0; } -bool IsEnabled() { return false; } -bool IsServer() { return false; } -} -// #error Provide a CPUCompare implementation or dummy it out, please - -#endif +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +#ifdef _WIN32 +#include +#endif + +#include "Common.h" +#include "../Core.h" + +#include "CPUCompare.h" +#include "../PowerPC/PowerPC.h" +#include "CommandProcessor.h" + + +#include "../Host.h" + +#ifdef _WIN32 + +namespace CPUCompare +{ + +HANDLE m_hPipe; +bool m_bIsServer; +bool m_bEnabled; +u32 m_BlockStart; + +#define PIPENAME "\\\\.\\pipe\\cpucompare" + + +int stateSize = 32*4 + 32*16 + 6*4; + + +void SetBlockStart(u32 addr) +{ + m_BlockStart = addr; +} + +void StartServer() +{ + _assert_msg_(GEKKO, Core::GetStartupParameter().bUseDualCore != true, "Don't use multithreading together with CPU-compare."); + + if (m_bEnabled) + return; + //TODO: error checking + m_hPipe = CreateNamedPipe( + PIPENAME, + PIPE_ACCESS_OUTBOUND, + PIPE_WAIT | PIPE_TYPE_MESSAGE | PIPE_READMODE_MESSAGE, + 1, //maxinst + 0x1000, //outbufsize + 0x1000, //inbufsize + INFINITE, //timeout + 0); + _assert_msg_(GEKKO, m_hPipe != INVALID_HANDLE_VALUE, "Failed to create pipe."); + //_assert_msg_(GEKKO, 0, "Pipe %s created.", PIPENAME); + + m_bIsServer = true; + m_bEnabled = true; +} + +void ConnectAsClient() +{ + _assert_msg_(GEKKO, Core::GetStartupParameter().bUseDualCore != true, "Don't use multithreading together with CPU-compare."); + + if (m_bEnabled) + return; + + //TODO: error checking + m_hPipe = CreateFile( + PIPENAME, + GENERIC_READ, + 0, //share + NULL, + OPEN_EXISTING, + 0, + NULL); + + _assert_msg_(GEKKO, m_hPipe != INVALID_HANDLE_VALUE, "Failed to connect to pipe. %08x (2 = file not found)", GetLastError()); + + m_bEnabled = true; + m_bIsServer = false; +} + +void Stop() +{ + if (m_bEnabled) + { + if (m_bIsServer) + { + DisconnectNamedPipe(m_hPipe); + CloseHandle(m_hPipe); + } + else + { + CloseHandle(m_hPipe); //both for server and client i guess + } + m_bEnabled=false; + } +} + +int Sync() +{ + _assert_msg_(GEKKO,0,"Sync - PC = %08x", PC); + + PowerPC::PowerPCState state; + if (!m_bEnabled) + return 0; + + if (m_bIsServer) // This should be interpreter + { + //write cpu state to m_hPipe + HRESULT result; + u32 written; + // LogManager::Redraw(); + result = WriteFile(m_hPipe, &PowerPC::ppcState, stateSize, (LPDWORD)&written,FALSE); + //_assert_msg_(GEKKO, 0, "Server Wrote!"); + if (FAILED(result)) + { + _assert_msg_(GEKKO,0,"Failed to write cpu state to named pipe"); + Stop(); + } + // LogManager::Redraw(); + } + else // This should be JIT + { + u32 read; + memset(&state,0xcc,stateSize); + BOOL res = ReadFile(m_hPipe, &state, stateSize, (LPDWORD)&read, FALSE); + //_assert_msg_(GEKKO, 0, "Client got data!"); + + //read cpu state to m_hPipe and compare + //if any errors, print report + if (!res || read != stateSize) + { + _assert_msg_(GEKKO,0,"Failed to read cpu state from named pipe"); + Stop(); + } + else + { + bool difference = false; + for (int i=0; i<32; i++) + { + if (PowerPC::ppcState.gpr[i] != state.gpr[i]) + { + LOG(GEKKO, "DIFFERENCE - r%i (local %08x, remote %08x)", i, PowerPC::ppcState.gpr[i], state.gpr[i]); + difference = true; + } + } + + for (int i=0; i<32; i++) + { + for (int j=0; j<2; j++) + { + if (PowerPC::ppcState.ps[i][j] != state.ps[i][j]) + { + LOG(GEKKO, "DIFFERENCE - ps%i_%i (local %f, remote %f)", i, j, PowerPC::ppcState.ps[i][j], state.ps[i][j]); + difference = true; + } + } + } + if (PowerPC::ppcState.cr != state.cr) + { + LOG(GEKKO, "DIFFERENCE - CR (local %08x, remote %08x)", PowerPC::ppcState.cr, state.cr); + difference = true; + } + if (PowerPC::ppcState.pc != state.pc) + { + LOG(GEKKO, "DIFFERENCE - PC (local %08x, remote %08x)", PowerPC::ppcState.pc, state.pc); + difference = true; + } + //if (PowerPC::ppcState.npc != state.npc) + ///{ + // LOG(GEKKO, "DIFFERENCE - NPC (local %08x, remote %08x)", PowerPC::ppcState.npc, state.npc); + // difference = true; + //} + if (PowerPC::ppcState.msr != state.msr) + { + LOG(GEKKO, "DIFFERENCE - MSR (local %08x, remote %08x)", PowerPC::ppcState.msr, state.msr); + difference = true; + } + if (PowerPC::ppcState.fpscr != state.fpscr) + { + LOG(GEKKO, "DIFFERENCE - FPSCR (local %08x, remote %08x)", PowerPC::ppcState.fpscr, state.fpscr); + difference = true; + } + + if (difference) + { + Host_UpdateLogDisplay(); + //Also show drec compare window here + //CDynaViewDlg::Show(true); + //CDynaViewDlg::ViewAddr(m_BlockStart); + //CDynaViewDlg::Show(true); + //Sleep(INFINITE); + return false; + } + else + { + return true; + //LOG(GEKKO, "No difference!"); + } + } + } + return 0; +} + +bool IsEnabled() +{ + return m_bEnabled; +} + +bool IsServer() +{ + return m_bIsServer; +} + + +} +#else + +namespace CPUCompare +{ +void StartServer() { } +void ConnectAsClient() { } +void Stop() { } +int Sync() { return 0; } +bool IsEnabled() { return false; } +bool IsServer() { return false; } +} +// #error Provide a CPUCompare implementation or dummy it out, please + +#endif diff --git a/Source/Core/Core/Src/HW/CommandProcessor.cpp b/Source/Core/Core/Src/HW/CommandProcessor.cpp index 2ab276b8a6..3d20622027 100644 --- a/Source/Core/Core/Src/HW/CommandProcessor.cpp +++ b/Source/Core/Core/Src/HW/CommandProcessor.cpp @@ -1,728 +1,728 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - - -// NOTES (mb2): - -// * GP/CPU sync can be done by several way: -// - MP1 use BP (breakpoint) in movie-menus and mostly PEtoken in 3D -// - ZWW as Crazy Taxi: PEfinish (GXSetDrawDone) -// - SMS: BP, PEToken, PEfinish -// - ZTP: seems to use PEfinish only -// - Animal Crossing: PEfinish at start but there's a bug... -// There's tons of HiWmk/LoWmk ping pong -> Another sync fashion? -// - Super Monkey Ball Adventures: PEToken. Oddity: read&check-PEToken-value-loop stays -// in its JITed block (never fall in Advance() until the game-watchdog's stuff). -// That's why we can't let perform the AdvanceCallBack as usual. -// The PEToken is volatile now and in the fifo struct. -// - Super Monkey Ball: PEFinish. This game has the lamest way to deal with fifo sync for our MT's stuff. -// A hack is mandatory. DONE and should be ok for other games. - -// *What I guess (thx to asynchronous DualCore mode): -// PPC have a frame-finish watchdog. Handled by system timming stuff like the decrementer. -// (DualCore mode): I have observed, after ZTP logos, a fifo-recovery start when DECREMENTER_EXCEPTION is throwned. -// The frame setting (by GP) took too much time and didn't finish properly due to this watchdog. -// Faster GX plugins required, indeed :p - -// * BPs are needed for some game GP/CPU sync. -// But it could slowdown (MP1 at least) because our GP in DC is faster than "expected" in some area. -// eg: in movie-menus in MP1, BP are reached quickly. -// The bad thing is that involve too much PPC work (int ack, lock GP, reset BP, new BP addr, unlock BP...) hence the slowdown. -// Anyway, emulation should more accurate like this and it emulate some sort of better load balancing. -// Eather way in those area a more accurate GP timing could be done by slowing down the GP or something less stupid. -// Not functional and not used atm (breaks MP2). - -// * funny, in revs before those with this note, BP irq wasn't cleared (a bug indeed) and MP1 menus was faster. -// BP irq was raised and ack just once but never cleared. However it's sufficient for MP1 to work. -// This hack is used atm. Known BPs handling doesn't work well (btw, BP irq clearing might be done by CPIntEnable raising edge). -// The hack seems to be responsible of the movie stutering in MP1 menus. - -// TODO (mb2): -// * raise watermark Ov/Un irq: POINTLESS since emulated GP timings can't be accuratly set. -// Only 3 choices IMHO for a correct emulated load balancing in DC mode: -// - make our own GP watchdog hack that can lock CPU if GP too slow. STARTED -// - hack directly something in PPC timings (dunno how) -// - boost GP so we can consider it as infinitely fast compared to CPU. -// * raise ReadIdle/CmdIdle flags and observe behaviour of MP1 & ZTP (at least) -// * Clean useless comments and debug stuff in Read16, Write16, GatherPipeBursted when sync will be fixed for DC -// * (reminder) do the same in: -// PeripheralInterface.cpp, PixelEngine.cpp, OGL->BPStructs.cpp, fifo.cpp... ok just check change log >> - -// TODO -// * Kick GPU from dispatcher, not from writes -// * Thunking framework -// * Cleanup of messy now unnecessary safety code in jit - -#include "Common.h" -#include "../Plugins/Plugin_Video.h" -#include "../PowerPC/PowerPC.h" -#include "../CoreTiming.h" - -#include "MathUtil.h" -#include "Thread.h" - -#include "Memmap.h" -#include "PeripheralInterface.h" -#include "GPFifo.h" -#include "CPU.h" -#include "../Core.h" -#include "CommandProcessor.h" - -namespace CommandProcessor -{ -// look for 1002 verts, breakpoint there, see why next draw is flushed -// TODO(ector): Warn on bbox read/write - -// Fifo Status Register -union UCPStatusReg -{ - struct - { - unsigned OverflowHiWatermark : 1; - unsigned UnderflowLoWatermark : 1; - unsigned ReadIdle : 1; - unsigned CommandIdle : 1; - unsigned Breakpoint : 1; - unsigned : 11; - }; - u16 Hex; - UCPStatusReg() {Hex = 0; } - UCPStatusReg(u16 _hex) {Hex = _hex; } -}; - -// Fifo Control Register -union UCPCtrlReg -{ - struct - { - unsigned GPReadEnable : 1; - unsigned CPIntEnable : 1; - unsigned FifoOverflowIntEnable : 1; - unsigned FifoUnderflowIntEnable : 1; - unsigned GPLinkEnable : 1; - unsigned BPEnable : 1; - unsigned : 10; - }; - u16 Hex; - UCPCtrlReg() {Hex = 0; } - UCPCtrlReg(u16 _hex) {Hex = _hex; } -}; - -// Fifo Control Register -union UCPClearReg -{ - struct - { - unsigned ClearFifoOverflow : 1; - unsigned ClearFifoUnderflow : 1; - unsigned ClearMetrices : 1; - unsigned : 13; - }; - u16 Hex; - UCPClearReg() {Hex = 0; } - UCPClearReg(u16 _hex) {Hex = _hex; } -}; - -// STATE_TO_SAVE -// variables -UCPStatusReg m_CPStatusReg; -UCPCtrlReg m_CPCtrlReg; -UCPClearReg m_CPClearReg; - -int m_bboxleft; -int m_bboxtop; -int m_bboxright; -int m_bboxbottom; -u16 m_tokenReg; - -SCPFifoStruct fifo; //This one is shared between gfx thread and emulator thread -static u32 fake_GPWatchdogLastToken = 0; - -void DoState(PointerWrap &p) -{ - p.Do(m_CPStatusReg); - p.Do(m_CPCtrlReg); - p.Do(m_CPClearReg); - p.Do(m_bboxleft); - p.Do(m_bboxtop); - p.Do(m_bboxright); - p.Do(m_bboxbottom); - p.Do(m_tokenReg); - p.Do(fifo); -} - -// function -void UpdateFifoRegister(); -void UpdateInterrupts(); - -//inline void WriteLow (u32& _reg, u16 lowbits) {_reg = (_reg & 0xFFFF0000) | lowbits;} -//inline void WriteHigh(u32& _reg, u16 highbits) {_reg = (_reg & 0x0000FFFF) | ((u32)highbits << 16);} -inline void WriteLow (volatile u32& _reg, u16 lowbits) {Common::SyncInterlockedExchange((LONG*)&_reg,(_reg & 0xFFFF0000) | lowbits);} -inline void WriteHigh(volatile u32& _reg, u16 highbits) {Common::SyncInterlockedExchange((LONG*)&_reg,(_reg & 0x0000FFFF) | ((u32)highbits << 16));} - -inline u16 ReadLow (u32 _reg) {return (u16)(_reg & 0xFFFF);} -inline u16 ReadHigh (u32 _reg) {return (u16)(_reg >> 16);} - -int et_UpdateInterrupts; - -// for GP watchdog hack -void IncrementGPWDToken() -{ - Common::SyncInterlockedIncrement((LONG*)&fifo.Fake_GPWDToken); -} - -// Check every FAKE_GP_WATCHDOG_PERIOD if a PE-frame-finish occured -// if not then lock CPUThread until GP finish a frame. -void WaitForFrameFinish() -{ - while ((fake_GPWatchdogLastToken == fifo.Fake_GPWDToken) && fifo.bFF_GPReadEnable && (fifo.CPReadWriteDistance > 0) && !(fifo.bFF_BPEnable && fifo.bFF_Breakpoint)) - ; - fake_GPWatchdogLastToken = fifo.Fake_GPWDToken; -} - - -void UpdateInterrupts_Wrapper(u64 userdata, int cyclesLate) -{ - UpdateInterrupts(); -} - -void Init() -{ - m_CPStatusReg.Hex = 0; - m_CPStatusReg.CommandIdle = 1; - m_CPStatusReg.ReadIdle = 1; - - m_CPCtrlReg.Hex = 0; - - m_bboxleft = 0; - m_bboxtop = 0; - m_bboxright = 640; - m_bboxbottom = 480; - - m_tokenReg = 0; - - fake_GPWatchdogLastToken = 0; - memset(&fifo,0,sizeof(fifo)); - fifo.CPCmdIdle = 1 ; - fifo.CPReadIdle = 1; - - et_UpdateInterrupts = CoreTiming::RegisterEvent("UpdateInterrupts", UpdateInterrupts_Wrapper); - -} - -void Shutdown() -{ -#ifndef _WIN32 - // delete fifo.sync; -#endif -} - -void Read16(u16& _rReturnValue, const u32 _Address) -{ - LOGV(COMMANDPROCESSOR, 1, "(r): 0x%08x", _Address); - switch (_Address & 0xFFF) - { - case STATUS_REGISTER: - //TODO?: if really needed - //m_CPStatusReg.CommandIdle = fifo.CPCmdIdle; - // uncomment: change a bit the behaviour MP1. Not very useful though - m_CPStatusReg.ReadIdle = fifo.CPReadIdle; - //m_CPStatusReg.CommandIdle = fifo.CPReadIdle; - - // hack: CPU will always believe fifo is empty and on idle - //m_CPStatusReg.ReadIdle = 1; - //m_CPStatusReg.CommandIdle = 1; - - _rReturnValue = m_CPStatusReg.Hex; - LOG(COMMANDPROCESSOR, "\t iBP %s | fREADIDLE %s | fCMDIDLE %s | iOvF %s | iUndF %s" - , m_CPStatusReg.Breakpoint ? "ON" : "OFF" - , m_CPStatusReg.ReadIdle ? "ON" : "OFF" - , m_CPStatusReg.CommandIdle ? "ON" : "OFF" - , m_CPStatusReg.OverflowHiWatermark ? "ON" : "OFF" - , m_CPStatusReg.UnderflowLoWatermark ? "ON" : "OFF" - ); - return; - case CTRL_REGISTER: _rReturnValue = m_CPCtrlReg.Hex; return; - case CLEAR_REGISTER: _rReturnValue = m_CPClearReg.Hex; return; - - case FIFO_TOKEN_REGISTER: _rReturnValue = m_tokenReg; return; - case FIFO_BOUNDING_BOX_LEFT: _rReturnValue = m_bboxleft; return; - case FIFO_BOUNDING_BOX_RIGHT: _rReturnValue = m_bboxright; return; - case FIFO_BOUNDING_BOX_TOP: _rReturnValue = m_bboxtop; return; - case FIFO_BOUNDING_BOX_BOTTOM: _rReturnValue = m_bboxbottom; return; - - case FIFO_BASE_LO: _rReturnValue = ReadLow (fifo.CPBase); return; - case FIFO_BASE_HI: _rReturnValue = ReadHigh(fifo.CPBase); return; - case FIFO_END_LO: _rReturnValue = ReadLow (fifo.CPEnd); return; - case FIFO_END_HI: _rReturnValue = ReadHigh(fifo.CPEnd); return; - case FIFO_HI_WATERMARK_LO: _rReturnValue = ReadLow (fifo.CPHiWatermark); return; - case FIFO_HI_WATERMARK_HI: _rReturnValue = ReadHigh(fifo.CPHiWatermark); return; - case FIFO_LO_WATERMARK_LO: _rReturnValue = ReadLow (fifo.CPLoWatermark); return; - case FIFO_LO_WATERMARK_HI: _rReturnValue = ReadHigh(fifo.CPLoWatermark); return; - - // TODO: cases cleanup - case FIFO_RW_DISTANCE_LO: - //_rReturnValue = ReadLow (fifo.CPReadWriteDistance); - // hack: CPU will always believe fifo is empty and on idle - _rReturnValue = 0; - LOG(COMMANDPROCESSOR,"read FIFO_RW_DISTANCE_LO : %04x", _rReturnValue); - return; - case FIFO_RW_DISTANCE_HI: - //_rReturnValue = ReadHigh(fifo.CPReadWriteDistance); - // hack: CPU will always believe fifo is empty and on idle - _rReturnValue = 0; - LOG(COMMANDPROCESSOR,"read FIFO_RW_DISTANCE_HI : %04x", _rReturnValue); - return; - case FIFO_WRITE_POINTER_LO: - _rReturnValue = ReadLow (fifo.CPWritePointer); - LOG(COMMANDPROCESSOR,"read FIFO_WRITE_POINTER_LO : %04x", _rReturnValue); - return; - case FIFO_WRITE_POINTER_HI: - _rReturnValue = ReadHigh(fifo.CPWritePointer); - LOG(COMMANDPROCESSOR,"read FIFO_WRITE_POINTER_HI : %04x", _rReturnValue); - return; - case FIFO_READ_POINTER_LO: - //_rReturnValue = ReadLow (fifo.CPReadPointer); - // hack: CPU will always believe fifo is empty and on idle - _rReturnValue = ReadLow (fifo.CPWritePointer); - LOG(COMMANDPROCESSOR,"read FIFO_READ_POINTER_LO : %04x", _rReturnValue); - return; - case FIFO_READ_POINTER_HI: - //_rReturnValue = ReadHigh(fifo.CPReadPointer); - // hack: CPU will always believe fifo is empty and on idle - _rReturnValue = ReadHigh(fifo.CPWritePointer); - LOG(COMMANDPROCESSOR,"read FIFO_READ_POINTER_HI : %04x", _rReturnValue); - return; - case FIFO_BP_LO: _rReturnValue = ReadLow (fifo.CPBreakpoint); return; - case FIFO_BP_HI: _rReturnValue = ReadHigh(fifo.CPBreakpoint); return; - -// case 0x42: // first metric reg (I guess) read in case of "fifo unknown state" -// Crash(); -// return; - -// case 0x64: -// return 4; //Number of clocks per vertex.. todo: calculate properly - - //add all the other regs here? are they ever read? - default: - { -// char szTemp[111]; -// sprintf(szTemp, "CCommandProcessor 0x%x", (_Address&0xFFF)); -// MessageBox(NULL, szTemp, "mm", MB_OK); - } - _rReturnValue = 0; - return; - } - -} - -bool AllowIdleSkipping() -{ - return !Core::g_CoreStartupParameter.bUseDualCore || (!m_CPCtrlReg.CPIntEnable && !m_CPCtrlReg.BPEnable); -} - -void Write16(const u16 _Value, const u32 _Address) -{ - LOGV(COMMANDPROCESSOR, 1, "(w): 0x%04x @ 0x%08x",_Value,_Address); - - //Spin until queue is empty - it WILL become empty because this is the only thread - //that submits data - - if (Core::g_CoreStartupParameter.bUseDualCore) - { - // Force complete fifo flush if we attempt to set/reset the fifo (API GXSetGPFifo or equivalent) - // It's kind of an API hack but it works for lots of games... and I hope it's the same way for every games. - // TODO: HLE for GX fifo's APIs? - // Here is the hack: - // - if (attempt to overwrite CTRL_REGISTER by 0x0000) - // // then we assume CPReadWriteDistance will be overwrited very soon. - // - if (fifo is not empty) - // // (not 100% sure): shouln't happen unless PPC think having trouble with the sync - // // and it attempt a fifo recovery (look for PI_FIFO_RESET in log). - // // If we want to emulate self fifo recovery we need proper GX metrics emulation... yeah sure :p - // - spin until fifo is empty - // - else - // - normal write16 - - if (((_Address&0xFFF) == CTRL_REGISTER) && (_Value == 0)) // API hack - { - // weird MP1 redo that right after linking fifo with GP... hmmm - /*_dbg_assert_msg_(COMMANDPROCESSOR, fifo.CPReadWriteDistance == 0, - "WTF! Something went wrong with GP/PPC the sync! -> CPReadWriteDistance: 0x%08X\n" - " - The fifo is not empty but we are going to lock it anyway.\n" - " - \"Normaly\", this is due to fifo-hang-so-lets-attempt-recovery.\n" - " - The bad news is dolphin don't support special recovery features like GXfifo's metric yet.\n" - " - The good news is, the time you read that message, the fifo should be empty now :p\n" - " - Anyway, fifo flush will be forced if you press OK and dolphin might continue to work...\n" - " - We aren't betting on that :)", fifo.CPReadWriteDistance); - */ - LOG(COMMANDPROCESSOR, "*********************** GXSetGPFifo very soon? ***********************"); - u32 ct=0; - while (fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance > 0 ) - ct++; - if (ct) {LOG(COMMANDPROCESSOR, "(Write16): %lu cycles for nothing :[ ", ct);} - } - } - - switch (_Address & 0xFFF) - { - case STATUS_REGISTER: - { - UCPStatusReg tmpStatus(_Value); - - // set the flags to "all is okay" - m_CPStatusReg.OverflowHiWatermark = 0; - m_CPStatusReg.UnderflowLoWatermark = 0; - - // TOCHECK (mb2): could BP irq be cleared here too? - //if (tmpStatus.Breakpoint!=m_CPStatusReg.Breakpoint) _asm int 3 - // breakpoint - /*if (tmpStatus.Breakpoint) - { - m_CPStatusReg.Breakpoint = 0; - } - //fifo.bFF_Breakpoint = m_CPStatusReg.Breakpoint; - fifo.bFF_Breakpoint = m_CPStatusReg.Breakpoint ? true : false; - //LOG(COMMANDPROCESSOR,"fifo.bFF_Breakpoint : %i",fifo.bFF_Breakpoint); - */ - - // update interrupts - UpdateInterrupts(); - - LOG(COMMANDPROCESSOR,"\t write to STATUS_REGISTER : %04x", _Value); - } - break; - - case CTRL_REGISTER: - { - UCPCtrlReg tmpCtrl(_Value); - - Common::SyncInterlockedExchange((LONG*)&fifo.bFF_GPReadEnable, tmpCtrl.GPReadEnable); - Common::SyncInterlockedExchange((LONG*)&fifo.bFF_GPLinkEnable, tmpCtrl.GPLinkEnable); - Common::SyncInterlockedExchange((LONG*)&fifo.bFF_BPEnable, tmpCtrl.BPEnable); - - // TOCHECK (mb2): could BP irq be cleared with w16 to STATUS_REGISTER? - // funny hack: eg in MP1 if we disable the clear breakpoint ability by commenting this block - // the game is of course faster but looks stable too. - // Well, the hack is more stable than the "proper" way actualy :p ... it breaks MP2 when ship lands - // So I let the hack for now. - // TODO (mb2): fix this! - - // BP interrupt is cleared here - /* - //if (tmpCtrl.CPIntEnable) - //if (!m_CPCtrlReg.CPIntEnable && tmpCtrl.Hex) // raising edge - //if (m_CPCtrlReg.CPIntEnable && !tmpCtrl.Hex) // falling edge - { - LOG(COMMANDPROCESSOR,"\t ClearBreakpoint interrupt"); - // yes an SC hack, single core mode isn't very gc spec compliant :D - // TODO / FIXME : fix SC BPs. Only because it's pretty ugly to have a if{} here just for that. - if (Core::g_CoreStartupParameter.bUseDualCore) - { - m_CPStatusReg.Breakpoint = 0; - InterlockedExchange((LONG*)&fifo.bFF_Breakpoint, 0); - } - }*/ - m_CPCtrlReg.Hex = tmpCtrl.Hex; - UpdateInterrupts(); - LOG(COMMANDPROCESSOR,"\t write to CTRL_REGISTER : %04x", _Value); - LOG(COMMANDPROCESSOR, "\t GPREAD %s | CPULINK %s | BP %s || CPIntEnable %s | OvF %s | UndF %s" - , fifo.bFF_GPReadEnable ? "ON" : "OFF" - , fifo.bFF_GPLinkEnable ? "ON" : "OFF" - , fifo.bFF_BPEnable ? "ON" : "OFF" - , m_CPCtrlReg.CPIntEnable ? "ON" : "OFF" - , m_CPCtrlReg.FifoOverflowIntEnable ? "ON" : "OFF" - , m_CPCtrlReg.FifoUnderflowIntEnable ? "ON" : "OFF" - ); - - } - break; - - case CLEAR_REGISTER: - { - UCPClearReg tmpClearReg(_Value); - m_CPClearReg.Hex = 0; - - LOG(COMMANDPROCESSOR,"\t write to CLEAR_REGISTER : %04x",_Value); - } - break; - - // Fifo Registers - case FIFO_TOKEN_REGISTER: - m_tokenReg = _Value; - LOG(COMMANDPROCESSOR,"\t write to FIFO_TOKEN_REGISTER : %04x", _Value); - break; - - case FIFO_BASE_LO: - WriteLow ((u32 &)fifo.CPBase, _Value); - fifo.CPBase &= 0xFFFFFFE0; - LOG(COMMANDPROCESSOR,"\t write to FIFO_BASE_LO : %04x", _Value); - break; - case FIFO_BASE_HI: - WriteHigh((u32 &)fifo.CPBase, _Value); - fifo.CPBase &= 0xFFFFFFE0; - LOG(COMMANDPROCESSOR,"\t write to FIFO_BASE_HI : %04x", _Value); - break; - case FIFO_END_LO: - WriteLow ((u32 &)fifo.CPEnd, _Value); - fifo.CPEnd &= 0xFFFFFFE0; - LOG(COMMANDPROCESSOR,"\t write to FIFO_END_LO : %04x", _Value); - break; - case FIFO_END_HI: - WriteHigh((u32 &)fifo.CPEnd, _Value); - fifo.CPEnd &= 0xFFFFFFE0; - LOG(COMMANDPROCESSOR,"\t write to FIFO_END_HI : %04x", _Value); - break; - - // Hm. Should we really & these with FFFFFFE0? - // (mb2): never seen 32B not aligned values for those following regs. - // fifo.CPEnd is the only value that could be not 32B aligned so far. - case FIFO_WRITE_POINTER_LO: - WriteLow ((u32 &)fifo.CPWritePointer, _Value); fifo.CPWritePointer &= 0xFFFFFFE0; - LOG(COMMANDPROCESSOR,"\t write to FIFO_WRITE_POINTER_LO : %04x", _Value); - break; - case FIFO_WRITE_POINTER_HI: - WriteHigh((u32 &)fifo.CPWritePointer, _Value); fifo.CPWritePointer &= 0xFFFFFFE0; - LOG(COMMANDPROCESSOR,"\t write to FIFO_WRITE_POINTER_HI : %04x", _Value); - break; - case FIFO_READ_POINTER_LO: - WriteLow ((u32 &)fifo.CPReadPointer, _Value); fifo.CPReadPointer &= 0xFFFFFFE0; - LOG(COMMANDPROCESSOR,"\t write to FIFO_READ_POINTER_LO : %04x", _Value); - break; - case FIFO_READ_POINTER_HI: - WriteHigh((u32 &)fifo.CPReadPointer, _Value); fifo.CPReadPointer &= 0xFFFFFFE0; - LOG(COMMANDPROCESSOR,"\t write to FIFO_READ_POINTER_HI : %04x", _Value); - break; - - case FIFO_HI_WATERMARK_LO: - WriteLow ((u32 &)fifo.CPHiWatermark, _Value); - LOG(COMMANDPROCESSOR,"\t write to FIFO_HI_WATERMARK_LO : %04x", _Value); - break; - case FIFO_HI_WATERMARK_HI: - WriteHigh((u32 &)fifo.CPHiWatermark, _Value); - LOG(COMMANDPROCESSOR,"\t write to FIFO_HI_WATERMARK_HI : %04x", _Value); - break; - case FIFO_LO_WATERMARK_LO: - WriteLow ((u32 &)fifo.CPLoWatermark, _Value); - LOG(COMMANDPROCESSOR,"\t write to FIFO_LO_WATERMARK_LO : %04x", _Value); - break; - case FIFO_LO_WATERMARK_HI: - WriteHigh((u32 &)fifo.CPLoWatermark, _Value); - LOG(COMMANDPROCESSOR,"\t write to FIFO_LO_WATERMARK_HI : %04x", _Value); - break; - - case FIFO_BP_LO: - WriteLow ((u32 &)fifo.CPBreakpoint, _Value); - LOG(COMMANDPROCESSOR,"write to FIFO_BP_LO : %04x", _Value); - break; - case FIFO_BP_HI: - WriteHigh((u32 &)fifo.CPBreakpoint, _Value); - LOG(COMMANDPROCESSOR,"write to FIFO_BP_HI : %04x", _Value); - break; - - // Super monkey try to overwrite CPReadWriteDistance by an old saved RWD value. Which is lame for us. - // hack: We have to force CPU to think fifo is alway empty and on idle. - // When we fall here CPReadWriteDistance should be always null and the game should always want to overwrite it by 0. - // So, we can skip it. - case FIFO_RW_DISTANCE_HI: - //WriteHigh((u32 &)fifo.CPReadWriteDistance, _Value); - LOG(COMMANDPROCESSOR,"try to write to FIFO_RW_DISTANCE_HI : %04x", _Value); - break; - case FIFO_RW_DISTANCE_LO: - //WriteLow((u32 &)fifo.CPReadWriteDistance, _Value); - LOG(COMMANDPROCESSOR,"try to write to FIFO_RW_DISTANCE_LO : %04x", _Value); - break; - } - - // TODO(mb2): better. Check if it help: avoid CPReadPointer overwrites when stupidly done like in Super Monkey Ball - if ((!fifo.bFF_GPReadEnable && fifo.CPReadIdle) || !Core::g_CoreStartupParameter.bUseDualCore) // TOCHECK(mb2): check again if thread safe? - UpdateFifoRegister(); -} - -void Read32(u32& _rReturnValue, const u32 _Address) -{ - _rReturnValue = 0; - _dbg_assert_msg_(COMMANDPROCESSOR, 0, "Read32 from CommandProccessor at 0x%08x", _Address); -} - -void Write32(const u32 _Data, const u32 _Address) -{ - _dbg_assert_msg_(COMMANDPROCESSOR, 0, "Write32 at CommandProccessor at 0x%08x", _Address); -} - -void GatherPipeBursted() -{ - // if we aren't linked, we don't care about gather pipe data - if (!fifo.bFF_GPLinkEnable) - return; - - if (Core::g_CoreStartupParameter.bUseDualCore) - { - // update the fifo-pointer - fifo.CPWritePointer += GPFifo::GATHER_PIPE_SIZE; - if (fifo.CPWritePointer >= fifo.CPEnd) - fifo.CPWritePointer = fifo.CPBase; - Common::SyncInterlockedExchangeAdd((LONG*)&fifo.CPReadWriteDistance, GPFifo::GATHER_PIPE_SIZE); - - // High watermark overflow handling (hacked way) - u32 ct=0; - if (fifo.CPReadWriteDistance > fifo.CPHiWatermark) - { - // we should raise an Ov interrupt for an accurate fifo emulation and let PPC deal with it. - // But it slowdowns things because of if(interrupt blah blah){} blocks for each 32B fifo transactions. - // CPU would be a bit more loaded too by its interrupt handling... - // Eather way, CPU would have the ability to resume another thread. - // To be clear: this spin loop is like a critical section spin loop in the emulated GX thread hence "hacked way" - - // Yes, in real life, the only purpose of the low watermark interrupt is just for cooling down OV contention. - // - @ game start -> watermark init: Overflow enabled, Underflow disabled - // - if (OV is raised) - // - CPU stop to write to fifo - // - enable Underflow interrupt (this only happens if OV is raised) - // - do other things - // - if (Underflow is raised (implicite: AND if an OV has been raised)) - // - CPU can write to fifo - // - disable Underflow interrupt - - LOG(COMMANDPROCESSOR, "(GatherPipeBursted): CPHiWatermark reached"); - // Wait for GPU to catch up - while (!(fifo.bFF_BPEnable && fifo.bFF_Breakpoint) && fifo.CPReadWriteDistance > fifo.CPLoWatermark) - { - ct++; - // dunno if others threads (like the audio thread) really need a forced context switch here - //Common::SleepCurrentThread(1); - } - if (ct) {LOG(COMMANDPROCESSOR, "(GatherPipeBursted): %lu cycles for nothing :[", ct);} - /**/ - } - // check if we are in sync - _assert_msg_(COMMANDPROCESSOR, fifo.CPWritePointer == CPeripheralInterface::Fifo_CPUWritePointer, "FIFOs linked but out of sync"); - _assert_msg_(COMMANDPROCESSOR, fifo.CPBase == CPeripheralInterface::Fifo_CPUBase, "FIFOs linked but out of sync"); - _assert_msg_(COMMANDPROCESSOR, fifo.CPEnd == CPeripheralInterface::Fifo_CPUEnd, "FIFOs linked but out of sync"); - } - else - { - fifo.CPWritePointer += GPFifo::GATHER_PIPE_SIZE; - if (fifo.CPWritePointer >= fifo.CPEnd) - fifo.CPWritePointer = fifo.CPBase; - // check if we are in sync - _assert_msg_(COMMANDPROCESSOR, fifo.CPWritePointer == CPeripheralInterface::Fifo_CPUWritePointer, "FIFOs linked but out of sync"); - _assert_msg_(COMMANDPROCESSOR, fifo.CPBase == CPeripheralInterface::Fifo_CPUBase, "FIFOs linked but out of sync"); - _assert_msg_(COMMANDPROCESSOR, fifo.CPEnd == CPeripheralInterface::Fifo_CPUEnd, "FIFOs linked but out of sync"); - - UpdateFifoRegister(); - } -} - - - -void CatchUpGPU() -{ - // check if we are able to run this buffer - if ((fifo.bFF_GPReadEnable) && !(fifo.bFF_BPEnable && fifo.bFF_Breakpoint)) - { - while (fifo.CPReadWriteDistance > 0) - { - // check if we are on a breakpoint - if (fifo.bFF_BPEnable) - { - //MessageBox(0,"Breakpoint enabled",0,0); - if ((fifo.CPReadPointer & ~0x1F) == (fifo.CPBreakpoint & ~0x1F)) - { - //_assert_msg_(GEKKO,0,"BP: %08x",fifo.CPBreakpoint); - //LOG(COMMANDPROCESSOR,"!!! BP irq raised"); - fifo.bFF_Breakpoint = 1; - m_CPStatusReg.Breakpoint = 1; - UpdateInterrupts(); - break; - } - } - - // read the data and send it to the VideoPlugin - - u8 *ptr = Memory::GetPointer(fifo.CPReadPointer); - fifo.CPReadPointer += 32; - // We are going to do FP math on the main thread so have to save the current state - SaveSSEState(); - LoadDefaultSSEState(); - PluginVideo::Video_SendFifoData(ptr,32); - LoadSSEState(); - - fifo.CPReadWriteDistance -= 32; - - // increase the ReadPtr - if (fifo.CPReadPointer >= fifo.CPEnd) - { - fifo.CPReadPointer = fifo.CPBase; - LOG(COMMANDPROCESSOR, "BUFFER LOOP"); - // PanicAlert("loop now"); - } - } - } -} - -// __________________________________________________________________________________________________ -// !!! Temporary (I hope): re-used in DC mode -// UpdateFifoRegister -// It's no problem if the gfx falls behind a little bit. Better make sure to stop the cpu thread -// when the distance is way huge, though. -// So: -// CPU thread -/// 0. Write data (done before entering this) -// 1. Compute distance -// 2. If distance > threshold, sleep and goto 1 -// GPU thread -// 1. Compute distance -// 2. If distance < threshold, sleep and goto 1 (or wait for trigger?) -// 3. Read and use a bit of data, goto 1 -void UpdateFifoRegister() -{ - // update the distance - int wp = fifo.CPWritePointer; - int rp = fifo.CPReadPointer; - int dist; - if (wp >= rp) - dist = wp - rp; - else - dist = (wp - fifo.CPBase) + (fifo.CPEnd - rp); - //fifo.CPReadWriteDistance = dist; - Common::SyncInterlockedExchange((LONG*)&fifo.CPReadWriteDistance, dist); - - if (!Core::g_CoreStartupParameter.bUseDualCore) - CatchUpGPU(); -} - -void UpdateInterrupts() -{ - if (fifo.bFF_BPEnable && fifo.bFF_Breakpoint) - { - CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_CP, true); - } - else - { - CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_CP, false); - } -} - -void UpdateInterruptsFromVideoPlugin() -{ - if (fifo.bFF_Breakpoint) // implicit since only BP trigger (see fifo.cpp) can call this - m_CPStatusReg.Breakpoint = 1; - CoreTiming::ScheduleEvent_Threadsafe(0, et_UpdateInterrupts); -} - -} // end of namespace CommandProcessor +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + + +// NOTES (mb2): + +// * GP/CPU sync can be done by several way: +// - MP1 use BP (breakpoint) in movie-menus and mostly PEtoken in 3D +// - ZWW as Crazy Taxi: PEfinish (GXSetDrawDone) +// - SMS: BP, PEToken, PEfinish +// - ZTP: seems to use PEfinish only +// - Animal Crossing: PEfinish at start but there's a bug... +// There's tons of HiWmk/LoWmk ping pong -> Another sync fashion? +// - Super Monkey Ball Adventures: PEToken. Oddity: read&check-PEToken-value-loop stays +// in its JITed block (never fall in Advance() until the game-watchdog's stuff). +// That's why we can't let perform the AdvanceCallBack as usual. +// The PEToken is volatile now and in the fifo struct. +// - Super Monkey Ball: PEFinish. This game has the lamest way to deal with fifo sync for our MT's stuff. +// A hack is mandatory. DONE and should be ok for other games. + +// *What I guess (thx to asynchronous DualCore mode): +// PPC have a frame-finish watchdog. Handled by system timming stuff like the decrementer. +// (DualCore mode): I have observed, after ZTP logos, a fifo-recovery start when DECREMENTER_EXCEPTION is throwned. +// The frame setting (by GP) took too much time and didn't finish properly due to this watchdog. +// Faster GX plugins required, indeed :p + +// * BPs are needed for some game GP/CPU sync. +// But it could slowdown (MP1 at least) because our GP in DC is faster than "expected" in some area. +// eg: in movie-menus in MP1, BP are reached quickly. +// The bad thing is that involve too much PPC work (int ack, lock GP, reset BP, new BP addr, unlock BP...) hence the slowdown. +// Anyway, emulation should more accurate like this and it emulate some sort of better load balancing. +// Eather way in those area a more accurate GP timing could be done by slowing down the GP or something less stupid. +// Not functional and not used atm (breaks MP2). + +// * funny, in revs before those with this note, BP irq wasn't cleared (a bug indeed) and MP1 menus was faster. +// BP irq was raised and ack just once but never cleared. However it's sufficient for MP1 to work. +// This hack is used atm. Known BPs handling doesn't work well (btw, BP irq clearing might be done by CPIntEnable raising edge). +// The hack seems to be responsible of the movie stutering in MP1 menus. + +// TODO (mb2): +// * raise watermark Ov/Un irq: POINTLESS since emulated GP timings can't be accuratly set. +// Only 3 choices IMHO for a correct emulated load balancing in DC mode: +// - make our own GP watchdog hack that can lock CPU if GP too slow. STARTED +// - hack directly something in PPC timings (dunno how) +// - boost GP so we can consider it as infinitely fast compared to CPU. +// * raise ReadIdle/CmdIdle flags and observe behaviour of MP1 & ZTP (at least) +// * Clean useless comments and debug stuff in Read16, Write16, GatherPipeBursted when sync will be fixed for DC +// * (reminder) do the same in: +// PeripheralInterface.cpp, PixelEngine.cpp, OGL->BPStructs.cpp, fifo.cpp... ok just check change log >> + +// TODO +// * Kick GPU from dispatcher, not from writes +// * Thunking framework +// * Cleanup of messy now unnecessary safety code in jit + +#include "Common.h" +#include "../Plugins/Plugin_Video.h" +#include "../PowerPC/PowerPC.h" +#include "../CoreTiming.h" + +#include "MathUtil.h" +#include "Thread.h" + +#include "Memmap.h" +#include "PeripheralInterface.h" +#include "GPFifo.h" +#include "CPU.h" +#include "../Core.h" +#include "CommandProcessor.h" + +namespace CommandProcessor +{ +// look for 1002 verts, breakpoint there, see why next draw is flushed +// TODO(ector): Warn on bbox read/write + +// Fifo Status Register +union UCPStatusReg +{ + struct + { + unsigned OverflowHiWatermark : 1; + unsigned UnderflowLoWatermark : 1; + unsigned ReadIdle : 1; + unsigned CommandIdle : 1; + unsigned Breakpoint : 1; + unsigned : 11; + }; + u16 Hex; + UCPStatusReg() {Hex = 0; } + UCPStatusReg(u16 _hex) {Hex = _hex; } +}; + +// Fifo Control Register +union UCPCtrlReg +{ + struct + { + unsigned GPReadEnable : 1; + unsigned CPIntEnable : 1; + unsigned FifoOverflowIntEnable : 1; + unsigned FifoUnderflowIntEnable : 1; + unsigned GPLinkEnable : 1; + unsigned BPEnable : 1; + unsigned : 10; + }; + u16 Hex; + UCPCtrlReg() {Hex = 0; } + UCPCtrlReg(u16 _hex) {Hex = _hex; } +}; + +// Fifo Control Register +union UCPClearReg +{ + struct + { + unsigned ClearFifoOverflow : 1; + unsigned ClearFifoUnderflow : 1; + unsigned ClearMetrices : 1; + unsigned : 13; + }; + u16 Hex; + UCPClearReg() {Hex = 0; } + UCPClearReg(u16 _hex) {Hex = _hex; } +}; + +// STATE_TO_SAVE +// variables +UCPStatusReg m_CPStatusReg; +UCPCtrlReg m_CPCtrlReg; +UCPClearReg m_CPClearReg; + +int m_bboxleft; +int m_bboxtop; +int m_bboxright; +int m_bboxbottom; +u16 m_tokenReg; + +SCPFifoStruct fifo; //This one is shared between gfx thread and emulator thread +static u32 fake_GPWatchdogLastToken = 0; + +void DoState(PointerWrap &p) +{ + p.Do(m_CPStatusReg); + p.Do(m_CPCtrlReg); + p.Do(m_CPClearReg); + p.Do(m_bboxleft); + p.Do(m_bboxtop); + p.Do(m_bboxright); + p.Do(m_bboxbottom); + p.Do(m_tokenReg); + p.Do(fifo); +} + +// function +void UpdateFifoRegister(); +void UpdateInterrupts(); + +//inline void WriteLow (u32& _reg, u16 lowbits) {_reg = (_reg & 0xFFFF0000) | lowbits;} +//inline void WriteHigh(u32& _reg, u16 highbits) {_reg = (_reg & 0x0000FFFF) | ((u32)highbits << 16);} +inline void WriteLow (volatile u32& _reg, u16 lowbits) {Common::SyncInterlockedExchange((LONG*)&_reg,(_reg & 0xFFFF0000) | lowbits);} +inline void WriteHigh(volatile u32& _reg, u16 highbits) {Common::SyncInterlockedExchange((LONG*)&_reg,(_reg & 0x0000FFFF) | ((u32)highbits << 16));} + +inline u16 ReadLow (u32 _reg) {return (u16)(_reg & 0xFFFF);} +inline u16 ReadHigh (u32 _reg) {return (u16)(_reg >> 16);} + +int et_UpdateInterrupts; + +// for GP watchdog hack +void IncrementGPWDToken() +{ + Common::SyncInterlockedIncrement((LONG*)&fifo.Fake_GPWDToken); +} + +// Check every FAKE_GP_WATCHDOG_PERIOD if a PE-frame-finish occured +// if not then lock CPUThread until GP finish a frame. +void WaitForFrameFinish() +{ + while ((fake_GPWatchdogLastToken == fifo.Fake_GPWDToken) && fifo.bFF_GPReadEnable && (fifo.CPReadWriteDistance > 0) && !(fifo.bFF_BPEnable && fifo.bFF_Breakpoint)) + ; + fake_GPWatchdogLastToken = fifo.Fake_GPWDToken; +} + + +void UpdateInterrupts_Wrapper(u64 userdata, int cyclesLate) +{ + UpdateInterrupts(); +} + +void Init() +{ + m_CPStatusReg.Hex = 0; + m_CPStatusReg.CommandIdle = 1; + m_CPStatusReg.ReadIdle = 1; + + m_CPCtrlReg.Hex = 0; + + m_bboxleft = 0; + m_bboxtop = 0; + m_bboxright = 640; + m_bboxbottom = 480; + + m_tokenReg = 0; + + fake_GPWatchdogLastToken = 0; + memset(&fifo,0,sizeof(fifo)); + fifo.CPCmdIdle = 1 ; + fifo.CPReadIdle = 1; + + et_UpdateInterrupts = CoreTiming::RegisterEvent("UpdateInterrupts", UpdateInterrupts_Wrapper); + +} + +void Shutdown() +{ +#ifndef _WIN32 + // delete fifo.sync; +#endif +} + +void Read16(u16& _rReturnValue, const u32 _Address) +{ + LOGV(COMMANDPROCESSOR, 1, "(r): 0x%08x", _Address); + switch (_Address & 0xFFF) + { + case STATUS_REGISTER: + //TODO?: if really needed + //m_CPStatusReg.CommandIdle = fifo.CPCmdIdle; + // uncomment: change a bit the behaviour MP1. Not very useful though + m_CPStatusReg.ReadIdle = fifo.CPReadIdle; + //m_CPStatusReg.CommandIdle = fifo.CPReadIdle; + + // hack: CPU will always believe fifo is empty and on idle + //m_CPStatusReg.ReadIdle = 1; + //m_CPStatusReg.CommandIdle = 1; + + _rReturnValue = m_CPStatusReg.Hex; + LOG(COMMANDPROCESSOR, "\t iBP %s | fREADIDLE %s | fCMDIDLE %s | iOvF %s | iUndF %s" + , m_CPStatusReg.Breakpoint ? "ON" : "OFF" + , m_CPStatusReg.ReadIdle ? "ON" : "OFF" + , m_CPStatusReg.CommandIdle ? "ON" : "OFF" + , m_CPStatusReg.OverflowHiWatermark ? "ON" : "OFF" + , m_CPStatusReg.UnderflowLoWatermark ? "ON" : "OFF" + ); + return; + case CTRL_REGISTER: _rReturnValue = m_CPCtrlReg.Hex; return; + case CLEAR_REGISTER: _rReturnValue = m_CPClearReg.Hex; return; + + case FIFO_TOKEN_REGISTER: _rReturnValue = m_tokenReg; return; + case FIFO_BOUNDING_BOX_LEFT: _rReturnValue = m_bboxleft; return; + case FIFO_BOUNDING_BOX_RIGHT: _rReturnValue = m_bboxright; return; + case FIFO_BOUNDING_BOX_TOP: _rReturnValue = m_bboxtop; return; + case FIFO_BOUNDING_BOX_BOTTOM: _rReturnValue = m_bboxbottom; return; + + case FIFO_BASE_LO: _rReturnValue = ReadLow (fifo.CPBase); return; + case FIFO_BASE_HI: _rReturnValue = ReadHigh(fifo.CPBase); return; + case FIFO_END_LO: _rReturnValue = ReadLow (fifo.CPEnd); return; + case FIFO_END_HI: _rReturnValue = ReadHigh(fifo.CPEnd); return; + case FIFO_HI_WATERMARK_LO: _rReturnValue = ReadLow (fifo.CPHiWatermark); return; + case FIFO_HI_WATERMARK_HI: _rReturnValue = ReadHigh(fifo.CPHiWatermark); return; + case FIFO_LO_WATERMARK_LO: _rReturnValue = ReadLow (fifo.CPLoWatermark); return; + case FIFO_LO_WATERMARK_HI: _rReturnValue = ReadHigh(fifo.CPLoWatermark); return; + + // TODO: cases cleanup + case FIFO_RW_DISTANCE_LO: + //_rReturnValue = ReadLow (fifo.CPReadWriteDistance); + // hack: CPU will always believe fifo is empty and on idle + _rReturnValue = 0; + LOG(COMMANDPROCESSOR,"read FIFO_RW_DISTANCE_LO : %04x", _rReturnValue); + return; + case FIFO_RW_DISTANCE_HI: + //_rReturnValue = ReadHigh(fifo.CPReadWriteDistance); + // hack: CPU will always believe fifo is empty and on idle + _rReturnValue = 0; + LOG(COMMANDPROCESSOR,"read FIFO_RW_DISTANCE_HI : %04x", _rReturnValue); + return; + case FIFO_WRITE_POINTER_LO: + _rReturnValue = ReadLow (fifo.CPWritePointer); + LOG(COMMANDPROCESSOR,"read FIFO_WRITE_POINTER_LO : %04x", _rReturnValue); + return; + case FIFO_WRITE_POINTER_HI: + _rReturnValue = ReadHigh(fifo.CPWritePointer); + LOG(COMMANDPROCESSOR,"read FIFO_WRITE_POINTER_HI : %04x", _rReturnValue); + return; + case FIFO_READ_POINTER_LO: + //_rReturnValue = ReadLow (fifo.CPReadPointer); + // hack: CPU will always believe fifo is empty and on idle + _rReturnValue = ReadLow (fifo.CPWritePointer); + LOG(COMMANDPROCESSOR,"read FIFO_READ_POINTER_LO : %04x", _rReturnValue); + return; + case FIFO_READ_POINTER_HI: + //_rReturnValue = ReadHigh(fifo.CPReadPointer); + // hack: CPU will always believe fifo is empty and on idle + _rReturnValue = ReadHigh(fifo.CPWritePointer); + LOG(COMMANDPROCESSOR,"read FIFO_READ_POINTER_HI : %04x", _rReturnValue); + return; + case FIFO_BP_LO: _rReturnValue = ReadLow (fifo.CPBreakpoint); return; + case FIFO_BP_HI: _rReturnValue = ReadHigh(fifo.CPBreakpoint); return; + +// case 0x42: // first metric reg (I guess) read in case of "fifo unknown state" +// Crash(); +// return; + +// case 0x64: +// return 4; //Number of clocks per vertex.. todo: calculate properly + + //add all the other regs here? are they ever read? + default: + { +// char szTemp[111]; +// sprintf(szTemp, "CCommandProcessor 0x%x", (_Address&0xFFF)); +// MessageBox(NULL, szTemp, "mm", MB_OK); + } + _rReturnValue = 0; + return; + } + +} + +bool AllowIdleSkipping() +{ + return !Core::g_CoreStartupParameter.bUseDualCore || (!m_CPCtrlReg.CPIntEnable && !m_CPCtrlReg.BPEnable); +} + +void Write16(const u16 _Value, const u32 _Address) +{ + LOGV(COMMANDPROCESSOR, 1, "(w): 0x%04x @ 0x%08x",_Value,_Address); + + //Spin until queue is empty - it WILL become empty because this is the only thread + //that submits data + + if (Core::g_CoreStartupParameter.bUseDualCore) + { + // Force complete fifo flush if we attempt to set/reset the fifo (API GXSetGPFifo or equivalent) + // It's kind of an API hack but it works for lots of games... and I hope it's the same way for every games. + // TODO: HLE for GX fifo's APIs? + // Here is the hack: + // - if (attempt to overwrite CTRL_REGISTER by 0x0000) + // // then we assume CPReadWriteDistance will be overwrited very soon. + // - if (fifo is not empty) + // // (not 100% sure): shouln't happen unless PPC think having trouble with the sync + // // and it attempt a fifo recovery (look for PI_FIFO_RESET in log). + // // If we want to emulate self fifo recovery we need proper GX metrics emulation... yeah sure :p + // - spin until fifo is empty + // - else + // - normal write16 + + if (((_Address&0xFFF) == CTRL_REGISTER) && (_Value == 0)) // API hack + { + // weird MP1 redo that right after linking fifo with GP... hmmm + /*_dbg_assert_msg_(COMMANDPROCESSOR, fifo.CPReadWriteDistance == 0, + "WTF! Something went wrong with GP/PPC the sync! -> CPReadWriteDistance: 0x%08X\n" + " - The fifo is not empty but we are going to lock it anyway.\n" + " - \"Normaly\", this is due to fifo-hang-so-lets-attempt-recovery.\n" + " - The bad news is dolphin don't support special recovery features like GXfifo's metric yet.\n" + " - The good news is, the time you read that message, the fifo should be empty now :p\n" + " - Anyway, fifo flush will be forced if you press OK and dolphin might continue to work...\n" + " - We aren't betting on that :)", fifo.CPReadWriteDistance); + */ + LOG(COMMANDPROCESSOR, "*********************** GXSetGPFifo very soon? ***********************"); + u32 ct=0; + while (fifo.bFF_GPReadEnable && fifo.CPReadWriteDistance > 0 ) + ct++; + if (ct) {LOG(COMMANDPROCESSOR, "(Write16): %lu cycles for nothing :[ ", ct);} + } + } + + switch (_Address & 0xFFF) + { + case STATUS_REGISTER: + { + UCPStatusReg tmpStatus(_Value); + + // set the flags to "all is okay" + m_CPStatusReg.OverflowHiWatermark = 0; + m_CPStatusReg.UnderflowLoWatermark = 0; + + // TOCHECK (mb2): could BP irq be cleared here too? + //if (tmpStatus.Breakpoint!=m_CPStatusReg.Breakpoint) _asm int 3 + // breakpoint + /*if (tmpStatus.Breakpoint) + { + m_CPStatusReg.Breakpoint = 0; + } + //fifo.bFF_Breakpoint = m_CPStatusReg.Breakpoint; + fifo.bFF_Breakpoint = m_CPStatusReg.Breakpoint ? true : false; + //LOG(COMMANDPROCESSOR,"fifo.bFF_Breakpoint : %i",fifo.bFF_Breakpoint); + */ + + // update interrupts + UpdateInterrupts(); + + LOG(COMMANDPROCESSOR,"\t write to STATUS_REGISTER : %04x", _Value); + } + break; + + case CTRL_REGISTER: + { + UCPCtrlReg tmpCtrl(_Value); + + Common::SyncInterlockedExchange((LONG*)&fifo.bFF_GPReadEnable, tmpCtrl.GPReadEnable); + Common::SyncInterlockedExchange((LONG*)&fifo.bFF_GPLinkEnable, tmpCtrl.GPLinkEnable); + Common::SyncInterlockedExchange((LONG*)&fifo.bFF_BPEnable, tmpCtrl.BPEnable); + + // TOCHECK (mb2): could BP irq be cleared with w16 to STATUS_REGISTER? + // funny hack: eg in MP1 if we disable the clear breakpoint ability by commenting this block + // the game is of course faster but looks stable too. + // Well, the hack is more stable than the "proper" way actualy :p ... it breaks MP2 when ship lands + // So I let the hack for now. + // TODO (mb2): fix this! + + // BP interrupt is cleared here + /* + //if (tmpCtrl.CPIntEnable) + //if (!m_CPCtrlReg.CPIntEnable && tmpCtrl.Hex) // raising edge + //if (m_CPCtrlReg.CPIntEnable && !tmpCtrl.Hex) // falling edge + { + LOG(COMMANDPROCESSOR,"\t ClearBreakpoint interrupt"); + // yes an SC hack, single core mode isn't very gc spec compliant :D + // TODO / FIXME : fix SC BPs. Only because it's pretty ugly to have a if{} here just for that. + if (Core::g_CoreStartupParameter.bUseDualCore) + { + m_CPStatusReg.Breakpoint = 0; + InterlockedExchange((LONG*)&fifo.bFF_Breakpoint, 0); + } + }*/ + m_CPCtrlReg.Hex = tmpCtrl.Hex; + UpdateInterrupts(); + LOG(COMMANDPROCESSOR,"\t write to CTRL_REGISTER : %04x", _Value); + LOG(COMMANDPROCESSOR, "\t GPREAD %s | CPULINK %s | BP %s || CPIntEnable %s | OvF %s | UndF %s" + , fifo.bFF_GPReadEnable ? "ON" : "OFF" + , fifo.bFF_GPLinkEnable ? "ON" : "OFF" + , fifo.bFF_BPEnable ? "ON" : "OFF" + , m_CPCtrlReg.CPIntEnable ? "ON" : "OFF" + , m_CPCtrlReg.FifoOverflowIntEnable ? "ON" : "OFF" + , m_CPCtrlReg.FifoUnderflowIntEnable ? "ON" : "OFF" + ); + + } + break; + + case CLEAR_REGISTER: + { + UCPClearReg tmpClearReg(_Value); + m_CPClearReg.Hex = 0; + + LOG(COMMANDPROCESSOR,"\t write to CLEAR_REGISTER : %04x",_Value); + } + break; + + // Fifo Registers + case FIFO_TOKEN_REGISTER: + m_tokenReg = _Value; + LOG(COMMANDPROCESSOR,"\t write to FIFO_TOKEN_REGISTER : %04x", _Value); + break; + + case FIFO_BASE_LO: + WriteLow ((u32 &)fifo.CPBase, _Value); + fifo.CPBase &= 0xFFFFFFE0; + LOG(COMMANDPROCESSOR,"\t write to FIFO_BASE_LO : %04x", _Value); + break; + case FIFO_BASE_HI: + WriteHigh((u32 &)fifo.CPBase, _Value); + fifo.CPBase &= 0xFFFFFFE0; + LOG(COMMANDPROCESSOR,"\t write to FIFO_BASE_HI : %04x", _Value); + break; + case FIFO_END_LO: + WriteLow ((u32 &)fifo.CPEnd, _Value); + fifo.CPEnd &= 0xFFFFFFE0; + LOG(COMMANDPROCESSOR,"\t write to FIFO_END_LO : %04x", _Value); + break; + case FIFO_END_HI: + WriteHigh((u32 &)fifo.CPEnd, _Value); + fifo.CPEnd &= 0xFFFFFFE0; + LOG(COMMANDPROCESSOR,"\t write to FIFO_END_HI : %04x", _Value); + break; + + // Hm. Should we really & these with FFFFFFE0? + // (mb2): never seen 32B not aligned values for those following regs. + // fifo.CPEnd is the only value that could be not 32B aligned so far. + case FIFO_WRITE_POINTER_LO: + WriteLow ((u32 &)fifo.CPWritePointer, _Value); fifo.CPWritePointer &= 0xFFFFFFE0; + LOG(COMMANDPROCESSOR,"\t write to FIFO_WRITE_POINTER_LO : %04x", _Value); + break; + case FIFO_WRITE_POINTER_HI: + WriteHigh((u32 &)fifo.CPWritePointer, _Value); fifo.CPWritePointer &= 0xFFFFFFE0; + LOG(COMMANDPROCESSOR,"\t write to FIFO_WRITE_POINTER_HI : %04x", _Value); + break; + case FIFO_READ_POINTER_LO: + WriteLow ((u32 &)fifo.CPReadPointer, _Value); fifo.CPReadPointer &= 0xFFFFFFE0; + LOG(COMMANDPROCESSOR,"\t write to FIFO_READ_POINTER_LO : %04x", _Value); + break; + case FIFO_READ_POINTER_HI: + WriteHigh((u32 &)fifo.CPReadPointer, _Value); fifo.CPReadPointer &= 0xFFFFFFE0; + LOG(COMMANDPROCESSOR,"\t write to FIFO_READ_POINTER_HI : %04x", _Value); + break; + + case FIFO_HI_WATERMARK_LO: + WriteLow ((u32 &)fifo.CPHiWatermark, _Value); + LOG(COMMANDPROCESSOR,"\t write to FIFO_HI_WATERMARK_LO : %04x", _Value); + break; + case FIFO_HI_WATERMARK_HI: + WriteHigh((u32 &)fifo.CPHiWatermark, _Value); + LOG(COMMANDPROCESSOR,"\t write to FIFO_HI_WATERMARK_HI : %04x", _Value); + break; + case FIFO_LO_WATERMARK_LO: + WriteLow ((u32 &)fifo.CPLoWatermark, _Value); + LOG(COMMANDPROCESSOR,"\t write to FIFO_LO_WATERMARK_LO : %04x", _Value); + break; + case FIFO_LO_WATERMARK_HI: + WriteHigh((u32 &)fifo.CPLoWatermark, _Value); + LOG(COMMANDPROCESSOR,"\t write to FIFO_LO_WATERMARK_HI : %04x", _Value); + break; + + case FIFO_BP_LO: + WriteLow ((u32 &)fifo.CPBreakpoint, _Value); + LOG(COMMANDPROCESSOR,"write to FIFO_BP_LO : %04x", _Value); + break; + case FIFO_BP_HI: + WriteHigh((u32 &)fifo.CPBreakpoint, _Value); + LOG(COMMANDPROCESSOR,"write to FIFO_BP_HI : %04x", _Value); + break; + + // Super monkey try to overwrite CPReadWriteDistance by an old saved RWD value. Which is lame for us. + // hack: We have to force CPU to think fifo is alway empty and on idle. + // When we fall here CPReadWriteDistance should be always null and the game should always want to overwrite it by 0. + // So, we can skip it. + case FIFO_RW_DISTANCE_HI: + //WriteHigh((u32 &)fifo.CPReadWriteDistance, _Value); + LOG(COMMANDPROCESSOR,"try to write to FIFO_RW_DISTANCE_HI : %04x", _Value); + break; + case FIFO_RW_DISTANCE_LO: + //WriteLow((u32 &)fifo.CPReadWriteDistance, _Value); + LOG(COMMANDPROCESSOR,"try to write to FIFO_RW_DISTANCE_LO : %04x", _Value); + break; + } + + // TODO(mb2): better. Check if it help: avoid CPReadPointer overwrites when stupidly done like in Super Monkey Ball + if ((!fifo.bFF_GPReadEnable && fifo.CPReadIdle) || !Core::g_CoreStartupParameter.bUseDualCore) // TOCHECK(mb2): check again if thread safe? + UpdateFifoRegister(); +} + +void Read32(u32& _rReturnValue, const u32 _Address) +{ + _rReturnValue = 0; + _dbg_assert_msg_(COMMANDPROCESSOR, 0, "Read32 from CommandProccessor at 0x%08x", _Address); +} + +void Write32(const u32 _Data, const u32 _Address) +{ + _dbg_assert_msg_(COMMANDPROCESSOR, 0, "Write32 at CommandProccessor at 0x%08x", _Address); +} + +void GatherPipeBursted() +{ + // if we aren't linked, we don't care about gather pipe data + if (!fifo.bFF_GPLinkEnable) + return; + + if (Core::g_CoreStartupParameter.bUseDualCore) + { + // update the fifo-pointer + fifo.CPWritePointer += GPFifo::GATHER_PIPE_SIZE; + if (fifo.CPWritePointer >= fifo.CPEnd) + fifo.CPWritePointer = fifo.CPBase; + Common::SyncInterlockedExchangeAdd((LONG*)&fifo.CPReadWriteDistance, GPFifo::GATHER_PIPE_SIZE); + + // High watermark overflow handling (hacked way) + u32 ct=0; + if (fifo.CPReadWriteDistance > fifo.CPHiWatermark) + { + // we should raise an Ov interrupt for an accurate fifo emulation and let PPC deal with it. + // But it slowdowns things because of if(interrupt blah blah){} blocks for each 32B fifo transactions. + // CPU would be a bit more loaded too by its interrupt handling... + // Eather way, CPU would have the ability to resume another thread. + // To be clear: this spin loop is like a critical section spin loop in the emulated GX thread hence "hacked way" + + // Yes, in real life, the only purpose of the low watermark interrupt is just for cooling down OV contention. + // - @ game start -> watermark init: Overflow enabled, Underflow disabled + // - if (OV is raised) + // - CPU stop to write to fifo + // - enable Underflow interrupt (this only happens if OV is raised) + // - do other things + // - if (Underflow is raised (implicite: AND if an OV has been raised)) + // - CPU can write to fifo + // - disable Underflow interrupt + + LOG(COMMANDPROCESSOR, "(GatherPipeBursted): CPHiWatermark reached"); + // Wait for GPU to catch up + while (!(fifo.bFF_BPEnable && fifo.bFF_Breakpoint) && fifo.CPReadWriteDistance > fifo.CPLoWatermark) + { + ct++; + // dunno if others threads (like the audio thread) really need a forced context switch here + //Common::SleepCurrentThread(1); + } + if (ct) {LOG(COMMANDPROCESSOR, "(GatherPipeBursted): %lu cycles for nothing :[", ct);} + /**/ + } + // check if we are in sync + _assert_msg_(COMMANDPROCESSOR, fifo.CPWritePointer == CPeripheralInterface::Fifo_CPUWritePointer, "FIFOs linked but out of sync"); + _assert_msg_(COMMANDPROCESSOR, fifo.CPBase == CPeripheralInterface::Fifo_CPUBase, "FIFOs linked but out of sync"); + _assert_msg_(COMMANDPROCESSOR, fifo.CPEnd == CPeripheralInterface::Fifo_CPUEnd, "FIFOs linked but out of sync"); + } + else + { + fifo.CPWritePointer += GPFifo::GATHER_PIPE_SIZE; + if (fifo.CPWritePointer >= fifo.CPEnd) + fifo.CPWritePointer = fifo.CPBase; + // check if we are in sync + _assert_msg_(COMMANDPROCESSOR, fifo.CPWritePointer == CPeripheralInterface::Fifo_CPUWritePointer, "FIFOs linked but out of sync"); + _assert_msg_(COMMANDPROCESSOR, fifo.CPBase == CPeripheralInterface::Fifo_CPUBase, "FIFOs linked but out of sync"); + _assert_msg_(COMMANDPROCESSOR, fifo.CPEnd == CPeripheralInterface::Fifo_CPUEnd, "FIFOs linked but out of sync"); + + UpdateFifoRegister(); + } +} + + + +void CatchUpGPU() +{ + // check if we are able to run this buffer + if ((fifo.bFF_GPReadEnable) && !(fifo.bFF_BPEnable && fifo.bFF_Breakpoint)) + { + while (fifo.CPReadWriteDistance > 0) + { + // check if we are on a breakpoint + if (fifo.bFF_BPEnable) + { + //MessageBox(0,"Breakpoint enabled",0,0); + if ((fifo.CPReadPointer & ~0x1F) == (fifo.CPBreakpoint & ~0x1F)) + { + //_assert_msg_(GEKKO,0,"BP: %08x",fifo.CPBreakpoint); + //LOG(COMMANDPROCESSOR,"!!! BP irq raised"); + fifo.bFF_Breakpoint = 1; + m_CPStatusReg.Breakpoint = 1; + UpdateInterrupts(); + break; + } + } + + // read the data and send it to the VideoPlugin + + u8 *ptr = Memory::GetPointer(fifo.CPReadPointer); + fifo.CPReadPointer += 32; + // We are going to do FP math on the main thread so have to save the current state + SaveSSEState(); + LoadDefaultSSEState(); + PluginVideo::Video_SendFifoData(ptr,32); + LoadSSEState(); + + fifo.CPReadWriteDistance -= 32; + + // increase the ReadPtr + if (fifo.CPReadPointer >= fifo.CPEnd) + { + fifo.CPReadPointer = fifo.CPBase; + LOG(COMMANDPROCESSOR, "BUFFER LOOP"); + // PanicAlert("loop now"); + } + } + } +} + +// __________________________________________________________________________________________________ +// !!! Temporary (I hope): re-used in DC mode +// UpdateFifoRegister +// It's no problem if the gfx falls behind a little bit. Better make sure to stop the cpu thread +// when the distance is way huge, though. +// So: +// CPU thread +/// 0. Write data (done before entering this) +// 1. Compute distance +// 2. If distance > threshold, sleep and goto 1 +// GPU thread +// 1. Compute distance +// 2. If distance < threshold, sleep and goto 1 (or wait for trigger?) +// 3. Read and use a bit of data, goto 1 +void UpdateFifoRegister() +{ + // update the distance + int wp = fifo.CPWritePointer; + int rp = fifo.CPReadPointer; + int dist; + if (wp >= rp) + dist = wp - rp; + else + dist = (wp - fifo.CPBase) + (fifo.CPEnd - rp); + //fifo.CPReadWriteDistance = dist; + Common::SyncInterlockedExchange((LONG*)&fifo.CPReadWriteDistance, dist); + + if (!Core::g_CoreStartupParameter.bUseDualCore) + CatchUpGPU(); +} + +void UpdateInterrupts() +{ + if (fifo.bFF_BPEnable && fifo.bFF_Breakpoint) + { + CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_CP, true); + } + else + { + CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_CP, false); + } +} + +void UpdateInterruptsFromVideoPlugin() +{ + if (fifo.bFF_Breakpoint) // implicit since only BP trigger (see fifo.cpp) can call this + m_CPStatusReg.Breakpoint = 1; + CoreTiming::ScheduleEvent_Threadsafe(0, et_UpdateInterrupts); +} + +} // end of namespace CommandProcessor diff --git a/Source/Core/Core/Src/HW/DSP.cpp b/Source/Core/Core/Src/HW/DSP.cpp index 81c480c8de..034e05d918 100644 --- a/Source/Core/Core/Src/HW/DSP.cpp +++ b/Source/Core/Core/Src/HW/DSP.cpp @@ -1,669 +1,669 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -// AID / AUDIO_DMA controls pushing audio out to the SRC and then the speakers. -// The audio DMA pushes audio through a small FIFO 32 bytes at a time, as needed. -// The SRC behind the fifo eats stereo 16-bit data at a sample rate of 32khz, -// that is, 4 bytes at 32 khz, which is 32 bytes at 4 khz. We thereforce schedule an -// event that runs at 4khz, that eats audio from the fifo. Thus, we have homebrew audio. - -// The AID interrupt is set when the fifo STARTS a transfer. It latches address and count -// into internal registers and starts copying. This means that the interrupt handler can simply -// set the registers to where the next buffer is, and start filling it. When the DMA is complete, -// it will automatically relatch and fire a new interrupt. - -// Then there's the DSP... what likely happens is that the fifo-latched-interrupt handler -// kicks off the DSP, requesting it to fill up the just used buffer through the AXList (or -// whatever it might be called in Nintendo games). - -#include "DSP.h" - -#include "../CoreTiming.h" -#include "../Core.h" -#include "CPU.h" -#include "MemoryUtil.h" -#include "Memmap.h" -#include "PeripheralInterface.h" -#include "AudioInterface.h" -#include "../PowerPC/PowerPC.h" -#include "../Plugins/Plugin_DSP.h" - -namespace DSP -{ - -// register offsets -enum -{ - DSP_MAIL_TO_DSP_HI = 0x5000, - DSP_MAIL_TO_DSP_LO = 0x5002, - DSP_MAIL_FROM_DSP_HI = 0x5004, - DSP_MAIL_FROM_DSP_LO = 0x5006, - DSP_CONTROL = 0x500A, - DSP_INTERRUPT_CONTROL = 0x5010, - AUDIO_DMA_START_HI = 0x5030, - AUDIO_DMA_START_LO = 0x5032, - AUDIO_DMA_CONTROL_LEN = 0x5036, - AUDIO_DMA_BYTES_LEFT = 0x503A, - AR_DMA_MMADDR_H = 0x5020, - AR_DMA_MMADDR_L = 0x5022, - AR_DMA_ARADDR_H = 0x5024, - AR_DMA_ARADDR_L = 0x5026, - AR_DMA_CNT_H = 0x5028, - AR_DMA_CNT_L = 0x502A -}; - -// aram size and mask -enum -{ - ARAM_SIZE = 0x01000000, // 16 MB - ARAM_MASK = 0x00FFFFFF, - WII_MASK = 0x017FFFFF, // 24 MB - WII_MEM2 = 0x03FFFFFF // 64 MB -}; - -// UARAMCount -union UARAMCount -{ - u32 Hex; - struct - { - unsigned count : 31; - unsigned dir : 1; - }; -}; - -// UDSPControl -#define DSP_CONTROL_MASK 0x0C07 -union UDSPControl -{ - u16 Hex; - struct - { - unsigned DSPReset : 1; // Write 1 to reset and waits for 0 - unsigned DSPAssertInt : 1; - unsigned DSPHalt : 1; - - unsigned AID : 1; - unsigned AID_mask : 1; - unsigned ARAM : 1; - unsigned ARAM_mask : 1; - unsigned DSP : 1; - unsigned DSP_mask : 1; - - unsigned ARAM_DMAState : 1; // DSPGetDMAStatus() uses this flag - unsigned unk3 : 1; - unsigned DSPInit : 1; // DSPInit() writes to this flag (1 as long as dsp PC is in IROM?) - unsigned pad : 4; - }; -}; - -// DSPState -struct DSPState -{ - u32 IntControl; - UDSPControl DSPControl; - - DSPState() - { - IntControl = 0; - DSPControl.Hex = 0; - } -}; - -// Blocks are 32 bytes. -union UAudioDMAControl -{ - u16 Hex; - struct - { - unsigned NumBlocks : 15; - unsigned Enabled : 1; - }; - - UAudioDMAControl(u16 _Hex = 0) : Hex(_Hex) - {} -}; - -// AudioDMA -struct AudioDMA -{ - u32 SourceAddress; - u32 ReadAddress; - UAudioDMAControl AudioDMAControl; - int BlocksLeft; -}; - -// ARDMA -struct ARDMA -{ - u32 MMAddr; - u32 ARAddr; - UARAMCount Cnt; - bool CntValid[2]; - - ARDMA() - { - MMAddr = 0; - ARAddr = 0; - Cnt.Hex = 0; - CntValid[0] = false; - CntValid[1] = false; - } -}; - - -// STATE_TO_SAVE -u8 *g_ARAM = NULL, *g_MEM2 = NULL; -DSPState g_dspState; -AudioDMA g_audioDMA; -ARDMA g_arDMA; -u16 g_AR_READY_FLAG = 0x01; -u16 g_AR_MODE = 0x43; // 0x23 -> Zelda standard mode (standard ARAM access ??) - // 0x43 -> written by OSAudioInit at the UCode upload (upload UCode) - // 0x63 -> ARCheckSize Mode (access AR-registers ??) or no exception ?? - -void DoState(PointerWrap &p) -{ - if (!Core::GetStartupParameter().bWii) - p.DoArray(g_ARAM, ARAM_SIZE); - p.Do(g_dspState); - p.Do(g_audioDMA); - p.Do(g_arDMA); - p.Do(g_AR_READY_FLAG); - p.Do(g_AR_MODE); -} - - -void UpdateInterrupts(); -void Update_ARAM_DMA(); -void WriteARAM(u8 _iValue, u32 _iAddress); -bool Update_DSP_ReadRegister(); -void Update_DSP_WriteRegister(); - -int et_GenerateDSPInterrupt; - -void GenerateDSPInterrupt_Wrapper(u64 userdata, int cyclesLate) -{ - GenerateDSPInterrupt((DSPInterruptType)(userdata&0xFFFF), (bool)((userdata>>16) & 1)); -} - -void Init() -{ - if (Core::GetStartupParameter().bWii) - { - // On the Wii, ARAM is simply mapped to EXRAM. - g_ARAM = Memory::GetPointer(0x00000000); - g_MEM2 = Memory::GetPointer(0x10000000); - } - else - { - g_ARAM = (u8 *)AllocateMemoryPages(ARAM_SIZE); - } - g_dspState.DSPControl.Hex = 0; - g_dspState.DSPControl.DSPHalt = 1; - et_GenerateDSPInterrupt = CoreTiming::RegisterEvent("DSPint", GenerateDSPInterrupt_Wrapper); -} - -void Shutdown() -{ - if (!Core::GetStartupParameter().bWii) - FreeMemoryPages(g_ARAM, ARAM_SIZE); - g_ARAM = NULL; -} - -void Read16(u16& _uReturnValue, const u32 _iAddress) -{ - // WTF is this check about? DSP is at 5000 TODO remove - if ((_iAddress & 0x6C00) != 0x6c00) - { - if (_iAddress != 0xCC005004) { - LOGV(DSPINTERFACE, 3, "DSPInterface(r16) 0x%08x", _iAddress); - } - - switch (_iAddress & 0xFFFF) - { - // ================================================================================== - // AI_REGS 0x5000+ - // ================================================================================== - case DSP_MAIL_TO_DSP_HI: - _uReturnValue = PluginDSP::DSP_ReadMailboxHigh(true); - return; - - case DSP_MAIL_TO_DSP_LO: - _uReturnValue = PluginDSP::DSP_ReadMailboxLow(true); - return; - - case DSP_MAIL_FROM_DSP_HI: - _uReturnValue = PluginDSP::DSP_ReadMailboxHigh(false); - return; - - case DSP_MAIL_FROM_DSP_LO: - _uReturnValue = PluginDSP::DSP_ReadMailboxLow(false); - return; - - case DSP_CONTROL: - _uReturnValue = (g_dspState.DSPControl.Hex & ~DSP_CONTROL_MASK) | - (PluginDSP::DSP_ReadControlRegister() & DSP_CONTROL_MASK); - return; - - // ================================================================================== - // AR_REGS 0x501x+ - // ================================================================================== - case 0x5012: - _uReturnValue = g_AR_MODE; - return; - - case 0x5016: // ready flag ? - _uReturnValue = g_AR_READY_FLAG; - return; - - case 0x501a: - _uReturnValue = 0x000; - return; - - case AR_DMA_MMADDR_H: _uReturnValue = g_arDMA.MMAddr>>16; return; - case AR_DMA_MMADDR_L: _uReturnValue = g_arDMA.MMAddr&0xFFFF; return; - case AR_DMA_ARADDR_H: _uReturnValue = g_arDMA.ARAddr>>16; return; - case AR_DMA_ARADDR_L: _uReturnValue = g_arDMA.ARAddr&0xFFFF; return; - case AR_DMA_CNT_H: _uReturnValue = g_arDMA.Cnt.Hex>>16; return; - case AR_DMA_CNT_L: _uReturnValue = g_arDMA.Cnt.Hex&0xFFFF; return; - - // ================================================================================== - // DMA_REGS 0x5030+ - // ================================================================================== - case AUDIO_DMA_BYTES_LEFT: - // Hmm. Would be stupid to ask for bytes left. Assume it wants blocks left. - _uReturnValue = g_audioDMA.BlocksLeft; - return; - - case AUDIO_DMA_START_LO: - _uReturnValue = g_audioDMA.SourceAddress & 0xFFFF; - return; - - case AUDIO_DMA_START_HI: - _uReturnValue = g_audioDMA.SourceAddress>>16; - return; - - case AUDIO_DMA_CONTROL_LEN: - _uReturnValue = g_audioDMA.AudioDMAControl.Hex; - return; - - default: - _dbg_assert_(DSPINTERFACE,0); - break; - } - } - else - { - _dbg_assert_(DSPINTERFACE,0); - } - _uReturnValue = 0x000; -} - -void Write16(const u16 _Value, const u32 _Address) -{ - LOGV(DSPINTERFACE, 3, "DSPInterface(w16) 0x%04x 0x%08x", _Value, _Address); - - switch(_Address & 0xFFFF) - { - // ================================================================================== - // DSP Regs 0x5000+ - // ================================================================================== - - case DSP_MAIL_TO_DSP_HI: - PluginDSP::DSP_WriteMailboxHigh(true, _Value); - break; - - case DSP_MAIL_TO_DSP_LO: - PluginDSP::DSP_WriteMailboxLow(true, _Value); - break; - - case DSP_MAIL_FROM_DSP_HI: - _dbg_assert_msg_(DSPINTERFACE, 0, "W16: DSP_MAIL_FROM_DSP_HI"); - break; - - case DSP_MAIL_FROM_DSP_LO: - _dbg_assert_msg_(DSPINTERFACE, 0, "W16: DSP_MAIL_FROM_DSP_LO"); - break; - - // ================================================================================== - // Control Register - // ================================================================================== - case DSP_CONTROL: - { - UDSPControl tmpControl; - tmpControl.Hex = (_Value& ~DSP_CONTROL_MASK) | - (PluginDSP::DSP_WriteControlRegister(_Value) & DSP_CONTROL_MASK); - - // Update DSP related flags - g_dspState.DSPControl.DSPReset = tmpControl.DSPReset; - g_dspState.DSPControl.DSPAssertInt = tmpControl.DSPAssertInt; - g_dspState.DSPControl.DSPHalt = tmpControl.DSPHalt; - g_dspState.DSPControl.DSPInit = tmpControl.DSPInit; - - // Interrupt (mask) - g_dspState.DSPControl.AID_mask = tmpControl.AID_mask; - g_dspState.DSPControl.ARAM_mask = tmpControl.ARAM_mask; - g_dspState.DSPControl.DSP_mask = tmpControl.DSP_mask; - - // Interrupt - if (tmpControl.AID) g_dspState.DSPControl.AID = 0; - if (tmpControl.ARAM) g_dspState.DSPControl.ARAM = 0; - if (tmpControl.DSP) g_dspState.DSPControl.DSP = 0; - - // g_ARAM - g_dspState.DSPControl.ARAM_DMAState = 0; // keep g_ARAM DMA State zero - - // unknown - g_dspState.DSPControl.unk3 = tmpControl.unk3; - g_dspState.DSPControl.pad = tmpControl.pad; - if (g_dspState.DSPControl.pad != 0) - { - LOG(DSPINTERFACE, "DSPInterface(w) g_dspState.DSPControl gets an unknown value"); - CCPU::Break(); - } - - UpdateInterrupts(); - } - break; - - // ================================================================================== - // AR_REGS 0x501x+ - // DMA back and forth between ARAM and RAM - // ================================================================================== - - case 0x5012: - g_AR_MODE = _Value; - break; - - case 0x5016: - g_AR_READY_FLAG = 0x01; // write what ya want we set 0x01 (rdy flag ??) - break; - - case 0x501a: - break; - - case AR_DMA_MMADDR_H: - g_arDMA.MMAddr = (g_arDMA.MMAddr & 0xFFFF) | (_Value<<16); break; - case AR_DMA_MMADDR_L: - g_arDMA.MMAddr = (g_arDMA.MMAddr & 0xFFFF0000) | (_Value); break; - - case AR_DMA_ARADDR_H: - g_arDMA.ARAddr = (g_arDMA.ARAddr & 0xFFFF) | (_Value<<16); break; - case AR_DMA_ARADDR_L: - g_arDMA.ARAddr = (g_arDMA.ARAddr & 0xFFFF0000) | (_Value); break; - - case AR_DMA_CNT_H: - g_arDMA.Cnt.Hex = (g_arDMA.Cnt.Hex & 0xFFFF) | (_Value<<16); - g_arDMA.CntValid[0] = true; - Update_ARAM_DMA(); - break; - - case AR_DMA_CNT_L: - g_arDMA.Cnt.Hex = (g_arDMA.Cnt.Hex & 0xFFFF0000) | (_Value); - g_arDMA.CntValid[1] = true; - Update_ARAM_DMA(); - break; - - // ================================================================================== - // Audio DMA_REGS 0x5030+ - // This is the DMA that goes straight out the speaker. - // ================================================================================== - case AUDIO_DMA_START_HI: - g_audioDMA.SourceAddress = (g_audioDMA.SourceAddress & 0xFFFF) | (_Value<<16); - break; - - case AUDIO_DMA_START_LO: - g_audioDMA.SourceAddress = (g_audioDMA.SourceAddress & 0xFFFF0000) | (_Value); - break; - - case AUDIO_DMA_CONTROL_LEN: // called by AIStartDMA() - { - UAudioDMAControl old_control = g_audioDMA.AudioDMAControl; - g_audioDMA.AudioDMAControl.Hex = _Value; - - if (!old_control.Enabled && g_audioDMA.AudioDMAControl.Enabled) - { - // Enabled bit was flipped to true, let's latch address & length and call the interrupt. - g_audioDMA.BlocksLeft = g_audioDMA.AudioDMAControl.NumBlocks; - g_audioDMA.ReadAddress = g_audioDMA.SourceAddress; - GenerateDSPInterrupt(DSP::INT_AID); - LOG(DSPINTERFACE, "AID DMA started - source address %08x, length %i blocks", g_audioDMA.SourceAddress, g_audioDMA.AudioDMAControl.NumBlocks); - } - break; - } - case AUDIO_DMA_BYTES_LEFT: - _dbg_assert_(DSPINTERFACE,0); - break; - - default: - _dbg_assert_(DSPINTERFACE,0); - break; - } -} - -// This happens at 4 khz, since 32 bytes at 4khz = 4 bytes at 32 khz (16bit stereo pcm) -void UpdateAudioDMA() -{ - if (g_audioDMA.AudioDMAControl.Enabled && g_audioDMA.BlocksLeft) { - // Read audio at g_audioDMA.ReadAddress in RAM and push onto an external audio fifo in the emulator, - // to be mixed with the disc streaming output. If that audio queue fills up, we delay the emulator. - - // TO RESTORE OLD BEHAVIOUR, COMMENT OUT THIS LINE - PluginDSP::DSP_SendAIBuffer(g_audioDMA.ReadAddress, AudioInterface::GetDSPSampleRate()); - - g_audioDMA.ReadAddress += 32; - g_audioDMA.BlocksLeft--; - if (!g_audioDMA.BlocksLeft) { - // No need to turn off the DMA - we can only get here if we had blocks left when we - // entered this function, and no longer have any. - // Latch new parameters - g_audioDMA.BlocksLeft = g_audioDMA.AudioDMAControl.NumBlocks; - g_audioDMA.ReadAddress = g_audioDMA.SourceAddress; - GenerateDSPInterrupt(DSP::INT_AID); - } - } else { - // Send silence. Yeah, it's a bit of a waste to sample rate convert silence. - // or hm. Maybe we shouldn't do this :) - // PluginDSP::DSP_SendAIBuffer(0, AudioInterface::GetDSPSampleRate()); - } -} - -void Read32(u32& _uReturnValue, const u32 _iAddress) -{ - LOG(DSPINTERFACE, "DSPInterface(r) 0x%08x", _iAddress); - switch (_iAddress & 0xFFFF) - { - case DSP_INTERRUPT_CONTROL: - _uReturnValue = g_dspState.IntControl; - return; - - default: - _dbg_assert_(DSPINTERFACE,0); - break; - } - _uReturnValue = 0; -} - -void Write32(const u32 _iValue, const u32 _iAddress) -{ - LOG(DSPINTERFACE, "DSPInterface(w) 0x%08x 0x%08x", _iValue, _iAddress); - - switch (_iAddress & 0xFFFF) - { - // ================================================================================== - // AR_REGS - i dont know why they are accessed 32 bit too ... - // ================================================================================== - - case AR_DMA_MMADDR_H: - g_arDMA.MMAddr = _iValue; - break; - - case AR_DMA_ARADDR_H: - g_arDMA.ARAddr = _iValue; - break; - - case AR_DMA_CNT_H: - g_arDMA.Cnt.Hex = _iValue; - g_arDMA.CntValid[0] = g_arDMA.CntValid[1] = true; - Update_ARAM_DMA(); - break; - - default: - _dbg_assert_(DSPINTERFACE,0); - break; - } -} - -// __________________________________________________________________________________________________ -// UpdateInterrupts -// -void UpdateInterrupts() -{ - if ((g_dspState.DSPControl.AID & g_dspState.DSPControl.AID_mask) || - (g_dspState.DSPControl.ARAM & g_dspState.DSPControl.ARAM_mask) || - (g_dspState.DSPControl.DSP & g_dspState.DSPControl.DSP_mask)) - { - CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_DSP, true); - } - else - { - CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_DSP, false); - } -} - -void GenerateDSPInterrupt(DSPInterruptType type, bool _bSet) -{ - switch (type) - { - case INT_DSP: g_dspState.DSPControl.DSP = _bSet ? 1 : 0; break; - case INT_ARAM: g_dspState.DSPControl.ARAM = _bSet ? 1 : 0; break; - case INT_AID: g_dspState.DSPControl.AID = _bSet ? 1 : 0; break; - } - - UpdateInterrupts(); -} - -// CALLED FROM DSP PLUGIN, POSSIBLY THREADED -void GenerateDSPInterruptFromPlugin(DSPInterruptType type, bool _bSet) -{ - CoreTiming::ScheduleEvent_Threadsafe( - 0, et_GenerateDSPInterrupt, type | (_bSet<<16)); -} - -void Update_ARAM_DMA() -{ - // check if the count reg is valid - if (!g_arDMA.CntValid[0] || !g_arDMA.CntValid[1]) - return; - g_arDMA.CntValid[0] = g_arDMA.CntValid[1] = false; - - LOGV(DSPINTERFACE, 1, "ARAM DMA triggered"); - - //TODO: speedup - if (g_arDMA.Cnt.dir) - { - //read from ARAM - LOGV(DSPINTERFACE, 1, "ARAM DMA read %08x bytes from %08x to Mem: %08x",g_arDMA.Cnt.count, g_arDMA.ARAddr, g_arDMA.MMAddr); - u32 iMemAddress = g_arDMA.MMAddr; - u32 iARAMAddress = g_arDMA.ARAddr; - - // TODO(??): sanity check instead of writing bogus data? - for (u32 i = 0; i < g_arDMA.Cnt.count; i++) - { - u32 tmp = (iARAMAddress < ARAM_SIZE) ? g_ARAM[iARAMAddress] : 0x05050505; - Memory::Write_U8(tmp, iMemAddress); - - iMemAddress++; - iARAMAddress++; - } - } - else - { - u32 iMemAddress = g_arDMA.MMAddr; - u32 iARAMAddress = g_arDMA.ARAddr; - - //write to g_ARAM - LOGV(DSPINTERFACE, 1, "g_ARAM DMA write %08x bytes from %08x to Aram: %08x", - g_arDMA.Cnt.count, g_arDMA.MMAddr, g_arDMA.ARAddr); - for (u32 i = 0; i < g_arDMA.Cnt.count; i++) - { - if (iARAMAddress < ARAM_SIZE) - g_ARAM[iARAMAddress] = Memory::Read_U8(iMemAddress); - - iMemAddress++; - iARAMAddress++; - } - } - - g_arDMA.Cnt.count = 0; - GenerateDSPInterrupt(INT_ARAM); -} - -// ============================================================= -// This is how it works: The game has written sound to RAM, the DSP will read it with -// this function. SamplePos in the plugin is double the value given here because it -// works on a nibble level. In Wii addresses can eather be for MEM1 or MEM2, when it wants -// to read from MEM2 it adds 0x2000000 (in nibble terms) to get up to the MEM2 hardware -// address. But in our case we use a second pointer and adjust the value down to 0x00... -// ------------------- -u8 ReadARAM(u32 _iAddress) -{ - //LOGV(DSPINTERFACE, 0, "ARAM (r) 0x%08x", _iAddress); - -// _dbg_assert_(DSPINTERFACE,(_iAddress) < ARAM_SIZE); - if(Core::GetStartupParameter().bWii) - { - //LOGV(DSPINTERFACE, 0, "ARAM (r) 0x%08x 0x%08x 0x%08x", WII_MASK, _iAddress, (_iAddress & WII_MASK)); - - // Does this make any sense? - if(_iAddress > WII_MASK) - { - if(_iAddress > WII_MEM2) - _iAddress = (_iAddress & WII_MEM2); - return g_MEM2[_iAddress]; - } - else - { - if(_iAddress > WII_MASK) - _iAddress = (_iAddress & WII_MASK); - return g_ARAM[_iAddress]; - } - } - else - return g_ARAM[_iAddress & ARAM_MASK]; -} - -u8* GetARAMPtr() -{ - return g_ARAM; -} - - - -void WriteARAM(u8 _iValue, u32 _iAddress) -{ - //LOGV(DSPINTERFACE, 0, "ARAM (w) 0x%08x = 0x%08x", _iAddress, (_iAddress & ARAM_MASK)); - -// _dbg_assert_(DSPINTERFACE,(_iAddress) < ARAM_SIZE); - //rouge leader writes WAY outside - //not really surprising since it uses a totally different memory model :P - g_ARAM[_iAddress & ARAM_MASK] = _iValue; -} - -} // end of namespace DSP -// =================== +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// AID / AUDIO_DMA controls pushing audio out to the SRC and then the speakers. +// The audio DMA pushes audio through a small FIFO 32 bytes at a time, as needed. +// The SRC behind the fifo eats stereo 16-bit data at a sample rate of 32khz, +// that is, 4 bytes at 32 khz, which is 32 bytes at 4 khz. We thereforce schedule an +// event that runs at 4khz, that eats audio from the fifo. Thus, we have homebrew audio. + +// The AID interrupt is set when the fifo STARTS a transfer. It latches address and count +// into internal registers and starts copying. This means that the interrupt handler can simply +// set the registers to where the next buffer is, and start filling it. When the DMA is complete, +// it will automatically relatch and fire a new interrupt. + +// Then there's the DSP... what likely happens is that the fifo-latched-interrupt handler +// kicks off the DSP, requesting it to fill up the just used buffer through the AXList (or +// whatever it might be called in Nintendo games). + +#include "DSP.h" + +#include "../CoreTiming.h" +#include "../Core.h" +#include "CPU.h" +#include "MemoryUtil.h" +#include "Memmap.h" +#include "PeripheralInterface.h" +#include "AudioInterface.h" +#include "../PowerPC/PowerPC.h" +#include "../Plugins/Plugin_DSP.h" + +namespace DSP +{ + +// register offsets +enum +{ + DSP_MAIL_TO_DSP_HI = 0x5000, + DSP_MAIL_TO_DSP_LO = 0x5002, + DSP_MAIL_FROM_DSP_HI = 0x5004, + DSP_MAIL_FROM_DSP_LO = 0x5006, + DSP_CONTROL = 0x500A, + DSP_INTERRUPT_CONTROL = 0x5010, + AUDIO_DMA_START_HI = 0x5030, + AUDIO_DMA_START_LO = 0x5032, + AUDIO_DMA_CONTROL_LEN = 0x5036, + AUDIO_DMA_BYTES_LEFT = 0x503A, + AR_DMA_MMADDR_H = 0x5020, + AR_DMA_MMADDR_L = 0x5022, + AR_DMA_ARADDR_H = 0x5024, + AR_DMA_ARADDR_L = 0x5026, + AR_DMA_CNT_H = 0x5028, + AR_DMA_CNT_L = 0x502A +}; + +// aram size and mask +enum +{ + ARAM_SIZE = 0x01000000, // 16 MB + ARAM_MASK = 0x00FFFFFF, + WII_MASK = 0x017FFFFF, // 24 MB + WII_MEM2 = 0x03FFFFFF // 64 MB +}; + +// UARAMCount +union UARAMCount +{ + u32 Hex; + struct + { + unsigned count : 31; + unsigned dir : 1; + }; +}; + +// UDSPControl +#define DSP_CONTROL_MASK 0x0C07 +union UDSPControl +{ + u16 Hex; + struct + { + unsigned DSPReset : 1; // Write 1 to reset and waits for 0 + unsigned DSPAssertInt : 1; + unsigned DSPHalt : 1; + + unsigned AID : 1; + unsigned AID_mask : 1; + unsigned ARAM : 1; + unsigned ARAM_mask : 1; + unsigned DSP : 1; + unsigned DSP_mask : 1; + + unsigned ARAM_DMAState : 1; // DSPGetDMAStatus() uses this flag + unsigned unk3 : 1; + unsigned DSPInit : 1; // DSPInit() writes to this flag (1 as long as dsp PC is in IROM?) + unsigned pad : 4; + }; +}; + +// DSPState +struct DSPState +{ + u32 IntControl; + UDSPControl DSPControl; + + DSPState() + { + IntControl = 0; + DSPControl.Hex = 0; + } +}; + +// Blocks are 32 bytes. +union UAudioDMAControl +{ + u16 Hex; + struct + { + unsigned NumBlocks : 15; + unsigned Enabled : 1; + }; + + UAudioDMAControl(u16 _Hex = 0) : Hex(_Hex) + {} +}; + +// AudioDMA +struct AudioDMA +{ + u32 SourceAddress; + u32 ReadAddress; + UAudioDMAControl AudioDMAControl; + int BlocksLeft; +}; + +// ARDMA +struct ARDMA +{ + u32 MMAddr; + u32 ARAddr; + UARAMCount Cnt; + bool CntValid[2]; + + ARDMA() + { + MMAddr = 0; + ARAddr = 0; + Cnt.Hex = 0; + CntValid[0] = false; + CntValid[1] = false; + } +}; + + +// STATE_TO_SAVE +u8 *g_ARAM = NULL, *g_MEM2 = NULL; +DSPState g_dspState; +AudioDMA g_audioDMA; +ARDMA g_arDMA; +u16 g_AR_READY_FLAG = 0x01; +u16 g_AR_MODE = 0x43; // 0x23 -> Zelda standard mode (standard ARAM access ??) + // 0x43 -> written by OSAudioInit at the UCode upload (upload UCode) + // 0x63 -> ARCheckSize Mode (access AR-registers ??) or no exception ?? + +void DoState(PointerWrap &p) +{ + if (!Core::GetStartupParameter().bWii) + p.DoArray(g_ARAM, ARAM_SIZE); + p.Do(g_dspState); + p.Do(g_audioDMA); + p.Do(g_arDMA); + p.Do(g_AR_READY_FLAG); + p.Do(g_AR_MODE); +} + + +void UpdateInterrupts(); +void Update_ARAM_DMA(); +void WriteARAM(u8 _iValue, u32 _iAddress); +bool Update_DSP_ReadRegister(); +void Update_DSP_WriteRegister(); + +int et_GenerateDSPInterrupt; + +void GenerateDSPInterrupt_Wrapper(u64 userdata, int cyclesLate) +{ + GenerateDSPInterrupt((DSPInterruptType)(userdata&0xFFFF), (bool)((userdata>>16) & 1)); +} + +void Init() +{ + if (Core::GetStartupParameter().bWii) + { + // On the Wii, ARAM is simply mapped to EXRAM. + g_ARAM = Memory::GetPointer(0x00000000); + g_MEM2 = Memory::GetPointer(0x10000000); + } + else + { + g_ARAM = (u8 *)AllocateMemoryPages(ARAM_SIZE); + } + g_dspState.DSPControl.Hex = 0; + g_dspState.DSPControl.DSPHalt = 1; + et_GenerateDSPInterrupt = CoreTiming::RegisterEvent("DSPint", GenerateDSPInterrupt_Wrapper); +} + +void Shutdown() +{ + if (!Core::GetStartupParameter().bWii) + FreeMemoryPages(g_ARAM, ARAM_SIZE); + g_ARAM = NULL; +} + +void Read16(u16& _uReturnValue, const u32 _iAddress) +{ + // WTF is this check about? DSP is at 5000 TODO remove + if ((_iAddress & 0x6C00) != 0x6c00) + { + if (_iAddress != 0xCC005004) { + LOGV(DSPINTERFACE, 3, "DSPInterface(r16) 0x%08x", _iAddress); + } + + switch (_iAddress & 0xFFFF) + { + // ================================================================================== + // AI_REGS 0x5000+ + // ================================================================================== + case DSP_MAIL_TO_DSP_HI: + _uReturnValue = PluginDSP::DSP_ReadMailboxHigh(true); + return; + + case DSP_MAIL_TO_DSP_LO: + _uReturnValue = PluginDSP::DSP_ReadMailboxLow(true); + return; + + case DSP_MAIL_FROM_DSP_HI: + _uReturnValue = PluginDSP::DSP_ReadMailboxHigh(false); + return; + + case DSP_MAIL_FROM_DSP_LO: + _uReturnValue = PluginDSP::DSP_ReadMailboxLow(false); + return; + + case DSP_CONTROL: + _uReturnValue = (g_dspState.DSPControl.Hex & ~DSP_CONTROL_MASK) | + (PluginDSP::DSP_ReadControlRegister() & DSP_CONTROL_MASK); + return; + + // ================================================================================== + // AR_REGS 0x501x+ + // ================================================================================== + case 0x5012: + _uReturnValue = g_AR_MODE; + return; + + case 0x5016: // ready flag ? + _uReturnValue = g_AR_READY_FLAG; + return; + + case 0x501a: + _uReturnValue = 0x000; + return; + + case AR_DMA_MMADDR_H: _uReturnValue = g_arDMA.MMAddr>>16; return; + case AR_DMA_MMADDR_L: _uReturnValue = g_arDMA.MMAddr&0xFFFF; return; + case AR_DMA_ARADDR_H: _uReturnValue = g_arDMA.ARAddr>>16; return; + case AR_DMA_ARADDR_L: _uReturnValue = g_arDMA.ARAddr&0xFFFF; return; + case AR_DMA_CNT_H: _uReturnValue = g_arDMA.Cnt.Hex>>16; return; + case AR_DMA_CNT_L: _uReturnValue = g_arDMA.Cnt.Hex&0xFFFF; return; + + // ================================================================================== + // DMA_REGS 0x5030+ + // ================================================================================== + case AUDIO_DMA_BYTES_LEFT: + // Hmm. Would be stupid to ask for bytes left. Assume it wants blocks left. + _uReturnValue = g_audioDMA.BlocksLeft; + return; + + case AUDIO_DMA_START_LO: + _uReturnValue = g_audioDMA.SourceAddress & 0xFFFF; + return; + + case AUDIO_DMA_START_HI: + _uReturnValue = g_audioDMA.SourceAddress>>16; + return; + + case AUDIO_DMA_CONTROL_LEN: + _uReturnValue = g_audioDMA.AudioDMAControl.Hex; + return; + + default: + _dbg_assert_(DSPINTERFACE,0); + break; + } + } + else + { + _dbg_assert_(DSPINTERFACE,0); + } + _uReturnValue = 0x000; +} + +void Write16(const u16 _Value, const u32 _Address) +{ + LOGV(DSPINTERFACE, 3, "DSPInterface(w16) 0x%04x 0x%08x", _Value, _Address); + + switch(_Address & 0xFFFF) + { + // ================================================================================== + // DSP Regs 0x5000+ + // ================================================================================== + + case DSP_MAIL_TO_DSP_HI: + PluginDSP::DSP_WriteMailboxHigh(true, _Value); + break; + + case DSP_MAIL_TO_DSP_LO: + PluginDSP::DSP_WriteMailboxLow(true, _Value); + break; + + case DSP_MAIL_FROM_DSP_HI: + _dbg_assert_msg_(DSPINTERFACE, 0, "W16: DSP_MAIL_FROM_DSP_HI"); + break; + + case DSP_MAIL_FROM_DSP_LO: + _dbg_assert_msg_(DSPINTERFACE, 0, "W16: DSP_MAIL_FROM_DSP_LO"); + break; + + // ================================================================================== + // Control Register + // ================================================================================== + case DSP_CONTROL: + { + UDSPControl tmpControl; + tmpControl.Hex = (_Value& ~DSP_CONTROL_MASK) | + (PluginDSP::DSP_WriteControlRegister(_Value) & DSP_CONTROL_MASK); + + // Update DSP related flags + g_dspState.DSPControl.DSPReset = tmpControl.DSPReset; + g_dspState.DSPControl.DSPAssertInt = tmpControl.DSPAssertInt; + g_dspState.DSPControl.DSPHalt = tmpControl.DSPHalt; + g_dspState.DSPControl.DSPInit = tmpControl.DSPInit; + + // Interrupt (mask) + g_dspState.DSPControl.AID_mask = tmpControl.AID_mask; + g_dspState.DSPControl.ARAM_mask = tmpControl.ARAM_mask; + g_dspState.DSPControl.DSP_mask = tmpControl.DSP_mask; + + // Interrupt + if (tmpControl.AID) g_dspState.DSPControl.AID = 0; + if (tmpControl.ARAM) g_dspState.DSPControl.ARAM = 0; + if (tmpControl.DSP) g_dspState.DSPControl.DSP = 0; + + // g_ARAM + g_dspState.DSPControl.ARAM_DMAState = 0; // keep g_ARAM DMA State zero + + // unknown + g_dspState.DSPControl.unk3 = tmpControl.unk3; + g_dspState.DSPControl.pad = tmpControl.pad; + if (g_dspState.DSPControl.pad != 0) + { + LOG(DSPINTERFACE, "DSPInterface(w) g_dspState.DSPControl gets an unknown value"); + CCPU::Break(); + } + + UpdateInterrupts(); + } + break; + + // ================================================================================== + // AR_REGS 0x501x+ + // DMA back and forth between ARAM and RAM + // ================================================================================== + + case 0x5012: + g_AR_MODE = _Value; + break; + + case 0x5016: + g_AR_READY_FLAG = 0x01; // write what ya want we set 0x01 (rdy flag ??) + break; + + case 0x501a: + break; + + case AR_DMA_MMADDR_H: + g_arDMA.MMAddr = (g_arDMA.MMAddr & 0xFFFF) | (_Value<<16); break; + case AR_DMA_MMADDR_L: + g_arDMA.MMAddr = (g_arDMA.MMAddr & 0xFFFF0000) | (_Value); break; + + case AR_DMA_ARADDR_H: + g_arDMA.ARAddr = (g_arDMA.ARAddr & 0xFFFF) | (_Value<<16); break; + case AR_DMA_ARADDR_L: + g_arDMA.ARAddr = (g_arDMA.ARAddr & 0xFFFF0000) | (_Value); break; + + case AR_DMA_CNT_H: + g_arDMA.Cnt.Hex = (g_arDMA.Cnt.Hex & 0xFFFF) | (_Value<<16); + g_arDMA.CntValid[0] = true; + Update_ARAM_DMA(); + break; + + case AR_DMA_CNT_L: + g_arDMA.Cnt.Hex = (g_arDMA.Cnt.Hex & 0xFFFF0000) | (_Value); + g_arDMA.CntValid[1] = true; + Update_ARAM_DMA(); + break; + + // ================================================================================== + // Audio DMA_REGS 0x5030+ + // This is the DMA that goes straight out the speaker. + // ================================================================================== + case AUDIO_DMA_START_HI: + g_audioDMA.SourceAddress = (g_audioDMA.SourceAddress & 0xFFFF) | (_Value<<16); + break; + + case AUDIO_DMA_START_LO: + g_audioDMA.SourceAddress = (g_audioDMA.SourceAddress & 0xFFFF0000) | (_Value); + break; + + case AUDIO_DMA_CONTROL_LEN: // called by AIStartDMA() + { + UAudioDMAControl old_control = g_audioDMA.AudioDMAControl; + g_audioDMA.AudioDMAControl.Hex = _Value; + + if (!old_control.Enabled && g_audioDMA.AudioDMAControl.Enabled) + { + // Enabled bit was flipped to true, let's latch address & length and call the interrupt. + g_audioDMA.BlocksLeft = g_audioDMA.AudioDMAControl.NumBlocks; + g_audioDMA.ReadAddress = g_audioDMA.SourceAddress; + GenerateDSPInterrupt(DSP::INT_AID); + LOG(DSPINTERFACE, "AID DMA started - source address %08x, length %i blocks", g_audioDMA.SourceAddress, g_audioDMA.AudioDMAControl.NumBlocks); + } + break; + } + case AUDIO_DMA_BYTES_LEFT: + _dbg_assert_(DSPINTERFACE,0); + break; + + default: + _dbg_assert_(DSPINTERFACE,0); + break; + } +} + +// This happens at 4 khz, since 32 bytes at 4khz = 4 bytes at 32 khz (16bit stereo pcm) +void UpdateAudioDMA() +{ + if (g_audioDMA.AudioDMAControl.Enabled && g_audioDMA.BlocksLeft) { + // Read audio at g_audioDMA.ReadAddress in RAM and push onto an external audio fifo in the emulator, + // to be mixed with the disc streaming output. If that audio queue fills up, we delay the emulator. + + // TO RESTORE OLD BEHAVIOUR, COMMENT OUT THIS LINE + PluginDSP::DSP_SendAIBuffer(g_audioDMA.ReadAddress, AudioInterface::GetDSPSampleRate()); + + g_audioDMA.ReadAddress += 32; + g_audioDMA.BlocksLeft--; + if (!g_audioDMA.BlocksLeft) { + // No need to turn off the DMA - we can only get here if we had blocks left when we + // entered this function, and no longer have any. + // Latch new parameters + g_audioDMA.BlocksLeft = g_audioDMA.AudioDMAControl.NumBlocks; + g_audioDMA.ReadAddress = g_audioDMA.SourceAddress; + GenerateDSPInterrupt(DSP::INT_AID); + } + } else { + // Send silence. Yeah, it's a bit of a waste to sample rate convert silence. + // or hm. Maybe we shouldn't do this :) + // PluginDSP::DSP_SendAIBuffer(0, AudioInterface::GetDSPSampleRate()); + } +} + +void Read32(u32& _uReturnValue, const u32 _iAddress) +{ + LOG(DSPINTERFACE, "DSPInterface(r) 0x%08x", _iAddress); + switch (_iAddress & 0xFFFF) + { + case DSP_INTERRUPT_CONTROL: + _uReturnValue = g_dspState.IntControl; + return; + + default: + _dbg_assert_(DSPINTERFACE,0); + break; + } + _uReturnValue = 0; +} + +void Write32(const u32 _iValue, const u32 _iAddress) +{ + LOG(DSPINTERFACE, "DSPInterface(w) 0x%08x 0x%08x", _iValue, _iAddress); + + switch (_iAddress & 0xFFFF) + { + // ================================================================================== + // AR_REGS - i dont know why they are accessed 32 bit too ... + // ================================================================================== + + case AR_DMA_MMADDR_H: + g_arDMA.MMAddr = _iValue; + break; + + case AR_DMA_ARADDR_H: + g_arDMA.ARAddr = _iValue; + break; + + case AR_DMA_CNT_H: + g_arDMA.Cnt.Hex = _iValue; + g_arDMA.CntValid[0] = g_arDMA.CntValid[1] = true; + Update_ARAM_DMA(); + break; + + default: + _dbg_assert_(DSPINTERFACE,0); + break; + } +} + +// __________________________________________________________________________________________________ +// UpdateInterrupts +// +void UpdateInterrupts() +{ + if ((g_dspState.DSPControl.AID & g_dspState.DSPControl.AID_mask) || + (g_dspState.DSPControl.ARAM & g_dspState.DSPControl.ARAM_mask) || + (g_dspState.DSPControl.DSP & g_dspState.DSPControl.DSP_mask)) + { + CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_DSP, true); + } + else + { + CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_DSP, false); + } +} + +void GenerateDSPInterrupt(DSPInterruptType type, bool _bSet) +{ + switch (type) + { + case INT_DSP: g_dspState.DSPControl.DSP = _bSet ? 1 : 0; break; + case INT_ARAM: g_dspState.DSPControl.ARAM = _bSet ? 1 : 0; break; + case INT_AID: g_dspState.DSPControl.AID = _bSet ? 1 : 0; break; + } + + UpdateInterrupts(); +} + +// CALLED FROM DSP PLUGIN, POSSIBLY THREADED +void GenerateDSPInterruptFromPlugin(DSPInterruptType type, bool _bSet) +{ + CoreTiming::ScheduleEvent_Threadsafe( + 0, et_GenerateDSPInterrupt, type | (_bSet<<16)); +} + +void Update_ARAM_DMA() +{ + // check if the count reg is valid + if (!g_arDMA.CntValid[0] || !g_arDMA.CntValid[1]) + return; + g_arDMA.CntValid[0] = g_arDMA.CntValid[1] = false; + + LOGV(DSPINTERFACE, 1, "ARAM DMA triggered"); + + //TODO: speedup + if (g_arDMA.Cnt.dir) + { + //read from ARAM + LOGV(DSPINTERFACE, 1, "ARAM DMA read %08x bytes from %08x to Mem: %08x",g_arDMA.Cnt.count, g_arDMA.ARAddr, g_arDMA.MMAddr); + u32 iMemAddress = g_arDMA.MMAddr; + u32 iARAMAddress = g_arDMA.ARAddr; + + // TODO(??): sanity check instead of writing bogus data? + for (u32 i = 0; i < g_arDMA.Cnt.count; i++) + { + u32 tmp = (iARAMAddress < ARAM_SIZE) ? g_ARAM[iARAMAddress] : 0x05050505; + Memory::Write_U8(tmp, iMemAddress); + + iMemAddress++; + iARAMAddress++; + } + } + else + { + u32 iMemAddress = g_arDMA.MMAddr; + u32 iARAMAddress = g_arDMA.ARAddr; + + //write to g_ARAM + LOGV(DSPINTERFACE, 1, "g_ARAM DMA write %08x bytes from %08x to Aram: %08x", + g_arDMA.Cnt.count, g_arDMA.MMAddr, g_arDMA.ARAddr); + for (u32 i = 0; i < g_arDMA.Cnt.count; i++) + { + if (iARAMAddress < ARAM_SIZE) + g_ARAM[iARAMAddress] = Memory::Read_U8(iMemAddress); + + iMemAddress++; + iARAMAddress++; + } + } + + g_arDMA.Cnt.count = 0; + GenerateDSPInterrupt(INT_ARAM); +} + +// ============================================================= +// This is how it works: The game has written sound to RAM, the DSP will read it with +// this function. SamplePos in the plugin is double the value given here because it +// works on a nibble level. In Wii addresses can eather be for MEM1 or MEM2, when it wants +// to read from MEM2 it adds 0x2000000 (in nibble terms) to get up to the MEM2 hardware +// address. But in our case we use a second pointer and adjust the value down to 0x00... +// ------------------- +u8 ReadARAM(u32 _iAddress) +{ + //LOGV(DSPINTERFACE, 0, "ARAM (r) 0x%08x", _iAddress); + +// _dbg_assert_(DSPINTERFACE,(_iAddress) < ARAM_SIZE); + if(Core::GetStartupParameter().bWii) + { + //LOGV(DSPINTERFACE, 0, "ARAM (r) 0x%08x 0x%08x 0x%08x", WII_MASK, _iAddress, (_iAddress & WII_MASK)); + + // Does this make any sense? + if(_iAddress > WII_MASK) + { + if(_iAddress > WII_MEM2) + _iAddress = (_iAddress & WII_MEM2); + return g_MEM2[_iAddress]; + } + else + { + if(_iAddress > WII_MASK) + _iAddress = (_iAddress & WII_MASK); + return g_ARAM[_iAddress]; + } + } + else + return g_ARAM[_iAddress & ARAM_MASK]; +} + +u8* GetARAMPtr() +{ + return g_ARAM; +} + + + +void WriteARAM(u8 _iValue, u32 _iAddress) +{ + //LOGV(DSPINTERFACE, 0, "ARAM (w) 0x%08x = 0x%08x", _iAddress, (_iAddress & ARAM_MASK)); + +// _dbg_assert_(DSPINTERFACE,(_iAddress) < ARAM_SIZE); + //rouge leader writes WAY outside + //not really surprising since it uses a totally different memory model :P + g_ARAM[_iAddress & ARAM_MASK] = _iValue; +} + +} // end of namespace DSP +// =================== diff --git a/Source/Core/Core/Src/HW/DVDInterface.cpp b/Source/Core/Core/Src/HW/DVDInterface.cpp index 9ce148d468..e112ea4c7d 100644 --- a/Source/Core/Core/Src/HW/DVDInterface.cpp +++ b/Source/Core/Core/Src/HW/DVDInterface.cpp @@ -1,599 +1,599 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "ChunkFile.h" - -#include "StreamADPCM.H" -#include "DVDInterface.h" - -#include "../PowerPC/PowerPC.h" -#include "PeripheralInterface.h" -#include "Memmap.h" -#include "Thread.h" - -#include "../VolumeHandler.h" - -namespace DVDInterface -{ - -/* -20975: 00000000 DVD (zzz_80146b84 ??, 0x80146bf8) : DVD(r): 0xcc006004 -20976: 00000000 DVD (zzz_80146b84 ??, 0x80146c00) : DVD(w): 0x00000000 @ 0xcc006004 -20977: 00000000 DVD (DVDLowRead, 0x801448a8) : DVD(w): 0x00000020 @ 0xcc006018 -20978: 00000000 DVD (Read, 0x80144744) : DVD(w): 0xa8000000 @ 0xcc006008 -20979: 00000000 DVD (Read, 0x80144750) : DVD(w): 0x01094227 @ 0xcc00600c -20980: 00000000 DVD (Read, 0x80144758) : DVD(w): 0x00000020 @ 0xcc006010 -20981: 00000000 DVD (Read, 0x8014475c) : DVD(w): 0x8167cc80 @ 0xcc006014 -20982: 00000000 DVD (Read, 0x80144760) : DVD(w): 0x00000020 @ 0xcc006018 -20983: 00000000 DVD (Read, 0x80144768) : DVD(w): 0x00000003 @ 0xcc00601c -20984: 00000000 DVD: DVD: Read ISO: DVDOffset=0425089c, DMABuffer=0167cc80, SrcLength=00000020, DMALength=00000020 -20989: 00000000 DVD (zzz_801442fc ??, 0x80144388) : DVD(r): 0xcc006000 -20990: 00000000 DVD (zzz_801442fc ??, 0x801443d8) : DVD(w): 0x0000003a @ 0xcc006000 -20992: 00000000 DVD (zzz_801442fc ??, 0x801444d0) : DVD(w): 0x00000000 @ 0xcc006004 -20993: 00000000 DVD (zzz_80146e44 ??, 0x80146fcc) : DVD(r): 0xcc006018 - -After this, Cubivore infinitely calls DVDGetDriveStatus, which does not even -bother to check any DVD regs. Waiting for interrupt? -*/ - -// internal hardware addresses -enum -{ - DI_STATUS_REGISTER = 0x00, - DI_COVER_REGISTER = 0x04, - DI_COMMAND_0 = 0x08, - DI_COMMAND_1 = 0x0C, - DI_COMMAND_2 = 0x10, - DI_DMA_ADDRESS_REGISTER = 0x14, - DI_DMA_LENGTH_REGISTER = 0x18, - DI_DMA_CONTROL_REGISTER = 0x1C, - DI_IMMEDIATE_DATA_BUFFER = 0x20, - DI_CONFIG_REGISTER = 0x24 -}; - - -// DVD IntteruptTypes -enum DVDInterruptType -{ - INT_DEINT = 0, - INT_TCINT = 1, - INT_BRKINT = 2, - INT_CVRINT = 3, -}; - -// DI Status Register -union UDISR -{ - u32 Hex; - struct - { - unsigned BREAK : 1; // Stop the Device + Interrupt - unsigned DEINITMASK : 1; // Access Device Error Int Mask - unsigned DEINT : 1; // Access Device Error Int - unsigned TCINTMASK : 1; // Transfer Complete Int Mask - unsigned TCINT : 1; // Transfer Complete Int - unsigned BRKINTMASK : 1; - unsigned BRKINT : 1; // w 1: clear brkint - unsigned : 25; - }; - UDISR() {Hex = 0;} - UDISR(u32 _hex) {Hex = _hex;} -}; - -// DI Cover Register -union UDICVR -{ - u32 Hex; - struct - { - unsigned CVR : 1; // 0: Cover closed 1: Cover open - unsigned CVRINTMASK : 1; // 1: Interrupt enabled; - unsigned CVRINT : 1; // 1: Interrupt clear - unsigned : 29; - }; - UDICVR() {Hex = 0;} - UDICVR(u32 _hex) {Hex = _hex;} -}; - -// DI DMA Address Register -union UDIDMAAddressRegister -{ - u32 Hex; - struct - { - unsigned Address : 26; - unsigned : 6; - }; -}; - -// DI DMA Address Length Register -union UDIDMAAddressLength -{ - u32 Hex; - struct - { - unsigned Length : 26; - unsigned : 6; - }; -}; - -// DI DMA Control Register -union UDIDMAControlRegister -{ - u32 Hex; - struct - { - unsigned TSTART : 1; // w:1 start r:0 ready - unsigned DMA : 1; // 1: DMA Mode 0: Immediate Mode (can only do Access Register Command) - unsigned RW : 1; // 0: Read Command (DVD to Memory) 1: Write COmmand (Memory to DVD) - unsigned : 29; - }; -}; - -// DI Config Register -union UDIConfigRegister -{ - u32 Hex; - struct - { - unsigned CONFIG : 8; - unsigned : 24; - }; - UDIConfigRegister() {Hex = 0;} - UDIConfigRegister(u32 _hex) {Hex = _hex;} -}; - -// hardware registers -struct DVDMemStruct -{ - UDISR StatusReg; - UDICVR CoverReg; - u32 Command[3]; - UDIDMAAddressRegister DMAAddress; - UDIDMAAddressLength DMALength; - UDIDMAControlRegister DMAControlReg; - u32 Immediate; - UDIConfigRegister ConfigReg; - u32 AudioStart; - u32 AudioPos; - u32 AudioLength; -}; - -// STATE_TO_SAVE -DVDMemStruct dvdMem; -u32 g_ErrorCode = 0x00; -bool g_bDiscInside = true; - -Common::CriticalSection dvdread_section; - -void DoState(PointerWrap &p) -{ - p.Do(dvdMem); - p.Do(g_ErrorCode); - p.Do(g_bDiscInside); -} - -void UpdateInterrupts(); -void GenerateDVDInterrupt(DVDInterruptType _DVDInterrupt); -void ExecuteCommand(UDIDMAControlRegister& _DMAControlReg); - -void Init() -{ - dvdMem.StatusReg.Hex = 0; - dvdMem.CoverReg.Hex = 0; - dvdMem.Command[0] = 0; - dvdMem.Command[1] = 0; - dvdMem.Command[2] = 0; - dvdMem.DMAAddress.Hex = 0; - dvdMem.DMALength.Hex = 0; - dvdMem.DMAControlReg.Hex = 0; - dvdMem.Immediate = 0; - dvdMem.ConfigReg.Hex = 0; - dvdMem.AudioStart = 0; - dvdMem.AudioPos = 0; - dvdMem.AudioLength = 0; - -// SetLidOpen(true); -} - -void Shutdown() -{ - -} - -void SetDiscInside(bool _DiscInside) -{ - g_bDiscInside = _DiscInside; -} - -void SetLidOpen(bool _bOpen) -{ - if (_bOpen) - dvdMem.CoverReg.CVR = 1; - else - dvdMem.CoverReg.CVR = 0; - - UpdateInterrupts(); -} - -bool IsLidOpen() -{ - if (dvdMem.CoverReg.CVR) - return true; - else - return false; -} - -bool DVDRead(u32 _iDVDOffset, u32 _iRamAddress, u32 _iLength) -{ - // We won't need the crit sec when DTK streaming has been rewritten correctly. - dvdread_section.Enter(); - bool retval = VolumeHandler::ReadToPtr(Memory::GetPointer(_iRamAddress), _iDVDOffset, _iLength); - dvdread_section.Leave(); - return retval; -} - -bool DVDReadADPCM(u8* _pDestBuffer, u32 _iNumSamples) -{ - if (dvdMem.AudioPos == 0) - { - //MessageBox(0,"DVD: Trying to stream from 0", "bah", 0); - memset(_pDestBuffer, 0, _iNumSamples); // probably __AI_SRC_INIT :P - return false; - } - _iNumSamples &= ~31; - dvdread_section.Enter(); - VolumeHandler::ReadToPtr(_pDestBuffer, dvdMem.AudioPos, _iNumSamples); - dvdread_section.Leave(); - - // - // FIX THIS - // - // loop check - // - dvdMem.AudioPos += _iNumSamples; - if (dvdMem.AudioPos >= dvdMem.AudioStart + dvdMem.AudioLength) - { - dvdMem.AudioPos = dvdMem.AudioStart; - NGCADPCM::InitFilter(); - } - - //LOG(DVDINTERFACE,"ReadADPCM"); - return true; -} - -void Read32(u32& _uReturnValue, const u32 _iAddress) -{ - LOGV(DVDINTERFACE, 3, "DVD(r): 0x%08x", _iAddress); - - switch (_iAddress & 0xFFF) - { - case DI_STATUS_REGISTER: _uReturnValue = dvdMem.StatusReg.Hex; return; - case DI_COVER_REGISTER: _uReturnValue = dvdMem.CoverReg.Hex; return; - case DI_COMMAND_0: _uReturnValue = dvdMem.Command[0]; return; - case DI_COMMAND_1: _uReturnValue = dvdMem.Command[1]; return; - case DI_COMMAND_2: _uReturnValue = dvdMem.Command[2]; return; - case DI_DMA_ADDRESS_REGISTER: _uReturnValue = dvdMem.DMAAddress.Hex; return; - case DI_DMA_LENGTH_REGISTER: _uReturnValue = dvdMem.DMALength.Hex; return; - case DI_DMA_CONTROL_REGISTER: _uReturnValue = dvdMem.DMAControlReg.Hex; return; - case DI_IMMEDIATE_DATA_BUFFER: _uReturnValue = dvdMem.Immediate; return; - case DI_CONFIG_REGISTER: - { - dvdMem.ConfigReg.Hex = 0x000000FF; - _uReturnValue = dvdMem.ConfigReg.Hex; - return; - } - - default: - _dbg_assert_(DVDINTERFACE,0); - } - - _uReturnValue = 0; -} - -void Write32(const u32 _iValue, const u32 _iAddress) -{ - LOGV(DVDINTERFACE, 3, "DVD(w): 0x%08x @ 0x%08x", _iValue, _iAddress); - - switch (_iAddress & 0x3FF) - { - case DI_STATUS_REGISTER: - { - UDISR tmpStatusReg(_iValue); - - dvdMem.StatusReg.DEINITMASK = tmpStatusReg.DEINITMASK; - dvdMem.StatusReg.TCINTMASK = tmpStatusReg.TCINTMASK; - dvdMem.StatusReg.BRKINTMASK = tmpStatusReg.BRKINTMASK; - - if (tmpStatusReg.DEINT) dvdMem.StatusReg.DEINT = 0; - if (tmpStatusReg.TCINT) dvdMem.StatusReg.TCINT = 0; - if (tmpStatusReg.BRKINT) dvdMem.StatusReg.BRKINT = 0; - - if (tmpStatusReg.BREAK) - { - _dbg_assert_(DVDINTERFACE, 0); - } - - UpdateInterrupts(); - } - break; - - case DI_COVER_REGISTER: - { - UDICVR tmpCoverReg(_iValue); - - dvdMem.CoverReg.CVR = 0; - dvdMem.CoverReg.CVRINTMASK = tmpCoverReg.CVRINTMASK; - if (tmpCoverReg.CVRINT) dvdMem.CoverReg.CVRINT = 0; - - UpdateInterrupts(); - - _dbg_assert_(DVDINTERFACE, (tmpCoverReg.CVR == 0)); - } - break; - - case DI_COMMAND_0: dvdMem.Command[0] = _iValue; break; - case DI_COMMAND_1: dvdMem.Command[1] = _iValue; break; - case DI_COMMAND_2: dvdMem.Command[2] = _iValue; break; - - case DI_DMA_ADDRESS_REGISTER: - { - dvdMem.DMAAddress.Hex = _iValue; - _dbg_assert_(DVDINTERFACE, ((dvdMem.DMAAddress.Hex & 0x1F) == 0)); - } - break; - case DI_DMA_LENGTH_REGISTER: dvdMem.DMALength.Hex = _iValue; break; - case DI_DMA_CONTROL_REGISTER: - { - dvdMem.DMAControlReg.Hex = _iValue; - if (dvdMem.DMAControlReg.TSTART) - { - ExecuteCommand(dvdMem.DMAControlReg); - } - } - break; - - case DI_IMMEDIATE_DATA_BUFFER: dvdMem.Immediate = _iValue; break; - case DI_CONFIG_REGISTER: - { - UDIConfigRegister tmpConfigReg(_iValue); - - dvdMem.ConfigReg.CONFIG = tmpConfigReg.CONFIG; - } - break; - - default: - _dbg_assert_msg_(DVDINTERFACE, 0, "Write to unknown DI address 0x%08x", _iAddress); - break; - } -} - -void UpdateInterrupts() -{ - if ((dvdMem.StatusReg.DEINT & dvdMem.StatusReg.DEINITMASK) || - (dvdMem.StatusReg.TCINT & dvdMem.StatusReg.TCINTMASK) || - (dvdMem.StatusReg.BRKINT & dvdMem.StatusReg.BRKINTMASK) || - (dvdMem.CoverReg.CVRINT & dvdMem.CoverReg.CVRINTMASK)) - { - CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_DI, true); - } - else - { - CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_DI, false); - } -} - -void GenerateDVDInterrupt(DVDInterruptType _DVDInterrupt) -{ - switch(_DVDInterrupt) - { - case INT_DEINT: dvdMem.StatusReg.DEINT = 1; break; - case INT_TCINT: dvdMem.StatusReg.TCINT = 1; break; - case INT_BRKINT: dvdMem.StatusReg.BRKINT = 1; break; - case INT_CVRINT: dvdMem.CoverReg.CVRINT = 1; break; - } - - UpdateInterrupts(); -} - -bool m_bStream = false; - -void ExecuteCommand(UDIDMAControlRegister& _DMAControlReg) -{ - _dbg_assert_(DVDINTERFACE, _DMAControlReg.RW == 0); // only DVD to Memory - - switch ((dvdMem.Command[0] & 0xFF000000) >> 24) - { - //========================================================================================================= - // DRIVE INFO (DMA) - // Command/Subcommand/Padding <- 12000000 - // Command0 <- 0 - // Command1 <- 0x20 - // Command2 <- Address in ram of the buffer - // - // output buffer: - // 0000-0001 revisionLevel - // 0002-0003 deviceCode - // 0004-0007 releaseDate - // 0008-001F padding(0) - //========================================================================================================= - case 0x12: - { -#ifdef LOGGING - u32 offset = dvdMem.Command[1]; - // u32 sourcelength = dvdMem.Command[2]; -#endif - u32 destbuffer = dvdMem.DMAAddress.Address; - u32 destlength = dvdMem.DMALength.Length; - dvdMem.DMALength.Length = 0; - - LOG(DVDINTERFACE, "[WARNING] DVD: Get drive info offset=%08x, destbuffer=%08x, destlength=%08x", offset * 4, destbuffer, destlength); - - // metroid uses this... - for (unsigned int i = 0; i < destlength / 4; i++) - { - Memory::Write_U32(0, destbuffer + i * 4); - } - } - break; - - //========================================================================================================= - // READ (DMA) - // Command/Subcommand/Padding <- A8000000 - // Command0 <- Position on DVD shr 2 - // Command1 <- Length of the read - // Command2 <- Address in ram of the buffer - //========================================================================================================= - case 0xA8: - { - if (g_bDiscInside) - { - u32 iDVDOffset = dvdMem.Command[1] << 2; - u32 iSrcLength = dvdMem.Command[2]; - if (false) { iSrcLength++; } // avoid warning << wtf is this? - LOGV(DVDINTERFACE, 2, "DVD: Read ISO: DVDOffset=%08x, DMABuffer=%08x, SrcLength=%08x, DMALength=%08x",iDVDOffset,dvdMem.DMAAddress.Address,iSrcLength,dvdMem.DMALength.Length); - _dbg_assert_(DVDINTERFACE, iSrcLength == dvdMem.DMALength.Length); - - if (DVDRead(iDVDOffset, dvdMem.DMAAddress.Address, dvdMem.DMALength.Length) != true) - { - PanicAlert("Cant read from DVD_Plugin - DVD-Interface: Fatal Error"); - } - } - else - { - // there is no disc to read - GenerateDVDInterrupt(INT_DEINT); - g_ErrorCode = 0x03023A00; - return; - } - } - break; - - //========================================================================================================= - // SEEK (Immediate) - // Command/Subcommand/Padding <- AB000000 - // Command0 <- Position on DVD shr 2 - //========================================================================================================= - case 0xAB: - { -#ifdef LOGGING - u32 offset = dvdMem.Command[1] << 2; -#endif - LOG(DVDINTERFACE, "DVD: Trying to seek: offset=%08x", offset); - } - break; - - //========================================================================================================= - // REQUEST ERROR (Immediate) - // Command/Subcommand/Padding <- E0000000 - //========================================================================================================= - case 0xE0: - LOG(DVDINTERFACE, "DVD: Requesting error"); - dvdMem.Immediate = g_ErrorCode; - break; - - //========================================================================================================= - // AUDIOSTREAM (Immediate) - // Command/Subcommand/Padding <- E1??0000 ?? = subcommand - // Command0 <- Position on DVD shr 2 - // Command1 <- Length of the stream - //========================================================================================================= - case 0xE1: - { - // i dunno if we need this check - // if (m_bStream) - // MessageBox(NULL, "dont overwrite a stream while you play it", "FATAL ERROR", MB_OK); - - // subcommand - - // ugly hack to catch the disable command - if (dvdMem.Command[1]!=0) - { -#ifdef LOGGING - u8 subCommand = (dvdMem.Command[0] & 0x00FF0000) >> 16; -#endif - - dvdMem.AudioPos = dvdMem.Command[1] << 2; - dvdMem.AudioStart = dvdMem.AudioPos; - dvdMem.AudioLength = dvdMem.Command[2]; - NGCADPCM::InitFilter(); - - m_bStream = true; - - LOG(DVDINTERFACE, "DVD(Audio) Stream subcmd = %02x offset = %08x length=%08x", subCommand, dvdMem.AudioPos, dvdMem.AudioLength); - } - } - break; - - //========================================================================================================= - // REQUEST AUDIO STATUS (Immediate) - // Command/Subcommand/Padding <- E2000000 - //========================================================================================================= - case 0xE2: - { - /* if (m_bStream) - dvdMem.Immediate = 1; - else - dvdMem.Immediate = 0;*/ - } - LOG(DVDINTERFACE, "DVD(Audio): Request Audio status"); - break; - - //========================================================================================================= - // STOP MOTOR (Immediate) - // Command/Subcommand/Padding <- E3000000 - //========================================================================================================= - case 0xE3: - LOG(DVDINTERFACE, "DVD: Stop motor"); - break; - - //========================================================================================================= - // DVD AUDIO DISABLE (Immediate)` - // Command/Subcommand/Padding <- E4000000 (disable) - // Command/Subcommand/Padding <- E4010000 (enable) - //========================================================================================================= - case 0xE4: -/* if (((dvdMem.Command[0] & 0x00FF0000) >> 16) == 1) - { - m_bStream = true; - LOG(DVDINTERFACE, "DVD(Audio): Audio enabled"); - } - else - { - m_bStream = false; - LOG(DVDINTERFACE, "DVD(Audio): Audio disabled"); - }*/ - break; - - //========================================================================================================= - // UNKNOWN DVD COMMAND - //========================================================================================================= - default: - PanicAlert("DVD - Unknown DVD command %08x - fatal error", dvdMem.Command[0]); - _dbg_assert_(DVDINTERFACE, 0); - break; - } - - // transfer is done - _DMAControlReg.TSTART = 0; - dvdMem.DMALength.Length = 0; - GenerateDVDInterrupt(INT_TCINT); - g_ErrorCode = 0x00; -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "ChunkFile.h" + +#include "StreamADPCM.H" +#include "DVDInterface.h" + +#include "../PowerPC/PowerPC.h" +#include "PeripheralInterface.h" +#include "Memmap.h" +#include "Thread.h" + +#include "../VolumeHandler.h" + +namespace DVDInterface +{ + +/* +20975: 00000000 DVD (zzz_80146b84 ??, 0x80146bf8) : DVD(r): 0xcc006004 +20976: 00000000 DVD (zzz_80146b84 ??, 0x80146c00) : DVD(w): 0x00000000 @ 0xcc006004 +20977: 00000000 DVD (DVDLowRead, 0x801448a8) : DVD(w): 0x00000020 @ 0xcc006018 +20978: 00000000 DVD (Read, 0x80144744) : DVD(w): 0xa8000000 @ 0xcc006008 +20979: 00000000 DVD (Read, 0x80144750) : DVD(w): 0x01094227 @ 0xcc00600c +20980: 00000000 DVD (Read, 0x80144758) : DVD(w): 0x00000020 @ 0xcc006010 +20981: 00000000 DVD (Read, 0x8014475c) : DVD(w): 0x8167cc80 @ 0xcc006014 +20982: 00000000 DVD (Read, 0x80144760) : DVD(w): 0x00000020 @ 0xcc006018 +20983: 00000000 DVD (Read, 0x80144768) : DVD(w): 0x00000003 @ 0xcc00601c +20984: 00000000 DVD: DVD: Read ISO: DVDOffset=0425089c, DMABuffer=0167cc80, SrcLength=00000020, DMALength=00000020 +20989: 00000000 DVD (zzz_801442fc ??, 0x80144388) : DVD(r): 0xcc006000 +20990: 00000000 DVD (zzz_801442fc ??, 0x801443d8) : DVD(w): 0x0000003a @ 0xcc006000 +20992: 00000000 DVD (zzz_801442fc ??, 0x801444d0) : DVD(w): 0x00000000 @ 0xcc006004 +20993: 00000000 DVD (zzz_80146e44 ??, 0x80146fcc) : DVD(r): 0xcc006018 + +After this, Cubivore infinitely calls DVDGetDriveStatus, which does not even +bother to check any DVD regs. Waiting for interrupt? +*/ + +// internal hardware addresses +enum +{ + DI_STATUS_REGISTER = 0x00, + DI_COVER_REGISTER = 0x04, + DI_COMMAND_0 = 0x08, + DI_COMMAND_1 = 0x0C, + DI_COMMAND_2 = 0x10, + DI_DMA_ADDRESS_REGISTER = 0x14, + DI_DMA_LENGTH_REGISTER = 0x18, + DI_DMA_CONTROL_REGISTER = 0x1C, + DI_IMMEDIATE_DATA_BUFFER = 0x20, + DI_CONFIG_REGISTER = 0x24 +}; + + +// DVD IntteruptTypes +enum DVDInterruptType +{ + INT_DEINT = 0, + INT_TCINT = 1, + INT_BRKINT = 2, + INT_CVRINT = 3, +}; + +// DI Status Register +union UDISR +{ + u32 Hex; + struct + { + unsigned BREAK : 1; // Stop the Device + Interrupt + unsigned DEINITMASK : 1; // Access Device Error Int Mask + unsigned DEINT : 1; // Access Device Error Int + unsigned TCINTMASK : 1; // Transfer Complete Int Mask + unsigned TCINT : 1; // Transfer Complete Int + unsigned BRKINTMASK : 1; + unsigned BRKINT : 1; // w 1: clear brkint + unsigned : 25; + }; + UDISR() {Hex = 0;} + UDISR(u32 _hex) {Hex = _hex;} +}; + +// DI Cover Register +union UDICVR +{ + u32 Hex; + struct + { + unsigned CVR : 1; // 0: Cover closed 1: Cover open + unsigned CVRINTMASK : 1; // 1: Interrupt enabled; + unsigned CVRINT : 1; // 1: Interrupt clear + unsigned : 29; + }; + UDICVR() {Hex = 0;} + UDICVR(u32 _hex) {Hex = _hex;} +}; + +// DI DMA Address Register +union UDIDMAAddressRegister +{ + u32 Hex; + struct + { + unsigned Address : 26; + unsigned : 6; + }; +}; + +// DI DMA Address Length Register +union UDIDMAAddressLength +{ + u32 Hex; + struct + { + unsigned Length : 26; + unsigned : 6; + }; +}; + +// DI DMA Control Register +union UDIDMAControlRegister +{ + u32 Hex; + struct + { + unsigned TSTART : 1; // w:1 start r:0 ready + unsigned DMA : 1; // 1: DMA Mode 0: Immediate Mode (can only do Access Register Command) + unsigned RW : 1; // 0: Read Command (DVD to Memory) 1: Write COmmand (Memory to DVD) + unsigned : 29; + }; +}; + +// DI Config Register +union UDIConfigRegister +{ + u32 Hex; + struct + { + unsigned CONFIG : 8; + unsigned : 24; + }; + UDIConfigRegister() {Hex = 0;} + UDIConfigRegister(u32 _hex) {Hex = _hex;} +}; + +// hardware registers +struct DVDMemStruct +{ + UDISR StatusReg; + UDICVR CoverReg; + u32 Command[3]; + UDIDMAAddressRegister DMAAddress; + UDIDMAAddressLength DMALength; + UDIDMAControlRegister DMAControlReg; + u32 Immediate; + UDIConfigRegister ConfigReg; + u32 AudioStart; + u32 AudioPos; + u32 AudioLength; +}; + +// STATE_TO_SAVE +DVDMemStruct dvdMem; +u32 g_ErrorCode = 0x00; +bool g_bDiscInside = true; + +Common::CriticalSection dvdread_section; + +void DoState(PointerWrap &p) +{ + p.Do(dvdMem); + p.Do(g_ErrorCode); + p.Do(g_bDiscInside); +} + +void UpdateInterrupts(); +void GenerateDVDInterrupt(DVDInterruptType _DVDInterrupt); +void ExecuteCommand(UDIDMAControlRegister& _DMAControlReg); + +void Init() +{ + dvdMem.StatusReg.Hex = 0; + dvdMem.CoverReg.Hex = 0; + dvdMem.Command[0] = 0; + dvdMem.Command[1] = 0; + dvdMem.Command[2] = 0; + dvdMem.DMAAddress.Hex = 0; + dvdMem.DMALength.Hex = 0; + dvdMem.DMAControlReg.Hex = 0; + dvdMem.Immediate = 0; + dvdMem.ConfigReg.Hex = 0; + dvdMem.AudioStart = 0; + dvdMem.AudioPos = 0; + dvdMem.AudioLength = 0; + +// SetLidOpen(true); +} + +void Shutdown() +{ + +} + +void SetDiscInside(bool _DiscInside) +{ + g_bDiscInside = _DiscInside; +} + +void SetLidOpen(bool _bOpen) +{ + if (_bOpen) + dvdMem.CoverReg.CVR = 1; + else + dvdMem.CoverReg.CVR = 0; + + UpdateInterrupts(); +} + +bool IsLidOpen() +{ + if (dvdMem.CoverReg.CVR) + return true; + else + return false; +} + +bool DVDRead(u32 _iDVDOffset, u32 _iRamAddress, u32 _iLength) +{ + // We won't need the crit sec when DTK streaming has been rewritten correctly. + dvdread_section.Enter(); + bool retval = VolumeHandler::ReadToPtr(Memory::GetPointer(_iRamAddress), _iDVDOffset, _iLength); + dvdread_section.Leave(); + return retval; +} + +bool DVDReadADPCM(u8* _pDestBuffer, u32 _iNumSamples) +{ + if (dvdMem.AudioPos == 0) + { + //MessageBox(0,"DVD: Trying to stream from 0", "bah", 0); + memset(_pDestBuffer, 0, _iNumSamples); // probably __AI_SRC_INIT :P + return false; + } + _iNumSamples &= ~31; + dvdread_section.Enter(); + VolumeHandler::ReadToPtr(_pDestBuffer, dvdMem.AudioPos, _iNumSamples); + dvdread_section.Leave(); + + // + // FIX THIS + // + // loop check + // + dvdMem.AudioPos += _iNumSamples; + if (dvdMem.AudioPos >= dvdMem.AudioStart + dvdMem.AudioLength) + { + dvdMem.AudioPos = dvdMem.AudioStart; + NGCADPCM::InitFilter(); + } + + //LOG(DVDINTERFACE,"ReadADPCM"); + return true; +} + +void Read32(u32& _uReturnValue, const u32 _iAddress) +{ + LOGV(DVDINTERFACE, 3, "DVD(r): 0x%08x", _iAddress); + + switch (_iAddress & 0xFFF) + { + case DI_STATUS_REGISTER: _uReturnValue = dvdMem.StatusReg.Hex; return; + case DI_COVER_REGISTER: _uReturnValue = dvdMem.CoverReg.Hex; return; + case DI_COMMAND_0: _uReturnValue = dvdMem.Command[0]; return; + case DI_COMMAND_1: _uReturnValue = dvdMem.Command[1]; return; + case DI_COMMAND_2: _uReturnValue = dvdMem.Command[2]; return; + case DI_DMA_ADDRESS_REGISTER: _uReturnValue = dvdMem.DMAAddress.Hex; return; + case DI_DMA_LENGTH_REGISTER: _uReturnValue = dvdMem.DMALength.Hex; return; + case DI_DMA_CONTROL_REGISTER: _uReturnValue = dvdMem.DMAControlReg.Hex; return; + case DI_IMMEDIATE_DATA_BUFFER: _uReturnValue = dvdMem.Immediate; return; + case DI_CONFIG_REGISTER: + { + dvdMem.ConfigReg.Hex = 0x000000FF; + _uReturnValue = dvdMem.ConfigReg.Hex; + return; + } + + default: + _dbg_assert_(DVDINTERFACE,0); + } + + _uReturnValue = 0; +} + +void Write32(const u32 _iValue, const u32 _iAddress) +{ + LOGV(DVDINTERFACE, 3, "DVD(w): 0x%08x @ 0x%08x", _iValue, _iAddress); + + switch (_iAddress & 0x3FF) + { + case DI_STATUS_REGISTER: + { + UDISR tmpStatusReg(_iValue); + + dvdMem.StatusReg.DEINITMASK = tmpStatusReg.DEINITMASK; + dvdMem.StatusReg.TCINTMASK = tmpStatusReg.TCINTMASK; + dvdMem.StatusReg.BRKINTMASK = tmpStatusReg.BRKINTMASK; + + if (tmpStatusReg.DEINT) dvdMem.StatusReg.DEINT = 0; + if (tmpStatusReg.TCINT) dvdMem.StatusReg.TCINT = 0; + if (tmpStatusReg.BRKINT) dvdMem.StatusReg.BRKINT = 0; + + if (tmpStatusReg.BREAK) + { + _dbg_assert_(DVDINTERFACE, 0); + } + + UpdateInterrupts(); + } + break; + + case DI_COVER_REGISTER: + { + UDICVR tmpCoverReg(_iValue); + + dvdMem.CoverReg.CVR = 0; + dvdMem.CoverReg.CVRINTMASK = tmpCoverReg.CVRINTMASK; + if (tmpCoverReg.CVRINT) dvdMem.CoverReg.CVRINT = 0; + + UpdateInterrupts(); + + _dbg_assert_(DVDINTERFACE, (tmpCoverReg.CVR == 0)); + } + break; + + case DI_COMMAND_0: dvdMem.Command[0] = _iValue; break; + case DI_COMMAND_1: dvdMem.Command[1] = _iValue; break; + case DI_COMMAND_2: dvdMem.Command[2] = _iValue; break; + + case DI_DMA_ADDRESS_REGISTER: + { + dvdMem.DMAAddress.Hex = _iValue; + _dbg_assert_(DVDINTERFACE, ((dvdMem.DMAAddress.Hex & 0x1F) == 0)); + } + break; + case DI_DMA_LENGTH_REGISTER: dvdMem.DMALength.Hex = _iValue; break; + case DI_DMA_CONTROL_REGISTER: + { + dvdMem.DMAControlReg.Hex = _iValue; + if (dvdMem.DMAControlReg.TSTART) + { + ExecuteCommand(dvdMem.DMAControlReg); + } + } + break; + + case DI_IMMEDIATE_DATA_BUFFER: dvdMem.Immediate = _iValue; break; + case DI_CONFIG_REGISTER: + { + UDIConfigRegister tmpConfigReg(_iValue); + + dvdMem.ConfigReg.CONFIG = tmpConfigReg.CONFIG; + } + break; + + default: + _dbg_assert_msg_(DVDINTERFACE, 0, "Write to unknown DI address 0x%08x", _iAddress); + break; + } +} + +void UpdateInterrupts() +{ + if ((dvdMem.StatusReg.DEINT & dvdMem.StatusReg.DEINITMASK) || + (dvdMem.StatusReg.TCINT & dvdMem.StatusReg.TCINTMASK) || + (dvdMem.StatusReg.BRKINT & dvdMem.StatusReg.BRKINTMASK) || + (dvdMem.CoverReg.CVRINT & dvdMem.CoverReg.CVRINTMASK)) + { + CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_DI, true); + } + else + { + CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_DI, false); + } +} + +void GenerateDVDInterrupt(DVDInterruptType _DVDInterrupt) +{ + switch(_DVDInterrupt) + { + case INT_DEINT: dvdMem.StatusReg.DEINT = 1; break; + case INT_TCINT: dvdMem.StatusReg.TCINT = 1; break; + case INT_BRKINT: dvdMem.StatusReg.BRKINT = 1; break; + case INT_CVRINT: dvdMem.CoverReg.CVRINT = 1; break; + } + + UpdateInterrupts(); +} + +bool m_bStream = false; + +void ExecuteCommand(UDIDMAControlRegister& _DMAControlReg) +{ + _dbg_assert_(DVDINTERFACE, _DMAControlReg.RW == 0); // only DVD to Memory + + switch ((dvdMem.Command[0] & 0xFF000000) >> 24) + { + //========================================================================================================= + // DRIVE INFO (DMA) + // Command/Subcommand/Padding <- 12000000 + // Command0 <- 0 + // Command1 <- 0x20 + // Command2 <- Address in ram of the buffer + // + // output buffer: + // 0000-0001 revisionLevel + // 0002-0003 deviceCode + // 0004-0007 releaseDate + // 0008-001F padding(0) + //========================================================================================================= + case 0x12: + { +#ifdef LOGGING + u32 offset = dvdMem.Command[1]; + // u32 sourcelength = dvdMem.Command[2]; +#endif + u32 destbuffer = dvdMem.DMAAddress.Address; + u32 destlength = dvdMem.DMALength.Length; + dvdMem.DMALength.Length = 0; + + LOG(DVDINTERFACE, "[WARNING] DVD: Get drive info offset=%08x, destbuffer=%08x, destlength=%08x", offset * 4, destbuffer, destlength); + + // metroid uses this... + for (unsigned int i = 0; i < destlength / 4; i++) + { + Memory::Write_U32(0, destbuffer + i * 4); + } + } + break; + + //========================================================================================================= + // READ (DMA) + // Command/Subcommand/Padding <- A8000000 + // Command0 <- Position on DVD shr 2 + // Command1 <- Length of the read + // Command2 <- Address in ram of the buffer + //========================================================================================================= + case 0xA8: + { + if (g_bDiscInside) + { + u32 iDVDOffset = dvdMem.Command[1] << 2; + u32 iSrcLength = dvdMem.Command[2]; + if (false) { iSrcLength++; } // avoid warning << wtf is this? + LOGV(DVDINTERFACE, 2, "DVD: Read ISO: DVDOffset=%08x, DMABuffer=%08x, SrcLength=%08x, DMALength=%08x",iDVDOffset,dvdMem.DMAAddress.Address,iSrcLength,dvdMem.DMALength.Length); + _dbg_assert_(DVDINTERFACE, iSrcLength == dvdMem.DMALength.Length); + + if (DVDRead(iDVDOffset, dvdMem.DMAAddress.Address, dvdMem.DMALength.Length) != true) + { + PanicAlert("Cant read from DVD_Plugin - DVD-Interface: Fatal Error"); + } + } + else + { + // there is no disc to read + GenerateDVDInterrupt(INT_DEINT); + g_ErrorCode = 0x03023A00; + return; + } + } + break; + + //========================================================================================================= + // SEEK (Immediate) + // Command/Subcommand/Padding <- AB000000 + // Command0 <- Position on DVD shr 2 + //========================================================================================================= + case 0xAB: + { +#ifdef LOGGING + u32 offset = dvdMem.Command[1] << 2; +#endif + LOG(DVDINTERFACE, "DVD: Trying to seek: offset=%08x", offset); + } + break; + + //========================================================================================================= + // REQUEST ERROR (Immediate) + // Command/Subcommand/Padding <- E0000000 + //========================================================================================================= + case 0xE0: + LOG(DVDINTERFACE, "DVD: Requesting error"); + dvdMem.Immediate = g_ErrorCode; + break; + + //========================================================================================================= + // AUDIOSTREAM (Immediate) + // Command/Subcommand/Padding <- E1??0000 ?? = subcommand + // Command0 <- Position on DVD shr 2 + // Command1 <- Length of the stream + //========================================================================================================= + case 0xE1: + { + // i dunno if we need this check + // if (m_bStream) + // MessageBox(NULL, "dont overwrite a stream while you play it", "FATAL ERROR", MB_OK); + + // subcommand + + // ugly hack to catch the disable command + if (dvdMem.Command[1]!=0) + { +#ifdef LOGGING + u8 subCommand = (dvdMem.Command[0] & 0x00FF0000) >> 16; +#endif + + dvdMem.AudioPos = dvdMem.Command[1] << 2; + dvdMem.AudioStart = dvdMem.AudioPos; + dvdMem.AudioLength = dvdMem.Command[2]; + NGCADPCM::InitFilter(); + + m_bStream = true; + + LOG(DVDINTERFACE, "DVD(Audio) Stream subcmd = %02x offset = %08x length=%08x", subCommand, dvdMem.AudioPos, dvdMem.AudioLength); + } + } + break; + + //========================================================================================================= + // REQUEST AUDIO STATUS (Immediate) + // Command/Subcommand/Padding <- E2000000 + //========================================================================================================= + case 0xE2: + { + /* if (m_bStream) + dvdMem.Immediate = 1; + else + dvdMem.Immediate = 0;*/ + } + LOG(DVDINTERFACE, "DVD(Audio): Request Audio status"); + break; + + //========================================================================================================= + // STOP MOTOR (Immediate) + // Command/Subcommand/Padding <- E3000000 + //========================================================================================================= + case 0xE3: + LOG(DVDINTERFACE, "DVD: Stop motor"); + break; + + //========================================================================================================= + // DVD AUDIO DISABLE (Immediate)` + // Command/Subcommand/Padding <- E4000000 (disable) + // Command/Subcommand/Padding <- E4010000 (enable) + //========================================================================================================= + case 0xE4: +/* if (((dvdMem.Command[0] & 0x00FF0000) >> 16) == 1) + { + m_bStream = true; + LOG(DVDINTERFACE, "DVD(Audio): Audio enabled"); + } + else + { + m_bStream = false; + LOG(DVDINTERFACE, "DVD(Audio): Audio disabled"); + }*/ + break; + + //========================================================================================================= + // UNKNOWN DVD COMMAND + //========================================================================================================= + default: + PanicAlert("DVD - Unknown DVD command %08x - fatal error", dvdMem.Command[0]); + _dbg_assert_(DVDINTERFACE, 0); + break; + } + + // transfer is done + _DMAControlReg.TSTART = 0; + dvdMem.DMALength.Length = 0; + GenerateDVDInterrupt(INT_TCINT); + g_ErrorCode = 0x00; +} + +} // namespace diff --git a/Source/Core/Core/Src/HW/EXI.cpp b/Source/Core/Core/Src/HW/EXI.cpp index 61922c5838..b50e9f0a1c 100644 --- a/Source/Core/Core/Src/HW/EXI.cpp +++ b/Source/Core/Core/Src/HW/EXI.cpp @@ -1,116 +1,116 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "ChunkFile.h" - -#include "PeripheralInterface.h" -#include "../PowerPC/PowerPC.h" - -#include "EXI_Device.h" -#include "EXI_Channel.h" - -namespace ExpansionInterface -{ - -enum -{ - NUM_CHANNELS = 3 -}; - -CEXIChannel *g_Channels; - -void Init() -{ - g_Channels = new CEXIChannel[3]; - g_Channels[0].m_ChannelId = 0; - g_Channels[1].m_ChannelId = 1; - g_Channels[2].m_ChannelId = 2; - - g_Channels[0].AddDevice(EXIDEVICE_MEMORYCARD_A, 0); - g_Channels[0].AddDevice(EXIDEVICE_IPL, 1); - g_Channels[1].AddDevice(EXIDEVICE_MEMORYCARD_B, 0); - #if 0 - g_Channels[0].AddDevice(EXIDEVICE_ETH, 2); - #endif - //g_Channels[1].AddDevice(EXIDEVICE_MIC, 0); - g_Channels[2].AddDevice(EXIDEVICE_AD16, 0); -} - -void Shutdown() -{ - delete [] g_Channels; - g_Channels = 0; -} - -void DoState(PointerWrap &p) -{ - // TODO: descend all the devices recursively. -} - -void Update() -{ - g_Channels[0].Update(); - g_Channels[1].Update(); - g_Channels[2].Update(); -} - -void Read32(u32& _uReturnValue, const u32 _iAddress) -{ - unsigned int iAddr = _iAddress & 0x3FF; - unsigned int iRegister = (iAddr >> 2) % 5; - unsigned int iChannel = (iAddr >> 2) / 5; - - _dbg_assert_(EXPANSIONINTERFACE, iChannel < NUM_CHANNELS); - - if (iChannel < NUM_CHANNELS) - { - g_Channels[iChannel].Read32(_uReturnValue, iRegister); - } - else - { - _uReturnValue = 0; - } -} - -void Write32(const u32 _iValue, const u32 _iAddress) -{ - int iAddr = _iAddress & 0x3FF; - int iRegister = (iAddr >> 2) % 5; - int iChannel = (iAddr >> 2) / 5; - - _dbg_assert_(EXPANSIONINTERFACE, iChannel < NUM_CHANNELS) - - if (iChannel < NUM_CHANNELS) - g_Channels[iChannel].Write32(_iValue, iRegister); -} - -void UpdateInterrupts() -{ - for(int i=0; i> 2) % 5; + unsigned int iChannel = (iAddr >> 2) / 5; + + _dbg_assert_(EXPANSIONINTERFACE, iChannel < NUM_CHANNELS); + + if (iChannel < NUM_CHANNELS) + { + g_Channels[iChannel].Read32(_uReturnValue, iRegister); + } + else + { + _uReturnValue = 0; + } +} + +void Write32(const u32 _iValue, const u32 _iAddress) +{ + int iAddr = _iAddress & 0x3FF; + int iRegister = (iAddr >> 2) % 5; + int iChannel = (iAddr >> 2) / 5; + + _dbg_assert_(EXPANSIONINTERFACE, iChannel < NUM_CHANNELS) + + if (iChannel < NUM_CHANNELS) + g_Channels[iChannel].Write32(_iValue, iRegister); +} + +void UpdateInterrupts() +{ + for(int i=0; iIsInterruptSet()) - m_Status.EXIINT = 1; - } - else /* Channel 2: In fact, Channel 0, Device 2 (Serial A) produces interrupt */ - { - // WTF? this[-2]??? EVIL HACK - if (this[-2].m_pDevices[2]->IsInterruptSet()) - m_Status.EXIINT = 1; - } - - if ((m_Status.EXIINT & m_Status.EXIINTMASK) || - (m_Status.TCINT & m_Status.TCINTMASK) || - (m_Status.EXTINT & m_Status.EXTINTMASK)) - { - return true; - } - else - { - return false; - } -} - -IEXIDevice* CEXIChannel::GetDevice(u8 _CHIP_SELECT) -{ - switch(_CHIP_SELECT) - { - case 1: return m_pDevices[0]; - case 2: return m_pDevices[1]; - case 4: return m_pDevices[2]; - } - return NULL; -} - -void CEXIChannel::Update() -{ - // start the transfer - for (int i = 0; i < NUM_DEVICES; i++) - { - m_pDevices[i]->Update(); - } -} - -void CEXIChannel::Read32(u32& _uReturnValue, const u32 _iRegister) -{ - LOGV(EXPANSIONINTERFACE, 3, "ExtensionInterface(R): channel: %i reg: %i", m_ChannelId, _iRegister); - - switch (_iRegister) - { - case EXI_STATUS: - { - // check if a device is present - for (int i = 0; i < NUM_DEVICES; i++) - { - if (m_pDevices[i]->IsPresent()) - { - m_Status.EXT = 1; - break; - } - } - _uReturnValue = m_Status.hex; - break; - } - - case EXI_DMAADDR: - _uReturnValue = m_DMAMemoryAddress; - break; - - case EXI_DMALENGTH: - _uReturnValue = m_DMALength; - break; - - case EXI_DMACONTROL: - _uReturnValue = m_Control.hex; - break; - - case EXI_IMMDATA: - _uReturnValue = m_ImmData; - break; - - default: - _dbg_assert_(EXPANSIONINTERFACE, 0); - _uReturnValue = 0xDEADBEEF; - } - -} - -void CEXIChannel::Write32(const u32 _iValue, const u32 _iRegister) -{ - LOGV(EXPANSIONINTERFACE, 2, "ExtensionInterface(W): 0x%08x channel: %i reg: %i", _iValue, m_ChannelId, _iRegister); - - switch (_iRegister) - { - case EXI_STATUS: - { - UEXI_STATUS newStatus(_iValue); - - // static - m_Status.EXIINTMASK = newStatus.EXIINTMASK; - m_Status.TCINTMASK = newStatus.TCINTMASK; - m_Status.EXTINTMASK = newStatus.EXTINTMASK; - m_Status.CLK = newStatus.CLK; - m_Status.ROMDIS = newStatus.ROMDIS; - - // Device - if (m_Status.CHIP_SELECT != newStatus.CHIP_SELECT) - { - for (int i = 0; i < NUM_DEVICES; i++) - { - u8 dwDeviceMask = 1 << i; - IEXIDevice* pDevice = GetDevice(dwDeviceMask); - if (pDevice != NULL) - { - if (((newStatus.CHIP_SELECT & dwDeviceMask) == dwDeviceMask) && - ((m_Status.CHIP_SELECT & dwDeviceMask) == 0)) - // device gets activated - pDevice->SetCS(1); - - if (((newStatus.CHIP_SELECT & dwDeviceMask) == 0) && - ((m_Status.CHIP_SELECT & dwDeviceMask) == dwDeviceMask)) - // device gets deactivated - pDevice->SetCS(0); - } - } - m_Status.CHIP_SELECT = newStatus.CHIP_SELECT; - } - - // External Status - IEXIDevice* pDevice = GetDevice(newStatus.CHIP_SELECT); - if (pDevice != NULL) - m_Status.EXT = pDevice->IsPresent() ? 1 : 0; - else - m_Status.EXT = 0; - - // interrupt - if (newStatus.EXIINT) m_Status.EXIINT = 0; - if (newStatus.TCINT) m_Status.TCINT = 0; - if (newStatus.EXTINT) m_Status.EXTINT = 0; - UpdateInterrupts(); - } - break; - - case EXI_DMAADDR: - LOGV(EXPANSIONINTERFACE, 2, "EXI: Wrote DMABuf, chan %i", m_ChannelId); - m_DMAMemoryAddress = _iValue; - break; - - case EXI_DMALENGTH: - LOGV(EXPANSIONINTERFACE, 2, "EXI: Wrote DMASize, chan %i", m_ChannelId); - m_DMALength = _iValue; - break; - - case EXI_DMACONTROL: - LOGV(EXPANSIONINTERFACE, 2, "EXI: Wrote DMAControl, chan %i", m_ChannelId); - m_Control.hex = _iValue; - - if (m_Control.TSTART) - { - IEXIDevice* pDevice = GetDevice(m_Status.CHIP_SELECT); - if (pDevice == NULL) - return; - - if (m_Control.DMA == 0) - { - // immediate data - switch (m_Control.RW) - { - case 0: m_ImmData = pDevice->ImmRead(m_Control.TLEN + 1); break; - case 1: pDevice->ImmWrite(m_ImmData, m_Control.TLEN + 1); break; - default: _dbg_assert_msg_(EXPANSIONINTERFACE,0,"EXI Imm: Unknown transfer type %i", m_Control.RW); - } - m_Control.TSTART = 0; - } - else - { - // DMA - switch (m_Control.RW) - { - case 0: pDevice->DMARead (m_DMAMemoryAddress, m_DMALength); break; - case 1: pDevice->DMAWrite(m_DMAMemoryAddress, m_DMALength); break; - default: _dbg_assert_msg_(EXPANSIONINTERFACE,0,"EXI DMA: Unknown transfer type %i", m_Control.RW); - } - m_Control.TSTART = 0; - } - - if(!m_Control.TSTART) // completed ! - { - m_Status.TCINT = 1; - UpdateInterrupts(); - } - } - break; - - case EXI_IMMDATA: - LOGV(EXPANSIONINTERFACE, 2, "EXI: Wrote IMMData, chan %i", m_ChannelId); - m_ImmData = _iValue; - break; - } -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "EXI_Channel.h" +#include "EXI.h" + +#include "PeripheralInterface.h" +#include "../PowerPC/PowerPC.h" + +CEXIChannel::CEXIChannel() : + m_DMAMemoryAddress(0), + m_DMALength(0), + m_ImmData(0), + m_ChannelId(-1) +{ + m_Control.hex = 0; + m_Status.hex = 0; + + m_Status.CHIP_SELECT = 1; + + for (int i = 0; i < NUM_DEVICES; i++) + { + m_pDevices[i] = EXIDevice_Create(EXIDEVICE_DUMMY); + _dbg_assert_(EXPANSIONINTERFACE, m_pDevices[i] != NULL); + } + + m_Status.TCINTMASK = 1; +} + +CEXIChannel::~CEXIChannel() +{ + RemoveDevices(); +} + +void CEXIChannel::RemoveDevices() +{ + for (int i = 0; i < NUM_DEVICES; i++) + { + if (m_pDevices[i] != NULL) + { + delete m_pDevices[i]; + m_pDevices[i] = NULL; + } + } +} + +void CEXIChannel::AddDevice(const TEXIDevices _device, const unsigned int _iSlot) +{ + _dbg_assert_(EXPANSIONINTERFACE, _iSlot < NUM_DEVICES); + + // delete the old device + if (m_pDevices[_iSlot] != NULL) + { + delete m_pDevices[_iSlot]; + m_pDevices[_iSlot] = NULL; + } + + // create the new one + m_pDevices[_iSlot] = EXIDevice_Create(_device); + _dbg_assert_(EXPANSIONINTERFACE, m_pDevices[_iSlot] != NULL); +} + +void CEXIChannel::UpdateInterrupts() +{ + ExpansionInterface::UpdateInterrupts(); +} + +bool CEXIChannel::IsCausingInterrupt() +{ + if (m_ChannelId != 2) /* Channels 0 and 1: Memcard slot (device 0) produces interrupt */ + { + for (int i = 0; i < NUM_DEVICES; i++) + if (m_pDevices[i]->IsInterruptSet()) + m_Status.EXIINT = 1; + } + else /* Channel 2: In fact, Channel 0, Device 2 (Serial A) produces interrupt */ + { + // WTF? this[-2]??? EVIL HACK + if (this[-2].m_pDevices[2]->IsInterruptSet()) + m_Status.EXIINT = 1; + } + + if ((m_Status.EXIINT & m_Status.EXIINTMASK) || + (m_Status.TCINT & m_Status.TCINTMASK) || + (m_Status.EXTINT & m_Status.EXTINTMASK)) + { + return true; + } + else + { + return false; + } +} + +IEXIDevice* CEXIChannel::GetDevice(u8 _CHIP_SELECT) +{ + switch(_CHIP_SELECT) + { + case 1: return m_pDevices[0]; + case 2: return m_pDevices[1]; + case 4: return m_pDevices[2]; + } + return NULL; +} + +void CEXIChannel::Update() +{ + // start the transfer + for (int i = 0; i < NUM_DEVICES; i++) + { + m_pDevices[i]->Update(); + } +} + +void CEXIChannel::Read32(u32& _uReturnValue, const u32 _iRegister) +{ + LOGV(EXPANSIONINTERFACE, 3, "ExtensionInterface(R): channel: %i reg: %i", m_ChannelId, _iRegister); + + switch (_iRegister) + { + case EXI_STATUS: + { + // check if a device is present + for (int i = 0; i < NUM_DEVICES; i++) + { + if (m_pDevices[i]->IsPresent()) + { + m_Status.EXT = 1; + break; + } + } + _uReturnValue = m_Status.hex; + break; + } + + case EXI_DMAADDR: + _uReturnValue = m_DMAMemoryAddress; + break; + + case EXI_DMALENGTH: + _uReturnValue = m_DMALength; + break; + + case EXI_DMACONTROL: + _uReturnValue = m_Control.hex; + break; + + case EXI_IMMDATA: + _uReturnValue = m_ImmData; + break; + + default: + _dbg_assert_(EXPANSIONINTERFACE, 0); + _uReturnValue = 0xDEADBEEF; + } + +} + +void CEXIChannel::Write32(const u32 _iValue, const u32 _iRegister) +{ + LOGV(EXPANSIONINTERFACE, 2, "ExtensionInterface(W): 0x%08x channel: %i reg: %i", _iValue, m_ChannelId, _iRegister); + + switch (_iRegister) + { + case EXI_STATUS: + { + UEXI_STATUS newStatus(_iValue); + + // static + m_Status.EXIINTMASK = newStatus.EXIINTMASK; + m_Status.TCINTMASK = newStatus.TCINTMASK; + m_Status.EXTINTMASK = newStatus.EXTINTMASK; + m_Status.CLK = newStatus.CLK; + m_Status.ROMDIS = newStatus.ROMDIS; + + // Device + if (m_Status.CHIP_SELECT != newStatus.CHIP_SELECT) + { + for (int i = 0; i < NUM_DEVICES; i++) + { + u8 dwDeviceMask = 1 << i; + IEXIDevice* pDevice = GetDevice(dwDeviceMask); + if (pDevice != NULL) + { + if (((newStatus.CHIP_SELECT & dwDeviceMask) == dwDeviceMask) && + ((m_Status.CHIP_SELECT & dwDeviceMask) == 0)) + // device gets activated + pDevice->SetCS(1); + + if (((newStatus.CHIP_SELECT & dwDeviceMask) == 0) && + ((m_Status.CHIP_SELECT & dwDeviceMask) == dwDeviceMask)) + // device gets deactivated + pDevice->SetCS(0); + } + } + m_Status.CHIP_SELECT = newStatus.CHIP_SELECT; + } + + // External Status + IEXIDevice* pDevice = GetDevice(newStatus.CHIP_SELECT); + if (pDevice != NULL) + m_Status.EXT = pDevice->IsPresent() ? 1 : 0; + else + m_Status.EXT = 0; + + // interrupt + if (newStatus.EXIINT) m_Status.EXIINT = 0; + if (newStatus.TCINT) m_Status.TCINT = 0; + if (newStatus.EXTINT) m_Status.EXTINT = 0; + UpdateInterrupts(); + } + break; + + case EXI_DMAADDR: + LOGV(EXPANSIONINTERFACE, 2, "EXI: Wrote DMABuf, chan %i", m_ChannelId); + m_DMAMemoryAddress = _iValue; + break; + + case EXI_DMALENGTH: + LOGV(EXPANSIONINTERFACE, 2, "EXI: Wrote DMASize, chan %i", m_ChannelId); + m_DMALength = _iValue; + break; + + case EXI_DMACONTROL: + LOGV(EXPANSIONINTERFACE, 2, "EXI: Wrote DMAControl, chan %i", m_ChannelId); + m_Control.hex = _iValue; + + if (m_Control.TSTART) + { + IEXIDevice* pDevice = GetDevice(m_Status.CHIP_SELECT); + if (pDevice == NULL) + return; + + if (m_Control.DMA == 0) + { + // immediate data + switch (m_Control.RW) + { + case 0: m_ImmData = pDevice->ImmRead(m_Control.TLEN + 1); break; + case 1: pDevice->ImmWrite(m_ImmData, m_Control.TLEN + 1); break; + default: _dbg_assert_msg_(EXPANSIONINTERFACE,0,"EXI Imm: Unknown transfer type %i", m_Control.RW); + } + m_Control.TSTART = 0; + } + else + { + // DMA + switch (m_Control.RW) + { + case 0: pDevice->DMARead (m_DMAMemoryAddress, m_DMALength); break; + case 1: pDevice->DMAWrite(m_DMAMemoryAddress, m_DMALength); break; + default: _dbg_assert_msg_(EXPANSIONINTERFACE,0,"EXI DMA: Unknown transfer type %i", m_Control.RW); + } + m_Control.TSTART = 0; + } + + if(!m_Control.TSTART) // completed ! + { + m_Status.TCINT = 1; + UpdateInterrupts(); + } + } + break; + + case EXI_IMMDATA: + LOGV(EXPANSIONINTERFACE, 2, "EXI: Wrote IMMData, chan %i", m_ChannelId); + m_ImmData = _iValue; + break; + } +} diff --git a/Source/Core/Core/Src/HW/EXI_Device.cpp b/Source/Core/Core/Src/HW/EXI_Device.cpp index 777e644021..968c1d5b4c 100644 --- a/Source/Core/Core/Src/HW/EXI_Device.cpp +++ b/Source/Core/Core/Src/HW/EXI_Device.cpp @@ -1,164 +1,164 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Memmap.h" - -#include "EXI_Device.h" -#include "EXI_DeviceIPL.h" -#include "EXI_DeviceMemoryCard.h" -#include "EXI_DeviceAD16.h" -#include "EXI_DeviceMic.h" -#if 0 -#include "EXI_DeviceEthernet.h" -#endif - -#include "../Core.h" - -///////////////////////////////////////////////////////////////////////////////////////////////////// -// --- interface IEXIDevice --- -///////////////////////////////////////////////////////////////////////////////////////////////////// - -void IEXIDevice::ImmWrite(u32 _uData, u32 _uSize) -{ - while (_uSize--) - { - u8 uByte = _uData >> 24; - TransferByte(uByte); - _uData <<= 8; - } -} - -u32 IEXIDevice::ImmRead(u32 _uSize) -{ - u32 uResult = 0; - u32 uPosition = 0; - while (_uSize--) - { - u8 uByte = 0; - TransferByte(uByte); - uResult |= uByte << (24-(uPosition++ * 8)); - } - return uResult; -} - -void IEXIDevice::DMAWrite(u32 _uAddr, u32 _uSize) -{ -// _dbg_assert_(EXPANSIONINTERFACE, 0); - while (_uSize--) - { - u8 uByte = Memory::Read_U8(_uAddr++); - TransferByte(uByte); - } -} - -void IEXIDevice::DMARead(u32 _uAddr, u32 _uSize) -{ -// _dbg_assert_(EXPANSIONINTERFACE, 0); - while (_uSize--) - { - u8 uByte = 0; - TransferByte(uByte); - Memory::Write_U8(uByte, _uAddr++); - } -}; - -bool IEXIDevice::IsPresent() -{ - return false; -} - -void IEXIDevice::Update() -{ -} - -bool IEXIDevice::IsInterruptSet() -{ - return false; -} - -void IEXIDevice::SetCS(int _iCS) -{ -} - -///////////////////////////////////////////////////////////////////////////////////////////////////// -// --- class CEXIDummy --- -///////////////////////////////////////////////////////////////////////////////////////////////////// - -// just a dummy that logs reads and writes -// to be used for EXI devices we haven't emulated -class CEXIDummy : public IEXIDevice -{ - std::string m_strName; - - void TransferByte(u8& _byte) {} - -public: - CEXIDummy(const std::string& _strName) : - m_strName(_strName) - { - } - - virtual ~CEXIDummy(){} - - void ImmWrite(u32 data, u32 size){LOG(EXPANSIONINTERFACE, "EXI DUMMY %s ImmWrite: %08x",m_strName.c_str(),data);} - u32 ImmRead (u32 size) {LOG(EXPANSIONINTERFACE, "EXI DUMMY %s ImmRead",m_strName.c_str()); return 0;} - void DMAWrite(u32 addr, u32 size) {LOG(EXPANSIONINTERFACE, "EXI DUMMY %s DMAWrite: %08x bytes, from %08x to device",m_strName.c_str(),size,addr);} - void DMARead (u32 addr, u32 size) {LOG(EXPANSIONINTERFACE, "EXI DUMMY %s DMARead: %08x bytes, from device to %08x",m_strName.c_str(),size,addr);} -}; - -///////////////////////////////////////////////////////////////////////////////////////////////////// -// F A C T O R Y //////////////////////////////////////////////////////////////////////////////////// -///////////////////////////////////////////////////////////////////////////////////////////////////// - -IEXIDevice* EXIDevice_Create(TEXIDevices _EXIDevice) -{ - switch(_EXIDevice) - { - case EXIDEVICE_DUMMY: - return new CEXIDummy("Dummy"); - break; - - case EXIDEVICE_MEMORYCARD_A: - return new CEXIMemoryCard("MemoryCardA", Core::GetStartupParameter().m_strMemoryCardA, 0); - break; - - case EXIDEVICE_MEMORYCARD_B: - return new CEXIMemoryCard("MemoryCardB", Core::GetStartupParameter().m_strMemoryCardB, 1); - break; - - case EXIDEVICE_IPL: - return new CEXIIPL(); - break; - - case EXIDEVICE_AD16: - return new CEXIAD16(); - break; - - case EXIDEVICE_MIC: - return new CEXIMic(1); - break; - - case EXIDEVICE_ETH: - #if 0 - return new CEXIETHERNET(); - #endif - break; - - } - return NULL; -} - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Memmap.h" + +#include "EXI_Device.h" +#include "EXI_DeviceIPL.h" +#include "EXI_DeviceMemoryCard.h" +#include "EXI_DeviceAD16.h" +#include "EXI_DeviceMic.h" +#if 0 +#include "EXI_DeviceEthernet.h" +#endif + +#include "../Core.h" + +///////////////////////////////////////////////////////////////////////////////////////////////////// +// --- interface IEXIDevice --- +///////////////////////////////////////////////////////////////////////////////////////////////////// + +void IEXIDevice::ImmWrite(u32 _uData, u32 _uSize) +{ + while (_uSize--) + { + u8 uByte = _uData >> 24; + TransferByte(uByte); + _uData <<= 8; + } +} + +u32 IEXIDevice::ImmRead(u32 _uSize) +{ + u32 uResult = 0; + u32 uPosition = 0; + while (_uSize--) + { + u8 uByte = 0; + TransferByte(uByte); + uResult |= uByte << (24-(uPosition++ * 8)); + } + return uResult; +} + +void IEXIDevice::DMAWrite(u32 _uAddr, u32 _uSize) +{ +// _dbg_assert_(EXPANSIONINTERFACE, 0); + while (_uSize--) + { + u8 uByte = Memory::Read_U8(_uAddr++); + TransferByte(uByte); + } +} + +void IEXIDevice::DMARead(u32 _uAddr, u32 _uSize) +{ +// _dbg_assert_(EXPANSIONINTERFACE, 0); + while (_uSize--) + { + u8 uByte = 0; + TransferByte(uByte); + Memory::Write_U8(uByte, _uAddr++); + } +}; + +bool IEXIDevice::IsPresent() +{ + return false; +} + +void IEXIDevice::Update() +{ +} + +bool IEXIDevice::IsInterruptSet() +{ + return false; +} + +void IEXIDevice::SetCS(int _iCS) +{ +} + +///////////////////////////////////////////////////////////////////////////////////////////////////// +// --- class CEXIDummy --- +///////////////////////////////////////////////////////////////////////////////////////////////////// + +// just a dummy that logs reads and writes +// to be used for EXI devices we haven't emulated +class CEXIDummy : public IEXIDevice +{ + std::string m_strName; + + void TransferByte(u8& _byte) {} + +public: + CEXIDummy(const std::string& _strName) : + m_strName(_strName) + { + } + + virtual ~CEXIDummy(){} + + void ImmWrite(u32 data, u32 size){LOG(EXPANSIONINTERFACE, "EXI DUMMY %s ImmWrite: %08x",m_strName.c_str(),data);} + u32 ImmRead (u32 size) {LOG(EXPANSIONINTERFACE, "EXI DUMMY %s ImmRead",m_strName.c_str()); return 0;} + void DMAWrite(u32 addr, u32 size) {LOG(EXPANSIONINTERFACE, "EXI DUMMY %s DMAWrite: %08x bytes, from %08x to device",m_strName.c_str(),size,addr);} + void DMARead (u32 addr, u32 size) {LOG(EXPANSIONINTERFACE, "EXI DUMMY %s DMARead: %08x bytes, from device to %08x",m_strName.c_str(),size,addr);} +}; + +///////////////////////////////////////////////////////////////////////////////////////////////////// +// F A C T O R Y //////////////////////////////////////////////////////////////////////////////////// +///////////////////////////////////////////////////////////////////////////////////////////////////// + +IEXIDevice* EXIDevice_Create(TEXIDevices _EXIDevice) +{ + switch(_EXIDevice) + { + case EXIDEVICE_DUMMY: + return new CEXIDummy("Dummy"); + break; + + case EXIDEVICE_MEMORYCARD_A: + return new CEXIMemoryCard("MemoryCardA", Core::GetStartupParameter().m_strMemoryCardA, 0); + break; + + case EXIDEVICE_MEMORYCARD_B: + return new CEXIMemoryCard("MemoryCardB", Core::GetStartupParameter().m_strMemoryCardB, 1); + break; + + case EXIDEVICE_IPL: + return new CEXIIPL(); + break; + + case EXIDEVICE_AD16: + return new CEXIAD16(); + break; + + case EXIDEVICE_MIC: + return new CEXIMic(1); + break; + + case EXIDEVICE_ETH: + #if 0 + return new CEXIETHERNET(); + #endif + break; + + } + return NULL; +} + diff --git a/Source/Core/Core/Src/HW/EXI_DeviceAD16.cpp b/Source/Core/Core/Src/HW/EXI_DeviceAD16.cpp index 6959b6412c..28b8faf8b5 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceAD16.cpp +++ b/Source/Core/Core/Src/HW/EXI_DeviceAD16.cpp @@ -1,92 +1,92 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "../Core.h" - -#include "EXI_Device.h" -#include "EXI_DeviceAD16.h" - -CEXIAD16::CEXIAD16() : - m_uPosition(0), - m_uCommand(0) -{ - m_uAD16Register.U32 = 0x00; -} - -void CEXIAD16::SetCS(int cs) -{ - if (cs) - m_uPosition = 0; -} - -bool CEXIAD16::IsPresent() -{ - return true; -} - -void CEXIAD16::TransferByte(u8& _byte) -{ - if (m_uPosition == 0) - { - m_uCommand = _byte; - } - else - { - switch(m_uCommand) - { - case init: - { - m_uAD16Register.U32 = 0x04120000; - switch(m_uPosition) - { - case 1: _dbg_assert_(EXPANSIONINTERFACE, (_byte == 0x00)); break; // just skip - case 2: _byte = m_uAD16Register.U8[0]; break; - case 3: _byte = m_uAD16Register.U8[1]; break; - case 4: _byte = m_uAD16Register.U8[2]; break; - case 5: _byte = m_uAD16Register.U8[3]; break; - } - } - break; - - case write: - { - switch(m_uPosition) - { - case 1: m_uAD16Register.U8[0] = _byte; break; - case 2: m_uAD16Register.U8[1] = _byte; break; - case 3: m_uAD16Register.U8[2] = _byte; break; - case 4: m_uAD16Register.U8[3] = _byte; break; - } - } - break; - - case read: - { - switch(m_uPosition) - { - case 1: _byte = m_uAD16Register.U8[0]; break; - case 2: _byte = m_uAD16Register.U8[1]; break; - case 3: _byte = m_uAD16Register.U8[2]; break; - case 4: _byte = m_uAD16Register.U8[3]; break; - } - } - break; - } - } - - m_uPosition++; -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "../Core.h" + +#include "EXI_Device.h" +#include "EXI_DeviceAD16.h" + +CEXIAD16::CEXIAD16() : + m_uPosition(0), + m_uCommand(0) +{ + m_uAD16Register.U32 = 0x00; +} + +void CEXIAD16::SetCS(int cs) +{ + if (cs) + m_uPosition = 0; +} + +bool CEXIAD16::IsPresent() +{ + return true; +} + +void CEXIAD16::TransferByte(u8& _byte) +{ + if (m_uPosition == 0) + { + m_uCommand = _byte; + } + else + { + switch(m_uCommand) + { + case init: + { + m_uAD16Register.U32 = 0x04120000; + switch(m_uPosition) + { + case 1: _dbg_assert_(EXPANSIONINTERFACE, (_byte == 0x00)); break; // just skip + case 2: _byte = m_uAD16Register.U8[0]; break; + case 3: _byte = m_uAD16Register.U8[1]; break; + case 4: _byte = m_uAD16Register.U8[2]; break; + case 5: _byte = m_uAD16Register.U8[3]; break; + } + } + break; + + case write: + { + switch(m_uPosition) + { + case 1: m_uAD16Register.U8[0] = _byte; break; + case 2: m_uAD16Register.U8[1] = _byte; break; + case 3: m_uAD16Register.U8[2] = _byte; break; + case 4: m_uAD16Register.U8[3] = _byte; break; + } + } + break; + + case read: + { + switch(m_uPosition) + { + case 1: _byte = m_uAD16Register.U8[0]; break; + case 2: _byte = m_uAD16Register.U8[1]; break; + case 3: _byte = m_uAD16Register.U8[2]; break; + case 4: _byte = m_uAD16Register.U8[3]; break; + } + } + break; + } + } + + m_uPosition++; +} diff --git a/Source/Core/Core/Src/HW/EXI_DeviceIPL.cpp b/Source/Core/Core/Src/HW/EXI_DeviceIPL.cpp index 62d51d3c74..96479d5589 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceIPL.cpp +++ b/Source/Core/Core/Src/HW/EXI_DeviceIPL.cpp @@ -1,282 +1,282 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Timer.h" - -#include "EXI_DeviceIPL.h" -#include "../Core.h" -#include "MemoryUtil.h" - -// english -const unsigned char sram_dump[64] = { - 0x04, 0x6B, 0xFB, 0x91, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x40, - 0x05, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0xD2, 0x2B, 0x29, 0xD5, 0xC7, 0xAA, 0x12, 0xCB, - 0x21, 0x27, 0xD1, 0x53, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x86, 0x00, 0xFF, 0x4A, 0x00, 0x00, 0x00, 0x00 -}; - -// german -const unsigned char sram_dump_german[64] ={ - 0x1F, 0x66, 0xE0, 0x96, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x04, 0xEA, 0x19, 0x40, - 0x00, 0x00, 0x01, 0x3C, 0x12, 0xD5, 0xEA, 0xD3, - 0x00, 0xFA, 0x2D, 0x33, 0x13, 0x41, 0x26, 0x03, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, - 0x00, 0x00, 0x84, 0xFF, 0x00, 0x00, 0x00, 0x00 -}; - - -// We should provide an option to choose from the above, or figure out the checksum (the algo in yagcd seems wrong) -// so that people can change default language. - -static const char iplver[0x100] = "(C) 1999-2001 Nintendo. All rights reserved." - "(C) 1999 ArtX Inc. All rights reserved." - "PAL Revision 1.0 "; - -CEXIIPL::CEXIIPL() : - m_uPosition(0), - m_uAddress(0), - m_uRWOffset(0), - m_count(0) -{ - // Load the IPL - m_pIPL = (u8*)AllocateMemoryPages(ROM_SIZE); - FILE* pStream = NULL; - pStream = fopen(FONT_ANSI_FILE, "rb"); - if (pStream != NULL) - { - fseek(pStream, 0, SEEK_END); - size_t filesize = (size_t)ftell(pStream); - rewind(pStream); - - fread(m_pIPL + 0x001fcf00, 1, filesize, pStream); - fclose(pStream); - } - else - { - PanicAlert("Error: failed to load font_ansi.bin. Fonts may bug"); - } - - pStream = fopen(FONT_SJIS_FILE, "rb"); - if (pStream != NULL) - { - fseek(pStream, 0, SEEK_END); - size_t filesize = (size_t)ftell(pStream); - rewind(pStream); - - fread(m_pIPL + 0x001aff00, 1, filesize, pStream); - fclose(pStream); - } - else - { - PanicAlert("Error: failed to load font_sjis.bin. Fonts may bug"); - } - - memcpy(m_pIPL, iplver, sizeof(iplver)); - - // Clear RTC - memset(m_RTC, 0, sizeof(m_RTC)); - - // SRAM - pStream = fopen(Core::GetStartupParameter().m_strSRAM.c_str(), "rb"); - if (pStream != NULL) - { - fread(m_SRAM, 1, 64, pStream); - fclose(pStream); - } - else - { - memcpy(m_SRAM, sram_dump, sizeof(m_SRAM)); - } - // We Overwrite it here since it's possible on the GC to change the language as you please - m_SRAM[0x12] = Core::GetStartupParameter().SelectedLanguage; - - WriteProtectMemory(m_pIPL, ROM_SIZE); - m_uAddress = 0; -} - -CEXIIPL::~CEXIIPL() -{ - if (m_count > 0) - { - m_szBuffer[m_count] = 0x00; - //MessageBox(NULL, m_szBuffer, "last message", MB_OK); - } - - if (m_pIPL != NULL) - { - FreeMemoryPages(m_pIPL, ROM_SIZE); - m_pIPL = NULL; - } - - // SRAM - FILE* pStream = NULL; - pStream = fopen(Core::GetStartupParameter().m_strSRAM.c_str(), "wb"); - if (pStream != NULL) - { - fwrite(m_SRAM, 1, 64, pStream); - fclose(pStream); - } -} - -void CEXIIPL::SetCS(int _iCS) -{ - if (_iCS) - { // cs transition to high - m_uPosition = 0; - } -} - -bool CEXIIPL::IsPresent() -{ - return true; -} - -void CEXIIPL::TransferByte(u8& _uByte) -{ - // The first 4 bytes must be the address - // If we haven't read it, do it now - if (m_uPosition < 4) - { - m_uAddress <<= 8; - m_uAddress |= _uByte; - m_uRWOffset = 0; - _uByte = 0xFF; - - // Check if the command is complete - if (m_uPosition == 3) - { - // Get the time ... - u32 GCTime = CEXIIPL::GetGCTime(); - u8* pGCTime = (u8*)&GCTime; - for (int i=0; i<4; i++) - { - m_RTC[i] = pGCTime[i^3]; - } - -#ifdef LOGGING - - if ((m_uAddress & 0xF0000000) == 0xb0000000) - { - LOG(EXPANSIONINTERFACE, "EXI IPL-DEV: WII something"); - } - else if ((m_uAddress & 0xF0000000) == 0x30000000) - { - // wii stuff perhaps wii SRAM? - LOG(EXPANSIONINTERFACE, "EXI IPL-DEV: WII something (perhaps SRAM?)"); - } - // debug only - else if ((m_uAddress & 0x60000000) == 0) - { - LOGV(EXPANSIONINTERFACE, 2, "EXI IPL-DEV: IPL access"); - } - else if ((m_uAddress & 0x7FFFFF00) == 0x20000000) - { - LOG(EXPANSIONINTERFACE, "EXI IPL-DEV: RTC access"); - } - else if ((m_uAddress & 0x7FFFFF00) == 0x20000100) - { - LOG(EXPANSIONINTERFACE, "EXI IPL-DEV: SRAM access"); - } - else if ((m_uAddress & 0x7FFFFF00) == 0x20010000) - { - LOGV(EXPANSIONINTERFACE, 3, "EXI IPL-DEV: UART"); - } - else - { - _dbg_assert_(EXPANSIONINTERFACE, 0); - LOG(EXPANSIONINTERFACE, "EXI IPL-DEV: illegal address %08x", m_uAddress); - } -#endif - } - } - else - { - // - // --- ROM --- - // - if ((m_uAddress & 0x60000000) == 0) - { - if ((m_uAddress & 0x80000000) == 0) - _uByte = m_pIPL[((m_uAddress >> 6) & ROM_MASK) + m_uRWOffset]; - } - // - // --- Real Time Clock (RTC) --- - // - else if ((m_uAddress & 0x7FFFFF00) == 0x20000000) - { - if (m_uAddress & 0x80000000) - m_RTC[(m_uAddress & 0x03) + m_uRWOffset] = _uByte; - else - _uByte = m_RTC[(m_uAddress & 0x03) + m_uRWOffset]; - } - // - // --- SRAM --- - // - else if ((m_uAddress & 0x7FFFFF00) == 0x20000100) - { - if (m_uAddress & 0x80000000) - m_SRAM[(m_uAddress & 0x3F) + m_uRWOffset] = _uByte; - else - _uByte = m_SRAM[(m_uAddress & 0x3F) + m_uRWOffset]; - } - // - // --- UART --- - // - else if ((m_uAddress & 0x7FFFFF00) == 0x20010000) - { - if (m_uAddress & 0x80000000) - { - m_szBuffer[m_count++] = _uByte; - if ((m_count >= 256) || (_uByte == 0xD)) - { - m_szBuffer[m_count] = 0x00; - LOG(OSREPORT, "%s", m_szBuffer); - memset(m_szBuffer, 0, sizeof(m_szBuffer)); - m_count = 0; - } - } - else - _uByte = 0x01; // dunno - } - m_uRWOffset++; - } - m_uPosition++; -} - -u32 CEXIIPL::GetGCTime() -{ - // Get SRAM bias - u32 Bias; - - for (int i=0; i<4; i++) - { - ((u8*)&Bias)[i] = sram_dump[0xc + (i^3)]; - } - - // Get the time ... - const u32 cJanuary2000 = 0x386d35a1; // Seconds between 1.1.1970 and 1.1.2000 - u64 ltime = Common::Timer::GetTimeSinceJan1970(); - return ((u32)ltime - cJanuary2000 - Bias); -} - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Timer.h" + +#include "EXI_DeviceIPL.h" +#include "../Core.h" +#include "MemoryUtil.h" + +// english +const unsigned char sram_dump[64] = { + 0x04, 0x6B, 0xFB, 0x91, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0xFF, 0xFF, 0xFF, 0x40, + 0x05, 0x00, 0x00, 0x2C, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0xD2, 0x2B, 0x29, 0xD5, 0xC7, 0xAA, 0x12, 0xCB, + 0x21, 0x27, 0xD1, 0x53, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x86, 0x00, 0xFF, 0x4A, 0x00, 0x00, 0x00, 0x00 +}; + +// german +const unsigned char sram_dump_german[64] ={ + 0x1F, 0x66, 0xE0, 0x96, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x04, 0xEA, 0x19, 0x40, + 0x00, 0x00, 0x01, 0x3C, 0x12, 0xD5, 0xEA, 0xD3, + 0x00, 0xFA, 0x2D, 0x33, 0x13, 0x41, 0x26, 0x03, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, + 0x00, 0x00, 0x84, 0xFF, 0x00, 0x00, 0x00, 0x00 +}; + + +// We should provide an option to choose from the above, or figure out the checksum (the algo in yagcd seems wrong) +// so that people can change default language. + +static const char iplver[0x100] = "(C) 1999-2001 Nintendo. All rights reserved." + "(C) 1999 ArtX Inc. All rights reserved." + "PAL Revision 1.0 "; + +CEXIIPL::CEXIIPL() : + m_uPosition(0), + m_uAddress(0), + m_uRWOffset(0), + m_count(0) +{ + // Load the IPL + m_pIPL = (u8*)AllocateMemoryPages(ROM_SIZE); + FILE* pStream = NULL; + pStream = fopen(FONT_ANSI_FILE, "rb"); + if (pStream != NULL) + { + fseek(pStream, 0, SEEK_END); + size_t filesize = (size_t)ftell(pStream); + rewind(pStream); + + fread(m_pIPL + 0x001fcf00, 1, filesize, pStream); + fclose(pStream); + } + else + { + PanicAlert("Error: failed to load font_ansi.bin. Fonts may bug"); + } + + pStream = fopen(FONT_SJIS_FILE, "rb"); + if (pStream != NULL) + { + fseek(pStream, 0, SEEK_END); + size_t filesize = (size_t)ftell(pStream); + rewind(pStream); + + fread(m_pIPL + 0x001aff00, 1, filesize, pStream); + fclose(pStream); + } + else + { + PanicAlert("Error: failed to load font_sjis.bin. Fonts may bug"); + } + + memcpy(m_pIPL, iplver, sizeof(iplver)); + + // Clear RTC + memset(m_RTC, 0, sizeof(m_RTC)); + + // SRAM + pStream = fopen(Core::GetStartupParameter().m_strSRAM.c_str(), "rb"); + if (pStream != NULL) + { + fread(m_SRAM, 1, 64, pStream); + fclose(pStream); + } + else + { + memcpy(m_SRAM, sram_dump, sizeof(m_SRAM)); + } + // We Overwrite it here since it's possible on the GC to change the language as you please + m_SRAM[0x12] = Core::GetStartupParameter().SelectedLanguage; + + WriteProtectMemory(m_pIPL, ROM_SIZE); + m_uAddress = 0; +} + +CEXIIPL::~CEXIIPL() +{ + if (m_count > 0) + { + m_szBuffer[m_count] = 0x00; + //MessageBox(NULL, m_szBuffer, "last message", MB_OK); + } + + if (m_pIPL != NULL) + { + FreeMemoryPages(m_pIPL, ROM_SIZE); + m_pIPL = NULL; + } + + // SRAM + FILE* pStream = NULL; + pStream = fopen(Core::GetStartupParameter().m_strSRAM.c_str(), "wb"); + if (pStream != NULL) + { + fwrite(m_SRAM, 1, 64, pStream); + fclose(pStream); + } +} + +void CEXIIPL::SetCS(int _iCS) +{ + if (_iCS) + { // cs transition to high + m_uPosition = 0; + } +} + +bool CEXIIPL::IsPresent() +{ + return true; +} + +void CEXIIPL::TransferByte(u8& _uByte) +{ + // The first 4 bytes must be the address + // If we haven't read it, do it now + if (m_uPosition < 4) + { + m_uAddress <<= 8; + m_uAddress |= _uByte; + m_uRWOffset = 0; + _uByte = 0xFF; + + // Check if the command is complete + if (m_uPosition == 3) + { + // Get the time ... + u32 GCTime = CEXIIPL::GetGCTime(); + u8* pGCTime = (u8*)&GCTime; + for (int i=0; i<4; i++) + { + m_RTC[i] = pGCTime[i^3]; + } + +#ifdef LOGGING + + if ((m_uAddress & 0xF0000000) == 0xb0000000) + { + LOG(EXPANSIONINTERFACE, "EXI IPL-DEV: WII something"); + } + else if ((m_uAddress & 0xF0000000) == 0x30000000) + { + // wii stuff perhaps wii SRAM? + LOG(EXPANSIONINTERFACE, "EXI IPL-DEV: WII something (perhaps SRAM?)"); + } + // debug only + else if ((m_uAddress & 0x60000000) == 0) + { + LOGV(EXPANSIONINTERFACE, 2, "EXI IPL-DEV: IPL access"); + } + else if ((m_uAddress & 0x7FFFFF00) == 0x20000000) + { + LOG(EXPANSIONINTERFACE, "EXI IPL-DEV: RTC access"); + } + else if ((m_uAddress & 0x7FFFFF00) == 0x20000100) + { + LOG(EXPANSIONINTERFACE, "EXI IPL-DEV: SRAM access"); + } + else if ((m_uAddress & 0x7FFFFF00) == 0x20010000) + { + LOGV(EXPANSIONINTERFACE, 3, "EXI IPL-DEV: UART"); + } + else + { + _dbg_assert_(EXPANSIONINTERFACE, 0); + LOG(EXPANSIONINTERFACE, "EXI IPL-DEV: illegal address %08x", m_uAddress); + } +#endif + } + } + else + { + // + // --- ROM --- + // + if ((m_uAddress & 0x60000000) == 0) + { + if ((m_uAddress & 0x80000000) == 0) + _uByte = m_pIPL[((m_uAddress >> 6) & ROM_MASK) + m_uRWOffset]; + } + // + // --- Real Time Clock (RTC) --- + // + else if ((m_uAddress & 0x7FFFFF00) == 0x20000000) + { + if (m_uAddress & 0x80000000) + m_RTC[(m_uAddress & 0x03) + m_uRWOffset] = _uByte; + else + _uByte = m_RTC[(m_uAddress & 0x03) + m_uRWOffset]; + } + // + // --- SRAM --- + // + else if ((m_uAddress & 0x7FFFFF00) == 0x20000100) + { + if (m_uAddress & 0x80000000) + m_SRAM[(m_uAddress & 0x3F) + m_uRWOffset] = _uByte; + else + _uByte = m_SRAM[(m_uAddress & 0x3F) + m_uRWOffset]; + } + // + // --- UART --- + // + else if ((m_uAddress & 0x7FFFFF00) == 0x20010000) + { + if (m_uAddress & 0x80000000) + { + m_szBuffer[m_count++] = _uByte; + if ((m_count >= 256) || (_uByte == 0xD)) + { + m_szBuffer[m_count] = 0x00; + LOG(OSREPORT, "%s", m_szBuffer); + memset(m_szBuffer, 0, sizeof(m_szBuffer)); + m_count = 0; + } + } + else + _uByte = 0x01; // dunno + } + m_uRWOffset++; + } + m_uPosition++; +} + +u32 CEXIIPL::GetGCTime() +{ + // Get SRAM bias + u32 Bias; + + for (int i=0; i<4; i++) + { + ((u8*)&Bias)[i] = sram_dump[0xc + (i^3)]; + } + + // Get the time ... + const u32 cJanuary2000 = 0x386d35a1; // Seconds between 1.1.1970 and 1.1.2000 + u64 ltime = Common::Timer::GetTimeSinceJan1970(); + return ((u32)ltime - cJanuary2000 - Bias); +} + diff --git a/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.cpp b/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.cpp index 8a6a8170cc..4c7d8c4868 100644 --- a/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.cpp +++ b/Source/Core/Core/Src/HW/EXI_DeviceMemoryCard.cpp @@ -1,387 +1,387 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "FileUtil.h" -#include "StringUtil.h" -#include "../Core.h" -#include "../CoreTiming.h" - -#include "EXI_Device.h" -#include "EXI_DeviceMemoryCard.h" - -#define MC_STATUS_BUSY 0x80 -#define MC_STATUS_UNLOCKED 0x40 -#define MC_STATUS_SLEEP 0x20 -#define MC_STATUS_ERASEERROR 0x10 -#define MC_STATUS_PROGRAMEERROR 0x08 -#define MC_STATUS_READY 0x01 - -static CEXIMemoryCard *cards[2]; - -void CEXIMemoryCard::FlushCallback(u64 userdata, int cyclesLate) -{ - CEXIMemoryCard *ptr = cards[userdata]; - ptr->Flush(); -} - -CEXIMemoryCard::CEXIMemoryCard(const std::string& _rName, const std::string& _rFilename, int _card_index) : - m_strFilename(_rFilename) -{ - this->card_index = _card_index; - cards[_card_index] = this; - et_this_card = CoreTiming::RegisterEvent(_rName.c_str(), FlushCallback); - - interruptSwitch = 0; - m_bInterruptSet = 0; - command = 0; - status = MC_STATUS_BUSY | MC_STATUS_UNLOCKED | MC_STATUS_READY; - m_uPosition = 0; - memset(programming_buffer, 0, sizeof(programming_buffer)); - formatDelay = 0; - - //Nintendo Memory Card EXI IDs - //0x00000004 Memory Card 59 4Mbit - //0x00000008 Memory Card 123 8Mb - //0x00000010 Memory Card 251 16Mb - //0x00000020 Memory Card 507 32Mb - //0x00000040 Memory Card 1019 64Mb - //0x00000080 Memory Card 2043 128Mb - - //0x00000510 16Mb "bigben" card - //card_id = 0xc243; - - card_id = 0xc221; // It's a nintendo brand memcard - - FILE* pFile = NULL; - pFile = fopen(m_strFilename.c_str(), "rb"); - if (pFile) - { - fseek( pFile, 0L, SEEK_END ); - u64 MemFileSize = ftell( pFile ); - - switch ((MemFileSize / (8 * 1024))-5) // Convert the filesize in bytes to the "nintendo-size" - { - case 59: - nintendo_card_id = 0x00000004; - memory_card_size = 512 * 1024; - break; - case 123: - nintendo_card_id = 0x00000008; - memory_card_size = 1024 * 1024; - break; - case 251: - nintendo_card_id = 0x00000010; - memory_card_size = 2 * 1024 * 1024; - break; - case 507: - nintendo_card_id = 0x00000020; - memory_card_size = 4 * 1024 * 1024; - break; - case 1019: - nintendo_card_id = 0x00000040; - memory_card_size = 8 * 1024 * 1024; - break; - case 2043: - default: - nintendo_card_id = 0x00000080; - memory_card_size = 16 * 1024 * 1024; - break; - } - - // Return to start otherwise the mem card is "corrupt" - fseek( pFile,0L,SEEK_SET); - - memory_card_content = new u8[memory_card_size]; - memset(memory_card_content, 0xFF, memory_card_size); - - LOG(EXPANSIONINTERFACE, "Reading memory card %s", m_strFilename.c_str()); - fread(memory_card_content, 1, memory_card_size, pFile); - fclose(pFile); - } - else - { - // Create a new 128Mb memcard - nintendo_card_id = 0x00000080; - memory_card_size = 16 * 1024 * 1024; - - memory_card_content = new u8[memory_card_size]; - memset(memory_card_content, 0xFF, memory_card_size); - - LOG(EXPANSIONINTERFACE, "No memory card found. Will create new."); - Flush(); - Core::DisplayMessage(StringFromFormat("Wrote memory card contents to %s", m_strFilename.c_str()), 4000); - } - -} - -void CEXIMemoryCard::Flush(bool exiting) -{ - FILE* pFile = NULL; - pFile = fopen(m_strFilename.c_str(), "wb"); - if (!pFile) - { - std::string dir; - SplitPath(m_strFilename, &dir, 0, 0); - if(!File::IsDirectory(dir.c_str())) - File::CreateDir(dir.c_str()); - pFile = fopen(m_strFilename.c_str(), "wb"); - } - if (!pFile) //Note - pFile changed inside above if - { - PanicAlert("Could not write memory card file %s.\n\n" - "Are you running Dolphin from a CD/DVD, or is the save file maybe write protected?", m_strFilename.c_str()); - return; - } - fwrite(memory_card_content, memory_card_size, 1, pFile); - fclose(pFile); - if (!exiting) - Core::DisplayMessage(StringFromFormat("Wrote memory card contents to %s", GetFileName().c_str()), 4000); -} - - -CEXIMemoryCard::~CEXIMemoryCard() -{ - Flush(true); - delete [] memory_card_content; - memory_card_content = NULL; -} - -bool CEXIMemoryCard::IsPresent() -{ - //return false; - return true; -} - -void CEXIMemoryCard::SetCS(int cs) -{ - if (cs) // not-selected to selected - m_uPosition = 0; - else - { - switch (command) - { - case cmdSectorErase: - if (m_uPosition > 2) - { - memset(memory_card_content + (address & (memory_card_size-1)), 0xFF, 0x2000); - status |= MC_STATUS_BUSY; - status &= ~MC_STATUS_READY; - - //??? - - status |= MC_STATUS_READY; - status &= ~MC_STATUS_BUSY; - - m_bInterruptSet = 1; - } - break; - - case cmdChipErase: - if (m_uPosition > 2) - { - memset(memory_card_content, 0xFF, memory_card_size); - status &= ~MC_STATUS_BUSY; - } - break; - - case cmdPageProgram: - if (m_uPosition >= 5) - { - int count = m_uPosition - 5; - int i=0; - status &= ~0x80; - - while (count--) - { - memory_card_content[address] = programming_buffer[i++]; - i &= 127; - address = (address & ~0x1FF) | ((address+1) & 0x1FF); - } - - status |= MC_STATUS_READY; - status &= ~MC_STATUS_BUSY; - - m_bInterruptSet = 1; - } - - // Page written to memory card, not just to buffer - let's schedule a flush 0.5b cycles into the future (1 sec) - // But first we unschedule already scheduled flushes - no point in flushing once per page for a large write. - CoreTiming::RemoveEvent(et_this_card); - CoreTiming::ScheduleEvent(500000000, et_this_card, card_index); - break; - } - } -} - -void CEXIMemoryCard::Update() -{ - if (formatDelay) - { - formatDelay--; - - if (!formatDelay) - { - status |= MC_STATUS_READY; - status &= ~MC_STATUS_BUSY; - - m_bInterruptSet = 1; - } - } -} - -bool CEXIMemoryCard::IsInterruptSet() -{ - if (interruptSwitch) - return m_bInterruptSet; - return false; -} - -void CEXIMemoryCard::TransferByte(u8 &byte) -{ - LOGV(EXPANSIONINTERFACE, 3, "EXI MEMCARD: > %02x", byte); - if (m_uPosition == 0) - { - command = byte; // first byte is command - byte = 0xFF; // would be tristate, but we don't care. - LOGV(EXPANSIONINTERFACE, 1, "EXI MEMCARD: command %02x", byte) - - if(command == cmdClearStatus) - { - status &= ~MC_STATUS_PROGRAMEERROR; - status &= ~MC_STATUS_ERASEERROR; - - status |= MC_STATUS_READY; - - m_bInterruptSet = 0; - - byte = 0xFF; - m_uPosition = 0; - } - } - else - { - switch (command) - { - case cmdNintendoID: - // - // nintendo card: - // 00 | 80 00 00 00 10 00 00 00 - // "bigben" card: - // 00 | ff 00 00 05 10 00 00 00 00 00 00 00 00 00 00 - // we do it the nintendo way. - if (m_uPosition == 1) - byte = 0x80; // dummy cycle - else - byte = (u8)(nintendo_card_id >> (24-(((m_uPosition-2) & 3) * 8))); - break; - - case cmdReadArray: - switch (m_uPosition) - { - case 1: // AD1 - address = byte << 17; - byte = 0xFF; - break; - case 2: // AD2 - address |= byte << 9; - break; - case 3: // AD3 - address |= (byte & 3) << 7; - break; - case 4: // BA - address |= (byte & 0x7F); - break; - } - if (m_uPosition > 1) // not specified for 1..8, anyway - { - byte = memory_card_content[address & (memory_card_size-1)]; - // after 9 bytes, we start incrementing the address, - // but only the sector offset - the pointer wraps around - if (m_uPosition >= 9) - address = (address & ~0x1FF) | ((address+1) & 0x1FF); - } - break; - - case cmdReadStatus: - // (unspecified for byte 1) - byte = status; - break; - - case cmdReadID: - if (m_uPosition == 1) // (unspecified) - byte = (u8)(card_id >> 8); - else - byte = (u8)((m_uPosition & 1) ? (card_id) : (card_id >> 8)); - break; - - case cmdSectorErase: - switch (m_uPosition) - { - case 1: // AD1 - address = byte << 17; - break; - case 2: // AD2 - address |= byte << 9; - break; - } - byte = 0xFF; - break; - - case cmdSetInterrupt: - if (m_uPosition == 1) - { - interruptSwitch = byte; - } - byte = 0xFF; - break; - - case cmdChipErase: - byte = 0xFF; - break; - - case cmdPageProgram: - switch (m_uPosition) - { - case 1: // AD1 - address = byte << 17; - break; - case 2: // AD2 - address |= byte << 9; - break; - case 3: // AD3 - address |= (byte & 3) << 7; - break; - case 4: // BA - address |= (byte & 0x7F); - break; - } - - if(m_uPosition >= 5) - programming_buffer[((m_uPosition - 5) & 0x7F)] = byte; // wrap around after 128 bytes - - byte = 0xFF; - break; - - default: - LOG(EXPANSIONINTERFACE, "EXI MEMCARD: unknown command byte %02x\n", byte); - byte = 0xFF; - } - } - m_uPosition++; - LOGV(EXPANSIONINTERFACE, 3, "EXI MEMCARD: < %02x", byte); -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "FileUtil.h" +#include "StringUtil.h" +#include "../Core.h" +#include "../CoreTiming.h" + +#include "EXI_Device.h" +#include "EXI_DeviceMemoryCard.h" + +#define MC_STATUS_BUSY 0x80 +#define MC_STATUS_UNLOCKED 0x40 +#define MC_STATUS_SLEEP 0x20 +#define MC_STATUS_ERASEERROR 0x10 +#define MC_STATUS_PROGRAMEERROR 0x08 +#define MC_STATUS_READY 0x01 + +static CEXIMemoryCard *cards[2]; + +void CEXIMemoryCard::FlushCallback(u64 userdata, int cyclesLate) +{ + CEXIMemoryCard *ptr = cards[userdata]; + ptr->Flush(); +} + +CEXIMemoryCard::CEXIMemoryCard(const std::string& _rName, const std::string& _rFilename, int _card_index) : + m_strFilename(_rFilename) +{ + this->card_index = _card_index; + cards[_card_index] = this; + et_this_card = CoreTiming::RegisterEvent(_rName.c_str(), FlushCallback); + + interruptSwitch = 0; + m_bInterruptSet = 0; + command = 0; + status = MC_STATUS_BUSY | MC_STATUS_UNLOCKED | MC_STATUS_READY; + m_uPosition = 0; + memset(programming_buffer, 0, sizeof(programming_buffer)); + formatDelay = 0; + + //Nintendo Memory Card EXI IDs + //0x00000004 Memory Card 59 4Mbit + //0x00000008 Memory Card 123 8Mb + //0x00000010 Memory Card 251 16Mb + //0x00000020 Memory Card 507 32Mb + //0x00000040 Memory Card 1019 64Mb + //0x00000080 Memory Card 2043 128Mb + + //0x00000510 16Mb "bigben" card + //card_id = 0xc243; + + card_id = 0xc221; // It's a nintendo brand memcard + + FILE* pFile = NULL; + pFile = fopen(m_strFilename.c_str(), "rb"); + if (pFile) + { + fseek( pFile, 0L, SEEK_END ); + u64 MemFileSize = ftell( pFile ); + + switch ((MemFileSize / (8 * 1024))-5) // Convert the filesize in bytes to the "nintendo-size" + { + case 59: + nintendo_card_id = 0x00000004; + memory_card_size = 512 * 1024; + break; + case 123: + nintendo_card_id = 0x00000008; + memory_card_size = 1024 * 1024; + break; + case 251: + nintendo_card_id = 0x00000010; + memory_card_size = 2 * 1024 * 1024; + break; + case 507: + nintendo_card_id = 0x00000020; + memory_card_size = 4 * 1024 * 1024; + break; + case 1019: + nintendo_card_id = 0x00000040; + memory_card_size = 8 * 1024 * 1024; + break; + case 2043: + default: + nintendo_card_id = 0x00000080; + memory_card_size = 16 * 1024 * 1024; + break; + } + + // Return to start otherwise the mem card is "corrupt" + fseek( pFile,0L,SEEK_SET); + + memory_card_content = new u8[memory_card_size]; + memset(memory_card_content, 0xFF, memory_card_size); + + LOG(EXPANSIONINTERFACE, "Reading memory card %s", m_strFilename.c_str()); + fread(memory_card_content, 1, memory_card_size, pFile); + fclose(pFile); + } + else + { + // Create a new 128Mb memcard + nintendo_card_id = 0x00000080; + memory_card_size = 16 * 1024 * 1024; + + memory_card_content = new u8[memory_card_size]; + memset(memory_card_content, 0xFF, memory_card_size); + + LOG(EXPANSIONINTERFACE, "No memory card found. Will create new."); + Flush(); + Core::DisplayMessage(StringFromFormat("Wrote memory card contents to %s", m_strFilename.c_str()), 4000); + } + +} + +void CEXIMemoryCard::Flush(bool exiting) +{ + FILE* pFile = NULL; + pFile = fopen(m_strFilename.c_str(), "wb"); + if (!pFile) + { + std::string dir; + SplitPath(m_strFilename, &dir, 0, 0); + if(!File::IsDirectory(dir.c_str())) + File::CreateDir(dir.c_str()); + pFile = fopen(m_strFilename.c_str(), "wb"); + } + if (!pFile) //Note - pFile changed inside above if + { + PanicAlert("Could not write memory card file %s.\n\n" + "Are you running Dolphin from a CD/DVD, or is the save file maybe write protected?", m_strFilename.c_str()); + return; + } + fwrite(memory_card_content, memory_card_size, 1, pFile); + fclose(pFile); + if (!exiting) + Core::DisplayMessage(StringFromFormat("Wrote memory card contents to %s", GetFileName().c_str()), 4000); +} + + +CEXIMemoryCard::~CEXIMemoryCard() +{ + Flush(true); + delete [] memory_card_content; + memory_card_content = NULL; +} + +bool CEXIMemoryCard::IsPresent() +{ + //return false; + return true; +} + +void CEXIMemoryCard::SetCS(int cs) +{ + if (cs) // not-selected to selected + m_uPosition = 0; + else + { + switch (command) + { + case cmdSectorErase: + if (m_uPosition > 2) + { + memset(memory_card_content + (address & (memory_card_size-1)), 0xFF, 0x2000); + status |= MC_STATUS_BUSY; + status &= ~MC_STATUS_READY; + + //??? + + status |= MC_STATUS_READY; + status &= ~MC_STATUS_BUSY; + + m_bInterruptSet = 1; + } + break; + + case cmdChipErase: + if (m_uPosition > 2) + { + memset(memory_card_content, 0xFF, memory_card_size); + status &= ~MC_STATUS_BUSY; + } + break; + + case cmdPageProgram: + if (m_uPosition >= 5) + { + int count = m_uPosition - 5; + int i=0; + status &= ~0x80; + + while (count--) + { + memory_card_content[address] = programming_buffer[i++]; + i &= 127; + address = (address & ~0x1FF) | ((address+1) & 0x1FF); + } + + status |= MC_STATUS_READY; + status &= ~MC_STATUS_BUSY; + + m_bInterruptSet = 1; + } + + // Page written to memory card, not just to buffer - let's schedule a flush 0.5b cycles into the future (1 sec) + // But first we unschedule already scheduled flushes - no point in flushing once per page for a large write. + CoreTiming::RemoveEvent(et_this_card); + CoreTiming::ScheduleEvent(500000000, et_this_card, card_index); + break; + } + } +} + +void CEXIMemoryCard::Update() +{ + if (formatDelay) + { + formatDelay--; + + if (!formatDelay) + { + status |= MC_STATUS_READY; + status &= ~MC_STATUS_BUSY; + + m_bInterruptSet = 1; + } + } +} + +bool CEXIMemoryCard::IsInterruptSet() +{ + if (interruptSwitch) + return m_bInterruptSet; + return false; +} + +void CEXIMemoryCard::TransferByte(u8 &byte) +{ + LOGV(EXPANSIONINTERFACE, 3, "EXI MEMCARD: > %02x", byte); + if (m_uPosition == 0) + { + command = byte; // first byte is command + byte = 0xFF; // would be tristate, but we don't care. + LOGV(EXPANSIONINTERFACE, 1, "EXI MEMCARD: command %02x", byte) + + if(command == cmdClearStatus) + { + status &= ~MC_STATUS_PROGRAMEERROR; + status &= ~MC_STATUS_ERASEERROR; + + status |= MC_STATUS_READY; + + m_bInterruptSet = 0; + + byte = 0xFF; + m_uPosition = 0; + } + } + else + { + switch (command) + { + case cmdNintendoID: + // + // nintendo card: + // 00 | 80 00 00 00 10 00 00 00 + // "bigben" card: + // 00 | ff 00 00 05 10 00 00 00 00 00 00 00 00 00 00 + // we do it the nintendo way. + if (m_uPosition == 1) + byte = 0x80; // dummy cycle + else + byte = (u8)(nintendo_card_id >> (24-(((m_uPosition-2) & 3) * 8))); + break; + + case cmdReadArray: + switch (m_uPosition) + { + case 1: // AD1 + address = byte << 17; + byte = 0xFF; + break; + case 2: // AD2 + address |= byte << 9; + break; + case 3: // AD3 + address |= (byte & 3) << 7; + break; + case 4: // BA + address |= (byte & 0x7F); + break; + } + if (m_uPosition > 1) // not specified for 1..8, anyway + { + byte = memory_card_content[address & (memory_card_size-1)]; + // after 9 bytes, we start incrementing the address, + // but only the sector offset - the pointer wraps around + if (m_uPosition >= 9) + address = (address & ~0x1FF) | ((address+1) & 0x1FF); + } + break; + + case cmdReadStatus: + // (unspecified for byte 1) + byte = status; + break; + + case cmdReadID: + if (m_uPosition == 1) // (unspecified) + byte = (u8)(card_id >> 8); + else + byte = (u8)((m_uPosition & 1) ? (card_id) : (card_id >> 8)); + break; + + case cmdSectorErase: + switch (m_uPosition) + { + case 1: // AD1 + address = byte << 17; + break; + case 2: // AD2 + address |= byte << 9; + break; + } + byte = 0xFF; + break; + + case cmdSetInterrupt: + if (m_uPosition == 1) + { + interruptSwitch = byte; + } + byte = 0xFF; + break; + + case cmdChipErase: + byte = 0xFF; + break; + + case cmdPageProgram: + switch (m_uPosition) + { + case 1: // AD1 + address = byte << 17; + break; + case 2: // AD2 + address |= byte << 9; + break; + case 3: // AD3 + address |= (byte & 3) << 7; + break; + case 4: // BA + address |= (byte & 0x7F); + break; + } + + if(m_uPosition >= 5) + programming_buffer[((m_uPosition - 5) & 0x7F)] = byte; // wrap around after 128 bytes + + byte = 0xFF; + break; + + default: + LOG(EXPANSIONINTERFACE, "EXI MEMCARD: unknown command byte %02x\n", byte); + byte = 0xFF; + } + } + m_uPosition++; + LOGV(EXPANSIONINTERFACE, 3, "EXI MEMCARD: < %02x", byte); +} diff --git a/Source/Core/Core/Src/HW/GPFifo.cpp b/Source/Core/Core/Src/HW/GPFifo.cpp index 6b47cb9147..176108e506 100644 --- a/Source/Core/Core/Src/HW/GPFifo.cpp +++ b/Source/Core/Core/Src/HW/GPFifo.cpp @@ -1,147 +1,147 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "ChunkFile.h" -#include "PeripheralInterface.h" -#include "CommandProcessor.h" -#include "Memmap.h" -#include "../PowerPC/PowerPC.h" - -#include "GPFifo.h" - -namespace GPFifo -{ - -// 32 Byte gather pipe with extra space -// Overfilling is no problem (up to the real limit), CheckGatherPipe will blast the -// contents in nicely sized chunks - -// Other optimizations to think about: - -// If the gp is NOT linked to the fifo, just blast to memory byte by word -// If the gp IS linked to the fifo, use a fast wrapping buffer and skip writing to memory - -// Both of these should actually work! Only problem is that we have to decide at run time, -// the same function could use both methods. Compile 2 different versions of each such block? - -u8 GC_ALIGNED32(m_gatherPipe[GATHER_PIPE_SIZE*16]); //more room, for the fastmodes - -// pipe counter -u32 m_gatherPipeCount = 0; - -void DoState(PointerWrap &p) -{ - p.Do(m_gatherPipe); - p.Do(m_gatherPipeCount); -} - -void Init() -{ - ResetGatherPipe(); -} - -bool IsEmpty() { - return m_gatherPipeCount == 0; -} - -void ResetGatherPipe() -{ - m_gatherPipeCount = 0; -} - -void CheckGatherPipe() -{ - while (m_gatherPipeCount >= GATHER_PIPE_SIZE) - { - // copy the GatherPipe - memcpy(Memory::GetPointer(CPeripheralInterface::Fifo_CPUWritePointer), m_gatherPipe, GATHER_PIPE_SIZE); - - // [F|RES]: i thought GP is forced to mem1 ... strange - // memcpy(&Memory::GetMainRAMPtr()[CPeripheralInterface::Fifo_CPUWritePointer], m_gatherPipe, GATHER_PIPE_SIZE); - - // move back the spill bytes - m_gatherPipeCount -= GATHER_PIPE_SIZE; - // This could be dangerous, memmove or ? - // Assuming that memcpy does its thing in the ordinary direction, there should be no problem. - // Actually, this shouldn't ever be necessary. If we're above 2 "blocks" of data, - // the above memcpy could take care of that easily. TODO - memmove(m_gatherPipe, m_gatherPipe + GATHER_PIPE_SIZE, m_gatherPipeCount); - - // increase the CPUWritePointer - CPeripheralInterface::Fifo_CPUWritePointer += GATHER_PIPE_SIZE; - if (CPeripheralInterface::Fifo_CPUWritePointer > CPeripheralInterface::Fifo_CPUEnd) - _assert_msg_(DYNA_REC, 0, "Fifo_CPUWritePointer out of bounds"); - - if (CPeripheralInterface::Fifo_CPUWritePointer >= CPeripheralInterface::Fifo_CPUEnd) - CPeripheralInterface::Fifo_CPUWritePointer = CPeripheralInterface::Fifo_CPUBase; - - CommandProcessor::GatherPipeBursted(); - } -} - -void Write8(const u8 _iValue, const u32 _iAddress) -{ -// LOG(GPFIFO, "GPFIFO #%x: 0x%02x",CPeripheralInterface::Fifo_CPUWritePointer+m_gatherPipeCount, _iValue); - m_gatherPipe[m_gatherPipeCount] = _iValue; - m_gatherPipeCount++; - CheckGatherPipe(); -} - -void Write16(const u16 _iValue, const u32 _iAddress) -{ -// LOG(GPFIFO, "GPFIFO #%x: 0x%04x",CPeripheralInterface::Fifo_CPUWritePointer+m_gatherPipeCount, _iValue); - *(u16*)(&m_gatherPipe[m_gatherPipeCount]) = Common::swap16(_iValue); - m_gatherPipeCount += 2; - CheckGatherPipe(); -} - -void Write32(const u32 _iValue, const u32 _iAddress) -{ -#ifdef _DEBUG - float floatvalue = *(float*)&_iValue; -// LOG(GPFIFO, "GPFIFO #%x: 0x%08x / %f",CPeripheralInterface::Fifo_CPUWritePointer+m_gatherPipeCount, _iValue, floatvalue); -#endif - *(u32*)(&m_gatherPipe[m_gatherPipeCount]) = Common::swap32(_iValue); - m_gatherPipeCount += 4; - CheckGatherPipe(); -} - -void FastWrite8(const u8 _iValue) -{ - m_gatherPipe[m_gatherPipeCount] = _iValue; - m_gatherPipeCount++; -} - -void FastWrite16(const u16 _iValue) -{ - *(u16*)(&m_gatherPipe[m_gatherPipeCount]) = Common::swap16(_iValue); - m_gatherPipeCount += 2; -} - -void FastWrite32(const u32 _iValue) -{ - *(u32*)(&m_gatherPipe[m_gatherPipeCount]) = Common::swap32(_iValue); - m_gatherPipeCount += 4; -} - -void FastWriteEnd() -{ - CheckGatherPipe(); -} - -} // end of namespace GPFifo +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "ChunkFile.h" +#include "PeripheralInterface.h" +#include "CommandProcessor.h" +#include "Memmap.h" +#include "../PowerPC/PowerPC.h" + +#include "GPFifo.h" + +namespace GPFifo +{ + +// 32 Byte gather pipe with extra space +// Overfilling is no problem (up to the real limit), CheckGatherPipe will blast the +// contents in nicely sized chunks + +// Other optimizations to think about: + +// If the gp is NOT linked to the fifo, just blast to memory byte by word +// If the gp IS linked to the fifo, use a fast wrapping buffer and skip writing to memory + +// Both of these should actually work! Only problem is that we have to decide at run time, +// the same function could use both methods. Compile 2 different versions of each such block? + +u8 GC_ALIGNED32(m_gatherPipe[GATHER_PIPE_SIZE*16]); //more room, for the fastmodes + +// pipe counter +u32 m_gatherPipeCount = 0; + +void DoState(PointerWrap &p) +{ + p.Do(m_gatherPipe); + p.Do(m_gatherPipeCount); +} + +void Init() +{ + ResetGatherPipe(); +} + +bool IsEmpty() { + return m_gatherPipeCount == 0; +} + +void ResetGatherPipe() +{ + m_gatherPipeCount = 0; +} + +void CheckGatherPipe() +{ + while (m_gatherPipeCount >= GATHER_PIPE_SIZE) + { + // copy the GatherPipe + memcpy(Memory::GetPointer(CPeripheralInterface::Fifo_CPUWritePointer), m_gatherPipe, GATHER_PIPE_SIZE); + + // [F|RES]: i thought GP is forced to mem1 ... strange + // memcpy(&Memory::GetMainRAMPtr()[CPeripheralInterface::Fifo_CPUWritePointer], m_gatherPipe, GATHER_PIPE_SIZE); + + // move back the spill bytes + m_gatherPipeCount -= GATHER_PIPE_SIZE; + // This could be dangerous, memmove or ? + // Assuming that memcpy does its thing in the ordinary direction, there should be no problem. + // Actually, this shouldn't ever be necessary. If we're above 2 "blocks" of data, + // the above memcpy could take care of that easily. TODO + memmove(m_gatherPipe, m_gatherPipe + GATHER_PIPE_SIZE, m_gatherPipeCount); + + // increase the CPUWritePointer + CPeripheralInterface::Fifo_CPUWritePointer += GATHER_PIPE_SIZE; + if (CPeripheralInterface::Fifo_CPUWritePointer > CPeripheralInterface::Fifo_CPUEnd) + _assert_msg_(DYNA_REC, 0, "Fifo_CPUWritePointer out of bounds"); + + if (CPeripheralInterface::Fifo_CPUWritePointer >= CPeripheralInterface::Fifo_CPUEnd) + CPeripheralInterface::Fifo_CPUWritePointer = CPeripheralInterface::Fifo_CPUBase; + + CommandProcessor::GatherPipeBursted(); + } +} + +void Write8(const u8 _iValue, const u32 _iAddress) +{ +// LOG(GPFIFO, "GPFIFO #%x: 0x%02x",CPeripheralInterface::Fifo_CPUWritePointer+m_gatherPipeCount, _iValue); + m_gatherPipe[m_gatherPipeCount] = _iValue; + m_gatherPipeCount++; + CheckGatherPipe(); +} + +void Write16(const u16 _iValue, const u32 _iAddress) +{ +// LOG(GPFIFO, "GPFIFO #%x: 0x%04x",CPeripheralInterface::Fifo_CPUWritePointer+m_gatherPipeCount, _iValue); + *(u16*)(&m_gatherPipe[m_gatherPipeCount]) = Common::swap16(_iValue); + m_gatherPipeCount += 2; + CheckGatherPipe(); +} + +void Write32(const u32 _iValue, const u32 _iAddress) +{ +#ifdef _DEBUG + float floatvalue = *(float*)&_iValue; +// LOG(GPFIFO, "GPFIFO #%x: 0x%08x / %f",CPeripheralInterface::Fifo_CPUWritePointer+m_gatherPipeCount, _iValue, floatvalue); +#endif + *(u32*)(&m_gatherPipe[m_gatherPipeCount]) = Common::swap32(_iValue); + m_gatherPipeCount += 4; + CheckGatherPipe(); +} + +void FastWrite8(const u8 _iValue) +{ + m_gatherPipe[m_gatherPipeCount] = _iValue; + m_gatherPipeCount++; +} + +void FastWrite16(const u16 _iValue) +{ + *(u16*)(&m_gatherPipe[m_gatherPipeCount]) = Common::swap16(_iValue); + m_gatherPipeCount += 2; +} + +void FastWrite32(const u32 _iValue) +{ + *(u32*)(&m_gatherPipe[m_gatherPipeCount]) = Common::swap32(_iValue); + m_gatherPipeCount += 4; +} + +void FastWriteEnd() +{ + CheckGatherPipe(); +} + +} // end of namespace GPFifo diff --git a/Source/Core/Core/Src/HW/HW.cpp b/Source/Core/Core/Src/HW/HW.cpp index 4cd1faf586..487e276a23 100644 --- a/Source/Core/Core/Src/HW/HW.cpp +++ b/Source/Core/Core/Src/HW/HW.cpp @@ -1,107 +1,107 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "Thunk.h" -#include "../Core.h" -#include "HW.h" -#include "../PowerPC/PowerPC.h" -#include "CPU.h" -#include "CommandProcessor.h" -#include "DSP.h" -#include "DVDInterface.h" -#include "EXI.h" -#include "GPFifo.h" -#include "Memmap.h" -#include "PeripheralInterface.h" -#include "PixelEngine.h" -#include "SerialInterface.h" -#include "AudioInterface.h" -#include "VideoInterface.h" -#include "WII_IPC.h" -#include "../Plugins/Plugin_Video.h" -#include "../CoreTiming.h" -#include "SystemTimers.h" -#include "../IPC_HLE/WII_IPC_HLE.h" -#include "../State.h" -#include "../PowerPC/PPCAnalyst.h" - -namespace HW -{ - void Init() - { - CoreTiming::Init(); - PPCAnalyst::Init(); - - Thunk_Init(); // not really hw, but this way we know it's inited early :P - State_Init(); - - // Init the whole Hardware - AudioInterface::Init(); - PixelEngine::Init(); - CommandProcessor::Init(); - VideoInterface::Init(); - SerialInterface::Init(); - CPeripheralInterface::Init(); - Memory::Init(); - DSP::Init(); - DVDInterface::Init(); - GPFifo::Init(); - ExpansionInterface::Init(); - CCPU::Init(); - SystemTimers::Init(); - - WII_IPC_HLE_Interface::Init(); - WII_IPCInterface::Init(); - } - - void Shutdown() - { - SystemTimers::Shutdown(); - CCPU::Shutdown(); - ExpansionInterface::Shutdown(); - DVDInterface::Shutdown(); - DSP::Shutdown(); - Memory::Shutdown(); - SerialInterface::Shutdown(); - AudioInterface::Shutdown(); - - WII_IPC_HLE_Interface::Shutdown(); - WII_IPCInterface::Shutdown(); - - State_Shutdown(); - Thunk_Shutdown(); - CoreTiming::Shutdown(); - PPCAnalyst::Shutdown(); - } - - void DoState(PointerWrap &p) - { - Memory::DoState(p); - PixelEngine::DoState(p); - CommandProcessor::DoState(p); - VideoInterface::DoState(p); - SerialInterface::DoState(p); - CPeripheralInterface::DoState(p); - DSP::DoState(p); - DVDInterface::DoState(p); - GPFifo::DoState(p); - ExpansionInterface::DoState(p); - AudioInterface::DoState(p); - WII_IPCInterface::DoState(p); - } -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "Thunk.h" +#include "../Core.h" +#include "HW.h" +#include "../PowerPC/PowerPC.h" +#include "CPU.h" +#include "CommandProcessor.h" +#include "DSP.h" +#include "DVDInterface.h" +#include "EXI.h" +#include "GPFifo.h" +#include "Memmap.h" +#include "PeripheralInterface.h" +#include "PixelEngine.h" +#include "SerialInterface.h" +#include "AudioInterface.h" +#include "VideoInterface.h" +#include "WII_IPC.h" +#include "../Plugins/Plugin_Video.h" +#include "../CoreTiming.h" +#include "SystemTimers.h" +#include "../IPC_HLE/WII_IPC_HLE.h" +#include "../State.h" +#include "../PowerPC/PPCAnalyst.h" + +namespace HW +{ + void Init() + { + CoreTiming::Init(); + PPCAnalyst::Init(); + + Thunk_Init(); // not really hw, but this way we know it's inited early :P + State_Init(); + + // Init the whole Hardware + AudioInterface::Init(); + PixelEngine::Init(); + CommandProcessor::Init(); + VideoInterface::Init(); + SerialInterface::Init(); + CPeripheralInterface::Init(); + Memory::Init(); + DSP::Init(); + DVDInterface::Init(); + GPFifo::Init(); + ExpansionInterface::Init(); + CCPU::Init(); + SystemTimers::Init(); + + WII_IPC_HLE_Interface::Init(); + WII_IPCInterface::Init(); + } + + void Shutdown() + { + SystemTimers::Shutdown(); + CCPU::Shutdown(); + ExpansionInterface::Shutdown(); + DVDInterface::Shutdown(); + DSP::Shutdown(); + Memory::Shutdown(); + SerialInterface::Shutdown(); + AudioInterface::Shutdown(); + + WII_IPC_HLE_Interface::Shutdown(); + WII_IPCInterface::Shutdown(); + + State_Shutdown(); + Thunk_Shutdown(); + CoreTiming::Shutdown(); + PPCAnalyst::Shutdown(); + } + + void DoState(PointerWrap &p) + { + Memory::DoState(p); + PixelEngine::DoState(p); + CommandProcessor::DoState(p); + VideoInterface::DoState(p); + SerialInterface::DoState(p); + CPeripheralInterface::DoState(p); + DSP::DoState(p); + DVDInterface::DoState(p); + GPFifo::DoState(p); + ExpansionInterface::DoState(p); + AudioInterface::DoState(p); + WII_IPCInterface::DoState(p); + } +} diff --git a/Source/Core/Core/Src/HW/Memmap.cpp b/Source/Core/Core/Src/HW/Memmap.cpp index f616d0df06..97aa9af4a6 100644 --- a/Source/Core/Core/Src/HW/Memmap.cpp +++ b/Source/Core/Core/Src/HW/Memmap.cpp @@ -1,1347 +1,1347 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "MemoryUtil.h" -#include "MemArena.h" -#include "ChunkFile.h" - -#include "Memmap.h" -#include "../Core.h" -#include "../PowerPC/PowerPC.h" -#include "../PowerPC/Jit64/JitCache.h" -#include "CPU.h" -#include "PeripheralInterface.h" -#include "DSP.h" -#include "DVDInterface.h" -#include "GPFifo.h" -#include "VideoInterface.h" -#include "SerialInterface.h" -#include "EXI.h" -#include "PixelEngine.h" -#include "CommandProcessor.h" -#include "AudioInterface.h" -#include "MemoryInterface.h" -#include "WII_IOB.h" -#include "WII_IPC.h" - -#include "../Debugger/Debugger_BreakPoints.h" -#include "../Debugger/Debugger_SymbolMap.h" - -namespace Memory -{ - -// GLOBAL DEFINES - -// #define NOCHECK - -static const bool bFakeVMEM = false; - -#ifndef LOGGING - #define NOCHECK -#endif - -// END: GLOBAL DEFINES - - -u8* base = NULL; - -// STATE_TO_SAVE (applies to a lot of things in this file) - -// Pointers to low memory -u8* m_pRAM = NULL; -u8* m_pFakeVMEM = NULL; -u8* m_pEXRAM = NULL; //wii -u8* m_pEFB = NULL; -u8* m_pL1Cache = NULL; -bool m_IsInitialized = false; - -MemArena g_arena; - -// Pointers into the "View" (rarely used) -u8* m_pPhysicalFakeVMEM; -u8* m_pPhysicalRAM; -u8* m_pPhysicalEXRAM; //wii -u8* m_pVirtualCachedRAM; -u8* m_pVirtualUncachedRAM; -u8* m_pVirtualCachedEXRAM; -u8* m_pVirtualUncachedEXRAM; -u8* m_pVirtualEFB; -u8* m_pVirtualL1Cache; - -#define NUMHWMEMFUN 64 -#define HWSHIFT 10 -#define HW_MASK 0x3FF - -writeFn8 hwWrite8 [NUMHWMEMFUN]; -writeFn16 hwWrite16[NUMHWMEMFUN]; -writeFn32 hwWrite32[NUMHWMEMFUN]; -writeFn64 hwWrite64[NUMHWMEMFUN]; - -readFn8 hwRead8 [NUMHWMEMFUN]; -readFn16 hwRead16[NUMHWMEMFUN]; -readFn32 hwRead32[NUMHWMEMFUN]; -readFn64 hwRead64[NUMHWMEMFUN]; - -writeFn8 hwWriteWii8 [NUMHWMEMFUN]; -writeFn16 hwWriteWii16[NUMHWMEMFUN]; -writeFn32 hwWriteWii32[NUMHWMEMFUN]; -writeFn64 hwWriteWii64[NUMHWMEMFUN]; - -readFn8 hwReadWii8 [NUMHWMEMFUN]; -readFn16 hwReadWii16[NUMHWMEMFUN]; -readFn32 hwReadWii32[NUMHWMEMFUN]; -readFn64 hwReadWii64[NUMHWMEMFUN]; - - -inline u8 bswap(u8 val) {return val;} -inline u16 bswap(u16 val) {return Common::swap16(val);} -inline u32 bswap(u32 val) {return Common::swap32(val);} -inline u64 bswap(u64 val) {return Common::swap64(val);} - -u32 CheckDTLB(u32 _Address, XCheckTLBFlag _Flag); - -template -void HWCALL HW_Default_Write(const T _Data, const u32 _Address){ LOG(MASTER_LOG, "Illegal HW Write%i %08x", sizeof(T)*8, _Address);_dbg_assert_(MEMMAP, 0);} - -template -void HWCALL HW_Default_Read(T _Data, const u32 _Address){ LOG(MASTER_LOG, "Illegal HW Read%i %08x", sizeof(T)*8, _Address); _dbg_assert_(MEMMAP, 0);} - -#define PAGE_SHIFT 10 -#define PAGE_SIZE (1 << PAGE_SHIFT) -#define PAGE_MASK (PAGE_SHIFT - 1) - - -template void HWCALL HW_Read_Memory(T &_Data, const u32 _Address) { _Data = *(T*)&P[_Address & PAGE_MASK]; } -template void HWCALL HW_Write_Memory(T _Data, const u32 _Address) { *(T*)&P[_Address & PAGE_MASK] = _Data; } - - -#define BLOCKSIZE 4 -#define CP_START 0x00 //0x0000 >> 10 -#define WII_IPC_START 0x00 //0x0000 >> 10 -#define PE_START 0x04 //0x1000 >> 10 -#define VI_START 0x08 //0x2000 >> 10 -#define PI_START 0x0C //0x3000 >> 10 -#define MI_START 0x10 //0x4000 >> 10 -#define DSP_START 0x14 //0x5000 >> 10 -#define DVD_START 0x18 //0x6000 >> 10 -#define SI_START 0x19 -#define EI_START 0x1A -#define AUDIO_START 0x1B -#define GP_START 0x20 - -void InitHWMemFuncs() -{ - for (int i = 0; i < NUMHWMEMFUN; i++) - { - hwWrite8 [i] = HW_Default_Write; - hwWrite16[i] = HW_Default_Write; - hwWrite32[i] = HW_Default_Write; - hwWrite64[i] = HW_Default_Write; - hwRead8 [i] = HW_Default_Read; - hwRead16 [i] = HW_Default_Read; - hwRead32 [i] = HW_Default_Read; - hwRead64 [i] = HW_Default_Read; - - // To prevent Dolphin from crashing when running Wii executables in Gc mode. - hwWriteWii8 [i] = HW_Default_Write; - hwWriteWii16[i] = HW_Default_Write; - hwWriteWii32[i] = HW_Default_Write; - hwWriteWii64[i] = HW_Default_Write; - hwReadWii8 [i] = HW_Default_Read; - hwReadWii16 [i] = HW_Default_Read; - hwReadWii32 [i] = HW_Default_Read; - hwReadWii64 [i] = HW_Default_Read; - } - - for (int i = 0; i < BLOCKSIZE; i++) - { - hwRead16 [CP_START+i] = CommandProcessor::Read16; - hwWrite16[CP_START+i] = CommandProcessor::Write16; - - hwRead16 [PE_START+i] = PixelEngine::Read16; - hwWrite16[PE_START+i] = PixelEngine::Write16; - hwWrite32[PE_START+i] = PixelEngine::Write32; - - hwRead16 [VI_START+i] = VideoInterface::Read16; - hwRead32 [VI_START+i] = VideoInterface::Read32; - hwWrite16[VI_START+i] = VideoInterface::Write16; - hwWrite32[VI_START+i] = VideoInterface::Write32; - - hwRead32 [PI_START+i] = CPeripheralInterface::Read32; - hwWrite32[PI_START+i] = CPeripheralInterface::Write32; - - hwRead16 [MI_START+i] = MemoryInterface::Read16; - hwRead32 [MI_START+i] = MemoryInterface::Read32; - hwWrite32[MI_START+i] = MemoryInterface::Write32; - hwWrite16[MI_START+i] = MemoryInterface::Write16; - - hwRead16 [DSP_START+i] = DSP::Read16; - hwWrite16[DSP_START+i] = DSP::Write16; - hwRead32 [DSP_START+i] = DSP::Read32; - hwWrite32[DSP_START+i] = DSP::Write32; - } - - hwRead32 [DVD_START] = DVDInterface::Read32; - hwWrite32[DVD_START] = DVDInterface::Write32; - - hwRead32 [SI_START] = SerialInterface::Read32; - hwWrite32[SI_START] = SerialInterface::Write32; - - hwRead32 [EI_START] = ExpansionInterface::Read32; - hwWrite32[EI_START] = ExpansionInterface::Write32; - - hwRead32 [AUDIO_START] = AudioInterface::Read32; - hwWrite32[AUDIO_START] = AudioInterface::Write32; - - hwWrite8 [GP_START] = GPFifo::Write8; - hwWrite16[GP_START] = GPFifo::Write16; - hwWrite32[GP_START] = GPFifo::Write32; -} - - -void InitHWMemFuncsWii() -{ - for (int i = 0; i < NUMHWMEMFUN; i++) - { - hwWrite8 [i] = HW_Default_Write; - hwWrite16[i] = HW_Default_Write; - hwWrite32[i] = HW_Default_Write; - hwWrite64[i] = HW_Default_Write; - hwRead8 [i] = HW_Default_Read; - hwRead16 [i] = HW_Default_Read; - hwRead32 [i] = HW_Default_Read; - hwRead64 [i] = HW_Default_Read; - - hwWriteWii8 [i] = HW_Default_Write; - hwWriteWii16[i] = HW_Default_Write; - hwWriteWii32[i] = HW_Default_Write; - hwWriteWii64[i] = HW_Default_Write; - hwReadWii8 [i] = HW_Default_Read; - hwReadWii16 [i] = HW_Default_Read; - hwReadWii32 [i] = HW_Default_Read; - hwReadWii64 [i] = HW_Default_Read; - } - - // MI, PI, DSP are still mapped to 0xCCxxxxxx - for (int i = 0; i < BLOCKSIZE; i++) - { - hwRead16 [CP_START+i] = CommandProcessor::Read16; - hwWrite16[CP_START+i] = CommandProcessor::Write16; - - hwRead16 [PE_START+i] = PixelEngine::Read16; - hwWrite16[PE_START+i] = PixelEngine::Write16; - hwWrite32[PE_START+i] = PixelEngine::Write32; - - hwRead32 [PI_START+i] = CPeripheralInterface::Read32; - hwWrite32[PI_START+i] = CPeripheralInterface::Write32; - - hwRead16 [VI_START+i] = VideoInterface::Read16; - hwRead32 [VI_START+i] = VideoInterface::Read32; - hwWrite16[VI_START+i] = VideoInterface::Write16; - hwWrite32[VI_START+i] = VideoInterface::Write32; - - hwRead16 [MI_START+i] = MemoryInterface::Read16; - hwRead32 [MI_START+i] = MemoryInterface::Read32; - hwWrite32[MI_START+i] = MemoryInterface::Write32; - hwWrite16[MI_START+i] = MemoryInterface::Write16; - - hwRead16 [DSP_START+i] = DSP::Read16; - hwWrite16[DSP_START+i] = DSP::Write16; - hwRead32 [DSP_START+i] = DSP::Read32; - hwWrite32[DSP_START+i] = DSP::Write32; - } - - hwWrite8 [GP_START] = GPFifo::Write8; - hwWrite16[GP_START] = GPFifo::Write16; - hwWrite32[GP_START] = GPFifo::Write32; - - for (int i = 0; i < BLOCKSIZE; i++) - { - hwReadWii32[WII_IPC_START+i] = WII_IPCInterface::Read32; - hwWriteWii32[WII_IPC_START+i] = WII_IPCInterface::Write32; - } - - hwReadWii32 [DVD_START] = DVDInterface::Read32; - hwWriteWii32[DVD_START] = DVDInterface::Write32; - - hwReadWii32 [SI_START] = SerialInterface::Read32; - hwWriteWii32[SI_START] = SerialInterface::Write32; - - hwReadWii32 [EI_START] = ExpansionInterface::Read32; - hwWriteWii32[EI_START] = ExpansionInterface::Write32; - - // [F|RES] i thought this doesn't exist anymore - hwReadWii32 [AUDIO_START] = AudioInterface::Read32; - hwWriteWii32[AUDIO_START] = AudioInterface::Write32; -} - - -writeFn32 GetHWWriteFun32(const u32 _Address) -{ - return hwWrite32[(_Address>>HWSHIFT) & (NUMHWMEMFUN-1)]; -} - - - - - - -// ======================================================= -/* 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) -{ - switch (_Address >> 24) - { - case 0x00: - case 0x01: - case 0x80: - case 0x81: - case 0xC0: - case 0xC1: - return true; - - 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 (Core::GetStartupParameter().bWii) - return true; - else - return false; - - case 0xE0: - if (_Address < (0xE0000000 + L1_CACHE_SIZE)) - return true; - case 0xCC: - case 0xCD: - case 0xC8: - return true; - } - return false; -} - -void CheckForBadAddresses(u32 Address, u32 Data, bool Read, int Bits) -{ - if (!ValidMemory(Address)) - { - if(Read) - { - LOG(CONSOLE, "Read%i: Program tried to read [%08x] from [%08x]", Bits, Address); - //PanicAlert("Write_U32: Program tried to write [%08x] to [%08x]", _Address); - } - else - { - LOGV(CONSOLE, 0, "Write%i: Program tried to write [%08x] to [%08x]", Bits, Data, Address); - //PanicAlert("Read: Program tried to write [%08x] to [%08x]", Data, Address); - } - } - - if (Address == 0) - { - if(Read) - { - LOGV(CONSOLE, 1, "Read%i: Program read [0x%08x] from [0x%08x] * * * 0 * * *", Bits, Data, Address); - //PanicAlert("Read: Program read [%08x] from [%08x]", Data, Address); - } - else - { - LOGV(CONSOLE, 1, "Write%i: Program wrote [0x%08x] to [0x%08x] * * * 0 * * *", Bits, Data, Address); - //PanicAlert("Read: Program wrote [%08x] to [%08x]", 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) - { - LOGV(CONSOLE, 0, "Read%i: Program read [0x%08x] from [0x%08x] * * * * * * * * * * * *", Bits, Data, Address); - //PanicAlert("Read%i: Program read [%08x] from [%08x]", Bits, Data, Address); - } - else - { - LOGV(CONSOLE, 0, "Write%i: Program wrote [0x%08x] to [0x%08x] * * * * * * * * * * * *", Bits,Data, Address); - //PanicAlert("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);} -// ============= - - - - - - -#define ReadFromHardware2(_var, _type, _Address, EffectiveAddress, flag) \ -{ \ - if ((_Address & 0xC8000000) == 0xC8000000) \ - if (_Address < 0xcc000000) \ - { \ - _var = bswap((*(u##_type*)&m_pEFB[_Address & EFB_MASK])); \ - } \ - else if (_Address <= 0xcc009000) \ - hwRead##_type[(_Address>>HWSHIFT) & (NUMHWMEMFUN-1)](_var, _Address); \ - /* WIIMODE */ \ - else if (((_Address & 0xFF000000) == 0xCD000000) && \ - (_Address <= 0xcd009000)) \ - hwReadWii##_type[(_Address>>HWSHIFT) & (NUMHWMEMFUN-1)](_var, _Address); \ - else if (((_Address & 0xFFF00000) == 0xCD800000) && \ - (_Address <= 0xCD809000)) \ - WII_IOBridge::Read##_type(_var,_Address); \ - else \ - { \ - /* Disabled because the debugger makes trouble with */ \ - /*_dbg_assert_(MEMMAP,0); */ \ - } \ - else if (((_Address & 0xF0000000) == 0x80000000) || \ - ((_Address & 0xF0000000) == 0xC0000000) || \ - ((_Address & 0xF0000000) == 0x00000000)) \ - _var = bswap((*(u##_type*)&m_pRAM[_Address & RAM_MASK])); \ - else if (((_Address & 0xF0000000) == 0x90000000) || \ - ((_Address & 0xF0000000) == 0xD0000000) || \ - ((_Address & 0xF0000000) == 0x10000000)) \ - _var = bswap((*(u##_type*)&m_pEXRAM[_Address & EXRAM_MASK])); \ - else if ((_Address >= 0xE0000000) && (_Address < (0xE0000000+L1_CACHE_SIZE))) \ - { \ - _var = bswap((*(u##_type*)&m_pL1Cache[_Address & L1_CACHE_MASK])); \ - } \ - else if (_Address >= 0xE0000000) \ - PanicAlert("READ: Invalid address: %08x", _Address); \ - else \ - { \ - if (bFakeVMEM && (_Address & 0xFE000000) == 0x7e000000) \ - { \ - _var = bswap((*(u##_type*)&m_pFakeVMEM[_Address & FAKEVMEM_MASK])); \ - } \ - else {/* LOG(MEMMAP,"READ (unknown): %08x (PC: %08x)",_Address,PC);*/ \ - /*CCPU::EnableStepping(TRUE);*/ \ - /*PanicAlert("READ: Unknown Address", "1", MB_OK);*/ \ - u32 tmpAddress = CheckDTLB(EffectiveAddress, flag); \ - tmpAddress =(tmpAddress & 0xFFFFFFF0) | (_Address & 0xF); \ - if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI)) \ - _var = bswap((*(u##_type*)&m_pRAM[tmpAddress & RAM_MASK])); \ - } \ - } \ - /* Uncomment this: CheckForBadAddresses##_type(_Address, _var, true);*/ \ -} - - -#define WriteToHardware2(_type, _Address, _Data, EffectiveAddress, flag) \ -{ \ - /* Uncomment this: CheckForBadAddresses##_type(_Address, _Data, false);*/ \ - if ((_Address & 0xC8000000) == 0xC8000000) \ - { \ - if (_Address < 0xcc000000) \ - { \ - *(u##_type*)&m_pEFB[_Address & EFB_MASK] = bswap(_Data); \ - return; \ - } \ - else if (_Address <= 0xcc009000) { \ - hwWrite##_type[(_Address>>HWSHIFT) & (NUMHWMEMFUN-1)](_Data,_Address); \ - return; \ - } \ - /* WIIMODE */ \ - else if (((_Address & 0xFF000000) == 0xCD000000) && \ - (_Address <= 0xcd009000)) { \ - hwWriteWii##_type[(_Address>>HWSHIFT) & (NUMHWMEMFUN-1)](_Data,_Address); \ - return; \ - } \ - else if (((_Address & 0xFFF00000) == 0xCD800000) && \ - (_Address <= 0xCD809000)) { \ - WII_IOBridge::Write##_type(_Data,_Address); \ - return; \ - } \ - else { \ - LOG(MEMMAP, "hwwrite [%08x] := %08x (PC: %08x)", _Address, _Data, PC); \ - _dbg_assert_msg_(MEMMAP,0,"Memory - Unknown HW address %08x", _Address); \ - } \ - } \ - else if ((_Address >= 0xE0000000) && (_Address < (0xE0000000+L1_CACHE_SIZE))) \ - { \ - *(u##_type*)&m_pL1Cache[_Address & L1_CACHE_MASK] = bswap(_Data); \ - return; \ - } \ - else if (_Address >= 0xE0000000) \ - { \ - LOG(MEMMAP,"WRITE: Cache address out of bounds (addr: %08x data: %08x)",_Address,_Data); \ -/* PanicAlert("WRITE: Cache address %08x out of bounds", _Address); */ \ - } \ - else if (((_Address & 0xF0000000) == 0x80000000) || \ - ((_Address & 0xF0000000) == 0xC0000000) || \ - ((_Address & 0xF0000000) == 0x00000000)) \ - { \ - *(u##_type*)&m_pRAM[_Address & RAM_MASK] = bswap(_Data); \ - return; \ - } \ - else if (((_Address & 0xF0000000) == 0x90000000) || \ - ((_Address & 0xF0000000) == 0xD0000000) || \ - ((_Address & 0xF0000000) == 0x10000000)) \ - { \ - *(u##_type*)&m_pEXRAM[_Address & EXRAM_MASK] = bswap(_Data); \ - return; \ - } \ - else \ - { \ - if (bFakeVMEM && (_Address & 0xFE000000) == 0x7e000000) \ - { \ - *(u##_type*)&m_pFakeVMEM[_Address & FAKEVMEM_MASK] = bswap(_Data); \ - return; \ - } \ - /* LOG(MEMMAP,"WRITE: %08x (PC: %08x)",_Address,PC);*/ \ - /*MessageBox(NULL, "WRITE: unknown Address", "1", MB_OK);*/ \ - /*CCPU::EnableStepping(TRUE);*/ \ - u32 tmpAddress = CheckDTLB(EffectiveAddress, flag); \ - tmpAddress = (tmpAddress & 0xFFFFFFF0) | (_Address & 0xF); \ - *(u##_type*)&m_pRAM[tmpAddress & RAM_MASK] = bswap(_Data); \ - } \ -} - - -bool IsInitialized() -{ - return m_IsInitialized; -} - - -bool Init() -{ - bool wii = Core::GetStartupParameter().bWii; - - int totalMemSize = RAM_SIZE + EFB_SIZE + L1_CACHE_SIZE + IO_SIZE; - if (bFakeVMEM) - totalMemSize += FAKEVMEM_SIZE; - if (wii) - totalMemSize += EXRAM_SIZE; - - //Grab some pagefile backed memory out of the void ... - g_arena.GrabLowMemSpace(totalMemSize); - - //First, map in our regular pointers - int position = 0; - m_pRAM = (u8*)g_arena.CreateView(position, RAM_SIZE); - position += RAM_SIZE; - m_pEFB = (u8*)g_arena.CreateView(position, EFB_SIZE); - position += EFB_SIZE; - m_pL1Cache = (u8*)g_arena.CreateView(position, L1_CACHE_SIZE); - position += L1_CACHE_SIZE; - if (bFakeVMEM) - { - m_pFakeVMEM = (u8*)g_arena.CreateView(position, FAKEVMEM_SIZE); - position += FAKEVMEM_SIZE; - } - - if (wii) - m_pEXRAM = (u8*)g_arena.CreateView(position, EXRAM_SIZE); - -#ifdef _M_X64 - //Then, in x64 mode where we have space, grab a 4GB virtual address space - base = MemArena::Find4GBBase(); - //OK, we know where to find free space. Now grab it! - - //Physical should be unmapped when not in "real mode" - //All in all, we should obey IBAT and DBAT. Maybe IBAT and DBAT should have a 4GB space each? - //It's not like 4GB is anything these days... - position = 0; - m_pPhysicalRAM = (u8*)g_arena.CreateViewAt(position, RAM_SIZE, base + 0x00000000); - m_pVirtualCachedRAM = (u8*)g_arena.CreateViewAt(position, RAM_SIZE, base + 0x80000000); - m_pVirtualUncachedRAM = (u8*)g_arena.CreateViewAt(position, RAM_SIZE, base + 0xC0000000); - position += RAM_SIZE; - m_pVirtualEFB = (u8*)g_arena.CreateViewAt(position, EFB_SIZE, base + 0xC8000000); - position += EFB_SIZE; - m_pVirtualL1Cache = (u8*)g_arena.CreateViewAt(position, L1_CACHE_SIZE, base + 0xE0000000); - position += L1_CACHE_SIZE; - if (bFakeVMEM) { - m_pPhysicalFakeVMEM = (u8*)g_arena.CreateViewAt(position, FAKEVMEM_SIZE, base + 0x7E000000); - position += FAKEVMEM_SIZE; - } - - //WriteProtectMemory(base + 24*1024*1024, 8*1024*1024); - if (wii) - { - m_pPhysicalEXRAM = (u8*)g_arena.CreateViewAt(position, EXRAM_SIZE, base + 0x10000000); - m_pVirtualCachedEXRAM = (u8*)g_arena.CreateViewAt(position, EXRAM_SIZE, base + 0x90000000); - m_pVirtualUncachedEXRAM = (u8*)g_arena.CreateViewAt(position, EXRAM_SIZE, base + 0xD0000000); - } -#else - // Do a poor mans version - just grab 1GB, possibly discontiguous, and use &0x3FFFFFFF as the mask whenever it is accessed. - base = MemArena::Find4GBBase(); - if (!base) { - PanicAlert("Failed to grab 1 GB of contiguous memory!\nDo you have an antivirus program or any other program\n" - "that injects itself into every process, consuming address space?\nOr simply a bad graphics driver?\n\n" - "Dolphin will handle this better in the future by falling back to slow memory emulation.\n" - "For now, sorry, but it won't work. Try the 64-bit build if you can."); - } - position = 0; - m_pPhysicalRAM = (u8*)g_arena.CreateViewAt(position, RAM_SIZE, base + (0x00000000 & MEMVIEW32_MASK)); - m_pVirtualCachedRAM = m_pPhysicalRAM; - m_pVirtualUncachedRAM = m_pPhysicalRAM; - position += RAM_SIZE; - m_pVirtualEFB = (u8*)g_arena.CreateViewAt(position, EFB_SIZE, base + (0xC8000000 & MEMVIEW32_MASK)); - position += EFB_SIZE; - m_pVirtualL1Cache = (u8*)g_arena.CreateViewAt(position, L1_CACHE_SIZE, base + (0xE0000000 & MEMVIEW32_MASK)); - position += L1_CACHE_SIZE; - if (bFakeVMEM) { - m_pPhysicalFakeVMEM = (u8*)g_arena.CreateViewAt(position, FAKEVMEM_SIZE, base + (0x7E000000 & MEMVIEW32_MASK)); - position += FAKEVMEM_SIZE; - } - //WriteProtectMemory(base + 24*1024*1024, 8*1024*1024); - if (wii) - { - m_pPhysicalEXRAM = (u8*)g_arena.CreateViewAt(position, EXRAM_SIZE, base + (0x10000000 & MEMVIEW32_MASK)); - m_pVirtualCachedEXRAM = m_pPhysicalEXRAM; - m_pVirtualUncachedEXRAM = m_pPhysicalEXRAM; - } -#endif - - memset(m_pRAM, 0, RAM_SIZE); - if (wii) { - memset(m_pPhysicalEXRAM, 0, EXRAM_SIZE); - } - memset(m_pEFB, 0, EFB_SIZE); - memset(m_pL1Cache, 0, L1_CACHE_SIZE); - - if (wii) - InitHWMemFuncsWii(); - else - InitHWMemFuncs(); - - m_IsInitialized = true; - return true; -} - -void DoState(PointerWrap &p) -{ - bool wii = Core::GetStartupParameter().bWii; - p.DoArray(m_pRAM, RAM_SIZE); - p.DoArray(m_pEFB, EFB_SIZE); - p.DoArray(m_pL1Cache, L1_CACHE_SIZE); - if (wii) - p.DoArray(m_pEXRAM, EXRAM_SIZE); -} - -bool Shutdown() -{ - m_IsInitialized = false; - - bool wii = Core::GetStartupParameter().bWii; - - g_arena.ReleaseView(m_pRAM, RAM_SIZE); - g_arena.ReleaseView(m_pEFB, EFB_SIZE); - g_arena.ReleaseView(m_pL1Cache, L1_CACHE_SIZE); - if (wii) { - g_arena.ReleaseView(m_pEXRAM, EXRAM_SIZE); - } - if (bFakeVMEM) { - g_arena.ReleaseView(m_pFakeVMEM, FAKEVMEM_SIZE); - } - -#ifdef _M_X64 - g_arena.ReleaseView(m_pPhysicalRAM, RAM_SIZE); - g_arena.ReleaseView(m_pVirtualCachedRAM, RAM_SIZE); - g_arena.ReleaseView(m_pVirtualUncachedRAM, RAM_SIZE); - g_arena.ReleaseView(m_pVirtualEFB, EFB_SIZE); - g_arena.ReleaseView(m_pVirtualL1Cache, L1_CACHE_SIZE); - if (wii) - { - g_arena.ReleaseView(m_pPhysicalEXRAM, EXRAM_SIZE); - g_arena.ReleaseView(m_pVirtualCachedEXRAM, EXRAM_SIZE); - g_arena.ReleaseView(m_pVirtualUncachedEXRAM, EXRAM_SIZE); - } - if (bFakeVMEM) { - g_arena.ReleaseView(m_pPhysicalFakeVMEM, FAKEVMEM_SIZE); - } -#else - g_arena.ReleaseView(m_pPhysicalRAM, RAM_SIZE); - g_arena.ReleaseView(m_pVirtualEFB, EFB_SIZE); - g_arena.ReleaseView(m_pVirtualL1Cache, L1_CACHE_SIZE); - if (wii) - g_arena.ReleaseView(m_pPhysicalEXRAM, EXRAM_SIZE); - if (bFakeVMEM) - g_arena.ReleaseView(m_pPhysicalFakeVMEM, FAKEVMEM_SIZE); -#endif - g_arena.ReleaseSpace(); - return true; -} - - -void Clear() -{ - if (m_pRAM) - memset(m_pRAM, 0, RAM_SIZE); - if (m_pL1Cache) - memset(m_pL1Cache, 0, L1_CACHE_SIZE); - if (m_pEFB) - memset(m_pEFB, 0, EFB_SIZE); - if (Core::GetStartupParameter().bWii && m_pEXRAM) - memset(m_pEXRAM, 0, EXRAM_SIZE); -} - - -bool AreMemoryBreakpointsActivated() -{ -#ifdef NOCHECK - return false; -#else - return true; -#endif -} - - -u32 Read_Instruction(const u32 _Address) -{ - return Jit64::GetOriginalCode(_Address); -} - -u32 Read_Opcode(const u32 _Address) -{ -#ifdef LOGGING - if (_Address == 0x00000000) - { - PanicAlert("Program tried to read from [00000000]"); - return 0x00000000; - } -#endif - - u32 _var = 0; - ReadFromHardware2(_var, 32, _Address, _Address, FLAG_OPCODE); - - return _var; -} - -u8 Read_U8(const u32 _Address) -{ - u8 _var = (u8)0xAFFEAFFE; - ReadFromHardware2(_var, 8, _Address, _Address, FLAG_READ); -#ifndef NOCHECK - TMemCheck *mc = CBreakPoints::GetMemCheck(_Address); - if (mc) - { - mc->numHits++; - mc->Action(_var, _Address,false,1,PC); - } -#endif - return (u8)_var; -} - -u16 Read_U16(const u32 _Address) -{ - u16 _var = 0; - ReadFromHardware2(_var, 16, _Address, _Address, FLAG_READ); -#ifndef NOCHECK - TMemCheck *mc = CBreakPoints::GetMemCheck(_Address); - if (mc) - { - mc->numHits++; - mc->Action(_var, _Address,false,2,PC); - } -#endif - return (u16)_var; -} - -u32 Read_U32(const u32 _Address) -{ -#ifdef LOGGING - if (_Address == 0x00000000) - { - //PanicAlert("Program tried to read from [00000000]"); - //return 0x00000000; - } -#endif - - u32 _var = 0; - ReadFromHardware2(_var, 32, _Address, _Address, FLAG_READ); -#ifndef NOCHECK - TMemCheck *mc = CBreakPoints::GetMemCheck(_Address); - if (mc) - { - mc->numHits++; - mc->Action(_var, _Address,false,4,PC); - } -#endif - return _var; -} - - -u64 Read_U64(const u32 _Address) -{ - u64 _var = 0; - ReadFromHardware2(_var, 64, _Address, _Address, FLAG_READ); -#ifndef NOCHECK - TMemCheck *mc = CBreakPoints::GetMemCheck(_Address); - if (mc) - { - mc->numHits++; - mc->Action((u32)_var, _Address,false,8,PC); - } -#endif - return _var; -} - - -void Write_U8(const u8 _Data, const u32 _Address) -{ -#ifndef NOCHECK - TMemCheck *mc = CBreakPoints::GetMemCheck(_Address); - if (mc) - { - mc->numHits++; - mc->Action(_Data,_Address,true,1,PC); - } -#endif - WriteToHardware2(8, _Address, _Data, _Address, FLAG_WRITE); -} - - -void Write_U16(const u16 _Data, const u32 _Address) -{ -#ifndef NOCHECK - TMemCheck *mc = CBreakPoints::GetMemCheck(_Address); - if (mc) - { - mc->numHits++; - mc->Action(_Data,_Address,true,2,PC); - } -#endif - - WriteToHardware2(16, _Address, _Data, _Address, FLAG_WRITE); -} - - -void Write_U32(const u32 _Data, const u32 _Address) -{ -#ifndef NOCHECK - TMemCheck *mc = CBreakPoints::GetMemCheck(_Address); - if (mc) - { - mc->numHits++; - mc->Action(_Data,_Address,true,4,PC); - } -#endif - WriteToHardware2(32, _Address, _Data, _Address, FLAG_WRITE); -} - - -void WriteHW_U32(const u32 _Data, const u32 _Address) -{ - hwWrite32[(_Address>>HWSHIFT) & (NUMHWMEMFUN-1)](_Data,_Address); -} - - -void Write_U64(const u64 _Data, const u32 _Address) -{ -#ifndef NOCHECK - TMemCheck *mc = CBreakPoints::GetMemCheck(_Address); - if (mc) - { - mc->numHits++; - mc->Action((u32)_Data,_Address,true,8,PC); - } -#endif - - WriteToHardware2(64, _Address, _Data, _Address + 4, FLAG_WRITE); -} - - -u8 ReadUnchecked_U8(const u32 _Address) -{ - u8 _var = (u8)0xAFFEAFFE; - ReadFromHardware2(_var, 8, _Address, _Address, FLAG_NO_EXCEPTION); - return (u8)_var; -} - - -u32 ReadUnchecked_U32(const u32 _Address) -{ - u32 _var = 0; - ReadFromHardware2(_var, 32, _Address, _Address, FLAG_NO_EXCEPTION); - return _var; -} - -void WriteUnchecked_U8(const u8 _iValue, const u32 _Address) -{ - WriteToHardware2(8, _Address, _iValue, _Address, FLAG_NO_EXCEPTION); -} - - -void WriteUnchecked_U32(const u32 _iValue, const u32 _Address) -{ - WriteToHardware2(32, _Address, _iValue, _Address, FLAG_NO_EXCEPTION); -} - - -void WriteBigEData(const u8 *_pData, const u32 _Address, const u32 _iSize) -{ - memcpy(GetPointer(_Address), _pData, _iSize); -} - - -void Memset(const u32 _Address, const u8 _iValue, const u32 _iLength) -{ - u8 *ptr = GetPointer(_Address); - if (ptr != NULL) - { - memset(ptr,_iValue,_iLength); - } - else - { - // (comment for old implementation) : F|RES: rouge squadron and other games use the TLB ... so this cant work - - // fixed implementation: - for (u32 i = 0; i < _iLength; i++) - Write_U8(_iValue, _Address + i); - } -} - - -void DMA_LCToMemory(const u32 _MemAddr, const u32 _CacheAddr, const u32 _iNumBlocks) -{ - u8 *src = GetCachePtr() + (_CacheAddr & 0x3FFFF); - u8 *dst = GetPointer(_MemAddr); - - if ((dst != NULL) && (src != NULL)) - { - memcpy(dst, src, 32 * _iNumBlocks); - } - else - { - for (u32 i = 0; i < 32 * _iNumBlocks; i++) - { - u8 Temp = Read_U8(_CacheAddr + i); - Write_U8(Temp, _MemAddr + i); - } - } -} - - -void DMA_MemoryToLC(const u32 _CacheAddr, const u32 _MemAddr, const u32 _iNumBlocks) -{ - u8 *src = GetPointer(_MemAddr); - u8 *dst = GetCachePtr() + (_CacheAddr & 0x3FFFF); - - if ((dst != NULL) && (src != NULL)) - { - memcpy(dst, src, 32 * _iNumBlocks); - } - else - { - for (u32 i = 0; i < 32 * _iNumBlocks; i++) - { - u8 Temp = Read_U8(_MemAddr + i); - Write_U8(Temp, _CacheAddr + i); - } - } -} - - -void ReadBigEData( u8 *_pData, const u32 _Address, const u32 size) -{ - u8 *src = GetPointer(_Address); - memcpy(_pData, src, size); -} - - -void GetString(std::string& _string, const u32 _Address) -{ - char stringBuffer[2048]; - char *string = stringBuffer; - char c; - int addr = _Address; - while ((c = Read_U8(addr))) - { - *string++ = c; - addr++; - } - *string++=0; - - _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) -{ - switch (_Address >> 24) - { - case 0x00: - case 0x01: - case 0x80: - case 0x81: - case 0xC0: - case 0xC1: - return (u8*)(((char*)m_pRAM) + (_Address&RAM_MASK)); - - 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 (Core::GetStartupParameter().bWii) - return (u8*)(((char*)m_pEXRAM) + (_Address&EXRAM_MASK)); - else - return 0; - - case 0xE0: - if (_Address < (0xE0000000 + L1_CACHE_SIZE)) - return GetCachePtr() + (_Address & L1_CACHE_MASK); - - case 0xC8: - return m_pEFB + (_Address & 0xFFFFFF); - case 0xCC: - case 0xCD: - _dbg_assert_msg_(MEMMAP, 0,"Memory", "GetPointer from IO Bridge doesnt work"); - return NULL; - } - return NULL; -} - - -bool IsRAMAddress(const u32 addr, bool allow_locked_cache) -{ - switch ((addr >> 24) & 0xFC) { - case 0x00: - case 0x80: - case 0xC0: - if ((addr & 0x1FFFFFFF) < RAM_SIZE) - return true; - else - return false; - case 0x10: - case 0x90: - case 0xD0: - if (Core::g_CoreStartupParameter.bWii && (addr & 0x0FFFFFFF) < EXRAM_SIZE) - return true; - else - return false; - case 0xE0: - if (allow_locked_cache && addr - 0xE0000000 < L1_CACHE_SIZE) - return true; - else - return false; - default: - return false; - } -} - -// ********************************************************************************* -// Warning: Test Area -// -// This code is for TESTING and it works in interpreter mode ONLY. Some games (like -// COD iirc) work thanks to this basic TLB emulation. -// It is just a small hack and we have never spend enough time to finalize it. -// Cheers PearPC! -// -// ********************************************************************************* - -/* -* PearPC -* ppc_mmu.cc -* -* Copyright (C) 2003, 2004 Sebastian Biallas (sb@biallas.net) -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License version 2 as -* published by the Free Software Foundation. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. -*/ - - -#define PPC_EXC_DSISR_PAGE (1<<30) -#define PPC_EXC_DSISR_PROT (1<<27) -#define PPC_EXC_DSISR_STORE (1<<25) - -#define SDR1_HTABORG(v) (((v)>>16)&0xffff) -#define SDR1_HTABMASK(v) ((v)&0x1ff) -#define SDR1_PAGETABLE_BASE(v) ((v)&0xffff) -#define SR_T (1<<31) -#define SR_Ks (1<<30) -#define SR_Kp (1<<29) -#define SR_N (1<<28) -#define SR_VSID(v) ((v)&0xffffff) -#define SR_BUID(v) (((v)>>20)&0x1ff) -#define SR_CNTRL_SPEC(v) ((v)&0xfffff) - -#define EA_SR(v) (((v)>>28)&0xf) -#define EA_PageIndex(v) (((v)>>12)&0xffff) -#define EA_Offset(v) ((v)&0xfff) -#define EA_API(v) (((v)>>22)&0x3f) - -#define PA_RPN(v) (((v)>>12)&0xfffff) -#define PA_Offset(v) ((v)&0xfff) - -#define PTE1_V (1<<31) -#define PTE1_VSID(v) (((v)>>7)&0xffffff) -#define PTE1_H (1<<6) -#define PTE1_API(v) ((v)&0x3f) - -#define PTE2_RPN(v) ((v)&0xfffff000) -#define PTE2_R (1<<8) -#define PTE2_C (1<<7) -#define PTE2_WIMG(v) (((v)>>3)&0xf) -#define PTE2_PP(v) ((v)&3) - -union UPTE1 -{ - struct - { - unsigned API : 6; - unsigned H : 1; - unsigned VSID : 24; - unsigned V : 1; - }; - u32 Hex; -}; - -union UPTE2 -{ - struct - { - unsigned PP : 2; - unsigned : 1; - unsigned WIMG : 4; - unsigned C : 1; - unsigned R : 1; - unsigned : 3; - unsigned RPN : 20; - }; - u32 Hex; -}; - -u32 pagetable_base = 0; -u32 pagetable_hashmask = 0; - -void GenerateDSIException(u32 _EffectiveAdress, 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; - - LOG(MEMMAP, "Generate DSI Exception 0x%08x", _EffectiveAdress); - PowerPC::ppcState.Exceptions |= EXCEPTION_DSI; -} - - -void GenerateISIException() -{ - // 4 bit for 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 DBAT register (page fault - // condition); otherwise cleared. - PowerPC::ppcState.spr[SPR_DSISR] = 0x4000000; - LOG(MEMMAP, "Generate ISI Exception"); - PowerPC::ppcState.Exceptions |= EXCEPTION_ISI; -} - - -void SDRUpdated() -{ - u32 htabmask = SDR1_HTABMASK(PowerPC::ppcState.spr[SPR_SDR]); - u32 x = 1; - u32 xx = 0; - int n = 0; - while ((htabmask & x) && (n < 9)) - { - n++; - xx|=x; - x<<=1; - } - if (htabmask & ~xx) - { - return; - } - u32 htaborg = SDR1_HTABORG(PowerPC::ppcState.spr[SPR_SDR]); - if (htaborg & xx) - { - return; - } - pagetable_base = htaborg<<16; - pagetable_hashmask = ((xx<<10)|0x3ff); -} - - -u32 CheckDTLB(u32 _Address, XCheckTLBFlag _Flag) -{ - //return 0; - if (Core::GetStartupParameter().bWii) { - // TLB is never used on Wii (except linux and stuff, but we don't care about that) - PanicAlert("%s invalid memory region (0x%08x)\n\n" - "There is no way to recover from this error," - "so Dolphin will now exit. Sorry!", - _Flag == FLAG_WRITE ? "Write to" : "Read from", _Address); - } - else { - PanicAlert("%s invalid memory region (0x%08x)\n\n" - "This is either the game crashing randomly, or a TLB write." - "Several games uses the TLB to map memory. This\n" - "function is not supported in Dolphin. " - "Unfortunately there is no way to recover from this error," - "so Dolphin will now exit abruptly. Sorry!", - _Flag == FLAG_WRITE ? "Write to" : "Read from", _Address); - } - exit(0); - u32 sr = PowerPC::ppcState.sr[EA_SR(_Address)]; - - u32 offset = EA_Offset(_Address); // 12 bit - u32 page_index = EA_PageIndex(_Address); // 16 bit - u32 VSID = SR_VSID(sr); // 24 bit - u32 api = EA_API(_Address); // 6 bit (part of page_index) - - u8* pRAM = GetPointer(0); - - // hash function no 1 "xor" .360 - u32 hash1 = (VSID ^ page_index); - u32 pteg_addr = ((hash1 & pagetable_hashmask)<<6) | pagetable_base; - - // hash1 - for (int i = 0; i < 8; i++) - { - UPTE1 PTE1; - PTE1.Hex = bswap(*(u32*)&pRAM[pteg_addr]); - - if (PTE1.V && !PTE1.H) - { - if (VSID == PTE1.VSID && (api == PTE1.API)) - { - UPTE2 PTE2; - PTE2.Hex = bswap((*(u32*)&pRAM[(pteg_addr + 4)])); - - // set the access bits - switch (_Flag) - { - case FLAG_READ: PTE2.R = 1; break; - case FLAG_WRITE: PTE2.C = 1; break; - case FLAG_NO_EXCEPTION: break; - case FLAG_OPCODE: break; - } - *(u32*)&pRAM[(pteg_addr + 4)] = bswap(PTE2.Hex); - - return ((PTE2.RPN << 12) | offset); - } - } - pteg_addr+=8; - } - - // hash function no 2 "not" .360 - hash1 = ~hash1; - pteg_addr = ((hash1 & pagetable_hashmask)<<6) | pagetable_base; - for (int i = 0; i < 8; i++) - { - u32 pte = bswap(*(u32*)&pRAM[pteg_addr]); - if ((pte & PTE1_V) && (pte & PTE1_H)) - { - if (VSID == PTE1_VSID(pte) && (api == PTE1_API(pte))) - { - PanicAlert("TLB: Address found at the second hash function.\n" - "i have never seen this before"); - - pte = bswap(*(u32*)&pRAM[(pteg_addr+4)]); - - u32 physAddress = PTE2_RPN(pte) | offset; - - // missing access bits - return physAddress; - } - } - pteg_addr+=8; - } - - - // exception generation - 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; -} - - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "MemoryUtil.h" +#include "MemArena.h" +#include "ChunkFile.h" + +#include "Memmap.h" +#include "../Core.h" +#include "../PowerPC/PowerPC.h" +#include "../PowerPC/Jit64/JitCache.h" +#include "CPU.h" +#include "PeripheralInterface.h" +#include "DSP.h" +#include "DVDInterface.h" +#include "GPFifo.h" +#include "VideoInterface.h" +#include "SerialInterface.h" +#include "EXI.h" +#include "PixelEngine.h" +#include "CommandProcessor.h" +#include "AudioInterface.h" +#include "MemoryInterface.h" +#include "WII_IOB.h" +#include "WII_IPC.h" + +#include "../Debugger/Debugger_BreakPoints.h" +#include "../Debugger/Debugger_SymbolMap.h" + +namespace Memory +{ + +// GLOBAL DEFINES + +// #define NOCHECK + +static const bool bFakeVMEM = false; + +#ifndef LOGGING + #define NOCHECK +#endif + +// END: GLOBAL DEFINES + + +u8* base = NULL; + +// STATE_TO_SAVE (applies to a lot of things in this file) + +// Pointers to low memory +u8* m_pRAM = NULL; +u8* m_pFakeVMEM = NULL; +u8* m_pEXRAM = NULL; //wii +u8* m_pEFB = NULL; +u8* m_pL1Cache = NULL; +bool m_IsInitialized = false; + +MemArena g_arena; + +// Pointers into the "View" (rarely used) +u8* m_pPhysicalFakeVMEM; +u8* m_pPhysicalRAM; +u8* m_pPhysicalEXRAM; //wii +u8* m_pVirtualCachedRAM; +u8* m_pVirtualUncachedRAM; +u8* m_pVirtualCachedEXRAM; +u8* m_pVirtualUncachedEXRAM; +u8* m_pVirtualEFB; +u8* m_pVirtualL1Cache; + +#define NUMHWMEMFUN 64 +#define HWSHIFT 10 +#define HW_MASK 0x3FF + +writeFn8 hwWrite8 [NUMHWMEMFUN]; +writeFn16 hwWrite16[NUMHWMEMFUN]; +writeFn32 hwWrite32[NUMHWMEMFUN]; +writeFn64 hwWrite64[NUMHWMEMFUN]; + +readFn8 hwRead8 [NUMHWMEMFUN]; +readFn16 hwRead16[NUMHWMEMFUN]; +readFn32 hwRead32[NUMHWMEMFUN]; +readFn64 hwRead64[NUMHWMEMFUN]; + +writeFn8 hwWriteWii8 [NUMHWMEMFUN]; +writeFn16 hwWriteWii16[NUMHWMEMFUN]; +writeFn32 hwWriteWii32[NUMHWMEMFUN]; +writeFn64 hwWriteWii64[NUMHWMEMFUN]; + +readFn8 hwReadWii8 [NUMHWMEMFUN]; +readFn16 hwReadWii16[NUMHWMEMFUN]; +readFn32 hwReadWii32[NUMHWMEMFUN]; +readFn64 hwReadWii64[NUMHWMEMFUN]; + + +inline u8 bswap(u8 val) {return val;} +inline u16 bswap(u16 val) {return Common::swap16(val);} +inline u32 bswap(u32 val) {return Common::swap32(val);} +inline u64 bswap(u64 val) {return Common::swap64(val);} + +u32 CheckDTLB(u32 _Address, XCheckTLBFlag _Flag); + +template +void HWCALL HW_Default_Write(const T _Data, const u32 _Address){ LOG(MASTER_LOG, "Illegal HW Write%i %08x", sizeof(T)*8, _Address);_dbg_assert_(MEMMAP, 0);} + +template +void HWCALL HW_Default_Read(T _Data, const u32 _Address){ LOG(MASTER_LOG, "Illegal HW Read%i %08x", sizeof(T)*8, _Address); _dbg_assert_(MEMMAP, 0);} + +#define PAGE_SHIFT 10 +#define PAGE_SIZE (1 << PAGE_SHIFT) +#define PAGE_MASK (PAGE_SHIFT - 1) + + +template void HWCALL HW_Read_Memory(T &_Data, const u32 _Address) { _Data = *(T*)&P[_Address & PAGE_MASK]; } +template void HWCALL HW_Write_Memory(T _Data, const u32 _Address) { *(T*)&P[_Address & PAGE_MASK] = _Data; } + + +#define BLOCKSIZE 4 +#define CP_START 0x00 //0x0000 >> 10 +#define WII_IPC_START 0x00 //0x0000 >> 10 +#define PE_START 0x04 //0x1000 >> 10 +#define VI_START 0x08 //0x2000 >> 10 +#define PI_START 0x0C //0x3000 >> 10 +#define MI_START 0x10 //0x4000 >> 10 +#define DSP_START 0x14 //0x5000 >> 10 +#define DVD_START 0x18 //0x6000 >> 10 +#define SI_START 0x19 +#define EI_START 0x1A +#define AUDIO_START 0x1B +#define GP_START 0x20 + +void InitHWMemFuncs() +{ + for (int i = 0; i < NUMHWMEMFUN; i++) + { + hwWrite8 [i] = HW_Default_Write; + hwWrite16[i] = HW_Default_Write; + hwWrite32[i] = HW_Default_Write; + hwWrite64[i] = HW_Default_Write; + hwRead8 [i] = HW_Default_Read; + hwRead16 [i] = HW_Default_Read; + hwRead32 [i] = HW_Default_Read; + hwRead64 [i] = HW_Default_Read; + + // To prevent Dolphin from crashing when running Wii executables in Gc mode. + hwWriteWii8 [i] = HW_Default_Write; + hwWriteWii16[i] = HW_Default_Write; + hwWriteWii32[i] = HW_Default_Write; + hwWriteWii64[i] = HW_Default_Write; + hwReadWii8 [i] = HW_Default_Read; + hwReadWii16 [i] = HW_Default_Read; + hwReadWii32 [i] = HW_Default_Read; + hwReadWii64 [i] = HW_Default_Read; + } + + for (int i = 0; i < BLOCKSIZE; i++) + { + hwRead16 [CP_START+i] = CommandProcessor::Read16; + hwWrite16[CP_START+i] = CommandProcessor::Write16; + + hwRead16 [PE_START+i] = PixelEngine::Read16; + hwWrite16[PE_START+i] = PixelEngine::Write16; + hwWrite32[PE_START+i] = PixelEngine::Write32; + + hwRead16 [VI_START+i] = VideoInterface::Read16; + hwRead32 [VI_START+i] = VideoInterface::Read32; + hwWrite16[VI_START+i] = VideoInterface::Write16; + hwWrite32[VI_START+i] = VideoInterface::Write32; + + hwRead32 [PI_START+i] = CPeripheralInterface::Read32; + hwWrite32[PI_START+i] = CPeripheralInterface::Write32; + + hwRead16 [MI_START+i] = MemoryInterface::Read16; + hwRead32 [MI_START+i] = MemoryInterface::Read32; + hwWrite32[MI_START+i] = MemoryInterface::Write32; + hwWrite16[MI_START+i] = MemoryInterface::Write16; + + hwRead16 [DSP_START+i] = DSP::Read16; + hwWrite16[DSP_START+i] = DSP::Write16; + hwRead32 [DSP_START+i] = DSP::Read32; + hwWrite32[DSP_START+i] = DSP::Write32; + } + + hwRead32 [DVD_START] = DVDInterface::Read32; + hwWrite32[DVD_START] = DVDInterface::Write32; + + hwRead32 [SI_START] = SerialInterface::Read32; + hwWrite32[SI_START] = SerialInterface::Write32; + + hwRead32 [EI_START] = ExpansionInterface::Read32; + hwWrite32[EI_START] = ExpansionInterface::Write32; + + hwRead32 [AUDIO_START] = AudioInterface::Read32; + hwWrite32[AUDIO_START] = AudioInterface::Write32; + + hwWrite8 [GP_START] = GPFifo::Write8; + hwWrite16[GP_START] = GPFifo::Write16; + hwWrite32[GP_START] = GPFifo::Write32; +} + + +void InitHWMemFuncsWii() +{ + for (int i = 0; i < NUMHWMEMFUN; i++) + { + hwWrite8 [i] = HW_Default_Write; + hwWrite16[i] = HW_Default_Write; + hwWrite32[i] = HW_Default_Write; + hwWrite64[i] = HW_Default_Write; + hwRead8 [i] = HW_Default_Read; + hwRead16 [i] = HW_Default_Read; + hwRead32 [i] = HW_Default_Read; + hwRead64 [i] = HW_Default_Read; + + hwWriteWii8 [i] = HW_Default_Write; + hwWriteWii16[i] = HW_Default_Write; + hwWriteWii32[i] = HW_Default_Write; + hwWriteWii64[i] = HW_Default_Write; + hwReadWii8 [i] = HW_Default_Read; + hwReadWii16 [i] = HW_Default_Read; + hwReadWii32 [i] = HW_Default_Read; + hwReadWii64 [i] = HW_Default_Read; + } + + // MI, PI, DSP are still mapped to 0xCCxxxxxx + for (int i = 0; i < BLOCKSIZE; i++) + { + hwRead16 [CP_START+i] = CommandProcessor::Read16; + hwWrite16[CP_START+i] = CommandProcessor::Write16; + + hwRead16 [PE_START+i] = PixelEngine::Read16; + hwWrite16[PE_START+i] = PixelEngine::Write16; + hwWrite32[PE_START+i] = PixelEngine::Write32; + + hwRead32 [PI_START+i] = CPeripheralInterface::Read32; + hwWrite32[PI_START+i] = CPeripheralInterface::Write32; + + hwRead16 [VI_START+i] = VideoInterface::Read16; + hwRead32 [VI_START+i] = VideoInterface::Read32; + hwWrite16[VI_START+i] = VideoInterface::Write16; + hwWrite32[VI_START+i] = VideoInterface::Write32; + + hwRead16 [MI_START+i] = MemoryInterface::Read16; + hwRead32 [MI_START+i] = MemoryInterface::Read32; + hwWrite32[MI_START+i] = MemoryInterface::Write32; + hwWrite16[MI_START+i] = MemoryInterface::Write16; + + hwRead16 [DSP_START+i] = DSP::Read16; + hwWrite16[DSP_START+i] = DSP::Write16; + hwRead32 [DSP_START+i] = DSP::Read32; + hwWrite32[DSP_START+i] = DSP::Write32; + } + + hwWrite8 [GP_START] = GPFifo::Write8; + hwWrite16[GP_START] = GPFifo::Write16; + hwWrite32[GP_START] = GPFifo::Write32; + + for (int i = 0; i < BLOCKSIZE; i++) + { + hwReadWii32[WII_IPC_START+i] = WII_IPCInterface::Read32; + hwWriteWii32[WII_IPC_START+i] = WII_IPCInterface::Write32; + } + + hwReadWii32 [DVD_START] = DVDInterface::Read32; + hwWriteWii32[DVD_START] = DVDInterface::Write32; + + hwReadWii32 [SI_START] = SerialInterface::Read32; + hwWriteWii32[SI_START] = SerialInterface::Write32; + + hwReadWii32 [EI_START] = ExpansionInterface::Read32; + hwWriteWii32[EI_START] = ExpansionInterface::Write32; + + // [F|RES] i thought this doesn't exist anymore + hwReadWii32 [AUDIO_START] = AudioInterface::Read32; + hwWriteWii32[AUDIO_START] = AudioInterface::Write32; +} + + +writeFn32 GetHWWriteFun32(const u32 _Address) +{ + return hwWrite32[(_Address>>HWSHIFT) & (NUMHWMEMFUN-1)]; +} + + + + + + +// ======================================================= +/* 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) +{ + switch (_Address >> 24) + { + case 0x00: + case 0x01: + case 0x80: + case 0x81: + case 0xC0: + case 0xC1: + return true; + + 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 (Core::GetStartupParameter().bWii) + return true; + else + return false; + + case 0xE0: + if (_Address < (0xE0000000 + L1_CACHE_SIZE)) + return true; + case 0xCC: + case 0xCD: + case 0xC8: + return true; + } + return false; +} + +void CheckForBadAddresses(u32 Address, u32 Data, bool Read, int Bits) +{ + if (!ValidMemory(Address)) + { + if(Read) + { + LOG(CONSOLE, "Read%i: Program tried to read [%08x] from [%08x]", Bits, Address); + //PanicAlert("Write_U32: Program tried to write [%08x] to [%08x]", _Address); + } + else + { + LOGV(CONSOLE, 0, "Write%i: Program tried to write [%08x] to [%08x]", Bits, Data, Address); + //PanicAlert("Read: Program tried to write [%08x] to [%08x]", Data, Address); + } + } + + if (Address == 0) + { + if(Read) + { + LOGV(CONSOLE, 1, "Read%i: Program read [0x%08x] from [0x%08x] * * * 0 * * *", Bits, Data, Address); + //PanicAlert("Read: Program read [%08x] from [%08x]", Data, Address); + } + else + { + LOGV(CONSOLE, 1, "Write%i: Program wrote [0x%08x] to [0x%08x] * * * 0 * * *", Bits, Data, Address); + //PanicAlert("Read: Program wrote [%08x] to [%08x]", 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) + { + LOGV(CONSOLE, 0, "Read%i: Program read [0x%08x] from [0x%08x] * * * * * * * * * * * *", Bits, Data, Address); + //PanicAlert("Read%i: Program read [%08x] from [%08x]", Bits, Data, Address); + } + else + { + LOGV(CONSOLE, 0, "Write%i: Program wrote [0x%08x] to [0x%08x] * * * * * * * * * * * *", Bits,Data, Address); + //PanicAlert("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);} +// ============= + + + + + + +#define ReadFromHardware2(_var, _type, _Address, EffectiveAddress, flag) \ +{ \ + if ((_Address & 0xC8000000) == 0xC8000000) \ + if (_Address < 0xcc000000) \ + { \ + _var = bswap((*(u##_type*)&m_pEFB[_Address & EFB_MASK])); \ + } \ + else if (_Address <= 0xcc009000) \ + hwRead##_type[(_Address>>HWSHIFT) & (NUMHWMEMFUN-1)](_var, _Address); \ + /* WIIMODE */ \ + else if (((_Address & 0xFF000000) == 0xCD000000) && \ + (_Address <= 0xcd009000)) \ + hwReadWii##_type[(_Address>>HWSHIFT) & (NUMHWMEMFUN-1)](_var, _Address); \ + else if (((_Address & 0xFFF00000) == 0xCD800000) && \ + (_Address <= 0xCD809000)) \ + WII_IOBridge::Read##_type(_var,_Address); \ + else \ + { \ + /* Disabled because the debugger makes trouble with */ \ + /*_dbg_assert_(MEMMAP,0); */ \ + } \ + else if (((_Address & 0xF0000000) == 0x80000000) || \ + ((_Address & 0xF0000000) == 0xC0000000) || \ + ((_Address & 0xF0000000) == 0x00000000)) \ + _var = bswap((*(u##_type*)&m_pRAM[_Address & RAM_MASK])); \ + else if (((_Address & 0xF0000000) == 0x90000000) || \ + ((_Address & 0xF0000000) == 0xD0000000) || \ + ((_Address & 0xF0000000) == 0x10000000)) \ + _var = bswap((*(u##_type*)&m_pEXRAM[_Address & EXRAM_MASK])); \ + else if ((_Address >= 0xE0000000) && (_Address < (0xE0000000+L1_CACHE_SIZE))) \ + { \ + _var = bswap((*(u##_type*)&m_pL1Cache[_Address & L1_CACHE_MASK])); \ + } \ + else if (_Address >= 0xE0000000) \ + PanicAlert("READ: Invalid address: %08x", _Address); \ + else \ + { \ + if (bFakeVMEM && (_Address & 0xFE000000) == 0x7e000000) \ + { \ + _var = bswap((*(u##_type*)&m_pFakeVMEM[_Address & FAKEVMEM_MASK])); \ + } \ + else {/* LOG(MEMMAP,"READ (unknown): %08x (PC: %08x)",_Address,PC);*/ \ + /*CCPU::EnableStepping(TRUE);*/ \ + /*PanicAlert("READ: Unknown Address", "1", MB_OK);*/ \ + u32 tmpAddress = CheckDTLB(EffectiveAddress, flag); \ + tmpAddress =(tmpAddress & 0xFFFFFFF0) | (_Address & 0xF); \ + if (!(PowerPC::ppcState.Exceptions & EXCEPTION_DSI)) \ + _var = bswap((*(u##_type*)&m_pRAM[tmpAddress & RAM_MASK])); \ + } \ + } \ + /* Uncomment this: CheckForBadAddresses##_type(_Address, _var, true);*/ \ +} + + +#define WriteToHardware2(_type, _Address, _Data, EffectiveAddress, flag) \ +{ \ + /* Uncomment this: CheckForBadAddresses##_type(_Address, _Data, false);*/ \ + if ((_Address & 0xC8000000) == 0xC8000000) \ + { \ + if (_Address < 0xcc000000) \ + { \ + *(u##_type*)&m_pEFB[_Address & EFB_MASK] = bswap(_Data); \ + return; \ + } \ + else if (_Address <= 0xcc009000) { \ + hwWrite##_type[(_Address>>HWSHIFT) & (NUMHWMEMFUN-1)](_Data,_Address); \ + return; \ + } \ + /* WIIMODE */ \ + else if (((_Address & 0xFF000000) == 0xCD000000) && \ + (_Address <= 0xcd009000)) { \ + hwWriteWii##_type[(_Address>>HWSHIFT) & (NUMHWMEMFUN-1)](_Data,_Address); \ + return; \ + } \ + else if (((_Address & 0xFFF00000) == 0xCD800000) && \ + (_Address <= 0xCD809000)) { \ + WII_IOBridge::Write##_type(_Data,_Address); \ + return; \ + } \ + else { \ + LOG(MEMMAP, "hwwrite [%08x] := %08x (PC: %08x)", _Address, _Data, PC); \ + _dbg_assert_msg_(MEMMAP,0,"Memory - Unknown HW address %08x", _Address); \ + } \ + } \ + else if ((_Address >= 0xE0000000) && (_Address < (0xE0000000+L1_CACHE_SIZE))) \ + { \ + *(u##_type*)&m_pL1Cache[_Address & L1_CACHE_MASK] = bswap(_Data); \ + return; \ + } \ + else if (_Address >= 0xE0000000) \ + { \ + LOG(MEMMAP,"WRITE: Cache address out of bounds (addr: %08x data: %08x)",_Address,_Data); \ +/* PanicAlert("WRITE: Cache address %08x out of bounds", _Address); */ \ + } \ + else if (((_Address & 0xF0000000) == 0x80000000) || \ + ((_Address & 0xF0000000) == 0xC0000000) || \ + ((_Address & 0xF0000000) == 0x00000000)) \ + { \ + *(u##_type*)&m_pRAM[_Address & RAM_MASK] = bswap(_Data); \ + return; \ + } \ + else if (((_Address & 0xF0000000) == 0x90000000) || \ + ((_Address & 0xF0000000) == 0xD0000000) || \ + ((_Address & 0xF0000000) == 0x10000000)) \ + { \ + *(u##_type*)&m_pEXRAM[_Address & EXRAM_MASK] = bswap(_Data); \ + return; \ + } \ + else \ + { \ + if (bFakeVMEM && (_Address & 0xFE000000) == 0x7e000000) \ + { \ + *(u##_type*)&m_pFakeVMEM[_Address & FAKEVMEM_MASK] = bswap(_Data); \ + return; \ + } \ + /* LOG(MEMMAP,"WRITE: %08x (PC: %08x)",_Address,PC);*/ \ + /*MessageBox(NULL, "WRITE: unknown Address", "1", MB_OK);*/ \ + /*CCPU::EnableStepping(TRUE);*/ \ + u32 tmpAddress = CheckDTLB(EffectiveAddress, flag); \ + tmpAddress = (tmpAddress & 0xFFFFFFF0) | (_Address & 0xF); \ + *(u##_type*)&m_pRAM[tmpAddress & RAM_MASK] = bswap(_Data); \ + } \ +} + + +bool IsInitialized() +{ + return m_IsInitialized; +} + + +bool Init() +{ + bool wii = Core::GetStartupParameter().bWii; + + int totalMemSize = RAM_SIZE + EFB_SIZE + L1_CACHE_SIZE + IO_SIZE; + if (bFakeVMEM) + totalMemSize += FAKEVMEM_SIZE; + if (wii) + totalMemSize += EXRAM_SIZE; + + //Grab some pagefile backed memory out of the void ... + g_arena.GrabLowMemSpace(totalMemSize); + + //First, map in our regular pointers + int position = 0; + m_pRAM = (u8*)g_arena.CreateView(position, RAM_SIZE); + position += RAM_SIZE; + m_pEFB = (u8*)g_arena.CreateView(position, EFB_SIZE); + position += EFB_SIZE; + m_pL1Cache = (u8*)g_arena.CreateView(position, L1_CACHE_SIZE); + position += L1_CACHE_SIZE; + if (bFakeVMEM) + { + m_pFakeVMEM = (u8*)g_arena.CreateView(position, FAKEVMEM_SIZE); + position += FAKEVMEM_SIZE; + } + + if (wii) + m_pEXRAM = (u8*)g_arena.CreateView(position, EXRAM_SIZE); + +#ifdef _M_X64 + //Then, in x64 mode where we have space, grab a 4GB virtual address space + base = MemArena::Find4GBBase(); + //OK, we know where to find free space. Now grab it! + + //Physical should be unmapped when not in "real mode" + //All in all, we should obey IBAT and DBAT. Maybe IBAT and DBAT should have a 4GB space each? + //It's not like 4GB is anything these days... + position = 0; + m_pPhysicalRAM = (u8*)g_arena.CreateViewAt(position, RAM_SIZE, base + 0x00000000); + m_pVirtualCachedRAM = (u8*)g_arena.CreateViewAt(position, RAM_SIZE, base + 0x80000000); + m_pVirtualUncachedRAM = (u8*)g_arena.CreateViewAt(position, RAM_SIZE, base + 0xC0000000); + position += RAM_SIZE; + m_pVirtualEFB = (u8*)g_arena.CreateViewAt(position, EFB_SIZE, base + 0xC8000000); + position += EFB_SIZE; + m_pVirtualL1Cache = (u8*)g_arena.CreateViewAt(position, L1_CACHE_SIZE, base + 0xE0000000); + position += L1_CACHE_SIZE; + if (bFakeVMEM) { + m_pPhysicalFakeVMEM = (u8*)g_arena.CreateViewAt(position, FAKEVMEM_SIZE, base + 0x7E000000); + position += FAKEVMEM_SIZE; + } + + //WriteProtectMemory(base + 24*1024*1024, 8*1024*1024); + if (wii) + { + m_pPhysicalEXRAM = (u8*)g_arena.CreateViewAt(position, EXRAM_SIZE, base + 0x10000000); + m_pVirtualCachedEXRAM = (u8*)g_arena.CreateViewAt(position, EXRAM_SIZE, base + 0x90000000); + m_pVirtualUncachedEXRAM = (u8*)g_arena.CreateViewAt(position, EXRAM_SIZE, base + 0xD0000000); + } +#else + // Do a poor mans version - just grab 1GB, possibly discontiguous, and use &0x3FFFFFFF as the mask whenever it is accessed. + base = MemArena::Find4GBBase(); + if (!base) { + PanicAlert("Failed to grab 1 GB of contiguous memory!\nDo you have an antivirus program or any other program\n" + "that injects itself into every process, consuming address space?\nOr simply a bad graphics driver?\n\n" + "Dolphin will handle this better in the future by falling back to slow memory emulation.\n" + "For now, sorry, but it won't work. Try the 64-bit build if you can."); + } + position = 0; + m_pPhysicalRAM = (u8*)g_arena.CreateViewAt(position, RAM_SIZE, base + (0x00000000 & MEMVIEW32_MASK)); + m_pVirtualCachedRAM = m_pPhysicalRAM; + m_pVirtualUncachedRAM = m_pPhysicalRAM; + position += RAM_SIZE; + m_pVirtualEFB = (u8*)g_arena.CreateViewAt(position, EFB_SIZE, base + (0xC8000000 & MEMVIEW32_MASK)); + position += EFB_SIZE; + m_pVirtualL1Cache = (u8*)g_arena.CreateViewAt(position, L1_CACHE_SIZE, base + (0xE0000000 & MEMVIEW32_MASK)); + position += L1_CACHE_SIZE; + if (bFakeVMEM) { + m_pPhysicalFakeVMEM = (u8*)g_arena.CreateViewAt(position, FAKEVMEM_SIZE, base + (0x7E000000 & MEMVIEW32_MASK)); + position += FAKEVMEM_SIZE; + } + //WriteProtectMemory(base + 24*1024*1024, 8*1024*1024); + if (wii) + { + m_pPhysicalEXRAM = (u8*)g_arena.CreateViewAt(position, EXRAM_SIZE, base + (0x10000000 & MEMVIEW32_MASK)); + m_pVirtualCachedEXRAM = m_pPhysicalEXRAM; + m_pVirtualUncachedEXRAM = m_pPhysicalEXRAM; + } +#endif + + memset(m_pRAM, 0, RAM_SIZE); + if (wii) { + memset(m_pPhysicalEXRAM, 0, EXRAM_SIZE); + } + memset(m_pEFB, 0, EFB_SIZE); + memset(m_pL1Cache, 0, L1_CACHE_SIZE); + + if (wii) + InitHWMemFuncsWii(); + else + InitHWMemFuncs(); + + m_IsInitialized = true; + return true; +} + +void DoState(PointerWrap &p) +{ + bool wii = Core::GetStartupParameter().bWii; + p.DoArray(m_pRAM, RAM_SIZE); + p.DoArray(m_pEFB, EFB_SIZE); + p.DoArray(m_pL1Cache, L1_CACHE_SIZE); + if (wii) + p.DoArray(m_pEXRAM, EXRAM_SIZE); +} + +bool Shutdown() +{ + m_IsInitialized = false; + + bool wii = Core::GetStartupParameter().bWii; + + g_arena.ReleaseView(m_pRAM, RAM_SIZE); + g_arena.ReleaseView(m_pEFB, EFB_SIZE); + g_arena.ReleaseView(m_pL1Cache, L1_CACHE_SIZE); + if (wii) { + g_arena.ReleaseView(m_pEXRAM, EXRAM_SIZE); + } + if (bFakeVMEM) { + g_arena.ReleaseView(m_pFakeVMEM, FAKEVMEM_SIZE); + } + +#ifdef _M_X64 + g_arena.ReleaseView(m_pPhysicalRAM, RAM_SIZE); + g_arena.ReleaseView(m_pVirtualCachedRAM, RAM_SIZE); + g_arena.ReleaseView(m_pVirtualUncachedRAM, RAM_SIZE); + g_arena.ReleaseView(m_pVirtualEFB, EFB_SIZE); + g_arena.ReleaseView(m_pVirtualL1Cache, L1_CACHE_SIZE); + if (wii) + { + g_arena.ReleaseView(m_pPhysicalEXRAM, EXRAM_SIZE); + g_arena.ReleaseView(m_pVirtualCachedEXRAM, EXRAM_SIZE); + g_arena.ReleaseView(m_pVirtualUncachedEXRAM, EXRAM_SIZE); + } + if (bFakeVMEM) { + g_arena.ReleaseView(m_pPhysicalFakeVMEM, FAKEVMEM_SIZE); + } +#else + g_arena.ReleaseView(m_pPhysicalRAM, RAM_SIZE); + g_arena.ReleaseView(m_pVirtualEFB, EFB_SIZE); + g_arena.ReleaseView(m_pVirtualL1Cache, L1_CACHE_SIZE); + if (wii) + g_arena.ReleaseView(m_pPhysicalEXRAM, EXRAM_SIZE); + if (bFakeVMEM) + g_arena.ReleaseView(m_pPhysicalFakeVMEM, FAKEVMEM_SIZE); +#endif + g_arena.ReleaseSpace(); + return true; +} + + +void Clear() +{ + if (m_pRAM) + memset(m_pRAM, 0, RAM_SIZE); + if (m_pL1Cache) + memset(m_pL1Cache, 0, L1_CACHE_SIZE); + if (m_pEFB) + memset(m_pEFB, 0, EFB_SIZE); + if (Core::GetStartupParameter().bWii && m_pEXRAM) + memset(m_pEXRAM, 0, EXRAM_SIZE); +} + + +bool AreMemoryBreakpointsActivated() +{ +#ifdef NOCHECK + return false; +#else + return true; +#endif +} + + +u32 Read_Instruction(const u32 _Address) +{ + return Jit64::GetOriginalCode(_Address); +} + +u32 Read_Opcode(const u32 _Address) +{ +#ifdef LOGGING + if (_Address == 0x00000000) + { + PanicAlert("Program tried to read from [00000000]"); + return 0x00000000; + } +#endif + + u32 _var = 0; + ReadFromHardware2(_var, 32, _Address, _Address, FLAG_OPCODE); + + return _var; +} + +u8 Read_U8(const u32 _Address) +{ + u8 _var = (u8)0xAFFEAFFE; + ReadFromHardware2(_var, 8, _Address, _Address, FLAG_READ); +#ifndef NOCHECK + TMemCheck *mc = CBreakPoints::GetMemCheck(_Address); + if (mc) + { + mc->numHits++; + mc->Action(_var, _Address,false,1,PC); + } +#endif + return (u8)_var; +} + +u16 Read_U16(const u32 _Address) +{ + u16 _var = 0; + ReadFromHardware2(_var, 16, _Address, _Address, FLAG_READ); +#ifndef NOCHECK + TMemCheck *mc = CBreakPoints::GetMemCheck(_Address); + if (mc) + { + mc->numHits++; + mc->Action(_var, _Address,false,2,PC); + } +#endif + return (u16)_var; +} + +u32 Read_U32(const u32 _Address) +{ +#ifdef LOGGING + if (_Address == 0x00000000) + { + //PanicAlert("Program tried to read from [00000000]"); + //return 0x00000000; + } +#endif + + u32 _var = 0; + ReadFromHardware2(_var, 32, _Address, _Address, FLAG_READ); +#ifndef NOCHECK + TMemCheck *mc = CBreakPoints::GetMemCheck(_Address); + if (mc) + { + mc->numHits++; + mc->Action(_var, _Address,false,4,PC); + } +#endif + return _var; +} + + +u64 Read_U64(const u32 _Address) +{ + u64 _var = 0; + ReadFromHardware2(_var, 64, _Address, _Address, FLAG_READ); +#ifndef NOCHECK + TMemCheck *mc = CBreakPoints::GetMemCheck(_Address); + if (mc) + { + mc->numHits++; + mc->Action((u32)_var, _Address,false,8,PC); + } +#endif + return _var; +} + + +void Write_U8(const u8 _Data, const u32 _Address) +{ +#ifndef NOCHECK + TMemCheck *mc = CBreakPoints::GetMemCheck(_Address); + if (mc) + { + mc->numHits++; + mc->Action(_Data,_Address,true,1,PC); + } +#endif + WriteToHardware2(8, _Address, _Data, _Address, FLAG_WRITE); +} + + +void Write_U16(const u16 _Data, const u32 _Address) +{ +#ifndef NOCHECK + TMemCheck *mc = CBreakPoints::GetMemCheck(_Address); + if (mc) + { + mc->numHits++; + mc->Action(_Data,_Address,true,2,PC); + } +#endif + + WriteToHardware2(16, _Address, _Data, _Address, FLAG_WRITE); +} + + +void Write_U32(const u32 _Data, const u32 _Address) +{ +#ifndef NOCHECK + TMemCheck *mc = CBreakPoints::GetMemCheck(_Address); + if (mc) + { + mc->numHits++; + mc->Action(_Data,_Address,true,4,PC); + } +#endif + WriteToHardware2(32, _Address, _Data, _Address, FLAG_WRITE); +} + + +void WriteHW_U32(const u32 _Data, const u32 _Address) +{ + hwWrite32[(_Address>>HWSHIFT) & (NUMHWMEMFUN-1)](_Data,_Address); +} + + +void Write_U64(const u64 _Data, const u32 _Address) +{ +#ifndef NOCHECK + TMemCheck *mc = CBreakPoints::GetMemCheck(_Address); + if (mc) + { + mc->numHits++; + mc->Action((u32)_Data,_Address,true,8,PC); + } +#endif + + WriteToHardware2(64, _Address, _Data, _Address + 4, FLAG_WRITE); +} + + +u8 ReadUnchecked_U8(const u32 _Address) +{ + u8 _var = (u8)0xAFFEAFFE; + ReadFromHardware2(_var, 8, _Address, _Address, FLAG_NO_EXCEPTION); + return (u8)_var; +} + + +u32 ReadUnchecked_U32(const u32 _Address) +{ + u32 _var = 0; + ReadFromHardware2(_var, 32, _Address, _Address, FLAG_NO_EXCEPTION); + return _var; +} + +void WriteUnchecked_U8(const u8 _iValue, const u32 _Address) +{ + WriteToHardware2(8, _Address, _iValue, _Address, FLAG_NO_EXCEPTION); +} + + +void WriteUnchecked_U32(const u32 _iValue, const u32 _Address) +{ + WriteToHardware2(32, _Address, _iValue, _Address, FLAG_NO_EXCEPTION); +} + + +void WriteBigEData(const u8 *_pData, const u32 _Address, const u32 _iSize) +{ + memcpy(GetPointer(_Address), _pData, _iSize); +} + + +void Memset(const u32 _Address, const u8 _iValue, const u32 _iLength) +{ + u8 *ptr = GetPointer(_Address); + if (ptr != NULL) + { + memset(ptr,_iValue,_iLength); + } + else + { + // (comment for old implementation) : F|RES: rouge squadron and other games use the TLB ... so this cant work + + // fixed implementation: + for (u32 i = 0; i < _iLength; i++) + Write_U8(_iValue, _Address + i); + } +} + + +void DMA_LCToMemory(const u32 _MemAddr, const u32 _CacheAddr, const u32 _iNumBlocks) +{ + u8 *src = GetCachePtr() + (_CacheAddr & 0x3FFFF); + u8 *dst = GetPointer(_MemAddr); + + if ((dst != NULL) && (src != NULL)) + { + memcpy(dst, src, 32 * _iNumBlocks); + } + else + { + for (u32 i = 0; i < 32 * _iNumBlocks; i++) + { + u8 Temp = Read_U8(_CacheAddr + i); + Write_U8(Temp, _MemAddr + i); + } + } +} + + +void DMA_MemoryToLC(const u32 _CacheAddr, const u32 _MemAddr, const u32 _iNumBlocks) +{ + u8 *src = GetPointer(_MemAddr); + u8 *dst = GetCachePtr() + (_CacheAddr & 0x3FFFF); + + if ((dst != NULL) && (src != NULL)) + { + memcpy(dst, src, 32 * _iNumBlocks); + } + else + { + for (u32 i = 0; i < 32 * _iNumBlocks; i++) + { + u8 Temp = Read_U8(_MemAddr + i); + Write_U8(Temp, _CacheAddr + i); + } + } +} + + +void ReadBigEData( u8 *_pData, const u32 _Address, const u32 size) +{ + u8 *src = GetPointer(_Address); + memcpy(_pData, src, size); +} + + +void GetString(std::string& _string, const u32 _Address) +{ + char stringBuffer[2048]; + char *string = stringBuffer; + char c; + int addr = _Address; + while ((c = Read_U8(addr))) + { + *string++ = c; + addr++; + } + *string++=0; + + _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) +{ + switch (_Address >> 24) + { + case 0x00: + case 0x01: + case 0x80: + case 0x81: + case 0xC0: + case 0xC1: + return (u8*)(((char*)m_pRAM) + (_Address&RAM_MASK)); + + 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 (Core::GetStartupParameter().bWii) + return (u8*)(((char*)m_pEXRAM) + (_Address&EXRAM_MASK)); + else + return 0; + + case 0xE0: + if (_Address < (0xE0000000 + L1_CACHE_SIZE)) + return GetCachePtr() + (_Address & L1_CACHE_MASK); + + case 0xC8: + return m_pEFB + (_Address & 0xFFFFFF); + case 0xCC: + case 0xCD: + _dbg_assert_msg_(MEMMAP, 0,"Memory", "GetPointer from IO Bridge doesnt work"); + return NULL; + } + return NULL; +} + + +bool IsRAMAddress(const u32 addr, bool allow_locked_cache) +{ + switch ((addr >> 24) & 0xFC) { + case 0x00: + case 0x80: + case 0xC0: + if ((addr & 0x1FFFFFFF) < RAM_SIZE) + return true; + else + return false; + case 0x10: + case 0x90: + case 0xD0: + if (Core::g_CoreStartupParameter.bWii && (addr & 0x0FFFFFFF) < EXRAM_SIZE) + return true; + else + return false; + case 0xE0: + if (allow_locked_cache && addr - 0xE0000000 < L1_CACHE_SIZE) + return true; + else + return false; + default: + return false; + } +} + +// ********************************************************************************* +// Warning: Test Area +// +// This code is for TESTING and it works in interpreter mode ONLY. Some games (like +// COD iirc) work thanks to this basic TLB emulation. +// It is just a small hack and we have never spend enough time to finalize it. +// Cheers PearPC! +// +// ********************************************************************************* + +/* +* PearPC +* ppc_mmu.cc +* +* Copyright (C) 2003, 2004 Sebastian Biallas (sb@biallas.net) +* +* This program is free software; you can redistribute it and/or modify +* it under the terms of the GNU General Public License version 2 as +* published by the Free Software Foundation. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program; if not, write to the Free Software +* Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA. +*/ + + +#define PPC_EXC_DSISR_PAGE (1<<30) +#define PPC_EXC_DSISR_PROT (1<<27) +#define PPC_EXC_DSISR_STORE (1<<25) + +#define SDR1_HTABORG(v) (((v)>>16)&0xffff) +#define SDR1_HTABMASK(v) ((v)&0x1ff) +#define SDR1_PAGETABLE_BASE(v) ((v)&0xffff) +#define SR_T (1<<31) +#define SR_Ks (1<<30) +#define SR_Kp (1<<29) +#define SR_N (1<<28) +#define SR_VSID(v) ((v)&0xffffff) +#define SR_BUID(v) (((v)>>20)&0x1ff) +#define SR_CNTRL_SPEC(v) ((v)&0xfffff) + +#define EA_SR(v) (((v)>>28)&0xf) +#define EA_PageIndex(v) (((v)>>12)&0xffff) +#define EA_Offset(v) ((v)&0xfff) +#define EA_API(v) (((v)>>22)&0x3f) + +#define PA_RPN(v) (((v)>>12)&0xfffff) +#define PA_Offset(v) ((v)&0xfff) + +#define PTE1_V (1<<31) +#define PTE1_VSID(v) (((v)>>7)&0xffffff) +#define PTE1_H (1<<6) +#define PTE1_API(v) ((v)&0x3f) + +#define PTE2_RPN(v) ((v)&0xfffff000) +#define PTE2_R (1<<8) +#define PTE2_C (1<<7) +#define PTE2_WIMG(v) (((v)>>3)&0xf) +#define PTE2_PP(v) ((v)&3) + +union UPTE1 +{ + struct + { + unsigned API : 6; + unsigned H : 1; + unsigned VSID : 24; + unsigned V : 1; + }; + u32 Hex; +}; + +union UPTE2 +{ + struct + { + unsigned PP : 2; + unsigned : 1; + unsigned WIMG : 4; + unsigned C : 1; + unsigned R : 1; + unsigned : 3; + unsigned RPN : 20; + }; + u32 Hex; +}; + +u32 pagetable_base = 0; +u32 pagetable_hashmask = 0; + +void GenerateDSIException(u32 _EffectiveAdress, 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; + + LOG(MEMMAP, "Generate DSI Exception 0x%08x", _EffectiveAdress); + PowerPC::ppcState.Exceptions |= EXCEPTION_DSI; +} + + +void GenerateISIException() +{ + // 4 bit for 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 DBAT register (page fault + // condition); otherwise cleared. + PowerPC::ppcState.spr[SPR_DSISR] = 0x4000000; + LOG(MEMMAP, "Generate ISI Exception"); + PowerPC::ppcState.Exceptions |= EXCEPTION_ISI; +} + + +void SDRUpdated() +{ + u32 htabmask = SDR1_HTABMASK(PowerPC::ppcState.spr[SPR_SDR]); + u32 x = 1; + u32 xx = 0; + int n = 0; + while ((htabmask & x) && (n < 9)) + { + n++; + xx|=x; + x<<=1; + } + if (htabmask & ~xx) + { + return; + } + u32 htaborg = SDR1_HTABORG(PowerPC::ppcState.spr[SPR_SDR]); + if (htaborg & xx) + { + return; + } + pagetable_base = htaborg<<16; + pagetable_hashmask = ((xx<<10)|0x3ff); +} + + +u32 CheckDTLB(u32 _Address, XCheckTLBFlag _Flag) +{ + //return 0; + if (Core::GetStartupParameter().bWii) { + // TLB is never used on Wii (except linux and stuff, but we don't care about that) + PanicAlert("%s invalid memory region (0x%08x)\n\n" + "There is no way to recover from this error," + "so Dolphin will now exit. Sorry!", + _Flag == FLAG_WRITE ? "Write to" : "Read from", _Address); + } + else { + PanicAlert("%s invalid memory region (0x%08x)\n\n" + "This is either the game crashing randomly, or a TLB write." + "Several games uses the TLB to map memory. This\n" + "function is not supported in Dolphin. " + "Unfortunately there is no way to recover from this error," + "so Dolphin will now exit abruptly. Sorry!", + _Flag == FLAG_WRITE ? "Write to" : "Read from", _Address); + } + exit(0); + u32 sr = PowerPC::ppcState.sr[EA_SR(_Address)]; + + u32 offset = EA_Offset(_Address); // 12 bit + u32 page_index = EA_PageIndex(_Address); // 16 bit + u32 VSID = SR_VSID(sr); // 24 bit + u32 api = EA_API(_Address); // 6 bit (part of page_index) + + u8* pRAM = GetPointer(0); + + // hash function no 1 "xor" .360 + u32 hash1 = (VSID ^ page_index); + u32 pteg_addr = ((hash1 & pagetable_hashmask)<<6) | pagetable_base; + + // hash1 + for (int i = 0; i < 8; i++) + { + UPTE1 PTE1; + PTE1.Hex = bswap(*(u32*)&pRAM[pteg_addr]); + + if (PTE1.V && !PTE1.H) + { + if (VSID == PTE1.VSID && (api == PTE1.API)) + { + UPTE2 PTE2; + PTE2.Hex = bswap((*(u32*)&pRAM[(pteg_addr + 4)])); + + // set the access bits + switch (_Flag) + { + case FLAG_READ: PTE2.R = 1; break; + case FLAG_WRITE: PTE2.C = 1; break; + case FLAG_NO_EXCEPTION: break; + case FLAG_OPCODE: break; + } + *(u32*)&pRAM[(pteg_addr + 4)] = bswap(PTE2.Hex); + + return ((PTE2.RPN << 12) | offset); + } + } + pteg_addr+=8; + } + + // hash function no 2 "not" .360 + hash1 = ~hash1; + pteg_addr = ((hash1 & pagetable_hashmask)<<6) | pagetable_base; + for (int i = 0; i < 8; i++) + { + u32 pte = bswap(*(u32*)&pRAM[pteg_addr]); + if ((pte & PTE1_V) && (pte & PTE1_H)) + { + if (VSID == PTE1_VSID(pte) && (api == PTE1_API(pte))) + { + PanicAlert("TLB: Address found at the second hash function.\n" + "i have never seen this before"); + + pte = bswap(*(u32*)&pRAM[(pteg_addr+4)]); + + u32 physAddress = PTE2_RPN(pte) | offset; + + // missing access bits + return physAddress; + } + } + pteg_addr+=8; + } + + + // exception generation + 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; +} + + +} // namespace diff --git a/Source/Core/Core/Src/HW/MemoryInterface.cpp b/Source/Core/Core/Src/HW/MemoryInterface.cpp index 7413f28ed2..8f45d25d29 100644 --- a/Source/Core/Core/Src/HW/MemoryInterface.cpp +++ b/Source/Core/Core/Src/HW/MemoryInterface.cpp @@ -1,95 +1,95 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "ChunkFile.h" - -#include "../PowerPC/PowerPC.h" -#include "MemoryInterface.h" - -namespace MemoryInterface -{ - -// internal hardware addresses -enum -{ - MEM_CHANNEL0_HI = 0x000, - MEM_CHANNEL0_LO = 0x002, - MEM_CHANNEL1_HI = 0x004, - MEM_CHANNEL1_LO = 0x006, - MEM_CHANNEL2_HI = 0x008, - MEM_CHANNEL2_LO = 0x00A, - MEM_CHANNEL3_HI = 0x00C, - MEM_CHANNEL3_LO = 0x00E, - MEM_CHANNEL_CTRL = 0x010 -}; - -struct MIMemStruct -{ - u32 Channel0_Addr; - u32 Channel1_Addr; - u32 Channel2_Addr; - u32 Channel3_Addr; - u32 Channel_Ctrl; -}; - -// STATE_TO_SAVE -static MIMemStruct miMem; - -void DoState(PointerWrap &p) -{ - p.Do(miMem); -} - -void Read16(u16& _uReturnValue, const u32 _iAddress) -{ - //0x30 -> 0x5a : gp memory metrics - LOG(MEMMAP, "(r16) 0x%04x @ 0x%08x", 0, _iAddress); - _uReturnValue = 0; -} - -void Read32(u32& _uReturnValue, const u32 _iAddress) -{ - LOG(MEMMAP, "(r32) 0x%08x @ 0x%08x", 0, _iAddress); - _uReturnValue = 0; -} - -void Write32(const u32 _iValue, const u32 _iAddress) -{ - LOG(MEMMAP, "(w32) 0x%08x @ 0x%08x", _iValue, _iAddress); -} - -//TODO : check -void Write16(const u16 _iValue, const u32 _iAddress) -{ - LOG(MEMMAP, "(w16) 0x%04x @ 0x%08x", _iValue, _iAddress); - switch(_iAddress & 0xFFF) - { - case MEM_CHANNEL0_HI: miMem.Channel0_Addr = (miMem.Channel0_Addr & 0xFFFF) | (_iValue<<16); return; - case MEM_CHANNEL0_LO: miMem.Channel0_Addr = (miMem.Channel0_Addr & 0xFFFF0000) | (_iValue); return; - case MEM_CHANNEL1_HI: miMem.Channel1_Addr = (miMem.Channel1_Addr & 0xFFFF) | (_iValue<<16); return; - case MEM_CHANNEL1_LO: miMem.Channel1_Addr = (miMem.Channel1_Addr & 0xFFFF0000) | (_iValue); return; - case MEM_CHANNEL2_HI: miMem.Channel2_Addr = (miMem.Channel2_Addr & 0xFFFF) | (_iValue<<16); return; - case MEM_CHANNEL2_LO: miMem.Channel2_Addr = (miMem.Channel2_Addr & 0xFFFF0000) | (_iValue); return; - case MEM_CHANNEL3_HI: miMem.Channel3_Addr = (miMem.Channel3_Addr & 0xFFFF) | (_iValue<<16); return; - case MEM_CHANNEL3_LO: miMem.Channel3_Addr = (miMem.Channel3_Addr & 0xFFFF0000) | (_iValue); return; - case MEM_CHANNEL_CTRL: miMem.Channel_Ctrl = _iValue; return; - } -} - -} // end of namespace MemoryInterface - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "ChunkFile.h" + +#include "../PowerPC/PowerPC.h" +#include "MemoryInterface.h" + +namespace MemoryInterface +{ + +// internal hardware addresses +enum +{ + MEM_CHANNEL0_HI = 0x000, + MEM_CHANNEL0_LO = 0x002, + MEM_CHANNEL1_HI = 0x004, + MEM_CHANNEL1_LO = 0x006, + MEM_CHANNEL2_HI = 0x008, + MEM_CHANNEL2_LO = 0x00A, + MEM_CHANNEL3_HI = 0x00C, + MEM_CHANNEL3_LO = 0x00E, + MEM_CHANNEL_CTRL = 0x010 +}; + +struct MIMemStruct +{ + u32 Channel0_Addr; + u32 Channel1_Addr; + u32 Channel2_Addr; + u32 Channel3_Addr; + u32 Channel_Ctrl; +}; + +// STATE_TO_SAVE +static MIMemStruct miMem; + +void DoState(PointerWrap &p) +{ + p.Do(miMem); +} + +void Read16(u16& _uReturnValue, const u32 _iAddress) +{ + //0x30 -> 0x5a : gp memory metrics + LOG(MEMMAP, "(r16) 0x%04x @ 0x%08x", 0, _iAddress); + _uReturnValue = 0; +} + +void Read32(u32& _uReturnValue, const u32 _iAddress) +{ + LOG(MEMMAP, "(r32) 0x%08x @ 0x%08x", 0, _iAddress); + _uReturnValue = 0; +} + +void Write32(const u32 _iValue, const u32 _iAddress) +{ + LOG(MEMMAP, "(w32) 0x%08x @ 0x%08x", _iValue, _iAddress); +} + +//TODO : check +void Write16(const u16 _iValue, const u32 _iAddress) +{ + LOG(MEMMAP, "(w16) 0x%04x @ 0x%08x", _iValue, _iAddress); + switch(_iAddress & 0xFFF) + { + case MEM_CHANNEL0_HI: miMem.Channel0_Addr = (miMem.Channel0_Addr & 0xFFFF) | (_iValue<<16); return; + case MEM_CHANNEL0_LO: miMem.Channel0_Addr = (miMem.Channel0_Addr & 0xFFFF0000) | (_iValue); return; + case MEM_CHANNEL1_HI: miMem.Channel1_Addr = (miMem.Channel1_Addr & 0xFFFF) | (_iValue<<16); return; + case MEM_CHANNEL1_LO: miMem.Channel1_Addr = (miMem.Channel1_Addr & 0xFFFF0000) | (_iValue); return; + case MEM_CHANNEL2_HI: miMem.Channel2_Addr = (miMem.Channel2_Addr & 0xFFFF) | (_iValue<<16); return; + case MEM_CHANNEL2_LO: miMem.Channel2_Addr = (miMem.Channel2_Addr & 0xFFFF0000) | (_iValue); return; + case MEM_CHANNEL3_HI: miMem.Channel3_Addr = (miMem.Channel3_Addr & 0xFFFF) | (_iValue<<16); return; + case MEM_CHANNEL3_LO: miMem.Channel3_Addr = (miMem.Channel3_Addr & 0xFFFF0000) | (_iValue); return; + case MEM_CHANNEL_CTRL: miMem.Channel_Ctrl = _iValue; return; + } +} + +} // end of namespace MemoryInterface + diff --git a/Source/Core/Core/Src/HW/PeripheralInterface.cpp b/Source/Core/Core/Src/HW/PeripheralInterface.cpp index 2d4424d9c5..88a2e5a66d 100644 --- a/Source/Core/Core/Src/HW/PeripheralInterface.cpp +++ b/Source/Core/Core/Src/HW/PeripheralInterface.cpp @@ -1,232 +1,232 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ -#include - -#include "Common.h" -#include "ChunkFile.h" -#include "../PowerPC/PowerPC.h" - -#include "../HW/CPU.h" -#include "PeripheralInterface.h" -#include "GPFifo.h" - -// STATE_TO_SAVE -u32 volatile CPeripheralInterface::m_InterruptMask; -u32 volatile CPeripheralInterface::m_InterruptCause; - -// addresses for CPU fifo accesses -u32 CPeripheralInterface::Fifo_CPUBase; -u32 CPeripheralInterface::Fifo_CPUEnd; -u32 CPeripheralInterface::Fifo_CPUWritePointer; - -void CPeripheralInterface::DoState(PointerWrap &p) -{ - p.Do(m_InterruptMask); - p.Do(m_InterruptCause); - p.Do(Fifo_CPUBase); - p.Do(Fifo_CPUEnd); - p.Do(Fifo_CPUWritePointer); -} - -void CPeripheralInterface::Init() -{ - m_InterruptMask = 0; - m_InterruptCause = 0; - - Fifo_CPUBase = 0; - Fifo_CPUEnd = 0; - Fifo_CPUWritePointer = 0; - - m_InterruptCause |= INT_CAUSE_RST_BUTTON; // Reset button state -} - -void CPeripheralInterface::Read32(u32& _uReturnValue, const u32 _iAddress) -{ - //LOG(PERIPHERALINTERFACE, "(r32) 0x%08x", _iAddress); - - switch(_iAddress & 0xFFF) - { - case PI_INTERRUPT_CAUSE: - _uReturnValue = m_InterruptCause; - return; - - case PI_INTERRUPT_MASK: - _uReturnValue = m_InterruptMask; - return; - - case PI_FIFO_BASE: - LOG(PERIPHERALINTERFACE,"read cpu fifo base, value = %08x",Fifo_CPUBase); - _uReturnValue = Fifo_CPUBase; - return; - - case PI_FIFO_END: - LOG(PERIPHERALINTERFACE,"read cpu fifo end, value = %08x",Fifo_CPUEnd); - _uReturnValue = Fifo_CPUEnd; - return; - - case PI_FIFO_WPTR: - LOGV(PERIPHERALINTERFACE, 3, "read writepointer, value = %08x",Fifo_CPUWritePointer); - _uReturnValue = Fifo_CPUWritePointer; //really writes in 32-byte chunks - - // Monk's gcube does some crazy align trickery here. - return; - - case PI_RESET_CODE: - _uReturnValue = 0x80000000; - return; - - case PI_MB_REV: - _uReturnValue = 0x20000000; // HW2 production board - return; - - default: - LOG(PERIPHERALINTERFACE,"!!!!Unknown write!!!! 0x%08x", _iAddress); - break; - } - - _uReturnValue = 0xAFFE0000; -} - -void CPeripheralInterface::Write32(const u32 _uValue, const u32 _iAddress) -{ - LOG(PERIPHERALINTERFACE, "(w32) 0x%08x @ 0x%08x", _uValue, _iAddress); - switch(_iAddress & 0xFFF) - { - case PI_INTERRUPT_CAUSE: - m_InterruptCause &= ~_uValue; //writes turns them off - UpdateException(); - return; - - case PI_INTERRUPT_MASK: - m_InterruptMask = _uValue; - LOG(PERIPHERALINTERFACE,"New Interrupt mask: %08x",m_InterruptMask); - UpdateException(); - return; - - case PI_FIFO_BASE: - Fifo_CPUBase = _uValue & 0xFFFFFFE0; - LOG(PERIPHERALINTERFACE,"Fifo base = %08x", _uValue); - break; - - case PI_FIFO_END: - Fifo_CPUEnd = _uValue & 0xFFFFFFE0; - LOG(PERIPHERALINTERFACE,"Fifo end = %08x", _uValue); - break; - - case PI_FIFO_WPTR: - Fifo_CPUWritePointer = _uValue & 0xFFFFFFE0; - LOG(PERIPHERALINTERFACE,"Fifo writeptr = %08x", _uValue); - break; - - case PI_FIFO_RESET: - // Fifo_CPUWritePointer = Fifo_CPUBase; ?? - // PanicAlert("Unknown write to PI_FIFO_RESET (%08x)", _uValue); - break; - - case PI_RESET_CODE: - { - LOG(PERIPHERALINTERFACE,"PI Reset = %08x ???", _uValue); - - if ((_uValue != 0x80000001) && (_uValue != 0x80000005)) // DVDLowReset - { - switch (_uValue) { - case 3: - PanicAlert("The game wants to go to memory card manager. BIOS is being HLE:d - so we can't do that.\n"); - break; - default: - { - TCHAR szTemp[256]; - sprintf(szTemp, "The game wants to reset the machine. PI_RESET_CODE: (%08x)", _uValue); - PanicAlert(szTemp); - } - break; - } - } - } - break; - - default: - LOG(PERIPHERALINTERFACE,"!!!!Unknown PI write!!!! 0x%08x", _iAddress); - PanicAlert("Unknown write to PI: %08x", _iAddress); - break; - } -} - -void CPeripheralInterface::UpdateException() -{ - if ((m_InterruptCause & m_InterruptMask) != 0) - PowerPC::ppcState.Exceptions |= EXCEPTION_EXTERNAL_INT; - else - PowerPC::ppcState.Exceptions &= ~EXCEPTION_EXTERNAL_INT; -} - -const char *CPeripheralInterface::Debug_GetInterruptName(InterruptCause _causemask) -{ - switch (_causemask) - { - case INT_CAUSE_ERROR: return "INT_CAUSE_ERROR"; - case INT_CAUSE_DI: return "INT_CAUSE_DI"; - case INT_CAUSE_RSW: return "INT_CAUSE_RSW"; - case INT_CAUSE_SI: return "INT_CAUSE_SI"; - case INT_CAUSE_EXI: return "INT_CAUSE_EXI"; - case INT_CAUSE_AUDIO: return "INT_CAUSE_AUDIO"; - case INT_CAUSE_DSP: return "INT_CAUSE_DSP"; - case INT_CAUSE_MEMORY: return "INT_CAUSE_MEMORY"; - case INT_CAUSE_VI: return "INT_CAUSE_VI"; - case INT_CAUSE_PE_TOKEN: return "INT_CAUSE_PE_TOKEN"; - case INT_CAUSE_PE_FINISH: return "INT_CAUSE_PE_FINISH"; - case INT_CAUSE_CP: return "INT_CAUSE_CP"; - case INT_CAUSE_DEBUG: return "INT_CAUSE_DEBUG"; - case INT_CAUSE_WII_IPC: return "INT_CAUSE_WII_IPC"; - case INT_CAUSE_HSP: return "INT_CAUSE_HSP"; - case INT_CAUSE_RST_BUTTON: return "INT_CAUSE_RST_BUTTON"; - } - - return "!!! ERROR-unknown Interrupt !!!"; -} - -void CPeripheralInterface::SetInterrupt(InterruptCause _causemask, bool _bSet) -{ - //TODO(ector): add sanity check that current thread id is cpu thread - - if (_bSet && !(m_InterruptCause & (u32)_causemask)) - { - LOGV(PERIPHERALINTERFACE, 2, "Setting Interrupt %s (%s)",Debug_GetInterruptName(_causemask), "set"); - } - - if (!_bSet && (m_InterruptCause & (u32)_causemask)) - { - LOGV(PERIPHERALINTERFACE, 2, "Setting Interrupt %s (%s)",Debug_GetInterruptName(_causemask), "clear"); - } - - if (_bSet) - m_InterruptCause |= (u32)_causemask; - else - m_InterruptCause &= ~(u32)_causemask; // is there any reason to have this possibility? - // F|RES: i think the hw devices reset the interrupt in the PI to 0 - // if the interrupt cause is eliminated. that isnt done by software (afaik) - UpdateException(); -} - -void CPeripheralInterface::SetResetButton(bool _bSet) -{ - if (_bSet) - m_InterruptCause &= ~INT_CAUSE_RST_BUTTON; - else - m_InterruptCause |= INT_CAUSE_RST_BUTTON; -} - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +#include + +#include "Common.h" +#include "ChunkFile.h" +#include "../PowerPC/PowerPC.h" + +#include "../HW/CPU.h" +#include "PeripheralInterface.h" +#include "GPFifo.h" + +// STATE_TO_SAVE +u32 volatile CPeripheralInterface::m_InterruptMask; +u32 volatile CPeripheralInterface::m_InterruptCause; + +// addresses for CPU fifo accesses +u32 CPeripheralInterface::Fifo_CPUBase; +u32 CPeripheralInterface::Fifo_CPUEnd; +u32 CPeripheralInterface::Fifo_CPUWritePointer; + +void CPeripheralInterface::DoState(PointerWrap &p) +{ + p.Do(m_InterruptMask); + p.Do(m_InterruptCause); + p.Do(Fifo_CPUBase); + p.Do(Fifo_CPUEnd); + p.Do(Fifo_CPUWritePointer); +} + +void CPeripheralInterface::Init() +{ + m_InterruptMask = 0; + m_InterruptCause = 0; + + Fifo_CPUBase = 0; + Fifo_CPUEnd = 0; + Fifo_CPUWritePointer = 0; + + m_InterruptCause |= INT_CAUSE_RST_BUTTON; // Reset button state +} + +void CPeripheralInterface::Read32(u32& _uReturnValue, const u32 _iAddress) +{ + //LOG(PERIPHERALINTERFACE, "(r32) 0x%08x", _iAddress); + + switch(_iAddress & 0xFFF) + { + case PI_INTERRUPT_CAUSE: + _uReturnValue = m_InterruptCause; + return; + + case PI_INTERRUPT_MASK: + _uReturnValue = m_InterruptMask; + return; + + case PI_FIFO_BASE: + LOG(PERIPHERALINTERFACE,"read cpu fifo base, value = %08x",Fifo_CPUBase); + _uReturnValue = Fifo_CPUBase; + return; + + case PI_FIFO_END: + LOG(PERIPHERALINTERFACE,"read cpu fifo end, value = %08x",Fifo_CPUEnd); + _uReturnValue = Fifo_CPUEnd; + return; + + case PI_FIFO_WPTR: + LOGV(PERIPHERALINTERFACE, 3, "read writepointer, value = %08x",Fifo_CPUWritePointer); + _uReturnValue = Fifo_CPUWritePointer; //really writes in 32-byte chunks + + // Monk's gcube does some crazy align trickery here. + return; + + case PI_RESET_CODE: + _uReturnValue = 0x80000000; + return; + + case PI_MB_REV: + _uReturnValue = 0x20000000; // HW2 production board + return; + + default: + LOG(PERIPHERALINTERFACE,"!!!!Unknown write!!!! 0x%08x", _iAddress); + break; + } + + _uReturnValue = 0xAFFE0000; +} + +void CPeripheralInterface::Write32(const u32 _uValue, const u32 _iAddress) +{ + LOG(PERIPHERALINTERFACE, "(w32) 0x%08x @ 0x%08x", _uValue, _iAddress); + switch(_iAddress & 0xFFF) + { + case PI_INTERRUPT_CAUSE: + m_InterruptCause &= ~_uValue; //writes turns them off + UpdateException(); + return; + + case PI_INTERRUPT_MASK: + m_InterruptMask = _uValue; + LOG(PERIPHERALINTERFACE,"New Interrupt mask: %08x",m_InterruptMask); + UpdateException(); + return; + + case PI_FIFO_BASE: + Fifo_CPUBase = _uValue & 0xFFFFFFE0; + LOG(PERIPHERALINTERFACE,"Fifo base = %08x", _uValue); + break; + + case PI_FIFO_END: + Fifo_CPUEnd = _uValue & 0xFFFFFFE0; + LOG(PERIPHERALINTERFACE,"Fifo end = %08x", _uValue); + break; + + case PI_FIFO_WPTR: + Fifo_CPUWritePointer = _uValue & 0xFFFFFFE0; + LOG(PERIPHERALINTERFACE,"Fifo writeptr = %08x", _uValue); + break; + + case PI_FIFO_RESET: + // Fifo_CPUWritePointer = Fifo_CPUBase; ?? + // PanicAlert("Unknown write to PI_FIFO_RESET (%08x)", _uValue); + break; + + case PI_RESET_CODE: + { + LOG(PERIPHERALINTERFACE,"PI Reset = %08x ???", _uValue); + + if ((_uValue != 0x80000001) && (_uValue != 0x80000005)) // DVDLowReset + { + switch (_uValue) { + case 3: + PanicAlert("The game wants to go to memory card manager. BIOS is being HLE:d - so we can't do that.\n"); + break; + default: + { + TCHAR szTemp[256]; + sprintf(szTemp, "The game wants to reset the machine. PI_RESET_CODE: (%08x)", _uValue); + PanicAlert(szTemp); + } + break; + } + } + } + break; + + default: + LOG(PERIPHERALINTERFACE,"!!!!Unknown PI write!!!! 0x%08x", _iAddress); + PanicAlert("Unknown write to PI: %08x", _iAddress); + break; + } +} + +void CPeripheralInterface::UpdateException() +{ + if ((m_InterruptCause & m_InterruptMask) != 0) + PowerPC::ppcState.Exceptions |= EXCEPTION_EXTERNAL_INT; + else + PowerPC::ppcState.Exceptions &= ~EXCEPTION_EXTERNAL_INT; +} + +const char *CPeripheralInterface::Debug_GetInterruptName(InterruptCause _causemask) +{ + switch (_causemask) + { + case INT_CAUSE_ERROR: return "INT_CAUSE_ERROR"; + case INT_CAUSE_DI: return "INT_CAUSE_DI"; + case INT_CAUSE_RSW: return "INT_CAUSE_RSW"; + case INT_CAUSE_SI: return "INT_CAUSE_SI"; + case INT_CAUSE_EXI: return "INT_CAUSE_EXI"; + case INT_CAUSE_AUDIO: return "INT_CAUSE_AUDIO"; + case INT_CAUSE_DSP: return "INT_CAUSE_DSP"; + case INT_CAUSE_MEMORY: return "INT_CAUSE_MEMORY"; + case INT_CAUSE_VI: return "INT_CAUSE_VI"; + case INT_CAUSE_PE_TOKEN: return "INT_CAUSE_PE_TOKEN"; + case INT_CAUSE_PE_FINISH: return "INT_CAUSE_PE_FINISH"; + case INT_CAUSE_CP: return "INT_CAUSE_CP"; + case INT_CAUSE_DEBUG: return "INT_CAUSE_DEBUG"; + case INT_CAUSE_WII_IPC: return "INT_CAUSE_WII_IPC"; + case INT_CAUSE_HSP: return "INT_CAUSE_HSP"; + case INT_CAUSE_RST_BUTTON: return "INT_CAUSE_RST_BUTTON"; + } + + return "!!! ERROR-unknown Interrupt !!!"; +} + +void CPeripheralInterface::SetInterrupt(InterruptCause _causemask, bool _bSet) +{ + //TODO(ector): add sanity check that current thread id is cpu thread + + if (_bSet && !(m_InterruptCause & (u32)_causemask)) + { + LOGV(PERIPHERALINTERFACE, 2, "Setting Interrupt %s (%s)",Debug_GetInterruptName(_causemask), "set"); + } + + if (!_bSet && (m_InterruptCause & (u32)_causemask)) + { + LOGV(PERIPHERALINTERFACE, 2, "Setting Interrupt %s (%s)",Debug_GetInterruptName(_causemask), "clear"); + } + + if (_bSet) + m_InterruptCause |= (u32)_causemask; + else + m_InterruptCause &= ~(u32)_causemask; // is there any reason to have this possibility? + // F|RES: i think the hw devices reset the interrupt in the PI to 0 + // if the interrupt cause is eliminated. that isnt done by software (afaik) + UpdateException(); +} + +void CPeripheralInterface::SetResetButton(bool _bSet) +{ + if (_bSet) + m_InterruptCause &= ~INT_CAUSE_RST_BUTTON; + else + m_InterruptCause |= INT_CAUSE_RST_BUTTON; +} + diff --git a/Source/Core/Core/Src/HW/PixelEngine.cpp b/Source/Core/Core/Src/HW/PixelEngine.cpp index 3b4a2dc3dc..9324e05e07 100644 --- a/Source/Core/Core/Src/HW/PixelEngine.cpp +++ b/Source/Core/Core/Src/HW/PixelEngine.cpp @@ -1,219 +1,219 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "ChunkFile.h" - -#include "PixelEngine.h" - -#include "../CoreTiming.h" -#include "../PowerPC/PowerPC.h" -#include "PeripheralInterface.h" -#include "CommandProcessor.h" -#include "CPU.h" -#include "../Core.h" - -namespace PixelEngine -{ - -// internal hardware addresses -enum -{ - CTRL_REGISTER = 0x00a, - TOKEN_REG = 0x00e, -}; - -// fifo Control Register -union UPECtrlReg -{ - struct - { - unsigned PETokenEnable : 1; - unsigned PEFinishEnable : 1; - unsigned PEToken : 1; // write only - unsigned PEFinish : 1; // write only - unsigned : 12; - }; - u16 Hex; - UPECtrlReg() {Hex = 0; } - UPECtrlReg(u16 _hex) {Hex = _hex; } -}; - -// STATE_TO_SAVE -static UPECtrlReg g_ctrlReg; - -static bool g_bSignalTokenInterrupt; -static bool g_bSignalFinishInterrupt; - -int et_SetTokenOnMainThread; -int et_SetFinishOnMainThread; - -void DoState(PointerWrap &p) -{ - p.Do(g_ctrlReg); - p.Do(CommandProcessor::fifo.PEToken); - p.Do(g_bSignalTokenInterrupt); - p.Do(g_bSignalFinishInterrupt); -} - -void UpdateInterrupts(); - -void SetToken_OnMainThread(u64 userdata, int cyclesLate); -void SetFinish_OnMainThread(u64 userdata, int cyclesLate); - -void Init() -{ - g_ctrlReg.Hex = 0; - - et_SetTokenOnMainThread = CoreTiming::RegisterEvent("SetToken", SetToken_OnMainThread); - et_SetFinishOnMainThread = CoreTiming::RegisterEvent("SetFinish", SetFinish_OnMainThread); -} - -void Read16(u16& _uReturnValue, const u32 _iAddress) -{ - LOGV(PIXELENGINE, 3, "(r16): 0x%08x", _iAddress); - - switch (_iAddress & 0xFFF) - { - case CTRL_REGISTER: - _uReturnValue = g_ctrlReg.Hex; - LOG(PIXELENGINE,"\t CTRL_REGISTER : %04x", _uReturnValue); - return; - - case TOKEN_REG: - _uReturnValue = CommandProcessor::fifo.PEToken; - LOG(PIXELENGINE,"\t TOKEN_REG : %04x", _uReturnValue); - return; - - default: - LOG(PIXELENGINE,"(unknown)"); - break; - } - - _uReturnValue = 0x001; -} - -void Write32(const u32 _iValue, const u32 _iAddress) -{ - LOGV(PIXELENGINE, 2, "(w32): 0x%08x @ 0x%08x",_iValue,_iAddress); -} - -void Write16(const u16 _iValue, const u32 _iAddress) -{ - LOGV(PIXELENGINE, 3, "(w16): 0x%04x @ 0x%08x",_iValue,_iAddress); - - switch(_iAddress & 0xFFF) - { - case CTRL_REGISTER: - { - UPECtrlReg tmpCtrl(_iValue); - - if (tmpCtrl.PEToken) g_bSignalTokenInterrupt = false; - if (tmpCtrl.PEFinish) g_bSignalFinishInterrupt = false; - - g_ctrlReg.PETokenEnable = tmpCtrl.PETokenEnable; - g_ctrlReg.PEFinishEnable = tmpCtrl.PEFinishEnable; - g_ctrlReg.PEToken = 0; // this flag is write only - g_ctrlReg.PEFinish = 0; // this flag is write only - - UpdateInterrupts(); - } - break; - - case TOKEN_REG: - //LOG(PIXELENGINE,"WEIRD: program wrote token: %i",_iValue); - PanicAlert("PIXELENGINE : (w16) WTF? program wrote token: %i",_iValue); - //only the gx pipeline is supposed to be able to write here - //g_token = _iValue; - break; - } -} - -bool AllowIdleSkipping() -{ - return !Core::g_CoreStartupParameter.bUseDualCore || (!g_ctrlReg.PETokenEnable && !g_ctrlReg.PEFinishEnable); -} - -void UpdateInterrupts() -{ - // check if there is a token-interrupt - if (g_bSignalTokenInterrupt & g_ctrlReg.PETokenEnable) - CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_PE_TOKEN, true); - else - CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_PE_TOKEN, false); - - // check if there is a finish-interrupt - if (g_bSignalFinishInterrupt & g_ctrlReg.PEFinishEnable) - CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_PE_FINISH, true); - else - CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_PE_FINISH, false); -} - -// TODO(mb2): Refactor SetTokenINT_OnMainThread(u64 userdata, int cyclesLate). -// Think about the right order between tokenVal and tokenINT... one day maybe. -// Cleanup++ - -// Called only if BPMEM_PE_TOKEN_INT_ID is ack by GP -void SetToken_OnMainThread(u64 userdata, int cyclesLate) -{ - //if (userdata >> 16) - //{ - g_bSignalTokenInterrupt = true; - //_dbg_assert_msg_(PIXELENGINE, (CommandProcessor::fifo.PEToken == (userdata&0xFFFF)), "WTF? BPMEM_PE_TOKEN_INT_ID's token != BPMEM_PE_TOKEN_ID's token" ); - LOGV(PIXELENGINE, 1, "VIDEO Plugin raises INT_CAUSE_PE_TOKEN (btw, token: %04x)", CommandProcessor::fifo.PEToken); - UpdateInterrupts(); - //} - //else - // LOGV(PIXELENGINE, 1, "VIDEO Plugin wrote token: %i", CommandProcessor::fifo.PEToken); -} - -void SetFinish_OnMainThread(u64 userdata, int cyclesLate) -{ - g_bSignalFinishInterrupt = 1; - UpdateInterrupts(); -} - -// SetToken -// THIS IS EXECUTED FROM VIDEO THREAD -void SetToken(const u16 _token, const int _bSetTokenAcknowledge) -{ - // TODO?: set-token-value and set-token-INT could be merged since set-token-INT own the token value. - if (_bSetTokenAcknowledge) // set token INT - { - CommandProcessor::IncrementGPWDToken(); // for DC watchdog hack since PEToken seems to be a frame-finish too - CoreTiming::ScheduleEvent_Threadsafe( - 0, et_SetTokenOnMainThread, _token | (_bSetTokenAcknowledge << 16)); - } - else // set token value - { - // we do it directly from videoThread because of - // Super Monkey Ball Advance - Common::SyncInterlockedExchange((LONG*)&CommandProcessor::fifo.PEToken, _token); - } -} - -// SetFinish -// THIS IS EXECUTED FROM VIDEO THREAD -void SetFinish() -{ - CommandProcessor::IncrementGPWDToken(); // for DC watchdog hack - CoreTiming::ScheduleEvent_Threadsafe( - 0, et_SetFinishOnMainThread); - LOGV(PIXELENGINE, 2, "VIDEO Set Finish"); -} - -} // end of namespace PixelEngine +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "ChunkFile.h" + +#include "PixelEngine.h" + +#include "../CoreTiming.h" +#include "../PowerPC/PowerPC.h" +#include "PeripheralInterface.h" +#include "CommandProcessor.h" +#include "CPU.h" +#include "../Core.h" + +namespace PixelEngine +{ + +// internal hardware addresses +enum +{ + CTRL_REGISTER = 0x00a, + TOKEN_REG = 0x00e, +}; + +// fifo Control Register +union UPECtrlReg +{ + struct + { + unsigned PETokenEnable : 1; + unsigned PEFinishEnable : 1; + unsigned PEToken : 1; // write only + unsigned PEFinish : 1; // write only + unsigned : 12; + }; + u16 Hex; + UPECtrlReg() {Hex = 0; } + UPECtrlReg(u16 _hex) {Hex = _hex; } +}; + +// STATE_TO_SAVE +static UPECtrlReg g_ctrlReg; + +static bool g_bSignalTokenInterrupt; +static bool g_bSignalFinishInterrupt; + +int et_SetTokenOnMainThread; +int et_SetFinishOnMainThread; + +void DoState(PointerWrap &p) +{ + p.Do(g_ctrlReg); + p.Do(CommandProcessor::fifo.PEToken); + p.Do(g_bSignalTokenInterrupt); + p.Do(g_bSignalFinishInterrupt); +} + +void UpdateInterrupts(); + +void SetToken_OnMainThread(u64 userdata, int cyclesLate); +void SetFinish_OnMainThread(u64 userdata, int cyclesLate); + +void Init() +{ + g_ctrlReg.Hex = 0; + + et_SetTokenOnMainThread = CoreTiming::RegisterEvent("SetToken", SetToken_OnMainThread); + et_SetFinishOnMainThread = CoreTiming::RegisterEvent("SetFinish", SetFinish_OnMainThread); +} + +void Read16(u16& _uReturnValue, const u32 _iAddress) +{ + LOGV(PIXELENGINE, 3, "(r16): 0x%08x", _iAddress); + + switch (_iAddress & 0xFFF) + { + case CTRL_REGISTER: + _uReturnValue = g_ctrlReg.Hex; + LOG(PIXELENGINE,"\t CTRL_REGISTER : %04x", _uReturnValue); + return; + + case TOKEN_REG: + _uReturnValue = CommandProcessor::fifo.PEToken; + LOG(PIXELENGINE,"\t TOKEN_REG : %04x", _uReturnValue); + return; + + default: + LOG(PIXELENGINE,"(unknown)"); + break; + } + + _uReturnValue = 0x001; +} + +void Write32(const u32 _iValue, const u32 _iAddress) +{ + LOGV(PIXELENGINE, 2, "(w32): 0x%08x @ 0x%08x",_iValue,_iAddress); +} + +void Write16(const u16 _iValue, const u32 _iAddress) +{ + LOGV(PIXELENGINE, 3, "(w16): 0x%04x @ 0x%08x",_iValue,_iAddress); + + switch(_iAddress & 0xFFF) + { + case CTRL_REGISTER: + { + UPECtrlReg tmpCtrl(_iValue); + + if (tmpCtrl.PEToken) g_bSignalTokenInterrupt = false; + if (tmpCtrl.PEFinish) g_bSignalFinishInterrupt = false; + + g_ctrlReg.PETokenEnable = tmpCtrl.PETokenEnable; + g_ctrlReg.PEFinishEnable = tmpCtrl.PEFinishEnable; + g_ctrlReg.PEToken = 0; // this flag is write only + g_ctrlReg.PEFinish = 0; // this flag is write only + + UpdateInterrupts(); + } + break; + + case TOKEN_REG: + //LOG(PIXELENGINE,"WEIRD: program wrote token: %i",_iValue); + PanicAlert("PIXELENGINE : (w16) WTF? program wrote token: %i",_iValue); + //only the gx pipeline is supposed to be able to write here + //g_token = _iValue; + break; + } +} + +bool AllowIdleSkipping() +{ + return !Core::g_CoreStartupParameter.bUseDualCore || (!g_ctrlReg.PETokenEnable && !g_ctrlReg.PEFinishEnable); +} + +void UpdateInterrupts() +{ + // check if there is a token-interrupt + if (g_bSignalTokenInterrupt & g_ctrlReg.PETokenEnable) + CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_PE_TOKEN, true); + else + CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_PE_TOKEN, false); + + // check if there is a finish-interrupt + if (g_bSignalFinishInterrupt & g_ctrlReg.PEFinishEnable) + CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_PE_FINISH, true); + else + CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_PE_FINISH, false); +} + +// TODO(mb2): Refactor SetTokenINT_OnMainThread(u64 userdata, int cyclesLate). +// Think about the right order between tokenVal and tokenINT... one day maybe. +// Cleanup++ + +// Called only if BPMEM_PE_TOKEN_INT_ID is ack by GP +void SetToken_OnMainThread(u64 userdata, int cyclesLate) +{ + //if (userdata >> 16) + //{ + g_bSignalTokenInterrupt = true; + //_dbg_assert_msg_(PIXELENGINE, (CommandProcessor::fifo.PEToken == (userdata&0xFFFF)), "WTF? BPMEM_PE_TOKEN_INT_ID's token != BPMEM_PE_TOKEN_ID's token" ); + LOGV(PIXELENGINE, 1, "VIDEO Plugin raises INT_CAUSE_PE_TOKEN (btw, token: %04x)", CommandProcessor::fifo.PEToken); + UpdateInterrupts(); + //} + //else + // LOGV(PIXELENGINE, 1, "VIDEO Plugin wrote token: %i", CommandProcessor::fifo.PEToken); +} + +void SetFinish_OnMainThread(u64 userdata, int cyclesLate) +{ + g_bSignalFinishInterrupt = 1; + UpdateInterrupts(); +} + +// SetToken +// THIS IS EXECUTED FROM VIDEO THREAD +void SetToken(const u16 _token, const int _bSetTokenAcknowledge) +{ + // TODO?: set-token-value and set-token-INT could be merged since set-token-INT own the token value. + if (_bSetTokenAcknowledge) // set token INT + { + CommandProcessor::IncrementGPWDToken(); // for DC watchdog hack since PEToken seems to be a frame-finish too + CoreTiming::ScheduleEvent_Threadsafe( + 0, et_SetTokenOnMainThread, _token | (_bSetTokenAcknowledge << 16)); + } + else // set token value + { + // we do it directly from videoThread because of + // Super Monkey Ball Advance + Common::SyncInterlockedExchange((LONG*)&CommandProcessor::fifo.PEToken, _token); + } +} + +// SetFinish +// THIS IS EXECUTED FROM VIDEO THREAD +void SetFinish() +{ + CommandProcessor::IncrementGPWDToken(); // for DC watchdog hack + CoreTiming::ScheduleEvent_Threadsafe( + 0, et_SetFinishOnMainThread); + LOGV(PIXELENGINE, 2, "VIDEO Set Finish"); +} + +} // end of namespace PixelEngine diff --git a/Source/Core/Core/Src/HW/SerialInterface.cpp b/Source/Core/Core/Src/HW/SerialInterface.cpp index f9f498b138..5757f4e0a7 100644 --- a/Source/Core/Core/Src/HW/SerialInterface.cpp +++ b/Source/Core/Core/Src/HW/SerialInterface.cpp @@ -1,557 +1,557 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include - -#include "Common.h" -#include "ChunkFile.h" - -#include "SerialInterface.h" -#include "SerialInterface_Devices.h" - -#include "PeripheralInterface.h" -#include "CPU.h" - -#include "../PowerPC/PowerPC.h" -#include "../Plugins/Plugin_PAD.h" - -namespace SerialInterface -{ - -// SI Interrupt Types -enum SIInterruptType -{ - INT_RDSTINT = 0, - INT_TCINT = 1, -}; - -// SI number of channels -enum -{ - NUMBER_OF_CHANNELS = 0x04 -}; - -// SI Internal Hardware Addresses -enum -{ - SI_CHANNEL_0_OUT = 0x00, - SI_CHANNEL_0_IN_HI = 0x04, - SI_CHANNEL_0_IN_LO = 0x08, - SI_CHANNEL_1_OUT = 0x0C, - SI_CHANNEL_1_IN_HI = 0x10, - SI_CHANNEL_1_IN_LO = 0x14, - SI_CHANNEL_2_OUT = 0x18, - SI_CHANNEL_2_IN_HI = 0x1C, - SI_CHANNEL_2_IN_LO = 0x20, - SI_CHANNEL_3_OUT = 0x24, - SI_CHANNEL_3_IN_HI = 0x28, - SI_CHANNEL_3_IN_LO = 0x2C, - SI_POLL = 0x30, - SI_COM_CSR = 0x34, - SI_STATUS_REG = 0x38, - SI_EXI_CLOCK_COUNT = 0x3C, -}; - -// SI Channel Output -union USIChannelOut -{ - u32 Hex; - struct - { - unsigned OUTPUT1 : 8; - unsigned OUTPUT0 : 8; - unsigned CMD : 8; - unsigned : 8; - }; -}; - -// SI Channel Input High u32 -union USIChannelIn_Hi -{ - u32 Hex; - struct - { - unsigned INPUT3 : 8; - unsigned INPUT2 : 8; - unsigned INPUT1 : 8; - unsigned INPUT0 : 6; - unsigned ERRLATCH : 1; // 0: no error 1: Error latched. Check SISR. - unsigned ERRSTAT : 1; // 0: no error 1: error on last transfer - }; -}; - -// SI Channel Input Low u32 -union USIChannelIn_Lo -{ - u32 Hex; - struct - { - unsigned INPUT7 : 8; - unsigned INPUT6 : 8; - unsigned INPUT5 : 8; - unsigned INPUT4 : 8; - }; -}; - -// SI Channel -struct SSIChannel -{ - USIChannelOut m_Out; - USIChannelIn_Hi m_InHi; - USIChannelIn_Lo m_InLo; - ISIDevice* m_pDevice; -}; - -// SI Poll: Controls how often a device is polled -union USIPoll -{ - u32 Hex; - struct - { - unsigned VBCPY3 : 1; - unsigned VBCPY2 : 1; - unsigned VBCPY1 : 1; - unsigned VBCPY0 : 1; - unsigned EN3 : 1; - unsigned EN2 : 1; - unsigned EN1 : 1; - unsigned EN0 : 1; - unsigned Y : 10; - unsigned X : 10; - unsigned : 6; - }; -}; - -// SI Communication Control Status Register -union USIComCSR -{ - u32 Hex; - struct - { - unsigned TSTART : 1; - unsigned CHANNEL : 2; // determines which SI channel will be used the communication interface. - unsigned : 5; - unsigned INLNGTH : 7; - unsigned : 1; - unsigned OUTLNGTH : 7; // Communication Channel Output Length in bytes - unsigned : 4; - unsigned RDSTINTMSK : 1; // Read Status Interrupt Status Mask - unsigned RDSTINT : 1; // Read Status Interrupt Status - unsigned COMERR : 1; // Communication Error (set 0) - unsigned TCINTMSK : 1; // Transfer Complete Interrupt Mask - unsigned TCINT : 1; // Transfer Complete Interrupt - }; - USIComCSR() {Hex = 0;} - USIComCSR(u32 _hex) {Hex = _hex;} -}; - -// SI Status Register -union USIStatusReg -{ - u32 Hex; - struct - { - unsigned UNRUN3 : 1; // (RWC) write 1: bit cleared read 1 main proc underrun error - unsigned OVRUN3 : 1; // (RWC) write 1: bit cleared read 1 overrun error - unsigned COLL3 : 1; // (RWC) write 1: bit cleared read 1 collision error - unsigned NOREP3 : 1; // (RWC) write 1: bit cleared read 1 response error - unsigned WRST3 : 1; // (R) 1: buffer channel0 not copied - unsigned RDST3 : 1; // (R) 1: new Data available - unsigned : 2; // 7:6 - unsigned UNRUN2 : 1; // (RWC) write 1: bit cleared read 1 main proc underrun error - unsigned OVRUN2 : 1; // (RWC) write 1: bit cleared read 1 overrun error - unsigned COLL2 : 1; // (RWC) write 1: bit cleared read 1 collision error - unsigned NOREP2 : 1; // (RWC) write 1: bit cleared read 1 response error - unsigned WRST2 : 1; // (R) 1: buffer channel0 not copied - unsigned RDST2 : 1; // (R) 1: new Data available - unsigned : 2; // 15:14 - unsigned UNRUN1 : 1; // (RWC) write 1: bit cleared read 1 main proc underrun error - unsigned OVRUN1 : 1; // (RWC) write 1: bit cleared read 1 overrun error - unsigned COLL1 : 1; // (RWC) write 1: bit cleared read 1 collision error - unsigned NOREP1 : 1; // (RWC) write 1: bit cleared read 1 response error - unsigned WRST1 : 1; // (R) 1: buffer channel0 not copied - unsigned RDST1 : 1; // (R) 1: new Data available - unsigned : 2; // 23:22 - unsigned UNRUN0 : 1; // (RWC) write 1: bit cleared read 1 main proc underrun error - unsigned OVRUN0 : 1; // (RWC) write 1: bit cleared read 1 overrun error - unsigned COLL0 : 1; // (RWC) write 1: bit cleared read 1 collision error - unsigned NOREP0 : 1; // (RWC) write 1: bit cleared read 1 response error - unsigned WRST0 : 1; // (R) 1: buffer channel0 not copied - unsigned RDST0 : 1; // (R) 1: new Data available - unsigned : 1; - unsigned WR : 1; // (RW) write 1 start copy, read 0 copy done - }; - USIStatusReg() {Hex = 0;} - USIStatusReg(u32 _hex) {Hex = _hex;} -}; - -// SI EXI Clock Count -union USIEXIClockCount -{ - u32 Hex; - struct - { - unsigned LOCK : 1; - unsigned : 30; - }; -}; - -// STATE_TO_SAVE -static SSIChannel g_Channel[NUMBER_OF_CHANNELS]; -static USIPoll g_Poll; -static USIComCSR g_ComCSR; -static USIStatusReg g_StatusReg; -static USIEXIClockCount g_EXIClockCount; -static u8 g_SIBuffer[128]; - -void DoState(PointerWrap &p) -{ - // p.DoArray(g_Channel); - p.Do(g_Poll); - p.Do(g_ComCSR); - p.Do(g_StatusReg); - p.Do(g_EXIClockCount); - p.Do(g_SIBuffer); -} - -static void GenerateSIInterrupt(SIInterruptType _SIInterrupt); -void RunSIBuffer(); -void UpdateInterrupts(); - -void Init() -{ - for (int i = 0; i < NUMBER_OF_CHANNELS; i++) - { - g_Channel[i].m_Out.Hex = 0; - g_Channel[i].m_InHi.Hex = 0; - g_Channel[i].m_InLo.Hex = 0; - } - - unsigned int AttachedPadMask = PluginPAD::PAD_GetAttachedPads ? PluginPAD::PAD_GetAttachedPads() : 1; - for (int i = 0; i < 4; i++) - { - if (AttachedPadMask & (1 << i)) - g_Channel[i].m_pDevice = new CSIDevice_GCController(i); - else - g_Channel[i].m_pDevice = new CSIDevice_Dummy(i); - } - - g_Poll.Hex = 0; - g_ComCSR.Hex = 0; - g_StatusReg.Hex = 0; - g_EXIClockCount.Hex = 0; - memset(g_SIBuffer, 0xce, 128); -} - -void Shutdown() -{ - for (int i = 0; i < NUMBER_OF_CHANNELS; i++) - { - delete g_Channel[i].m_pDevice; - g_Channel[i].m_pDevice = NULL; - } -} - -void Read32(u32& _uReturnValue, const u32 _iAddress) -{ - LOGV(SERIALINTERFACE, 3, "(r32): 0x%08x", _iAddress); - - // SIBuffer - if ((_iAddress >= 0xCC006480 && _iAddress < 0xCC006500) || - (_iAddress >= 0xCD006480 && _iAddress < 0xCD006500)) - { - _uReturnValue = *(u32*)&g_SIBuffer[_iAddress & 0x7F]; - return; - } - - // registers - switch (_iAddress & 0x3FF) - { - //=================================================================================================== - // Channel 0 - //=================================================================================================== - case SI_CHANNEL_0_OUT: - _uReturnValue = g_Channel[0].m_Out.Hex; - return; - - case SI_CHANNEL_0_IN_HI: - g_StatusReg.RDST0 = 0; - UpdateInterrupts(); - _uReturnValue = g_Channel[0].m_InHi.Hex; - return; - - case SI_CHANNEL_0_IN_LO: - g_StatusReg.RDST0 = 0; - UpdateInterrupts(); - _uReturnValue = g_Channel[0].m_InLo.Hex; - return; - - //=================================================================================================== - // Channel 1 - //=================================================================================================== - case SI_CHANNEL_1_OUT: - _uReturnValue = g_Channel[1].m_Out.Hex; - return; - - case SI_CHANNEL_1_IN_HI: - g_StatusReg.RDST1 = 0; - UpdateInterrupts(); - _uReturnValue = g_Channel[1].m_InHi.Hex; - return; - - case SI_CHANNEL_1_IN_LO: - g_StatusReg.RDST1 = 0; - UpdateInterrupts(); - _uReturnValue = g_Channel[1].m_InLo.Hex; - return; - - //=================================================================================================== - // Channel 2 - //=================================================================================================== - case SI_CHANNEL_2_OUT: - _uReturnValue = g_Channel[2].m_Out.Hex; - return; - - case SI_CHANNEL_2_IN_HI: - g_StatusReg.RDST2 = 0; - UpdateInterrupts(); - _uReturnValue = g_Channel[2].m_InHi.Hex; - return; - - case SI_CHANNEL_2_IN_LO: - g_StatusReg.RDST2 = 0; - UpdateInterrupts(); - _uReturnValue = g_Channel[2].m_InLo.Hex; - return; - - //=================================================================================================== - // Channel 3 - //=================================================================================================== - case SI_CHANNEL_3_OUT: - _uReturnValue = g_Channel[3].m_Out.Hex; - return; - - case SI_CHANNEL_3_IN_HI: - g_StatusReg.RDST3 = 0; - UpdateInterrupts(); - _uReturnValue = g_Channel[3].m_InHi.Hex; - return; - - case SI_CHANNEL_3_IN_LO: - g_StatusReg.RDST3 = 0; - UpdateInterrupts(); - _uReturnValue = g_Channel[3].m_InLo.Hex; - return; - - case SI_POLL: _uReturnValue = g_Poll.Hex; return; - case SI_COM_CSR: _uReturnValue = g_ComCSR.Hex; return; - case SI_STATUS_REG: _uReturnValue = g_StatusReg.Hex; return; - - case SI_EXI_CLOCK_COUNT: _uReturnValue = g_EXIClockCount.Hex; return; - - default: - LOG(SERIALINTERFACE, "(r32-unk): 0x%08x", _iAddress); - _dbg_assert_(SERIALINTERFACE,0); - break; - } - - // error - _uReturnValue = 0xdeadbeef; -} - -void Write32(const u32 _iValue, const u32 _iAddress) -{ - LOGV(SERIALINTERFACE, 3, "(w32): 0x%08x 0x%08x", _iValue,_iAddress); - - // SIBuffer - if ((_iAddress >= 0xCC006480 && _iAddress < 0xCC006500) || - (_iAddress >= 0xCD006480 && _iAddress < 0xCD006500)) - { - *(u32*)&g_SIBuffer[_iAddress & 0x7F] = _iValue; - return; - } - - // registers - switch (_iAddress & 0x3FF) - { - case SI_CHANNEL_0_OUT: g_Channel[0].m_Out.Hex = _iValue; break; - case SI_CHANNEL_0_IN_HI: g_Channel[0].m_InHi.Hex = _iValue; break; - case SI_CHANNEL_0_IN_LO: g_Channel[0].m_InLo.Hex = _iValue; break; - case SI_CHANNEL_1_OUT: g_Channel[1].m_Out.Hex = _iValue; break; - case SI_CHANNEL_1_IN_HI: g_Channel[1].m_InHi.Hex = _iValue; break; - case SI_CHANNEL_1_IN_LO: g_Channel[1].m_InLo.Hex = _iValue; break; - case SI_CHANNEL_2_OUT: g_Channel[2].m_Out.Hex = _iValue; break; - case SI_CHANNEL_2_IN_HI: g_Channel[2].m_InHi.Hex = _iValue; break; - case SI_CHANNEL_2_IN_LO: g_Channel[2].m_InLo.Hex = _iValue; break; - case SI_CHANNEL_3_OUT: g_Channel[3].m_Out.Hex = _iValue; break; - case SI_CHANNEL_3_IN_HI: g_Channel[3].m_InHi.Hex = _iValue; break; - case SI_CHANNEL_3_IN_LO: g_Channel[3].m_InLo.Hex = _iValue; break; - - case SI_POLL: - g_Poll.Hex = _iValue; - break; - - case SI_COM_CSR: - { - USIComCSR tmpComCSR(_iValue); - - g_ComCSR.CHANNEL = tmpComCSR.CHANNEL; - g_ComCSR.INLNGTH = tmpComCSR.INLNGTH; - g_ComCSR.OUTLNGTH = tmpComCSR.OUTLNGTH; - g_ComCSR.RDSTINTMSK = tmpComCSR.RDSTINTMSK; - g_ComCSR.TCINTMSK = tmpComCSR.TCINTMSK; - - g_ComCSR.COMERR = 0; - - if (tmpComCSR.RDSTINT) g_ComCSR.RDSTINT = 0; - if (tmpComCSR.TCINT) g_ComCSR.TCINT = 0; - - // be careful: runsi-buffer after updating the INT flags - if (tmpComCSR.TSTART) RunSIBuffer(); - UpdateInterrupts(); - } - break; - - case SI_STATUS_REG: - { - USIStatusReg tmpStatus(_iValue); - - // just update the writable bits - g_StatusReg.NOREP0 = tmpStatus.NOREP0 ? 1 : 0; - g_StatusReg.COLL0 = tmpStatus.COLL0 ? 1 : 0; - g_StatusReg.OVRUN0 = tmpStatus.OVRUN0 ? 1 : 0; - g_StatusReg.UNRUN0 = tmpStatus.UNRUN0 ? 1 : 0; - - g_StatusReg.NOREP1 = tmpStatus.NOREP1 ? 1 : 0; - g_StatusReg.COLL1 = tmpStatus.COLL1 ? 1 : 0; - g_StatusReg.OVRUN1 = tmpStatus.OVRUN1 ? 1 : 0; - g_StatusReg.UNRUN1 = tmpStatus.UNRUN1 ? 1 : 0; - - g_StatusReg.NOREP2 = tmpStatus.NOREP2 ? 1 : 0; - g_StatusReg.COLL2 = tmpStatus.COLL2 ? 1 : 0; - g_StatusReg.OVRUN2 = tmpStatus.OVRUN2 ? 1 : 0; - g_StatusReg.UNRUN2 = tmpStatus.UNRUN2 ? 1 : 0; - - g_StatusReg.NOREP3 = tmpStatus.NOREP3 ? 1 : 0; - g_StatusReg.COLL3 = tmpStatus.COLL3 ? 1 : 0; - g_StatusReg.OVRUN3 = tmpStatus.OVRUN3 ? 1 : 0; - g_StatusReg.UNRUN3 = tmpStatus.UNRUN3 ? 1 : 0; - - // send command to devices - if (tmpStatus.WR) - { - g_StatusReg.WR = 0; - g_Channel[0].m_pDevice->SendCommand(g_Channel[0].m_Out.Hex); - g_Channel[1].m_pDevice->SendCommand(g_Channel[1].m_Out.Hex); - g_Channel[2].m_pDevice->SendCommand(g_Channel[2].m_Out.Hex); - g_Channel[3].m_pDevice->SendCommand(g_Channel[3].m_Out.Hex); - - g_StatusReg.WRST0 = 0; - g_StatusReg.WRST1 = 0; - g_StatusReg.WRST2 = 0; - g_StatusReg.WRST3 = 0; - } - } - break; - - case SI_EXI_CLOCK_COUNT: - g_EXIClockCount.Hex = _iValue; - break; - - case 0x80: - LOG(SERIALINTERFACE, "WII something at 0xCD006480"); - break; - - default: - _dbg_assert_(SERIALINTERFACE,0); - break; - } -} - -void UpdateInterrupts() -{ - // check if we have to update the RDSTINT flag - if (g_StatusReg.RDST0 || g_StatusReg.RDST1 || - g_StatusReg.RDST2 || g_StatusReg.RDST3) - g_ComCSR.RDSTINT = 1; - else - g_ComCSR.RDSTINT = 0; - - // check if we have to generate an interrupt - if ((g_ComCSR.RDSTINT & g_ComCSR.RDSTINTMSK) || - (g_ComCSR.TCINT & g_ComCSR.TCINTMSK)) - { - CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_SI, true); - } - else - { - CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_SI, false); - } -} - -void GenerateSIInterrupt(SIInterruptType _SIInterrupt) -{ - switch(_SIInterrupt) - { - case INT_RDSTINT: g_ComCSR.RDSTINT = 1; break; - case INT_TCINT: g_ComCSR.TCINT = 1; break; - } - - UpdateInterrupts(); -} - -void UpdateDevices() -{ - // update channels - g_StatusReg.RDST0 = g_Channel[0].m_pDevice->GetData(g_Channel[0].m_InHi.Hex, g_Channel[0].m_InLo.Hex) ? 1 : 0; - g_StatusReg.RDST1 = g_Channel[1].m_pDevice->GetData(g_Channel[1].m_InHi.Hex, g_Channel[1].m_InLo.Hex) ? 1 : 0; - g_StatusReg.RDST2 = g_Channel[2].m_pDevice->GetData(g_Channel[2].m_InHi.Hex, g_Channel[2].m_InLo.Hex) ? 1 : 0; - g_StatusReg.RDST3 = g_Channel[3].m_pDevice->GetData(g_Channel[3].m_InHi.Hex, g_Channel[3].m_InLo.Hex) ? 1 : 0; - - // update interrupts - UpdateInterrupts(); -} - -void RunSIBuffer() -{ - // math inLength - int inLength = g_ComCSR.INLNGTH; - if (inLength == 0) - inLength = 128; - else - inLength++; - - // math outLength - int outLength = g_ComCSR.OUTLNGTH; - if (outLength == 0) - outLength = 128; - else - outLength++; - -#ifdef LOGGING - int numOutput = -#endif - g_Channel[g_ComCSR.CHANNEL].m_pDevice->RunBuffer(g_SIBuffer, inLength); - LOGV(SERIALINTERFACE, 2, "RunSIBuffer (intLen: %i outLen: %i) (processed: %i)", inLength, outLength, numOutput); - - // Transfer completed - GenerateSIInterrupt(INT_TCINT); - g_ComCSR.TSTART = 0; -} - -} // end of namespace SerialInterface - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "Common.h" +#include "ChunkFile.h" + +#include "SerialInterface.h" +#include "SerialInterface_Devices.h" + +#include "PeripheralInterface.h" +#include "CPU.h" + +#include "../PowerPC/PowerPC.h" +#include "../Plugins/Plugin_PAD.h" + +namespace SerialInterface +{ + +// SI Interrupt Types +enum SIInterruptType +{ + INT_RDSTINT = 0, + INT_TCINT = 1, +}; + +// SI number of channels +enum +{ + NUMBER_OF_CHANNELS = 0x04 +}; + +// SI Internal Hardware Addresses +enum +{ + SI_CHANNEL_0_OUT = 0x00, + SI_CHANNEL_0_IN_HI = 0x04, + SI_CHANNEL_0_IN_LO = 0x08, + SI_CHANNEL_1_OUT = 0x0C, + SI_CHANNEL_1_IN_HI = 0x10, + SI_CHANNEL_1_IN_LO = 0x14, + SI_CHANNEL_2_OUT = 0x18, + SI_CHANNEL_2_IN_HI = 0x1C, + SI_CHANNEL_2_IN_LO = 0x20, + SI_CHANNEL_3_OUT = 0x24, + SI_CHANNEL_3_IN_HI = 0x28, + SI_CHANNEL_3_IN_LO = 0x2C, + SI_POLL = 0x30, + SI_COM_CSR = 0x34, + SI_STATUS_REG = 0x38, + SI_EXI_CLOCK_COUNT = 0x3C, +}; + +// SI Channel Output +union USIChannelOut +{ + u32 Hex; + struct + { + unsigned OUTPUT1 : 8; + unsigned OUTPUT0 : 8; + unsigned CMD : 8; + unsigned : 8; + }; +}; + +// SI Channel Input High u32 +union USIChannelIn_Hi +{ + u32 Hex; + struct + { + unsigned INPUT3 : 8; + unsigned INPUT2 : 8; + unsigned INPUT1 : 8; + unsigned INPUT0 : 6; + unsigned ERRLATCH : 1; // 0: no error 1: Error latched. Check SISR. + unsigned ERRSTAT : 1; // 0: no error 1: error on last transfer + }; +}; + +// SI Channel Input Low u32 +union USIChannelIn_Lo +{ + u32 Hex; + struct + { + unsigned INPUT7 : 8; + unsigned INPUT6 : 8; + unsigned INPUT5 : 8; + unsigned INPUT4 : 8; + }; +}; + +// SI Channel +struct SSIChannel +{ + USIChannelOut m_Out; + USIChannelIn_Hi m_InHi; + USIChannelIn_Lo m_InLo; + ISIDevice* m_pDevice; +}; + +// SI Poll: Controls how often a device is polled +union USIPoll +{ + u32 Hex; + struct + { + unsigned VBCPY3 : 1; + unsigned VBCPY2 : 1; + unsigned VBCPY1 : 1; + unsigned VBCPY0 : 1; + unsigned EN3 : 1; + unsigned EN2 : 1; + unsigned EN1 : 1; + unsigned EN0 : 1; + unsigned Y : 10; + unsigned X : 10; + unsigned : 6; + }; +}; + +// SI Communication Control Status Register +union USIComCSR +{ + u32 Hex; + struct + { + unsigned TSTART : 1; + unsigned CHANNEL : 2; // determines which SI channel will be used the communication interface. + unsigned : 5; + unsigned INLNGTH : 7; + unsigned : 1; + unsigned OUTLNGTH : 7; // Communication Channel Output Length in bytes + unsigned : 4; + unsigned RDSTINTMSK : 1; // Read Status Interrupt Status Mask + unsigned RDSTINT : 1; // Read Status Interrupt Status + unsigned COMERR : 1; // Communication Error (set 0) + unsigned TCINTMSK : 1; // Transfer Complete Interrupt Mask + unsigned TCINT : 1; // Transfer Complete Interrupt + }; + USIComCSR() {Hex = 0;} + USIComCSR(u32 _hex) {Hex = _hex;} +}; + +// SI Status Register +union USIStatusReg +{ + u32 Hex; + struct + { + unsigned UNRUN3 : 1; // (RWC) write 1: bit cleared read 1 main proc underrun error + unsigned OVRUN3 : 1; // (RWC) write 1: bit cleared read 1 overrun error + unsigned COLL3 : 1; // (RWC) write 1: bit cleared read 1 collision error + unsigned NOREP3 : 1; // (RWC) write 1: bit cleared read 1 response error + unsigned WRST3 : 1; // (R) 1: buffer channel0 not copied + unsigned RDST3 : 1; // (R) 1: new Data available + unsigned : 2; // 7:6 + unsigned UNRUN2 : 1; // (RWC) write 1: bit cleared read 1 main proc underrun error + unsigned OVRUN2 : 1; // (RWC) write 1: bit cleared read 1 overrun error + unsigned COLL2 : 1; // (RWC) write 1: bit cleared read 1 collision error + unsigned NOREP2 : 1; // (RWC) write 1: bit cleared read 1 response error + unsigned WRST2 : 1; // (R) 1: buffer channel0 not copied + unsigned RDST2 : 1; // (R) 1: new Data available + unsigned : 2; // 15:14 + unsigned UNRUN1 : 1; // (RWC) write 1: bit cleared read 1 main proc underrun error + unsigned OVRUN1 : 1; // (RWC) write 1: bit cleared read 1 overrun error + unsigned COLL1 : 1; // (RWC) write 1: bit cleared read 1 collision error + unsigned NOREP1 : 1; // (RWC) write 1: bit cleared read 1 response error + unsigned WRST1 : 1; // (R) 1: buffer channel0 not copied + unsigned RDST1 : 1; // (R) 1: new Data available + unsigned : 2; // 23:22 + unsigned UNRUN0 : 1; // (RWC) write 1: bit cleared read 1 main proc underrun error + unsigned OVRUN0 : 1; // (RWC) write 1: bit cleared read 1 overrun error + unsigned COLL0 : 1; // (RWC) write 1: bit cleared read 1 collision error + unsigned NOREP0 : 1; // (RWC) write 1: bit cleared read 1 response error + unsigned WRST0 : 1; // (R) 1: buffer channel0 not copied + unsigned RDST0 : 1; // (R) 1: new Data available + unsigned : 1; + unsigned WR : 1; // (RW) write 1 start copy, read 0 copy done + }; + USIStatusReg() {Hex = 0;} + USIStatusReg(u32 _hex) {Hex = _hex;} +}; + +// SI EXI Clock Count +union USIEXIClockCount +{ + u32 Hex; + struct + { + unsigned LOCK : 1; + unsigned : 30; + }; +}; + +// STATE_TO_SAVE +static SSIChannel g_Channel[NUMBER_OF_CHANNELS]; +static USIPoll g_Poll; +static USIComCSR g_ComCSR; +static USIStatusReg g_StatusReg; +static USIEXIClockCount g_EXIClockCount; +static u8 g_SIBuffer[128]; + +void DoState(PointerWrap &p) +{ + // p.DoArray(g_Channel); + p.Do(g_Poll); + p.Do(g_ComCSR); + p.Do(g_StatusReg); + p.Do(g_EXIClockCount); + p.Do(g_SIBuffer); +} + +static void GenerateSIInterrupt(SIInterruptType _SIInterrupt); +void RunSIBuffer(); +void UpdateInterrupts(); + +void Init() +{ + for (int i = 0; i < NUMBER_OF_CHANNELS; i++) + { + g_Channel[i].m_Out.Hex = 0; + g_Channel[i].m_InHi.Hex = 0; + g_Channel[i].m_InLo.Hex = 0; + } + + unsigned int AttachedPadMask = PluginPAD::PAD_GetAttachedPads ? PluginPAD::PAD_GetAttachedPads() : 1; + for (int i = 0; i < 4; i++) + { + if (AttachedPadMask & (1 << i)) + g_Channel[i].m_pDevice = new CSIDevice_GCController(i); + else + g_Channel[i].m_pDevice = new CSIDevice_Dummy(i); + } + + g_Poll.Hex = 0; + g_ComCSR.Hex = 0; + g_StatusReg.Hex = 0; + g_EXIClockCount.Hex = 0; + memset(g_SIBuffer, 0xce, 128); +} + +void Shutdown() +{ + for (int i = 0; i < NUMBER_OF_CHANNELS; i++) + { + delete g_Channel[i].m_pDevice; + g_Channel[i].m_pDevice = NULL; + } +} + +void Read32(u32& _uReturnValue, const u32 _iAddress) +{ + LOGV(SERIALINTERFACE, 3, "(r32): 0x%08x", _iAddress); + + // SIBuffer + if ((_iAddress >= 0xCC006480 && _iAddress < 0xCC006500) || + (_iAddress >= 0xCD006480 && _iAddress < 0xCD006500)) + { + _uReturnValue = *(u32*)&g_SIBuffer[_iAddress & 0x7F]; + return; + } + + // registers + switch (_iAddress & 0x3FF) + { + //=================================================================================================== + // Channel 0 + //=================================================================================================== + case SI_CHANNEL_0_OUT: + _uReturnValue = g_Channel[0].m_Out.Hex; + return; + + case SI_CHANNEL_0_IN_HI: + g_StatusReg.RDST0 = 0; + UpdateInterrupts(); + _uReturnValue = g_Channel[0].m_InHi.Hex; + return; + + case SI_CHANNEL_0_IN_LO: + g_StatusReg.RDST0 = 0; + UpdateInterrupts(); + _uReturnValue = g_Channel[0].m_InLo.Hex; + return; + + //=================================================================================================== + // Channel 1 + //=================================================================================================== + case SI_CHANNEL_1_OUT: + _uReturnValue = g_Channel[1].m_Out.Hex; + return; + + case SI_CHANNEL_1_IN_HI: + g_StatusReg.RDST1 = 0; + UpdateInterrupts(); + _uReturnValue = g_Channel[1].m_InHi.Hex; + return; + + case SI_CHANNEL_1_IN_LO: + g_StatusReg.RDST1 = 0; + UpdateInterrupts(); + _uReturnValue = g_Channel[1].m_InLo.Hex; + return; + + //=================================================================================================== + // Channel 2 + //=================================================================================================== + case SI_CHANNEL_2_OUT: + _uReturnValue = g_Channel[2].m_Out.Hex; + return; + + case SI_CHANNEL_2_IN_HI: + g_StatusReg.RDST2 = 0; + UpdateInterrupts(); + _uReturnValue = g_Channel[2].m_InHi.Hex; + return; + + case SI_CHANNEL_2_IN_LO: + g_StatusReg.RDST2 = 0; + UpdateInterrupts(); + _uReturnValue = g_Channel[2].m_InLo.Hex; + return; + + //=================================================================================================== + // Channel 3 + //=================================================================================================== + case SI_CHANNEL_3_OUT: + _uReturnValue = g_Channel[3].m_Out.Hex; + return; + + case SI_CHANNEL_3_IN_HI: + g_StatusReg.RDST3 = 0; + UpdateInterrupts(); + _uReturnValue = g_Channel[3].m_InHi.Hex; + return; + + case SI_CHANNEL_3_IN_LO: + g_StatusReg.RDST3 = 0; + UpdateInterrupts(); + _uReturnValue = g_Channel[3].m_InLo.Hex; + return; + + case SI_POLL: _uReturnValue = g_Poll.Hex; return; + case SI_COM_CSR: _uReturnValue = g_ComCSR.Hex; return; + case SI_STATUS_REG: _uReturnValue = g_StatusReg.Hex; return; + + case SI_EXI_CLOCK_COUNT: _uReturnValue = g_EXIClockCount.Hex; return; + + default: + LOG(SERIALINTERFACE, "(r32-unk): 0x%08x", _iAddress); + _dbg_assert_(SERIALINTERFACE,0); + break; + } + + // error + _uReturnValue = 0xdeadbeef; +} + +void Write32(const u32 _iValue, const u32 _iAddress) +{ + LOGV(SERIALINTERFACE, 3, "(w32): 0x%08x 0x%08x", _iValue,_iAddress); + + // SIBuffer + if ((_iAddress >= 0xCC006480 && _iAddress < 0xCC006500) || + (_iAddress >= 0xCD006480 && _iAddress < 0xCD006500)) + { + *(u32*)&g_SIBuffer[_iAddress & 0x7F] = _iValue; + return; + } + + // registers + switch (_iAddress & 0x3FF) + { + case SI_CHANNEL_0_OUT: g_Channel[0].m_Out.Hex = _iValue; break; + case SI_CHANNEL_0_IN_HI: g_Channel[0].m_InHi.Hex = _iValue; break; + case SI_CHANNEL_0_IN_LO: g_Channel[0].m_InLo.Hex = _iValue; break; + case SI_CHANNEL_1_OUT: g_Channel[1].m_Out.Hex = _iValue; break; + case SI_CHANNEL_1_IN_HI: g_Channel[1].m_InHi.Hex = _iValue; break; + case SI_CHANNEL_1_IN_LO: g_Channel[1].m_InLo.Hex = _iValue; break; + case SI_CHANNEL_2_OUT: g_Channel[2].m_Out.Hex = _iValue; break; + case SI_CHANNEL_2_IN_HI: g_Channel[2].m_InHi.Hex = _iValue; break; + case SI_CHANNEL_2_IN_LO: g_Channel[2].m_InLo.Hex = _iValue; break; + case SI_CHANNEL_3_OUT: g_Channel[3].m_Out.Hex = _iValue; break; + case SI_CHANNEL_3_IN_HI: g_Channel[3].m_InHi.Hex = _iValue; break; + case SI_CHANNEL_3_IN_LO: g_Channel[3].m_InLo.Hex = _iValue; break; + + case SI_POLL: + g_Poll.Hex = _iValue; + break; + + case SI_COM_CSR: + { + USIComCSR tmpComCSR(_iValue); + + g_ComCSR.CHANNEL = tmpComCSR.CHANNEL; + g_ComCSR.INLNGTH = tmpComCSR.INLNGTH; + g_ComCSR.OUTLNGTH = tmpComCSR.OUTLNGTH; + g_ComCSR.RDSTINTMSK = tmpComCSR.RDSTINTMSK; + g_ComCSR.TCINTMSK = tmpComCSR.TCINTMSK; + + g_ComCSR.COMERR = 0; + + if (tmpComCSR.RDSTINT) g_ComCSR.RDSTINT = 0; + if (tmpComCSR.TCINT) g_ComCSR.TCINT = 0; + + // be careful: runsi-buffer after updating the INT flags + if (tmpComCSR.TSTART) RunSIBuffer(); + UpdateInterrupts(); + } + break; + + case SI_STATUS_REG: + { + USIStatusReg tmpStatus(_iValue); + + // just update the writable bits + g_StatusReg.NOREP0 = tmpStatus.NOREP0 ? 1 : 0; + g_StatusReg.COLL0 = tmpStatus.COLL0 ? 1 : 0; + g_StatusReg.OVRUN0 = tmpStatus.OVRUN0 ? 1 : 0; + g_StatusReg.UNRUN0 = tmpStatus.UNRUN0 ? 1 : 0; + + g_StatusReg.NOREP1 = tmpStatus.NOREP1 ? 1 : 0; + g_StatusReg.COLL1 = tmpStatus.COLL1 ? 1 : 0; + g_StatusReg.OVRUN1 = tmpStatus.OVRUN1 ? 1 : 0; + g_StatusReg.UNRUN1 = tmpStatus.UNRUN1 ? 1 : 0; + + g_StatusReg.NOREP2 = tmpStatus.NOREP2 ? 1 : 0; + g_StatusReg.COLL2 = tmpStatus.COLL2 ? 1 : 0; + g_StatusReg.OVRUN2 = tmpStatus.OVRUN2 ? 1 : 0; + g_StatusReg.UNRUN2 = tmpStatus.UNRUN2 ? 1 : 0; + + g_StatusReg.NOREP3 = tmpStatus.NOREP3 ? 1 : 0; + g_StatusReg.COLL3 = tmpStatus.COLL3 ? 1 : 0; + g_StatusReg.OVRUN3 = tmpStatus.OVRUN3 ? 1 : 0; + g_StatusReg.UNRUN3 = tmpStatus.UNRUN3 ? 1 : 0; + + // send command to devices + if (tmpStatus.WR) + { + g_StatusReg.WR = 0; + g_Channel[0].m_pDevice->SendCommand(g_Channel[0].m_Out.Hex); + g_Channel[1].m_pDevice->SendCommand(g_Channel[1].m_Out.Hex); + g_Channel[2].m_pDevice->SendCommand(g_Channel[2].m_Out.Hex); + g_Channel[3].m_pDevice->SendCommand(g_Channel[3].m_Out.Hex); + + g_StatusReg.WRST0 = 0; + g_StatusReg.WRST1 = 0; + g_StatusReg.WRST2 = 0; + g_StatusReg.WRST3 = 0; + } + } + break; + + case SI_EXI_CLOCK_COUNT: + g_EXIClockCount.Hex = _iValue; + break; + + case 0x80: + LOG(SERIALINTERFACE, "WII something at 0xCD006480"); + break; + + default: + _dbg_assert_(SERIALINTERFACE,0); + break; + } +} + +void UpdateInterrupts() +{ + // check if we have to update the RDSTINT flag + if (g_StatusReg.RDST0 || g_StatusReg.RDST1 || + g_StatusReg.RDST2 || g_StatusReg.RDST3) + g_ComCSR.RDSTINT = 1; + else + g_ComCSR.RDSTINT = 0; + + // check if we have to generate an interrupt + if ((g_ComCSR.RDSTINT & g_ComCSR.RDSTINTMSK) || + (g_ComCSR.TCINT & g_ComCSR.TCINTMSK)) + { + CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_SI, true); + } + else + { + CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_SI, false); + } +} + +void GenerateSIInterrupt(SIInterruptType _SIInterrupt) +{ + switch(_SIInterrupt) + { + case INT_RDSTINT: g_ComCSR.RDSTINT = 1; break; + case INT_TCINT: g_ComCSR.TCINT = 1; break; + } + + UpdateInterrupts(); +} + +void UpdateDevices() +{ + // update channels + g_StatusReg.RDST0 = g_Channel[0].m_pDevice->GetData(g_Channel[0].m_InHi.Hex, g_Channel[0].m_InLo.Hex) ? 1 : 0; + g_StatusReg.RDST1 = g_Channel[1].m_pDevice->GetData(g_Channel[1].m_InHi.Hex, g_Channel[1].m_InLo.Hex) ? 1 : 0; + g_StatusReg.RDST2 = g_Channel[2].m_pDevice->GetData(g_Channel[2].m_InHi.Hex, g_Channel[2].m_InLo.Hex) ? 1 : 0; + g_StatusReg.RDST3 = g_Channel[3].m_pDevice->GetData(g_Channel[3].m_InHi.Hex, g_Channel[3].m_InLo.Hex) ? 1 : 0; + + // update interrupts + UpdateInterrupts(); +} + +void RunSIBuffer() +{ + // math inLength + int inLength = g_ComCSR.INLNGTH; + if (inLength == 0) + inLength = 128; + else + inLength++; + + // math outLength + int outLength = g_ComCSR.OUTLNGTH; + if (outLength == 0) + outLength = 128; + else + outLength++; + +#ifdef LOGGING + int numOutput = +#endif + g_Channel[g_ComCSR.CHANNEL].m_pDevice->RunBuffer(g_SIBuffer, inLength); + LOGV(SERIALINTERFACE, 2, "RunSIBuffer (intLen: %i outLen: %i) (processed: %i)", inLength, outLength, numOutput); + + // Transfer completed + GenerateSIInterrupt(INT_TCINT); + g_ComCSR.TSTART = 0; +} + +} // end of namespace SerialInterface + diff --git a/Source/Core/Core/Src/HW/SerialInterface_Devices.cpp b/Source/Core/Core/Src/HW/SerialInterface_Devices.cpp index 82fe3b5b48..1719fb83fc 100644 --- a/Source/Core/Core/Src/HW/SerialInterface_Devices.cpp +++ b/Source/Core/Core/Src/HW/SerialInterface_Devices.cpp @@ -1,238 +1,238 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include -#include - -#include "SerialInterface_Devices.h" - -#include "EXI_Device.h" -#include "EXI_DeviceMic.h" - -#include "../Plugins/Plugin_PAD.h" - -#include "../PowerPC/PowerPC.h" -#include "CPU.h" - -#define SI_TYPE_GC 0x08000000u - -#define SI_GC_STANDARD 0x01000000u // dolphin standard controller -#define SI_GC_NOMOTOR 0x20000000u // no rumble motor - -#define SI_GC_KEYBOARD (SI_TYPE_GC | 0x00200000) -#define SI_GC_CONTROLLER (SI_TYPE_GC | SI_GC_STANDARD) - -#define SI_MAX_COMCSR_INLNGTH 128 -#define SI_MAX_COMCSR_OUTLNGTH 128 - -// ===================================================================================================== -// --- base class --- -// ===================================================================================================== - -int ISIDevice::RunBuffer(u8* _pBuffer, int _iLength) -{ -#ifdef _DEBUG - LOG(SERIALINTERFACE, "Send Data Device(%i) - Length(%i) ", ISIDevice::m_iDeviceNumber,_iLength); - - char szTemp[256] = ""; - int num = 0; - while(num < _iLength) - { - char szTemp2[128] = ""; - sprintf(szTemp2, "0x%02x ", _pBuffer[num^3]); - strcat(szTemp, szTemp2); - num++; - - if ((num % 8) == 0) - { - LOG(SERIALINTERFACE, szTemp); - szTemp[0] = '\0'; - } - } - LOG(SERIALINTERFACE, szTemp); -#endif - return 0; -}; - -// ===================================================================================================== -// --- standard gamecube controller --- -// ===================================================================================================== - -CSIDevice_GCController::CSIDevice_GCController(int _iDeviceNumber) : - ISIDevice(_iDeviceNumber) -{ - memset(&m_origin, 0, sizeof(SOrigin)); - - m_origin.uCommand = 0x41; - m_origin.uOriginStickX = 0x80; - m_origin.uOriginStickY = 0x80; - m_origin.uSubStickStickX = 0x80; - m_origin.uSubStickStickY = 0x80; - m_origin.uTrigger_L = 0x1F; - m_origin.uTrigger_R = 0x1F; -} - -int CSIDevice_GCController::RunBuffer(u8* _pBuffer, int _iLength) -{ - // for debug logging only - ISIDevice::RunBuffer(_pBuffer, _iLength); - - int iPosition = 0; - while(iPosition < _iLength) - { - // read the command - EBufferCommands command = static_cast(_pBuffer[iPosition ^ 3]); - iPosition++; - - // handle it - switch(command) - { - case CMD_RESET: - { - *(u32*)&_pBuffer[0] = SI_GC_CONTROLLER; // | SI_GC_NOMOTOR; - iPosition = _iLength; // break the while loop - } - break; - - case CMD_ORIGIN: - { - LOG(SERIALINTERFACE, "SI - Get Origin"); - u8* pCalibration = reinterpret_cast(&m_origin); - for (int i = 0; i < (int)sizeof(SOrigin); i++) - { - _pBuffer[i ^ 3] = *pCalibration++; - } - } - iPosition = _iLength; - break; - - // Recalibrate (FiRES: i am not 100 percent sure about this) - case CMD_RECALIBRATE: - { - LOG(SERIALINTERFACE, "SI - Recalibrate"); - u8* pCalibration = reinterpret_cast(&m_origin); - for (int i = 0; i < (int)sizeof(SOrigin); i++) - { - _pBuffer[i ^ 3] = *pCalibration++; - } - } - iPosition = _iLength; - break; - - // WII Something - case 0xCE: - LOG(SERIALINTERFACE, "Unknown Wii SI Command"); - break; - - // DEFAULT - default: - { - LOG(SERIALINTERFACE, "unknown SI command (0x%x)", command); - PanicAlert("SI: Unknown command"); - iPosition = _iLength; - } - break; - } - } - - return iPosition; -} - -// __________________________________________________________________________________________________ -// GetData -// -// return true on new data (max 7 Bytes and 6 bits ;) -// -bool -CSIDevice_GCController::GetData(u32& _Hi, u32& _Low) -{ - SPADStatus PadStatus; - memset(&PadStatus, 0 ,sizeof(PadStatus)); - PluginPAD::PAD_GetStatus(ISIDevice::m_iDeviceNumber, &PadStatus); - - _Hi = (u32)((u8)PadStatus.stickY); - _Hi |= (u32)((u8)PadStatus.stickX << 8); - _Hi |= (u32)((u16)PadStatus.button << 16); - - _Low = (u8)PadStatus.triggerRight; - _Low |= (u32)((u8)PadStatus.triggerLeft << 8); - _Low |= (u32)((u8)PadStatus.substickY << 16); - _Low |= (u32)((u8)PadStatus.substickX << 24); - SetMic(PadStatus.MicButton); - - // F|RES: - // i dunno if i should force it here - // means that the pad must be "combined" with the origin to math the "final" OSPad-Struct - _Hi |= 0x00800000; - - return true; -} - -// __________________________________________________________________________________________________ -// SendCommand -// -void -CSIDevice_GCController::SendCommand(u32 _Cmd) -{ - UCommand command(_Cmd); - switch(command.Command) - { - // Costis sent it in some demos :) - case 0x00: - break; - - case CMD_RUMBLE: - { - unsigned int uType = command.Parameter1; // 0 = stop, 1 = rumble, 2 = stop hard - unsigned int uStrength = command.Parameter2; - if (PluginPAD::PAD_Rumble) - PluginPAD::PAD_Rumble(ISIDevice::m_iDeviceNumber, uType, uStrength); - } - break; - - default: - { - LOG(SERIALINTERFACE, "unknown direct command (0x%x)", _Cmd); - PanicAlert("SI: Unknown direct command"); - } - break; - } -} - -// ===================================================================================================== -// --- dummy device --- -// ===================================================================================================== - -CSIDevice_Dummy::CSIDevice_Dummy(int _iDeviceNumber) : - ISIDevice(_iDeviceNumber) -{} - -int CSIDevice_Dummy::RunBuffer(u8* _pBuffer, int _iLength) -{ - reinterpret_cast(_pBuffer)[0] = 0x00000000; // no device - return 4; -} - -bool CSIDevice_Dummy::GetData(u32& _Hi, u32& _Low) -{ - return false; -} - -void CSIDevice_Dummy::SendCommand(u32 _Cmd) -{ -} - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include + +#include "SerialInterface_Devices.h" + +#include "EXI_Device.h" +#include "EXI_DeviceMic.h" + +#include "../Plugins/Plugin_PAD.h" + +#include "../PowerPC/PowerPC.h" +#include "CPU.h" + +#define SI_TYPE_GC 0x08000000u + +#define SI_GC_STANDARD 0x01000000u // dolphin standard controller +#define SI_GC_NOMOTOR 0x20000000u // no rumble motor + +#define SI_GC_KEYBOARD (SI_TYPE_GC | 0x00200000) +#define SI_GC_CONTROLLER (SI_TYPE_GC | SI_GC_STANDARD) + +#define SI_MAX_COMCSR_INLNGTH 128 +#define SI_MAX_COMCSR_OUTLNGTH 128 + +// ===================================================================================================== +// --- base class --- +// ===================================================================================================== + +int ISIDevice::RunBuffer(u8* _pBuffer, int _iLength) +{ +#ifdef _DEBUG + LOG(SERIALINTERFACE, "Send Data Device(%i) - Length(%i) ", ISIDevice::m_iDeviceNumber,_iLength); + + char szTemp[256] = ""; + int num = 0; + while(num < _iLength) + { + char szTemp2[128] = ""; + sprintf(szTemp2, "0x%02x ", _pBuffer[num^3]); + strcat(szTemp, szTemp2); + num++; + + if ((num % 8) == 0) + { + LOG(SERIALINTERFACE, szTemp); + szTemp[0] = '\0'; + } + } + LOG(SERIALINTERFACE, szTemp); +#endif + return 0; +}; + +// ===================================================================================================== +// --- standard gamecube controller --- +// ===================================================================================================== + +CSIDevice_GCController::CSIDevice_GCController(int _iDeviceNumber) : + ISIDevice(_iDeviceNumber) +{ + memset(&m_origin, 0, sizeof(SOrigin)); + + m_origin.uCommand = 0x41; + m_origin.uOriginStickX = 0x80; + m_origin.uOriginStickY = 0x80; + m_origin.uSubStickStickX = 0x80; + m_origin.uSubStickStickY = 0x80; + m_origin.uTrigger_L = 0x1F; + m_origin.uTrigger_R = 0x1F; +} + +int CSIDevice_GCController::RunBuffer(u8* _pBuffer, int _iLength) +{ + // for debug logging only + ISIDevice::RunBuffer(_pBuffer, _iLength); + + int iPosition = 0; + while(iPosition < _iLength) + { + // read the command + EBufferCommands command = static_cast(_pBuffer[iPosition ^ 3]); + iPosition++; + + // handle it + switch(command) + { + case CMD_RESET: + { + *(u32*)&_pBuffer[0] = SI_GC_CONTROLLER; // | SI_GC_NOMOTOR; + iPosition = _iLength; // break the while loop + } + break; + + case CMD_ORIGIN: + { + LOG(SERIALINTERFACE, "SI - Get Origin"); + u8* pCalibration = reinterpret_cast(&m_origin); + for (int i = 0; i < (int)sizeof(SOrigin); i++) + { + _pBuffer[i ^ 3] = *pCalibration++; + } + } + iPosition = _iLength; + break; + + // Recalibrate (FiRES: i am not 100 percent sure about this) + case CMD_RECALIBRATE: + { + LOG(SERIALINTERFACE, "SI - Recalibrate"); + u8* pCalibration = reinterpret_cast(&m_origin); + for (int i = 0; i < (int)sizeof(SOrigin); i++) + { + _pBuffer[i ^ 3] = *pCalibration++; + } + } + iPosition = _iLength; + break; + + // WII Something + case 0xCE: + LOG(SERIALINTERFACE, "Unknown Wii SI Command"); + break; + + // DEFAULT + default: + { + LOG(SERIALINTERFACE, "unknown SI command (0x%x)", command); + PanicAlert("SI: Unknown command"); + iPosition = _iLength; + } + break; + } + } + + return iPosition; +} + +// __________________________________________________________________________________________________ +// GetData +// +// return true on new data (max 7 Bytes and 6 bits ;) +// +bool +CSIDevice_GCController::GetData(u32& _Hi, u32& _Low) +{ + SPADStatus PadStatus; + memset(&PadStatus, 0 ,sizeof(PadStatus)); + PluginPAD::PAD_GetStatus(ISIDevice::m_iDeviceNumber, &PadStatus); + + _Hi = (u32)((u8)PadStatus.stickY); + _Hi |= (u32)((u8)PadStatus.stickX << 8); + _Hi |= (u32)((u16)PadStatus.button << 16); + + _Low = (u8)PadStatus.triggerRight; + _Low |= (u32)((u8)PadStatus.triggerLeft << 8); + _Low |= (u32)((u8)PadStatus.substickY << 16); + _Low |= (u32)((u8)PadStatus.substickX << 24); + SetMic(PadStatus.MicButton); + + // F|RES: + // i dunno if i should force it here + // means that the pad must be "combined" with the origin to math the "final" OSPad-Struct + _Hi |= 0x00800000; + + return true; +} + +// __________________________________________________________________________________________________ +// SendCommand +// +void +CSIDevice_GCController::SendCommand(u32 _Cmd) +{ + UCommand command(_Cmd); + switch(command.Command) + { + // Costis sent it in some demos :) + case 0x00: + break; + + case CMD_RUMBLE: + { + unsigned int uType = command.Parameter1; // 0 = stop, 1 = rumble, 2 = stop hard + unsigned int uStrength = command.Parameter2; + if (PluginPAD::PAD_Rumble) + PluginPAD::PAD_Rumble(ISIDevice::m_iDeviceNumber, uType, uStrength); + } + break; + + default: + { + LOG(SERIALINTERFACE, "unknown direct command (0x%x)", _Cmd); + PanicAlert("SI: Unknown direct command"); + } + break; + } +} + +// ===================================================================================================== +// --- dummy device --- +// ===================================================================================================== + +CSIDevice_Dummy::CSIDevice_Dummy(int _iDeviceNumber) : + ISIDevice(_iDeviceNumber) +{} + +int CSIDevice_Dummy::RunBuffer(u8* _pBuffer, int _iLength) +{ + reinterpret_cast(_pBuffer)[0] = 0x00000000; // no device + return 4; +} + +bool CSIDevice_Dummy::GetData(u32& _Hi, u32& _Low) +{ + return false; +} + +void CSIDevice_Dummy::SendCommand(u32 _Cmd) +{ +} + diff --git a/Source/Core/Core/Src/HW/StreamADPCM.cpp b/Source/Core/Core/Src/HW/StreamADPCM.cpp index 7a90f8c5e0..99e1a2e958 100644 --- a/Source/Core/Core/Src/HW/StreamADPCM.cpp +++ b/Source/Core/Core/Src/HW/StreamADPCM.cpp @@ -1,61 +1,61 @@ -/********************************************************************* - - Nintendo GameCube ADPCM Decoder Core Class - Author: Shinji Chiba - -*********************************************************************/ - -#include "StreamADPCM.H" - -// STATE_TO_SAVE -float NGCADPCM::iir1[STEREO], - NGCADPCM::iir2[STEREO]; - -void NGCADPCM::InitFilter() -{ - iir1[0] = iir1[1] = - iir2[0] = iir2[1] = 0.0f; -} - -short NGCADPCM::DecodeSample( int bits, int q, int ch ) -{ - static const float coef[4] = { 0.86f, 1.8f, 0.82f, 1.53f }; - float iir_filter; - - iir_filter = (float) ((short) (bits << 12) >> (q & 0xf)); - switch (q >> 4) - { - case 1: - iir_filter += (iir1[ch] * coef[0]); - break; - case 2: - iir_filter += (iir1[ch] * coef[1] - iir2[ch] * coef[2]); - break; - case 3: - iir_filter += (iir1[ch] * coef[3] - iir2[ch] * coef[0]); - } - - iir2[ch] = iir1[ch]; - if ( iir_filter <= -32768.5f ) - { - iir1[ch] = -32767.5f; - return -32767; - } - else if ( iir_filter >= 32767.5f ) - { - iir1[ch] = 32767.5f; - return 32767; - } - return (short) ((iir1[ch] = iir_filter) * 0.5f); -} - -void NGCADPCM::DecodeBlock( short *pcm, u8* adpcm) -{ - int ch = 1; - int i; - for( i = 0; i < SAMPLES_PER_BLOCK; i++ ) - { - pcm[i * STEREO] = DecodeSample( adpcm[i + (ONE_BLOCK_SIZE - SAMPLES_PER_BLOCK)] & 0xf, adpcm[0], 0 ); - pcm[i * STEREO + MONO] = DecodeSample( adpcm[i + (ONE_BLOCK_SIZE - SAMPLES_PER_BLOCK)] >> 4, adpcm[ch], ch ); - } -} +/********************************************************************* + + Nintendo GameCube ADPCM Decoder Core Class + Author: Shinji Chiba + +*********************************************************************/ + +#include "StreamADPCM.H" + +// STATE_TO_SAVE +float NGCADPCM::iir1[STEREO], + NGCADPCM::iir2[STEREO]; + +void NGCADPCM::InitFilter() +{ + iir1[0] = iir1[1] = + iir2[0] = iir2[1] = 0.0f; +} + +short NGCADPCM::DecodeSample( int bits, int q, int ch ) +{ + static const float coef[4] = { 0.86f, 1.8f, 0.82f, 1.53f }; + float iir_filter; + + iir_filter = (float) ((short) (bits << 12) >> (q & 0xf)); + switch (q >> 4) + { + case 1: + iir_filter += (iir1[ch] * coef[0]); + break; + case 2: + iir_filter += (iir1[ch] * coef[1] - iir2[ch] * coef[2]); + break; + case 3: + iir_filter += (iir1[ch] * coef[3] - iir2[ch] * coef[0]); + } + + iir2[ch] = iir1[ch]; + if ( iir_filter <= -32768.5f ) + { + iir1[ch] = -32767.5f; + return -32767; + } + else if ( iir_filter >= 32767.5f ) + { + iir1[ch] = 32767.5f; + return 32767; + } + return (short) ((iir1[ch] = iir_filter) * 0.5f); +} + +void NGCADPCM::DecodeBlock( short *pcm, u8* adpcm) +{ + int ch = 1; + int i; + for( i = 0; i < SAMPLES_PER_BLOCK; i++ ) + { + pcm[i * STEREO] = DecodeSample( adpcm[i + (ONE_BLOCK_SIZE - SAMPLES_PER_BLOCK)] & 0xf, adpcm[0], 0 ); + pcm[i * STEREO + MONO] = DecodeSample( adpcm[i + (ONE_BLOCK_SIZE - SAMPLES_PER_BLOCK)] >> 4, adpcm[ch], ch ); + } +} diff --git a/Source/Core/Core/Src/HW/SystemTimers.cpp b/Source/Core/Core/Src/HW/SystemTimers.cpp index e8482070d2..b5e1b10b13 100644 --- a/Source/Core/Core/Src/HW/SystemTimers.cpp +++ b/Source/Core/Core/Src/HW/SystemTimers.cpp @@ -1,234 +1,234 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "../PatchEngine.h" -#include "SystemTimers.h" -#include "../Plugins/Plugin_DSP.h" -#include "../Plugins/Plugin_Video.h" -#include "../HW/DSP.h" -#include "../HW/AudioInterface.h" -#include "../HW/VideoInterface.h" -#include "../HW/SerialInterface.h" -#include "../HW/CommandProcessor.h" // for DC watchdog hack -#include "../PowerPC/PowerPC.h" -#include "../CoreTiming.h" -#include "../Core.h" -#include "../IPC_HLE/WII_IPC_HLE.h" -#include "Thread.h" -#include "Timer.h" - -namespace SystemTimers -{ - -u32 CPU_CORE_CLOCK = 486000000u; // 486 mhz (its not 485, stop bugging me!) - -s64 fakeDec; - -// ratio of TB and Decrementer to clock cycles -// With TB clk at 1/4 of BUS clk -// and it seems BUS clk is really 1/3 of CPU clk -// note: ZWW is ok and faster with TIMER_RATIO=8 though. -// !!! POSSIBLE STABLE PERF BOOST HACK THERE !!! -enum { - TIMER_RATIO = 12 -}; - -int et_Dec; -int et_VI; -int et_SI; -int et_AI; -int et_AudioFifo; -int et_DSP; -int et_IPC_HLE; -int et_FakeGPWD; // for DC watchdog hack - -// These are badly educated guesses -// Feel free to experiment -int - // update VI often to let it go through its scanlines with decent accuracy - // Maybe should actually align this with the scanline update? Current method in - // VideoInterface::Update is stupid! - VI_PERIOD = GetTicksPerSecond() / (60*120), - - // TODO: The SI interfact actually has a register that determines the polling frequency. - // We should obey that instead of arbitrarly checking at 60fps. - SI_PERIOD = GetTicksPerSecond() / 60, //once a frame is good for controllers - - // This one should simply be determined by the increasing counter in AI. - AI_PERIOD = GetTicksPerSecond() / 80, - - // These shouldn't be period controlled either, most likely. - DSP_PERIOD = GetTicksPerSecond() / 250, - - // This is completely arbitrary. If we find that we need lower latency, we can just - // increase this number. - IPC_HLE_PERIOD = GetTicksPerSecond() / 250, - - // For DC watchdog hack - // Once every 2 frame-period should be enough. - // Assuming game's frame-finish-watchdog wait more than 2 emulated frame-period before starting its mess. - FAKE_GP_WATCHDOG_PERIOD = GetTicksPerSecond() / 30; - -u32 GetTicksPerSecond() -{ - return CPU_CORE_CLOCK; -} - -u32 ConvertMillisecondsToTicks(u32 _Milliseconds) -{ - return GetTicksPerSecond() / 1000 * _Milliseconds; -} - -void AICallback(u64 userdata, int cyclesLate) -{ - // Update disk streaming. All that code really needs a revamp, including replacing the codec with the one - // from in_cube. - AudioInterface::Update(); - CoreTiming::ScheduleEvent(AI_PERIOD-cyclesLate, et_AI); -} - -void DSPCallback(u64 userdata, int cyclesLate) -{ - // ~1/6th as many cycles as the period PPC-side. - PluginDSP::DSP_Update(DSP_PERIOD / 6); - CoreTiming::ScheduleEvent(DSP_PERIOD-cyclesLate, et_DSP); -} - -void AudioFifoCallback(u64 userdata, int cyclesLate) -{ - int period = CPU_CORE_CLOCK / (AudioInterface::GetDSPSampleRate() * 4 / 32); - DSP::UpdateAudioDMA(); // Push audio to speakers. - - CoreTiming::ScheduleEvent(period - cyclesLate, et_AudioFifo); -} - -void IPC_HLE_UpdateCallback(u64 userdata, int cyclesLate) -{ - WII_IPC_HLE_Interface::Update(); - CoreTiming::ScheduleEvent(IPC_HLE_PERIOD-cyclesLate, et_IPC_HLE); -} - -void VICallback(u64 userdata, int cyclesLate) -{ - VideoInterface::Update(); - CoreTiming::ScheduleEvent(VI_PERIOD-cyclesLate, et_VI); -} - -void SICallback(u64 userdata, int cyclesLate) -{ - // This is once per frame - good candidate for patching stuff - PatchEngine::ApplyFramePatches(); - // Apply AR cheats - PatchEngine::ApplyARPatches(); - // OK, do what we are here to do. - SerialInterface::UpdateDevices(); - CoreTiming::ScheduleEvent(SI_PERIOD-cyclesLate, et_SI); -} - -void DecrementerCallback(u64 userdata, int cyclesLate) -{ - //Why is fakeDec too big here? - fakeDec = -1; - PowerPC::ppcState.spr[SPR_DEC] = 0xFFFFFFFF; - PowerPC::ppcState.Exceptions |= EXCEPTION_DECREMENTER; -} - -void DecrementerSet() -{ - u32 decValue = PowerPC::ppcState.spr[SPR_DEC]; - fakeDec = decValue*TIMER_RATIO; - CoreTiming::RemoveEvent(et_Dec); - CoreTiming::ScheduleEvent(decValue * TIMER_RATIO, et_Dec); -} - -void AdvanceCallback(int cyclesExecuted) -{ - fakeDec -= cyclesExecuted; - u64 timebase_ticks = CoreTiming::GetTicks() / TIMER_RATIO; //works since we are little endian and TL comes first :) - *(u64*)&TL = timebase_ticks; - if (fakeDec >= 0) - PowerPC::ppcState.spr[SPR_DEC] = (u32)fakeDec / TIMER_RATIO; -} - - -// For DC watchdog hack -void FakeGPWatchdogCallback(u64 userdata, int cyclesLate) -{ - CommandProcessor::WaitForFrameFinish(); // lock CPUThread until frame finish - CoreTiming::ScheduleEvent(FAKE_GP_WATCHDOG_PERIOD-cyclesLate, et_FakeGPWD); -} - -void Init() -{ - if (Core::GetStartupParameter().bWii) - { - CPU_CORE_CLOCK = 721000000; - VI_PERIOD = GetTicksPerSecond() / (60*120); - SI_PERIOD = GetTicksPerSecond() / 60; // once a frame is good for controllers - - // These are the big question marks IMHO :) - AI_PERIOD = GetTicksPerSecond() / 80; - DSP_PERIOD = (int)(GetTicksPerSecond() * 0.003f); - - IPC_HLE_PERIOD = (int)(GetTicksPerSecond() * 0.003f); - } - else - { - CPU_CORE_CLOCK = 486000000; - VI_PERIOD = GetTicksPerSecond() / (60*120); - SI_PERIOD = GetTicksPerSecond() / 60; // once a frame is good for controllers - - // These are the big question marks IMHO :) - AI_PERIOD = GetTicksPerSecond() / 80; - DSP_PERIOD = (int)(GetTicksPerSecond() * 0.005f); - } - Common::Timer::IncreaseResolution(); - - et_Dec = CoreTiming::RegisterEvent("DecCallback", DecrementerCallback); - et_AI = CoreTiming::RegisterEvent("AICallback", AICallback); - et_VI = CoreTiming::RegisterEvent("VICallback", VICallback); - et_SI = CoreTiming::RegisterEvent("SICallback", SICallback); - et_DSP = CoreTiming::RegisterEvent("DSPCallback", DSPCallback); - et_AudioFifo = CoreTiming::RegisterEvent("AudioFifoCallback", AudioFifoCallback); - et_IPC_HLE = CoreTiming::RegisterEvent("IPC_HLE_UpdateCallback", IPC_HLE_UpdateCallback); - - CoreTiming::ScheduleEvent(AI_PERIOD, et_AI); - CoreTiming::ScheduleEvent(VI_PERIOD, et_VI); - CoreTiming::ScheduleEvent(DSP_PERIOD, et_DSP); - CoreTiming::ScheduleEvent(SI_PERIOD, et_SI); - CoreTiming::ScheduleEvent(CPU_CORE_CLOCK / (32000 * 4 / 32), et_AudioFifo); - - // For DC watchdog hack - if (Core::GetStartupParameter().bUseDualCore) - { - et_FakeGPWD = CoreTiming::RegisterEvent("FakeGPWatchdogCallback", FakeGPWatchdogCallback); - CoreTiming::ScheduleEvent(FAKE_GP_WATCHDOG_PERIOD, et_FakeGPWD); - } - - if (Core::GetStartupParameter().bWii) - CoreTiming::ScheduleEvent(IPC_HLE_PERIOD, et_IPC_HLE); - - CoreTiming::RegisterAdvanceCallback(&AdvanceCallback); -} - -void Shutdown() -{ - Common::Timer::RestoreResolution(); -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "../PatchEngine.h" +#include "SystemTimers.h" +#include "../Plugins/Plugin_DSP.h" +#include "../Plugins/Plugin_Video.h" +#include "../HW/DSP.h" +#include "../HW/AudioInterface.h" +#include "../HW/VideoInterface.h" +#include "../HW/SerialInterface.h" +#include "../HW/CommandProcessor.h" // for DC watchdog hack +#include "../PowerPC/PowerPC.h" +#include "../CoreTiming.h" +#include "../Core.h" +#include "../IPC_HLE/WII_IPC_HLE.h" +#include "Thread.h" +#include "Timer.h" + +namespace SystemTimers +{ + +u32 CPU_CORE_CLOCK = 486000000u; // 486 mhz (its not 485, stop bugging me!) + +s64 fakeDec; + +// ratio of TB and Decrementer to clock cycles +// With TB clk at 1/4 of BUS clk +// and it seems BUS clk is really 1/3 of CPU clk +// note: ZWW is ok and faster with TIMER_RATIO=8 though. +// !!! POSSIBLE STABLE PERF BOOST HACK THERE !!! +enum { + TIMER_RATIO = 12 +}; + +int et_Dec; +int et_VI; +int et_SI; +int et_AI; +int et_AudioFifo; +int et_DSP; +int et_IPC_HLE; +int et_FakeGPWD; // for DC watchdog hack + +// These are badly educated guesses +// Feel free to experiment +int + // update VI often to let it go through its scanlines with decent accuracy + // Maybe should actually align this with the scanline update? Current method in + // VideoInterface::Update is stupid! + VI_PERIOD = GetTicksPerSecond() / (60*120), + + // TODO: The SI interfact actually has a register that determines the polling frequency. + // We should obey that instead of arbitrarly checking at 60fps. + SI_PERIOD = GetTicksPerSecond() / 60, //once a frame is good for controllers + + // This one should simply be determined by the increasing counter in AI. + AI_PERIOD = GetTicksPerSecond() / 80, + + // These shouldn't be period controlled either, most likely. + DSP_PERIOD = GetTicksPerSecond() / 250, + + // This is completely arbitrary. If we find that we need lower latency, we can just + // increase this number. + IPC_HLE_PERIOD = GetTicksPerSecond() / 250, + + // For DC watchdog hack + // Once every 2 frame-period should be enough. + // Assuming game's frame-finish-watchdog wait more than 2 emulated frame-period before starting its mess. + FAKE_GP_WATCHDOG_PERIOD = GetTicksPerSecond() / 30; + +u32 GetTicksPerSecond() +{ + return CPU_CORE_CLOCK; +} + +u32 ConvertMillisecondsToTicks(u32 _Milliseconds) +{ + return GetTicksPerSecond() / 1000 * _Milliseconds; +} + +void AICallback(u64 userdata, int cyclesLate) +{ + // Update disk streaming. All that code really needs a revamp, including replacing the codec with the one + // from in_cube. + AudioInterface::Update(); + CoreTiming::ScheduleEvent(AI_PERIOD-cyclesLate, et_AI); +} + +void DSPCallback(u64 userdata, int cyclesLate) +{ + // ~1/6th as many cycles as the period PPC-side. + PluginDSP::DSP_Update(DSP_PERIOD / 6); + CoreTiming::ScheduleEvent(DSP_PERIOD-cyclesLate, et_DSP); +} + +void AudioFifoCallback(u64 userdata, int cyclesLate) +{ + int period = CPU_CORE_CLOCK / (AudioInterface::GetDSPSampleRate() * 4 / 32); + DSP::UpdateAudioDMA(); // Push audio to speakers. + + CoreTiming::ScheduleEvent(period - cyclesLate, et_AudioFifo); +} + +void IPC_HLE_UpdateCallback(u64 userdata, int cyclesLate) +{ + WII_IPC_HLE_Interface::Update(); + CoreTiming::ScheduleEvent(IPC_HLE_PERIOD-cyclesLate, et_IPC_HLE); +} + +void VICallback(u64 userdata, int cyclesLate) +{ + VideoInterface::Update(); + CoreTiming::ScheduleEvent(VI_PERIOD-cyclesLate, et_VI); +} + +void SICallback(u64 userdata, int cyclesLate) +{ + // This is once per frame - good candidate for patching stuff + PatchEngine::ApplyFramePatches(); + // Apply AR cheats + PatchEngine::ApplyARPatches(); + // OK, do what we are here to do. + SerialInterface::UpdateDevices(); + CoreTiming::ScheduleEvent(SI_PERIOD-cyclesLate, et_SI); +} + +void DecrementerCallback(u64 userdata, int cyclesLate) +{ + //Why is fakeDec too big here? + fakeDec = -1; + PowerPC::ppcState.spr[SPR_DEC] = 0xFFFFFFFF; + PowerPC::ppcState.Exceptions |= EXCEPTION_DECREMENTER; +} + +void DecrementerSet() +{ + u32 decValue = PowerPC::ppcState.spr[SPR_DEC]; + fakeDec = decValue*TIMER_RATIO; + CoreTiming::RemoveEvent(et_Dec); + CoreTiming::ScheduleEvent(decValue * TIMER_RATIO, et_Dec); +} + +void AdvanceCallback(int cyclesExecuted) +{ + fakeDec -= cyclesExecuted; + u64 timebase_ticks = CoreTiming::GetTicks() / TIMER_RATIO; //works since we are little endian and TL comes first :) + *(u64*)&TL = timebase_ticks; + if (fakeDec >= 0) + PowerPC::ppcState.spr[SPR_DEC] = (u32)fakeDec / TIMER_RATIO; +} + + +// For DC watchdog hack +void FakeGPWatchdogCallback(u64 userdata, int cyclesLate) +{ + CommandProcessor::WaitForFrameFinish(); // lock CPUThread until frame finish + CoreTiming::ScheduleEvent(FAKE_GP_WATCHDOG_PERIOD-cyclesLate, et_FakeGPWD); +} + +void Init() +{ + if (Core::GetStartupParameter().bWii) + { + CPU_CORE_CLOCK = 721000000; + VI_PERIOD = GetTicksPerSecond() / (60*120); + SI_PERIOD = GetTicksPerSecond() / 60; // once a frame is good for controllers + + // These are the big question marks IMHO :) + AI_PERIOD = GetTicksPerSecond() / 80; + DSP_PERIOD = (int)(GetTicksPerSecond() * 0.003f); + + IPC_HLE_PERIOD = (int)(GetTicksPerSecond() * 0.003f); + } + else + { + CPU_CORE_CLOCK = 486000000; + VI_PERIOD = GetTicksPerSecond() / (60*120); + SI_PERIOD = GetTicksPerSecond() / 60; // once a frame is good for controllers + + // These are the big question marks IMHO :) + AI_PERIOD = GetTicksPerSecond() / 80; + DSP_PERIOD = (int)(GetTicksPerSecond() * 0.005f); + } + Common::Timer::IncreaseResolution(); + + et_Dec = CoreTiming::RegisterEvent("DecCallback", DecrementerCallback); + et_AI = CoreTiming::RegisterEvent("AICallback", AICallback); + et_VI = CoreTiming::RegisterEvent("VICallback", VICallback); + et_SI = CoreTiming::RegisterEvent("SICallback", SICallback); + et_DSP = CoreTiming::RegisterEvent("DSPCallback", DSPCallback); + et_AudioFifo = CoreTiming::RegisterEvent("AudioFifoCallback", AudioFifoCallback); + et_IPC_HLE = CoreTiming::RegisterEvent("IPC_HLE_UpdateCallback", IPC_HLE_UpdateCallback); + + CoreTiming::ScheduleEvent(AI_PERIOD, et_AI); + CoreTiming::ScheduleEvent(VI_PERIOD, et_VI); + CoreTiming::ScheduleEvent(DSP_PERIOD, et_DSP); + CoreTiming::ScheduleEvent(SI_PERIOD, et_SI); + CoreTiming::ScheduleEvent(CPU_CORE_CLOCK / (32000 * 4 / 32), et_AudioFifo); + + // For DC watchdog hack + if (Core::GetStartupParameter().bUseDualCore) + { + et_FakeGPWD = CoreTiming::RegisterEvent("FakeGPWatchdogCallback", FakeGPWatchdogCallback); + CoreTiming::ScheduleEvent(FAKE_GP_WATCHDOG_PERIOD, et_FakeGPWD); + } + + if (Core::GetStartupParameter().bWii) + CoreTiming::ScheduleEvent(IPC_HLE_PERIOD, et_IPC_HLE); + + CoreTiming::RegisterAdvanceCallback(&AdvanceCallback); +} + +void Shutdown() +{ + Common::Timer::RestoreResolution(); +} + +} // namespace diff --git a/Source/Core/Core/Src/HW/VideoInterface.cpp b/Source/Core/Core/Src/HW/VideoInterface.cpp index c89f46a8cd..3cbfcb7f9e 100644 --- a/Source/Core/Core/Src/HW/VideoInterface.cpp +++ b/Source/Core/Core/Src/HW/VideoInterface.cpp @@ -1,562 +1,562 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "ChunkFile.h" - -#include "../PowerPC/PowerPC.h" - -#include "PeripheralInterface.h" -#include "VideoInterface.h" -#include "Memmap.h" -#include "../Plugins/Plugin_Video.h" -#include "../CoreTiming.h" -#include "../HW/SystemTimers.h" - -namespace VideoInterface -{ -// VI Internal Hardware Addresses -enum -{ - VI_VERTICAL_TIMING = 0x0, - VI_CONTROL_REGISTER = 0x02, - VI_FRAMEBUFFER_TOP_HI = 0x01C, - VI_FRAMEBUFFER_TOP_LO = 0x01E, - VI_FRAMEBUFFER_BOTTOM_HI = 0x024, - VI_FRAMEBUFFER_BOTTOM_LO = 0x026, - VI_VERTICAL_BEAM_POSITION = 0x02C, - VI_HORIZONTAL_BEAM_POSITION = 0x02e, - VI_PRERETRACE = 0x030, - VI_POSTRETRACE = 0x034, - VI_DI2 = 0x038, - VI_DI3 = 0x03C, - VI_INTERLACE = 0x850, - VI_HSCALEW = 0x048, - VI_HSCALER = 0x04A, - VI_FBWIDTH = 0x070, -}; - -union UVIVerticalTimingRegister -{ - u16 Hex; - struct - { - unsigned EQU : 4; - unsigned ACV : 10; - unsigned : 2; - }; - UVIVerticalTimingRegister(u16 _hex) { Hex = _hex;} - UVIVerticalTimingRegister() { Hex = 0;} -}; - -union UVIDisplayControlRegister -{ - u16 Hex; - struct - { - unsigned ENB : 1; - unsigned RST : 1; - unsigned NIN : 1; - unsigned DLR : 1; - unsigned LE0 : 2; - unsigned LE1 : 2; - unsigned FMT : 2; - unsigned : 6; - }; - UVIDisplayControlRegister(u16 _hex) { Hex = _hex;} - UVIDisplayControlRegister() { Hex = 0;} -}; - -// VI Interrupt Register -union UVIInterruptRegister -{ - u32 Hex; - struct - { - u16 Lo; - u16 Hi; - }; - struct - { - unsigned HCT : 11; - unsigned : 5; - unsigned VCT : 11; - unsigned : 1; - unsigned IR_MASK : 1; - unsigned : 2; - unsigned IR_INT : 1; - }; -}; - -union UVIHorizontalStepping -{ - u16 Hex; - struct - { - unsigned FbSteps : 8; - unsigned FieldSteps : 8; - }; -}; - -union UVIHorizontalScaling -{ - u16 Hex; - struct - { - unsigned STP : 9; - unsigned : 3; - unsigned HS_EN : 1; - unsigned : 3; - }; - UVIHorizontalScaling(u16 _hex) { Hex = _hex;} - UVIHorizontalScaling() { Hex = 0;} -}; - -union UVIFrameBufferAddress -{ - u32 Hex; - struct - { - u16 Lo; - u16 Hi; - }; - UVIFrameBufferAddress(u16 _hex) { Hex = _hex;} - UVIFrameBufferAddress() { Hex = 0;} -}; - -// STATE_TO_SAVE -static UVIVerticalTimingRegister m_VIVerticalTimingRegister; - -static UVIDisplayControlRegister m_VIDisplayControlRegister; - -// Framebuffers -static UVIFrameBufferAddress m_FrameBufferTop; // normal framebuffer address -static UVIFrameBufferAddress m_FrameBufferBottom; - -// VI Interrupt Registers -static UVIInterruptRegister m_VIInterruptRegister[4]; - -static UVIHorizontalStepping m_VIHorizontalStepping; -static UVIHorizontalScaling m_VIHorizontalScaling; - -u8 m_UVIUnknownRegs[0x1000]; - -static u16 HorizontalBeamPos = 0; -static u16 VerticalBeamPos = 0; - -static u32 TicksPerFrame = 0; -static u32 LineCount = 0; -static u32 LinesPerField = 0; -static u64 LastTime = 0; -static u32 NextXFBRender = 0; - -// only correct when scaling is enabled? -static u16 FbWidth = 0; - -void DoState(PointerWrap &p) -{ - p.Do(m_VIVerticalTimingRegister); - p.Do(m_VIDisplayControlRegister); - p.Do(m_FrameBufferTop); - p.Do(m_FrameBufferBottom); - p.Do(m_VIInterruptRegister); - p.DoArray(m_UVIUnknownRegs, 0x1000); - p.Do(HorizontalBeamPos); - p.Do(VerticalBeamPos); - p.Do(TicksPerFrame); - p.Do(LineCount); - p.Do(LinesPerField); - p.Do(LastTime); - // p.Do(NextXFBRender); // Activate when changing saves next time. -} - -void Init() -{ - for (int i = 0; i < 4; i++) - m_VIInterruptRegister[i].Hex = 0x00; - - m_VIDisplayControlRegister.Hex = 0x0000; - m_VIDisplayControlRegister.ENB = 0; - m_VIDisplayControlRegister.FMT = 0; - - NextXFBRender = 1; -} - -void Read16(u16& _uReturnValue, const u32 _iAddress) -{ - switch (_iAddress & 0xFFF) - { - case VI_VERTICAL_TIMING: - _uReturnValue = m_VIVerticalTimingRegister.Hex; - return; - - case VI_CONTROL_REGISTER: - LOGV(VIDEOINTERFACE, 3, "VideoInterface(r16): VI_CONTROL_REGISTER 0x%08x", m_VIDisplayControlRegister.Hex); - _uReturnValue = m_VIDisplayControlRegister.Hex; - return; - - case VI_FRAMEBUFFER_TOP_HI: - _uReturnValue = m_FrameBufferTop.Hi; - break; - - case VI_FRAMEBUFFER_TOP_LO: - _uReturnValue = m_FrameBufferTop.Lo; - break; - - case VI_FRAMEBUFFER_BOTTOM_HI: - _uReturnValue = m_FrameBufferBottom.Hi; - break; - - case VI_FRAMEBUFFER_BOTTOM_LO: - _uReturnValue = m_FrameBufferBottom.Lo; - break; - - case VI_VERTICAL_BEAM_POSITION: - _uReturnValue = VerticalBeamPos; - return; - - case VI_HORIZONTAL_BEAM_POSITION: - _uReturnValue = HorizontalBeamPos; - return; - - // RETRACE STUFF ... - case VI_PRERETRACE: - _uReturnValue = m_VIInterruptRegister[0].Hi; - return; - - case VI_POSTRETRACE: - _uReturnValue = m_VIInterruptRegister[1].Hi; - return; - - case VI_DI2: - _uReturnValue = m_VIInterruptRegister[2].Hi; - return; - - case VI_DI3: - _uReturnValue = m_VIInterruptRegister[3].Hi; - return; - - case VI_HSCALEW: - _uReturnValue = m_VIHorizontalStepping.Hex; - break; - - case VI_HSCALER: - _uReturnValue = m_VIHorizontalScaling.Hex; - break; - - case VI_FBWIDTH: - _uReturnValue = FbWidth; - break; - - default: - _uReturnValue = *(u16*)&m_UVIUnknownRegs[_iAddress & 0xFFF]; - return; - } - - _uReturnValue = 0x0; -} - -void Write16(const u16 _iValue, const u32 _iAddress) -{ - LOGV(VIDEOINTERFACE, 3, "(w16): 0x%04x, 0x%08x",_iValue,_iAddress); - - //Somewhere it sets screen width.. we need to communicate this to the gfx plugin... - - switch (_iAddress & 0xFFF) - { - case VI_VERTICAL_TIMING: - m_VIVerticalTimingRegister.Hex = _iValue; - break; - - case VI_CONTROL_REGISTER: - { - UVIDisplayControlRegister tmpConfig(_iValue); - m_VIDisplayControlRegister.ENB = tmpConfig.ENB; - m_VIDisplayControlRegister.NIN = tmpConfig.NIN; - m_VIDisplayControlRegister.DLR = tmpConfig.DLR; - m_VIDisplayControlRegister.LE0 = tmpConfig.LE0; - m_VIDisplayControlRegister.LE1 = tmpConfig.LE1; - m_VIDisplayControlRegister.FMT = tmpConfig.FMT; - - if (tmpConfig.RST) - { - m_VIDisplayControlRegister.RST = 0; - } - - UpdateTiming(); - } - break; - - case VI_FRAMEBUFFER_TOP_HI: - m_FrameBufferTop.Hi = _iValue; - break; - - case VI_FRAMEBUFFER_TOP_LO: - m_FrameBufferTop.Lo = _iValue; - break; - - case VI_FRAMEBUFFER_BOTTOM_HI: - m_FrameBufferBottom.Hi = _iValue; - break; - - case VI_FRAMEBUFFER_BOTTOM_LO: - m_FrameBufferBottom.Lo = _iValue; - break; - - case VI_VERTICAL_BEAM_POSITION: - _dbg_assert_(VIDEOINTERFACE,0); - break; - - case VI_HORIZONTAL_BEAM_POSITION: - _dbg_assert_(VIDEOINTERFACE,0); - break; - - // RETRACE STUFF ... - case VI_PRERETRACE: - m_VIInterruptRegister[0].Hi = _iValue; - UpdateInterrupts(); - break; - - case VI_POSTRETRACE: - m_VIInterruptRegister[1].Hi = _iValue; - UpdateInterrupts(); - break; - - case VI_DI2: - m_VIInterruptRegister[2].Hi = _iValue; - UpdateInterrupts(); - break; - - case VI_DI3: - m_VIInterruptRegister[3].Hi = _iValue; - UpdateInterrupts(); - break; - - case VI_HSCALEW: - m_VIHorizontalStepping.Hex = _iValue; - break; - - case VI_HSCALER: - m_VIHorizontalScaling.Hex = _iValue; - break; - - case VI_FBWIDTH: - FbWidth = _iValue; - break; - - default: - *(u16*)&m_UVIUnknownRegs[_iAddress & 0xFFF] = _iValue; - break; - } -} - -void Read32(u32& _uReturnValue, const u32 _iAddress) -{ - LOG(VIDEOINTERFACE, "(r32): 0x%08x", _iAddress); - - if ((_iAddress & 0xFFF) < 0x20) - { - _uReturnValue = 0; - return; - } - switch (_iAddress & 0xFFF) - { - case 0x000: - case 0x004: - case 0x008: - case 0x00C: - _uReturnValue = 0; //FL - return; - - default: - //_assert_(0); - _uReturnValue = 0xAFFE; - return; - } -} - -void Write32(const u32 _iValue, const u32 _iAddress) -{ - LOG(VIDEOINTERFACE, "(w32): 0x%08x, 0x%08x",_iValue,_iAddress); - - // Allow 32-bit writes to the VI: although this is officially not - // allowed, the hardware seems to accept it (for example, DesktopMan GC - // Tetris uses it). - Write16(_iValue >> 16, _iAddress); - Write16(_iValue & 0xFFFF, _iAddress + 2); -} - -void UpdateInterrupts() -{ - if ((m_VIInterruptRegister[0].IR_INT & m_VIInterruptRegister[0].IR_MASK) || - (m_VIInterruptRegister[1].IR_INT & m_VIInterruptRegister[1].IR_MASK) || - (m_VIInterruptRegister[2].IR_INT & m_VIInterruptRegister[2].IR_MASK) || - (m_VIInterruptRegister[3].IR_INT & m_VIInterruptRegister[3].IR_MASK)) - { - CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_VI, true); - } - else - { - CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_VI, false); - } -} - -void GenerateVIInterrupt(VIInterruptType _VIInterrupt) -{ - switch(_VIInterrupt) - { - case INT_PRERETRACE: m_VIInterruptRegister[0].IR_INT = 1; break; - case INT_POSTRETRACE: m_VIInterruptRegister[1].IR_INT = 1; break; - case INT_REG_2: m_VIInterruptRegister[2].IR_INT = 1; break; - case INT_REG_3: m_VIInterruptRegister[3].IR_INT = 1; break; - } - - UpdateInterrupts(); - - // debug check - if ((m_VIInterruptRegister[2].IR_MASK == 1) || - (m_VIInterruptRegister[3].IR_MASK == 1)) - { - PanicAlert("m_VIInterruptRegister[2 and 3] activated - Tell F|RES :)"); - } -} - -u8* GetFrameBufferPointer() -{ - return Memory::GetPointer(VideoInterface::m_FrameBufferTop.Hex); -} - -void PreInit(bool _bNTSC) -{ - TicksPerFrame = 0; - LineCount = 0; - LastTime = 0; - - Write16(0x4769, 0xcc002004); - Write16(0x01ad, 0xcc002006); - Write16(0x5140, 0xcc00200a); - Write16(0x02ea, 0xcc002008); - Write16(0x0006, 0xcc002000); - Write16(0x01f6, 0xcc00200e); - Write16(0x0005, 0xcc00200c); - Write16(0x01f7, 0xcc002012); - Write16(0x0004, 0xcc002010); - Write16(0x410c, 0xcc002016); - Write16(0x410c, 0xcc002014); - Write16(0x40ed, 0xcc00201a); - Write16(0x40ed, 0xcc002018); - Write16(0x2828, 0xcc002048); - Write16(0x0001, 0xcc002036); - Write16(0x1001, 0xcc002034); - Write16(0x01ae, 0xcc002032); - Write16(0x1107, 0xcc002030); - Write16(0x0000, 0xcc00206c); - Write16(0x0001, 0xcc00206e); // component cable is connected - - if (_bNTSC) - Write16(0x0001, 0xcc002002); // STATUS REG - else - Write16(0x0101, 0xcc002002); // STATUS REG -} - -void UpdateTiming() -{ - switch (m_VIDisplayControlRegister.FMT) - { - case 0: - case 2: - TicksPerFrame = SystemTimers::GetTicksPerSecond() / 30; - LineCount = m_VIDisplayControlRegister.NIN ? 263 : 525; - LinesPerField = 263; - break; - - case 1: - TicksPerFrame = SystemTimers::GetTicksPerSecond() / 25; - LineCount = m_VIDisplayControlRegister.NIN ? 313 : 625; - LinesPerField = 313; - break; - - default: - PanicAlert("Unknown Video Format - CVideoInterface"); - break; - } -} - -void Update() -{ - while ((CoreTiming::GetTicks() - LastTime) > (TicksPerFrame / LineCount)) - { - LastTime += (TicksPerFrame / LineCount); - - // - VerticalBeamPos++; - if (VerticalBeamPos > LineCount) - { - VerticalBeamPos = 1; - } - - if (VerticalBeamPos == NextXFBRender) - { - - u8* xfbPtr = 0; - int yOffset = 0; - - if (NextXFBRender == 1) - { - NextXFBRender = LinesPerField; - // The & mask is a hack for mario kart - u32 addr = (VideoInterface::m_FrameBufferTop.Hex & 0xFFFFFFF) | 0x80000000; - if (addr >= 0x80000000 && - addr <= (0x81800000-640*480*2)) - xfbPtr = Memory::GetPointer(addr); - } - else - { - NextXFBRender = 1; - u32 addr = (VideoInterface::m_FrameBufferBottom.Hex & 0xFFFFFFF) | 0x80000000; - if (addr >= 0x80000000 && - addr <= (0x81800000-640*480*2)) - xfbPtr = Memory::GetPointer(addr); - yOffset = -1; - } - - if (xfbPtr && PluginVideo::IsLoaded()) - { - int fbWidth = m_VIHorizontalStepping.FieldSteps * 16; - int fbHeight = (m_VIHorizontalStepping.FbSteps / m_VIHorizontalStepping.FieldSteps) * m_VIVerticalTimingRegister.ACV; - PluginVideo::Video_UpdateXFB(xfbPtr, fbWidth, fbHeight, yOffset); - } - } - - // check INT_PRERETRACE - if (m_VIInterruptRegister[0].VCT == VerticalBeamPos) - { - m_VIInterruptRegister[0].IR_INT = 1; - UpdateInterrupts(); - } - - // INT_POSTRETRACE - if (m_VIInterruptRegister[1].VCT == VerticalBeamPos) - { - m_VIInterruptRegister[1].IR_INT = 1; - UpdateInterrupts(); - } - } -} - -} - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "ChunkFile.h" + +#include "../PowerPC/PowerPC.h" + +#include "PeripheralInterface.h" +#include "VideoInterface.h" +#include "Memmap.h" +#include "../Plugins/Plugin_Video.h" +#include "../CoreTiming.h" +#include "../HW/SystemTimers.h" + +namespace VideoInterface +{ +// VI Internal Hardware Addresses +enum +{ + VI_VERTICAL_TIMING = 0x0, + VI_CONTROL_REGISTER = 0x02, + VI_FRAMEBUFFER_TOP_HI = 0x01C, + VI_FRAMEBUFFER_TOP_LO = 0x01E, + VI_FRAMEBUFFER_BOTTOM_HI = 0x024, + VI_FRAMEBUFFER_BOTTOM_LO = 0x026, + VI_VERTICAL_BEAM_POSITION = 0x02C, + VI_HORIZONTAL_BEAM_POSITION = 0x02e, + VI_PRERETRACE = 0x030, + VI_POSTRETRACE = 0x034, + VI_DI2 = 0x038, + VI_DI3 = 0x03C, + VI_INTERLACE = 0x850, + VI_HSCALEW = 0x048, + VI_HSCALER = 0x04A, + VI_FBWIDTH = 0x070, +}; + +union UVIVerticalTimingRegister +{ + u16 Hex; + struct + { + unsigned EQU : 4; + unsigned ACV : 10; + unsigned : 2; + }; + UVIVerticalTimingRegister(u16 _hex) { Hex = _hex;} + UVIVerticalTimingRegister() { Hex = 0;} +}; + +union UVIDisplayControlRegister +{ + u16 Hex; + struct + { + unsigned ENB : 1; + unsigned RST : 1; + unsigned NIN : 1; + unsigned DLR : 1; + unsigned LE0 : 2; + unsigned LE1 : 2; + unsigned FMT : 2; + unsigned : 6; + }; + UVIDisplayControlRegister(u16 _hex) { Hex = _hex;} + UVIDisplayControlRegister() { Hex = 0;} +}; + +// VI Interrupt Register +union UVIInterruptRegister +{ + u32 Hex; + struct + { + u16 Lo; + u16 Hi; + }; + struct + { + unsigned HCT : 11; + unsigned : 5; + unsigned VCT : 11; + unsigned : 1; + unsigned IR_MASK : 1; + unsigned : 2; + unsigned IR_INT : 1; + }; +}; + +union UVIHorizontalStepping +{ + u16 Hex; + struct + { + unsigned FbSteps : 8; + unsigned FieldSteps : 8; + }; +}; + +union UVIHorizontalScaling +{ + u16 Hex; + struct + { + unsigned STP : 9; + unsigned : 3; + unsigned HS_EN : 1; + unsigned : 3; + }; + UVIHorizontalScaling(u16 _hex) { Hex = _hex;} + UVIHorizontalScaling() { Hex = 0;} +}; + +union UVIFrameBufferAddress +{ + u32 Hex; + struct + { + u16 Lo; + u16 Hi; + }; + UVIFrameBufferAddress(u16 _hex) { Hex = _hex;} + UVIFrameBufferAddress() { Hex = 0;} +}; + +// STATE_TO_SAVE +static UVIVerticalTimingRegister m_VIVerticalTimingRegister; + +static UVIDisplayControlRegister m_VIDisplayControlRegister; + +// Framebuffers +static UVIFrameBufferAddress m_FrameBufferTop; // normal framebuffer address +static UVIFrameBufferAddress m_FrameBufferBottom; + +// VI Interrupt Registers +static UVIInterruptRegister m_VIInterruptRegister[4]; + +static UVIHorizontalStepping m_VIHorizontalStepping; +static UVIHorizontalScaling m_VIHorizontalScaling; + +u8 m_UVIUnknownRegs[0x1000]; + +static u16 HorizontalBeamPos = 0; +static u16 VerticalBeamPos = 0; + +static u32 TicksPerFrame = 0; +static u32 LineCount = 0; +static u32 LinesPerField = 0; +static u64 LastTime = 0; +static u32 NextXFBRender = 0; + +// only correct when scaling is enabled? +static u16 FbWidth = 0; + +void DoState(PointerWrap &p) +{ + p.Do(m_VIVerticalTimingRegister); + p.Do(m_VIDisplayControlRegister); + p.Do(m_FrameBufferTop); + p.Do(m_FrameBufferBottom); + p.Do(m_VIInterruptRegister); + p.DoArray(m_UVIUnknownRegs, 0x1000); + p.Do(HorizontalBeamPos); + p.Do(VerticalBeamPos); + p.Do(TicksPerFrame); + p.Do(LineCount); + p.Do(LinesPerField); + p.Do(LastTime); + // p.Do(NextXFBRender); // Activate when changing saves next time. +} + +void Init() +{ + for (int i = 0; i < 4; i++) + m_VIInterruptRegister[i].Hex = 0x00; + + m_VIDisplayControlRegister.Hex = 0x0000; + m_VIDisplayControlRegister.ENB = 0; + m_VIDisplayControlRegister.FMT = 0; + + NextXFBRender = 1; +} + +void Read16(u16& _uReturnValue, const u32 _iAddress) +{ + switch (_iAddress & 0xFFF) + { + case VI_VERTICAL_TIMING: + _uReturnValue = m_VIVerticalTimingRegister.Hex; + return; + + case VI_CONTROL_REGISTER: + LOGV(VIDEOINTERFACE, 3, "VideoInterface(r16): VI_CONTROL_REGISTER 0x%08x", m_VIDisplayControlRegister.Hex); + _uReturnValue = m_VIDisplayControlRegister.Hex; + return; + + case VI_FRAMEBUFFER_TOP_HI: + _uReturnValue = m_FrameBufferTop.Hi; + break; + + case VI_FRAMEBUFFER_TOP_LO: + _uReturnValue = m_FrameBufferTop.Lo; + break; + + case VI_FRAMEBUFFER_BOTTOM_HI: + _uReturnValue = m_FrameBufferBottom.Hi; + break; + + case VI_FRAMEBUFFER_BOTTOM_LO: + _uReturnValue = m_FrameBufferBottom.Lo; + break; + + case VI_VERTICAL_BEAM_POSITION: + _uReturnValue = VerticalBeamPos; + return; + + case VI_HORIZONTAL_BEAM_POSITION: + _uReturnValue = HorizontalBeamPos; + return; + + // RETRACE STUFF ... + case VI_PRERETRACE: + _uReturnValue = m_VIInterruptRegister[0].Hi; + return; + + case VI_POSTRETRACE: + _uReturnValue = m_VIInterruptRegister[1].Hi; + return; + + case VI_DI2: + _uReturnValue = m_VIInterruptRegister[2].Hi; + return; + + case VI_DI3: + _uReturnValue = m_VIInterruptRegister[3].Hi; + return; + + case VI_HSCALEW: + _uReturnValue = m_VIHorizontalStepping.Hex; + break; + + case VI_HSCALER: + _uReturnValue = m_VIHorizontalScaling.Hex; + break; + + case VI_FBWIDTH: + _uReturnValue = FbWidth; + break; + + default: + _uReturnValue = *(u16*)&m_UVIUnknownRegs[_iAddress & 0xFFF]; + return; + } + + _uReturnValue = 0x0; +} + +void Write16(const u16 _iValue, const u32 _iAddress) +{ + LOGV(VIDEOINTERFACE, 3, "(w16): 0x%04x, 0x%08x",_iValue,_iAddress); + + //Somewhere it sets screen width.. we need to communicate this to the gfx plugin... + + switch (_iAddress & 0xFFF) + { + case VI_VERTICAL_TIMING: + m_VIVerticalTimingRegister.Hex = _iValue; + break; + + case VI_CONTROL_REGISTER: + { + UVIDisplayControlRegister tmpConfig(_iValue); + m_VIDisplayControlRegister.ENB = tmpConfig.ENB; + m_VIDisplayControlRegister.NIN = tmpConfig.NIN; + m_VIDisplayControlRegister.DLR = tmpConfig.DLR; + m_VIDisplayControlRegister.LE0 = tmpConfig.LE0; + m_VIDisplayControlRegister.LE1 = tmpConfig.LE1; + m_VIDisplayControlRegister.FMT = tmpConfig.FMT; + + if (tmpConfig.RST) + { + m_VIDisplayControlRegister.RST = 0; + } + + UpdateTiming(); + } + break; + + case VI_FRAMEBUFFER_TOP_HI: + m_FrameBufferTop.Hi = _iValue; + break; + + case VI_FRAMEBUFFER_TOP_LO: + m_FrameBufferTop.Lo = _iValue; + break; + + case VI_FRAMEBUFFER_BOTTOM_HI: + m_FrameBufferBottom.Hi = _iValue; + break; + + case VI_FRAMEBUFFER_BOTTOM_LO: + m_FrameBufferBottom.Lo = _iValue; + break; + + case VI_VERTICAL_BEAM_POSITION: + _dbg_assert_(VIDEOINTERFACE,0); + break; + + case VI_HORIZONTAL_BEAM_POSITION: + _dbg_assert_(VIDEOINTERFACE,0); + break; + + // RETRACE STUFF ... + case VI_PRERETRACE: + m_VIInterruptRegister[0].Hi = _iValue; + UpdateInterrupts(); + break; + + case VI_POSTRETRACE: + m_VIInterruptRegister[1].Hi = _iValue; + UpdateInterrupts(); + break; + + case VI_DI2: + m_VIInterruptRegister[2].Hi = _iValue; + UpdateInterrupts(); + break; + + case VI_DI3: + m_VIInterruptRegister[3].Hi = _iValue; + UpdateInterrupts(); + break; + + case VI_HSCALEW: + m_VIHorizontalStepping.Hex = _iValue; + break; + + case VI_HSCALER: + m_VIHorizontalScaling.Hex = _iValue; + break; + + case VI_FBWIDTH: + FbWidth = _iValue; + break; + + default: + *(u16*)&m_UVIUnknownRegs[_iAddress & 0xFFF] = _iValue; + break; + } +} + +void Read32(u32& _uReturnValue, const u32 _iAddress) +{ + LOG(VIDEOINTERFACE, "(r32): 0x%08x", _iAddress); + + if ((_iAddress & 0xFFF) < 0x20) + { + _uReturnValue = 0; + return; + } + switch (_iAddress & 0xFFF) + { + case 0x000: + case 0x004: + case 0x008: + case 0x00C: + _uReturnValue = 0; //FL + return; + + default: + //_assert_(0); + _uReturnValue = 0xAFFE; + return; + } +} + +void Write32(const u32 _iValue, const u32 _iAddress) +{ + LOG(VIDEOINTERFACE, "(w32): 0x%08x, 0x%08x",_iValue,_iAddress); + + // Allow 32-bit writes to the VI: although this is officially not + // allowed, the hardware seems to accept it (for example, DesktopMan GC + // Tetris uses it). + Write16(_iValue >> 16, _iAddress); + Write16(_iValue & 0xFFFF, _iAddress + 2); +} + +void UpdateInterrupts() +{ + if ((m_VIInterruptRegister[0].IR_INT & m_VIInterruptRegister[0].IR_MASK) || + (m_VIInterruptRegister[1].IR_INT & m_VIInterruptRegister[1].IR_MASK) || + (m_VIInterruptRegister[2].IR_INT & m_VIInterruptRegister[2].IR_MASK) || + (m_VIInterruptRegister[3].IR_INT & m_VIInterruptRegister[3].IR_MASK)) + { + CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_VI, true); + } + else + { + CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_VI, false); + } +} + +void GenerateVIInterrupt(VIInterruptType _VIInterrupt) +{ + switch(_VIInterrupt) + { + case INT_PRERETRACE: m_VIInterruptRegister[0].IR_INT = 1; break; + case INT_POSTRETRACE: m_VIInterruptRegister[1].IR_INT = 1; break; + case INT_REG_2: m_VIInterruptRegister[2].IR_INT = 1; break; + case INT_REG_3: m_VIInterruptRegister[3].IR_INT = 1; break; + } + + UpdateInterrupts(); + + // debug check + if ((m_VIInterruptRegister[2].IR_MASK == 1) || + (m_VIInterruptRegister[3].IR_MASK == 1)) + { + PanicAlert("m_VIInterruptRegister[2 and 3] activated - Tell F|RES :)"); + } +} + +u8* GetFrameBufferPointer() +{ + return Memory::GetPointer(VideoInterface::m_FrameBufferTop.Hex); +} + +void PreInit(bool _bNTSC) +{ + TicksPerFrame = 0; + LineCount = 0; + LastTime = 0; + + Write16(0x4769, 0xcc002004); + Write16(0x01ad, 0xcc002006); + Write16(0x5140, 0xcc00200a); + Write16(0x02ea, 0xcc002008); + Write16(0x0006, 0xcc002000); + Write16(0x01f6, 0xcc00200e); + Write16(0x0005, 0xcc00200c); + Write16(0x01f7, 0xcc002012); + Write16(0x0004, 0xcc002010); + Write16(0x410c, 0xcc002016); + Write16(0x410c, 0xcc002014); + Write16(0x40ed, 0xcc00201a); + Write16(0x40ed, 0xcc002018); + Write16(0x2828, 0xcc002048); + Write16(0x0001, 0xcc002036); + Write16(0x1001, 0xcc002034); + Write16(0x01ae, 0xcc002032); + Write16(0x1107, 0xcc002030); + Write16(0x0000, 0xcc00206c); + Write16(0x0001, 0xcc00206e); // component cable is connected + + if (_bNTSC) + Write16(0x0001, 0xcc002002); // STATUS REG + else + Write16(0x0101, 0xcc002002); // STATUS REG +} + +void UpdateTiming() +{ + switch (m_VIDisplayControlRegister.FMT) + { + case 0: + case 2: + TicksPerFrame = SystemTimers::GetTicksPerSecond() / 30; + LineCount = m_VIDisplayControlRegister.NIN ? 263 : 525; + LinesPerField = 263; + break; + + case 1: + TicksPerFrame = SystemTimers::GetTicksPerSecond() / 25; + LineCount = m_VIDisplayControlRegister.NIN ? 313 : 625; + LinesPerField = 313; + break; + + default: + PanicAlert("Unknown Video Format - CVideoInterface"); + break; + } +} + +void Update() +{ + while ((CoreTiming::GetTicks() - LastTime) > (TicksPerFrame / LineCount)) + { + LastTime += (TicksPerFrame / LineCount); + + // + VerticalBeamPos++; + if (VerticalBeamPos > LineCount) + { + VerticalBeamPos = 1; + } + + if (VerticalBeamPos == NextXFBRender) + { + + u8* xfbPtr = 0; + int yOffset = 0; + + if (NextXFBRender == 1) + { + NextXFBRender = LinesPerField; + // The & mask is a hack for mario kart + u32 addr = (VideoInterface::m_FrameBufferTop.Hex & 0xFFFFFFF) | 0x80000000; + if (addr >= 0x80000000 && + addr <= (0x81800000-640*480*2)) + xfbPtr = Memory::GetPointer(addr); + } + else + { + NextXFBRender = 1; + u32 addr = (VideoInterface::m_FrameBufferBottom.Hex & 0xFFFFFFF) | 0x80000000; + if (addr >= 0x80000000 && + addr <= (0x81800000-640*480*2)) + xfbPtr = Memory::GetPointer(addr); + yOffset = -1; + } + + if (xfbPtr && PluginVideo::IsLoaded()) + { + int fbWidth = m_VIHorizontalStepping.FieldSteps * 16; + int fbHeight = (m_VIHorizontalStepping.FbSteps / m_VIHorizontalStepping.FieldSteps) * m_VIVerticalTimingRegister.ACV; + PluginVideo::Video_UpdateXFB(xfbPtr, fbWidth, fbHeight, yOffset); + } + } + + // check INT_PRERETRACE + if (m_VIInterruptRegister[0].VCT == VerticalBeamPos) + { + m_VIInterruptRegister[0].IR_INT = 1; + UpdateInterrupts(); + } + + // INT_POSTRETRACE + if (m_VIInterruptRegister[1].VCT == VerticalBeamPos) + { + m_VIInterruptRegister[1].IR_INT = 1; + UpdateInterrupts(); + } + } +} + +} + diff --git a/Source/Core/Core/Src/HW/WII_IOB.cpp b/Source/Core/Core/Src/HW/WII_IOB.cpp index 9b9bfe5a3c..2cfb29b247 100644 --- a/Source/Core/Core/Src/HW/WII_IOB.cpp +++ b/Source/Core/Core/Src/HW/WII_IOB.cpp @@ -1,134 +1,134 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "ChunkFile.h" -#include "WII_IOB.h" - -namespace WII_IOBridge -{ - -void HWCALL Read8(u8& _rReturnValue, const u32 _Address) -{ - _dbg_assert_(WII_IOB, 0); -} - -void HWCALL Read16(u16& _rReturnValue, const u32 _Address) -{ - _dbg_assert_(WII_IOB, 0); -} - -void HWCALL Read32(u32& _rReturnValue, const u32 _Address) -{ - switch(_Address & 0xFFFF) - { - case 0xc0: // __VISendI2CData - _rReturnValue = 0; - LOGV(WII_IOB, 2, "IOP: Read32 from 0xc0 = 0x%08x (__VISendI2CData)", _rReturnValue); - break; - - case 0xc4: // __VISendI2CData - _rReturnValue = 0; - LOGV(WII_IOB, 2, "IOP: Read32 from 0xc4 = 0x%08x (__VISendI2CData)", _rReturnValue); - break; - - case 0xc8: // __VISendI2CData - _rReturnValue = 0; - LOGV(WII_IOB, 2, "IOP: Read32 from 0xc8 = 0x%08x (__VISendI2CData)", _rReturnValue); - break; - - case 0x180: // __AIClockInit - _rReturnValue = 0; - LOG(WII_IOB, "IOP: Read32 from 0x180 = 0x%08x (__AIClockInit)", _rReturnValue); - return; - - case 0x1CC: // __AIClockInit - _rReturnValue = 0; - LOG(WII_IOB, "IOP: Read32 from 0x1CC = 0x%08x (__AIClockInit)", _rReturnValue); - return; - - case 0x1D0: // __AIClockInit - _rReturnValue = 0; - LOG(WII_IOB, "IOP: Read32 from 0x1D0 = 0x%08x (__AIClockInit)", _rReturnValue); - return; - - default: - _dbg_assert_msg_(WII_IOB, 0, "IOP: Read32 from 0x%08x", _Address); - break; - } -} - -void HWCALL Read64(u64& _rReturnValue, const u32 _Address) -{ - _dbg_assert_(WII_IOB, 0); -} - -void HWCALL Write8(const u8 _Value, const u32 _Address) -{ - _dbg_assert_(WII_IOB, 0); -} - -void HWCALL Write16(const u16 _Value, const u32 _Address) -{ - _dbg_assert_(WII_IOB, 0); -} - -void HWCALL Write32(const u32 _Value, const u32 _Address) -{ - switch(_Address & 0xFFFF) - { - case 0xc0: // __VISendI2CData - LOGV(WII_IOB, 2, "IOP: Write32 to 0xc0 = 0x%08x (__VISendI2CData)", _Value); - break; - - case 0xc4: // __VISendI2CData - LOGV(WII_IOB, 2, "IOP: Write32 to 0xc4 = 0x%08x (__VISendI2CData)", _Value); - break; - - case 0xc8: // __VISendI2CData - LOGV(WII_IOB, 2, "IOP: Write32 to 0xc8 = 0x%08x (__VISendI2CData)", _Value); - break; - - case 0x180: // __AIClockInit - LOG(WII_IOB, "IOP: Write32 to 0x180 = 0x%08x (__AIClockInit)", _Value); - return; - - case 0x1CC: // __AIClockInit - LOG(WII_IOB, "IOP: Write32 to 0x1D0 = 0x%08x (__AIClockInit)", _Value); - return; - - case 0x1D0: // __AIClockInit - LOG(WII_IOB, "IOP: Write32 to 0x1D0 = 0x%08x (__AIClockInit)", _Value); - return; - - default: - _dbg_assert_msg_(WII_IOB, 0, "IOP: Write32 to 0x%08x", _Address); - break; - } -} - -void HWCALL Write64(const u64 _Value, const u32 _Address) -{ - //switch(_Address) - //{ - //default: - _dbg_assert_msg_(WII_IOB, 0, "IOP: Write32 to 0x%08x", _Address); - //break; - //} -} - -} // end of namespace AudioInterfac +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "ChunkFile.h" +#include "WII_IOB.h" + +namespace WII_IOBridge +{ + +void HWCALL Read8(u8& _rReturnValue, const u32 _Address) +{ + _dbg_assert_(WII_IOB, 0); +} + +void HWCALL Read16(u16& _rReturnValue, const u32 _Address) +{ + _dbg_assert_(WII_IOB, 0); +} + +void HWCALL Read32(u32& _rReturnValue, const u32 _Address) +{ + switch(_Address & 0xFFFF) + { + case 0xc0: // __VISendI2CData + _rReturnValue = 0; + LOGV(WII_IOB, 2, "IOP: Read32 from 0xc0 = 0x%08x (__VISendI2CData)", _rReturnValue); + break; + + case 0xc4: // __VISendI2CData + _rReturnValue = 0; + LOGV(WII_IOB, 2, "IOP: Read32 from 0xc4 = 0x%08x (__VISendI2CData)", _rReturnValue); + break; + + case 0xc8: // __VISendI2CData + _rReturnValue = 0; + LOGV(WII_IOB, 2, "IOP: Read32 from 0xc8 = 0x%08x (__VISendI2CData)", _rReturnValue); + break; + + case 0x180: // __AIClockInit + _rReturnValue = 0; + LOG(WII_IOB, "IOP: Read32 from 0x180 = 0x%08x (__AIClockInit)", _rReturnValue); + return; + + case 0x1CC: // __AIClockInit + _rReturnValue = 0; + LOG(WII_IOB, "IOP: Read32 from 0x1CC = 0x%08x (__AIClockInit)", _rReturnValue); + return; + + case 0x1D0: // __AIClockInit + _rReturnValue = 0; + LOG(WII_IOB, "IOP: Read32 from 0x1D0 = 0x%08x (__AIClockInit)", _rReturnValue); + return; + + default: + _dbg_assert_msg_(WII_IOB, 0, "IOP: Read32 from 0x%08x", _Address); + break; + } +} + +void HWCALL Read64(u64& _rReturnValue, const u32 _Address) +{ + _dbg_assert_(WII_IOB, 0); +} + +void HWCALL Write8(const u8 _Value, const u32 _Address) +{ + _dbg_assert_(WII_IOB, 0); +} + +void HWCALL Write16(const u16 _Value, const u32 _Address) +{ + _dbg_assert_(WII_IOB, 0); +} + +void HWCALL Write32(const u32 _Value, const u32 _Address) +{ + switch(_Address & 0xFFFF) + { + case 0xc0: // __VISendI2CData + LOGV(WII_IOB, 2, "IOP: Write32 to 0xc0 = 0x%08x (__VISendI2CData)", _Value); + break; + + case 0xc4: // __VISendI2CData + LOGV(WII_IOB, 2, "IOP: Write32 to 0xc4 = 0x%08x (__VISendI2CData)", _Value); + break; + + case 0xc8: // __VISendI2CData + LOGV(WII_IOB, 2, "IOP: Write32 to 0xc8 = 0x%08x (__VISendI2CData)", _Value); + break; + + case 0x180: // __AIClockInit + LOG(WII_IOB, "IOP: Write32 to 0x180 = 0x%08x (__AIClockInit)", _Value); + return; + + case 0x1CC: // __AIClockInit + LOG(WII_IOB, "IOP: Write32 to 0x1D0 = 0x%08x (__AIClockInit)", _Value); + return; + + case 0x1D0: // __AIClockInit + LOG(WII_IOB, "IOP: Write32 to 0x1D0 = 0x%08x (__AIClockInit)", _Value); + return; + + default: + _dbg_assert_msg_(WII_IOB, 0, "IOP: Write32 to 0x%08x", _Address); + break; + } +} + +void HWCALL Write64(const u64 _Value, const u32 _Address) +{ + //switch(_Address) + //{ + //default: + _dbg_assert_msg_(WII_IOB, 0, "IOP: Write32 to 0x%08x", _Address); + //break; + //} +} + +} // end of namespace AudioInterfac diff --git a/Source/Core/Core/Src/HW/WII_IPC.cpp b/Source/Core/Core/Src/HW/WII_IPC.cpp index efbc978201..70216dbbde 100644 --- a/Source/Core/Core/Src/HW/WII_IPC.cpp +++ b/Source/Core/Core/Src/HW/WII_IPC.cpp @@ -1,262 +1,262 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include - -#include "Common.h" -#include "ChunkFile.h" - -#include "CPU.h" -#include "Memmap.h" -#include "PeripheralInterface.h" - -#include "../IPC_HLE/WII_IPC_HLE.h" -#include "WII_IPC.h" - -namespace WII_IPCInterface -{ - -enum -{ - IPC_COMMAND_REGISTER = 0x00, - IPC_CONTROL_REGISTER = 0x04, - IPC_REPLY_REGISTER = 0x08, - IPC_STATUS_REGISTER = 0x30, - IPC_CONFIG_REGISTER = 0x34, - IPC_SENSOR_BAR_POWER_REGISTER = 0xC0 -}; - -union UIPC_Control -{ - u32 Hex; - struct - { - unsigned ExecuteCmd : 1; - unsigned AckReady : 1; - unsigned ReplyReady : 1; - unsigned Relaunch : 1; - unsigned unk5 : 1; - unsigned unk6 : 1; - unsigned pad : 26; - }; - UIPC_Control(u32 _Hex = 0) {Hex = _Hex;} -}; - -union UIPC_Status -{ - u32 Hex; - struct - { - unsigned : 30; - unsigned INTERRUPT : 1; // 0x40000000 - unsigned : 1; - }; - UIPC_Status(u32 _Hex = 0) {Hex = _Hex;} -}; - -union UIPC_Config -{ - u32 Hex; - struct - { - unsigned : 30; - unsigned INT_MASK : 1; // 0x40000000 - unsigned : 1; - }; - UIPC_Config(u32 _Hex = 0) - { - Hex = _Hex; - INT_MASK = 1; - } -}; - -// STATE_TO_SAVE -UIPC_Status g_IPC_Status; -UIPC_Config g_IPC_Config; -UIPC_Control g_IPC_Control; - -u32 g_Address = 0; -u32 g_Reply = 0; -u32 g_SensorBarPower = 0; - -void DoState(PointerWrap &p) -{ - p.Do(g_IPC_Status); - p.Do(g_IPC_Config); - p.Do(g_IPC_Control); - p.Do(g_Address); - p.Do(g_Reply); - p.Do(g_SensorBarPower); -} - -void UpdateInterrupts(); - -// Init -void Init() -{ - g_Address = 0; - g_Reply = 0; - g_SensorBarPower = 0; - - g_IPC_Status = UIPC_Status(); - g_IPC_Config = UIPC_Config(); - g_IPC_Control = UIPC_Control(); -} - -void Shutdown() -{ -} - -void HWCALL Read32(u32& _rReturnValue, const u32 _Address) -{ - switch(_Address & 0xFFFF) - { - case IPC_CONTROL_REGISTER: - - _rReturnValue = g_IPC_Control.Hex; - - LOGV(WII_IPC, 2, "IOP: Read32 from IPC_CONTROL_REGISTER(0x04) = 0x%08x", _rReturnValue); - // CCPU::Break(); - - // if ((REASON_REG & 0x14) == 0x14) CALL IPCReplayHanlder - // if ((REASON_REG & 0x22) != 0x22) Jumps to the end - - break; - - case IPC_REPLY_REGISTER: // looks a little bit like a callback function - _rReturnValue = g_Reply; - LOGV(WII_IPC, 2, "IOP: Write32 to IPC_REPLAY_REGISTER(0x08) = 0x%08x ", _rReturnValue); - break; - - case IPC_SENSOR_BAR_POWER_REGISTER: - _rReturnValue = g_SensorBarPower; - break; - - default: - _dbg_assert_msg_(WII_IPC, 0, "IOP: Read32 from 0x%08x", _Address); - break; - } -} - -void HWCALL Write32(const u32 _Value, const u32 _Address) -{ - switch(_Address & 0xFFFF) - { - // write only ?? - case IPC_COMMAND_REGISTER: // __ios_Ipc2 ... a value from __responses is loaded - { - g_Address = _Value; - LOGV(WII_IPC, 1, "IOP: Write32 to IPC_ADDRESS_REGISTER(0x00) = 0x%08x", g_Address); - } - break; - - case IPC_CONTROL_REGISTER: - { - LOGV(WII_IPC, 1, "IOP: Write32 to IPC_CONTROL_REGISTER(0x04) = 0x%08x (old: 0x%08x)", _Value, g_IPC_Control.Hex); - - UIPC_Control TempControl(_Value); - _dbg_assert_msg_(WII_IPC, TempControl.pad == 0, "IOP: Write to UIPC_Control.pad", _Address); - - - if (TempControl.AckReady) { g_IPC_Control.AckReady = 0; } - if (TempControl.ReplyReady) { g_IPC_Control.ReplyReady = 0; } - if (TempControl.Relaunch) { g_IPC_Control.Relaunch = 0; } - - g_IPC_Control.unk5 = TempControl.unk5; - g_IPC_Control.unk6 = TempControl.unk6; - g_IPC_Control.pad = TempControl.pad; - - if (TempControl.ExecuteCmd) - { - WII_IPC_HLE_Interface::AckCommand(g_Address); - } - } - break; - - case IPC_STATUS_REGISTER: // ACR REGISTER IT IS CALLED IN DEBUG - { - UIPC_Status NewStatus(_Value); - if (NewStatus.INTERRUPT) g_IPC_Status.INTERRUPT = 0; // clear interrupt - - LOGV(WII_IPC, 1, "IOP: Write32 to IPC_STATUS_REGISTER(0x30) = 0x%08x", _Value); - } - break; - - case IPC_CONFIG_REGISTER: // __OSInterruptInit (0x40000000) - { - LOG(WII_IPC, "IOP: Write32 to IPC_CONFIG_REGISTER(0x33) = 0x%08x", _Value); - g_IPC_Config.Hex = _Value; - } - break; - - case IPC_SENSOR_BAR_POWER_REGISTER: - g_SensorBarPower = _Value; - break; - - default: - { - _dbg_assert_msg_(WII_IPC, 0, "IOP: Write32 to 0x%08x", _Address); - } - break; - } - - // update the interrupts - UpdateInterrupts(); -} - -void UpdateInterrupts() -{ - if ((g_IPC_Control.AckReady == 1) || - (g_IPC_Control.ReplyReady == 1)) - { - g_IPC_Status.INTERRUPT = 1; - } - - // check if we have to generate an interrupt - if (g_IPC_Config.INT_MASK & g_IPC_Status.INTERRUPT) - { - CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_WII_IPC, true); - } - else - { - CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_WII_IPC, false); - } -} - -bool IsReady() -{ - return ((g_IPC_Control.ReplyReady == 0) && (g_IPC_Control.AckReady == 0) && (g_IPC_Status.INTERRUPT == 0)); -} - -void GenerateAck(u32 _AnswerAddress) -{ - g_Reply = _AnswerAddress; - g_IPC_Control.AckReady = 1; - - UpdateInterrupts(); -} - -void GenerateReply(u32 _AnswerAddress) -{ - g_Reply = _AnswerAddress; - g_IPC_Control.ReplyReady = 1; - - UpdateInterrupts(); -} - -} // end of namespace IPC - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "Common.h" +#include "ChunkFile.h" + +#include "CPU.h" +#include "Memmap.h" +#include "PeripheralInterface.h" + +#include "../IPC_HLE/WII_IPC_HLE.h" +#include "WII_IPC.h" + +namespace WII_IPCInterface +{ + +enum +{ + IPC_COMMAND_REGISTER = 0x00, + IPC_CONTROL_REGISTER = 0x04, + IPC_REPLY_REGISTER = 0x08, + IPC_STATUS_REGISTER = 0x30, + IPC_CONFIG_REGISTER = 0x34, + IPC_SENSOR_BAR_POWER_REGISTER = 0xC0 +}; + +union UIPC_Control +{ + u32 Hex; + struct + { + unsigned ExecuteCmd : 1; + unsigned AckReady : 1; + unsigned ReplyReady : 1; + unsigned Relaunch : 1; + unsigned unk5 : 1; + unsigned unk6 : 1; + unsigned pad : 26; + }; + UIPC_Control(u32 _Hex = 0) {Hex = _Hex;} +}; + +union UIPC_Status +{ + u32 Hex; + struct + { + unsigned : 30; + unsigned INTERRUPT : 1; // 0x40000000 + unsigned : 1; + }; + UIPC_Status(u32 _Hex = 0) {Hex = _Hex;} +}; + +union UIPC_Config +{ + u32 Hex; + struct + { + unsigned : 30; + unsigned INT_MASK : 1; // 0x40000000 + unsigned : 1; + }; + UIPC_Config(u32 _Hex = 0) + { + Hex = _Hex; + INT_MASK = 1; + } +}; + +// STATE_TO_SAVE +UIPC_Status g_IPC_Status; +UIPC_Config g_IPC_Config; +UIPC_Control g_IPC_Control; + +u32 g_Address = 0; +u32 g_Reply = 0; +u32 g_SensorBarPower = 0; + +void DoState(PointerWrap &p) +{ + p.Do(g_IPC_Status); + p.Do(g_IPC_Config); + p.Do(g_IPC_Control); + p.Do(g_Address); + p.Do(g_Reply); + p.Do(g_SensorBarPower); +} + +void UpdateInterrupts(); + +// Init +void Init() +{ + g_Address = 0; + g_Reply = 0; + g_SensorBarPower = 0; + + g_IPC_Status = UIPC_Status(); + g_IPC_Config = UIPC_Config(); + g_IPC_Control = UIPC_Control(); +} + +void Shutdown() +{ +} + +void HWCALL Read32(u32& _rReturnValue, const u32 _Address) +{ + switch(_Address & 0xFFFF) + { + case IPC_CONTROL_REGISTER: + + _rReturnValue = g_IPC_Control.Hex; + + LOGV(WII_IPC, 2, "IOP: Read32 from IPC_CONTROL_REGISTER(0x04) = 0x%08x", _rReturnValue); + // CCPU::Break(); + + // if ((REASON_REG & 0x14) == 0x14) CALL IPCReplayHanlder + // if ((REASON_REG & 0x22) != 0x22) Jumps to the end + + break; + + case IPC_REPLY_REGISTER: // looks a little bit like a callback function + _rReturnValue = g_Reply; + LOGV(WII_IPC, 2, "IOP: Write32 to IPC_REPLAY_REGISTER(0x08) = 0x%08x ", _rReturnValue); + break; + + case IPC_SENSOR_BAR_POWER_REGISTER: + _rReturnValue = g_SensorBarPower; + break; + + default: + _dbg_assert_msg_(WII_IPC, 0, "IOP: Read32 from 0x%08x", _Address); + break; + } +} + +void HWCALL Write32(const u32 _Value, const u32 _Address) +{ + switch(_Address & 0xFFFF) + { + // write only ?? + case IPC_COMMAND_REGISTER: // __ios_Ipc2 ... a value from __responses is loaded + { + g_Address = _Value; + LOGV(WII_IPC, 1, "IOP: Write32 to IPC_ADDRESS_REGISTER(0x00) = 0x%08x", g_Address); + } + break; + + case IPC_CONTROL_REGISTER: + { + LOGV(WII_IPC, 1, "IOP: Write32 to IPC_CONTROL_REGISTER(0x04) = 0x%08x (old: 0x%08x)", _Value, g_IPC_Control.Hex); + + UIPC_Control TempControl(_Value); + _dbg_assert_msg_(WII_IPC, TempControl.pad == 0, "IOP: Write to UIPC_Control.pad", _Address); + + + if (TempControl.AckReady) { g_IPC_Control.AckReady = 0; } + if (TempControl.ReplyReady) { g_IPC_Control.ReplyReady = 0; } + if (TempControl.Relaunch) { g_IPC_Control.Relaunch = 0; } + + g_IPC_Control.unk5 = TempControl.unk5; + g_IPC_Control.unk6 = TempControl.unk6; + g_IPC_Control.pad = TempControl.pad; + + if (TempControl.ExecuteCmd) + { + WII_IPC_HLE_Interface::AckCommand(g_Address); + } + } + break; + + case IPC_STATUS_REGISTER: // ACR REGISTER IT IS CALLED IN DEBUG + { + UIPC_Status NewStatus(_Value); + if (NewStatus.INTERRUPT) g_IPC_Status.INTERRUPT = 0; // clear interrupt + + LOGV(WII_IPC, 1, "IOP: Write32 to IPC_STATUS_REGISTER(0x30) = 0x%08x", _Value); + } + break; + + case IPC_CONFIG_REGISTER: // __OSInterruptInit (0x40000000) + { + LOG(WII_IPC, "IOP: Write32 to IPC_CONFIG_REGISTER(0x33) = 0x%08x", _Value); + g_IPC_Config.Hex = _Value; + } + break; + + case IPC_SENSOR_BAR_POWER_REGISTER: + g_SensorBarPower = _Value; + break; + + default: + { + _dbg_assert_msg_(WII_IPC, 0, "IOP: Write32 to 0x%08x", _Address); + } + break; + } + + // update the interrupts + UpdateInterrupts(); +} + +void UpdateInterrupts() +{ + if ((g_IPC_Control.AckReady == 1) || + (g_IPC_Control.ReplyReady == 1)) + { + g_IPC_Status.INTERRUPT = 1; + } + + // check if we have to generate an interrupt + if (g_IPC_Config.INT_MASK & g_IPC_Status.INTERRUPT) + { + CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_WII_IPC, true); + } + else + { + CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_WII_IPC, false); + } +} + +bool IsReady() +{ + return ((g_IPC_Control.ReplyReady == 0) && (g_IPC_Control.AckReady == 0) && (g_IPC_Status.INTERRUPT == 0)); +} + +void GenerateAck(u32 _AnswerAddress) +{ + g_Reply = _AnswerAddress; + g_IPC_Control.AckReady = 1; + + UpdateInterrupts(); +} + +void GenerateReply(u32 _AnswerAddress) +{ + g_Reply = _AnswerAddress; + g_IPC_Control.ReplyReady = 1; + + UpdateInterrupts(); +} + +} // end of namespace IPC + diff --git a/Source/Core/Core/Src/Host.cpp b/Source/Core/Core/Src/Host.cpp index f2dd14f536..b9b91d7fac 100644 --- a/Source/Core/Core/Src/Host.cpp +++ b/Source/Core/Core/Src/Host.cpp @@ -1,2 +1,2 @@ -#include "Host.h" - +#include "Host.h" + diff --git a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE.cpp b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE.cpp index 0d5ff68a4e..74963a925f 100644 --- a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE.cpp +++ b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE.cpp @@ -1,480 +1,480 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - - - -// ======================================================= -// File description -// ------------- -/* This is the main Wii IPC file that handles all incoming IPC calls and directs them - to the right function. - - IPC basics: - - Return values for file handles: All IPC calls will generate a return value to 0x04, - in case of success they are - Open: DeviceID - Close: 0 - Read: Bytes read - Write: Bytes written - Seek: Seek position - Ioctl: 0 (in addition to that there may be messages to the out buffers) - Ioctlv: 0 (in addition to that there may be messages to the out buffers) - They will also generate a true or false return for UpdateInterrupts() in WII_IPC.cpp. */ -// ============= - - - - -#include -#include -#include - -#include "Common.h" -#include "WII_IPC_HLE.h" -#include "WII_IPC_HLE_Device.h" -#include "WII_IPC_HLE_Device_Error.h" -#include "WII_IPC_HLE_Device_DI.h" -#include "WII_IPC_HLE_Device_FileIO.h" -#include "WII_IPC_HLE_Device_stm.h" -#include "WII_IPC_HLE_Device_fs.h" -#include "WII_IPC_HLE_Device_net.h" -#include "WII_IPC_HLE_Device_es.h" -#include "WII_IPC_HLE_Device_usb.h" -#include "WII_IPC_HLE_Device_sdio_slot0.h" - -#include "FileUtil.h" // For Copy -#include "../Core.h" -#include "../HW/CPU.h" -#include "../HW/Memmap.h" -#include "../HW/WII_IPC.h" -#include "../Debugger/Debugger_SymbolMap.h" - -namespace WII_IPC_HLE_Interface -{ - - -typedef std::map TDeviceMap; -TDeviceMap g_DeviceMap; - -// STATE_TO_SAVE -u32 g_LastDeviceID = 0x13370000; -std::list g_Ack; -std::queue > g_ReplyQueue; -void ExecuteCommand(u32 _Address); - -// General IPC functions -void Init() -{ - _dbg_assert_msg_(WII_IPC, g_DeviceMap.empty(), "DeviceMap isnt empty on init"); -} - -void Shutdown() -{ - TDeviceMap::const_iterator itr = g_DeviceMap.begin(); - while(itr != g_DeviceMap.end()) - { - delete itr->second; - ++itr; - } - g_DeviceMap.clear(); -} - -u32 GetDeviceIDByName(const std::string& _rDeviceName) -{ - TDeviceMap::const_iterator itr = g_DeviceMap.begin(); - while(itr != g_DeviceMap.end()) - { - if (itr->second->GetDeviceName() == _rDeviceName) - return itr->first; - - ++itr; - } - - return 0; -} - -IWII_IPC_HLE_Device* AccessDeviceByID(u32 _ID) -{ - if (g_DeviceMap.find(_ID) != g_DeviceMap.end()) - return g_DeviceMap[_ID]; - - _dbg_assert_msg_(WII_IPC, 0, "IOP tries to access an unknown device 0x%x", _ID); - - return NULL; -} - -void DeleteDeviceByID(u32 ID) -{ - IWII_IPC_HLE_Device* pDevice = AccessDeviceByID(ID); - if (pDevice != NULL) - { - delete pDevice; - g_DeviceMap.erase(ID); - } -} - -// This is called from COMMAND_OPEN_DEVICE. Here we either create a new device -// or open a new file handle. -IWII_IPC_HLE_Device* CreateDevice(u32 _DeviceID, const std::string& _rDeviceName) -{ - // scan device name and create the right one - IWII_IPC_HLE_Device* pDevice = NULL; - if (_rDeviceName.find("/dev/") != std::string::npos) - { - if (_rDeviceName.c_str() == std::string("/dev/stm/immediate")) - { - pDevice = new CWII_IPC_HLE_Device_stm_immediate(_DeviceID, _rDeviceName); - } - else if (_rDeviceName.c_str() == std::string("/dev/stm/eventhook")) - { - pDevice = new CWII_IPC_HLE_Device_stm_eventhook(_DeviceID, _rDeviceName); - } - else if (_rDeviceName.c_str() == std::string("/dev/di")) - { - pDevice = new CWII_IPC_HLE_Device_di(_DeviceID, _rDeviceName); - } - else if (_rDeviceName.c_str() == std::string("/dev/fs")) - { - pDevice = new CWII_IPC_HLE_Device_fs(_DeviceID, _rDeviceName); - } - else if (_rDeviceName.c_str() == std::string("/dev/net/kd/request")) - { - pDevice = new CWII_IPC_HLE_Device_net_kd_request(_DeviceID, _rDeviceName); - } - else if (_rDeviceName.c_str() == std::string("/dev/net/kd/time")) - { - pDevice = new CWII_IPC_HLE_Device_net_kd_time(_DeviceID, _rDeviceName); - } - else if (_rDeviceName.c_str() == std::string("/dev/net/ncd/manage")) - { - pDevice = new CWII_IPC_HLE_Device_net_ncd_manage(_DeviceID, _rDeviceName); - } - else if (_rDeviceName.c_str() == std::string("/dev/es")) - { - pDevice = new CWII_IPC_HLE_Device_es(_DeviceID, _rDeviceName); - } - else if (_rDeviceName.find("/dev/usb/oh1/57e/305") != std::string::npos) - { - pDevice = new CWII_IPC_HLE_Device_usb_oh1_57e_305(_DeviceID, _rDeviceName); - } - else if (_rDeviceName.find("/dev/sdio/slot0") != std::string::npos) - { - pDevice = new CWII_IPC_HLE_Device_sdio_slot0(_DeviceID, _rDeviceName); - } - else - { - PanicAlert("Unknown device: %s", _rDeviceName.c_str()); - pDevice = new CWII_IPC_HLE_Device_Error(u32(-1), _rDeviceName); - } - } - else - { - LOGV(WII_IPC_FILEIO, 0, "IOP: Create Device %s", _rDeviceName.c_str()); - pDevice = new CWII_IPC_HLE_Device_FileIO(_DeviceID, _rDeviceName); - } - - return pDevice; -} - -// =================================================== -/* This generates an acknowledgment to IPC calls. This function is called from - IPC_CONTROL_REGISTER requests in WII_IPC.cpp. The acknowledgment _Address will - start with 0x033e...., it will be for the _CommandAddress 0x133e...., from - debugging I also noticed that the Ioctl arguments are stored temporarily in - 0x933e.... with the same .... as in the _CommandAddress. */ -// ---------------- -bool AckCommand(u32 _Address) -{ - Debugger::PrintCallstack(LogTypes::WII_IPC_HLE); - LOGV(WII_IPC_HLE, 1, "AckCommand: 0%08x", _Address); - - std::list::iterator itr = g_Ack.begin(); - while (itr != g_Ack.end()) - { - if (*itr == _Address) - { - PanicAlert("execute a command two times"); - return false; - } - - itr++; - } - - g_Ack.push_back(_Address); - - return true; -} - -// Let the game read the setting.txt file -void CopySettingsFile(std::string DeviceName) -{ - std::string Source = FULL_WII_SYS_DIR; - if(Core::GetStartupParameter().bNTSC) - Source += "setting-usa.txt"; - else - Source += "setting-eur.txt"; - - std::string Target = FULL_WII_ROOT_DIR + DeviceName; - - // Check if the target dir exists, otherwise create it - std::string TargetDir = Target.substr(0, Target.find_last_of(DIR_SEP)); - if(!File::IsDirectory(TargetDir.c_str())) File::CreateDirectoryStructure(Target.c_str()); - - if (File::Copy(Source.c_str(), Target.c_str())) - { - LOG(WII_IPC_FILEIO, "FS: Copied %s to %s", Source.c_str(), Target.c_str()); - } - else - { - LOG(WII_IPC_FILEIO, "Could not copy %s to %s", Source.c_str(), Target.c_str()); - PanicAlert("Could not copy %s to %s", Source.c_str(), Target.c_str()); - } -} - -void ExecuteCommand(u32 _Address) -{ - bool GenerateReply = false; - u32 erased = 0; - - ECommandType Command = static_cast(Memory::Read_U32(_Address)); - switch (Command) - { - case COMMAND_OPEN_DEVICE: - { - // Create a new HLE device. The Mode and DeviceName is given to us but we - // generate a DeviceID to be used for access to this device until it is Closed. - std::string DeviceName; - Memory::GetString(DeviceName, Memory::Read_U32(_Address + 0xC)); - - // The game may try to read setting.txt here, in that case copy it so it can read it - if(DeviceName.find("setting.txt") != std::string::npos) CopySettingsFile(DeviceName); - - u32 Mode = Memory::Read_U32(_Address + 0x10); - u32 DeviceID = GetDeviceIDByName(DeviceName); - - // check if a device with this name has been created already - if (DeviceID == 0) - { - // create the new device - // alternatively we could pre create all devices and put them in a directory tree structure - // then this would just return a pointer to the wanted device. - u32 CurrentDeviceID = g_LastDeviceID; - IWII_IPC_HLE_Device* pDevice = CreateDevice(CurrentDeviceID, DeviceName); - g_DeviceMap[CurrentDeviceID] = pDevice; - g_LastDeviceID++; - - GenerateReply = pDevice->Open(_Address, Mode); - if(pDevice->GetDeviceName().find("/dev/") == std::string::npos - || pDevice->GetDeviceName().c_str() == std::string("/dev/fs")) - { - LOG(WII_IPC_FILEIO, "IOP: Open (Device=%s, DeviceID=%08x, Mode=%i, GenerateReply=%i)", - pDevice->GetDeviceName().c_str(), CurrentDeviceID, Mode, (int)GenerateReply); - } - else - { - LOG(WII_IPC_HLE, "IOP: Open (Device=%s, DeviceID=%08x, Mode=%i)", - pDevice->GetDeviceName().c_str(), CurrentDeviceID, Mode); - } - } - else - { - // The device has already been opened and was not closed, reuse the same DeviceID. - - IWII_IPC_HLE_Device* pDevice = AccessDeviceByID(DeviceID); - // If we return -6 here after a Open > Failed > CREATE_FILE > ReOpen call - // sequence Mario Galaxy and Mario Kart Wii will not start writing to the file, - // it will just (seemingly) wait for one or two seconds and then give an error - // message. So I'm trying to return the DeviceID instead to make it write to the file. - // (Which was most likely the reason it created the file in the first place.) */ - - // F|RES: prolly the re-open is just a mode change - - LOG(WII_IPC_FILEIO, "IOP: ReOpen (Device=%s, DeviceID=%08x, Mode=%i)", - pDevice->GetDeviceName().c_str(), DeviceID, Mode); - - if(DeviceName.find("/dev/") == std::string::npos) - { - - u32 newMode = Memory::Read_U32(_Address + 0x10); - - // We may not have a file handle at this point, in Mario Kart I got a - // Open > Failed > ... other stuff > ReOpen call sequence, in that case - // we have no file and no file handle, so we call Open again to basically - // get a -106 error so that the game call CreateFile and then ReOpen again. - if(pDevice->ReturnFileHandle()) - Memory::Write_U32(DeviceID, _Address + 4); - else - GenerateReply = pDevice->Open(_Address, newMode); - } - else - { - // We have already opened this device, return -6 - Memory::Write_U32(u32(-6), _Address + 4); - } - GenerateReply = true; - } - } - break; - - case COMMAND_CLOSE_DEVICE: - { - u32 DeviceID = Memory::Read_U32(_Address + 8); - - IWII_IPC_HLE_Device* pDevice = AccessDeviceByID(DeviceID); - if (pDevice != NULL) - { - pDevice->Close(_Address); - - // Delete the device when CLOSE is called, this does not effect - // GenerateReply() for any other purpose than the logging because - // it's a true / false only function // - erased = DeviceID; - GenerateReply = true; - } - } - break; - - case COMMAND_READ: - { - u32 DeviceID = Memory::Read_U32(_Address+8); - IWII_IPC_HLE_Device* pDevice = AccessDeviceByID(DeviceID); - if (pDevice != NULL) - GenerateReply = pDevice->Read(_Address); - } - break; - - case COMMAND_WRITE: - { - u32 DeviceID = Memory::Read_U32(_Address+8); - IWII_IPC_HLE_Device* pDevice = AccessDeviceByID(DeviceID); - if (pDevice != NULL) - GenerateReply = pDevice->Write(_Address); - } - break; - - case COMMAND_SEEK: - { - u32 DeviceID = Memory::Read_U32(_Address+8); - IWII_IPC_HLE_Device* pDevice = AccessDeviceByID(DeviceID); - if (pDevice != NULL) - GenerateReply = pDevice->Seek(_Address); - } - break; - - case COMMAND_IOCTL: - { - u32 DeviceID = Memory::Read_U32(_Address+8); - IWII_IPC_HLE_Device* pDevice = AccessDeviceByID(DeviceID); - if (pDevice != NULL) - GenerateReply = pDevice->IOCtl(_Address); - } - break; - - case COMMAND_IOCTLV: - { - u32 DeviceID = Memory::Read_U32(_Address+8); - IWII_IPC_HLE_Device* pDevice = AccessDeviceByID(DeviceID); - if (pDevice) - GenerateReply = pDevice->IOCtlV(_Address); - } - break; - - default: - _dbg_assert_msg_(WII_IPC_HLE, 0, "Unknown Command %i (0x%08x)", Command, _Address); - CCPU::Break(); - break; - } - - // It seems that the original hardware overwrites the command after it has been - // executed. We write 8 which is not any valid command. - Memory::Write_U32(8, _Address); - - // Generate a reply to the IPC command - if (GenerateReply) - { - // Get device id - u32 DeviceID = Memory::Read_U32(_Address + 8); - IWII_IPC_HLE_Device* pDevice = NULL; - - // Get the device from the device map - if (DeviceID != 0) { - if (g_DeviceMap.find(DeviceID) != g_DeviceMap.end()) - pDevice = g_DeviceMap[DeviceID]; - - if (pDevice != NULL) { - // Write reply, this will later be executed in Update() - g_ReplyQueue.push(std::pair(_Address, pDevice->GetDeviceName())); - } else { - LOG(WII_IPC_HLE, "IOP: Reply to unknown device ID (DeviceID=%i)", DeviceID); - g_ReplyQueue.push(std::pair(_Address, "unknown")); - } - - if (erased > 0 && erased == DeviceID) - DeleteDeviceByID(DeviceID); - - } else { - // 0 is ok, as it's used for devices that weren't created yet - g_ReplyQueue.push(std::pair(_Address, "unknown")); - } - } -} - - -// =================================================== -/* This is called continuously from SystemTimers.cpp and WII_IPCInterface::IsReady() - is controlled from WII_IPC.cpp. */ -// ---------------- -void Update() -{ - if (WII_IPCInterface::IsReady()) - { - // check if an executed must be updated - TDeviceMap::const_iterator itr = g_DeviceMap.begin(); - while(itr != g_DeviceMap.end()) - { - u32 CommandAddr = itr->second->Update(); - if (CommandAddr != 0) - { - g_ReplyQueue.push(std::pair(CommandAddr, itr->second->GetDeviceName())); - } - ++itr; - } - - // Check if we have to execute an acknowledge command... - if (!g_ReplyQueue.empty()) - { - WII_IPCInterface::GenerateReply(g_ReplyQueue.front().first); - g_ReplyQueue.pop(); - return; - } - - // ...no we don't, we can now execute the IPC command - if (g_ReplyQueue.empty() && !g_Ack.empty()) - { - u32 _Address = g_Ack.front(); - g_Ack.pop_front(); - ExecuteCommand(_Address); - LOGV(WII_IPC_HLE, 1, "-- Generate Ack (0x%08x)", _Address); - - // Go back to WII_IPC.cpp and generate an acknowledgement - WII_IPCInterface::GenerateAck(_Address); - } - } -} - -} // end of namespace IPC +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + + + +// ======================================================= +// File description +// ------------- +/* This is the main Wii IPC file that handles all incoming IPC calls and directs them + to the right function. + + IPC basics: + + Return values for file handles: All IPC calls will generate a return value to 0x04, + in case of success they are + Open: DeviceID + Close: 0 + Read: Bytes read + Write: Bytes written + Seek: Seek position + Ioctl: 0 (in addition to that there may be messages to the out buffers) + Ioctlv: 0 (in addition to that there may be messages to the out buffers) + They will also generate a true or false return for UpdateInterrupts() in WII_IPC.cpp. */ +// ============= + + + + +#include +#include +#include + +#include "Common.h" +#include "WII_IPC_HLE.h" +#include "WII_IPC_HLE_Device.h" +#include "WII_IPC_HLE_Device_Error.h" +#include "WII_IPC_HLE_Device_DI.h" +#include "WII_IPC_HLE_Device_FileIO.h" +#include "WII_IPC_HLE_Device_stm.h" +#include "WII_IPC_HLE_Device_fs.h" +#include "WII_IPC_HLE_Device_net.h" +#include "WII_IPC_HLE_Device_es.h" +#include "WII_IPC_HLE_Device_usb.h" +#include "WII_IPC_HLE_Device_sdio_slot0.h" + +#include "FileUtil.h" // For Copy +#include "../Core.h" +#include "../HW/CPU.h" +#include "../HW/Memmap.h" +#include "../HW/WII_IPC.h" +#include "../Debugger/Debugger_SymbolMap.h" + +namespace WII_IPC_HLE_Interface +{ + + +typedef std::map TDeviceMap; +TDeviceMap g_DeviceMap; + +// STATE_TO_SAVE +u32 g_LastDeviceID = 0x13370000; +std::list g_Ack; +std::queue > g_ReplyQueue; +void ExecuteCommand(u32 _Address); + +// General IPC functions +void Init() +{ + _dbg_assert_msg_(WII_IPC, g_DeviceMap.empty(), "DeviceMap isnt empty on init"); +} + +void Shutdown() +{ + TDeviceMap::const_iterator itr = g_DeviceMap.begin(); + while(itr != g_DeviceMap.end()) + { + delete itr->second; + ++itr; + } + g_DeviceMap.clear(); +} + +u32 GetDeviceIDByName(const std::string& _rDeviceName) +{ + TDeviceMap::const_iterator itr = g_DeviceMap.begin(); + while(itr != g_DeviceMap.end()) + { + if (itr->second->GetDeviceName() == _rDeviceName) + return itr->first; + + ++itr; + } + + return 0; +} + +IWII_IPC_HLE_Device* AccessDeviceByID(u32 _ID) +{ + if (g_DeviceMap.find(_ID) != g_DeviceMap.end()) + return g_DeviceMap[_ID]; + + _dbg_assert_msg_(WII_IPC, 0, "IOP tries to access an unknown device 0x%x", _ID); + + return NULL; +} + +void DeleteDeviceByID(u32 ID) +{ + IWII_IPC_HLE_Device* pDevice = AccessDeviceByID(ID); + if (pDevice != NULL) + { + delete pDevice; + g_DeviceMap.erase(ID); + } +} + +// This is called from COMMAND_OPEN_DEVICE. Here we either create a new device +// or open a new file handle. +IWII_IPC_HLE_Device* CreateDevice(u32 _DeviceID, const std::string& _rDeviceName) +{ + // scan device name and create the right one + IWII_IPC_HLE_Device* pDevice = NULL; + if (_rDeviceName.find("/dev/") != std::string::npos) + { + if (_rDeviceName.c_str() == std::string("/dev/stm/immediate")) + { + pDevice = new CWII_IPC_HLE_Device_stm_immediate(_DeviceID, _rDeviceName); + } + else if (_rDeviceName.c_str() == std::string("/dev/stm/eventhook")) + { + pDevice = new CWII_IPC_HLE_Device_stm_eventhook(_DeviceID, _rDeviceName); + } + else if (_rDeviceName.c_str() == std::string("/dev/di")) + { + pDevice = new CWII_IPC_HLE_Device_di(_DeviceID, _rDeviceName); + } + else if (_rDeviceName.c_str() == std::string("/dev/fs")) + { + pDevice = new CWII_IPC_HLE_Device_fs(_DeviceID, _rDeviceName); + } + else if (_rDeviceName.c_str() == std::string("/dev/net/kd/request")) + { + pDevice = new CWII_IPC_HLE_Device_net_kd_request(_DeviceID, _rDeviceName); + } + else if (_rDeviceName.c_str() == std::string("/dev/net/kd/time")) + { + pDevice = new CWII_IPC_HLE_Device_net_kd_time(_DeviceID, _rDeviceName); + } + else if (_rDeviceName.c_str() == std::string("/dev/net/ncd/manage")) + { + pDevice = new CWII_IPC_HLE_Device_net_ncd_manage(_DeviceID, _rDeviceName); + } + else if (_rDeviceName.c_str() == std::string("/dev/es")) + { + pDevice = new CWII_IPC_HLE_Device_es(_DeviceID, _rDeviceName); + } + else if (_rDeviceName.find("/dev/usb/oh1/57e/305") != std::string::npos) + { + pDevice = new CWII_IPC_HLE_Device_usb_oh1_57e_305(_DeviceID, _rDeviceName); + } + else if (_rDeviceName.find("/dev/sdio/slot0") != std::string::npos) + { + pDevice = new CWII_IPC_HLE_Device_sdio_slot0(_DeviceID, _rDeviceName); + } + else + { + PanicAlert("Unknown device: %s", _rDeviceName.c_str()); + pDevice = new CWII_IPC_HLE_Device_Error(u32(-1), _rDeviceName); + } + } + else + { + LOGV(WII_IPC_FILEIO, 0, "IOP: Create Device %s", _rDeviceName.c_str()); + pDevice = new CWII_IPC_HLE_Device_FileIO(_DeviceID, _rDeviceName); + } + + return pDevice; +} + +// =================================================== +/* This generates an acknowledgment to IPC calls. This function is called from + IPC_CONTROL_REGISTER requests in WII_IPC.cpp. The acknowledgment _Address will + start with 0x033e...., it will be for the _CommandAddress 0x133e...., from + debugging I also noticed that the Ioctl arguments are stored temporarily in + 0x933e.... with the same .... as in the _CommandAddress. */ +// ---------------- +bool AckCommand(u32 _Address) +{ + Debugger::PrintCallstack(LogTypes::WII_IPC_HLE); + LOGV(WII_IPC_HLE, 1, "AckCommand: 0%08x", _Address); + + std::list::iterator itr = g_Ack.begin(); + while (itr != g_Ack.end()) + { + if (*itr == _Address) + { + PanicAlert("execute a command two times"); + return false; + } + + itr++; + } + + g_Ack.push_back(_Address); + + return true; +} + +// Let the game read the setting.txt file +void CopySettingsFile(std::string DeviceName) +{ + std::string Source = FULL_WII_SYS_DIR; + if(Core::GetStartupParameter().bNTSC) + Source += "setting-usa.txt"; + else + Source += "setting-eur.txt"; + + std::string Target = FULL_WII_ROOT_DIR + DeviceName; + + // Check if the target dir exists, otherwise create it + std::string TargetDir = Target.substr(0, Target.find_last_of(DIR_SEP)); + if(!File::IsDirectory(TargetDir.c_str())) File::CreateDirectoryStructure(Target.c_str()); + + if (File::Copy(Source.c_str(), Target.c_str())) + { + LOG(WII_IPC_FILEIO, "FS: Copied %s to %s", Source.c_str(), Target.c_str()); + } + else + { + LOG(WII_IPC_FILEIO, "Could not copy %s to %s", Source.c_str(), Target.c_str()); + PanicAlert("Could not copy %s to %s", Source.c_str(), Target.c_str()); + } +} + +void ExecuteCommand(u32 _Address) +{ + bool GenerateReply = false; + u32 erased = 0; + + ECommandType Command = static_cast(Memory::Read_U32(_Address)); + switch (Command) + { + case COMMAND_OPEN_DEVICE: + { + // Create a new HLE device. The Mode and DeviceName is given to us but we + // generate a DeviceID to be used for access to this device until it is Closed. + std::string DeviceName; + Memory::GetString(DeviceName, Memory::Read_U32(_Address + 0xC)); + + // The game may try to read setting.txt here, in that case copy it so it can read it + if(DeviceName.find("setting.txt") != std::string::npos) CopySettingsFile(DeviceName); + + u32 Mode = Memory::Read_U32(_Address + 0x10); + u32 DeviceID = GetDeviceIDByName(DeviceName); + + // check if a device with this name has been created already + if (DeviceID == 0) + { + // create the new device + // alternatively we could pre create all devices and put them in a directory tree structure + // then this would just return a pointer to the wanted device. + u32 CurrentDeviceID = g_LastDeviceID; + IWII_IPC_HLE_Device* pDevice = CreateDevice(CurrentDeviceID, DeviceName); + g_DeviceMap[CurrentDeviceID] = pDevice; + g_LastDeviceID++; + + GenerateReply = pDevice->Open(_Address, Mode); + if(pDevice->GetDeviceName().find("/dev/") == std::string::npos + || pDevice->GetDeviceName().c_str() == std::string("/dev/fs")) + { + LOG(WII_IPC_FILEIO, "IOP: Open (Device=%s, DeviceID=%08x, Mode=%i, GenerateReply=%i)", + pDevice->GetDeviceName().c_str(), CurrentDeviceID, Mode, (int)GenerateReply); + } + else + { + LOG(WII_IPC_HLE, "IOP: Open (Device=%s, DeviceID=%08x, Mode=%i)", + pDevice->GetDeviceName().c_str(), CurrentDeviceID, Mode); + } + } + else + { + // The device has already been opened and was not closed, reuse the same DeviceID. + + IWII_IPC_HLE_Device* pDevice = AccessDeviceByID(DeviceID); + // If we return -6 here after a Open > Failed > CREATE_FILE > ReOpen call + // sequence Mario Galaxy and Mario Kart Wii will not start writing to the file, + // it will just (seemingly) wait for one or two seconds and then give an error + // message. So I'm trying to return the DeviceID instead to make it write to the file. + // (Which was most likely the reason it created the file in the first place.) */ + + // F|RES: prolly the re-open is just a mode change + + LOG(WII_IPC_FILEIO, "IOP: ReOpen (Device=%s, DeviceID=%08x, Mode=%i)", + pDevice->GetDeviceName().c_str(), DeviceID, Mode); + + if(DeviceName.find("/dev/") == std::string::npos) + { + + u32 newMode = Memory::Read_U32(_Address + 0x10); + + // We may not have a file handle at this point, in Mario Kart I got a + // Open > Failed > ... other stuff > ReOpen call sequence, in that case + // we have no file and no file handle, so we call Open again to basically + // get a -106 error so that the game call CreateFile and then ReOpen again. + if(pDevice->ReturnFileHandle()) + Memory::Write_U32(DeviceID, _Address + 4); + else + GenerateReply = pDevice->Open(_Address, newMode); + } + else + { + // We have already opened this device, return -6 + Memory::Write_U32(u32(-6), _Address + 4); + } + GenerateReply = true; + } + } + break; + + case COMMAND_CLOSE_DEVICE: + { + u32 DeviceID = Memory::Read_U32(_Address + 8); + + IWII_IPC_HLE_Device* pDevice = AccessDeviceByID(DeviceID); + if (pDevice != NULL) + { + pDevice->Close(_Address); + + // Delete the device when CLOSE is called, this does not effect + // GenerateReply() for any other purpose than the logging because + // it's a true / false only function // + erased = DeviceID; + GenerateReply = true; + } + } + break; + + case COMMAND_READ: + { + u32 DeviceID = Memory::Read_U32(_Address+8); + IWII_IPC_HLE_Device* pDevice = AccessDeviceByID(DeviceID); + if (pDevice != NULL) + GenerateReply = pDevice->Read(_Address); + } + break; + + case COMMAND_WRITE: + { + u32 DeviceID = Memory::Read_U32(_Address+8); + IWII_IPC_HLE_Device* pDevice = AccessDeviceByID(DeviceID); + if (pDevice != NULL) + GenerateReply = pDevice->Write(_Address); + } + break; + + case COMMAND_SEEK: + { + u32 DeviceID = Memory::Read_U32(_Address+8); + IWII_IPC_HLE_Device* pDevice = AccessDeviceByID(DeviceID); + if (pDevice != NULL) + GenerateReply = pDevice->Seek(_Address); + } + break; + + case COMMAND_IOCTL: + { + u32 DeviceID = Memory::Read_U32(_Address+8); + IWII_IPC_HLE_Device* pDevice = AccessDeviceByID(DeviceID); + if (pDevice != NULL) + GenerateReply = pDevice->IOCtl(_Address); + } + break; + + case COMMAND_IOCTLV: + { + u32 DeviceID = Memory::Read_U32(_Address+8); + IWII_IPC_HLE_Device* pDevice = AccessDeviceByID(DeviceID); + if (pDevice) + GenerateReply = pDevice->IOCtlV(_Address); + } + break; + + default: + _dbg_assert_msg_(WII_IPC_HLE, 0, "Unknown Command %i (0x%08x)", Command, _Address); + CCPU::Break(); + break; + } + + // It seems that the original hardware overwrites the command after it has been + // executed. We write 8 which is not any valid command. + Memory::Write_U32(8, _Address); + + // Generate a reply to the IPC command + if (GenerateReply) + { + // Get device id + u32 DeviceID = Memory::Read_U32(_Address + 8); + IWII_IPC_HLE_Device* pDevice = NULL; + + // Get the device from the device map + if (DeviceID != 0) { + if (g_DeviceMap.find(DeviceID) != g_DeviceMap.end()) + pDevice = g_DeviceMap[DeviceID]; + + if (pDevice != NULL) { + // Write reply, this will later be executed in Update() + g_ReplyQueue.push(std::pair(_Address, pDevice->GetDeviceName())); + } else { + LOG(WII_IPC_HLE, "IOP: Reply to unknown device ID (DeviceID=%i)", DeviceID); + g_ReplyQueue.push(std::pair(_Address, "unknown")); + } + + if (erased > 0 && erased == DeviceID) + DeleteDeviceByID(DeviceID); + + } else { + // 0 is ok, as it's used for devices that weren't created yet + g_ReplyQueue.push(std::pair(_Address, "unknown")); + } + } +} + + +// =================================================== +/* This is called continuously from SystemTimers.cpp and WII_IPCInterface::IsReady() + is controlled from WII_IPC.cpp. */ +// ---------------- +void Update() +{ + if (WII_IPCInterface::IsReady()) + { + // check if an executed must be updated + TDeviceMap::const_iterator itr = g_DeviceMap.begin(); + while(itr != g_DeviceMap.end()) + { + u32 CommandAddr = itr->second->Update(); + if (CommandAddr != 0) + { + g_ReplyQueue.push(std::pair(CommandAddr, itr->second->GetDeviceName())); + } + ++itr; + } + + // Check if we have to execute an acknowledge command... + if (!g_ReplyQueue.empty()) + { + WII_IPCInterface::GenerateReply(g_ReplyQueue.front().first); + g_ReplyQueue.pop(); + return; + } + + // ...no we don't, we can now execute the IPC command + if (g_ReplyQueue.empty() && !g_Ack.empty()) + { + u32 _Address = g_Ack.front(); + g_Ack.pop_front(); + ExecuteCommand(_Address); + LOGV(WII_IPC_HLE, 1, "-- Generate Ack (0x%08x)", _Address); + + // Go back to WII_IPC.cpp and generate an acknowledgement + WII_IPCInterface::GenerateAck(_Address); + } + } +} + +} // end of namespace IPC diff --git a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_DI.cpp b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_DI.cpp index 21b559de91..4b42de8b43 100644 --- a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_DI.cpp +++ b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_DI.cpp @@ -1,272 +1,272 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" - -#include "WII_IPC_HLE_Device_DI.h" - -#include "../HW/CPU.h" -#include "../HW/Memmap.h" -#include "../Core.h" - -#include "../VolumeHandler.h" - -#include "VolumeCreator.h" -#include "Filesystem.h" - - -CWII_IPC_HLE_Device_di::CWII_IPC_HLE_Device_di(u32 _DeviceID, const std::string& _rDeviceName ) - : IWII_IPC_HLE_Device(_DeviceID, _rDeviceName) - , m_pVolume(NULL) - , m_pFileSystem(NULL) -{ - m_pVolume = VolumeHandler::GetVolume(); - if (m_pVolume) - m_pFileSystem = DiscIO::CreateFileSystem(m_pVolume); -} - -CWII_IPC_HLE_Device_di::~CWII_IPC_HLE_Device_di() -{ - delete m_pFileSystem; - delete m_pVolume; -} - -bool CWII_IPC_HLE_Device_di::Open(u32 _CommandAddress, u32 _Mode) -{ - Memory::Write_U32(GetDeviceID(), _CommandAddress+4); - return true; -} - -bool CWII_IPC_HLE_Device_di::IOCtl(u32 _CommandAddress) -{ - LOGV(WII_IPC_DVD, 1, "*******************************"); - LOGV(WII_IPC_DVD, 1, "CWII_IPC_DVD_Device_di::IOCtl"); - LOGV(WII_IPC_DVD, 1, "*******************************"); - - - u32 BufferIn = Memory::Read_U32(_CommandAddress + 0x10); - u32 BufferInSize = Memory::Read_U32(_CommandAddress + 0x14); - u32 BufferOut = Memory::Read_U32(_CommandAddress + 0x18); - u32 BufferOutSize = Memory::Read_U32(_CommandAddress + 0x1C); - u32 Command = Memory::Read_U32(BufferIn) >> 24; - - LOG(WII_IPC_DVD, "%s - Command(0x%08x) BufferIn(0x%08x, 0x%x) BufferOut(0x%08x, 0x%x)", GetDeviceName().c_str(), Command, BufferIn, BufferInSize, BufferOut, BufferOutSize); - - u32 ReturnValue = ExecuteCommand(BufferIn, BufferInSize, BufferOut, BufferOutSize); - Memory::Write_U32(ReturnValue, _CommandAddress + 0x4); - - return true; -} - -bool CWII_IPC_HLE_Device_di::IOCtlV(u32 _CommandAddress) -{ - PanicAlert("CWII_IPC_HLE_Device_di::IOCtlV() unknown"); - - DumpCommands(_CommandAddress); - - return true; -} - -u32 CWII_IPC_HLE_Device_di::ExecuteCommand(u32 _BufferIn, u32 _BufferInSize, u32 _BufferOut, u32 _BufferOutSize) -{ - u32 Command = Memory::Read_U32(_BufferIn) >> 24; - - /* Set out buffer to zeroes as a safety precaution to avoid answering - nonsense values */ - Memory::Memset(_BufferOut, 0, _BufferOutSize); - - switch (Command) - { - // DVDLowInquiry - case 0x12: - { - u8* buffer = Memory::GetPointer(_BufferOut); - - /* In theory this gives a game the option to use different read / write behaviors - depending on which hardware revision that is used, if there have been more than - one. But it's probably not used at all by any game, in any case it would be strange - if it refused a certain value here if it's possible that that would make it - incompatible with new DVD drives for example. From an actual Wii the code was - 0x0000, 0x0002, 0x20060526, I tried it in Balls of Fury that gives a DVD error - message after the DVDLowInquiry, but that did't change anything, it must be - something else. */ - buffer[0] = 0x01; // rev - buffer[1] = 0x02; - buffer[2] = 0x03; // dev code - buffer[3] = 0x04; - buffer[4] = 0x20; // firmware date - buffer[5] = 0x08; - buffer[6] = 0x08; - buffer[7] = 0x29; - - LOG(WII_IPC_DVD, "%s executes DVDLowInquiry (Buffer 0x%08x, 0x%x)", GetDeviceName().c_str(), _BufferOut, _BufferOutSize); - - return 0x1; - } - break; - - // DVDLowReadDiskID - case 0x70: - { - // TODO - verify that this is correct - VolumeHandler::ReadToPtr(Memory::GetPointer(_BufferOut), 0, _BufferOutSize); - - LOG(WII_IPC_DVD, "%s executes DVDLowReadDiskID (Buffer 0x%08x, 0x%x)", GetDeviceName().c_str(), _BufferOut, _BufferOutSize); - - return 0x1; - } - break; - - // DVDLowRead - case 0x71: - { - u32 Size = Memory::Read_U32(_BufferIn + 0x04); - u64 DVDAddress = (u64)Memory::Read_U32(_BufferIn + 0x08) << 2; - - const char* pFilename = m_pFileSystem->GetFileName(DVDAddress); - if (pFilename != NULL) - { - LOG(WII_IPC_DVD, " DVDLowRead: %s (0x%x) - (DVDAddr: 0x%x, Size: 0x%x)", pFilename, m_pFileSystem->GetFileSize(pFilename), DVDAddress, Size); - } - else - { - LOG(WII_IPC_DVD, " DVDLowRead: file unkw - (DVDAddr: 0x%x, Size: 0x%x)", GetDeviceName().c_str(), DVDAddress, Size); - } - - if (Size > _BufferOutSize) - { - PanicAlert("Detected attempt to read more data from the DVD than fit inside the out buffer. Clamp."); - Size = _BufferOutSize; - } - - if (VolumeHandler::ReadToPtr(Memory::GetPointer(_BufferOut), DVDAddress, Size) != true) - { - PanicAlert("Cant read from DVD_Plugin - DVD-Interface: Fatal Error"); - } - - return 0x1; - } - break; - - // DVDLowWaitForCoverClose - case 0x79: - { - Memory::Memset(_BufferOut, 0, _BufferOutSize); - LOG(WII_IPC_DVD, "%s executes DVDLowWaitForCoverClose (Buffer 0x%08x, 0x%x)", GetDeviceName().c_str(), _BufferOut, _BufferOutSize); - return 4; - } - break; - - // DVDLowGetCoverReg - Called by "Legend of Spyro" and MP3 - case 0x7a: - { - LOG(WII_IPC_DVD, "%s executes DVDLowGetCoverReg (Buffer 0x%08x, 0x%x)", GetDeviceName().c_str(), _BufferOut, _BufferOutSize); - - // Write zeroes to the out buffer just in case there is some nonsense data there - Memory::Memset(_BufferOut, 0, _BufferOutSize); - - // -------------------------------------------------------------------- - /* Hack for Legend of Spyro. Switching the 4th byte between 0 and 1 gets - through this check. The out buffer address remains the same all the - time so we don't have to bother making a global function. - - TODO: Make this compatible with MP3 */ - // ------------------------- - /* - static u8 coverByte = 0; - - u8* buffer = Memory::GetPointer(_BufferOut); - buffer[3] = coverByte; - - if(coverByte) - coverByte = 0; - else - coverByte = 0x01; - - return 1; - */ - } - break; - - // DVDLowClearCoverInterrupt - case 0x86: - { - Memory::Memset(_BufferOut, 0, _BufferOutSize); - LOGV(WII_IPC_DVD, 1, "%s executes DVDLowClearCoverInterrupt (Buffer 0x%08x, 0x%x)", GetDeviceName().c_str(), _BufferOut, _BufferOutSize); - } - break; - - // DVDLowGetCoverStatus - case 0x88: - { - Memory::Memset(_BufferOut, 0, _BufferOutSize); - LOG(WII_IPC_DVD, "%s executes DVDLowGetCoverStatus (Buffer 0x%08x, 0x%x)", GetDeviceName().c_str(), _BufferOut, _BufferOutSize); - return 1; - } - break; - - // DVDLowReset - case 0x8a: - { - Memory::Memset(_BufferOut, 0, _BufferOutSize); - LOG(WII_IPC_DVD, "%s executes DVDLowReset (Buffer 0x%08x, 0x%x)", GetDeviceName().c_str(), _BufferOut, _BufferOutSize); - return 1; - } - break; - - // DVDLowOpenPartition - case 0x8b: - PanicAlert("DVDLowOpenPartition", Command); - break; - - - // DVDLowUnencryptedRead - case 0x8d: - PanicAlert("DVDLowUnencryptedRead"); - break; - - // DVDLowSeek - case 0xab: - // PanicAlert("DVDLowSeek"); - break; - - // DVDLowStopMotor - case 0xe3: - { - Memory::Memset(_BufferOut, 0, _BufferOutSize); - u32 eject = Memory::Read_U32(_BufferIn + 0x04); - - LOG(WII_IPC_DVD, "%s executes DVDLowStopMotor (Buffer 0x%08x, 0x%x)", GetDeviceName().c_str(), _BufferOut, _BufferOutSize); - - if(eject) - { - LOG(WII_IPC_DVD, "Eject disc", GetDeviceName().c_str(), _BufferOut, _BufferOutSize); - // TODO: eject the disc - } - } - break; - - default: - LOG(WII_IPC_DVD, "%s executes unknown cmd 0x%08x (Buffer 0x%08x, 0x%x)", GetDeviceName().c_str(), Command, _BufferOut, _BufferOutSize); - - PanicAlert("%s executes unknown cmd 0x%08x", GetDeviceName().c_str(), Command); - break; - } - - // i dunno but prolly 1 is okay all the time :) - return 1; -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" + +#include "WII_IPC_HLE_Device_DI.h" + +#include "../HW/CPU.h" +#include "../HW/Memmap.h" +#include "../Core.h" + +#include "../VolumeHandler.h" + +#include "VolumeCreator.h" +#include "Filesystem.h" + + +CWII_IPC_HLE_Device_di::CWII_IPC_HLE_Device_di(u32 _DeviceID, const std::string& _rDeviceName ) + : IWII_IPC_HLE_Device(_DeviceID, _rDeviceName) + , m_pVolume(NULL) + , m_pFileSystem(NULL) +{ + m_pVolume = VolumeHandler::GetVolume(); + if (m_pVolume) + m_pFileSystem = DiscIO::CreateFileSystem(m_pVolume); +} + +CWII_IPC_HLE_Device_di::~CWII_IPC_HLE_Device_di() +{ + delete m_pFileSystem; + delete m_pVolume; +} + +bool CWII_IPC_HLE_Device_di::Open(u32 _CommandAddress, u32 _Mode) +{ + Memory::Write_U32(GetDeviceID(), _CommandAddress+4); + return true; +} + +bool CWII_IPC_HLE_Device_di::IOCtl(u32 _CommandAddress) +{ + LOGV(WII_IPC_DVD, 1, "*******************************"); + LOGV(WII_IPC_DVD, 1, "CWII_IPC_DVD_Device_di::IOCtl"); + LOGV(WII_IPC_DVD, 1, "*******************************"); + + + u32 BufferIn = Memory::Read_U32(_CommandAddress + 0x10); + u32 BufferInSize = Memory::Read_U32(_CommandAddress + 0x14); + u32 BufferOut = Memory::Read_U32(_CommandAddress + 0x18); + u32 BufferOutSize = Memory::Read_U32(_CommandAddress + 0x1C); + u32 Command = Memory::Read_U32(BufferIn) >> 24; + + LOG(WII_IPC_DVD, "%s - Command(0x%08x) BufferIn(0x%08x, 0x%x) BufferOut(0x%08x, 0x%x)", GetDeviceName().c_str(), Command, BufferIn, BufferInSize, BufferOut, BufferOutSize); + + u32 ReturnValue = ExecuteCommand(BufferIn, BufferInSize, BufferOut, BufferOutSize); + Memory::Write_U32(ReturnValue, _CommandAddress + 0x4); + + return true; +} + +bool CWII_IPC_HLE_Device_di::IOCtlV(u32 _CommandAddress) +{ + PanicAlert("CWII_IPC_HLE_Device_di::IOCtlV() unknown"); + + DumpCommands(_CommandAddress); + + return true; +} + +u32 CWII_IPC_HLE_Device_di::ExecuteCommand(u32 _BufferIn, u32 _BufferInSize, u32 _BufferOut, u32 _BufferOutSize) +{ + u32 Command = Memory::Read_U32(_BufferIn) >> 24; + + /* Set out buffer to zeroes as a safety precaution to avoid answering + nonsense values */ + Memory::Memset(_BufferOut, 0, _BufferOutSize); + + switch (Command) + { + // DVDLowInquiry + case 0x12: + { + u8* buffer = Memory::GetPointer(_BufferOut); + + /* In theory this gives a game the option to use different read / write behaviors + depending on which hardware revision that is used, if there have been more than + one. But it's probably not used at all by any game, in any case it would be strange + if it refused a certain value here if it's possible that that would make it + incompatible with new DVD drives for example. From an actual Wii the code was + 0x0000, 0x0002, 0x20060526, I tried it in Balls of Fury that gives a DVD error + message after the DVDLowInquiry, but that did't change anything, it must be + something else. */ + buffer[0] = 0x01; // rev + buffer[1] = 0x02; + buffer[2] = 0x03; // dev code + buffer[3] = 0x04; + buffer[4] = 0x20; // firmware date + buffer[5] = 0x08; + buffer[6] = 0x08; + buffer[7] = 0x29; + + LOG(WII_IPC_DVD, "%s executes DVDLowInquiry (Buffer 0x%08x, 0x%x)", GetDeviceName().c_str(), _BufferOut, _BufferOutSize); + + return 0x1; + } + break; + + // DVDLowReadDiskID + case 0x70: + { + // TODO - verify that this is correct + VolumeHandler::ReadToPtr(Memory::GetPointer(_BufferOut), 0, _BufferOutSize); + + LOG(WII_IPC_DVD, "%s executes DVDLowReadDiskID (Buffer 0x%08x, 0x%x)", GetDeviceName().c_str(), _BufferOut, _BufferOutSize); + + return 0x1; + } + break; + + // DVDLowRead + case 0x71: + { + u32 Size = Memory::Read_U32(_BufferIn + 0x04); + u64 DVDAddress = (u64)Memory::Read_U32(_BufferIn + 0x08) << 2; + + const char* pFilename = m_pFileSystem->GetFileName(DVDAddress); + if (pFilename != NULL) + { + LOG(WII_IPC_DVD, " DVDLowRead: %s (0x%x) - (DVDAddr: 0x%x, Size: 0x%x)", pFilename, m_pFileSystem->GetFileSize(pFilename), DVDAddress, Size); + } + else + { + LOG(WII_IPC_DVD, " DVDLowRead: file unkw - (DVDAddr: 0x%x, Size: 0x%x)", GetDeviceName().c_str(), DVDAddress, Size); + } + + if (Size > _BufferOutSize) + { + PanicAlert("Detected attempt to read more data from the DVD than fit inside the out buffer. Clamp."); + Size = _BufferOutSize; + } + + if (VolumeHandler::ReadToPtr(Memory::GetPointer(_BufferOut), DVDAddress, Size) != true) + { + PanicAlert("Cant read from DVD_Plugin - DVD-Interface: Fatal Error"); + } + + return 0x1; + } + break; + + // DVDLowWaitForCoverClose + case 0x79: + { + Memory::Memset(_BufferOut, 0, _BufferOutSize); + LOG(WII_IPC_DVD, "%s executes DVDLowWaitForCoverClose (Buffer 0x%08x, 0x%x)", GetDeviceName().c_str(), _BufferOut, _BufferOutSize); + return 4; + } + break; + + // DVDLowGetCoverReg - Called by "Legend of Spyro" and MP3 + case 0x7a: + { + LOG(WII_IPC_DVD, "%s executes DVDLowGetCoverReg (Buffer 0x%08x, 0x%x)", GetDeviceName().c_str(), _BufferOut, _BufferOutSize); + + // Write zeroes to the out buffer just in case there is some nonsense data there + Memory::Memset(_BufferOut, 0, _BufferOutSize); + + // -------------------------------------------------------------------- + /* Hack for Legend of Spyro. Switching the 4th byte between 0 and 1 gets + through this check. The out buffer address remains the same all the + time so we don't have to bother making a global function. + + TODO: Make this compatible with MP3 */ + // ------------------------- + /* + static u8 coverByte = 0; + + u8* buffer = Memory::GetPointer(_BufferOut); + buffer[3] = coverByte; + + if(coverByte) + coverByte = 0; + else + coverByte = 0x01; + + return 1; + */ + } + break; + + // DVDLowClearCoverInterrupt + case 0x86: + { + Memory::Memset(_BufferOut, 0, _BufferOutSize); + LOGV(WII_IPC_DVD, 1, "%s executes DVDLowClearCoverInterrupt (Buffer 0x%08x, 0x%x)", GetDeviceName().c_str(), _BufferOut, _BufferOutSize); + } + break; + + // DVDLowGetCoverStatus + case 0x88: + { + Memory::Memset(_BufferOut, 0, _BufferOutSize); + LOG(WII_IPC_DVD, "%s executes DVDLowGetCoverStatus (Buffer 0x%08x, 0x%x)", GetDeviceName().c_str(), _BufferOut, _BufferOutSize); + return 1; + } + break; + + // DVDLowReset + case 0x8a: + { + Memory::Memset(_BufferOut, 0, _BufferOutSize); + LOG(WII_IPC_DVD, "%s executes DVDLowReset (Buffer 0x%08x, 0x%x)", GetDeviceName().c_str(), _BufferOut, _BufferOutSize); + return 1; + } + break; + + // DVDLowOpenPartition + case 0x8b: + PanicAlert("DVDLowOpenPartition", Command); + break; + + + // DVDLowUnencryptedRead + case 0x8d: + PanicAlert("DVDLowUnencryptedRead"); + break; + + // DVDLowSeek + case 0xab: + // PanicAlert("DVDLowSeek"); + break; + + // DVDLowStopMotor + case 0xe3: + { + Memory::Memset(_BufferOut, 0, _BufferOutSize); + u32 eject = Memory::Read_U32(_BufferIn + 0x04); + + LOG(WII_IPC_DVD, "%s executes DVDLowStopMotor (Buffer 0x%08x, 0x%x)", GetDeviceName().c_str(), _BufferOut, _BufferOutSize); + + if(eject) + { + LOG(WII_IPC_DVD, "Eject disc", GetDeviceName().c_str(), _BufferOut, _BufferOutSize); + // TODO: eject the disc + } + } + break; + + default: + LOG(WII_IPC_DVD, "%s executes unknown cmd 0x%08x (Buffer 0x%08x, 0x%x)", GetDeviceName().c_str(), Command, _BufferOut, _BufferOutSize); + + PanicAlert("%s executes unknown cmd 0x%08x", GetDeviceName().c_str(), Command); + break; + } + + // i dunno but prolly 1 is okay all the time :) + return 1; +} diff --git a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_FileIO.cpp b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_FileIO.cpp index 401a9b8c43..196b08ea64 100644 --- a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_FileIO.cpp +++ b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_FileIO.cpp @@ -1,246 +1,246 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "FileUtil.h" - -#include "WII_IPC_HLE_Device_FileIO.h" - - -// =================================================== -/* This is used by several of the FileIO and /dev/fs/ functions */ -// ---------------- -std::string HLE_IPC_BuildFilename(const char* _pFilename, int _size) -{ - char Buffer[128]; - memcpy(Buffer, _pFilename, _size); - - std::string Filename(FULL_WII_ROOT_DIR); - if (Buffer[1] == '0') - Filename += std::string("/title"); // this looks and feel like an hack... - - Filename += Buffer; - - return Filename; -} - -CWII_IPC_HLE_Device_FileIO::CWII_IPC_HLE_Device_FileIO(u32 _DeviceID, const std::string& _rDeviceName ) - : IWII_IPC_HLE_Device(_DeviceID, _rDeviceName) - , m_pFileHandle(NULL) - , m_FileLength(0) -{ -} - -CWII_IPC_HLE_Device_FileIO::~CWII_IPC_HLE_Device_FileIO() -{ - if (m_pFileHandle != NULL) - { - fclose(m_pFileHandle); - m_pFileHandle = NULL; - } -} - -bool -CWII_IPC_HLE_Device_FileIO::Close(u32 _CommandAddress) -{ - u32 DeviceID = Memory::Read_U32(_CommandAddress + 8); - LOG(WII_IPC_FILEIO, "FileIO: Close %s (DeviceID=%08x)", GetDeviceName().c_str(), DeviceID); - - // Close always return 0 for success - Memory::Write_U32(0, _CommandAddress + 4); - - return true; -} - -bool -CWII_IPC_HLE_Device_FileIO::Open(u32 _CommandAddress, u32 _Mode) -{ - // close the file handle if we get a reopen - if (m_pFileHandle != NULL) - { - fclose(m_pFileHandle); - m_pFileHandle = NULL; - } - - const char Modes[][128] = - { - { "Unk Mode" }, - { "Read only" }, - { "Write only" }, - { "Read and Write" } - }; - - LOG(WII_IPC_FILEIO, "FileIO: Open %s (%s)", GetDeviceName().c_str(), Modes[_Mode]); - - m_Filename = std::string(HLE_IPC_BuildFilename(GetDeviceName().c_str(), 64)); - - if (File::Exists(m_Filename.c_str())) - { - switch(_Mode) - { - // Do "r+b" for all writing to avoid truncating the file - case 0x01: m_pFileHandle = fopen(m_Filename.c_str(), "rb"); break; - case 0x02: //m_pFileHandle = fopen(m_Filename.c_str(), "wb"); break; - case 0x03: m_pFileHandle = fopen(m_Filename.c_str(), "r+b"); break; - default: PanicAlert("CWII_IPC_HLE_Device_FileIO: unknown open mode"); break; - } - } - - u32 ReturnValue = 0; - if (m_pFileHandle != NULL) - { - m_FileLength = File::GetSize(m_Filename.c_str()); - ReturnValue = GetDeviceID(); - } - else - { - LOG(WII_IPC_FILEIO, " failed - File doesn't exist"); - ReturnValue = -106; - } - - Memory::Write_U32(ReturnValue, _CommandAddress+4); - return true; -} - -bool -CWII_IPC_HLE_Device_FileIO::Seek(u32 _CommandAddress) -{ - u32 ReturnValue = 0; - u32 SeekPosition = Memory::Read_U32(_CommandAddress + 0xC); - u32 Mode = Memory::Read_U32(_CommandAddress +0x10); - - LOG(WII_IPC_FILEIO, "FileIO: Seek Pos: %i, Mode: %i (Device=%s)", SeekPosition, Mode, GetDeviceName().c_str()); - - switch(Mode) - { - case 0: - if (fseek(m_pFileHandle, SeekPosition, SEEK_SET) == 0) { - // Seek always return the seek position for success - ReturnValue = SeekPosition; - } else { - LOG(WII_IPC_FILEIO, "FILEIO: Seek failed"); - } - break; - - case 1: // cur - case 2: // end - default: - PanicAlert("CWII_IPC_HLE_Device_FileIO unsupported seek mode"); - break; - } - - Memory::Write_U32(ReturnValue, _CommandAddress + 0x4); - - return true; -} - -bool -CWII_IPC_HLE_Device_FileIO::Read(u32 _CommandAddress) -{ - u32 ReturnValue = 0; - u32 Address = Memory::Read_U32(_CommandAddress +0xC); - u32 Size = Memory::Read_U32(_CommandAddress +0x10); - - if (m_pFileHandle != NULL) - { - size_t readItems = fread(Memory::GetPointer(Address), 1, Size, m_pFileHandle); - ReturnValue = (u32)readItems; - LOG(WII_IPC_FILEIO, "FileIO reads from %s (Addr=0x%08x Size=0x%x)", GetDeviceName().c_str(), Address, Size); - } - else - { - LOG(WII_IPC_FILEIO, "FileIO failed to read from %s (Addr=0x%08x Size=0x%x) - file not open", GetDeviceName().c_str(), Address, Size); - } - - Memory::Write_U32(ReturnValue, _CommandAddress + 0x4); - - return true; -} - -bool -CWII_IPC_HLE_Device_FileIO::Write(u32 _CommandAddress) -{ - u32 ReturnValue = 0; - u32 Address = Memory::Read_U32(_CommandAddress +0xC); - u32 Size = Memory::Read_U32(_CommandAddress +0x10); - - LOG(WII_IPC_FILEIO, "FileIO: Write Addr: 0x%08x Size: %i (Device=%s)", Address, Size, GetDeviceName().c_str()); - - if (m_pFileHandle) - { - fwrite(Memory::GetPointer(Address), Size, 1, m_pFileHandle); - - // Write always return the written bytes for success - ReturnValue = Size; - } - - Memory::Write_U32(ReturnValue, _CommandAddress + 0x4); - - return true; -} - -bool -CWII_IPC_HLE_Device_FileIO::IOCtl(u32 _CommandAddress) -{ - LOG(WII_IPC_FILEIO, "FileIO: IOCtl (Device=%s)", GetDeviceName().c_str()); - DumpCommands(_CommandAddress); - - u32 Parameter = Memory::Read_U32(_CommandAddress + 0xC); - - // u32 BufferIn = Memory::Read_U32(_CommandAddress + 0x10); - // u32 BufferInSize = Memory::Read_U32(_CommandAddress + 0x14); - // u32 BufferOut = Memory::Read_U32(_CommandAddress + 0x18); - // u32 BufferOutSize = Memory::Read_U32(_CommandAddress + 0x1C); - - switch(Parameter) - { - case ISFS_IOCTL_GETFILESTATS: - { - u32 Position = (u32)ftell(m_pFileHandle); - - u32 BufferOut = Memory::Read_U32(_CommandAddress + 0x18); - LOG(WII_IPC_FILEIO, "FileIO: ISFS_IOCTL_GETFILESTATS"); - LOG(WII_IPC_FILEIO, " Length: %i Seek: %i", m_FileLength, Position); - - Memory::Write_U32((u32)m_FileLength, BufferOut); - Memory::Write_U32(Position, BufferOut+4); - } - break; - - default: - { - PanicAlert("CWII_IPC_HLE_Device_FileIO: Parameter %i", Parameter); - } - break; - - } - - // Return Value - u32 ReturnValue = 0; // no error - Memory::Write_U32(ReturnValue, _CommandAddress + 0x4); - - return true; -} - -bool -CWII_IPC_HLE_Device_FileIO::ReturnFileHandle() -{ - if(m_pFileHandle == NULL) - return false; - else - return true; -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "FileUtil.h" + +#include "WII_IPC_HLE_Device_FileIO.h" + + +// =================================================== +/* This is used by several of the FileIO and /dev/fs/ functions */ +// ---------------- +std::string HLE_IPC_BuildFilename(const char* _pFilename, int _size) +{ + char Buffer[128]; + memcpy(Buffer, _pFilename, _size); + + std::string Filename(FULL_WII_ROOT_DIR); + if (Buffer[1] == '0') + Filename += std::string("/title"); // this looks and feel like an hack... + + Filename += Buffer; + + return Filename; +} + +CWII_IPC_HLE_Device_FileIO::CWII_IPC_HLE_Device_FileIO(u32 _DeviceID, const std::string& _rDeviceName ) + : IWII_IPC_HLE_Device(_DeviceID, _rDeviceName) + , m_pFileHandle(NULL) + , m_FileLength(0) +{ +} + +CWII_IPC_HLE_Device_FileIO::~CWII_IPC_HLE_Device_FileIO() +{ + if (m_pFileHandle != NULL) + { + fclose(m_pFileHandle); + m_pFileHandle = NULL; + } +} + +bool +CWII_IPC_HLE_Device_FileIO::Close(u32 _CommandAddress) +{ + u32 DeviceID = Memory::Read_U32(_CommandAddress + 8); + LOG(WII_IPC_FILEIO, "FileIO: Close %s (DeviceID=%08x)", GetDeviceName().c_str(), DeviceID); + + // Close always return 0 for success + Memory::Write_U32(0, _CommandAddress + 4); + + return true; +} + +bool +CWII_IPC_HLE_Device_FileIO::Open(u32 _CommandAddress, u32 _Mode) +{ + // close the file handle if we get a reopen + if (m_pFileHandle != NULL) + { + fclose(m_pFileHandle); + m_pFileHandle = NULL; + } + + const char Modes[][128] = + { + { "Unk Mode" }, + { "Read only" }, + { "Write only" }, + { "Read and Write" } + }; + + LOG(WII_IPC_FILEIO, "FileIO: Open %s (%s)", GetDeviceName().c_str(), Modes[_Mode]); + + m_Filename = std::string(HLE_IPC_BuildFilename(GetDeviceName().c_str(), 64)); + + if (File::Exists(m_Filename.c_str())) + { + switch(_Mode) + { + // Do "r+b" for all writing to avoid truncating the file + case 0x01: m_pFileHandle = fopen(m_Filename.c_str(), "rb"); break; + case 0x02: //m_pFileHandle = fopen(m_Filename.c_str(), "wb"); break; + case 0x03: m_pFileHandle = fopen(m_Filename.c_str(), "r+b"); break; + default: PanicAlert("CWII_IPC_HLE_Device_FileIO: unknown open mode"); break; + } + } + + u32 ReturnValue = 0; + if (m_pFileHandle != NULL) + { + m_FileLength = File::GetSize(m_Filename.c_str()); + ReturnValue = GetDeviceID(); + } + else + { + LOG(WII_IPC_FILEIO, " failed - File doesn't exist"); + ReturnValue = -106; + } + + Memory::Write_U32(ReturnValue, _CommandAddress+4); + return true; +} + +bool +CWII_IPC_HLE_Device_FileIO::Seek(u32 _CommandAddress) +{ + u32 ReturnValue = 0; + u32 SeekPosition = Memory::Read_U32(_CommandAddress + 0xC); + u32 Mode = Memory::Read_U32(_CommandAddress +0x10); + + LOG(WII_IPC_FILEIO, "FileIO: Seek Pos: %i, Mode: %i (Device=%s)", SeekPosition, Mode, GetDeviceName().c_str()); + + switch(Mode) + { + case 0: + if (fseek(m_pFileHandle, SeekPosition, SEEK_SET) == 0) { + // Seek always return the seek position for success + ReturnValue = SeekPosition; + } else { + LOG(WII_IPC_FILEIO, "FILEIO: Seek failed"); + } + break; + + case 1: // cur + case 2: // end + default: + PanicAlert("CWII_IPC_HLE_Device_FileIO unsupported seek mode"); + break; + } + + Memory::Write_U32(ReturnValue, _CommandAddress + 0x4); + + return true; +} + +bool +CWII_IPC_HLE_Device_FileIO::Read(u32 _CommandAddress) +{ + u32 ReturnValue = 0; + u32 Address = Memory::Read_U32(_CommandAddress +0xC); + u32 Size = Memory::Read_U32(_CommandAddress +0x10); + + if (m_pFileHandle != NULL) + { + size_t readItems = fread(Memory::GetPointer(Address), 1, Size, m_pFileHandle); + ReturnValue = (u32)readItems; + LOG(WII_IPC_FILEIO, "FileIO reads from %s (Addr=0x%08x Size=0x%x)", GetDeviceName().c_str(), Address, Size); + } + else + { + LOG(WII_IPC_FILEIO, "FileIO failed to read from %s (Addr=0x%08x Size=0x%x) - file not open", GetDeviceName().c_str(), Address, Size); + } + + Memory::Write_U32(ReturnValue, _CommandAddress + 0x4); + + return true; +} + +bool +CWII_IPC_HLE_Device_FileIO::Write(u32 _CommandAddress) +{ + u32 ReturnValue = 0; + u32 Address = Memory::Read_U32(_CommandAddress +0xC); + u32 Size = Memory::Read_U32(_CommandAddress +0x10); + + LOG(WII_IPC_FILEIO, "FileIO: Write Addr: 0x%08x Size: %i (Device=%s)", Address, Size, GetDeviceName().c_str()); + + if (m_pFileHandle) + { + fwrite(Memory::GetPointer(Address), Size, 1, m_pFileHandle); + + // Write always return the written bytes for success + ReturnValue = Size; + } + + Memory::Write_U32(ReturnValue, _CommandAddress + 0x4); + + return true; +} + +bool +CWII_IPC_HLE_Device_FileIO::IOCtl(u32 _CommandAddress) +{ + LOG(WII_IPC_FILEIO, "FileIO: IOCtl (Device=%s)", GetDeviceName().c_str()); + DumpCommands(_CommandAddress); + + u32 Parameter = Memory::Read_U32(_CommandAddress + 0xC); + + // u32 BufferIn = Memory::Read_U32(_CommandAddress + 0x10); + // u32 BufferInSize = Memory::Read_U32(_CommandAddress + 0x14); + // u32 BufferOut = Memory::Read_U32(_CommandAddress + 0x18); + // u32 BufferOutSize = Memory::Read_U32(_CommandAddress + 0x1C); + + switch(Parameter) + { + case ISFS_IOCTL_GETFILESTATS: + { + u32 Position = (u32)ftell(m_pFileHandle); + + u32 BufferOut = Memory::Read_U32(_CommandAddress + 0x18); + LOG(WII_IPC_FILEIO, "FileIO: ISFS_IOCTL_GETFILESTATS"); + LOG(WII_IPC_FILEIO, " Length: %i Seek: %i", m_FileLength, Position); + + Memory::Write_U32((u32)m_FileLength, BufferOut); + Memory::Write_U32(Position, BufferOut+4); + } + break; + + default: + { + PanicAlert("CWII_IPC_HLE_Device_FileIO: Parameter %i", Parameter); + } + break; + + } + + // Return Value + u32 ReturnValue = 0; // no error + Memory::Write_U32(ReturnValue, _CommandAddress + 0x4); + + return true; +} + +bool +CWII_IPC_HLE_Device_FileIO::ReturnFileHandle() +{ + if(m_pFileHandle == NULL) + return false; + else + return true; +} diff --git a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_fs.cpp b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_fs.cpp index d2c79d446a..4c184a3dbf 100644 --- a/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_fs.cpp +++ b/Source/Core/Core/Src/IPC_HLE/WII_IPC_HLE_Device_fs.cpp @@ -1,499 +1,499 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" - -#include "WII_IPC_HLE_Device_fs.h" - -#include "StringUtil.h" -#include "FileSearch.h" -#include "FileUtil.h" - -#include "../VolumeHandler.h" - -extern std::string HLE_IPC_BuildFilename(const char* _pFilename, int _size); - -#define FS_RESULT_OK (0) -#define FS_DIRFILE_NOT_FOUND (-6) -#define FS_INVALID_ARGUMENT (-101) -#define FS_FILE_EXIST (-105) -#define FS_FILE_NOT_EXIST (-106) -#define FS_RESULT_FATAL (-128) - -#define MAX_NAME (12) - - -CWII_IPC_HLE_Device_fs::CWII_IPC_HLE_Device_fs(u32 _DeviceID, const std::string& _rDeviceName) - : IWII_IPC_HLE_Device(_DeviceID, _rDeviceName) -{} - -CWII_IPC_HLE_Device_fs::~CWII_IPC_HLE_Device_fs() -{} - -bool CWII_IPC_HLE_Device_fs::Open(u32 _CommandAddress, u32 _Mode) -{ - // clear tmp folder - { - std::string WiiTempFolder(FULL_WII_USER_DIR "tmp"); - File::DeleteDirRecursively(WiiTempFolder.c_str()); - File::CreateDir(WiiTempFolder.c_str()); - } - - // create home directory - { - u32 TitleID = VolumeHandler::Read32(0); - if (TitleID == 0) TitleID = 0xF00DBEEF; - - char* pTitleID = (char*)&TitleID; - - char Path[260+1]; - sprintf(Path, FULL_WII_USER_DIR "title/00010000/%02x%02x%02x%02x/data/nocopy/", - (u8)pTitleID[3], (u8)pTitleID[2], (u8)pTitleID[1], (u8)pTitleID[0]); - - File::CreateDirectoryStructure(Path); - } - - Memory::Write_U32(GetDeviceID(), _CommandAddress+4); - return true; -} - - -// ======================================================= -// IOCtlV calls begin here -// ------------- -bool CWII_IPC_HLE_Device_fs::IOCtlV(u32 _CommandAddress) -{ - u32 ReturnValue = FS_RESULT_OK; - SIOCtlVBuffer CommandBuffer(_CommandAddress); - - // Prepare the out buffer(s) with zeroes as a safety precaution - // to avoid returning bad values - for(u32 i = 0; i < CommandBuffer.NumberPayloadBuffer; i++) - { - Memory::Memset(CommandBuffer.PayloadBuffer[i].m_Address, 0, - CommandBuffer.PayloadBuffer[i].m_Size); - } - - switch(CommandBuffer.Parameter) - { - case IOCTL_READ_DIR: - { - // the wii uses this function to define the type (dir or file) - std::string Filename(HLE_IPC_BuildFilename((const char*)Memory::GetPointer( - CommandBuffer.InBuffer[0].m_Address), CommandBuffer.InBuffer[0].m_Size)); - - LOG(WII_IPC_FILEIO, "FS: IOCTL_READ_DIR %s", Filename.c_str()); - - /* Check if this is really a directory. Or a file, because it seems like Mario Kart - did a IOCTL_READ_DIR on the save file to check if it existed before deleting it, - and if I didn't returned a -something it never deleted the file presumably because - it thought it didn't exist. So this solution worked for Mario Kart. - - F|RES: i dont have mkart but -6 is a wrong return value if you try to read from a - directory which doesnt exist - - JP: Okay, but Mario Kart calls this for files and if I return 0 here it never - creates a new file in any event, it just calls a DELETE_FILE and never close - the handle, so perhaps this is better - */ - - if (!File::Exists(Filename.c_str())) - { - LOG(WII_IPC_FILEIO, " directory does not exist - return FS_DIRFILE_NOT_FOUND", Filename.c_str()); - ReturnValue = FS_DIRFILE_NOT_FOUND; - break; - } - /* Okay, maybe it is a file but not a directory, then we should return -101? - I have not seen any example of this. */ - else if (!File::IsDirectory(Filename.c_str())) - { - LOG(WII_IPC_FILEIO, " Not a directory - return FS_INVALID_ARGUMENT", Filename.c_str()); - ReturnValue = FS_INVALID_ARGUMENT; - break; - } - - // make a file search - CFileSearch::XStringVector Directories; - Directories.push_back(Filename); - - CFileSearch::XStringVector Extensions; - Extensions.push_back("*.*"); - - CFileSearch FileSearch(Extensions, Directories); - - // it is one - if ((CommandBuffer.InBuffer.size() == 1) && (CommandBuffer.PayloadBuffer.size() == 1)) - { - size_t numFile = FileSearch.GetFileNames().size(); - LOG(WII_IPC_FILEIO, " Files in directory: %i", numFile); - - Memory::Write_U32((u32)numFile, CommandBuffer.PayloadBuffer[0].m_Address); - } - else - { - u32 MaxEntries = Memory::Read_U32(CommandBuffer.InBuffer[0].m_Address); - - memset(Memory::GetPointer(CommandBuffer.PayloadBuffer[0].m_Address), 0, CommandBuffer.PayloadBuffer[0].m_Size); - - size_t numFiles = 0; - char* pFilename = (char*)Memory::GetPointer((u32)(CommandBuffer.PayloadBuffer[0].m_Address)); - - for (size_t i=0; i= MaxEntries) - break; - - std::string filename, ext; - SplitPath(FileSearch.GetFileNames()[i], NULL, &filename, &ext); - std::string CompleteFilename = filename + ext; - - strcpy(pFilename, CompleteFilename.c_str()); - pFilename += CompleteFilename.length(); - *pFilename++ = 0x00; // termination - numFiles++; - - LOG(WII_IPC_FILEIO, " %s", CompleteFilename.c_str()); - } - - Memory::Write_U32((u32)numFiles, CommandBuffer.PayloadBuffer[1].m_Address); - } - - ReturnValue = FS_RESULT_OK; - } - break; - - case IOCTL_GETUSAGE: - { - // check buffer sizes - _dbg_assert_(WII_IPC_FILEIO, CommandBuffer.PayloadBuffer.size() == 2); - _dbg_assert_(WII_IPC_FILEIO, CommandBuffer.PayloadBuffer[0].m_Size == 4); - _dbg_assert_(WII_IPC_FILEIO, CommandBuffer.PayloadBuffer[1].m_Size == 4); - - // this command sucks because it asks of the number of used - // fsBlocks and inodes - // we answer nothing is used, but if a program uses it to check - // how much memory has been used we are doomed... - std::string Filename(HLE_IPC_BuildFilename((const char*)Memory::GetPointer(CommandBuffer.InBuffer[0].m_Address), CommandBuffer.InBuffer[0].m_Size)); - u32 fsBlock = 0; - u32 iNodes = 0; - - LOGV(WII_IPC_FILEIO, 1, "FS: IOCTL_GETUSAGE %s", Filename.c_str()); - if (File::IsDirectory(Filename.c_str())) - { - // make a file search - CFileSearch::XStringVector Directories; - Directories.push_back(Filename); - - CFileSearch::XStringVector Extensions; - Extensions.push_back("*.*"); - - CFileSearch FileSearch(Extensions, Directories); - - u64 overAllSize = 0; - for (size_t i=0; i= MaxEntries) + break; + + std::string filename, ext; + SplitPath(FileSearch.GetFileNames()[i], NULL, &filename, &ext); + std::string CompleteFilename = filename + ext; + + strcpy(pFilename, CompleteFilename.c_str()); + pFilename += CompleteFilename.length(); + *pFilename++ = 0x00; // termination + numFiles++; + + LOG(WII_IPC_FILEIO, " %s", CompleteFilename.c_str()); + } + + Memory::Write_U32((u32)numFiles, CommandBuffer.PayloadBuffer[1].m_Address); + } + + ReturnValue = FS_RESULT_OK; + } + break; + + case IOCTL_GETUSAGE: + { + // check buffer sizes + _dbg_assert_(WII_IPC_FILEIO, CommandBuffer.PayloadBuffer.size() == 2); + _dbg_assert_(WII_IPC_FILEIO, CommandBuffer.PayloadBuffer[0].m_Size == 4); + _dbg_assert_(WII_IPC_FILEIO, CommandBuffer.PayloadBuffer[1].m_Size == 4); + + // this command sucks because it asks of the number of used + // fsBlocks and inodes + // we answer nothing is used, but if a program uses it to check + // how much memory has been used we are doomed... + std::string Filename(HLE_IPC_BuildFilename((const char*)Memory::GetPointer(CommandBuffer.InBuffer[0].m_Address), CommandBuffer.InBuffer[0].m_Size)); + u32 fsBlock = 0; + u32 iNodes = 0; + + LOGV(WII_IPC_FILEIO, 1, "FS: IOCTL_GETUSAGE %s", Filename.c_str()); + if (File::IsDirectory(Filename.c_str())) + { + // make a file search + CFileSearch::XStringVector Directories; + Directories.push_back(Filename); + + CFileSearch::XStringVector Extensions; + Extensions.push_back("*.*"); + + CFileSearch FileSearch(Extensions, Directories); + + u64 overAllSize = 0; + for (size_t i=0; i= 4 and you will get a lot of event messages of the same type - - Memory::Write_U8(1, 0x80152018); // WUD - - Memory::Write_U8(1, 0x80151FC8); // DEBUGPrint */ - - - // even it it wasn't very useful yet... - // Memory::Write_U8(1, 0x80151488); // WPAD_LOG - // Memory::Write_U8(1, 0x801514A8); // USB_LOG - // Memory::Write_U8(1, 0x801514D8); // WUD_DEBUGPrint - // Memory::Write_U8(1, 0x80148E09); // HID LOG - - SIOCtlVBuffer CommandBuffer(_CommandAddress); - - switch(CommandBuffer.Parameter) - { - case USB_IOCTL_HCI_COMMAND_MESSAGE: - { - SHCICommandMessage CtrlSetup; - - // the USB stuff is little endian.. - CtrlSetup.bRequestType = *(u8*)Memory::GetPointer(CommandBuffer.InBuffer[0].m_Address); - CtrlSetup.bRequest = *(u8*)Memory::GetPointer(CommandBuffer.InBuffer[1].m_Address); - CtrlSetup.wValue = *(u16*)Memory::GetPointer(CommandBuffer.InBuffer[2].m_Address); - CtrlSetup.wIndex = *(u16*)Memory::GetPointer(CommandBuffer.InBuffer[3].m_Address); - CtrlSetup.wLength = *(u16*)Memory::GetPointer(CommandBuffer.InBuffer[4].m_Address); - CtrlSetup.m_PayLoadAddr = CommandBuffer.PayloadBuffer[0].m_Address; - CtrlSetup.m_PayLoadSize = CommandBuffer.PayloadBuffer[0].m_Size; - - // check termination - _dbg_assert_msg_(WII_IPC_WIIMOTE, *(u8*)Memory::GetPointer(CommandBuffer.InBuffer[5].m_Address) == 0, - "WIIMOTE: Termination != 0"); - -#if 0 - LOG(WII_IPC_WIIMOTE, "USB_IOCTL_CTRLMSG (0x%08x) - execute command", _CommandAddress); - - LOG(WII_IPC_WIIMOTE, " bRequestType: 0x%x", CtrlSetup.bRequestType); - LOG(WII_IPC_WIIMOTE, " bRequest: 0x%x", CtrlSetup.bRequest); - LOG(WII_IPC_WIIMOTE, " wValue: 0x%x", CtrlSetup.wValue); - LOG(WII_IPC_WIIMOTE, " wIndex: 0x%x", CtrlSetup.wIndex); - LOG(WII_IPC_WIIMOTE, " wLength: 0x%x", CtrlSetup.wLength); -#endif - - ExecuteHCICommandMessage(CtrlSetup); - - // control message has been sent executed - Memory::Write_U32(0, _CommandAddress + 0x4); - - return true; - } - break; - - case USB_IOCTL_BLKMSG: - { - u8 Command = Memory::Read_U8(CommandBuffer.InBuffer[0].m_Address); - switch (Command) - { - case ACL_DATA_ENDPOINT_READ: - { - // write - DumpAsync(CommandBuffer.BufferVector, _CommandAddress, CommandBuffer.NumberInBuffer, CommandBuffer.NumberPayloadBuffer); - - SIOCtlVBuffer pBulkBuffer(_CommandAddress); - UACLHeader* pACLHeader = (UACLHeader*)Memory::GetPointer(pBulkBuffer.PayloadBuffer[0].m_Address); - - _dbg_assert_(WII_IPC_WIIMOTE, pACLHeader->BCFlag == 0); - _dbg_assert_(WII_IPC_WIIMOTE, pACLHeader->PBFlag == 2); - - SendToDevice(pACLHeader->ConnectionHandle, Memory::GetPointer(pBulkBuffer.PayloadBuffer[0].m_Address + 4), pACLHeader->Size); - } - break; - - case ACL_DATA_ENDPOINT: - { - if (m_pACLBuffer) - delete m_pACLBuffer; - m_pACLBuffer = new SIOCtlVBuffer(_CommandAddress); - - LOGV(WII_IPC_WIIMOTE, 2, "ACL_DATA_ENDPOINT: 0x%08x ", _CommandAddress); - return false; - } - break; - - default: - { - _dbg_assert_msg_(WII_IPC_WIIMOTE, 0, "Unknown USB_IOCTL_BLKMSG: %x", Command); - } - break; - } - } - break; - - - case USB_IOCTL_INTRMSG: - { - u8 Command = Memory::Read_U8(CommandBuffer.InBuffer[0].m_Address); - switch (Command) - { - case HCI_EVENT_ENDPOINT: - { - if (m_pHCIBuffer) - { - PanicAlert("Kill current hci buffer... there could be a comand inside"); - delete m_pHCIBuffer; - } - m_pHCIBuffer = new SIOCtlVBuffer(_CommandAddress); - return false; - } - break; - - default: - { - _dbg_assert_msg_(WII_IPC_WIIMOTE, 0, "Unknown USB_IOCTL_INTRMSG: %x", Command); - } - break; - } - } - break; - - default: - { - _dbg_assert_msg_(WII_IPC_WIIMOTE, 0, "Unknown CWII_IPC_HLE_Device_usb_oh1_57e_305: %x", CommandBuffer.Parameter); - - LOG(WII_IPC_WIIMOTE, "%s - IOCtlV:", GetDeviceName().c_str()); - LOG(WII_IPC_WIIMOTE, " Parameter: 0x%x", CommandBuffer.Parameter); - LOG(WII_IPC_WIIMOTE, " NumberIn: 0x%08x", CommandBuffer.NumberInBuffer); - LOG(WII_IPC_WIIMOTE, " NumberOut: 0x%08x", CommandBuffer.NumberPayloadBuffer); - LOG(WII_IPC_WIIMOTE, " BufferVector: 0x%08x", CommandBuffer.BufferVector); - LOG(WII_IPC_WIIMOTE, " BufferSize: 0x%08x", CommandBuffer.BufferSize); - DumpAsync(CommandBuffer.BufferVector, _CommandAddress, CommandBuffer.NumberInBuffer, CommandBuffer.NumberPayloadBuffer); - } - break; - } - - // write return value - Memory::Write_U32(0, _CommandAddress + 0x4); - - return true; -} -// ================ - - - -// =================================================== -/* Here we handle the USB_IOCTL_BLKMSG Ioctlv */ -// ---------------- - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::SendToDevice(u16 _ConnectionHandle, u8* _pData, u32 _Size) -{ - CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_ConnectionHandle); - if (pWiiMote == NULL) - { - PanicAlert("SendToDevice: Cant find WiiMote by connection handle: %02x", _ConnectionHandle); - return; - } - - pWiiMote->SendACLFrame(_pData, _Size); -} -// ================ - - - -// =================================================== -/* Here we queue the ACL frames we receive from the Wiimote plugin. They will consist of - header + data. The header is for example 07 00 41 00 which means size 0x0007 and - channel 0x0041. */ -// ---------------- -void CWII_IPC_HLE_Device_usb_oh1_57e_305::SendACLFrame(u16 _ConnectionHandle, u8* _pData, u32 _Size) -{ - LOGV(WII_IPC_WIIMOTE, 1, "Queing ACL frame."); - - // Queue the packet - ACLFrame frame; - frame.ConnectionHandle = _ConnectionHandle; - frame.data = new u8[_Size]; - memcpy(frame.data, _pData, _Size); - frame.size = _Size; - m_AclFrameQue.push(frame); - - /* Debugging - std::string Temp; - for (u32 j = 0; j < _Size; j++) - { - char Buffer[128]; - sprintf(Buffer, "%02x ", frame.data[j]); - Temp.append(Buffer); - } - LOGV(WII_IPC_WIIMOTE, 1, " Size: 0x%08x", _Size); - LOGV(WII_IPC_WIIMOTE, 1, " Data: %s", Temp.c_str()); */ - - g_HCICount++; -} - - -// =================================================== -/* This is called from WII_IPC_HLE_Interface::Update() */ -// ---------------- -u32 CWII_IPC_HLE_Device_usb_oh1_57e_305::Update() -{ - if (!m_EventQueue.empty() && m_pHCIBuffer) - { - SIOCtlVBuffer* pHCIBuffer = m_pHCIBuffer; - m_pHCIBuffer = NULL; - - // copy the event to memory - const SQueuedEvent& rEvent = m_EventQueue.front(); - u8* pHCIEvent = Memory::GetPointer(pHCIBuffer->PayloadBuffer[0].m_Address); - memcpy(pHCIEvent, rEvent.m_buffer, rEvent.m_size); - - // return reply buffer size - Memory::Write_U32((u32)rEvent.m_size, pHCIBuffer->m_Address + 0x4); - - if (rEvent.m_connectionHandle > 0) - { - g_HCICount++; - } - - m_EventQueue.pop(); - - u32 Addr = pHCIBuffer->m_Address; - delete pHCIBuffer; - - return Addr; - } - - // check if we can fill the aclbuffer - if(!m_AclFrameQue.empty() && m_pACLBuffer) - { - ACLFrame& frame = m_AclFrameQue.front(); - - LOGV(WII_IPC_WIIMOTE, 1, "Sending ACL frame."); - UACLHeader* pHeader = (UACLHeader*)Memory::GetPointer(m_pACLBuffer->PayloadBuffer[0].m_Address); - pHeader->ConnectionHandle = frame.ConnectionHandle; - pHeader->BCFlag = 0; - pHeader->PBFlag = 2; - pHeader->Size = frame.size; - - // Write the fram to the PayloadBuffer - memcpy(Memory::GetPointer(m_pACLBuffer->PayloadBuffer[0].m_Address + sizeof(UACLHeader)), - frame.data, frame.size); - - // return reply buffer size - Memory::Write_U32(sizeof(UACLHeader) + frame.size, m_pACLBuffer->m_Address + 0x4); - - delete frame.data; - m_AclFrameQue.pop(); - - u32 Addr = m_pACLBuffer->m_Address; - delete m_pACLBuffer; - m_pACLBuffer = NULL; - - - /* Debugging - std::string Temp; - for (u32 j = 0; j < frame.size; j++) - { - char Buffer[128]; - sprintf(Buffer, "%02x ", frame.data[j]); - Temp.append(Buffer); - } - LOGV(WII_IPC_WIIMOTE, 1, " Size: 0x%08x", frame.size); - LOGV(WII_IPC_WIIMOTE, 1, " Size of UACLHeader: 0x%08x", sizeof(UACLHeader)); - LOGV(WII_IPC_WIIMOTE, 1, " Data: %s", Temp.c_str()); */ - - return Addr; - } - - if ((g_GlobalHandle != 0) && (g_HCICount > 0)) - { - SendEventNumberOfCompletedPackets(g_GlobalHandle, g_HCICount*2); - g_HCICount = 0; - } - - if (m_AclFrameQue.empty()) - { - for (size_t i=0; iEventType = 0x0F; - pHCIEvent->PayloadLength = sizeof(SHCIEventStatus) - 2; - pHCIEvent->Status = 0x0; - pHCIEvent->PacketIndicator = 0x01; - pHCIEvent->Opcode = _Opcode; - - AddEventToQueue(Event); - - LOG(WII_IPC_WIIMOTE, "Event: Command Status"); - LOG(WII_IPC_WIIMOTE, " Opcode: 0x%04x", pHCIEvent->Opcode); - - return true; -} - - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventCommandComplete(u16 _OpCode, void* _pData, u32 _DataSize) -{ - _dbg_assert_(WII_IPC_WIIMOTE, (sizeof(SHCIEventCommand) - 2 + _DataSize) < 256); - - SQueuedEvent Event(sizeof(SHCIEventCommand) + _DataSize, 0); - - SHCIEventCommand* pHCIEvent = (SHCIEventCommand*)Event.m_buffer; - pHCIEvent->EventType = 0x0E; - pHCIEvent->PayloadLength = (u8)(sizeof(SHCIEventCommand) - 2 + _DataSize); - pHCIEvent->PacketIndicator = 0x01; - pHCIEvent->Opcode = _OpCode; - - // add the payload - if ((_pData != NULL) && (_DataSize > 0)) - { - u8* pPayload = Event.m_buffer + sizeof(SHCIEventCommand); - memcpy(pPayload, _pData, _DataSize); - } - - AddEventToQueue(Event); - - LOG(WII_IPC_WIIMOTE, "Event: Command Complete"); - LOG(WII_IPC_WIIMOTE, " Opcode: 0x%04x", pHCIEvent->Opcode); -} - -bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventInquiryResponse() -{ - if (m_WiiMotes.empty()) - return false; - - _dbg_assert_(WII_IPC_WIIMOTE, sizeof(SHCIEventInquiryResult) - 2 + (m_WiiMotes.size() * sizeof(hci_inquiry_response)) < 256); - - SQueuedEvent Event(sizeof(SHCIEventInquiryResult) + m_WiiMotes.size()*sizeof(hci_inquiry_response), 0); - - SHCIEventInquiryResult* pInquiryResult = (SHCIEventInquiryResult*)Event.m_buffer; - - pInquiryResult->EventType = 0x02; - pInquiryResult->PayloadLength = (u8)(sizeof(SHCIEventInquiryResult) - 2 + (m_WiiMotes.size() * sizeof(hci_inquiry_response))); - pInquiryResult->num_responses = (u8)m_WiiMotes.size(); - - for (size_t i=0; ibdaddr = m_WiiMotes[i].GetBD(); - pResponse->uclass[0]= m_WiiMotes[i].GetClass()[0]; - pResponse->uclass[1]= m_WiiMotes[i].GetClass()[1]; - pResponse->uclass[2]= m_WiiMotes[i].GetClass()[2]; - - pResponse->page_scan_rep_mode = 1; - pResponse->page_scan_period_mode = 0; - pResponse->page_scan_mode = 0; - pResponse->clock_offset = 0x3818; - - LOG(WII_IPC_WIIMOTE, "Event: Send Fake Inquriy of one controller"); - LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", - pResponse->bdaddr.b[0], pResponse->bdaddr.b[1], pResponse->bdaddr.b[2], - pResponse->bdaddr.b[3], pResponse->bdaddr.b[4], pResponse->bdaddr.b[5]); - } - - AddEventToQueue(Event); - - return true; -} - -bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventInquiryComplete() -{ - SQueuedEvent Event(sizeof(SHCIEventInquiryComplete), 0); - - SHCIEventInquiryComplete* pInquiryComplete = (SHCIEventInquiryComplete*)Event.m_buffer; - pInquiryComplete->EventType = 0x01; - pInquiryComplete->PayloadLength = sizeof(SHCIEventInquiryComplete) - 2; - pInquiryComplete->Status = 0x00; - - AddEventToQueue(Event); - - LOG(WII_IPC_WIIMOTE, "Event: Inquiry complete"); - - return true; -} - -bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventRemoteNameReq(bdaddr_t _bd) -{ - CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_bd); - if (pWiiMote == NULL) - { - PanicAlert("SendEventRemoteNameReq: Cant find WiiMote by bd: %02x:%02x:%02x:%02x:%02x:%02x", - _bd.b[0], _bd.b[1], _bd.b[2], _bd.b[3], _bd.b[4], _bd.b[5]); - return false; - } - - SQueuedEvent Event(sizeof(SHCIEventRemoteNameReq), 0); - - SHCIEventRemoteNameReq* pRemoteNameReq = (SHCIEventRemoteNameReq*)Event.m_buffer; - - pRemoteNameReq->EventType = 0x07; - pRemoteNameReq->PayloadLength = sizeof(SHCIEventRemoteNameReq) - 2; - pRemoteNameReq->Status = 0x00; - pRemoteNameReq->bdaddr = pWiiMote->GetBD(); - strcpy((char*)pRemoteNameReq->RemoteName, pWiiMote->GetName()); - - AddEventToQueue(Event); - - LOG(WII_IPC_WIIMOTE, "Event: SendEventRemoteNameReq"); - LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", - pRemoteNameReq->bdaddr.b[0], pRemoteNameReq->bdaddr.b[1], pRemoteNameReq->bdaddr.b[2], - pRemoteNameReq->bdaddr.b[3], pRemoteNameReq->bdaddr.b[4], pRemoteNameReq->bdaddr.b[5]); - LOG(WII_IPC_WIIMOTE, " remotename: %s", pRemoteNameReq->RemoteName); - - return true; -} - -bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventRequestConnection(CWII_IPC_HLE_WiiMote& _rWiiMote) -{ - SQueuedEvent Event(sizeof(SHCIEventRequestConnection), 0); - - SHCIEventRequestConnection* pEventRequestConnection = (SHCIEventRequestConnection*)Event.m_buffer; - - pEventRequestConnection->EventType = 0x04; - pEventRequestConnection->PayloadLength = sizeof(SHCIEventRequestConnection) - 2; - pEventRequestConnection->bdaddr = _rWiiMote.GetBD(); - pEventRequestConnection->uclass[0] = _rWiiMote.GetClass()[0]; - pEventRequestConnection->uclass[1] = _rWiiMote.GetClass()[1]; - pEventRequestConnection->uclass[2] = _rWiiMote.GetClass()[2]; - pEventRequestConnection->LinkType = 0x01; - - AddEventToQueue(Event); - - // Log -#ifdef LOGGING - static char LinkType[][128] = - { - { "HCI_LINK_SCO 0x00 - Voice"}, - { "HCI_LINK_ACL 0x01 - Data"}, - { "HCI_LINK_eSCO 0x02 - eSCO"}, - }; -#endif - - LOG(WII_IPC_WIIMOTE, "Event: SendEventRequestConnection"); - LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", - pEventRequestConnection->bdaddr.b[0], pEventRequestConnection->bdaddr.b[1], pEventRequestConnection->bdaddr.b[2], - pEventRequestConnection->bdaddr.b[3], pEventRequestConnection->bdaddr.b[4], pEventRequestConnection->bdaddr.b[5]); - LOG(WII_IPC_WIIMOTE, " COD[0]: 0x%02x", pEventRequestConnection->uclass[0]); - LOG(WII_IPC_WIIMOTE, " COD[1]: 0x%02x", pEventRequestConnection->uclass[1]); - LOG(WII_IPC_WIIMOTE, " COD[2]: 0x%02x", pEventRequestConnection->uclass[2]); - LOG(WII_IPC_WIIMOTE, " LinkType: %s", LinkType[pEventRequestConnection->LinkType]); - - return true; -}; - -bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventRequestLinkKey(bdaddr_t _bd) -{ - SQueuedEvent Event(sizeof(SHCIEventRequestLinkKey), 0); - - SHCIEventRequestLinkKey* pEventRequestLinkKey = (SHCIEventRequestLinkKey*)Event.m_buffer; - - pEventRequestLinkKey->EventType = 0x17; - pEventRequestLinkKey->PayloadLength = sizeof(SHCIEventRequestLinkKey) - 2; - pEventRequestLinkKey->bdaddr = _bd; - - AddEventToQueue(Event); - - LOG(WII_IPC_WIIMOTE, "Event: SendEventRequestLinkKey"); - LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", - pEventRequestLinkKey->bdaddr.b[0], pEventRequestLinkKey->bdaddr.b[1], pEventRequestLinkKey->bdaddr.b[2], - pEventRequestLinkKey->bdaddr.b[3], pEventRequestLinkKey->bdaddr.b[4], pEventRequestLinkKey->bdaddr.b[5]); - - return true; -}; - -bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventLinkKeyNotification(const CWII_IPC_HLE_WiiMote& _rWiiMote) -{ - SQueuedEvent Event(sizeof(SHCIEventLinkKeyNotification), 0); - - SHCIEventLinkKeyNotification* pEventLinkKey = (SHCIEventLinkKeyNotification*)Event.m_buffer; - - pEventLinkKey->EventType = 0x15; - pEventLinkKey->PayloadLength = sizeof(SHCIEventLinkKeyNotification) - 2; - pEventLinkKey->numKeys = 1; - pEventLinkKey->bdaddr = _rWiiMote.GetBD(); - memcpy(pEventLinkKey->LinkKey, _rWiiMote.GetLinkKey(), 16); - - AddEventToQueue(Event); - - LOG(WII_IPC_WIIMOTE, "Event: SendEventLinkKeyNotification"); - LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", - pEventLinkKey->bdaddr.b[0], pEventLinkKey->bdaddr.b[1], pEventLinkKey->bdaddr.b[2], - pEventLinkKey->bdaddr.b[3], pEventLinkKey->bdaddr.b[4], pEventLinkKey->bdaddr.b[5]); - LOG_LinkKey(pEventLinkKey->LinkKey); - - return true; -}; - - - -bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventConnectionComplete(bdaddr_t _bd) -{ - CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_bd); - if (pWiiMote == NULL) - { - PanicAlert("SendEventConnectionComplete: Cant find WiiMote by bd: %02x:%02x:%02x:%02x:%02x:%02x", - _bd.b[0], _bd.b[1], _bd.b[2], - _bd.b[3], _bd.b[4], _bd.b[5]); - return false; - } - - SQueuedEvent Event(sizeof(SHCIEventConnectionComplete), 0); - - SHCIEventConnectionComplete* pConnectionComplete = (SHCIEventConnectionComplete*)Event.m_buffer; - - pConnectionComplete->EventType = 0x03; - pConnectionComplete->PayloadLength = sizeof(SHCIEventConnectionComplete) - 2; - pConnectionComplete->Status = 0x00; - pConnectionComplete->Connection_Handle = pWiiMote->GetConnectionHandle(); - pConnectionComplete->bdaddr = pWiiMote->GetBD(); - pConnectionComplete->LinkType = 0x01; // ACL - pConnectionComplete->EncryptionEnabled = 0x00; - - AddEventToQueue(Event); - - CWII_IPC_HLE_WiiMote* pWiimote = AccessWiiMote(_bd); - if (pWiimote) - { - pWiimote->EventConnectionAccepted(); - } - - - g_GlobalHandle = pConnectionComplete->Connection_Handle; - -#ifdef LOGGING - static char s_szLinkType[][128] = - { - { "HCI_LINK_SCO 0x00 - Voice"}, - { "HCI_LINK_ACL 0x01 - Data"}, - { "HCI_LINK_eSCO 0x02 - eSCO"}, - }; -#endif - - LOG(WII_IPC_WIIMOTE, "Event: SendEventConnectionComplete"); - LOG(WII_IPC_WIIMOTE, " Connection_Handle: 0x%04x", pConnectionComplete->Connection_Handle); - LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", - pConnectionComplete->bdaddr.b[0], pConnectionComplete->bdaddr.b[1], pConnectionComplete->bdaddr.b[2], - pConnectionComplete->bdaddr.b[3], pConnectionComplete->bdaddr.b[4], pConnectionComplete->bdaddr.b[5]); - LOG(WII_IPC_WIIMOTE, " LinkType: %s", s_szLinkType[pConnectionComplete->LinkType]); - LOG(WII_IPC_WIIMOTE, " EncryptionEnabled: %i", pConnectionComplete->EncryptionEnabled); - - return true; -} - -bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventRoleChange(bdaddr_t _bd, bool _master) -{ - CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_bd); - if (pWiiMote == NULL) - { - PanicAlert("SendEventRoleChange: Cant find WiiMote by bd: %02x:%02x:%02x:%02x:%02x:%02x", - _bd.b[0], _bd.b[1], _bd.b[2], - _bd.b[3], _bd.b[4], _bd.b[5]); - return false; - } - - SQueuedEvent Event(sizeof(SHCIEventRoleChange), 0); - - SHCIEventRoleChange* pRoleChange = (SHCIEventRoleChange*)Event.m_buffer; - - pRoleChange->EventType = 0x12; - pRoleChange->PayloadLength = sizeof(SHCIEventRoleChange) - 2; - pRoleChange->Status = 0x00; - pRoleChange->bdaddr = pWiiMote->GetBD(); - pRoleChange->NewRole = _master ? 0x00 : 0x01; - - AddEventToQueue(Event); - - LOG(WII_IPC_WIIMOTE, "Event: SendEventRoleChange"); - LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", - pRoleChange->bdaddr.b[0], pRoleChange->bdaddr.b[1], pRoleChange->bdaddr.b[2], - pRoleChange->bdaddr.b[3], pRoleChange->bdaddr.b[4], pRoleChange->bdaddr.b[5]); - LOG(WII_IPC_WIIMOTE, " NewRole: %i", pRoleChange->NewRole); - - return true; -} - - - -bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventReadClockOffsetComplete(u16 _connectionHandle) -{ - CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_connectionHandle); - if (pWiiMote == NULL) - { - PanicAlert("SendEventReadClockOffsetComplete: Cant find WiiMote by connection handle: %02x", _connectionHandle); - return false; - } - - SQueuedEvent Event(sizeof(SHCIEventReadClockOffsetComplete), _connectionHandle); - - SHCIEventReadClockOffsetComplete* pReadClockOffsetComplete = (SHCIEventReadClockOffsetComplete*)Event.m_buffer; - pReadClockOffsetComplete->EventType = 0x1C; - pReadClockOffsetComplete->PayloadLength = sizeof(SHCIEventReadClockOffsetComplete) - 2; - pReadClockOffsetComplete->Status = 0x00; - pReadClockOffsetComplete->ConnectionHandle = pWiiMote->GetConnectionHandle(); - pReadClockOffsetComplete->ClockOffset = 0x3818; - - AddEventToQueue(Event); - - // Log - LOG(WII_IPC_WIIMOTE, "Event: SendEventReadClockOffsetComplete"); - LOG(WII_IPC_WIIMOTE, " Connection_Handle: 0x%04x", pReadClockOffsetComplete->ConnectionHandle); - LOG(WII_IPC_WIIMOTE, " ClockOffset: 0x%04x", pReadClockOffsetComplete->ClockOffset); - - return true; -} - -bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventReadRemoteVerInfo(u16 _connectionHandle) -{ - CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_connectionHandle); - if (pWiiMote == NULL) - { - PanicAlert("SendEventReadRemoteVerInfo: Cant find WiiMote by connection handle: %02x", _connectionHandle); - return false; - } - - SQueuedEvent Event(sizeof(SHCIEventReadRemoteVerInfo), _connectionHandle); - - SHCIEventReadRemoteVerInfo* pReadRemoteVerInfo = (SHCIEventReadRemoteVerInfo*)Event.m_buffer; - pReadRemoteVerInfo->EventType = 0x0C; - pReadRemoteVerInfo->PayloadLength = sizeof(SHCIEventReadRemoteVerInfo) - 2; - pReadRemoteVerInfo->Status = 0x00; - pReadRemoteVerInfo->ConnectionHandle = pWiiMote->GetConnectionHandle(); - pReadRemoteVerInfo->lmp_version = pWiiMote->GetLMPVersion(); - pReadRemoteVerInfo->manufacturer = pWiiMote->GetManufactorID(); - pReadRemoteVerInfo->lmp_subversion = pWiiMote->GetLMPSubVersion(); - - AddEventToQueue(Event); - - // Log - LOG(WII_IPC_WIIMOTE, "Event: SendEventReadRemoteVerInfo"); - LOG(WII_IPC_WIIMOTE, " Connection_Handle: 0x%04x", pReadRemoteVerInfo->ConnectionHandle); - LOG(WII_IPC_WIIMOTE, " lmp_version: 0x%02x", pReadRemoteVerInfo->lmp_version); - LOG(WII_IPC_WIIMOTE, " manufacturer: 0x%04x", pReadRemoteVerInfo->manufacturer); - LOG(WII_IPC_WIIMOTE, " lmp_subversion: 0x%04x", pReadRemoteVerInfo->lmp_subversion); - - return true; -} - -bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventReadRemoteFeatures(u16 _connectionHandle) -{ - CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_connectionHandle); - if (pWiiMote == NULL) - { - PanicAlert("SendEventReadRemoteFeatures: Cant find WiiMote by connection handle: %02x", _connectionHandle); - return false; - } - - SQueuedEvent Event(sizeof(SHCIEventReadRemoteFeatures), _connectionHandle); - - SHCIEventReadRemoteFeatures* pReadRemoteFeatures = (SHCIEventReadRemoteFeatures*)Event.m_buffer; - pReadRemoteFeatures->EventType = 0x0C; - pReadRemoteFeatures->PayloadLength = sizeof(SHCIEventReadRemoteFeatures) - 2; - pReadRemoteFeatures->Status = 0x00; - pReadRemoteFeatures->ConnectionHandle = pWiiMote->GetConnectionHandle(); - pReadRemoteFeatures->features[0] = pWiiMote->GetFeatures()[0]; - pReadRemoteFeatures->features[1] = pWiiMote->GetFeatures()[1]; - pReadRemoteFeatures->features[2] = pWiiMote->GetFeatures()[2]; - pReadRemoteFeatures->features[3] = pWiiMote->GetFeatures()[3]; - pReadRemoteFeatures->features[4] = pWiiMote->GetFeatures()[4]; - pReadRemoteFeatures->features[5] = pWiiMote->GetFeatures()[5]; - pReadRemoteFeatures->features[6] = pWiiMote->GetFeatures()[6]; - pReadRemoteFeatures->features[7] = pWiiMote->GetFeatures()[7]; - - AddEventToQueue(Event); - - // Log - LOG(WII_IPC_WIIMOTE, "Event: SendEventReadRemoteFeatures"); - LOG(WII_IPC_WIIMOTE, " Connection_Handle: 0x%04x", pReadRemoteFeatures->ConnectionHandle); - LOG(WII_IPC_WIIMOTE, " features: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", - pReadRemoteFeatures->features[0], pReadRemoteFeatures->features[1], pReadRemoteFeatures->features[2], - pReadRemoteFeatures->features[3], pReadRemoteFeatures->features[4], pReadRemoteFeatures->features[5], - pReadRemoteFeatures->features[6], pReadRemoteFeatures->features[7]); - - return true; -} - -bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventNumberOfCompletedPackets(u16 _connectionHandle, u16 _count) -{ - CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_connectionHandle); - if (pWiiMote == NULL) - { - PanicAlert("SendEventNumberOfCompletedPackets: Cant find WiiMote by connection handle %02x", _connectionHandle); - return false; - } - - SQueuedEvent Event(sizeof(SHCIEventNumberOfCompletedPackets), 0); // zero, so this packet isnt counted - - SHCIEventNumberOfCompletedPackets* pNumberOfCompletedPackets = (SHCIEventNumberOfCompletedPackets*)Event.m_buffer; - pNumberOfCompletedPackets->EventType = 0x13; - pNumberOfCompletedPackets->PayloadLength = sizeof(SHCIEventNumberOfCompletedPackets) - 2; - pNumberOfCompletedPackets->NumberOfHandles = 1; - pNumberOfCompletedPackets->Connection_Handle = _connectionHandle; - pNumberOfCompletedPackets->Number_Of_Completed_Packets = _count; - - AddEventToQueue(Event); - - // Log - LOGV(WII_IPC_WIIMOTE, 1, "Event: SendEventNumberOfCompletedPackets"); - LOGV(WII_IPC_WIIMOTE, 1, " Connection_Handle: 0x%04x", pNumberOfCompletedPackets->Connection_Handle); - LOGV(WII_IPC_WIIMOTE, 1, " Number_Of_Completed_Packets: %i", pNumberOfCompletedPackets->Number_Of_Completed_Packets); - - return true; -} - -bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventAuthenticationCompleted(u16 _connectionHandle) -{ - CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_connectionHandle); - if (pWiiMote == NULL) - { - PanicAlert("SendEventAuthenticationCompleted: Cant find WiiMote by connection handle %02x", _connectionHandle); - return false; - } - - SQueuedEvent Event(sizeof(SHCIEventAuthenticationCompleted), _connectionHandle); - - SHCIEventAuthenticationCompleted* pEventAuthenticationCompleted = (SHCIEventAuthenticationCompleted*)Event.m_buffer; - pEventAuthenticationCompleted->EventType = 0x06; - pEventAuthenticationCompleted->PayloadLength = sizeof(SHCIEventAuthenticationCompleted) - 2; - pEventAuthenticationCompleted->Status = 0; - pEventAuthenticationCompleted->Connection_Handle = _connectionHandle; - - AddEventToQueue(Event); - - // Log - LOGV(WII_IPC_WIIMOTE, 1, "Event: SendEventAuthenticationCompleted"); - LOGV(WII_IPC_WIIMOTE, 1, " Connection_Handle: 0x%04x", pEventAuthenticationCompleted->Connection_Handle); - - return true; -} - -bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventModeChange(u16 _connectionHandle, u8 _mode, u16 _value) -{ - CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_connectionHandle); - if (pWiiMote == NULL) - { - PanicAlert("SendEventModeChange: Cant find WiiMote by connection handle %02x", _connectionHandle); - return false; - } - - SQueuedEvent Event(sizeof(SHCIEventModeChange), _connectionHandle); - - SHCIEventModeChange* pModeChange = (SHCIEventModeChange*)Event.m_buffer; - pModeChange->EventType = 0x14; - pModeChange->PayloadLength = sizeof(SHCIEventModeChange) - 2; - pModeChange->Status = 0; - pModeChange->Connection_Handle = _connectionHandle; - pModeChange->CurrentMode = _mode; - pModeChange->Value = _value; - - AddEventToQueue(Event); - - // Log - LOG(WII_IPC_WIIMOTE, "Event: SendEventModeChange"); - LOG(WII_IPC_WIIMOTE, " Connection_Handle: 0x%04x", pModeChange->Connection_Handle); - LOG(WII_IPC_WIIMOTE, " missing other paramter :)"); - - return true; -} - -bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventDisconnect(u16 _connectionHandle, u8 _Reason) -{ - CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_connectionHandle); - if (pWiiMote == NULL) - { - PanicAlert("SendEventDisconnect: Cant find WiiMote by connection handle %02x", _connectionHandle); - return false; - } - - SQueuedEvent Event(sizeof(SHCIEventDisconnectCompleted), _connectionHandle); - - SHCIEventDisconnectCompleted* pDisconnect = (SHCIEventDisconnectCompleted*)Event.m_buffer; - pDisconnect->EventType = 0x06; - pDisconnect->PayloadLength = sizeof(SHCIEventDisconnectCompleted) - 2; - pDisconnect->Status = 0; - pDisconnect->Connection_Handle = _connectionHandle; - pDisconnect->Reason = _Reason; - - AddEventToQueue(Event); - - // Log - LOG(WII_IPC_WIIMOTE, "Event: SendEventDisconnect"); - LOG(WII_IPC_WIIMOTE, " Connection_Handle: 0x%04x", pDisconnect->Connection_Handle); - LOG(WII_IPC_WIIMOTE, " Reason: 0x%02x", pDisconnect->Reason); - - return true; -} - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// Command dispacther -// ----------------- -// This is called from the USB_IOCTL_HCI_COMMAND_MESSAGE Ioctlv -// -// -//////////////////////////////////////////////////////////////////////////////////////////////////// - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::ExecuteHCICommandMessage(const SHCICommandMessage& _rHCICommandMessage) -{ - u8* pInput = Memory::GetPointer(_rHCICommandMessage.m_PayLoadAddr + 3); - SCommandMessage* pMsg = (SCommandMessage*)Memory::GetPointer(_rHCICommandMessage.m_PayLoadAddr); - - u16 ocf = HCI_OCF(pMsg->Opcode); - u16 ogf = HCI_OGF(pMsg->Opcode); - LOG(WII_IPC_WIIMOTE, "******************************"); - LOG(WII_IPC_WIIMOTE, "ExecuteHCICommandMessage(0x%04x)(ocf: 0x%02x, ogf: 0x%02x)", - pMsg->Opcode, ocf, ogf); - - switch(pMsg->Opcode) - { - // - // --- read commandos --- - // - case HCI_CMD_RESET: - CommandReset(pInput); - break; - - case HCI_CMD_READ_BUFFER_SIZE: - CommandReadBufferSize(pInput); - break; - - case HCI_CMD_READ_LOCAL_VER: - CommandReadLocalVer(pInput); - break; - - case HCI_CMD_READ_BDADDR: - CommandReadBDAdrr(pInput); - break; - - case HCI_CMD_READ_LOCAL_FEATURES: - CommandReadLocalFeatures(pInput); - break; - - case HCI_CMD_READ_STORED_LINK_KEY: - CommandReadStoredLinkKey(pInput); - break; - - case HCI_CMD_WRITE_UNIT_CLASS: - CommandWriteUnitClass(pInput); - break; - - case HCI_CMD_WRITE_LOCAL_NAME: - CommandWriteLocalName(pInput); - break; - - case HCI_CMD_WRITE_PIN_TYPE: - CommandWritePinType(pInput); - break; - - case HCI_CMD_HOST_BUFFER_SIZE: - CommandHostBufferSize(pInput); - break; - - case HCI_CMD_WRITE_PAGE_TIMEOUT: - CommandWritePageTimeOut(pInput); - break; - - case HCI_CMD_WRITE_SCAN_ENABLE: - CommandWriteScanEnable(pInput); - break; - - case HCI_CMD_WRITE_INQUIRY_MODE: - CommandWriteInquiryMode(pInput); - break; - - case HCI_CMD_WRITE_PAGE_SCAN_TYPE: - CommandWritePageScanType(pInput); - break; - - case HCI_CMD_SET_EVENT_FILTER: - CommandSetEventFilter(pInput); - break; - - case HCI_CMD_INQUIRY: - CommandInquiry(pInput); - break; - - case HCI_CMD_WRITE_INQUIRY_SCAN_TYPE: - CommandWriteInquiryScanType(pInput); - break; - - // vendor specific... - case 0xFC4C: - CommandVendorSpecific_FC4C(pInput, _rHCICommandMessage.m_PayLoadSize - 3); - break; - - case 0xFC4F: - CommandVendorSpecific_FC4F(pInput, _rHCICommandMessage.m_PayLoadSize - 3); - break; - - case HCI_CMD_INQUIRY_CANCEL: - CommandInquiryCancel(pInput); - break; - - case HCI_CMD_REMOTE_NAME_REQ: - CommandRemoteNameReq(pInput); - break; - - case HCI_CMD_CREATE_CON: - CommandCreateCon(pInput); - break; - - case HCI_CMD_ACCEPT_CON: - CommandAcceptCon(pInput); - break; - - case HCI_CMD_READ_CLOCK_OFFSET: - CommandReadClockOffset(pInput); - break; - - case HCI_CMD_READ_REMOTE_VER_INFO: - CommandReadRemoteVerInfo(pInput); - break; - - case HCI_CMD_READ_REMOTE_FEATURES: - CommandReadRemoteFeatures(pInput); - break; - - case HCI_CMD_WRITE_LINK_POLICY_SETTINGS: - CommandWriteLinkPolicy(pInput); - break; - - case HCI_CMD_AUTH_REQ: - CommandAuthenticationRequested(pInput); - break; - - case HCI_CMD_SNIFF_MODE: - CommandSniffMode(pInput); - break; - - case HCI_CMD_DISCONNECT: - CommandDisconnect(pInput); - break; - - case HCI_CMD_WRITE_LINK_SUPERVISION_TIMEOUT: - CommandWriteLinkSupervisionTimeout(pInput); - break; - - case HCI_CMD_LINK_KEY_NEG_REP: - CommandLinkKeyNegRep(pInput); - break; - - case HCI_CMD_LINK_KEY_REP: - CommandLinkKeyRep(pInput); - break; - - - // - // --- default --- - // - default: - { - u16 _ocf = HCI_OCF(pMsg->Opcode); - u16 _ogf = HCI_OGF(pMsg->Opcode); - - if (_ogf == 0x3f) - { - PanicAlert("Vendor specific HCI command"); - LOG(WII_IPC_WIIMOTE, "Command: vendor specific: 0x%04X (ocf: 0x%x)", pMsg->Opcode, _ocf); - - for (int i=0; ilen; i++) - { - LOG(WII_IPC_WIIMOTE, " 0x02%x", pInput[i]); - } - } - else - { - _dbg_assert_msg_(WII_IPC_WIIMOTE, 0, "Unknown USB_IOCTL_CTRLMSG: 0x%04X (ocf: 0x%x ogf 0x%x)", pMsg->Opcode, _ocf, _ogf); - } - - // send fake all is okay msg... - SendEventCommandComplete(pMsg->Opcode, NULL, 0); - } - break; - } -} - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// -// -// --- command helper -// -// -//////////////////////////////////////////////////////////////////////////////////////////////////// - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandReset(u8* _Input) -{ - // reply - hci_status_rp Reply; - Reply.status = 0x00; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_RESET"); - - SendEventCommandComplete(HCI_CMD_RESET, &Reply, sizeof(hci_status_rp)); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandReadBufferSize(u8* _Input) -{ - // reply - hci_read_buffer_size_rp Reply; - Reply.status = 0x00; - Reply.max_acl_size = 339; - Reply.num_acl_pkts = 10; - Reply.max_sco_size = 64; - Reply.num_sco_pkts = 0; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_READ_BUFFER_SIZE:"); - LOG(WII_IPC_WIIMOTE, "return:"); - LOG(WII_IPC_WIIMOTE, " max_acl_size: %i", Reply.max_acl_size); - LOG(WII_IPC_WIIMOTE, " num_acl_pkts: %i", Reply.num_acl_pkts); - LOG(WII_IPC_WIIMOTE, " max_sco_size: %i", Reply.max_sco_size); - LOG(WII_IPC_WIIMOTE, " num_sco_pkts: %i", Reply.num_sco_pkts); - - SendEventCommandComplete(HCI_CMD_READ_BUFFER_SIZE, &Reply, sizeof(hci_read_buffer_size_rp)); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandReadLocalVer(u8* _Input) -{ - // reply - hci_read_local_ver_rp Reply; - Reply.status = 0x00; - Reply.hci_version = 0x03; // HCI version: 1.1 - Reply.hci_revision = 0x40a7; // current revision (?) - Reply.lmp_version = 0x03; // LMP version: 1.1 - Reply.manufacturer = 0x000F; // manufacturer: reserved for tests - Reply.lmp_subversion = 0x430e; // LMP subversion - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_READ_LOCAL_VER:"); - LOG(WII_IPC_WIIMOTE, "return:"); - LOG(WII_IPC_WIIMOTE, " status: %i", Reply.status); - LOG(WII_IPC_WIIMOTE, " hci_revision: %i", Reply.hci_revision); - LOG(WII_IPC_WIIMOTE, " lmp_version: %i", Reply.lmp_version); - LOG(WII_IPC_WIIMOTE, " manufacturer: %i", Reply.manufacturer); - LOG(WII_IPC_WIIMOTE, " lmp_subversion: %i", Reply.lmp_subversion); - - SendEventCommandComplete(HCI_CMD_READ_LOCAL_VER, &Reply, sizeof(hci_read_local_ver_rp)); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandReadBDAdrr(u8* _Input) -{ - // reply - hci_read_bdaddr_rp Reply; - Reply.status = 0x00; - Reply.bdaddr = m_ControllerBD; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_READ_BDADDR:"); - LOG(WII_IPC_WIIMOTE, "return:"); - LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", - Reply.bdaddr.b[0], Reply.bdaddr.b[1], Reply.bdaddr.b[2], - Reply.bdaddr.b[3], Reply.bdaddr.b[4], Reply.bdaddr.b[5]); - - SendEventCommandComplete(HCI_CMD_READ_BDADDR, &Reply, sizeof(hci_read_bdaddr_rp)); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandReadLocalFeatures(u8* _Input) -{ - // reply - hci_read_local_features_rp Reply; - Reply.status = 0x00; - Reply.features[0] = 0xFF; - Reply.features[1] = 0xFF; - Reply.features[2] = 0x8D; - Reply.features[3] = 0xFE; - Reply.features[4] = 0x9B; - Reply.features[5] = 0xF9; - Reply.features[6] = 0x00; - Reply.features[7] = 0x80; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_READ_LOCAL_FEATURES:"); - LOG(WII_IPC_WIIMOTE, "return:"); - LOG(WII_IPC_WIIMOTE, " features: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", - Reply.features[0], Reply.features[1], Reply.features[2], - Reply.features[3], Reply.features[4], Reply.features[5], - Reply.features[6], Reply.features[7]); - - SendEventCommandComplete(HCI_CMD_READ_LOCAL_FEATURES, &Reply, sizeof(hci_read_local_features_rp)); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandReadStoredLinkKey(u8* _Input) -{ - hci_read_stored_link_key_cp* ReadStoredLinkKey = (hci_read_stored_link_key_cp*)_Input; - - // reply - hci_read_stored_link_key_rp Reply; - Reply.status = 0x00; - - Reply.max_num_keys = 255; - if (ReadStoredLinkKey->read_all) - { - Reply.num_keys_read = (u16)m_WiiMotes.size(); - } - else - { - PanicAlert("CommandReadStoredLinkKey"); - } - - // logging - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_READ_STORED_LINK_KEY:"); - LOG(WII_IPC_WIIMOTE, "input:"); - LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", - ReadStoredLinkKey->bdaddr.b[0], ReadStoredLinkKey->bdaddr.b[1], ReadStoredLinkKey->bdaddr.b[2], - ReadStoredLinkKey->bdaddr.b[3], ReadStoredLinkKey->bdaddr.b[4], ReadStoredLinkKey->bdaddr.b[5]); - LOG(WII_IPC_WIIMOTE, " read_all: %i", ReadStoredLinkKey->read_all); - LOG(WII_IPC_WIIMOTE, "return:"); - LOG(WII_IPC_WIIMOTE, " max_num_keys: %i", Reply.max_num_keys); - LOG(WII_IPC_WIIMOTE, " num_keys_read: %i", Reply.num_keys_read); - - // generate link key - for (size_t i=0; iuclass[0]; - m_ClassOfDevice[1] = pWriteUnitClass->uclass[1]; - m_ClassOfDevice[2] = pWriteUnitClass->uclass[2]; - - // reply - hci_write_unit_class_rp Reply; - Reply.status = 0x00; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_WRITE_UNIT_CLASS:"); - LOG(WII_IPC_WIIMOTE, "write:"); - LOG(WII_IPC_WIIMOTE, " COD[0]: 0x%02x", pWriteUnitClass->uclass[0]); - LOG(WII_IPC_WIIMOTE, " COD[1]: 0x%02x", pWriteUnitClass->uclass[1]); - LOG(WII_IPC_WIIMOTE, " COD[2]: 0x%02x", pWriteUnitClass->uclass[2]); - - SendEventCommandComplete(HCI_CMD_WRITE_UNIT_CLASS, &Reply, sizeof(hci_write_unit_class_rp)); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandWriteLocalName(u8* _Input) -{ - // command parameters - hci_write_local_name_cp* pWriteLocalName = (hci_write_local_name_cp*)_Input; - memcpy(m_LocalName, pWriteLocalName->name, HCI_UNIT_NAME_SIZE); - - // reply - hci_write_local_name_rp Reply; - Reply.status = 0x00; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_WRITE_LOCAL_NAME:"); - LOG(WII_IPC_WIIMOTE, "write:"); - LOG(WII_IPC_WIIMOTE, " local_name: %s", pWriteLocalName->name); - - SendEventCommandComplete(HCI_CMD_WRITE_LOCAL_NAME, &Reply, sizeof(hci_write_local_name_rp)); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandWritePinType(u8* _Input) -{ - // command parameters - hci_write_pin_type_cp* pWritePinType = (hci_write_pin_type_cp*)_Input; - m_PINType = pWritePinType->pin_type; - - // reply - hci_write_pin_type_rp Reply; - Reply.status = 0x00; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_WRITE_PIN_TYPE:"); - LOG(WII_IPC_WIIMOTE, "write:"); - LOG(WII_IPC_WIIMOTE, " pin_type: %x", pWritePinType->pin_type); - - SendEventCommandComplete(HCI_CMD_WRITE_PIN_TYPE, &Reply, sizeof(hci_write_pin_type_rp)); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandHostBufferSize(u8* _Input) -{ - // command parameters - hci_host_buffer_size_cp* pHostBufferSize = (hci_host_buffer_size_cp*)_Input; - m_HostMaxACLSize = pHostBufferSize->max_acl_size; - m_HostMaxSCOSize = pHostBufferSize->max_sco_size; - m_HostNumACLPackets = pHostBufferSize->num_acl_pkts; - m_HostNumSCOPackets = pHostBufferSize->num_sco_pkts; - - // reply - hci_host_buffer_size_rp Reply; - Reply.status = 0x00; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_HOST_BUFFER_SIZE:"); - LOG(WII_IPC_WIIMOTE, "write:"); - LOG(WII_IPC_WIIMOTE, " max_acl_size: %i", pHostBufferSize->max_acl_size); - LOG(WII_IPC_WIIMOTE, " max_sco_size: %i", pHostBufferSize->max_sco_size); - LOG(WII_IPC_WIIMOTE, " num_acl_pkts: %i", pHostBufferSize->num_acl_pkts); - LOG(WII_IPC_WIIMOTE, " num_sco_pkts: %i", pHostBufferSize->num_sco_pkts); - - SendEventCommandComplete(HCI_CMD_HOST_BUFFER_SIZE, &Reply, sizeof(hci_host_buffer_size_rp)); -} - - -// =================================================== -/* Here we normally receive the timeout interval. But not from homebrew games that use - lwbt. Why not? */ -// ---------------- -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandWritePageTimeOut(u8* _Input) -{ -#ifdef LOGGING - // command parameters - hci_write_page_timeout_cp* pWritePageTimeOut = (hci_write_page_timeout_cp*)_Input; -#endif - - // reply - hci_host_buffer_size_rp Reply; - Reply.status = 0x00; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_WRITE_PAGE_TIMEOUT:"); - LOG(WII_IPC_WIIMOTE, "write:"); - LOG(WII_IPC_WIIMOTE, " timeout: %i", pWritePageTimeOut->timeout); - - SendEventCommandComplete(HCI_CMD_WRITE_PAGE_TIMEOUT, &Reply, sizeof(hci_host_buffer_size_rp)); -} - - - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandWriteScanEnable(u8* _Input) -{ - // Command parameters - hci_write_scan_enable_cp* pWriteScanEnable = (hci_write_scan_enable_cp*)_Input; - m_ScanEnable = pWriteScanEnable->scan_enable; - - // Reply - hci_write_scan_enable_rp Reply; - Reply.status = 0x00; - -#ifdef LOGGING - static char Scanning[][128] = - { - { "HCI_NO_SCAN_ENABLE"}, - { "HCI_INQUIRY_SCAN_ENABLE"}, - { "HCI_PAGE_SCAN_ENABLE"}, - { "HCI_INQUIRY_AND_PAGE_SCAN_ENABLE"}, - }; -#endif - - LOGV(WII_IPC_WIIMOTE, 0, "Command: HCI_CMD_WRITE_SCAN_ENABLE:"); - LOGV(WII_IPC_WIIMOTE, 0, "write:"); - LOGV(WII_IPC_WIIMOTE, 0, " scan_enable: %s", Scanning[pWriteScanEnable->scan_enable]); - - SendEventCommandComplete(HCI_CMD_WRITE_SCAN_ENABLE, &Reply, sizeof(hci_write_scan_enable_rp)); -} - - - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandWriteInquiryMode(u8* _Input) -{ -#ifdef LOGGING - // command parameters - hci_write_inquiry_mode_cp* pInquiryMode = (hci_write_inquiry_mode_cp*)_Input; -#endif - - // reply - hci_write_inquiry_mode_rp Reply; - Reply.status = 0x00; - -#ifdef LOGGING - static char InquiryMode[][128] = - { - { "Standard Inquiry Result event format (default)" }, - { "Inquiry Result format with RSSI" }, - { "Inquiry Result with RSSI format or Extended Inquiry Result format" } - }; -#endif - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_WRITE_INQUIRY_MODE:"); - LOG(WII_IPC_WIIMOTE, "write:"); - LOG(WII_IPC_WIIMOTE, " mode: %s", InquiryMode[pInquiryMode->mode]); - - SendEventCommandComplete(HCI_CMD_WRITE_INQUIRY_MODE, &Reply, sizeof(hci_write_inquiry_mode_rp)); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandWritePageScanType(u8* _Input) -{ -#ifdef LOGGING - // command parameters - hci_write_page_scan_type_cp* pWritePageScanType = (hci_write_page_scan_type_cp*)_Input; -#endif - - // reply - hci_write_page_scan_type_rp Reply; - Reply.status = 0x00; - -#ifdef LOGGING - static char PageScanType[][128] = - { - { "Mandatory: Standard Scan (default)" }, - { "Optional: Interlaced Scan" } - }; -#endif - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_WRITE_PAGE_SCAN_TYPE:"); - LOG(WII_IPC_WIIMOTE, "write:"); - LOG(WII_IPC_WIIMOTE, " type: %s", PageScanType[pWritePageScanType->type]); - - SendEventCommandComplete(HCI_CMD_WRITE_PAGE_SCAN_TYPE, &Reply, sizeof(hci_write_page_scan_type_rp)); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandSetEventFilter(u8* _Input) -{ - // command parameters - hci_set_event_filter_cp* pSetEventFilter = (hci_set_event_filter_cp*)_Input; - m_EventFilterType = pSetEventFilter->filter_type; - m_EventFilterCondition = pSetEventFilter->filter_condition_type; - - // reply - hci_set_event_filter_rp Reply; - Reply.status = 0x00; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_SET_EVENT_FILTER:"); - LOG(WII_IPC_WIIMOTE, "write:"); - LOG(WII_IPC_WIIMOTE, " filter_type: %i", pSetEventFilter->filter_type); - LOG(WII_IPC_WIIMOTE, " filter_condition_type: %i", pSetEventFilter->filter_condition_type); - - SendEventCommandComplete(HCI_CMD_SET_EVENT_FILTER, &Reply, sizeof(hci_set_event_filter_rp)); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandInquiry(u8* _Input) -{ - // command parameters - hci_inquiry_cp* pInquiry = (hci_inquiry_cp*)_Input; - u8 lap[HCI_LAP_SIZE]; - - memcpy(lap, pInquiry->lap, HCI_LAP_SIZE); - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_INQUIRY:"); - LOG(WII_IPC_WIIMOTE, "write:"); - LOG(WII_IPC_WIIMOTE, " LAP[0]: 0x%02x", pInquiry->lap[0]); - LOG(WII_IPC_WIIMOTE, " LAP[1]: 0x%02x", pInquiry->lap[1]); - LOG(WII_IPC_WIIMOTE, " LAP[2]: 0x%02x", pInquiry->lap[2]); - LOG(WII_IPC_WIIMOTE, " inquiry_length: %i (N x 1.28) sec", pInquiry->inquiry_length); - LOG(WII_IPC_WIIMOTE, " num_responses: %i (N x 1.28) sec", pInquiry->num_responses); - - SendEventCommandStatus(HCI_CMD_INQUIRY); - SendEventInquiryResponse(); - SendEventInquiryComplete(); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandWriteInquiryScanType(u8* _Input) -{ -#ifdef LOGGING - // command parameters - hci_write_inquiry_scan_type_cp* pSetEventFilter = (hci_write_inquiry_scan_type_cp*)_Input; -#endif - // reply - hci_write_inquiry_scan_type_rp Reply; - Reply.status = 0x00; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_WRITE_INQUIRY_SCAN_TYPE:"); - LOG(WII_IPC_WIIMOTE, "write:"); - LOG(WII_IPC_WIIMOTE, " type: %i", pSetEventFilter->type); - - SendEventCommandComplete(HCI_CMD_WRITE_INQUIRY_SCAN_TYPE, &Reply, sizeof(hci_write_inquiry_scan_type_rp)); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandVendorSpecific_FC4F(u8* _Input, u32 _Size) -{ - // callstack... - // BTM_VendorSpecificCommad() - // WUDiRemovePatch() - // WUDiAppendRuntimePatch() - // WUDiGetFirmwareVersion() - // WUDiStackSetupComplete() - - // reply - hci_status_rp Reply; - Reply.status = 0x00; - - LOG(WII_IPC_WIIMOTE, "Command: CommandVendorSpecific_FC4F: (callstack WUDiRemovePatch)"); - LOG(WII_IPC_WIIMOTE, "input (size 0x%x):", _Size); - - Debugger::PrintDataBuffer(LogTypes::WII_IPC_WIIMOTE, _Input, _Size, "Data: "); - - SendEventCommandComplete(0xFC4F, &Reply, sizeof(hci_status_rp)); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandVendorSpecific_FC4C(u8* _Input, u32 _Size) -{ - // reply - hci_status_rp Reply; - Reply.status = 0x00; - - LOG(WII_IPC_WIIMOTE, "Command: CommandVendorSpecific_FC4C:"); - LOG(WII_IPC_WIIMOTE, "input (size 0x%x):", _Size); - Debugger::PrintDataBuffer(LogTypes::WII_IPC_WIIMOTE, _Input, _Size, "Data: "); - - SendEventCommandComplete(0xFC4C, &Reply, sizeof(hci_status_rp)); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandInquiryCancel(u8* _Input) -{ - // reply - hci_inquiry_cancel_rp Reply; - Reply.status = 0x00; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_INQUIRY_CANCEL"); - - SendEventCommandComplete(HCI_CMD_INQUIRY_CANCEL, &Reply, sizeof(hci_inquiry_cancel_rp)); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandRemoteNameReq(u8* _Input) -{ - // command parameters - hci_remote_name_req_cp* pRemoteNameReq = (hci_remote_name_req_cp*)_Input; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_REMOTE_NAME_REQ"); - LOG(WII_IPC_WIIMOTE, "Input:"); - LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", - pRemoteNameReq->bdaddr.b[0], pRemoteNameReq->bdaddr.b[1], pRemoteNameReq->bdaddr.b[2], - pRemoteNameReq->bdaddr.b[3], pRemoteNameReq->bdaddr.b[4], pRemoteNameReq->bdaddr.b[5]); - LOG(WII_IPC_WIIMOTE, " page_scan_rep_mode: %i", pRemoteNameReq->page_scan_rep_mode); - LOG(WII_IPC_WIIMOTE, " page_scan_mode: %i", pRemoteNameReq->page_scan_mode); - LOG(WII_IPC_WIIMOTE, " clock_offset: %i", pRemoteNameReq->clock_offset); - - SendEventCommandStatus(HCI_CMD_REMOTE_NAME_REQ); - SendEventRemoteNameReq(pRemoteNameReq->bdaddr); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandCreateCon(u8* _Input) -{ - // command parameters - hci_create_con_cp* pCreateCon = (hci_create_con_cp*)_Input; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_CREATE_CON"); - LOG(WII_IPC_WIIMOTE, "Input:"); - LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", - pCreateCon->bdaddr.b[0], pCreateCon->bdaddr.b[1], pCreateCon->bdaddr.b[2], - pCreateCon->bdaddr.b[3], pCreateCon->bdaddr.b[4], pCreateCon->bdaddr.b[5]); - - LOG(WII_IPC_WIIMOTE, " pkt_type: %i", pCreateCon->pkt_type); - LOG(WII_IPC_WIIMOTE, " page_scan_rep_mode: %i", pCreateCon->page_scan_rep_mode); - LOG(WII_IPC_WIIMOTE, " page_scan_mode: %i", pCreateCon->page_scan_mode); - LOG(WII_IPC_WIIMOTE, " clock_offset: %i", pCreateCon->clock_offset); - LOG(WII_IPC_WIIMOTE, " accept_role_switch: %i", pCreateCon->accept_role_switch); - - SendEventCommandStatus(HCI_CMD_CREATE_CON); - SendEventConnectionComplete(pCreateCon->bdaddr); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandAcceptCon(u8* _Input) -{ - // command parameters - hci_accept_con_cp* pAcceptCon = (hci_accept_con_cp*)_Input; - -#ifdef LOGGING - static char s_szRole[][128] = - { - { "Master (0x00)"}, - { "Slave (0x01)"}, - }; -#endif - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_ACCEPT_CON"); - LOG(WII_IPC_WIIMOTE, "Input:"); - LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", - pAcceptCon->bdaddr.b[0], pAcceptCon->bdaddr.b[1], pAcceptCon->bdaddr.b[2], - pAcceptCon->bdaddr.b[3], pAcceptCon->bdaddr.b[4], pAcceptCon->bdaddr.b[5]); - LOG(WII_IPC_WIIMOTE, " role: %s", s_szRole[pAcceptCon->role]); - - SendEventCommandStatus(HCI_CMD_ACCEPT_CON); - - // this connection wants to be the master - if (pAcceptCon->role == 0) - { - SendEventRoleChange(pAcceptCon->bdaddr, true); - } - - SendEventConnectionComplete(pAcceptCon->bdaddr); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandReadClockOffset(u8* _Input) -{ - // command parameters - hci_read_clock_offset_cp* pReadClockOffset = (hci_read_clock_offset_cp*)_Input; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_READ_CLOCK_OFFSET"); - LOG(WII_IPC_WIIMOTE, "Input:"); - LOG(WII_IPC_WIIMOTE, " ConnectionHandle: 0x%02x", pReadClockOffset->con_handle); - - SendEventCommandStatus(HCI_CMD_READ_CLOCK_OFFSET); - SendEventReadClockOffsetComplete(pReadClockOffset->con_handle); - - -// CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(pReadClockOffset->con_handle); -// SendEventRequestLinkKey(pWiiMote->GetBD()); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandReadRemoteVerInfo(u8* _Input) -{ - // command parameters - hci_read_remote_ver_info_cp* pReadRemoteVerInfo = (hci_read_remote_ver_info_cp*)_Input; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_READ_REMOTE_VER_INFO"); - LOG(WII_IPC_WIIMOTE, "Input:"); - LOG(WII_IPC_WIIMOTE, " ConnectionHandle: 0x%02x", pReadRemoteVerInfo->con_handle); - - SendEventCommandStatus(HCI_CMD_READ_REMOTE_VER_INFO); - SendEventReadRemoteVerInfo(pReadRemoteVerInfo->con_handle); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandReadRemoteFeatures(u8* _Input) -{ - // command parameters - hci_read_remote_features_cp* pReadRemoteFeatures = (hci_read_remote_features_cp*)_Input; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_READ_REMOTE_FEATURES"); - LOG(WII_IPC_WIIMOTE, "Input:"); - LOG(WII_IPC_WIIMOTE, " ConnectionHandle: 0x%04x", pReadRemoteFeatures->con_handle); - - SendEventCommandStatus(HCI_CMD_READ_REMOTE_FEATURES); - SendEventReadRemoteFeatures(pReadRemoteFeatures->con_handle); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandWriteLinkPolicy(u8* _Input) -{ - // command parameters - hci_write_link_policy_settings_cp* pLinkPolicy = (hci_write_link_policy_settings_cp*)_Input; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_WRITE_LINK_POLICY_SETTINGS"); - LOG(WII_IPC_WIIMOTE, "Input:"); - LOG(WII_IPC_WIIMOTE, " ConnectionHandle: 0x%04x", pLinkPolicy->con_handle); - LOG(WII_IPC_WIIMOTE, " Policy: 0x%04x", pLinkPolicy->settings); - - SendEventCommandStatus(HCI_CMD_WRITE_LINK_POLICY_SETTINGS); - - CWII_IPC_HLE_WiiMote* pWiimote = AccessWiiMote(pLinkPolicy->con_handle); - if (pWiimote) - { - pWiimote->EventCommandWriteLinkPolicy(); - } -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandAuthenticationRequested(u8* _Input) -{ - // command parameters - hci_auth_req_cp* pAuthReq = (hci_auth_req_cp*)_Input; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_AUTH_REQ"); - LOG(WII_IPC_WIIMOTE, "Input:"); - LOG(WII_IPC_WIIMOTE, " ConnectionHandle: 0x%04x", pAuthReq->con_handle); - - SendEventCommandStatus(HCI_CMD_AUTH_REQ); - SendEventAuthenticationCompleted(pAuthReq->con_handle); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandSniffMode(u8* _Input) -{ - // command parameters - hci_sniff_mode_cp* pSniffMode = (hci_sniff_mode_cp*)_Input; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_SNIFF_MODE"); - LOG(WII_IPC_WIIMOTE, "Input:"); - LOG(WII_IPC_WIIMOTE, " ConnectionHandle: 0x%04x", pSniffMode->con_handle); - LOG(WII_IPC_WIIMOTE, " max_interval: 0x%04x", pSniffMode->max_interval); - LOG(WII_IPC_WIIMOTE, " min_interval: 0x%04x", pSniffMode->min_interval); - LOG(WII_IPC_WIIMOTE, " attempt: 0x%04x", pSniffMode->attempt); - LOG(WII_IPC_WIIMOTE, " timeout: 0x%04x", pSniffMode->timeout); - - SendEventCommandStatus(HCI_CMD_SNIFF_MODE); - SendEventModeChange(pSniffMode->con_handle, 0x02, pSniffMode->max_interval); // 0x02 - sniff mode -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandDisconnect(u8* _Input) -{ - // command parameters - hci_discon_cp* pDiscon = (hci_discon_cp*)_Input; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_DISCONNECT"); - LOG(WII_IPC_WIIMOTE, "Input:"); - LOG(WII_IPC_WIIMOTE, " ConnectionHandle: 0x%04x", pDiscon->con_handle); - LOG(WII_IPC_WIIMOTE, " Reason: 0x%02x", pDiscon->reason); - - SendEventCommandStatus(HCI_CMD_DISCONNECT); - SendEventDisconnect(pDiscon->con_handle, pDiscon->reason); - - CWII_IPC_HLE_WiiMote* pWiimote = AccessWiiMote(pDiscon->con_handle); - if (pWiimote) - { - pWiimote->EventDisconnect(); - } - - static bool OneShotMessage = true; - if (OneShotMessage) - { - OneShotMessage = false; - PanicAlert("IPC CommandDisconnect: WiiMote emulation is out of sync.\n" - "This message will be shot one time only, because dolphin does\n" - "not executes the disconnect at all and some times you can play\n" - "anyway. It is strongly recommed to save and/or restart the\n" - "emulation."); - } -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandWriteLinkSupervisionTimeout(u8* _Input) -{ - // command parameters - hci_write_link_supervision_timeout_cp* pSuperVision = (hci_write_link_supervision_timeout_cp*)_Input; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_OCF_WRITE_LINK_SUPERVISION_TIMEOUT"); - LOG(WII_IPC_WIIMOTE, "Input:"); - LOG(WII_IPC_WIIMOTE, " con_handle: 0x%04x", pSuperVision->con_handle); - LOG(WII_IPC_WIIMOTE, " timeout: 0x%02x", pSuperVision->timeout); - - hci_write_link_supervision_timeout_rp Reply; - Reply.status = 0x00; - Reply.con_handle = pSuperVision->con_handle; - - SendEventCommandComplete(HCI_OCF_WRITE_LINK_SUPERVISION_TIMEOUT, &Reply, sizeof(hci_write_link_supervision_timeout_rp)); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandLinkKeyNegRep(u8* _Input) -{ - // command parameters - hci_link_key_neg_rep_cp* pKeyNeg = (hci_link_key_neg_rep_cp*)_Input; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_LINK_KEY_NEG_REP"); - LOG(WII_IPC_WIIMOTE, "Input:"); - LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", - pKeyNeg->bdaddr.b[0], pKeyNeg->bdaddr.b[1], pKeyNeg->bdaddr.b[2], - pKeyNeg->bdaddr.b[3], pKeyNeg->bdaddr.b[4], pKeyNeg->bdaddr.b[5]); - - hci_link_key_neg_rep_rp Reply; - Reply.status = 0x00; - Reply.bdaddr = pKeyNeg->bdaddr; - - SendEventCommandComplete(HCI_OCF_WRITE_LINK_SUPERVISION_TIMEOUT, &Reply, sizeof(hci_link_key_neg_rep_rp)); -} - -void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandLinkKeyRep(u8* _Input) -{ - // command parameters - hci_link_key_rep_cp* pKeyRep = (hci_link_key_rep_cp*)_Input; - - LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_LINK_KEY_REP"); - LOG(WII_IPC_WIIMOTE, "Input:"); - LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", - pKeyRep->bdaddr.b[0], pKeyRep->bdaddr.b[1], pKeyRep->bdaddr.b[2], - pKeyRep->bdaddr.b[3], pKeyRep->bdaddr.b[4], pKeyRep->bdaddr.b[5]); - LOG_LinkKey(pKeyRep->key); - - - hci_link_key_rep_rp Reply; - Reply.status = 0x00; - Reply.bdaddr = pKeyRep->bdaddr; - - SendEventCommandComplete(HCI_CMD_LINK_KEY_REP, &Reply, sizeof(hci_link_key_rep_rp)); -} - - - -//////////////////////////////////////////////////////////////////////////////////////////////////// -// -// -// --- helper -// -// -//////////////////////////////////////////////////////////////////////////////////////////////////// - -CWII_IPC_HLE_WiiMote* CWII_IPC_HLE_Device_usb_oh1_57e_305::AccessWiiMote(const bdaddr_t& _rAddr) -{ - for (size_t i=0; i= 4 and you will get a lot of event messages of the same type + + Memory::Write_U8(1, 0x80152018); // WUD + + Memory::Write_U8(1, 0x80151FC8); // DEBUGPrint */ + + + // even it it wasn't very useful yet... + // Memory::Write_U8(1, 0x80151488); // WPAD_LOG + // Memory::Write_U8(1, 0x801514A8); // USB_LOG + // Memory::Write_U8(1, 0x801514D8); // WUD_DEBUGPrint + // Memory::Write_U8(1, 0x80148E09); // HID LOG + + SIOCtlVBuffer CommandBuffer(_CommandAddress); + + switch(CommandBuffer.Parameter) + { + case USB_IOCTL_HCI_COMMAND_MESSAGE: + { + SHCICommandMessage CtrlSetup; + + // the USB stuff is little endian.. + CtrlSetup.bRequestType = *(u8*)Memory::GetPointer(CommandBuffer.InBuffer[0].m_Address); + CtrlSetup.bRequest = *(u8*)Memory::GetPointer(CommandBuffer.InBuffer[1].m_Address); + CtrlSetup.wValue = *(u16*)Memory::GetPointer(CommandBuffer.InBuffer[2].m_Address); + CtrlSetup.wIndex = *(u16*)Memory::GetPointer(CommandBuffer.InBuffer[3].m_Address); + CtrlSetup.wLength = *(u16*)Memory::GetPointer(CommandBuffer.InBuffer[4].m_Address); + CtrlSetup.m_PayLoadAddr = CommandBuffer.PayloadBuffer[0].m_Address; + CtrlSetup.m_PayLoadSize = CommandBuffer.PayloadBuffer[0].m_Size; + + // check termination + _dbg_assert_msg_(WII_IPC_WIIMOTE, *(u8*)Memory::GetPointer(CommandBuffer.InBuffer[5].m_Address) == 0, + "WIIMOTE: Termination != 0"); + +#if 0 + LOG(WII_IPC_WIIMOTE, "USB_IOCTL_CTRLMSG (0x%08x) - execute command", _CommandAddress); + + LOG(WII_IPC_WIIMOTE, " bRequestType: 0x%x", CtrlSetup.bRequestType); + LOG(WII_IPC_WIIMOTE, " bRequest: 0x%x", CtrlSetup.bRequest); + LOG(WII_IPC_WIIMOTE, " wValue: 0x%x", CtrlSetup.wValue); + LOG(WII_IPC_WIIMOTE, " wIndex: 0x%x", CtrlSetup.wIndex); + LOG(WII_IPC_WIIMOTE, " wLength: 0x%x", CtrlSetup.wLength); +#endif + + ExecuteHCICommandMessage(CtrlSetup); + + // control message has been sent executed + Memory::Write_U32(0, _CommandAddress + 0x4); + + return true; + } + break; + + case USB_IOCTL_BLKMSG: + { + u8 Command = Memory::Read_U8(CommandBuffer.InBuffer[0].m_Address); + switch (Command) + { + case ACL_DATA_ENDPOINT_READ: + { + // write + DumpAsync(CommandBuffer.BufferVector, _CommandAddress, CommandBuffer.NumberInBuffer, CommandBuffer.NumberPayloadBuffer); + + SIOCtlVBuffer pBulkBuffer(_CommandAddress); + UACLHeader* pACLHeader = (UACLHeader*)Memory::GetPointer(pBulkBuffer.PayloadBuffer[0].m_Address); + + _dbg_assert_(WII_IPC_WIIMOTE, pACLHeader->BCFlag == 0); + _dbg_assert_(WII_IPC_WIIMOTE, pACLHeader->PBFlag == 2); + + SendToDevice(pACLHeader->ConnectionHandle, Memory::GetPointer(pBulkBuffer.PayloadBuffer[0].m_Address + 4), pACLHeader->Size); + } + break; + + case ACL_DATA_ENDPOINT: + { + if (m_pACLBuffer) + delete m_pACLBuffer; + m_pACLBuffer = new SIOCtlVBuffer(_CommandAddress); + + LOGV(WII_IPC_WIIMOTE, 2, "ACL_DATA_ENDPOINT: 0x%08x ", _CommandAddress); + return false; + } + break; + + default: + { + _dbg_assert_msg_(WII_IPC_WIIMOTE, 0, "Unknown USB_IOCTL_BLKMSG: %x", Command); + } + break; + } + } + break; + + + case USB_IOCTL_INTRMSG: + { + u8 Command = Memory::Read_U8(CommandBuffer.InBuffer[0].m_Address); + switch (Command) + { + case HCI_EVENT_ENDPOINT: + { + if (m_pHCIBuffer) + { + PanicAlert("Kill current hci buffer... there could be a comand inside"); + delete m_pHCIBuffer; + } + m_pHCIBuffer = new SIOCtlVBuffer(_CommandAddress); + return false; + } + break; + + default: + { + _dbg_assert_msg_(WII_IPC_WIIMOTE, 0, "Unknown USB_IOCTL_INTRMSG: %x", Command); + } + break; + } + } + break; + + default: + { + _dbg_assert_msg_(WII_IPC_WIIMOTE, 0, "Unknown CWII_IPC_HLE_Device_usb_oh1_57e_305: %x", CommandBuffer.Parameter); + + LOG(WII_IPC_WIIMOTE, "%s - IOCtlV:", GetDeviceName().c_str()); + LOG(WII_IPC_WIIMOTE, " Parameter: 0x%x", CommandBuffer.Parameter); + LOG(WII_IPC_WIIMOTE, " NumberIn: 0x%08x", CommandBuffer.NumberInBuffer); + LOG(WII_IPC_WIIMOTE, " NumberOut: 0x%08x", CommandBuffer.NumberPayloadBuffer); + LOG(WII_IPC_WIIMOTE, " BufferVector: 0x%08x", CommandBuffer.BufferVector); + LOG(WII_IPC_WIIMOTE, " BufferSize: 0x%08x", CommandBuffer.BufferSize); + DumpAsync(CommandBuffer.BufferVector, _CommandAddress, CommandBuffer.NumberInBuffer, CommandBuffer.NumberPayloadBuffer); + } + break; + } + + // write return value + Memory::Write_U32(0, _CommandAddress + 0x4); + + return true; +} +// ================ + + + +// =================================================== +/* Here we handle the USB_IOCTL_BLKMSG Ioctlv */ +// ---------------- + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::SendToDevice(u16 _ConnectionHandle, u8* _pData, u32 _Size) +{ + CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_ConnectionHandle); + if (pWiiMote == NULL) + { + PanicAlert("SendToDevice: Cant find WiiMote by connection handle: %02x", _ConnectionHandle); + return; + } + + pWiiMote->SendACLFrame(_pData, _Size); +} +// ================ + + + +// =================================================== +/* Here we queue the ACL frames we receive from the Wiimote plugin. They will consist of + header + data. The header is for example 07 00 41 00 which means size 0x0007 and + channel 0x0041. */ +// ---------------- +void CWII_IPC_HLE_Device_usb_oh1_57e_305::SendACLFrame(u16 _ConnectionHandle, u8* _pData, u32 _Size) +{ + LOGV(WII_IPC_WIIMOTE, 1, "Queing ACL frame."); + + // Queue the packet + ACLFrame frame; + frame.ConnectionHandle = _ConnectionHandle; + frame.data = new u8[_Size]; + memcpy(frame.data, _pData, _Size); + frame.size = _Size; + m_AclFrameQue.push(frame); + + /* Debugging + std::string Temp; + for (u32 j = 0; j < _Size; j++) + { + char Buffer[128]; + sprintf(Buffer, "%02x ", frame.data[j]); + Temp.append(Buffer); + } + LOGV(WII_IPC_WIIMOTE, 1, " Size: 0x%08x", _Size); + LOGV(WII_IPC_WIIMOTE, 1, " Data: %s", Temp.c_str()); */ + + g_HCICount++; +} + + +// =================================================== +/* This is called from WII_IPC_HLE_Interface::Update() */ +// ---------------- +u32 CWII_IPC_HLE_Device_usb_oh1_57e_305::Update() +{ + if (!m_EventQueue.empty() && m_pHCIBuffer) + { + SIOCtlVBuffer* pHCIBuffer = m_pHCIBuffer; + m_pHCIBuffer = NULL; + + // copy the event to memory + const SQueuedEvent& rEvent = m_EventQueue.front(); + u8* pHCIEvent = Memory::GetPointer(pHCIBuffer->PayloadBuffer[0].m_Address); + memcpy(pHCIEvent, rEvent.m_buffer, rEvent.m_size); + + // return reply buffer size + Memory::Write_U32((u32)rEvent.m_size, pHCIBuffer->m_Address + 0x4); + + if (rEvent.m_connectionHandle > 0) + { + g_HCICount++; + } + + m_EventQueue.pop(); + + u32 Addr = pHCIBuffer->m_Address; + delete pHCIBuffer; + + return Addr; + } + + // check if we can fill the aclbuffer + if(!m_AclFrameQue.empty() && m_pACLBuffer) + { + ACLFrame& frame = m_AclFrameQue.front(); + + LOGV(WII_IPC_WIIMOTE, 1, "Sending ACL frame."); + UACLHeader* pHeader = (UACLHeader*)Memory::GetPointer(m_pACLBuffer->PayloadBuffer[0].m_Address); + pHeader->ConnectionHandle = frame.ConnectionHandle; + pHeader->BCFlag = 0; + pHeader->PBFlag = 2; + pHeader->Size = frame.size; + + // Write the fram to the PayloadBuffer + memcpy(Memory::GetPointer(m_pACLBuffer->PayloadBuffer[0].m_Address + sizeof(UACLHeader)), + frame.data, frame.size); + + // return reply buffer size + Memory::Write_U32(sizeof(UACLHeader) + frame.size, m_pACLBuffer->m_Address + 0x4); + + delete frame.data; + m_AclFrameQue.pop(); + + u32 Addr = m_pACLBuffer->m_Address; + delete m_pACLBuffer; + m_pACLBuffer = NULL; + + + /* Debugging + std::string Temp; + for (u32 j = 0; j < frame.size; j++) + { + char Buffer[128]; + sprintf(Buffer, "%02x ", frame.data[j]); + Temp.append(Buffer); + } + LOGV(WII_IPC_WIIMOTE, 1, " Size: 0x%08x", frame.size); + LOGV(WII_IPC_WIIMOTE, 1, " Size of UACLHeader: 0x%08x", sizeof(UACLHeader)); + LOGV(WII_IPC_WIIMOTE, 1, " Data: %s", Temp.c_str()); */ + + return Addr; + } + + if ((g_GlobalHandle != 0) && (g_HCICount > 0)) + { + SendEventNumberOfCompletedPackets(g_GlobalHandle, g_HCICount*2); + g_HCICount = 0; + } + + if (m_AclFrameQue.empty()) + { + for (size_t i=0; iEventType = 0x0F; + pHCIEvent->PayloadLength = sizeof(SHCIEventStatus) - 2; + pHCIEvent->Status = 0x0; + pHCIEvent->PacketIndicator = 0x01; + pHCIEvent->Opcode = _Opcode; + + AddEventToQueue(Event); + + LOG(WII_IPC_WIIMOTE, "Event: Command Status"); + LOG(WII_IPC_WIIMOTE, " Opcode: 0x%04x", pHCIEvent->Opcode); + + return true; +} + + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventCommandComplete(u16 _OpCode, void* _pData, u32 _DataSize) +{ + _dbg_assert_(WII_IPC_WIIMOTE, (sizeof(SHCIEventCommand) - 2 + _DataSize) < 256); + + SQueuedEvent Event(sizeof(SHCIEventCommand) + _DataSize, 0); + + SHCIEventCommand* pHCIEvent = (SHCIEventCommand*)Event.m_buffer; + pHCIEvent->EventType = 0x0E; + pHCIEvent->PayloadLength = (u8)(sizeof(SHCIEventCommand) - 2 + _DataSize); + pHCIEvent->PacketIndicator = 0x01; + pHCIEvent->Opcode = _OpCode; + + // add the payload + if ((_pData != NULL) && (_DataSize > 0)) + { + u8* pPayload = Event.m_buffer + sizeof(SHCIEventCommand); + memcpy(pPayload, _pData, _DataSize); + } + + AddEventToQueue(Event); + + LOG(WII_IPC_WIIMOTE, "Event: Command Complete"); + LOG(WII_IPC_WIIMOTE, " Opcode: 0x%04x", pHCIEvent->Opcode); +} + +bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventInquiryResponse() +{ + if (m_WiiMotes.empty()) + return false; + + _dbg_assert_(WII_IPC_WIIMOTE, sizeof(SHCIEventInquiryResult) - 2 + (m_WiiMotes.size() * sizeof(hci_inquiry_response)) < 256); + + SQueuedEvent Event(sizeof(SHCIEventInquiryResult) + m_WiiMotes.size()*sizeof(hci_inquiry_response), 0); + + SHCIEventInquiryResult* pInquiryResult = (SHCIEventInquiryResult*)Event.m_buffer; + + pInquiryResult->EventType = 0x02; + pInquiryResult->PayloadLength = (u8)(sizeof(SHCIEventInquiryResult) - 2 + (m_WiiMotes.size() * sizeof(hci_inquiry_response))); + pInquiryResult->num_responses = (u8)m_WiiMotes.size(); + + for (size_t i=0; ibdaddr = m_WiiMotes[i].GetBD(); + pResponse->uclass[0]= m_WiiMotes[i].GetClass()[0]; + pResponse->uclass[1]= m_WiiMotes[i].GetClass()[1]; + pResponse->uclass[2]= m_WiiMotes[i].GetClass()[2]; + + pResponse->page_scan_rep_mode = 1; + pResponse->page_scan_period_mode = 0; + pResponse->page_scan_mode = 0; + pResponse->clock_offset = 0x3818; + + LOG(WII_IPC_WIIMOTE, "Event: Send Fake Inquriy of one controller"); + LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", + pResponse->bdaddr.b[0], pResponse->bdaddr.b[1], pResponse->bdaddr.b[2], + pResponse->bdaddr.b[3], pResponse->bdaddr.b[4], pResponse->bdaddr.b[5]); + } + + AddEventToQueue(Event); + + return true; +} + +bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventInquiryComplete() +{ + SQueuedEvent Event(sizeof(SHCIEventInquiryComplete), 0); + + SHCIEventInquiryComplete* pInquiryComplete = (SHCIEventInquiryComplete*)Event.m_buffer; + pInquiryComplete->EventType = 0x01; + pInquiryComplete->PayloadLength = sizeof(SHCIEventInquiryComplete) - 2; + pInquiryComplete->Status = 0x00; + + AddEventToQueue(Event); + + LOG(WII_IPC_WIIMOTE, "Event: Inquiry complete"); + + return true; +} + +bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventRemoteNameReq(bdaddr_t _bd) +{ + CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_bd); + if (pWiiMote == NULL) + { + PanicAlert("SendEventRemoteNameReq: Cant find WiiMote by bd: %02x:%02x:%02x:%02x:%02x:%02x", + _bd.b[0], _bd.b[1], _bd.b[2], _bd.b[3], _bd.b[4], _bd.b[5]); + return false; + } + + SQueuedEvent Event(sizeof(SHCIEventRemoteNameReq), 0); + + SHCIEventRemoteNameReq* pRemoteNameReq = (SHCIEventRemoteNameReq*)Event.m_buffer; + + pRemoteNameReq->EventType = 0x07; + pRemoteNameReq->PayloadLength = sizeof(SHCIEventRemoteNameReq) - 2; + pRemoteNameReq->Status = 0x00; + pRemoteNameReq->bdaddr = pWiiMote->GetBD(); + strcpy((char*)pRemoteNameReq->RemoteName, pWiiMote->GetName()); + + AddEventToQueue(Event); + + LOG(WII_IPC_WIIMOTE, "Event: SendEventRemoteNameReq"); + LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", + pRemoteNameReq->bdaddr.b[0], pRemoteNameReq->bdaddr.b[1], pRemoteNameReq->bdaddr.b[2], + pRemoteNameReq->bdaddr.b[3], pRemoteNameReq->bdaddr.b[4], pRemoteNameReq->bdaddr.b[5]); + LOG(WII_IPC_WIIMOTE, " remotename: %s", pRemoteNameReq->RemoteName); + + return true; +} + +bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventRequestConnection(CWII_IPC_HLE_WiiMote& _rWiiMote) +{ + SQueuedEvent Event(sizeof(SHCIEventRequestConnection), 0); + + SHCIEventRequestConnection* pEventRequestConnection = (SHCIEventRequestConnection*)Event.m_buffer; + + pEventRequestConnection->EventType = 0x04; + pEventRequestConnection->PayloadLength = sizeof(SHCIEventRequestConnection) - 2; + pEventRequestConnection->bdaddr = _rWiiMote.GetBD(); + pEventRequestConnection->uclass[0] = _rWiiMote.GetClass()[0]; + pEventRequestConnection->uclass[1] = _rWiiMote.GetClass()[1]; + pEventRequestConnection->uclass[2] = _rWiiMote.GetClass()[2]; + pEventRequestConnection->LinkType = 0x01; + + AddEventToQueue(Event); + + // Log +#ifdef LOGGING + static char LinkType[][128] = + { + { "HCI_LINK_SCO 0x00 - Voice"}, + { "HCI_LINK_ACL 0x01 - Data"}, + { "HCI_LINK_eSCO 0x02 - eSCO"}, + }; +#endif + + LOG(WII_IPC_WIIMOTE, "Event: SendEventRequestConnection"); + LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", + pEventRequestConnection->bdaddr.b[0], pEventRequestConnection->bdaddr.b[1], pEventRequestConnection->bdaddr.b[2], + pEventRequestConnection->bdaddr.b[3], pEventRequestConnection->bdaddr.b[4], pEventRequestConnection->bdaddr.b[5]); + LOG(WII_IPC_WIIMOTE, " COD[0]: 0x%02x", pEventRequestConnection->uclass[0]); + LOG(WII_IPC_WIIMOTE, " COD[1]: 0x%02x", pEventRequestConnection->uclass[1]); + LOG(WII_IPC_WIIMOTE, " COD[2]: 0x%02x", pEventRequestConnection->uclass[2]); + LOG(WII_IPC_WIIMOTE, " LinkType: %s", LinkType[pEventRequestConnection->LinkType]); + + return true; +}; + +bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventRequestLinkKey(bdaddr_t _bd) +{ + SQueuedEvent Event(sizeof(SHCIEventRequestLinkKey), 0); + + SHCIEventRequestLinkKey* pEventRequestLinkKey = (SHCIEventRequestLinkKey*)Event.m_buffer; + + pEventRequestLinkKey->EventType = 0x17; + pEventRequestLinkKey->PayloadLength = sizeof(SHCIEventRequestLinkKey) - 2; + pEventRequestLinkKey->bdaddr = _bd; + + AddEventToQueue(Event); + + LOG(WII_IPC_WIIMOTE, "Event: SendEventRequestLinkKey"); + LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", + pEventRequestLinkKey->bdaddr.b[0], pEventRequestLinkKey->bdaddr.b[1], pEventRequestLinkKey->bdaddr.b[2], + pEventRequestLinkKey->bdaddr.b[3], pEventRequestLinkKey->bdaddr.b[4], pEventRequestLinkKey->bdaddr.b[5]); + + return true; +}; + +bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventLinkKeyNotification(const CWII_IPC_HLE_WiiMote& _rWiiMote) +{ + SQueuedEvent Event(sizeof(SHCIEventLinkKeyNotification), 0); + + SHCIEventLinkKeyNotification* pEventLinkKey = (SHCIEventLinkKeyNotification*)Event.m_buffer; + + pEventLinkKey->EventType = 0x15; + pEventLinkKey->PayloadLength = sizeof(SHCIEventLinkKeyNotification) - 2; + pEventLinkKey->numKeys = 1; + pEventLinkKey->bdaddr = _rWiiMote.GetBD(); + memcpy(pEventLinkKey->LinkKey, _rWiiMote.GetLinkKey(), 16); + + AddEventToQueue(Event); + + LOG(WII_IPC_WIIMOTE, "Event: SendEventLinkKeyNotification"); + LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", + pEventLinkKey->bdaddr.b[0], pEventLinkKey->bdaddr.b[1], pEventLinkKey->bdaddr.b[2], + pEventLinkKey->bdaddr.b[3], pEventLinkKey->bdaddr.b[4], pEventLinkKey->bdaddr.b[5]); + LOG_LinkKey(pEventLinkKey->LinkKey); + + return true; +}; + + + +bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventConnectionComplete(bdaddr_t _bd) +{ + CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_bd); + if (pWiiMote == NULL) + { + PanicAlert("SendEventConnectionComplete: Cant find WiiMote by bd: %02x:%02x:%02x:%02x:%02x:%02x", + _bd.b[0], _bd.b[1], _bd.b[2], + _bd.b[3], _bd.b[4], _bd.b[5]); + return false; + } + + SQueuedEvent Event(sizeof(SHCIEventConnectionComplete), 0); + + SHCIEventConnectionComplete* pConnectionComplete = (SHCIEventConnectionComplete*)Event.m_buffer; + + pConnectionComplete->EventType = 0x03; + pConnectionComplete->PayloadLength = sizeof(SHCIEventConnectionComplete) - 2; + pConnectionComplete->Status = 0x00; + pConnectionComplete->Connection_Handle = pWiiMote->GetConnectionHandle(); + pConnectionComplete->bdaddr = pWiiMote->GetBD(); + pConnectionComplete->LinkType = 0x01; // ACL + pConnectionComplete->EncryptionEnabled = 0x00; + + AddEventToQueue(Event); + + CWII_IPC_HLE_WiiMote* pWiimote = AccessWiiMote(_bd); + if (pWiimote) + { + pWiimote->EventConnectionAccepted(); + } + + + g_GlobalHandle = pConnectionComplete->Connection_Handle; + +#ifdef LOGGING + static char s_szLinkType[][128] = + { + { "HCI_LINK_SCO 0x00 - Voice"}, + { "HCI_LINK_ACL 0x01 - Data"}, + { "HCI_LINK_eSCO 0x02 - eSCO"}, + }; +#endif + + LOG(WII_IPC_WIIMOTE, "Event: SendEventConnectionComplete"); + LOG(WII_IPC_WIIMOTE, " Connection_Handle: 0x%04x", pConnectionComplete->Connection_Handle); + LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", + pConnectionComplete->bdaddr.b[0], pConnectionComplete->bdaddr.b[1], pConnectionComplete->bdaddr.b[2], + pConnectionComplete->bdaddr.b[3], pConnectionComplete->bdaddr.b[4], pConnectionComplete->bdaddr.b[5]); + LOG(WII_IPC_WIIMOTE, " LinkType: %s", s_szLinkType[pConnectionComplete->LinkType]); + LOG(WII_IPC_WIIMOTE, " EncryptionEnabled: %i", pConnectionComplete->EncryptionEnabled); + + return true; +} + +bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventRoleChange(bdaddr_t _bd, bool _master) +{ + CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_bd); + if (pWiiMote == NULL) + { + PanicAlert("SendEventRoleChange: Cant find WiiMote by bd: %02x:%02x:%02x:%02x:%02x:%02x", + _bd.b[0], _bd.b[1], _bd.b[2], + _bd.b[3], _bd.b[4], _bd.b[5]); + return false; + } + + SQueuedEvent Event(sizeof(SHCIEventRoleChange), 0); + + SHCIEventRoleChange* pRoleChange = (SHCIEventRoleChange*)Event.m_buffer; + + pRoleChange->EventType = 0x12; + pRoleChange->PayloadLength = sizeof(SHCIEventRoleChange) - 2; + pRoleChange->Status = 0x00; + pRoleChange->bdaddr = pWiiMote->GetBD(); + pRoleChange->NewRole = _master ? 0x00 : 0x01; + + AddEventToQueue(Event); + + LOG(WII_IPC_WIIMOTE, "Event: SendEventRoleChange"); + LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", + pRoleChange->bdaddr.b[0], pRoleChange->bdaddr.b[1], pRoleChange->bdaddr.b[2], + pRoleChange->bdaddr.b[3], pRoleChange->bdaddr.b[4], pRoleChange->bdaddr.b[5]); + LOG(WII_IPC_WIIMOTE, " NewRole: %i", pRoleChange->NewRole); + + return true; +} + + + +bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventReadClockOffsetComplete(u16 _connectionHandle) +{ + CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_connectionHandle); + if (pWiiMote == NULL) + { + PanicAlert("SendEventReadClockOffsetComplete: Cant find WiiMote by connection handle: %02x", _connectionHandle); + return false; + } + + SQueuedEvent Event(sizeof(SHCIEventReadClockOffsetComplete), _connectionHandle); + + SHCIEventReadClockOffsetComplete* pReadClockOffsetComplete = (SHCIEventReadClockOffsetComplete*)Event.m_buffer; + pReadClockOffsetComplete->EventType = 0x1C; + pReadClockOffsetComplete->PayloadLength = sizeof(SHCIEventReadClockOffsetComplete) - 2; + pReadClockOffsetComplete->Status = 0x00; + pReadClockOffsetComplete->ConnectionHandle = pWiiMote->GetConnectionHandle(); + pReadClockOffsetComplete->ClockOffset = 0x3818; + + AddEventToQueue(Event); + + // Log + LOG(WII_IPC_WIIMOTE, "Event: SendEventReadClockOffsetComplete"); + LOG(WII_IPC_WIIMOTE, " Connection_Handle: 0x%04x", pReadClockOffsetComplete->ConnectionHandle); + LOG(WII_IPC_WIIMOTE, " ClockOffset: 0x%04x", pReadClockOffsetComplete->ClockOffset); + + return true; +} + +bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventReadRemoteVerInfo(u16 _connectionHandle) +{ + CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_connectionHandle); + if (pWiiMote == NULL) + { + PanicAlert("SendEventReadRemoteVerInfo: Cant find WiiMote by connection handle: %02x", _connectionHandle); + return false; + } + + SQueuedEvent Event(sizeof(SHCIEventReadRemoteVerInfo), _connectionHandle); + + SHCIEventReadRemoteVerInfo* pReadRemoteVerInfo = (SHCIEventReadRemoteVerInfo*)Event.m_buffer; + pReadRemoteVerInfo->EventType = 0x0C; + pReadRemoteVerInfo->PayloadLength = sizeof(SHCIEventReadRemoteVerInfo) - 2; + pReadRemoteVerInfo->Status = 0x00; + pReadRemoteVerInfo->ConnectionHandle = pWiiMote->GetConnectionHandle(); + pReadRemoteVerInfo->lmp_version = pWiiMote->GetLMPVersion(); + pReadRemoteVerInfo->manufacturer = pWiiMote->GetManufactorID(); + pReadRemoteVerInfo->lmp_subversion = pWiiMote->GetLMPSubVersion(); + + AddEventToQueue(Event); + + // Log + LOG(WII_IPC_WIIMOTE, "Event: SendEventReadRemoteVerInfo"); + LOG(WII_IPC_WIIMOTE, " Connection_Handle: 0x%04x", pReadRemoteVerInfo->ConnectionHandle); + LOG(WII_IPC_WIIMOTE, " lmp_version: 0x%02x", pReadRemoteVerInfo->lmp_version); + LOG(WII_IPC_WIIMOTE, " manufacturer: 0x%04x", pReadRemoteVerInfo->manufacturer); + LOG(WII_IPC_WIIMOTE, " lmp_subversion: 0x%04x", pReadRemoteVerInfo->lmp_subversion); + + return true; +} + +bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventReadRemoteFeatures(u16 _connectionHandle) +{ + CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_connectionHandle); + if (pWiiMote == NULL) + { + PanicAlert("SendEventReadRemoteFeatures: Cant find WiiMote by connection handle: %02x", _connectionHandle); + return false; + } + + SQueuedEvent Event(sizeof(SHCIEventReadRemoteFeatures), _connectionHandle); + + SHCIEventReadRemoteFeatures* pReadRemoteFeatures = (SHCIEventReadRemoteFeatures*)Event.m_buffer; + pReadRemoteFeatures->EventType = 0x0C; + pReadRemoteFeatures->PayloadLength = sizeof(SHCIEventReadRemoteFeatures) - 2; + pReadRemoteFeatures->Status = 0x00; + pReadRemoteFeatures->ConnectionHandle = pWiiMote->GetConnectionHandle(); + pReadRemoteFeatures->features[0] = pWiiMote->GetFeatures()[0]; + pReadRemoteFeatures->features[1] = pWiiMote->GetFeatures()[1]; + pReadRemoteFeatures->features[2] = pWiiMote->GetFeatures()[2]; + pReadRemoteFeatures->features[3] = pWiiMote->GetFeatures()[3]; + pReadRemoteFeatures->features[4] = pWiiMote->GetFeatures()[4]; + pReadRemoteFeatures->features[5] = pWiiMote->GetFeatures()[5]; + pReadRemoteFeatures->features[6] = pWiiMote->GetFeatures()[6]; + pReadRemoteFeatures->features[7] = pWiiMote->GetFeatures()[7]; + + AddEventToQueue(Event); + + // Log + LOG(WII_IPC_WIIMOTE, "Event: SendEventReadRemoteFeatures"); + LOG(WII_IPC_WIIMOTE, " Connection_Handle: 0x%04x", pReadRemoteFeatures->ConnectionHandle); + LOG(WII_IPC_WIIMOTE, " features: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + pReadRemoteFeatures->features[0], pReadRemoteFeatures->features[1], pReadRemoteFeatures->features[2], + pReadRemoteFeatures->features[3], pReadRemoteFeatures->features[4], pReadRemoteFeatures->features[5], + pReadRemoteFeatures->features[6], pReadRemoteFeatures->features[7]); + + return true; +} + +bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventNumberOfCompletedPackets(u16 _connectionHandle, u16 _count) +{ + CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_connectionHandle); + if (pWiiMote == NULL) + { + PanicAlert("SendEventNumberOfCompletedPackets: Cant find WiiMote by connection handle %02x", _connectionHandle); + return false; + } + + SQueuedEvent Event(sizeof(SHCIEventNumberOfCompletedPackets), 0); // zero, so this packet isnt counted + + SHCIEventNumberOfCompletedPackets* pNumberOfCompletedPackets = (SHCIEventNumberOfCompletedPackets*)Event.m_buffer; + pNumberOfCompletedPackets->EventType = 0x13; + pNumberOfCompletedPackets->PayloadLength = sizeof(SHCIEventNumberOfCompletedPackets) - 2; + pNumberOfCompletedPackets->NumberOfHandles = 1; + pNumberOfCompletedPackets->Connection_Handle = _connectionHandle; + pNumberOfCompletedPackets->Number_Of_Completed_Packets = _count; + + AddEventToQueue(Event); + + // Log + LOGV(WII_IPC_WIIMOTE, 1, "Event: SendEventNumberOfCompletedPackets"); + LOGV(WII_IPC_WIIMOTE, 1, " Connection_Handle: 0x%04x", pNumberOfCompletedPackets->Connection_Handle); + LOGV(WII_IPC_WIIMOTE, 1, " Number_Of_Completed_Packets: %i", pNumberOfCompletedPackets->Number_Of_Completed_Packets); + + return true; +} + +bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventAuthenticationCompleted(u16 _connectionHandle) +{ + CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_connectionHandle); + if (pWiiMote == NULL) + { + PanicAlert("SendEventAuthenticationCompleted: Cant find WiiMote by connection handle %02x", _connectionHandle); + return false; + } + + SQueuedEvent Event(sizeof(SHCIEventAuthenticationCompleted), _connectionHandle); + + SHCIEventAuthenticationCompleted* pEventAuthenticationCompleted = (SHCIEventAuthenticationCompleted*)Event.m_buffer; + pEventAuthenticationCompleted->EventType = 0x06; + pEventAuthenticationCompleted->PayloadLength = sizeof(SHCIEventAuthenticationCompleted) - 2; + pEventAuthenticationCompleted->Status = 0; + pEventAuthenticationCompleted->Connection_Handle = _connectionHandle; + + AddEventToQueue(Event); + + // Log + LOGV(WII_IPC_WIIMOTE, 1, "Event: SendEventAuthenticationCompleted"); + LOGV(WII_IPC_WIIMOTE, 1, " Connection_Handle: 0x%04x", pEventAuthenticationCompleted->Connection_Handle); + + return true; +} + +bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventModeChange(u16 _connectionHandle, u8 _mode, u16 _value) +{ + CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_connectionHandle); + if (pWiiMote == NULL) + { + PanicAlert("SendEventModeChange: Cant find WiiMote by connection handle %02x", _connectionHandle); + return false; + } + + SQueuedEvent Event(sizeof(SHCIEventModeChange), _connectionHandle); + + SHCIEventModeChange* pModeChange = (SHCIEventModeChange*)Event.m_buffer; + pModeChange->EventType = 0x14; + pModeChange->PayloadLength = sizeof(SHCIEventModeChange) - 2; + pModeChange->Status = 0; + pModeChange->Connection_Handle = _connectionHandle; + pModeChange->CurrentMode = _mode; + pModeChange->Value = _value; + + AddEventToQueue(Event); + + // Log + LOG(WII_IPC_WIIMOTE, "Event: SendEventModeChange"); + LOG(WII_IPC_WIIMOTE, " Connection_Handle: 0x%04x", pModeChange->Connection_Handle); + LOG(WII_IPC_WIIMOTE, " missing other paramter :)"); + + return true; +} + +bool CWII_IPC_HLE_Device_usb_oh1_57e_305::SendEventDisconnect(u16 _connectionHandle, u8 _Reason) +{ + CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(_connectionHandle); + if (pWiiMote == NULL) + { + PanicAlert("SendEventDisconnect: Cant find WiiMote by connection handle %02x", _connectionHandle); + return false; + } + + SQueuedEvent Event(sizeof(SHCIEventDisconnectCompleted), _connectionHandle); + + SHCIEventDisconnectCompleted* pDisconnect = (SHCIEventDisconnectCompleted*)Event.m_buffer; + pDisconnect->EventType = 0x06; + pDisconnect->PayloadLength = sizeof(SHCIEventDisconnectCompleted) - 2; + pDisconnect->Status = 0; + pDisconnect->Connection_Handle = _connectionHandle; + pDisconnect->Reason = _Reason; + + AddEventToQueue(Event); + + // Log + LOG(WII_IPC_WIIMOTE, "Event: SendEventDisconnect"); + LOG(WII_IPC_WIIMOTE, " Connection_Handle: 0x%04x", pDisconnect->Connection_Handle); + LOG(WII_IPC_WIIMOTE, " Reason: 0x%02x", pDisconnect->Reason); + + return true; +} + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// Command dispacther +// ----------------- +// This is called from the USB_IOCTL_HCI_COMMAND_MESSAGE Ioctlv +// +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::ExecuteHCICommandMessage(const SHCICommandMessage& _rHCICommandMessage) +{ + u8* pInput = Memory::GetPointer(_rHCICommandMessage.m_PayLoadAddr + 3); + SCommandMessage* pMsg = (SCommandMessage*)Memory::GetPointer(_rHCICommandMessage.m_PayLoadAddr); + + u16 ocf = HCI_OCF(pMsg->Opcode); + u16 ogf = HCI_OGF(pMsg->Opcode); + LOG(WII_IPC_WIIMOTE, "******************************"); + LOG(WII_IPC_WIIMOTE, "ExecuteHCICommandMessage(0x%04x)(ocf: 0x%02x, ogf: 0x%02x)", + pMsg->Opcode, ocf, ogf); + + switch(pMsg->Opcode) + { + // + // --- read commandos --- + // + case HCI_CMD_RESET: + CommandReset(pInput); + break; + + case HCI_CMD_READ_BUFFER_SIZE: + CommandReadBufferSize(pInput); + break; + + case HCI_CMD_READ_LOCAL_VER: + CommandReadLocalVer(pInput); + break; + + case HCI_CMD_READ_BDADDR: + CommandReadBDAdrr(pInput); + break; + + case HCI_CMD_READ_LOCAL_FEATURES: + CommandReadLocalFeatures(pInput); + break; + + case HCI_CMD_READ_STORED_LINK_KEY: + CommandReadStoredLinkKey(pInput); + break; + + case HCI_CMD_WRITE_UNIT_CLASS: + CommandWriteUnitClass(pInput); + break; + + case HCI_CMD_WRITE_LOCAL_NAME: + CommandWriteLocalName(pInput); + break; + + case HCI_CMD_WRITE_PIN_TYPE: + CommandWritePinType(pInput); + break; + + case HCI_CMD_HOST_BUFFER_SIZE: + CommandHostBufferSize(pInput); + break; + + case HCI_CMD_WRITE_PAGE_TIMEOUT: + CommandWritePageTimeOut(pInput); + break; + + case HCI_CMD_WRITE_SCAN_ENABLE: + CommandWriteScanEnable(pInput); + break; + + case HCI_CMD_WRITE_INQUIRY_MODE: + CommandWriteInquiryMode(pInput); + break; + + case HCI_CMD_WRITE_PAGE_SCAN_TYPE: + CommandWritePageScanType(pInput); + break; + + case HCI_CMD_SET_EVENT_FILTER: + CommandSetEventFilter(pInput); + break; + + case HCI_CMD_INQUIRY: + CommandInquiry(pInput); + break; + + case HCI_CMD_WRITE_INQUIRY_SCAN_TYPE: + CommandWriteInquiryScanType(pInput); + break; + + // vendor specific... + case 0xFC4C: + CommandVendorSpecific_FC4C(pInput, _rHCICommandMessage.m_PayLoadSize - 3); + break; + + case 0xFC4F: + CommandVendorSpecific_FC4F(pInput, _rHCICommandMessage.m_PayLoadSize - 3); + break; + + case HCI_CMD_INQUIRY_CANCEL: + CommandInquiryCancel(pInput); + break; + + case HCI_CMD_REMOTE_NAME_REQ: + CommandRemoteNameReq(pInput); + break; + + case HCI_CMD_CREATE_CON: + CommandCreateCon(pInput); + break; + + case HCI_CMD_ACCEPT_CON: + CommandAcceptCon(pInput); + break; + + case HCI_CMD_READ_CLOCK_OFFSET: + CommandReadClockOffset(pInput); + break; + + case HCI_CMD_READ_REMOTE_VER_INFO: + CommandReadRemoteVerInfo(pInput); + break; + + case HCI_CMD_READ_REMOTE_FEATURES: + CommandReadRemoteFeatures(pInput); + break; + + case HCI_CMD_WRITE_LINK_POLICY_SETTINGS: + CommandWriteLinkPolicy(pInput); + break; + + case HCI_CMD_AUTH_REQ: + CommandAuthenticationRequested(pInput); + break; + + case HCI_CMD_SNIFF_MODE: + CommandSniffMode(pInput); + break; + + case HCI_CMD_DISCONNECT: + CommandDisconnect(pInput); + break; + + case HCI_CMD_WRITE_LINK_SUPERVISION_TIMEOUT: + CommandWriteLinkSupervisionTimeout(pInput); + break; + + case HCI_CMD_LINK_KEY_NEG_REP: + CommandLinkKeyNegRep(pInput); + break; + + case HCI_CMD_LINK_KEY_REP: + CommandLinkKeyRep(pInput); + break; + + + // + // --- default --- + // + default: + { + u16 _ocf = HCI_OCF(pMsg->Opcode); + u16 _ogf = HCI_OGF(pMsg->Opcode); + + if (_ogf == 0x3f) + { + PanicAlert("Vendor specific HCI command"); + LOG(WII_IPC_WIIMOTE, "Command: vendor specific: 0x%04X (ocf: 0x%x)", pMsg->Opcode, _ocf); + + for (int i=0; ilen; i++) + { + LOG(WII_IPC_WIIMOTE, " 0x02%x", pInput[i]); + } + } + else + { + _dbg_assert_msg_(WII_IPC_WIIMOTE, 0, "Unknown USB_IOCTL_CTRLMSG: 0x%04X (ocf: 0x%x ogf 0x%x)", pMsg->Opcode, _ocf, _ogf); + } + + // send fake all is okay msg... + SendEventCommandComplete(pMsg->Opcode, NULL, 0); + } + break; + } +} + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// +// --- command helper +// +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandReset(u8* _Input) +{ + // reply + hci_status_rp Reply; + Reply.status = 0x00; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_RESET"); + + SendEventCommandComplete(HCI_CMD_RESET, &Reply, sizeof(hci_status_rp)); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandReadBufferSize(u8* _Input) +{ + // reply + hci_read_buffer_size_rp Reply; + Reply.status = 0x00; + Reply.max_acl_size = 339; + Reply.num_acl_pkts = 10; + Reply.max_sco_size = 64; + Reply.num_sco_pkts = 0; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_READ_BUFFER_SIZE:"); + LOG(WII_IPC_WIIMOTE, "return:"); + LOG(WII_IPC_WIIMOTE, " max_acl_size: %i", Reply.max_acl_size); + LOG(WII_IPC_WIIMOTE, " num_acl_pkts: %i", Reply.num_acl_pkts); + LOG(WII_IPC_WIIMOTE, " max_sco_size: %i", Reply.max_sco_size); + LOG(WII_IPC_WIIMOTE, " num_sco_pkts: %i", Reply.num_sco_pkts); + + SendEventCommandComplete(HCI_CMD_READ_BUFFER_SIZE, &Reply, sizeof(hci_read_buffer_size_rp)); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandReadLocalVer(u8* _Input) +{ + // reply + hci_read_local_ver_rp Reply; + Reply.status = 0x00; + Reply.hci_version = 0x03; // HCI version: 1.1 + Reply.hci_revision = 0x40a7; // current revision (?) + Reply.lmp_version = 0x03; // LMP version: 1.1 + Reply.manufacturer = 0x000F; // manufacturer: reserved for tests + Reply.lmp_subversion = 0x430e; // LMP subversion + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_READ_LOCAL_VER:"); + LOG(WII_IPC_WIIMOTE, "return:"); + LOG(WII_IPC_WIIMOTE, " status: %i", Reply.status); + LOG(WII_IPC_WIIMOTE, " hci_revision: %i", Reply.hci_revision); + LOG(WII_IPC_WIIMOTE, " lmp_version: %i", Reply.lmp_version); + LOG(WII_IPC_WIIMOTE, " manufacturer: %i", Reply.manufacturer); + LOG(WII_IPC_WIIMOTE, " lmp_subversion: %i", Reply.lmp_subversion); + + SendEventCommandComplete(HCI_CMD_READ_LOCAL_VER, &Reply, sizeof(hci_read_local_ver_rp)); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandReadBDAdrr(u8* _Input) +{ + // reply + hci_read_bdaddr_rp Reply; + Reply.status = 0x00; + Reply.bdaddr = m_ControllerBD; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_READ_BDADDR:"); + LOG(WII_IPC_WIIMOTE, "return:"); + LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", + Reply.bdaddr.b[0], Reply.bdaddr.b[1], Reply.bdaddr.b[2], + Reply.bdaddr.b[3], Reply.bdaddr.b[4], Reply.bdaddr.b[5]); + + SendEventCommandComplete(HCI_CMD_READ_BDADDR, &Reply, sizeof(hci_read_bdaddr_rp)); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandReadLocalFeatures(u8* _Input) +{ + // reply + hci_read_local_features_rp Reply; + Reply.status = 0x00; + Reply.features[0] = 0xFF; + Reply.features[1] = 0xFF; + Reply.features[2] = 0x8D; + Reply.features[3] = 0xFE; + Reply.features[4] = 0x9B; + Reply.features[5] = 0xF9; + Reply.features[6] = 0x00; + Reply.features[7] = 0x80; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_READ_LOCAL_FEATURES:"); + LOG(WII_IPC_WIIMOTE, "return:"); + LOG(WII_IPC_WIIMOTE, " features: %02x:%02x:%02x:%02x:%02x:%02x:%02x:%02x", + Reply.features[0], Reply.features[1], Reply.features[2], + Reply.features[3], Reply.features[4], Reply.features[5], + Reply.features[6], Reply.features[7]); + + SendEventCommandComplete(HCI_CMD_READ_LOCAL_FEATURES, &Reply, sizeof(hci_read_local_features_rp)); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandReadStoredLinkKey(u8* _Input) +{ + hci_read_stored_link_key_cp* ReadStoredLinkKey = (hci_read_stored_link_key_cp*)_Input; + + // reply + hci_read_stored_link_key_rp Reply; + Reply.status = 0x00; + + Reply.max_num_keys = 255; + if (ReadStoredLinkKey->read_all) + { + Reply.num_keys_read = (u16)m_WiiMotes.size(); + } + else + { + PanicAlert("CommandReadStoredLinkKey"); + } + + // logging + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_READ_STORED_LINK_KEY:"); + LOG(WII_IPC_WIIMOTE, "input:"); + LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", + ReadStoredLinkKey->bdaddr.b[0], ReadStoredLinkKey->bdaddr.b[1], ReadStoredLinkKey->bdaddr.b[2], + ReadStoredLinkKey->bdaddr.b[3], ReadStoredLinkKey->bdaddr.b[4], ReadStoredLinkKey->bdaddr.b[5]); + LOG(WII_IPC_WIIMOTE, " read_all: %i", ReadStoredLinkKey->read_all); + LOG(WII_IPC_WIIMOTE, "return:"); + LOG(WII_IPC_WIIMOTE, " max_num_keys: %i", Reply.max_num_keys); + LOG(WII_IPC_WIIMOTE, " num_keys_read: %i", Reply.num_keys_read); + + // generate link key + for (size_t i=0; iuclass[0]; + m_ClassOfDevice[1] = pWriteUnitClass->uclass[1]; + m_ClassOfDevice[2] = pWriteUnitClass->uclass[2]; + + // reply + hci_write_unit_class_rp Reply; + Reply.status = 0x00; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_WRITE_UNIT_CLASS:"); + LOG(WII_IPC_WIIMOTE, "write:"); + LOG(WII_IPC_WIIMOTE, " COD[0]: 0x%02x", pWriteUnitClass->uclass[0]); + LOG(WII_IPC_WIIMOTE, " COD[1]: 0x%02x", pWriteUnitClass->uclass[1]); + LOG(WII_IPC_WIIMOTE, " COD[2]: 0x%02x", pWriteUnitClass->uclass[2]); + + SendEventCommandComplete(HCI_CMD_WRITE_UNIT_CLASS, &Reply, sizeof(hci_write_unit_class_rp)); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandWriteLocalName(u8* _Input) +{ + // command parameters + hci_write_local_name_cp* pWriteLocalName = (hci_write_local_name_cp*)_Input; + memcpy(m_LocalName, pWriteLocalName->name, HCI_UNIT_NAME_SIZE); + + // reply + hci_write_local_name_rp Reply; + Reply.status = 0x00; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_WRITE_LOCAL_NAME:"); + LOG(WII_IPC_WIIMOTE, "write:"); + LOG(WII_IPC_WIIMOTE, " local_name: %s", pWriteLocalName->name); + + SendEventCommandComplete(HCI_CMD_WRITE_LOCAL_NAME, &Reply, sizeof(hci_write_local_name_rp)); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandWritePinType(u8* _Input) +{ + // command parameters + hci_write_pin_type_cp* pWritePinType = (hci_write_pin_type_cp*)_Input; + m_PINType = pWritePinType->pin_type; + + // reply + hci_write_pin_type_rp Reply; + Reply.status = 0x00; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_WRITE_PIN_TYPE:"); + LOG(WII_IPC_WIIMOTE, "write:"); + LOG(WII_IPC_WIIMOTE, " pin_type: %x", pWritePinType->pin_type); + + SendEventCommandComplete(HCI_CMD_WRITE_PIN_TYPE, &Reply, sizeof(hci_write_pin_type_rp)); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandHostBufferSize(u8* _Input) +{ + // command parameters + hci_host_buffer_size_cp* pHostBufferSize = (hci_host_buffer_size_cp*)_Input; + m_HostMaxACLSize = pHostBufferSize->max_acl_size; + m_HostMaxSCOSize = pHostBufferSize->max_sco_size; + m_HostNumACLPackets = pHostBufferSize->num_acl_pkts; + m_HostNumSCOPackets = pHostBufferSize->num_sco_pkts; + + // reply + hci_host_buffer_size_rp Reply; + Reply.status = 0x00; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_HOST_BUFFER_SIZE:"); + LOG(WII_IPC_WIIMOTE, "write:"); + LOG(WII_IPC_WIIMOTE, " max_acl_size: %i", pHostBufferSize->max_acl_size); + LOG(WII_IPC_WIIMOTE, " max_sco_size: %i", pHostBufferSize->max_sco_size); + LOG(WII_IPC_WIIMOTE, " num_acl_pkts: %i", pHostBufferSize->num_acl_pkts); + LOG(WII_IPC_WIIMOTE, " num_sco_pkts: %i", pHostBufferSize->num_sco_pkts); + + SendEventCommandComplete(HCI_CMD_HOST_BUFFER_SIZE, &Reply, sizeof(hci_host_buffer_size_rp)); +} + + +// =================================================== +/* Here we normally receive the timeout interval. But not from homebrew games that use + lwbt. Why not? */ +// ---------------- +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandWritePageTimeOut(u8* _Input) +{ +#ifdef LOGGING + // command parameters + hci_write_page_timeout_cp* pWritePageTimeOut = (hci_write_page_timeout_cp*)_Input; +#endif + + // reply + hci_host_buffer_size_rp Reply; + Reply.status = 0x00; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_WRITE_PAGE_TIMEOUT:"); + LOG(WII_IPC_WIIMOTE, "write:"); + LOG(WII_IPC_WIIMOTE, " timeout: %i", pWritePageTimeOut->timeout); + + SendEventCommandComplete(HCI_CMD_WRITE_PAGE_TIMEOUT, &Reply, sizeof(hci_host_buffer_size_rp)); +} + + + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandWriteScanEnable(u8* _Input) +{ + // Command parameters + hci_write_scan_enable_cp* pWriteScanEnable = (hci_write_scan_enable_cp*)_Input; + m_ScanEnable = pWriteScanEnable->scan_enable; + + // Reply + hci_write_scan_enable_rp Reply; + Reply.status = 0x00; + +#ifdef LOGGING + static char Scanning[][128] = + { + { "HCI_NO_SCAN_ENABLE"}, + { "HCI_INQUIRY_SCAN_ENABLE"}, + { "HCI_PAGE_SCAN_ENABLE"}, + { "HCI_INQUIRY_AND_PAGE_SCAN_ENABLE"}, + }; +#endif + + LOGV(WII_IPC_WIIMOTE, 0, "Command: HCI_CMD_WRITE_SCAN_ENABLE:"); + LOGV(WII_IPC_WIIMOTE, 0, "write:"); + LOGV(WII_IPC_WIIMOTE, 0, " scan_enable: %s", Scanning[pWriteScanEnable->scan_enable]); + + SendEventCommandComplete(HCI_CMD_WRITE_SCAN_ENABLE, &Reply, sizeof(hci_write_scan_enable_rp)); +} + + + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandWriteInquiryMode(u8* _Input) +{ +#ifdef LOGGING + // command parameters + hci_write_inquiry_mode_cp* pInquiryMode = (hci_write_inquiry_mode_cp*)_Input; +#endif + + // reply + hci_write_inquiry_mode_rp Reply; + Reply.status = 0x00; + +#ifdef LOGGING + static char InquiryMode[][128] = + { + { "Standard Inquiry Result event format (default)" }, + { "Inquiry Result format with RSSI" }, + { "Inquiry Result with RSSI format or Extended Inquiry Result format" } + }; +#endif + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_WRITE_INQUIRY_MODE:"); + LOG(WII_IPC_WIIMOTE, "write:"); + LOG(WII_IPC_WIIMOTE, " mode: %s", InquiryMode[pInquiryMode->mode]); + + SendEventCommandComplete(HCI_CMD_WRITE_INQUIRY_MODE, &Reply, sizeof(hci_write_inquiry_mode_rp)); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandWritePageScanType(u8* _Input) +{ +#ifdef LOGGING + // command parameters + hci_write_page_scan_type_cp* pWritePageScanType = (hci_write_page_scan_type_cp*)_Input; +#endif + + // reply + hci_write_page_scan_type_rp Reply; + Reply.status = 0x00; + +#ifdef LOGGING + static char PageScanType[][128] = + { + { "Mandatory: Standard Scan (default)" }, + { "Optional: Interlaced Scan" } + }; +#endif + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_WRITE_PAGE_SCAN_TYPE:"); + LOG(WII_IPC_WIIMOTE, "write:"); + LOG(WII_IPC_WIIMOTE, " type: %s", PageScanType[pWritePageScanType->type]); + + SendEventCommandComplete(HCI_CMD_WRITE_PAGE_SCAN_TYPE, &Reply, sizeof(hci_write_page_scan_type_rp)); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandSetEventFilter(u8* _Input) +{ + // command parameters + hci_set_event_filter_cp* pSetEventFilter = (hci_set_event_filter_cp*)_Input; + m_EventFilterType = pSetEventFilter->filter_type; + m_EventFilterCondition = pSetEventFilter->filter_condition_type; + + // reply + hci_set_event_filter_rp Reply; + Reply.status = 0x00; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_SET_EVENT_FILTER:"); + LOG(WII_IPC_WIIMOTE, "write:"); + LOG(WII_IPC_WIIMOTE, " filter_type: %i", pSetEventFilter->filter_type); + LOG(WII_IPC_WIIMOTE, " filter_condition_type: %i", pSetEventFilter->filter_condition_type); + + SendEventCommandComplete(HCI_CMD_SET_EVENT_FILTER, &Reply, sizeof(hci_set_event_filter_rp)); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandInquiry(u8* _Input) +{ + // command parameters + hci_inquiry_cp* pInquiry = (hci_inquiry_cp*)_Input; + u8 lap[HCI_LAP_SIZE]; + + memcpy(lap, pInquiry->lap, HCI_LAP_SIZE); + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_INQUIRY:"); + LOG(WII_IPC_WIIMOTE, "write:"); + LOG(WII_IPC_WIIMOTE, " LAP[0]: 0x%02x", pInquiry->lap[0]); + LOG(WII_IPC_WIIMOTE, " LAP[1]: 0x%02x", pInquiry->lap[1]); + LOG(WII_IPC_WIIMOTE, " LAP[2]: 0x%02x", pInquiry->lap[2]); + LOG(WII_IPC_WIIMOTE, " inquiry_length: %i (N x 1.28) sec", pInquiry->inquiry_length); + LOG(WII_IPC_WIIMOTE, " num_responses: %i (N x 1.28) sec", pInquiry->num_responses); + + SendEventCommandStatus(HCI_CMD_INQUIRY); + SendEventInquiryResponse(); + SendEventInquiryComplete(); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandWriteInquiryScanType(u8* _Input) +{ +#ifdef LOGGING + // command parameters + hci_write_inquiry_scan_type_cp* pSetEventFilter = (hci_write_inquiry_scan_type_cp*)_Input; +#endif + // reply + hci_write_inquiry_scan_type_rp Reply; + Reply.status = 0x00; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_WRITE_INQUIRY_SCAN_TYPE:"); + LOG(WII_IPC_WIIMOTE, "write:"); + LOG(WII_IPC_WIIMOTE, " type: %i", pSetEventFilter->type); + + SendEventCommandComplete(HCI_CMD_WRITE_INQUIRY_SCAN_TYPE, &Reply, sizeof(hci_write_inquiry_scan_type_rp)); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandVendorSpecific_FC4F(u8* _Input, u32 _Size) +{ + // callstack... + // BTM_VendorSpecificCommad() + // WUDiRemovePatch() + // WUDiAppendRuntimePatch() + // WUDiGetFirmwareVersion() + // WUDiStackSetupComplete() + + // reply + hci_status_rp Reply; + Reply.status = 0x00; + + LOG(WII_IPC_WIIMOTE, "Command: CommandVendorSpecific_FC4F: (callstack WUDiRemovePatch)"); + LOG(WII_IPC_WIIMOTE, "input (size 0x%x):", _Size); + + Debugger::PrintDataBuffer(LogTypes::WII_IPC_WIIMOTE, _Input, _Size, "Data: "); + + SendEventCommandComplete(0xFC4F, &Reply, sizeof(hci_status_rp)); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandVendorSpecific_FC4C(u8* _Input, u32 _Size) +{ + // reply + hci_status_rp Reply; + Reply.status = 0x00; + + LOG(WII_IPC_WIIMOTE, "Command: CommandVendorSpecific_FC4C:"); + LOG(WII_IPC_WIIMOTE, "input (size 0x%x):", _Size); + Debugger::PrintDataBuffer(LogTypes::WII_IPC_WIIMOTE, _Input, _Size, "Data: "); + + SendEventCommandComplete(0xFC4C, &Reply, sizeof(hci_status_rp)); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandInquiryCancel(u8* _Input) +{ + // reply + hci_inquiry_cancel_rp Reply; + Reply.status = 0x00; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_INQUIRY_CANCEL"); + + SendEventCommandComplete(HCI_CMD_INQUIRY_CANCEL, &Reply, sizeof(hci_inquiry_cancel_rp)); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandRemoteNameReq(u8* _Input) +{ + // command parameters + hci_remote_name_req_cp* pRemoteNameReq = (hci_remote_name_req_cp*)_Input; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_REMOTE_NAME_REQ"); + LOG(WII_IPC_WIIMOTE, "Input:"); + LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", + pRemoteNameReq->bdaddr.b[0], pRemoteNameReq->bdaddr.b[1], pRemoteNameReq->bdaddr.b[2], + pRemoteNameReq->bdaddr.b[3], pRemoteNameReq->bdaddr.b[4], pRemoteNameReq->bdaddr.b[5]); + LOG(WII_IPC_WIIMOTE, " page_scan_rep_mode: %i", pRemoteNameReq->page_scan_rep_mode); + LOG(WII_IPC_WIIMOTE, " page_scan_mode: %i", pRemoteNameReq->page_scan_mode); + LOG(WII_IPC_WIIMOTE, " clock_offset: %i", pRemoteNameReq->clock_offset); + + SendEventCommandStatus(HCI_CMD_REMOTE_NAME_REQ); + SendEventRemoteNameReq(pRemoteNameReq->bdaddr); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandCreateCon(u8* _Input) +{ + // command parameters + hci_create_con_cp* pCreateCon = (hci_create_con_cp*)_Input; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_CREATE_CON"); + LOG(WII_IPC_WIIMOTE, "Input:"); + LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", + pCreateCon->bdaddr.b[0], pCreateCon->bdaddr.b[1], pCreateCon->bdaddr.b[2], + pCreateCon->bdaddr.b[3], pCreateCon->bdaddr.b[4], pCreateCon->bdaddr.b[5]); + + LOG(WII_IPC_WIIMOTE, " pkt_type: %i", pCreateCon->pkt_type); + LOG(WII_IPC_WIIMOTE, " page_scan_rep_mode: %i", pCreateCon->page_scan_rep_mode); + LOG(WII_IPC_WIIMOTE, " page_scan_mode: %i", pCreateCon->page_scan_mode); + LOG(WII_IPC_WIIMOTE, " clock_offset: %i", pCreateCon->clock_offset); + LOG(WII_IPC_WIIMOTE, " accept_role_switch: %i", pCreateCon->accept_role_switch); + + SendEventCommandStatus(HCI_CMD_CREATE_CON); + SendEventConnectionComplete(pCreateCon->bdaddr); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandAcceptCon(u8* _Input) +{ + // command parameters + hci_accept_con_cp* pAcceptCon = (hci_accept_con_cp*)_Input; + +#ifdef LOGGING + static char s_szRole[][128] = + { + { "Master (0x00)"}, + { "Slave (0x01)"}, + }; +#endif + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_ACCEPT_CON"); + LOG(WII_IPC_WIIMOTE, "Input:"); + LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", + pAcceptCon->bdaddr.b[0], pAcceptCon->bdaddr.b[1], pAcceptCon->bdaddr.b[2], + pAcceptCon->bdaddr.b[3], pAcceptCon->bdaddr.b[4], pAcceptCon->bdaddr.b[5]); + LOG(WII_IPC_WIIMOTE, " role: %s", s_szRole[pAcceptCon->role]); + + SendEventCommandStatus(HCI_CMD_ACCEPT_CON); + + // this connection wants to be the master + if (pAcceptCon->role == 0) + { + SendEventRoleChange(pAcceptCon->bdaddr, true); + } + + SendEventConnectionComplete(pAcceptCon->bdaddr); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandReadClockOffset(u8* _Input) +{ + // command parameters + hci_read_clock_offset_cp* pReadClockOffset = (hci_read_clock_offset_cp*)_Input; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_READ_CLOCK_OFFSET"); + LOG(WII_IPC_WIIMOTE, "Input:"); + LOG(WII_IPC_WIIMOTE, " ConnectionHandle: 0x%02x", pReadClockOffset->con_handle); + + SendEventCommandStatus(HCI_CMD_READ_CLOCK_OFFSET); + SendEventReadClockOffsetComplete(pReadClockOffset->con_handle); + + +// CWII_IPC_HLE_WiiMote* pWiiMote = AccessWiiMote(pReadClockOffset->con_handle); +// SendEventRequestLinkKey(pWiiMote->GetBD()); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandReadRemoteVerInfo(u8* _Input) +{ + // command parameters + hci_read_remote_ver_info_cp* pReadRemoteVerInfo = (hci_read_remote_ver_info_cp*)_Input; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_READ_REMOTE_VER_INFO"); + LOG(WII_IPC_WIIMOTE, "Input:"); + LOG(WII_IPC_WIIMOTE, " ConnectionHandle: 0x%02x", pReadRemoteVerInfo->con_handle); + + SendEventCommandStatus(HCI_CMD_READ_REMOTE_VER_INFO); + SendEventReadRemoteVerInfo(pReadRemoteVerInfo->con_handle); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandReadRemoteFeatures(u8* _Input) +{ + // command parameters + hci_read_remote_features_cp* pReadRemoteFeatures = (hci_read_remote_features_cp*)_Input; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_READ_REMOTE_FEATURES"); + LOG(WII_IPC_WIIMOTE, "Input:"); + LOG(WII_IPC_WIIMOTE, " ConnectionHandle: 0x%04x", pReadRemoteFeatures->con_handle); + + SendEventCommandStatus(HCI_CMD_READ_REMOTE_FEATURES); + SendEventReadRemoteFeatures(pReadRemoteFeatures->con_handle); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandWriteLinkPolicy(u8* _Input) +{ + // command parameters + hci_write_link_policy_settings_cp* pLinkPolicy = (hci_write_link_policy_settings_cp*)_Input; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_WRITE_LINK_POLICY_SETTINGS"); + LOG(WII_IPC_WIIMOTE, "Input:"); + LOG(WII_IPC_WIIMOTE, " ConnectionHandle: 0x%04x", pLinkPolicy->con_handle); + LOG(WII_IPC_WIIMOTE, " Policy: 0x%04x", pLinkPolicy->settings); + + SendEventCommandStatus(HCI_CMD_WRITE_LINK_POLICY_SETTINGS); + + CWII_IPC_HLE_WiiMote* pWiimote = AccessWiiMote(pLinkPolicy->con_handle); + if (pWiimote) + { + pWiimote->EventCommandWriteLinkPolicy(); + } +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandAuthenticationRequested(u8* _Input) +{ + // command parameters + hci_auth_req_cp* pAuthReq = (hci_auth_req_cp*)_Input; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_AUTH_REQ"); + LOG(WII_IPC_WIIMOTE, "Input:"); + LOG(WII_IPC_WIIMOTE, " ConnectionHandle: 0x%04x", pAuthReq->con_handle); + + SendEventCommandStatus(HCI_CMD_AUTH_REQ); + SendEventAuthenticationCompleted(pAuthReq->con_handle); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandSniffMode(u8* _Input) +{ + // command parameters + hci_sniff_mode_cp* pSniffMode = (hci_sniff_mode_cp*)_Input; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_SNIFF_MODE"); + LOG(WII_IPC_WIIMOTE, "Input:"); + LOG(WII_IPC_WIIMOTE, " ConnectionHandle: 0x%04x", pSniffMode->con_handle); + LOG(WII_IPC_WIIMOTE, " max_interval: 0x%04x", pSniffMode->max_interval); + LOG(WII_IPC_WIIMOTE, " min_interval: 0x%04x", pSniffMode->min_interval); + LOG(WII_IPC_WIIMOTE, " attempt: 0x%04x", pSniffMode->attempt); + LOG(WII_IPC_WIIMOTE, " timeout: 0x%04x", pSniffMode->timeout); + + SendEventCommandStatus(HCI_CMD_SNIFF_MODE); + SendEventModeChange(pSniffMode->con_handle, 0x02, pSniffMode->max_interval); // 0x02 - sniff mode +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandDisconnect(u8* _Input) +{ + // command parameters + hci_discon_cp* pDiscon = (hci_discon_cp*)_Input; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_DISCONNECT"); + LOG(WII_IPC_WIIMOTE, "Input:"); + LOG(WII_IPC_WIIMOTE, " ConnectionHandle: 0x%04x", pDiscon->con_handle); + LOG(WII_IPC_WIIMOTE, " Reason: 0x%02x", pDiscon->reason); + + SendEventCommandStatus(HCI_CMD_DISCONNECT); + SendEventDisconnect(pDiscon->con_handle, pDiscon->reason); + + CWII_IPC_HLE_WiiMote* pWiimote = AccessWiiMote(pDiscon->con_handle); + if (pWiimote) + { + pWiimote->EventDisconnect(); + } + + static bool OneShotMessage = true; + if (OneShotMessage) + { + OneShotMessage = false; + PanicAlert("IPC CommandDisconnect: WiiMote emulation is out of sync.\n" + "This message will be shot one time only, because dolphin does\n" + "not executes the disconnect at all and some times you can play\n" + "anyway. It is strongly recommed to save and/or restart the\n" + "emulation."); + } +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandWriteLinkSupervisionTimeout(u8* _Input) +{ + // command parameters + hci_write_link_supervision_timeout_cp* pSuperVision = (hci_write_link_supervision_timeout_cp*)_Input; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_OCF_WRITE_LINK_SUPERVISION_TIMEOUT"); + LOG(WII_IPC_WIIMOTE, "Input:"); + LOG(WII_IPC_WIIMOTE, " con_handle: 0x%04x", pSuperVision->con_handle); + LOG(WII_IPC_WIIMOTE, " timeout: 0x%02x", pSuperVision->timeout); + + hci_write_link_supervision_timeout_rp Reply; + Reply.status = 0x00; + Reply.con_handle = pSuperVision->con_handle; + + SendEventCommandComplete(HCI_OCF_WRITE_LINK_SUPERVISION_TIMEOUT, &Reply, sizeof(hci_write_link_supervision_timeout_rp)); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandLinkKeyNegRep(u8* _Input) +{ + // command parameters + hci_link_key_neg_rep_cp* pKeyNeg = (hci_link_key_neg_rep_cp*)_Input; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_LINK_KEY_NEG_REP"); + LOG(WII_IPC_WIIMOTE, "Input:"); + LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", + pKeyNeg->bdaddr.b[0], pKeyNeg->bdaddr.b[1], pKeyNeg->bdaddr.b[2], + pKeyNeg->bdaddr.b[3], pKeyNeg->bdaddr.b[4], pKeyNeg->bdaddr.b[5]); + + hci_link_key_neg_rep_rp Reply; + Reply.status = 0x00; + Reply.bdaddr = pKeyNeg->bdaddr; + + SendEventCommandComplete(HCI_OCF_WRITE_LINK_SUPERVISION_TIMEOUT, &Reply, sizeof(hci_link_key_neg_rep_rp)); +} + +void CWII_IPC_HLE_Device_usb_oh1_57e_305::CommandLinkKeyRep(u8* _Input) +{ + // command parameters + hci_link_key_rep_cp* pKeyRep = (hci_link_key_rep_cp*)_Input; + + LOG(WII_IPC_WIIMOTE, "Command: HCI_CMD_LINK_KEY_REP"); + LOG(WII_IPC_WIIMOTE, "Input:"); + LOG(WII_IPC_WIIMOTE, " bd: %02x:%02x:%02x:%02x:%02x:%02x", + pKeyRep->bdaddr.b[0], pKeyRep->bdaddr.b[1], pKeyRep->bdaddr.b[2], + pKeyRep->bdaddr.b[3], pKeyRep->bdaddr.b[4], pKeyRep->bdaddr.b[5]); + LOG_LinkKey(pKeyRep->key); + + + hci_link_key_rep_rp Reply; + Reply.status = 0x00; + Reply.bdaddr = pKeyRep->bdaddr; + + SendEventCommandComplete(HCI_CMD_LINK_KEY_REP, &Reply, sizeof(hci_link_key_rep_rp)); +} + + + +//////////////////////////////////////////////////////////////////////////////////////////////////// +// +// +// --- helper +// +// +//////////////////////////////////////////////////////////////////////////////////////////////////// + +CWII_IPC_HLE_WiiMote* CWII_IPC_HLE_Device_usb_oh1_57e_305::AccessWiiMote(const bdaddr_t& _rAddr) +{ + for (size_t i=0; iCID, pHeader->Length, DataSize); - - if(pHeader->Length != DataSize) - { - LOGV(WII_IPC_WIIMOTE, 2, "Faulty packet. It is dropped."); - return; - } - - switch (pHeader->CID) - { - case 0x0001: - SignalChannel(pData, DataSize); - break; - - default: - { - _dbg_assert_msg_(WII_IPC_WIIMOTE, DoesChannelExist(pHeader->CID), "SendACLFrame to unknown channel %i", pHeader->CID); - CChannelMap::iterator itr= m_Channel.find(pHeader->CID); - if (itr != m_Channel.end()) - { - SChannel& rChannel = itr->second; - switch(rChannel.PSM) - { - case SDP_CHANNEL: - HandleSDP(pHeader->CID, pData, DataSize); - break; - - case HIDP_CONTROL_CHANNEL: - PluginWiimote::Wiimote_ControlChannel(rChannel.DCID, pData, DataSize); - break; - - case HID_INTERRUPT_CHANNEL: - PluginWiimote::Wiimote_InterruptChannel(rChannel.DCID, pData, DataSize); - break; - - default: - PanicAlert("channel 0x04%x has unknown PSM %x", pHeader->CID, rChannel.PSM); - break; - } - } - } - break; - } -} - -void CWII_IPC_HLE_WiiMote::SignalChannel(u8* _pData, u32 _Size) -{ - while (_Size >= sizeof(SL2CAP_Command)) - { - SL2CAP_Command* pCommand = (SL2CAP_Command*)_pData; - _pData += sizeof(SL2CAP_Command); - _Size = _Size - sizeof(SL2CAP_Command) - pCommand->len; - - switch(pCommand->code) - { - case L2CAP_CONN_REQ: - CommandConnectionReq(pCommand->ident, _pData, pCommand->len); - break; - - case L2CAP_CONF_REQ: - CommandCofigurationReq(pCommand->ident, _pData, pCommand->len); - break; - - case L2CAP_CONN_RSP: - CommandConnectionResponse(pCommand->ident, _pData, pCommand->len); - break; - - case L2CAP_DISCONN_REQ: - CommandDisconnectionReq(pCommand->ident, _pData, pCommand->len); - break; - - case L2CAP_CONF_RSP: - CommandCofigurationResponse(pCommand->ident, _pData, pCommand->len); - break; - - case L2CAP_COMMAND_REJ: - PanicAlert("SignalChannel - L2CAP_COMMAND_REJ (something went wrong)",pCommand->code); - break; - - default: - LOG(WII_IPC_WIIMOTE, " Unknown Command-Code (0x%02x)", pCommand->code); - PanicAlert("SignalChannel %x",pCommand->code); - return; - } - - _pData += pCommand->len; - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// -// -// -// --- Send Commands To Device -// -// -// -// -// -///////////////////////////////////////////////////////////////////////////////////////////////// - - -void CWII_IPC_HLE_WiiMote::CommandConnectionReq(u8 _Ident, u8* _pData, u32 _Size) -{ - SL2CAP_CommandConnectionReq* pCommandConnectionReq = (SL2CAP_CommandConnectionReq*)_pData; - - // create the channel - SChannel& rChannel = m_Channel[pCommandConnectionReq->scid]; - rChannel.PSM = pCommandConnectionReq->psm; - rChannel.SCID = pCommandConnectionReq->scid; - rChannel.DCID = pCommandConnectionReq->scid; - - LOG(WII_IPC_WIIMOTE, " CommandConnectionReq"); - LOG(WII_IPC_WIIMOTE, " Ident: 0x%02x", _Ident); - LOG(WII_IPC_WIIMOTE, " PSM: 0x%04x", rChannel.PSM); - LOG(WII_IPC_WIIMOTE, " SCID: 0x%04x", rChannel.SCID); - LOG(WII_IPC_WIIMOTE, " DCID: 0x%04x", rChannel.DCID); - - // response - SL2CAP_ConnectionResponse Rsp; - Rsp.scid = rChannel.SCID; - Rsp.dcid = rChannel.DCID; - Rsp.result = 0x00; - Rsp.status = 0x00; - - SendCommandToACL(_Ident, L2CAP_CONN_RSP, sizeof(SL2CAP_ConnectionResponse), (u8*)&Rsp); - - // update state machine - if (rChannel.PSM == HIDP_CONTROL_CHANNEL) - m_HIDControlChannel_Connected = true; - - if (rChannel.PSM == HID_INTERRUPT_CHANNEL) - m_HIDInterruptChannel_Connected = true; -} - -void CWII_IPC_HLE_WiiMote::CommandCofigurationReq(u8 _Ident, u8* _pData, u32 _Size) -{ - LOGV(WII_IPC_WIIMOTE, 1, "*******************************************************"); - u32 Offset = 0; - SL2CAP_CommandConfigurationReq* pCommandConfigReq = (SL2CAP_CommandConfigurationReq*)_pData; - - _dbg_assert_(WII_IPC_WIIMOTE, pCommandConfigReq->flags == 0x00); // 1 means that the options are send in multi-packets - - _dbg_assert_(WII_IPC_WIIMOTE, DoesChannelExist(pCommandConfigReq->dcid)); - SChannel& rChannel = m_Channel[pCommandConfigReq->dcid]; - - LOGV(WII_IPC_WIIMOTE, 1, " CommandCofigurationReq"); - LOGV(WII_IPC_WIIMOTE, 1, " Ident: 0x%02x", _Ident); - LOGV(WII_IPC_WIIMOTE, 1, " DCID: 0x%04x", pCommandConfigReq->dcid); - LOGV(WII_IPC_WIIMOTE, 1, " Flags: 0x%04x", pCommandConfigReq->flags); - - Offset += sizeof(SL2CAP_CommandConfigurationReq); - - u8 TempBuffer[1024]; - u32 RespLen = 0; - - SL2CAP_CommandConfigurationResponse* Rsp = (SL2CAP_CommandConfigurationResponse*)TempBuffer; - Rsp->scid = rChannel.DCID; - Rsp->flags = 0x00; - Rsp->result = 0x00; - - RespLen += sizeof(SL2CAP_CommandConfigurationResponse); - - // read configuration options - while (Offset < _Size) - { - SL2CAP_Options* pOptions = (SL2CAP_Options*)&_pData[Offset]; - Offset += sizeof(SL2CAP_Options); - - switch(pOptions->type) - { - case 0x01: - { - _dbg_assert_(WII_IPC_WIIMOTE, pOptions->length == 2); - SL2CAP_OptionsMTU* pMTU = (SL2CAP_OptionsMTU*)&_pData[Offset]; - rChannel.MTU = pMTU->MTU; - LOGV(WII_IPC_WIIMOTE, 1, " Config MTU: 0x%04x", pMTU->MTU); - } - break; - - case 0x02: - { - _dbg_assert_(WII_IPC_WIIMOTE, pOptions->length == 2); - SL2CAP_OptionsFlushTimeOut* pFlushTimeOut = (SL2CAP_OptionsFlushTimeOut*)&_pData[Offset]; - rChannel.FlushTimeOut = pFlushTimeOut->TimeOut; - LOGV(WII_IPC_WIIMOTE, 1, " Config FlushTimeOut: 0x%04x", pFlushTimeOut->TimeOut); - } - break; - - default: - _dbg_assert_msg_(WII_IPC_WIIMOTE, 0, "Unknown Option: 0x%02x", pOptions->type); - break; - } - - Offset += pOptions->length; - - u32 OptionSize = sizeof(SL2CAP_Options) + pOptions->length; - memcpy(&TempBuffer[RespLen], pOptions, OptionSize); - RespLen += OptionSize; - } - - SendCommandToACL(_Ident, L2CAP_CONF_RSP, RespLen, TempBuffer); - LOGV(WII_IPC_WIIMOTE, 1, "*******************************************************"); -} - -void CWII_IPC_HLE_WiiMote::CommandConnectionResponse(u8 _Ident, u8* _pData, u32 _Size) -{ - l2cap_conn_rsp* rsp = (l2cap_conn_rsp*)_pData; - - _dbg_assert_(WII_IPC_WIIMOTE, _Size == sizeof(l2cap_conn_rsp)); - - LOG(WII_IPC_WIIMOTE, " CommandConnectionResponse"); - LOG(WII_IPC_WIIMOTE, " DCID: 0x%04x", rsp->dcid); - LOG(WII_IPC_WIIMOTE, " SCID: 0x%04x", rsp->scid); - LOG(WII_IPC_WIIMOTE, " Result: 0x%04x", rsp->result); - LOG(WII_IPC_WIIMOTE, " Status: 0x%04x", rsp->status); - - _dbg_assert_(WII_IPC_WIIMOTE, rsp->result == 0); - _dbg_assert_(WII_IPC_WIIMOTE, rsp->status == 0); - - _dbg_assert_(WII_IPC_WIIMOTE, DoesChannelExist(rsp->scid)); - SChannel& rChannel = m_Channel[rsp->scid]; - rChannel.DCID = rsp->dcid; - - // update state machine - if (rChannel.PSM == HIDP_CONTROL_CHANNEL) - m_HIDControlChannel_Connected = true; - - if (rChannel.PSM == HID_INTERRUPT_CHANNEL) - m_HIDInterruptChannel_Connected = true; -} - -void CWII_IPC_HLE_WiiMote::CommandCofigurationResponse(u8 _Ident, u8* _pData, u32 _Size) -{ - l2cap_conf_rsp* rsp = (l2cap_conf_rsp*)_pData; - - _dbg_assert_(WII_IPC_WIIMOTE, _Size == sizeof(l2cap_conf_rsp)); - - LOG(WII_IPC_WIIMOTE, " CommandCofigurationResponse"); - LOG(WII_IPC_WIIMOTE, " SCID: 0x%04x", rsp->scid); - LOG(WII_IPC_WIIMOTE, " Flags: 0x%04x", rsp->flags); - LOG(WII_IPC_WIIMOTE, " Result: 0x%04x", rsp->result); - - _dbg_assert_(WII_IPC_WIIMOTE, rsp->result == 0); - - // update state machine - SChannel& rChannel = m_Channel[rsp->scid]; - if (rChannel.PSM == HIDP_CONTROL_CHANNEL) - m_HIDControlChannel_Config = true; - - if (rChannel.PSM == HID_INTERRUPT_CHANNEL) - m_HIDInterruptChannel_Config = true; -} - -void CWII_IPC_HLE_WiiMote::CommandDisconnectionReq(u8 _Ident, u8* _pData, u32 _Size) -{ - SL2CAP_CommandDisconnectionReq* pCommandDisconnectionReq = (SL2CAP_CommandDisconnectionReq*)_pData; - - // create the channel - _dbg_assert_(WII_IPC_WIIMOTE, m_Channel.find(pCommandDisconnectionReq->scid) != m_Channel.end()); - - LOG(WII_IPC_WIIMOTE, " CommandDisconnectionReq"); - LOG(WII_IPC_WIIMOTE, " Ident: 0x%02x", _Ident); - LOG(WII_IPC_WIIMOTE, " SCID: 0x%04x", pCommandDisconnectionReq->dcid); - LOG(WII_IPC_WIIMOTE, " DCID: 0x%04x", pCommandDisconnectionReq->scid); - - // response - SL2CAP_CommandDisconnectionResponse Rsp; - Rsp.scid = pCommandDisconnectionReq->scid; - Rsp.dcid = pCommandDisconnectionReq->dcid; - - SendCommandToACL(_Ident, L2CAP_DISCONN_RSP, sizeof(SL2CAP_CommandDisconnectionResponse), (u8*)&Rsp); -} - -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// -// -// -// --- Send Commands To Device -// -// -// -// -// -///////////////////////////////////////////////////////////////////////////////////////////////// - - -void CWII_IPC_HLE_WiiMote::SendConnectionRequest(u16 scid, u16 psm) -{ - // create the channel - SChannel& rChannel = m_Channel[scid]; - rChannel.PSM = psm; - rChannel.SCID = scid; - - l2cap_conn_req cr; - cr.psm = psm; - cr.scid = scid; - - LOG(WII_IPC_WIIMOTE, " SendConnectionRequest()"); - LOG(WII_IPC_WIIMOTE, " Psm: 0x%04x", cr.psm); - LOG(WII_IPC_WIIMOTE, " Scid: 0x%04x", cr.scid); - - SendCommandToACL(L2CAP_CONN_REQ, L2CAP_CONN_REQ, sizeof(l2cap_conn_req), (u8*)&cr); -} - -void CWII_IPC_HLE_WiiMote::SendDisconnectRequest(u16 scid) -{ - // create the channel - SChannel& rChannel = m_Channel[scid]; - - l2cap_disconn_req cr; - cr.dcid = rChannel.DCID; - cr.scid = rChannel.SCID; - - LOG(WII_IPC_WIIMOTE, " SendDisconnectionRequest()"); - LOG(WII_IPC_WIIMOTE, " Dcid: 0x%04x", cr.dcid); - LOG(WII_IPC_WIIMOTE, " Scid: 0x%04x", cr.scid); - - SendCommandToACL(L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ, sizeof(l2cap_disconn_req), (u8*)&cr); -} - -void CWII_IPC_HLE_WiiMote::SendConfigurationRequest(u16 scid, u16* MTU, u16* FlushTimeOut) -{ - _dbg_assert_(WII_IPC_WIIMOTE, DoesChannelExist(scid)); - SChannel& rChannel = m_Channel[scid]; - - u8 Buffer[1024]; - int Offset = 0; - - l2cap_conf_req* cr = (l2cap_conf_req*)&Buffer[Offset]; - cr->dcid = rChannel.DCID; - cr->flags = 0; - Offset += sizeof(l2cap_conf_req); - - if (MTU != NULL) - { - SL2CAP_Options* pOptions = (SL2CAP_Options*)&Buffer[Offset]; - Offset += sizeof(SL2CAP_Options); - - pOptions->type = 1; - pOptions->length = 2; - - *(u16*)&Buffer[Offset] = *MTU; Offset += 2; - } - - if (FlushTimeOut != NULL) - { - SL2CAP_Options* pOptions = (SL2CAP_Options*)&Buffer[Offset]; - Offset += sizeof(SL2CAP_Options); - - pOptions->type = 2; - pOptions->length = 2; - - *(u16*)&Buffer[Offset] = *FlushTimeOut; Offset += 2; - } - - LOG(WII_IPC_WIIMOTE, " SendConfigurationRequest()"); - LOG(WII_IPC_WIIMOTE, " Dcid: 0x%04x", cr->dcid); - LOG(WII_IPC_WIIMOTE, " Flags: 0x%04x", cr->flags); - - // hack: - static u8 ident = 99; - ident++; - SendCommandToACL(L2CAP_CONF_REQ, L2CAP_CONF_REQ, Offset, Buffer); -} - -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// -// -// -// --- SDP -// -// -// -// -// -///////////////////////////////////////////////////////////////////////////////////////////////// - -#define SDP_UINT8 0x08 -#define SDP_UINT16 0x09 -#define SDP_UINT32 0x0A -#define SDP_SEQ8 0x35 -#define SDP_SEQ16 0x36 - -void CWII_IPC_HLE_WiiMote::SDPSendServiceSearchResponse(u16 cid, u16 TransactionID, u8* pServiceSearchPattern, u16 MaximumServiceRecordCount) -{ - // verify block... we hanlde search pattern for HID service only - { - CBigEndianBuffer buffer(pServiceSearchPattern); - _dbg_assert_(WII_IPC_WIIMOTE, buffer.Read8(0) == SDP_SEQ8); // data sequence - _dbg_assert_(WII_IPC_WIIMOTE, buffer.Read8(1) == 0x03); // sequence size - - // HIDClassID - _dbg_assert_(WII_IPC_WIIMOTE, buffer.Read8(2) == 0x19); - _dbg_assert_(WII_IPC_WIIMOTE, buffer.Read16(3) == 0x1124); - } - - u8 DataFrame[1000]; - CBigEndianBuffer buffer(DataFrame); - - int Offset = 0; - SL2CAP_Header* pHeader = (SL2CAP_Header*)&DataFrame[Offset]; Offset += sizeof(SL2CAP_Header); - pHeader->CID = cid; - - buffer.Write8 (Offset, 0x03); Offset++; - buffer.Write16(Offset, TransactionID); Offset += 2; // transaction ID - buffer.Write16(Offset, 0x0009); Offset += 2; // param length - buffer.Write16(Offset, 0x0001); Offset += 2; // TotalServiceRecordCount - buffer.Write16(Offset, 0x0001); Offset += 2; // CurrentServiceRecordCount - buffer.Write32(Offset, 0x10000); Offset += 4; // ServiceRecordHandleList[4] - buffer.Write8(Offset, 0x00); Offset++; // no continuation state; - - - pHeader->Length = (u16)(Offset - sizeof(SL2CAP_Header)); - m_pHost->SendACLFrame(GetConnectionHandle(), DataFrame, pHeader->Length + sizeof(SL2CAP_Header)); -} - -u32 ParseCont(u8* pCont) -{ - u32 attribOffset = 0; - CBigEndianBuffer attribList(pCont); - u8 typeID = attribList.Read8(attribOffset); attribOffset++; - - if (typeID == 0x02) - { - return attribList.Read16(attribOffset); - } - else if (typeID == 0x00) - { - return 0x00; - } - - PanicAlert("wrong cont: %i", typeID); - return 0; -} - - -int ParseAttribList(u8* pAttribIDList, u16& _startID, u16& _endID) -{ - u32 attribOffset = 0; - CBigEndianBuffer attribList(pAttribIDList); - - u8 sequence = attribList.Read8(attribOffset); attribOffset++; _dbg_assert_(WII_IPC_WIIMOTE, sequence == SDP_SEQ8); - u8 seqSize = attribList.Read8(attribOffset); attribOffset++; - u8 typeID = attribList.Read8(attribOffset); attribOffset++; - - if (typeID == SDP_UINT32) - { - _startID = attribList.Read16(attribOffset); attribOffset += 2; - _endID = attribList.Read16(attribOffset); attribOffset += 2; - } - else - { - _startID = attribList.Read16(attribOffset); attribOffset += 2; - _endID = _startID; - PanicAlert("Read just a single attrib - not tested"); - } - - return attribOffset; -} - - -void CWII_IPC_HLE_WiiMote::SDPSendServiceAttributeResponse(u16 cid, u16 TransactionID, u32 ServiceHandle, - u16 startAttrID, u16 endAttrID, - u16 MaximumAttributeByteCount, u8* pContinuationState) -{ - if (ServiceHandle != 0x10000) - { - PanicAlert("unknown service handle %x" , ServiceHandle); - } - - -// _dbg_assert_(WII_IPC_WIIMOTE, ServiceHandle == 0x10000); - - u32 contState = ParseCont(pContinuationState); - - u32 packetSize = 0; - const u8* pPacket = GetAttribPacket(ServiceHandle, contState, packetSize); - - // generate package - u8 DataFrame[1000]; - CBigEndianBuffer buffer(DataFrame); - - int Offset = 0; - SL2CAP_Header* pHeader = (SL2CAP_Header*)&DataFrame[Offset]; Offset += sizeof(SL2CAP_Header); - pHeader->CID = cid; - - buffer.Write8 (Offset, 0x05); Offset++; - buffer.Write16(Offset, TransactionID); Offset += 2; // transaction ID - - memcpy(buffer.GetPointer(Offset), pPacket, packetSize); Offset += packetSize; - - pHeader->Length = (u16)(Offset - sizeof(SL2CAP_Header)); - m_pHost->SendACLFrame(GetConnectionHandle(), DataFrame, pHeader->Length + sizeof(SL2CAP_Header)); - -// Debugger::PrintDataBuffer(LogTypes::WIIMOTE, DataFrame, pHeader->Length + sizeof(SL2CAP_Header), "test response: "); -} - -void CWII_IPC_HLE_WiiMote::HandleSDP(u16 cid, u8* _pData, u32 _Size) -{ - // Debugger::PrintDataBuffer(LogTypes::WIIMOTE, _pData, _Size, "HandleSDP: "); - - CBigEndianBuffer buffer(_pData); - - switch(buffer.Read8(0)) - { - // SDP_ServiceSearchRequest - case 0x02: - { - LOG(WII_IPC_WIIMOTE, "!!! SDP_ServiceSearchRequest !!!"); - - _dbg_assert_(WII_IPC_WIIMOTE, _Size == 13); - - u16 TransactionID = buffer.Read16(1); - u16 ParameterLength = buffer.Read16(3); - u8* pServiceSearchPattern = buffer.GetPointer(5); - u16 MaximumServiceRecordCount = buffer.Read16(10); - u8 ContinuationState = buffer.Read8(12); - - SDPSendServiceSearchResponse(cid, TransactionID, pServiceSearchPattern, MaximumServiceRecordCount); - } - break; - - // SDP_ServiceAttributeRequest - case 0x04: - { - LOG(WII_IPC_WIIMOTE, "!!! SDP_ServiceAttributeRequest !!!"); - - u16 startAttrID, endAttrID; - u32 offset = 1; - - u16 TransactionID = buffer.Read16(offset); offset += 2; - u16 ParameterLength = buffer.Read16(offset); offset += 2; - u32 ServiceHandle = buffer.Read32(offset); offset += 4; - u16 MaximumAttributeByteCount = buffer.Read16(offset); offset += 2; - offset += ParseAttribList(buffer.GetPointer(offset), startAttrID, endAttrID); - u8* pContinuationState = buffer.GetPointer(offset); - - SDPSendServiceAttributeResponse(cid, TransactionID, ServiceHandle, startAttrID, endAttrID, MaximumAttributeByteCount, pContinuationState); - } - break; - - default: - PanicAlert("Unknown SDP command %x", _pData[0]); - break; - } -} - -///////////////////////////////////////////////////////////////////////////////////////////////// -// -// -// -// -// --- Data Send Functions -// -// -// -// -// -///////////////////////////////////////////////////////////////////////////////////////////////// - - -void CWII_IPC_HLE_WiiMote::SendCommandToACL(u8 _Ident, u8 _Code, u8 _CommandLength, u8* _pCommandData) -{ - u8 DataFrame[1024]; - u32 Offset = 0; - - SL2CAP_Header* pHeader = (SL2CAP_Header*)&DataFrame[Offset]; Offset += sizeof(SL2CAP_Header); - pHeader->CID = 0x0001; - pHeader->Length = sizeof(SL2CAP_Command) + _CommandLength; - - SL2CAP_Command* pCommand = (SL2CAP_Command*)&DataFrame[Offset]; Offset += sizeof(SL2CAP_Command); - pCommand->len = _CommandLength; - pCommand->ident = _Ident; - pCommand->code = _Code; - - memcpy(&DataFrame[Offset], _pCommandData, _CommandLength); - - LOG(WII_IPC_WIIMOTE, " SendCommandToACL (answer)"); - LOG(WII_IPC_WIIMOTE, " Ident: 0x%02x", _Ident); - LOG(WII_IPC_WIIMOTE, " Code: 0x%02x", _Code); - - // send .... - m_pHost->SendACLFrame(GetConnectionHandle(), DataFrame, pHeader->Length + sizeof(SL2CAP_Header)); - - // Debugger::PrintDataBuffer(LogTypes::WIIMOTE, DataFrame, pHeader->Length + sizeof(SL2CAP_Header), "m_pHost->SendACLFrame: "); -} - -void CWII_IPC_HLE_WiiMote::SendL2capData(u16 scid, const void* _pData, u32 _Size) -{ - //allocate - u8 DataFrame[1024]; - u32 Offset = 0; - SL2CAP_Header* pHeader = (SL2CAP_Header*)DataFrame; - Offset += sizeof(SL2CAP_Header); - - _dbg_assert_(WII_IPC_WIIMOTE, DoesChannelExist(scid)); - SChannel& rChannel = m_Channel[scid]; - - //assemble - pHeader->CID = rChannel.DCID; - pHeader->Length = _Size; - - memcpy(DataFrame + Offset, _pData, _Size); - Offset += _Size; - - //send - m_pHost->SendACLFrame(GetConnectionHandle(), DataFrame, Offset); - - // - Host_SetWiiMoteConnectionState(2); -} - - -namespace Core -{ - /* This is called continously from the Wiimote plugin as soon as it has received - a reporting mode */ - void Callback_WiimoteInput(u16 _channelID, const void* _pData, u32 _Size) - { - LOGV(WII_IPC_WIIMOTE, 3, "========================================================="); - const u8* pData = (const u8*)_pData; - LOGV(WII_IPC_WIIMOTE, 3, "Callback_WiimoteInput:"); - std::string Temp; - for (u32 j=0; j<_Size; j++) - { - char Buffer[128]; - sprintf(Buffer, "%02x ", pData[j]); - Temp.append(Buffer); - } - LOGV(WII_IPC_WIIMOTE, 3, " Data: %s", Temp.c_str()); - //LOGV(WII_IPC_WIIMOTE, 3, " Channel: %s", _channelID); - - s_Usb->m_WiiMotes[0].SendL2capData(_channelID, _pData, _Size); - LOGV(WII_IPC_WIIMOTE, 3, "========================================================="); - } -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "WII_IPC_HLE_WiiMote.h" + +#include "../Plugins/Plugin_Wiimote.h" +#include "../Host.h" + +#include "WII_IPC_HLE_Device_usb.h" + +#include "l2cap.h" +#include "WiiMote_HID_Attr.h" + +#if defined(_MSC_VER) +#pragma pack(push, 1) +#endif + +#define SDP_CHANNEL 0x01 +#define HIDP_CONTROL_CHANNEL 0x11 +#define HID_INTERRUPT_CHANNEL 0x13 + + +struct SL2CAP_Header +{ + u16 Length; + u16 CID; +}; + +/* L2CAP command codes */ +#define L2CAP_COMMAND_REJ 0x01 +#define L2CAP_CONN_REQ 0x02 +#define L2CAP_CONN_RSP 0x03 +#define L2CAP_CONF_REQ 0x04 +#define L2CAP_CONF_RSP 0x05 +#define L2CAP_DISCONN_REQ 0x06 +#define L2CAP_DISCONN_RSP 0x07 +#define L2CAP_ECHO_REQ 0x08 +#define L2CAP_ECHO_RSP 0x09 +#define L2CAP_INFO_REQ 0x0a +#define L2CAP_INFO_RSP 0x0b + +/* connect result */ +#define L2CAP_CR_SUCCESS 0x0000 +#define L2CAP_CR_PEND 0x0001 +#define L2CAP_CR_BAD_PSM 0x0002 +#define L2CAP_CR_SEC_BLOCK 0x0003 +#define L2CAP_CR_NO_MEM 0x0004 + +/* connect status */ +#define L2CAP_CS_NO_INFO 0x0000 +#define L2CAP_CS_AUTHEN_PEND 0x0001 +#define L2CAP_CS_AUTHOR_PEND 0x0002 + + +struct SL2CAP_Command +{ + u8 code; + u8 ident; + u16 len; +}; + +struct SL2CAP_CommandConnectionReq // 0x02 +{ + u16 psm; + u16 scid; +}; + +struct SL2CAP_ConnectionResponse // 0x03 +{ + u16 dcid; + u16 scid; + u16 result; + u16 status; +}; + +struct SL2CAP_Options +{ + u8 type; + u8 length; +}; + +struct SL2CAP_OptionsMTU +{ + u16 MTU; +}; + +struct SL2CAP_OptionsFlushTimeOut +{ + u16 TimeOut; +}; + +struct SL2CAP_CommandConfigurationReq // 0x04 +{ + u16 dcid; + u16 flags; +}; + +struct SL2CAP_CommandConfigurationResponse // 0x05 +{ + u16 scid; + u16 flags; + u16 result; +}; + +struct SL2CAP_CommandDisconnectionReq // 0x06 +{ + u16 dcid; + u16 scid; +}; + +struct SL2CAP_CommandDisconnectionResponse // 0x07 +{ + u16 dcid; + u16 scid; +}; + +#if defined(_MSC_VER) +#pragma pack(pop) +#endif + +static CWII_IPC_HLE_Device_usb_oh1_57e_305* s_Usb; + + +CWII_IPC_HLE_WiiMote::CWII_IPC_HLE_WiiMote(CWII_IPC_HLE_Device_usb_oh1_57e_305* _pHost, int _Number) + : m_Connected(false) + , m_HIDControlChannel_Connected(false) + , m_HIDControlChannel_ConnectedWait(false) + , m_HIDControlChannel_Config(false) + , m_HIDControlChannel_ConfigWait(false) + , m_HIDInterruptChannel_Connected(false) + , m_HIDInterruptChannel_ConnectedWait(false) + , m_HIDInterruptChannel_Config(false) + , m_HIDInterruptChannel_ConfigWait(false) + , m_Name("Nintendo RVL-CNT-01") + , m_pHost(_pHost) + + +{ + s_Usb = _pHost; + LOG(WII_IPC_WIIMOTE, "Wiimote %i constructed", _Number); + + m_BD.b[0] = 0x11; + m_BD.b[1] = 0x02; + m_BD.b[2] = 0x19; + m_BD.b[3] = 0x79; + m_BD.b[4] = 0x00; + m_BD.b[5] = _Number; + + m_ControllerConnectionHandle = 0x100 + _Number; + + uclass[0]= 0x00; + uclass[1]= 0x04; + uclass[2]= 0x48; + + features[0] = 0xBC; + features[1] = 0x02; + features[2] = 0x04; + features[3] = 0x38; + features[4] = 0x08; + features[5] = 0x00; + features[6] = 0x00; + features[7] = 0x00; + + lmp_version = 0x2; + lmp_subversion = 0x229; + + memset(m_LinkKey, 0xA0 + _Number, 16); +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// +// +// +// --- Simple and ugly state machine +// +// +// +// +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +bool CWII_IPC_HLE_WiiMote::Update() +{ + if (m_Connected == false) + return false; + + // try to connect HIDP_CONTROL_CHANNEL + if (!m_HIDControlChannel_Connected) + { + if (m_HIDControlChannel_ConnectedWait) + return false; + + m_HIDControlChannel_ConnectedWait = true; + SendConnectionRequest(0x0040, HIDP_CONTROL_CHANNEL); + + return true; + } + + // try to config HIDP_CONTROL_CHANNEL + if (!m_HIDControlChannel_Config) + { + if (m_HIDControlChannel_ConfigWait) + return false; + + SChannel& rChannel = m_Channel[0x0040]; + + m_HIDControlChannel_ConfigWait = true; + SendConfigurationRequest(rChannel.DCID); + return true; + } + + // try to connect HID_INTERRUPT_CHANNEL + if (!m_HIDInterruptChannel_Connected) + { + if (m_HIDInterruptChannel_ConnectedWait) + return false; + + m_HIDInterruptChannel_ConnectedWait = true; + SendConnectionRequest(0x0041, HID_INTERRUPT_CHANNEL); + return true; + } + + // try to config HID_INTERRUPT_CHANNEL + if (!m_HIDInterruptChannel_Config) + { + if (m_HIDInterruptChannel_ConfigWait) + return false; + + m_HIDInterruptChannel_ConfigWait = true; + + SChannel& rChannel = m_Channel[0x0041]; + SendConfigurationRequest(rChannel.DCID); + return true; + } + + return false; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// +// +// +// --- Events +// +// +// +// +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +void CWII_IPC_HLE_WiiMote::EventConnectionAccepted() +{ + m_Connected = true; +} + +void CWII_IPC_HLE_WiiMote::EventDisconnect() +{ + m_Connected = false; +} + +bool CWII_IPC_HLE_WiiMote::EventPagingChanged(u8 _pageMode) +{ + if (m_Connected) + return false; + + if ((_pageMode & 2) == 0) + return false; + + return true; +} + +void CWII_IPC_HLE_WiiMote::EventCommandWriteLinkPolicy() +{ + // reset connection process + + m_HIDControlChannel_Connected = false; + m_HIDControlChannel_Config = false; + m_HIDInterruptChannel_Connected = false; + m_HIDInterruptChannel_Config = false; + m_HIDControlChannel_ConnectedWait = false; + m_HIDControlChannel_ConfigWait = false; + m_HIDInterruptChannel_ConnectedWait = false; + m_HIDInterruptChannel_ConfigWait = false; +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// +// +// +// --- Input parsing +// +// +// +// +// +///////////////////////////////////////////////////////////////////////////////////////////////// + + +// =================================================== +/* This function send ACL frams from the Wii to Wiimote_ControlChannel() in the Wiimote. + It's called from SendToDevice() in WII_IPC_HLE_Device_usb.cpp. */ +// ---------------- +void CWII_IPC_HLE_WiiMote::SendACLFrame(u8* _pData, u32 _Size) +{ + // Debugger::PrintDataBuffer(LogTypes::WIIMOTE, _pData, _Size, "SendACLFrame: "); + + // parse the command + SL2CAP_Header* pHeader = (SL2CAP_Header*)_pData; + u8* pData = _pData + sizeof(SL2CAP_Header); + u32 DataSize = _Size - sizeof(SL2CAP_Header); + + LOGV(WII_IPC_WIIMOTE, 2, "L2Cap-SendFrame: Channel 0x%04x, Len 0x%x, DataSize 0x%x", + pHeader->CID, pHeader->Length, DataSize); + + if(pHeader->Length != DataSize) + { + LOGV(WII_IPC_WIIMOTE, 2, "Faulty packet. It is dropped."); + return; + } + + switch (pHeader->CID) + { + case 0x0001: + SignalChannel(pData, DataSize); + break; + + default: + { + _dbg_assert_msg_(WII_IPC_WIIMOTE, DoesChannelExist(pHeader->CID), "SendACLFrame to unknown channel %i", pHeader->CID); + CChannelMap::iterator itr= m_Channel.find(pHeader->CID); + if (itr != m_Channel.end()) + { + SChannel& rChannel = itr->second; + switch(rChannel.PSM) + { + case SDP_CHANNEL: + HandleSDP(pHeader->CID, pData, DataSize); + break; + + case HIDP_CONTROL_CHANNEL: + PluginWiimote::Wiimote_ControlChannel(rChannel.DCID, pData, DataSize); + break; + + case HID_INTERRUPT_CHANNEL: + PluginWiimote::Wiimote_InterruptChannel(rChannel.DCID, pData, DataSize); + break; + + default: + PanicAlert("channel 0x04%x has unknown PSM %x", pHeader->CID, rChannel.PSM); + break; + } + } + } + break; + } +} + +void CWII_IPC_HLE_WiiMote::SignalChannel(u8* _pData, u32 _Size) +{ + while (_Size >= sizeof(SL2CAP_Command)) + { + SL2CAP_Command* pCommand = (SL2CAP_Command*)_pData; + _pData += sizeof(SL2CAP_Command); + _Size = _Size - sizeof(SL2CAP_Command) - pCommand->len; + + switch(pCommand->code) + { + case L2CAP_CONN_REQ: + CommandConnectionReq(pCommand->ident, _pData, pCommand->len); + break; + + case L2CAP_CONF_REQ: + CommandCofigurationReq(pCommand->ident, _pData, pCommand->len); + break; + + case L2CAP_CONN_RSP: + CommandConnectionResponse(pCommand->ident, _pData, pCommand->len); + break; + + case L2CAP_DISCONN_REQ: + CommandDisconnectionReq(pCommand->ident, _pData, pCommand->len); + break; + + case L2CAP_CONF_RSP: + CommandCofigurationResponse(pCommand->ident, _pData, pCommand->len); + break; + + case L2CAP_COMMAND_REJ: + PanicAlert("SignalChannel - L2CAP_COMMAND_REJ (something went wrong)",pCommand->code); + break; + + default: + LOG(WII_IPC_WIIMOTE, " Unknown Command-Code (0x%02x)", pCommand->code); + PanicAlert("SignalChannel %x",pCommand->code); + return; + } + + _pData += pCommand->len; + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// +// +// +// --- Send Commands To Device +// +// +// +// +// +///////////////////////////////////////////////////////////////////////////////////////////////// + + +void CWII_IPC_HLE_WiiMote::CommandConnectionReq(u8 _Ident, u8* _pData, u32 _Size) +{ + SL2CAP_CommandConnectionReq* pCommandConnectionReq = (SL2CAP_CommandConnectionReq*)_pData; + + // create the channel + SChannel& rChannel = m_Channel[pCommandConnectionReq->scid]; + rChannel.PSM = pCommandConnectionReq->psm; + rChannel.SCID = pCommandConnectionReq->scid; + rChannel.DCID = pCommandConnectionReq->scid; + + LOG(WII_IPC_WIIMOTE, " CommandConnectionReq"); + LOG(WII_IPC_WIIMOTE, " Ident: 0x%02x", _Ident); + LOG(WII_IPC_WIIMOTE, " PSM: 0x%04x", rChannel.PSM); + LOG(WII_IPC_WIIMOTE, " SCID: 0x%04x", rChannel.SCID); + LOG(WII_IPC_WIIMOTE, " DCID: 0x%04x", rChannel.DCID); + + // response + SL2CAP_ConnectionResponse Rsp; + Rsp.scid = rChannel.SCID; + Rsp.dcid = rChannel.DCID; + Rsp.result = 0x00; + Rsp.status = 0x00; + + SendCommandToACL(_Ident, L2CAP_CONN_RSP, sizeof(SL2CAP_ConnectionResponse), (u8*)&Rsp); + + // update state machine + if (rChannel.PSM == HIDP_CONTROL_CHANNEL) + m_HIDControlChannel_Connected = true; + + if (rChannel.PSM == HID_INTERRUPT_CHANNEL) + m_HIDInterruptChannel_Connected = true; +} + +void CWII_IPC_HLE_WiiMote::CommandCofigurationReq(u8 _Ident, u8* _pData, u32 _Size) +{ + LOGV(WII_IPC_WIIMOTE, 1, "*******************************************************"); + u32 Offset = 0; + SL2CAP_CommandConfigurationReq* pCommandConfigReq = (SL2CAP_CommandConfigurationReq*)_pData; + + _dbg_assert_(WII_IPC_WIIMOTE, pCommandConfigReq->flags == 0x00); // 1 means that the options are send in multi-packets + + _dbg_assert_(WII_IPC_WIIMOTE, DoesChannelExist(pCommandConfigReq->dcid)); + SChannel& rChannel = m_Channel[pCommandConfigReq->dcid]; + + LOGV(WII_IPC_WIIMOTE, 1, " CommandCofigurationReq"); + LOGV(WII_IPC_WIIMOTE, 1, " Ident: 0x%02x", _Ident); + LOGV(WII_IPC_WIIMOTE, 1, " DCID: 0x%04x", pCommandConfigReq->dcid); + LOGV(WII_IPC_WIIMOTE, 1, " Flags: 0x%04x", pCommandConfigReq->flags); + + Offset += sizeof(SL2CAP_CommandConfigurationReq); + + u8 TempBuffer[1024]; + u32 RespLen = 0; + + SL2CAP_CommandConfigurationResponse* Rsp = (SL2CAP_CommandConfigurationResponse*)TempBuffer; + Rsp->scid = rChannel.DCID; + Rsp->flags = 0x00; + Rsp->result = 0x00; + + RespLen += sizeof(SL2CAP_CommandConfigurationResponse); + + // read configuration options + while (Offset < _Size) + { + SL2CAP_Options* pOptions = (SL2CAP_Options*)&_pData[Offset]; + Offset += sizeof(SL2CAP_Options); + + switch(pOptions->type) + { + case 0x01: + { + _dbg_assert_(WII_IPC_WIIMOTE, pOptions->length == 2); + SL2CAP_OptionsMTU* pMTU = (SL2CAP_OptionsMTU*)&_pData[Offset]; + rChannel.MTU = pMTU->MTU; + LOGV(WII_IPC_WIIMOTE, 1, " Config MTU: 0x%04x", pMTU->MTU); + } + break; + + case 0x02: + { + _dbg_assert_(WII_IPC_WIIMOTE, pOptions->length == 2); + SL2CAP_OptionsFlushTimeOut* pFlushTimeOut = (SL2CAP_OptionsFlushTimeOut*)&_pData[Offset]; + rChannel.FlushTimeOut = pFlushTimeOut->TimeOut; + LOGV(WII_IPC_WIIMOTE, 1, " Config FlushTimeOut: 0x%04x", pFlushTimeOut->TimeOut); + } + break; + + default: + _dbg_assert_msg_(WII_IPC_WIIMOTE, 0, "Unknown Option: 0x%02x", pOptions->type); + break; + } + + Offset += pOptions->length; + + u32 OptionSize = sizeof(SL2CAP_Options) + pOptions->length; + memcpy(&TempBuffer[RespLen], pOptions, OptionSize); + RespLen += OptionSize; + } + + SendCommandToACL(_Ident, L2CAP_CONF_RSP, RespLen, TempBuffer); + LOGV(WII_IPC_WIIMOTE, 1, "*******************************************************"); +} + +void CWII_IPC_HLE_WiiMote::CommandConnectionResponse(u8 _Ident, u8* _pData, u32 _Size) +{ + l2cap_conn_rsp* rsp = (l2cap_conn_rsp*)_pData; + + _dbg_assert_(WII_IPC_WIIMOTE, _Size == sizeof(l2cap_conn_rsp)); + + LOG(WII_IPC_WIIMOTE, " CommandConnectionResponse"); + LOG(WII_IPC_WIIMOTE, " DCID: 0x%04x", rsp->dcid); + LOG(WII_IPC_WIIMOTE, " SCID: 0x%04x", rsp->scid); + LOG(WII_IPC_WIIMOTE, " Result: 0x%04x", rsp->result); + LOG(WII_IPC_WIIMOTE, " Status: 0x%04x", rsp->status); + + _dbg_assert_(WII_IPC_WIIMOTE, rsp->result == 0); + _dbg_assert_(WII_IPC_WIIMOTE, rsp->status == 0); + + _dbg_assert_(WII_IPC_WIIMOTE, DoesChannelExist(rsp->scid)); + SChannel& rChannel = m_Channel[rsp->scid]; + rChannel.DCID = rsp->dcid; + + // update state machine + if (rChannel.PSM == HIDP_CONTROL_CHANNEL) + m_HIDControlChannel_Connected = true; + + if (rChannel.PSM == HID_INTERRUPT_CHANNEL) + m_HIDInterruptChannel_Connected = true; +} + +void CWII_IPC_HLE_WiiMote::CommandCofigurationResponse(u8 _Ident, u8* _pData, u32 _Size) +{ + l2cap_conf_rsp* rsp = (l2cap_conf_rsp*)_pData; + + _dbg_assert_(WII_IPC_WIIMOTE, _Size == sizeof(l2cap_conf_rsp)); + + LOG(WII_IPC_WIIMOTE, " CommandCofigurationResponse"); + LOG(WII_IPC_WIIMOTE, " SCID: 0x%04x", rsp->scid); + LOG(WII_IPC_WIIMOTE, " Flags: 0x%04x", rsp->flags); + LOG(WII_IPC_WIIMOTE, " Result: 0x%04x", rsp->result); + + _dbg_assert_(WII_IPC_WIIMOTE, rsp->result == 0); + + // update state machine + SChannel& rChannel = m_Channel[rsp->scid]; + if (rChannel.PSM == HIDP_CONTROL_CHANNEL) + m_HIDControlChannel_Config = true; + + if (rChannel.PSM == HID_INTERRUPT_CHANNEL) + m_HIDInterruptChannel_Config = true; +} + +void CWII_IPC_HLE_WiiMote::CommandDisconnectionReq(u8 _Ident, u8* _pData, u32 _Size) +{ + SL2CAP_CommandDisconnectionReq* pCommandDisconnectionReq = (SL2CAP_CommandDisconnectionReq*)_pData; + + // create the channel + _dbg_assert_(WII_IPC_WIIMOTE, m_Channel.find(pCommandDisconnectionReq->scid) != m_Channel.end()); + + LOG(WII_IPC_WIIMOTE, " CommandDisconnectionReq"); + LOG(WII_IPC_WIIMOTE, " Ident: 0x%02x", _Ident); + LOG(WII_IPC_WIIMOTE, " SCID: 0x%04x", pCommandDisconnectionReq->dcid); + LOG(WII_IPC_WIIMOTE, " DCID: 0x%04x", pCommandDisconnectionReq->scid); + + // response + SL2CAP_CommandDisconnectionResponse Rsp; + Rsp.scid = pCommandDisconnectionReq->scid; + Rsp.dcid = pCommandDisconnectionReq->dcid; + + SendCommandToACL(_Ident, L2CAP_DISCONN_RSP, sizeof(SL2CAP_CommandDisconnectionResponse), (u8*)&Rsp); +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// +// +// +// --- Send Commands To Device +// +// +// +// +// +///////////////////////////////////////////////////////////////////////////////////////////////// + + +void CWII_IPC_HLE_WiiMote::SendConnectionRequest(u16 scid, u16 psm) +{ + // create the channel + SChannel& rChannel = m_Channel[scid]; + rChannel.PSM = psm; + rChannel.SCID = scid; + + l2cap_conn_req cr; + cr.psm = psm; + cr.scid = scid; + + LOG(WII_IPC_WIIMOTE, " SendConnectionRequest()"); + LOG(WII_IPC_WIIMOTE, " Psm: 0x%04x", cr.psm); + LOG(WII_IPC_WIIMOTE, " Scid: 0x%04x", cr.scid); + + SendCommandToACL(L2CAP_CONN_REQ, L2CAP_CONN_REQ, sizeof(l2cap_conn_req), (u8*)&cr); +} + +void CWII_IPC_HLE_WiiMote::SendDisconnectRequest(u16 scid) +{ + // create the channel + SChannel& rChannel = m_Channel[scid]; + + l2cap_disconn_req cr; + cr.dcid = rChannel.DCID; + cr.scid = rChannel.SCID; + + LOG(WII_IPC_WIIMOTE, " SendDisconnectionRequest()"); + LOG(WII_IPC_WIIMOTE, " Dcid: 0x%04x", cr.dcid); + LOG(WII_IPC_WIIMOTE, " Scid: 0x%04x", cr.scid); + + SendCommandToACL(L2CAP_DISCONN_REQ, L2CAP_DISCONN_REQ, sizeof(l2cap_disconn_req), (u8*)&cr); +} + +void CWII_IPC_HLE_WiiMote::SendConfigurationRequest(u16 scid, u16* MTU, u16* FlushTimeOut) +{ + _dbg_assert_(WII_IPC_WIIMOTE, DoesChannelExist(scid)); + SChannel& rChannel = m_Channel[scid]; + + u8 Buffer[1024]; + int Offset = 0; + + l2cap_conf_req* cr = (l2cap_conf_req*)&Buffer[Offset]; + cr->dcid = rChannel.DCID; + cr->flags = 0; + Offset += sizeof(l2cap_conf_req); + + if (MTU != NULL) + { + SL2CAP_Options* pOptions = (SL2CAP_Options*)&Buffer[Offset]; + Offset += sizeof(SL2CAP_Options); + + pOptions->type = 1; + pOptions->length = 2; + + *(u16*)&Buffer[Offset] = *MTU; Offset += 2; + } + + if (FlushTimeOut != NULL) + { + SL2CAP_Options* pOptions = (SL2CAP_Options*)&Buffer[Offset]; + Offset += sizeof(SL2CAP_Options); + + pOptions->type = 2; + pOptions->length = 2; + + *(u16*)&Buffer[Offset] = *FlushTimeOut; Offset += 2; + } + + LOG(WII_IPC_WIIMOTE, " SendConfigurationRequest()"); + LOG(WII_IPC_WIIMOTE, " Dcid: 0x%04x", cr->dcid); + LOG(WII_IPC_WIIMOTE, " Flags: 0x%04x", cr->flags); + + // hack: + static u8 ident = 99; + ident++; + SendCommandToACL(L2CAP_CONF_REQ, L2CAP_CONF_REQ, Offset, Buffer); +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// +// +// +// --- SDP +// +// +// +// +// +///////////////////////////////////////////////////////////////////////////////////////////////// + +#define SDP_UINT8 0x08 +#define SDP_UINT16 0x09 +#define SDP_UINT32 0x0A +#define SDP_SEQ8 0x35 +#define SDP_SEQ16 0x36 + +void CWII_IPC_HLE_WiiMote::SDPSendServiceSearchResponse(u16 cid, u16 TransactionID, u8* pServiceSearchPattern, u16 MaximumServiceRecordCount) +{ + // verify block... we hanlde search pattern for HID service only + { + CBigEndianBuffer buffer(pServiceSearchPattern); + _dbg_assert_(WII_IPC_WIIMOTE, buffer.Read8(0) == SDP_SEQ8); // data sequence + _dbg_assert_(WII_IPC_WIIMOTE, buffer.Read8(1) == 0x03); // sequence size + + // HIDClassID + _dbg_assert_(WII_IPC_WIIMOTE, buffer.Read8(2) == 0x19); + _dbg_assert_(WII_IPC_WIIMOTE, buffer.Read16(3) == 0x1124); + } + + u8 DataFrame[1000]; + CBigEndianBuffer buffer(DataFrame); + + int Offset = 0; + SL2CAP_Header* pHeader = (SL2CAP_Header*)&DataFrame[Offset]; Offset += sizeof(SL2CAP_Header); + pHeader->CID = cid; + + buffer.Write8 (Offset, 0x03); Offset++; + buffer.Write16(Offset, TransactionID); Offset += 2; // transaction ID + buffer.Write16(Offset, 0x0009); Offset += 2; // param length + buffer.Write16(Offset, 0x0001); Offset += 2; // TotalServiceRecordCount + buffer.Write16(Offset, 0x0001); Offset += 2; // CurrentServiceRecordCount + buffer.Write32(Offset, 0x10000); Offset += 4; // ServiceRecordHandleList[4] + buffer.Write8(Offset, 0x00); Offset++; // no continuation state; + + + pHeader->Length = (u16)(Offset - sizeof(SL2CAP_Header)); + m_pHost->SendACLFrame(GetConnectionHandle(), DataFrame, pHeader->Length + sizeof(SL2CAP_Header)); +} + +u32 ParseCont(u8* pCont) +{ + u32 attribOffset = 0; + CBigEndianBuffer attribList(pCont); + u8 typeID = attribList.Read8(attribOffset); attribOffset++; + + if (typeID == 0x02) + { + return attribList.Read16(attribOffset); + } + else if (typeID == 0x00) + { + return 0x00; + } + + PanicAlert("wrong cont: %i", typeID); + return 0; +} + + +int ParseAttribList(u8* pAttribIDList, u16& _startID, u16& _endID) +{ + u32 attribOffset = 0; + CBigEndianBuffer attribList(pAttribIDList); + + u8 sequence = attribList.Read8(attribOffset); attribOffset++; _dbg_assert_(WII_IPC_WIIMOTE, sequence == SDP_SEQ8); + u8 seqSize = attribList.Read8(attribOffset); attribOffset++; + u8 typeID = attribList.Read8(attribOffset); attribOffset++; + + if (typeID == SDP_UINT32) + { + _startID = attribList.Read16(attribOffset); attribOffset += 2; + _endID = attribList.Read16(attribOffset); attribOffset += 2; + } + else + { + _startID = attribList.Read16(attribOffset); attribOffset += 2; + _endID = _startID; + PanicAlert("Read just a single attrib - not tested"); + } + + return attribOffset; +} + + +void CWII_IPC_HLE_WiiMote::SDPSendServiceAttributeResponse(u16 cid, u16 TransactionID, u32 ServiceHandle, + u16 startAttrID, u16 endAttrID, + u16 MaximumAttributeByteCount, u8* pContinuationState) +{ + if (ServiceHandle != 0x10000) + { + PanicAlert("unknown service handle %x" , ServiceHandle); + } + + +// _dbg_assert_(WII_IPC_WIIMOTE, ServiceHandle == 0x10000); + + u32 contState = ParseCont(pContinuationState); + + u32 packetSize = 0; + const u8* pPacket = GetAttribPacket(ServiceHandle, contState, packetSize); + + // generate package + u8 DataFrame[1000]; + CBigEndianBuffer buffer(DataFrame); + + int Offset = 0; + SL2CAP_Header* pHeader = (SL2CAP_Header*)&DataFrame[Offset]; Offset += sizeof(SL2CAP_Header); + pHeader->CID = cid; + + buffer.Write8 (Offset, 0x05); Offset++; + buffer.Write16(Offset, TransactionID); Offset += 2; // transaction ID + + memcpy(buffer.GetPointer(Offset), pPacket, packetSize); Offset += packetSize; + + pHeader->Length = (u16)(Offset - sizeof(SL2CAP_Header)); + m_pHost->SendACLFrame(GetConnectionHandle(), DataFrame, pHeader->Length + sizeof(SL2CAP_Header)); + +// Debugger::PrintDataBuffer(LogTypes::WIIMOTE, DataFrame, pHeader->Length + sizeof(SL2CAP_Header), "test response: "); +} + +void CWII_IPC_HLE_WiiMote::HandleSDP(u16 cid, u8* _pData, u32 _Size) +{ + // Debugger::PrintDataBuffer(LogTypes::WIIMOTE, _pData, _Size, "HandleSDP: "); + + CBigEndianBuffer buffer(_pData); + + switch(buffer.Read8(0)) + { + // SDP_ServiceSearchRequest + case 0x02: + { + LOG(WII_IPC_WIIMOTE, "!!! SDP_ServiceSearchRequest !!!"); + + _dbg_assert_(WII_IPC_WIIMOTE, _Size == 13); + + u16 TransactionID = buffer.Read16(1); + u16 ParameterLength = buffer.Read16(3); + u8* pServiceSearchPattern = buffer.GetPointer(5); + u16 MaximumServiceRecordCount = buffer.Read16(10); + u8 ContinuationState = buffer.Read8(12); + + SDPSendServiceSearchResponse(cid, TransactionID, pServiceSearchPattern, MaximumServiceRecordCount); + } + break; + + // SDP_ServiceAttributeRequest + case 0x04: + { + LOG(WII_IPC_WIIMOTE, "!!! SDP_ServiceAttributeRequest !!!"); + + u16 startAttrID, endAttrID; + u32 offset = 1; + + u16 TransactionID = buffer.Read16(offset); offset += 2; + u16 ParameterLength = buffer.Read16(offset); offset += 2; + u32 ServiceHandle = buffer.Read32(offset); offset += 4; + u16 MaximumAttributeByteCount = buffer.Read16(offset); offset += 2; + offset += ParseAttribList(buffer.GetPointer(offset), startAttrID, endAttrID); + u8* pContinuationState = buffer.GetPointer(offset); + + SDPSendServiceAttributeResponse(cid, TransactionID, ServiceHandle, startAttrID, endAttrID, MaximumAttributeByteCount, pContinuationState); + } + break; + + default: + PanicAlert("Unknown SDP command %x", _pData[0]); + break; + } +} + +///////////////////////////////////////////////////////////////////////////////////////////////// +// +// +// +// +// --- Data Send Functions +// +// +// +// +// +///////////////////////////////////////////////////////////////////////////////////////////////// + + +void CWII_IPC_HLE_WiiMote::SendCommandToACL(u8 _Ident, u8 _Code, u8 _CommandLength, u8* _pCommandData) +{ + u8 DataFrame[1024]; + u32 Offset = 0; + + SL2CAP_Header* pHeader = (SL2CAP_Header*)&DataFrame[Offset]; Offset += sizeof(SL2CAP_Header); + pHeader->CID = 0x0001; + pHeader->Length = sizeof(SL2CAP_Command) + _CommandLength; + + SL2CAP_Command* pCommand = (SL2CAP_Command*)&DataFrame[Offset]; Offset += sizeof(SL2CAP_Command); + pCommand->len = _CommandLength; + pCommand->ident = _Ident; + pCommand->code = _Code; + + memcpy(&DataFrame[Offset], _pCommandData, _CommandLength); + + LOG(WII_IPC_WIIMOTE, " SendCommandToACL (answer)"); + LOG(WII_IPC_WIIMOTE, " Ident: 0x%02x", _Ident); + LOG(WII_IPC_WIIMOTE, " Code: 0x%02x", _Code); + + // send .... + m_pHost->SendACLFrame(GetConnectionHandle(), DataFrame, pHeader->Length + sizeof(SL2CAP_Header)); + + // Debugger::PrintDataBuffer(LogTypes::WIIMOTE, DataFrame, pHeader->Length + sizeof(SL2CAP_Header), "m_pHost->SendACLFrame: "); +} + +void CWII_IPC_HLE_WiiMote::SendL2capData(u16 scid, const void* _pData, u32 _Size) +{ + //allocate + u8 DataFrame[1024]; + u32 Offset = 0; + SL2CAP_Header* pHeader = (SL2CAP_Header*)DataFrame; + Offset += sizeof(SL2CAP_Header); + + _dbg_assert_(WII_IPC_WIIMOTE, DoesChannelExist(scid)); + SChannel& rChannel = m_Channel[scid]; + + //assemble + pHeader->CID = rChannel.DCID; + pHeader->Length = _Size; + + memcpy(DataFrame + Offset, _pData, _Size); + Offset += _Size; + + //send + m_pHost->SendACLFrame(GetConnectionHandle(), DataFrame, Offset); + + // + Host_SetWiiMoteConnectionState(2); +} + + +namespace Core +{ + /* This is called continously from the Wiimote plugin as soon as it has received + a reporting mode */ + void Callback_WiimoteInput(u16 _channelID, const void* _pData, u32 _Size) + { + LOGV(WII_IPC_WIIMOTE, 3, "========================================================="); + const u8* pData = (const u8*)_pData; + LOGV(WII_IPC_WIIMOTE, 3, "Callback_WiimoteInput:"); + std::string Temp; + for (u32 j=0; j<_Size; j++) + { + char Buffer[128]; + sprintf(Buffer, "%02x ", pData[j]); + Temp.append(Buffer); + } + LOGV(WII_IPC_WIIMOTE, 3, " Data: %s", Temp.c_str()); + //LOGV(WII_IPC_WIIMOTE, 3, " Channel: %s", _channelID); + + s_Usb->m_WiiMotes[0].SendL2capData(_channelID, _pData, _Size); + LOGV(WII_IPC_WIIMOTE, 3, "========================================================="); + } +} diff --git a/Source/Core/Core/Src/IPC_HLE/WiiMote_HID_Attr.cpp b/Source/Core/Core/Src/IPC_HLE/WiiMote_HID_Attr.cpp index c67a28fcb4..dd0d90ce1b 100644 --- a/Source/Core/Core/Src/IPC_HLE/WiiMote_HID_Attr.cpp +++ b/Source/Core/Core/Src/IPC_HLE/WiiMote_HID_Attr.cpp @@ -1,272 +1,272 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include -#include "WiiMote_HID_Attr.h" - -CAttribTable m_AttribTable; - - -// 0x00 (checked) -u8 ServiceRecordHandle[] = { 0x0a, 0x00, 0x01, 0x00, 0x00 }; -// 0x01 (checked) -u8 SrvClassIDList[] = { 0x35, 0x03, - 0x19, 0x11, 0x24 }; -// 0x04 (checked) -u8 ProtocolDescriptorList[] = { 0x35, 0x0D, - 0x35, 0x06, - 0x19, 0x01, 0x00, // Element 0 - 0x09, 0x00, 0x11, // Element 1 - 0x35, 0x03, - 0x19, 0x00, 0x11}; // Element 0 -// 0x5 (checked) -u8 BrowseGroupList[] = { 0x35, 0x03, - 0x19, 0x10, 0x02 }; -// 0x6 (checked) -u8 LanguageBaseAttributeIDList[] = { 0x35, 0x09, - 0x09, 0x65, 0x6e, - 0x09, 0x00, 0x6a, - 0x09, 0x01, 0x00 }; -// 0x09 (checked) -u8 BluetoothProfileDescriptorList[] = { 0x35, 0x08, - 0x35, 0x06, - 0x19, 0x11, 0x24, - 0x09, 0x01, 0x00 }; -// 0x0D (checked) -u8 AdditionalProtocolDescriptorLists[] = { 0x35, 0x0F, - 0x35, 0x0D, - 0x35, 0x06, - 0x19, 0x01, 0x00, - 0x09, 0x00, 0x13, - 0x35, 0x03, - 0x19, 0x00, 0x11 }; -// 0x100 -u8 ServiceName[] = { 0x25, 0x13, 'N','i','n','t','e','n','d','o',' ','R','V','L','-','C','N','T','-','0','1' }; -// 0x101 -u8 ServiceDescription[] = { 0x25, 0x13, 'N','i','n','t','e','n','d','o',' ','R','V','L','-','C','N','T','-','0','1' }; -// 0x102 -u8 ProviderName [] = { 0x25, 0x8, 'N','i','n','t','e','n','d','o'}; - -// 0x200 -u8 HIDDeviceReleaseNumber[] = { 0x09, 0x01, 0x00 }; -// 0x201 -u8 HIDParserVersion[] = { 0x09, 0x01, 0x11 }; -// 0x202 -u8 HIDDeviceSubclass[] = { 0x09, 0x00, 0x04 }; -// 0x203 -u8 HIDCountryCode[] = { 0x09, 0x00, 0x33 }; -// 0x204 -u8 HIDVirtualCable[] = { 0x09, 0x00, 0x00 }; -// 0x205 -u8 HIDReconnectInitiate[] = { 0x09, 0x00, 0x01 }; - -// 0x206 -u8 HIDDescriptorList[] = { 0x35, 0xDF, - 0x35, 0xDD, - 0x08, 0x22, // Element 0 - 0x25, 0xD9, // hmm... <- 0x25 is a string but there is Data - - // 0xD9 Bytes - Element 1 - 0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x10, - 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, - 0x01, 0x06, 0x00, 0xff, 0x09, 0x01, 0x91, 0x00, - 0x85, 0x11, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, - 0x85, 0x12, 0x95, 0x02, 0x09, 0x01, 0x91, 0x00, - 0x85, 0x13, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, - 0x85, 0x14, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, - 0x85, 0x15, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, - 0x85, 0x16, 0x95, 0x15, 0x09, 0x01, 0x91, 0x00, - 0x85, 0x17, 0x95, 0x06, 0x09, 0x01, 0x91, 0x00, - 0x85, 0x18, 0x95, 0x15, 0x09, 0x01, 0x91, 0x00, - 0x85, 0x19, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, - 0x85, 0x1a, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, - 0x85, 0x20, 0x95, 0x06, 0x09, 0x01, 0x81, 0x00, - 0x85, 0x21, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, - 0x85, 0x22, 0x95, 0x04, 0x09, 0x01, 0x81, 0x00, - 0x85, 0x30, 0x95, 0x02, 0x09, 0x01, 0x81, 0x00, - 0x85, 0x31, 0x95, 0x05, 0x09, 0x01, 0x81, 0x00, - 0x85, 0x32, 0x95, 0x0a, 0x09, 0x01, 0x81, 0x00, - 0x85, 0x33, 0x95, 0x11, 0x09, 0x01, 0x81, 0x00, - 0x85, 0x34, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, - 0x85, 0x35, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, - 0x85, 0x36, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, - 0x85, 0x37, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, - 0x85, 0x3d, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, - 0x85, 0x3e, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, - 0x85, 0x3f, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, - 0xc0 }; // end tag - - -// 0x207 -u8 HIDLANGIDBaseList[] = { 0x35, 0x08, - 0x35, 0x06, - 0x09, 0x04, 0x09, - 0x09, 0x01, 0x00 }; - -// 0x208 -u8 HIDSDPDisable[] = { 0x28, 0x00 }; -// 0x209 -u8 HIDBatteryPower[] = { 0x28, 0x01 }; -// 0x20a -u8 HIDRemoteWake[] = { 0x28, 0x01 }; -// 0x20b -u8 HIDUnk_020B[] = { 0x09, 0x01, 0x00 }; -// 0x20c -u8 HIDUnk_020C[] = { 0x09, 0x0c, 0x80 }; -// 0x20d -u8 HIDUnk_020D[] = { 0x28, 0x00 }; -// 0x20e -u8 HIDBootDevice[] = { 0x28, 0x00 }; - - -u8 packet1[] = { - 0x00, 0x7b, 0x00, 0x76, 0x36, 0x01, 0xcc, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x01, - 0x00, 0x00, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11, 0x24, 0x09, 0x00, 0x04, 0x35, 0x0d, 0x35, - 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x11, 0x35, 0x03, 0x19, 0x00, 0x11, 0x09, 0x00, 0x05, 0x35, - 0x03, 0x19, 0x10, 0x02, 0x09, 0x00, 0x06, 0x35, 0x09, 0x09, 0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, - 0x01, 0x00, 0x09, 0x00, 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, 0x11, 0x24, 0x09, 0x01, 0x00, 0x09, - 0x00, 0x0d, 0x35, 0x0f, 0x35, 0x0d, 0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x13, 0x35, 0x03, - 0x19, 0x00, 0x11, 0x09, 0x01, 0x00, 0x25, 0x13, 0x4e, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x6f, - 0x20, 0x52, 0x56, 0x4c, 0x2d, 0x43, 0x4e, 0x54, 0x2d, 0x30, 0x31, 0x09, 0x01, 0x02, 0x00, 0x76, -}; - -u8 packet2[] = { - 0x00, 0x7b, 0x00, 0x76, 0x01, 0x25, 0x13, 0x4e, 0x69, 0x6e, 0x74, 0x65, 0x6e, - 0x64, 0x6f, 0x20, 0x52, 0x56, 0x4c, 0x2d, 0x43, 0x4e, 0x54, 0x2d, 0x30, 0x31, 0x09, 0x01, 0x02, - 0x25, 0x08, 0x4e, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x6f, 0x09, 0x02, 0x00, 0x09, 0x01, 0x00, - 0x09, 0x02, 0x01, 0x09, 0x01, 0x11, 0x09, 0x02, 0x02, 0x08, 0x04, 0x09, 0x02, 0x03, 0x08, 0x33, - 0x09, 0x02, 0x04, 0x28, 0x00, 0x09, 0x02, 0x05, 0x28, 0x01, 0x09, 0x02, 0x06, 0x35, 0xdf, 0x35, - 0xdd, 0x08, 0x22, 0x25, 0xd9, 0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x10, 0x15, 0x00, 0x26, - 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x06, 0x00, 0xff, 0x09, 0x01, 0x91, 0x00, 0x85, 0x11, 0x95, - 0x01, 0x09, 0x01, 0x91, 0x00, 0x85, 0x12, 0x95, 0x02, 0x09, 0x01, 0x91, 0x00, 0x02, 0x00, 0xec, -}; - -u8 packet3[] = { - - 0x00, 0x7b, 0x00, 0x76, 0x85, 0x13, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, 0x85, - 0x14, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, 0x85, 0x15, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, 0x85, - 0x16, 0x95, 0x15, 0x09, 0x01, 0x91, 0x00, 0x85, 0x17, 0x95, 0x06, 0x09, 0x01, 0x91, 0x00, 0x85, - 0x18, 0x95, 0x15, 0x09, 0x01, 0x91, 0x00, 0x85, 0x19, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, 0x85, - 0x1a, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, 0x85, 0x20, 0x95, 0x06, 0x09, 0x01, 0x81, 0x00, 0x85, - 0x21, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, 0x85, 0x22, 0x95, 0x04, 0x09, 0x01, 0x81, 0x00, 0x85, - 0x30, 0x95, 0x02, 0x09, 0x01, 0x81, 0x00, 0x85, 0x31, 0x95, 0x05, 0x09, 0x01, 0x81, 0x00, 0x85, - 0x32, 0x95, 0x0a, 0x09, 0x01, 0x81, 0x00, 0x85, 0x33, 0x95, 0x11, 0x09, 0x01, 0x02, 0x01, 0x62, -}; - -u8 packet4[] = { - 0x00, 0x70, 0x00, 0x6d, 0x81, 0x00, 0x85, 0x34, 0x95, 0x15, 0x09, 0x01, 0x81, - 0x00, 0x85, 0x35, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, 0x85, 0x36, 0x95, 0x15, 0x09, 0x01, 0x81, - 0x00, 0x85, 0x37, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, 0x85, 0x3d, 0x95, 0x15, 0x09, 0x01, 0x81, - 0x00, 0x85, 0x3e, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, 0x85, 0x3f, 0x95, 0x15, 0x09, 0x01, 0x81, - 0x00, 0xc0, 0x09, 0x02, 0x07, 0x35, 0x08, 0x35, 0x06, 0x09, 0x04, 0x09, 0x09, 0x01, 0x00, 0x09, - 0x02, 0x08, 0x28, 0x00, 0x09, 0x02, 0x09, 0x28, 0x01, 0x09, 0x02, 0x0a, 0x28, 0x01, 0x09, 0x02, - 0x0b, 0x09, 0x01, 0x00, 0x09, 0x02, 0x0c, 0x09, 0x0c, 0x80, 0x09, 0x02, 0x0d, 0x28, 0x00, 0x09, - 0x02, 0x0e, 0x28, 0x00, 0x00, - -}; - - -u8 packet4_0x10001[] = { - 0x00, 0x60, 0x00, 0x5d, 0x36, 0x00, 0x5a, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x01, - 0x00, 0x01, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x12, 0x00, 0x09, 0x00, 0x04, 0x35, 0x0d, 0x35, - 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x00, 0x01, 0x09, 0x00, 0x05, 0x35, - 0x03, 0x19, 0x10, 0x02, 0x09, 0x00, 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, 0x12, 0x00, 0x09, 0x01, - 0x00, 0x09, 0x02, 0x00, 0x09, 0x01, 0x00, 0x09, 0x02, 0x01, 0x09, 0x05, 0x7e, 0x09, 0x02, 0x02, - 0x09, 0x03, 0x06, 0x09, 0x02, 0x03, 0x09, 0x06, 0x00, 0x09, 0x02, 0x04, 0x28, 0x01, 0x09, 0x02, - 0x05, 0x09, 0x00, 0x02, 0x00, -}; - - - -const u8* GetAttribPacket(u32 serviceHandle, u32 cont, u32& _size) -{ - if (serviceHandle == 0x10000) - { - if (cont == 0) - { - _size = sizeof(packet1); - return packet1; - } - else if (cont == 0x76) - { - _size = sizeof(packet2); - return packet2; - } - else if (cont == 0xec) - { - _size = sizeof(packet3); - return packet3; - } - else if (cont == 0x162) - { - _size = sizeof(packet4); - return packet4; - } - } - - if (serviceHandle == 0x10001) - { - _dbg_assert_(WII_IPC_WIIMOTE, cont == 0x00); - _size = sizeof(packet4_0x10001); - return packet4_0x10001; - } - - return 0; -} - -void InitAttribTable() -{ - m_AttribTable.push_back(SAttrib(0x00, ServiceRecordHandle, sizeof(ServiceRecordHandle))); - m_AttribTable.push_back(SAttrib(0x01, SrvClassIDList, sizeof(SrvClassIDList))); - m_AttribTable.push_back(SAttrib(0x04, ProtocolDescriptorList, sizeof(ProtocolDescriptorList))); - m_AttribTable.push_back(SAttrib(0x05, BrowseGroupList, sizeof(BrowseGroupList))); - m_AttribTable.push_back(SAttrib(0x06, LanguageBaseAttributeIDList, sizeof(LanguageBaseAttributeIDList))); - m_AttribTable.push_back(SAttrib(0x09, BluetoothProfileDescriptorList, sizeof(BluetoothProfileDescriptorList))); - m_AttribTable.push_back(SAttrib(0x0D, AdditionalProtocolDescriptorLists, sizeof(AdditionalProtocolDescriptorLists))); - - - m_AttribTable.push_back(SAttrib(0x100, ServiceName, sizeof(ServiceName))); - m_AttribTable.push_back(SAttrib(0x101, ServiceDescription, sizeof(ServiceDescription))); - m_AttribTable.push_back(SAttrib(0x102, ProviderName, sizeof(ProviderName))); - - m_AttribTable.push_back(SAttrib(0x200, HIDDeviceReleaseNumber, sizeof(HIDDeviceReleaseNumber))); - m_AttribTable.push_back(SAttrib(0x201, HIDParserVersion, sizeof(HIDParserVersion))); - m_AttribTable.push_back(SAttrib(0x202, HIDDeviceSubclass, sizeof(HIDDeviceSubclass))); - m_AttribTable.push_back(SAttrib(0x203, HIDCountryCode, sizeof(HIDCountryCode))); - m_AttribTable.push_back(SAttrib(0x204, HIDVirtualCable, sizeof(HIDVirtualCable))); - m_AttribTable.push_back(SAttrib(0x205, HIDReconnectInitiate, sizeof(HIDReconnectInitiate))); - m_AttribTable.push_back(SAttrib(0x206, HIDDescriptorList, sizeof(HIDDescriptorList))); - m_AttribTable.push_back(SAttrib(0x207, HIDLANGIDBaseList, sizeof(HIDLANGIDBaseList))); - m_AttribTable.push_back(SAttrib(0x208, HIDSDPDisable, sizeof(HIDSDPDisable))); - m_AttribTable.push_back(SAttrib(0x209, HIDBatteryPower, sizeof(HIDBatteryPower))); - m_AttribTable.push_back(SAttrib(0x20a, HIDRemoteWake, sizeof(HIDRemoteWake))); - m_AttribTable.push_back(SAttrib(0x20b, HIDUnk_020B, sizeof(HIDUnk_020B))); - m_AttribTable.push_back(SAttrib(0x20c, HIDUnk_020C, sizeof(HIDUnk_020C))); - m_AttribTable.push_back(SAttrib(0x20d, HIDUnk_020D, sizeof(HIDUnk_020D))); - m_AttribTable.push_back(SAttrib(0x20e, HIDBootDevice, sizeof(HIDBootDevice))); -} - -const CAttribTable& GetAttribTable() -{ - if (m_AttribTable.empty()) - { - InitAttribTable(); - } - - return m_AttribTable; +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include +#include "WiiMote_HID_Attr.h" + +CAttribTable m_AttribTable; + + +// 0x00 (checked) +u8 ServiceRecordHandle[] = { 0x0a, 0x00, 0x01, 0x00, 0x00 }; +// 0x01 (checked) +u8 SrvClassIDList[] = { 0x35, 0x03, + 0x19, 0x11, 0x24 }; +// 0x04 (checked) +u8 ProtocolDescriptorList[] = { 0x35, 0x0D, + 0x35, 0x06, + 0x19, 0x01, 0x00, // Element 0 + 0x09, 0x00, 0x11, // Element 1 + 0x35, 0x03, + 0x19, 0x00, 0x11}; // Element 0 +// 0x5 (checked) +u8 BrowseGroupList[] = { 0x35, 0x03, + 0x19, 0x10, 0x02 }; +// 0x6 (checked) +u8 LanguageBaseAttributeIDList[] = { 0x35, 0x09, + 0x09, 0x65, 0x6e, + 0x09, 0x00, 0x6a, + 0x09, 0x01, 0x00 }; +// 0x09 (checked) +u8 BluetoothProfileDescriptorList[] = { 0x35, 0x08, + 0x35, 0x06, + 0x19, 0x11, 0x24, + 0x09, 0x01, 0x00 }; +// 0x0D (checked) +u8 AdditionalProtocolDescriptorLists[] = { 0x35, 0x0F, + 0x35, 0x0D, + 0x35, 0x06, + 0x19, 0x01, 0x00, + 0x09, 0x00, 0x13, + 0x35, 0x03, + 0x19, 0x00, 0x11 }; +// 0x100 +u8 ServiceName[] = { 0x25, 0x13, 'N','i','n','t','e','n','d','o',' ','R','V','L','-','C','N','T','-','0','1' }; +// 0x101 +u8 ServiceDescription[] = { 0x25, 0x13, 'N','i','n','t','e','n','d','o',' ','R','V','L','-','C','N','T','-','0','1' }; +// 0x102 +u8 ProviderName [] = { 0x25, 0x8, 'N','i','n','t','e','n','d','o'}; + +// 0x200 +u8 HIDDeviceReleaseNumber[] = { 0x09, 0x01, 0x00 }; +// 0x201 +u8 HIDParserVersion[] = { 0x09, 0x01, 0x11 }; +// 0x202 +u8 HIDDeviceSubclass[] = { 0x09, 0x00, 0x04 }; +// 0x203 +u8 HIDCountryCode[] = { 0x09, 0x00, 0x33 }; +// 0x204 +u8 HIDVirtualCable[] = { 0x09, 0x00, 0x00 }; +// 0x205 +u8 HIDReconnectInitiate[] = { 0x09, 0x00, 0x01 }; + +// 0x206 +u8 HIDDescriptorList[] = { 0x35, 0xDF, + 0x35, 0xDD, + 0x08, 0x22, // Element 0 + 0x25, 0xD9, // hmm... <- 0x25 is a string but there is Data + + // 0xD9 Bytes - Element 1 + 0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x10, + 0x15, 0x00, 0x26, 0xff, 0x00, 0x75, 0x08, 0x95, + 0x01, 0x06, 0x00, 0xff, 0x09, 0x01, 0x91, 0x00, + 0x85, 0x11, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, + 0x85, 0x12, 0x95, 0x02, 0x09, 0x01, 0x91, 0x00, + 0x85, 0x13, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, + 0x85, 0x14, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, + 0x85, 0x15, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, + 0x85, 0x16, 0x95, 0x15, 0x09, 0x01, 0x91, 0x00, + 0x85, 0x17, 0x95, 0x06, 0x09, 0x01, 0x91, 0x00, + 0x85, 0x18, 0x95, 0x15, 0x09, 0x01, 0x91, 0x00, + 0x85, 0x19, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, + 0x85, 0x1a, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, + 0x85, 0x20, 0x95, 0x06, 0x09, 0x01, 0x81, 0x00, + 0x85, 0x21, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, + 0x85, 0x22, 0x95, 0x04, 0x09, 0x01, 0x81, 0x00, + 0x85, 0x30, 0x95, 0x02, 0x09, 0x01, 0x81, 0x00, + 0x85, 0x31, 0x95, 0x05, 0x09, 0x01, 0x81, 0x00, + 0x85, 0x32, 0x95, 0x0a, 0x09, 0x01, 0x81, 0x00, + 0x85, 0x33, 0x95, 0x11, 0x09, 0x01, 0x81, 0x00, + 0x85, 0x34, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, + 0x85, 0x35, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, + 0x85, 0x36, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, + 0x85, 0x37, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, + 0x85, 0x3d, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, + 0x85, 0x3e, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, + 0x85, 0x3f, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, + 0xc0 }; // end tag + + +// 0x207 +u8 HIDLANGIDBaseList[] = { 0x35, 0x08, + 0x35, 0x06, + 0x09, 0x04, 0x09, + 0x09, 0x01, 0x00 }; + +// 0x208 +u8 HIDSDPDisable[] = { 0x28, 0x00 }; +// 0x209 +u8 HIDBatteryPower[] = { 0x28, 0x01 }; +// 0x20a +u8 HIDRemoteWake[] = { 0x28, 0x01 }; +// 0x20b +u8 HIDUnk_020B[] = { 0x09, 0x01, 0x00 }; +// 0x20c +u8 HIDUnk_020C[] = { 0x09, 0x0c, 0x80 }; +// 0x20d +u8 HIDUnk_020D[] = { 0x28, 0x00 }; +// 0x20e +u8 HIDBootDevice[] = { 0x28, 0x00 }; + + +u8 packet1[] = { + 0x00, 0x7b, 0x00, 0x76, 0x36, 0x01, 0xcc, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x01, + 0x00, 0x00, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x11, 0x24, 0x09, 0x00, 0x04, 0x35, 0x0d, 0x35, + 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x11, 0x35, 0x03, 0x19, 0x00, 0x11, 0x09, 0x00, 0x05, 0x35, + 0x03, 0x19, 0x10, 0x02, 0x09, 0x00, 0x06, 0x35, 0x09, 0x09, 0x65, 0x6e, 0x09, 0x00, 0x6a, 0x09, + 0x01, 0x00, 0x09, 0x00, 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, 0x11, 0x24, 0x09, 0x01, 0x00, 0x09, + 0x00, 0x0d, 0x35, 0x0f, 0x35, 0x0d, 0x35, 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x13, 0x35, 0x03, + 0x19, 0x00, 0x11, 0x09, 0x01, 0x00, 0x25, 0x13, 0x4e, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x6f, + 0x20, 0x52, 0x56, 0x4c, 0x2d, 0x43, 0x4e, 0x54, 0x2d, 0x30, 0x31, 0x09, 0x01, 0x02, 0x00, 0x76, +}; + +u8 packet2[] = { + 0x00, 0x7b, 0x00, 0x76, 0x01, 0x25, 0x13, 0x4e, 0x69, 0x6e, 0x74, 0x65, 0x6e, + 0x64, 0x6f, 0x20, 0x52, 0x56, 0x4c, 0x2d, 0x43, 0x4e, 0x54, 0x2d, 0x30, 0x31, 0x09, 0x01, 0x02, + 0x25, 0x08, 0x4e, 0x69, 0x6e, 0x74, 0x65, 0x6e, 0x64, 0x6f, 0x09, 0x02, 0x00, 0x09, 0x01, 0x00, + 0x09, 0x02, 0x01, 0x09, 0x01, 0x11, 0x09, 0x02, 0x02, 0x08, 0x04, 0x09, 0x02, 0x03, 0x08, 0x33, + 0x09, 0x02, 0x04, 0x28, 0x00, 0x09, 0x02, 0x05, 0x28, 0x01, 0x09, 0x02, 0x06, 0x35, 0xdf, 0x35, + 0xdd, 0x08, 0x22, 0x25, 0xd9, 0x05, 0x01, 0x09, 0x05, 0xa1, 0x01, 0x85, 0x10, 0x15, 0x00, 0x26, + 0xff, 0x00, 0x75, 0x08, 0x95, 0x01, 0x06, 0x00, 0xff, 0x09, 0x01, 0x91, 0x00, 0x85, 0x11, 0x95, + 0x01, 0x09, 0x01, 0x91, 0x00, 0x85, 0x12, 0x95, 0x02, 0x09, 0x01, 0x91, 0x00, 0x02, 0x00, 0xec, +}; + +u8 packet3[] = { + + 0x00, 0x7b, 0x00, 0x76, 0x85, 0x13, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, 0x85, + 0x14, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, 0x85, 0x15, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, 0x85, + 0x16, 0x95, 0x15, 0x09, 0x01, 0x91, 0x00, 0x85, 0x17, 0x95, 0x06, 0x09, 0x01, 0x91, 0x00, 0x85, + 0x18, 0x95, 0x15, 0x09, 0x01, 0x91, 0x00, 0x85, 0x19, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, 0x85, + 0x1a, 0x95, 0x01, 0x09, 0x01, 0x91, 0x00, 0x85, 0x20, 0x95, 0x06, 0x09, 0x01, 0x81, 0x00, 0x85, + 0x21, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, 0x85, 0x22, 0x95, 0x04, 0x09, 0x01, 0x81, 0x00, 0x85, + 0x30, 0x95, 0x02, 0x09, 0x01, 0x81, 0x00, 0x85, 0x31, 0x95, 0x05, 0x09, 0x01, 0x81, 0x00, 0x85, + 0x32, 0x95, 0x0a, 0x09, 0x01, 0x81, 0x00, 0x85, 0x33, 0x95, 0x11, 0x09, 0x01, 0x02, 0x01, 0x62, +}; + +u8 packet4[] = { + 0x00, 0x70, 0x00, 0x6d, 0x81, 0x00, 0x85, 0x34, 0x95, 0x15, 0x09, 0x01, 0x81, + 0x00, 0x85, 0x35, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, 0x85, 0x36, 0x95, 0x15, 0x09, 0x01, 0x81, + 0x00, 0x85, 0x37, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, 0x85, 0x3d, 0x95, 0x15, 0x09, 0x01, 0x81, + 0x00, 0x85, 0x3e, 0x95, 0x15, 0x09, 0x01, 0x81, 0x00, 0x85, 0x3f, 0x95, 0x15, 0x09, 0x01, 0x81, + 0x00, 0xc0, 0x09, 0x02, 0x07, 0x35, 0x08, 0x35, 0x06, 0x09, 0x04, 0x09, 0x09, 0x01, 0x00, 0x09, + 0x02, 0x08, 0x28, 0x00, 0x09, 0x02, 0x09, 0x28, 0x01, 0x09, 0x02, 0x0a, 0x28, 0x01, 0x09, 0x02, + 0x0b, 0x09, 0x01, 0x00, 0x09, 0x02, 0x0c, 0x09, 0x0c, 0x80, 0x09, 0x02, 0x0d, 0x28, 0x00, 0x09, + 0x02, 0x0e, 0x28, 0x00, 0x00, + +}; + + +u8 packet4_0x10001[] = { + 0x00, 0x60, 0x00, 0x5d, 0x36, 0x00, 0x5a, 0x09, 0x00, 0x00, 0x0a, 0x00, 0x01, + 0x00, 0x01, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x12, 0x00, 0x09, 0x00, 0x04, 0x35, 0x0d, 0x35, + 0x06, 0x19, 0x01, 0x00, 0x09, 0x00, 0x01, 0x35, 0x03, 0x19, 0x00, 0x01, 0x09, 0x00, 0x05, 0x35, + 0x03, 0x19, 0x10, 0x02, 0x09, 0x00, 0x09, 0x35, 0x08, 0x35, 0x06, 0x19, 0x12, 0x00, 0x09, 0x01, + 0x00, 0x09, 0x02, 0x00, 0x09, 0x01, 0x00, 0x09, 0x02, 0x01, 0x09, 0x05, 0x7e, 0x09, 0x02, 0x02, + 0x09, 0x03, 0x06, 0x09, 0x02, 0x03, 0x09, 0x06, 0x00, 0x09, 0x02, 0x04, 0x28, 0x01, 0x09, 0x02, + 0x05, 0x09, 0x00, 0x02, 0x00, +}; + + + +const u8* GetAttribPacket(u32 serviceHandle, u32 cont, u32& _size) +{ + if (serviceHandle == 0x10000) + { + if (cont == 0) + { + _size = sizeof(packet1); + return packet1; + } + else if (cont == 0x76) + { + _size = sizeof(packet2); + return packet2; + } + else if (cont == 0xec) + { + _size = sizeof(packet3); + return packet3; + } + else if (cont == 0x162) + { + _size = sizeof(packet4); + return packet4; + } + } + + if (serviceHandle == 0x10001) + { + _dbg_assert_(WII_IPC_WIIMOTE, cont == 0x00); + _size = sizeof(packet4_0x10001); + return packet4_0x10001; + } + + return 0; +} + +void InitAttribTable() +{ + m_AttribTable.push_back(SAttrib(0x00, ServiceRecordHandle, sizeof(ServiceRecordHandle))); + m_AttribTable.push_back(SAttrib(0x01, SrvClassIDList, sizeof(SrvClassIDList))); + m_AttribTable.push_back(SAttrib(0x04, ProtocolDescriptorList, sizeof(ProtocolDescriptorList))); + m_AttribTable.push_back(SAttrib(0x05, BrowseGroupList, sizeof(BrowseGroupList))); + m_AttribTable.push_back(SAttrib(0x06, LanguageBaseAttributeIDList, sizeof(LanguageBaseAttributeIDList))); + m_AttribTable.push_back(SAttrib(0x09, BluetoothProfileDescriptorList, sizeof(BluetoothProfileDescriptorList))); + m_AttribTable.push_back(SAttrib(0x0D, AdditionalProtocolDescriptorLists, sizeof(AdditionalProtocolDescriptorLists))); + + + m_AttribTable.push_back(SAttrib(0x100, ServiceName, sizeof(ServiceName))); + m_AttribTable.push_back(SAttrib(0x101, ServiceDescription, sizeof(ServiceDescription))); + m_AttribTable.push_back(SAttrib(0x102, ProviderName, sizeof(ProviderName))); + + m_AttribTable.push_back(SAttrib(0x200, HIDDeviceReleaseNumber, sizeof(HIDDeviceReleaseNumber))); + m_AttribTable.push_back(SAttrib(0x201, HIDParserVersion, sizeof(HIDParserVersion))); + m_AttribTable.push_back(SAttrib(0x202, HIDDeviceSubclass, sizeof(HIDDeviceSubclass))); + m_AttribTable.push_back(SAttrib(0x203, HIDCountryCode, sizeof(HIDCountryCode))); + m_AttribTable.push_back(SAttrib(0x204, HIDVirtualCable, sizeof(HIDVirtualCable))); + m_AttribTable.push_back(SAttrib(0x205, HIDReconnectInitiate, sizeof(HIDReconnectInitiate))); + m_AttribTable.push_back(SAttrib(0x206, HIDDescriptorList, sizeof(HIDDescriptorList))); + m_AttribTable.push_back(SAttrib(0x207, HIDLANGIDBaseList, sizeof(HIDLANGIDBaseList))); + m_AttribTable.push_back(SAttrib(0x208, HIDSDPDisable, sizeof(HIDSDPDisable))); + m_AttribTable.push_back(SAttrib(0x209, HIDBatteryPower, sizeof(HIDBatteryPower))); + m_AttribTable.push_back(SAttrib(0x20a, HIDRemoteWake, sizeof(HIDRemoteWake))); + m_AttribTable.push_back(SAttrib(0x20b, HIDUnk_020B, sizeof(HIDUnk_020B))); + m_AttribTable.push_back(SAttrib(0x20c, HIDUnk_020C, sizeof(HIDUnk_020C))); + m_AttribTable.push_back(SAttrib(0x20d, HIDUnk_020D, sizeof(HIDUnk_020D))); + m_AttribTable.push_back(SAttrib(0x20e, HIDBootDevice, sizeof(HIDBootDevice))); +} + +const CAttribTable& GetAttribTable() +{ + if (m_AttribTable.empty()) + { + InitAttribTable(); + } + + return m_AttribTable; } \ No newline at end of file diff --git a/Source/Core/Core/Src/LogManager.cpp b/Source/Core/Core/Src/LogManager.cpp index 605d5444a4..9f49e4a589 100644 --- a/Source/Core/Core/Src/LogManager.cpp +++ b/Source/Core/Core/Src/LogManager.cpp @@ -1,368 +1,368 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include -#include "Common.h" -#if defined(HAVE_WX) && HAVE_WX -#include // for the timestamps -#endif -#include "StringUtil.h" -#include "LogManager.h" -#include "PowerPC/PowerPC.h" -#include "PowerPC/SymbolDB.h" // for g_symbolDB -#include "Debugger/Debugger_SymbolMap.h" - - -LogManager::SMessage (*LogManager::m_Messages)[MAX_MESSAGES]; -int LogManager::m_nextMessages[LogManager::VERBOSITY_LEVELS + 1]; - -CDebugger_Log* LogManager::m_Log[LogTypes::NUMBER_OF_LOGS + (LogManager::VERBOSITY_LEVELS * 100)]; -int LogManager::m_activeLog = LogTypes::MASTER_LOG; -bool LogManager::m_bDirty = true; -bool LogManager::m_bInitialized = false; - - -void __Log(int log, const char *format, ...) -{ - char* temp = (char*)alloca(strlen(format)+512); - va_list args; - va_start(args, format); - CharArrayFromFormatV(temp, 512, format, args); - va_end(args); - LogManager::Log((LogTypes::LOG_TYPE)log, temp); -} - -void __Logv(int log, int v, const char *format, ...) -{ - char* temp = (char*)alloca(strlen(format)+512); - va_list args; - va_start(args, format); - CharArrayFromFormatV(temp, 512, format, args); - va_end(args); - LogManager::Log((LogTypes::LOG_TYPE)(log + v*100), temp); -} - -CDebugger_Log::CDebugger_Log(const char* _szShortName, const char* _szName, int a) : - m_bLogToFile(true), // write to file or not - m_bShowInLog(false), - m_bEnable(false), - m_pFile(NULL) -{ - strcpy((char*)m_szName, _szName); - strcpy((char*)m_szShortName_, _szShortName); - sprintf((char*)m_szShortName, "%s%i", _szShortName, a); - sprintf((char*)m_szFilename, FULL_LOGS_DIR "%s%i.txt", _szName, a); - - unlink(m_szFilename); -} - -CDebugger_Log::~CDebugger_Log(void) -{ - if (m_pFile) - { - fclose(m_pFile); - m_pFile = NULL; - } -} - -// we may need to declare these -CDebugger_LogSettings::CDebugger_LogSettings() {} -CDebugger_LogSettings::~CDebugger_LogSettings(void) {} - -void CDebugger_Log::Init() -{ -#ifdef LOGGING - m_pFile = fopen(m_szFilename, "wtb"); -#endif -} - -void CDebugger_Log::Shutdown() -{ -#ifdef LOGGING - if (m_pFile != NULL) - { - fclose(m_pFile); - m_pFile = NULL; - } -#endif -} - - -void LogManager::Init() -{ - m_Messages = new SMessage[LogManager::VERBOSITY_LEVELS + 1][MAX_MESSAGES]; - m_bDirty = true; - - // create log files - for(int i = 0; i <= LogManager::VERBOSITY_LEVELS; i++) - { - m_Log[LogTypes::MASTER_LOG + i*100] = new CDebugger_Log("*", "Master Log", i); - m_Log[LogTypes::BOOT + i*100] = new CDebugger_Log("BOOT", "Boot", i); - m_Log[LogTypes::PIXELENGINE + i*100] = new CDebugger_Log("PE", "PixelEngine", i); - m_Log[LogTypes::COMMANDPROCESSOR + i*100] = new CDebugger_Log("CP", "CommandProc", i); - m_Log[LogTypes::VIDEOINTERFACE + i*100] = new CDebugger_Log("VI", "VideoInt", i); - m_Log[LogTypes::SERIALINTERFACE + i*100] = new CDebugger_Log("SI", "SerialInt", i); - m_Log[LogTypes::PERIPHERALINTERFACE + i*100]= new CDebugger_Log("PI", "PeripheralInt", i); - m_Log[LogTypes::MEMMAP + i*100] = new CDebugger_Log("MI", "MI & memmap", i); - m_Log[LogTypes::STREAMINGINTERFACE + i*100] = new CDebugger_Log("Stream", "StreamingInt", i); - m_Log[LogTypes::DSPINTERFACE + i*100] = new CDebugger_Log("DSP", "DSPInterface", i); - m_Log[LogTypes::DVDINTERFACE + i*100] = new CDebugger_Log("DVD", "DVDInterface", i); - m_Log[LogTypes::GPFIFO + i*100] = new CDebugger_Log("GP", "GPFifo", i); - m_Log[LogTypes::EXPANSIONINTERFACE + i*100] = new CDebugger_Log("EXI", "ExpansionInt", i); - m_Log[LogTypes::AUDIO_INTERFACE + i*100] = new CDebugger_Log("AI", "AudioInt", i); - m_Log[LogTypes::GEKKO + i*100] = new CDebugger_Log("GEKKO", "IBM CPU", i); - m_Log[LogTypes::HLE + i*100] = new CDebugger_Log("HLE", "HLE", i); - m_Log[LogTypes::DSPHLE + i*100] = new CDebugger_Log("DSPHLE", "DSP HLE", i); - m_Log[LogTypes::VIDEO + i*100] = new CDebugger_Log("Video", "Video Plugin", i); - m_Log[LogTypes::AUDIO + i*100] = new CDebugger_Log("Audio", "Audio Plugin", i); - m_Log[LogTypes::DYNA_REC + i*100] = new CDebugger_Log("DYNA", "Dynamic Recompiler", i); - m_Log[LogTypes::CONSOLE + i*100] = new CDebugger_Log("CONSOLE", "Dolphin Console", i); - m_Log[LogTypes::OSREPORT + i*100] = new CDebugger_Log("OSREPORT", "OSReport", i); - m_Log[LogTypes::WII_IOB + i*100] = new CDebugger_Log("WII_IOB", "WII IO Bridge", i); - m_Log[LogTypes::WII_IPC + i*100] = new CDebugger_Log("WII_IPC", "WII IPC", i); - m_Log[LogTypes::WII_IPC_HLE + i*100] = new CDebugger_Log("WII_IPC_HLE", "WII IPC HLE", i); - m_Log[LogTypes::WII_IPC_DVD + i*100] = new CDebugger_Log("WII_IPC_DVD", "WII IPC DVD", i); - m_Log[LogTypes::WII_IPC_ES + i*100] = new CDebugger_Log("WII_IPC_ES", "WII IPC ES", i); - m_Log[LogTypes::WII_IPC_FILEIO + i*100] = new CDebugger_Log("WII_IPC_FILEIO", "WII IPC FILEIO", i); - m_Log[LogTypes::WII_IPC_SD + i*100] = new CDebugger_Log("WII_IPC_SD", "WII IPC SD", i); - m_Log[LogTypes::WII_IPC_NET + i*100] = new CDebugger_Log("WII_IPC_NET", "WII IPC NET", i); - m_Log[LogTypes::WII_IPC_WIIMOTE + i*100] = new CDebugger_Log("WII_IPC_WIIMOTE", "WII IPC WIIMOTE", i); - m_Log[LogTypes::ACTIONREPLAY + i*100] = new CDebugger_Log("ActionReplay", "ActionReplay", i); - - m_nextMessages[i] = 0; // initiate to zero - } - - // create the files - for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) - { - for (int j = 0; j <= LogManager::VERBOSITY_LEVELS; j++) - { - m_Log[j*100 + i]->Init(); - } - } - m_bInitialized = true; -} - - -void LogManager::Clear() -{ - for (int v = 0; v <= LogManager::VERBOSITY_LEVELS; v++) - { - for (int i = 0; i < MAX_MESSAGES; i++) - { - strcpy(m_Messages[v][i].m_szMessage,""); - m_Messages[v][i].m_dwMsgLen = 0; - m_Messages[v][i].m_bInUse = false; - } - m_nextMessages[v] = 0; - } -} - -// __________________________________________________________________________________________________ -// Shutdown -// -void LogManager::Shutdown() -{ - m_bInitialized = false; - - // delete all loggers - for (int i=0; iShutdown(); - delete m_Log[i]; - m_Log[i] = NULL; - } - } - - delete [] m_Messages; -} - - -// ========================================================================================== -// The function that finally writes the log. -// --------------- -u32 lastPC; -std::string lastSymbol; -void LogManager::Log(LogTypes::LOG_TYPE _type, const char *_fmt, ...) -{ - if (m_LogSettings == NULL) - return; - - // declarations - int v; // verbosity level - int type; // the log type, CONSOLE etc. - char cvv[20]; - std::string svv; - - // get the current verbosity level and type - sprintf(cvv, "%03i", (int)_type); - svv = cvv; - v = atoi(svv.substr(0, 1).c_str()); - type = atoi(svv.substr(1, 2).c_str()); - - // security checks - if (m_Log[_type] == NULL || !m_Log[_type]->m_bEnable || PC == 0 - || _type > (LogTypes::NUMBER_OF_LOGS + LogManager::VERBOSITY_LEVELS * 100) - || _type < 0) - return; - - // prepare message - char Msg[512]; - va_list ap; - va_start(ap, _fmt); - vsprintf(Msg, _fmt, ap); - va_end(ap); - - static u32 count = 0; -#if defined(HAVE_WX) && HAVE_WX - wxDateTime datetime = wxDateTime::UNow(); // get timestamp -#endif - char* Msg2 = (char*)alloca(strlen(_fmt)+512); - - // Here's the old symbol request - //Debugger::FindSymbol(PC); - // const Debugger::Symbol& symbol = Debugger::GetSymbol(Index); - //symbol.GetName().c_str(), - - // Warning: Getting the function name this often is very demanding on the CPU. - // I have limited it to the two lowest verbosity levels because of that. I've also - // added a simple caching function so that we don't search again if we get the same - // question again. - std::string symbol; - - if ((v == 0 || v == 1) && lastPC != PC && LogManager::m_LogSettings->bResolve) - { - symbol = g_symbolDB.GetDescription(PC); - lastSymbol = symbol; - lastPC = PC; - } - else if(lastPC == PC && LogManager::m_LogSettings->bResolve) - { - symbol = lastSymbol; - } - else - { - symbol = "---"; - } - - int Index = 1; - const char *eol = "\n"; - if (Index > 0) - { - //sprintf(Msg2, "%i | %i %02i:%02i:%03i: %x %s (%s, %08x) : %s%s", - sprintf(Msg2, "%i %02i:%02i:%03i: %x %s (%s, %08x) : %s%s", - //v, - ++count, -#if defined(HAVE_WX) && HAVE_WX - datetime.GetMinute(), datetime.GetSecond(), datetime.GetMillisecond(), -#else - 0, 0, 0, - // TODO get proper values -#endif - PowerPC::ppcState.DebugCount, - m_Log[_type]->m_szShortName_, // (CONSOLE etc) - symbol.c_str(), PC, // current PC location (name, address) - Msg, eol); - } - - // ========================================================================================== - /* Here we have two options - 1. Verbosity mode where level 0 verbosity logs will be written to all verbosity - levels. Given that logging is enabled for that level. Level 1 verbosity will - only be written to level 1, 2, 3 and so on. - 2. Unify mode where everything is written to the last message struct and the - last file */ - // --------------- - - // Check if we should do a unified write to a single file - if(m_LogSettings->bUnify) - { - // prepare the right id - int id = VERBOSITY_LEVELS*100 + type; - int ver = VERBOSITY_LEVELS; - - // write to memory - m_Messages[ver][m_nextMessages[ver]].Set((LogTypes::LOG_TYPE)id, v, Msg2); - - // ---------------------------------------------------------------------------------------- - // Write to file - // --------------- - if (m_Log[id]->m_pFile && m_Log[id]->m_bLogToFile) - fprintf(m_Log[id]->m_pFile, "%s", Msg2); - if (m_Log[ver*100 + LogTypes::MASTER_LOG] && m_Log[ver*100 + LogTypes::MASTER_LOG]->m_pFile - && m_LogSettings->bWriteMaster) - fprintf(m_Log[ver*100 + LogTypes::MASTER_LOG]->m_pFile, "%s", Msg2); - - /* In case it crashes write now to make sure you get the last messages. - Is this slower than caching it? */ - //fflush(m_Log[id]->m_pFile); - //fflush(m_Log[ver*100 + LogTypes::MASTER_LOG]->m_pFile); - - printf("%s", Msg2); // write to console screen - - // this limits the memory space used for the memory logs to MAX_MESSAGES rows - m_nextMessages[ver]++; - if (m_nextMessages[ver] >= MAX_MESSAGES) - m_nextMessages[ver] = 0; - // --------------- - } - else // write to separate files and structs - { - int id; - for (int i = VERBOSITY_LEVELS; i >= v ; i--) - { - // prepare the right id - id = i*100 + type; - - // write to memory - m_Messages[i][m_nextMessages[i]].Set((LogTypes::LOG_TYPE)id, v, Msg2); - - // ---------------------------------------------------------------------------------------- - // Write to file - // --------------- - if (m_Log[id]->m_pFile && m_Log[id]->m_bLogToFile) - fprintf(m_Log[id]->m_pFile, "%s", Msg2); - if (m_Log[i*100 + LogTypes::MASTER_LOG] && m_Log[i*100 + LogTypes::MASTER_LOG]->m_pFile - && m_LogSettings->bWriteMaster) - fprintf(m_Log[i*100 + LogTypes::MASTER_LOG]->m_pFile, "%s", Msg2); - - // Write now. Is this slower than caching it? - //fflush(m_Log[id]->m_pFile); - //fflush(m_Log[i*100 + LogTypes::MASTER_LOG]->m_pFile); - - printf("%s", Msg2); // write to console screen - - // this limits the memory space used for the memory logs to MAX_MESSAGES rows - m_nextMessages[i]++; - if (m_nextMessages[i] >= MAX_MESSAGES) - m_nextMessages[i] = 0; - // --------------- - } - } - m_bDirty = true; // tell LogWindow that the log has been updated -} - -bool IsLoggingActivated() -{ -#ifdef LOGGING - return true; -#else - return false; -#endif -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include "Common.h" +#if defined(HAVE_WX) && HAVE_WX +#include // for the timestamps +#endif +#include "StringUtil.h" +#include "LogManager.h" +#include "PowerPC/PowerPC.h" +#include "PowerPC/SymbolDB.h" // for g_symbolDB +#include "Debugger/Debugger_SymbolMap.h" + + +LogManager::SMessage (*LogManager::m_Messages)[MAX_MESSAGES]; +int LogManager::m_nextMessages[LogManager::VERBOSITY_LEVELS + 1]; + +CDebugger_Log* LogManager::m_Log[LogTypes::NUMBER_OF_LOGS + (LogManager::VERBOSITY_LEVELS * 100)]; +int LogManager::m_activeLog = LogTypes::MASTER_LOG; +bool LogManager::m_bDirty = true; +bool LogManager::m_bInitialized = false; + + +void __Log(int log, const char *format, ...) +{ + char* temp = (char*)alloca(strlen(format)+512); + va_list args; + va_start(args, format); + CharArrayFromFormatV(temp, 512, format, args); + va_end(args); + LogManager::Log((LogTypes::LOG_TYPE)log, temp); +} + +void __Logv(int log, int v, const char *format, ...) +{ + char* temp = (char*)alloca(strlen(format)+512); + va_list args; + va_start(args, format); + CharArrayFromFormatV(temp, 512, format, args); + va_end(args); + LogManager::Log((LogTypes::LOG_TYPE)(log + v*100), temp); +} + +CDebugger_Log::CDebugger_Log(const char* _szShortName, const char* _szName, int a) : + m_bLogToFile(true), // write to file or not + m_bShowInLog(false), + m_bEnable(false), + m_pFile(NULL) +{ + strcpy((char*)m_szName, _szName); + strcpy((char*)m_szShortName_, _szShortName); + sprintf((char*)m_szShortName, "%s%i", _szShortName, a); + sprintf((char*)m_szFilename, FULL_LOGS_DIR "%s%i.txt", _szName, a); + + unlink(m_szFilename); +} + +CDebugger_Log::~CDebugger_Log(void) +{ + if (m_pFile) + { + fclose(m_pFile); + m_pFile = NULL; + } +} + +// we may need to declare these +CDebugger_LogSettings::CDebugger_LogSettings() {} +CDebugger_LogSettings::~CDebugger_LogSettings(void) {} + +void CDebugger_Log::Init() +{ +#ifdef LOGGING + m_pFile = fopen(m_szFilename, "wtb"); +#endif +} + +void CDebugger_Log::Shutdown() +{ +#ifdef LOGGING + if (m_pFile != NULL) + { + fclose(m_pFile); + m_pFile = NULL; + } +#endif +} + + +void LogManager::Init() +{ + m_Messages = new SMessage[LogManager::VERBOSITY_LEVELS + 1][MAX_MESSAGES]; + m_bDirty = true; + + // create log files + for(int i = 0; i <= LogManager::VERBOSITY_LEVELS; i++) + { + m_Log[LogTypes::MASTER_LOG + i*100] = new CDebugger_Log("*", "Master Log", i); + m_Log[LogTypes::BOOT + i*100] = new CDebugger_Log("BOOT", "Boot", i); + m_Log[LogTypes::PIXELENGINE + i*100] = new CDebugger_Log("PE", "PixelEngine", i); + m_Log[LogTypes::COMMANDPROCESSOR + i*100] = new CDebugger_Log("CP", "CommandProc", i); + m_Log[LogTypes::VIDEOINTERFACE + i*100] = new CDebugger_Log("VI", "VideoInt", i); + m_Log[LogTypes::SERIALINTERFACE + i*100] = new CDebugger_Log("SI", "SerialInt", i); + m_Log[LogTypes::PERIPHERALINTERFACE + i*100]= new CDebugger_Log("PI", "PeripheralInt", i); + m_Log[LogTypes::MEMMAP + i*100] = new CDebugger_Log("MI", "MI & memmap", i); + m_Log[LogTypes::STREAMINGINTERFACE + i*100] = new CDebugger_Log("Stream", "StreamingInt", i); + m_Log[LogTypes::DSPINTERFACE + i*100] = new CDebugger_Log("DSP", "DSPInterface", i); + m_Log[LogTypes::DVDINTERFACE + i*100] = new CDebugger_Log("DVD", "DVDInterface", i); + m_Log[LogTypes::GPFIFO + i*100] = new CDebugger_Log("GP", "GPFifo", i); + m_Log[LogTypes::EXPANSIONINTERFACE + i*100] = new CDebugger_Log("EXI", "ExpansionInt", i); + m_Log[LogTypes::AUDIO_INTERFACE + i*100] = new CDebugger_Log("AI", "AudioInt", i); + m_Log[LogTypes::GEKKO + i*100] = new CDebugger_Log("GEKKO", "IBM CPU", i); + m_Log[LogTypes::HLE + i*100] = new CDebugger_Log("HLE", "HLE", i); + m_Log[LogTypes::DSPHLE + i*100] = new CDebugger_Log("DSPHLE", "DSP HLE", i); + m_Log[LogTypes::VIDEO + i*100] = new CDebugger_Log("Video", "Video Plugin", i); + m_Log[LogTypes::AUDIO + i*100] = new CDebugger_Log("Audio", "Audio Plugin", i); + m_Log[LogTypes::DYNA_REC + i*100] = new CDebugger_Log("DYNA", "Dynamic Recompiler", i); + m_Log[LogTypes::CONSOLE + i*100] = new CDebugger_Log("CONSOLE", "Dolphin Console", i); + m_Log[LogTypes::OSREPORT + i*100] = new CDebugger_Log("OSREPORT", "OSReport", i); + m_Log[LogTypes::WII_IOB + i*100] = new CDebugger_Log("WII_IOB", "WII IO Bridge", i); + m_Log[LogTypes::WII_IPC + i*100] = new CDebugger_Log("WII_IPC", "WII IPC", i); + m_Log[LogTypes::WII_IPC_HLE + i*100] = new CDebugger_Log("WII_IPC_HLE", "WII IPC HLE", i); + m_Log[LogTypes::WII_IPC_DVD + i*100] = new CDebugger_Log("WII_IPC_DVD", "WII IPC DVD", i); + m_Log[LogTypes::WII_IPC_ES + i*100] = new CDebugger_Log("WII_IPC_ES", "WII IPC ES", i); + m_Log[LogTypes::WII_IPC_FILEIO + i*100] = new CDebugger_Log("WII_IPC_FILEIO", "WII IPC FILEIO", i); + m_Log[LogTypes::WII_IPC_SD + i*100] = new CDebugger_Log("WII_IPC_SD", "WII IPC SD", i); + m_Log[LogTypes::WII_IPC_NET + i*100] = new CDebugger_Log("WII_IPC_NET", "WII IPC NET", i); + m_Log[LogTypes::WII_IPC_WIIMOTE + i*100] = new CDebugger_Log("WII_IPC_WIIMOTE", "WII IPC WIIMOTE", i); + m_Log[LogTypes::ACTIONREPLAY + i*100] = new CDebugger_Log("ActionReplay", "ActionReplay", i); + + m_nextMessages[i] = 0; // initiate to zero + } + + // create the files + for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) + { + for (int j = 0; j <= LogManager::VERBOSITY_LEVELS; j++) + { + m_Log[j*100 + i]->Init(); + } + } + m_bInitialized = true; +} + + +void LogManager::Clear() +{ + for (int v = 0; v <= LogManager::VERBOSITY_LEVELS; v++) + { + for (int i = 0; i < MAX_MESSAGES; i++) + { + strcpy(m_Messages[v][i].m_szMessage,""); + m_Messages[v][i].m_dwMsgLen = 0; + m_Messages[v][i].m_bInUse = false; + } + m_nextMessages[v] = 0; + } +} + +// __________________________________________________________________________________________________ +// Shutdown +// +void LogManager::Shutdown() +{ + m_bInitialized = false; + + // delete all loggers + for (int i=0; iShutdown(); + delete m_Log[i]; + m_Log[i] = NULL; + } + } + + delete [] m_Messages; +} + + +// ========================================================================================== +// The function that finally writes the log. +// --------------- +u32 lastPC; +std::string lastSymbol; +void LogManager::Log(LogTypes::LOG_TYPE _type, const char *_fmt, ...) +{ + if (m_LogSettings == NULL) + return; + + // declarations + int v; // verbosity level + int type; // the log type, CONSOLE etc. + char cvv[20]; + std::string svv; + + // get the current verbosity level and type + sprintf(cvv, "%03i", (int)_type); + svv = cvv; + v = atoi(svv.substr(0, 1).c_str()); + type = atoi(svv.substr(1, 2).c_str()); + + // security checks + if (m_Log[_type] == NULL || !m_Log[_type]->m_bEnable || PC == 0 + || _type > (LogTypes::NUMBER_OF_LOGS + LogManager::VERBOSITY_LEVELS * 100) + || _type < 0) + return; + + // prepare message + char Msg[512]; + va_list ap; + va_start(ap, _fmt); + vsprintf(Msg, _fmt, ap); + va_end(ap); + + static u32 count = 0; +#if defined(HAVE_WX) && HAVE_WX + wxDateTime datetime = wxDateTime::UNow(); // get timestamp +#endif + char* Msg2 = (char*)alloca(strlen(_fmt)+512); + + // Here's the old symbol request + //Debugger::FindSymbol(PC); + // const Debugger::Symbol& symbol = Debugger::GetSymbol(Index); + //symbol.GetName().c_str(), + + // Warning: Getting the function name this often is very demanding on the CPU. + // I have limited it to the two lowest verbosity levels because of that. I've also + // added a simple caching function so that we don't search again if we get the same + // question again. + std::string symbol; + + if ((v == 0 || v == 1) && lastPC != PC && LogManager::m_LogSettings->bResolve) + { + symbol = g_symbolDB.GetDescription(PC); + lastSymbol = symbol; + lastPC = PC; + } + else if(lastPC == PC && LogManager::m_LogSettings->bResolve) + { + symbol = lastSymbol; + } + else + { + symbol = "---"; + } + + int Index = 1; + const char *eol = "\n"; + if (Index > 0) + { + //sprintf(Msg2, "%i | %i %02i:%02i:%03i: %x %s (%s, %08x) : %s%s", + sprintf(Msg2, "%i %02i:%02i:%03i: %x %s (%s, %08x) : %s%s", + //v, + ++count, +#if defined(HAVE_WX) && HAVE_WX + datetime.GetMinute(), datetime.GetSecond(), datetime.GetMillisecond(), +#else + 0, 0, 0, + // TODO get proper values +#endif + PowerPC::ppcState.DebugCount, + m_Log[_type]->m_szShortName_, // (CONSOLE etc) + symbol.c_str(), PC, // current PC location (name, address) + Msg, eol); + } + + // ========================================================================================== + /* Here we have two options + 1. Verbosity mode where level 0 verbosity logs will be written to all verbosity + levels. Given that logging is enabled for that level. Level 1 verbosity will + only be written to level 1, 2, 3 and so on. + 2. Unify mode where everything is written to the last message struct and the + last file */ + // --------------- + + // Check if we should do a unified write to a single file + if(m_LogSettings->bUnify) + { + // prepare the right id + int id = VERBOSITY_LEVELS*100 + type; + int ver = VERBOSITY_LEVELS; + + // write to memory + m_Messages[ver][m_nextMessages[ver]].Set((LogTypes::LOG_TYPE)id, v, Msg2); + + // ---------------------------------------------------------------------------------------- + // Write to file + // --------------- + if (m_Log[id]->m_pFile && m_Log[id]->m_bLogToFile) + fprintf(m_Log[id]->m_pFile, "%s", Msg2); + if (m_Log[ver*100 + LogTypes::MASTER_LOG] && m_Log[ver*100 + LogTypes::MASTER_LOG]->m_pFile + && m_LogSettings->bWriteMaster) + fprintf(m_Log[ver*100 + LogTypes::MASTER_LOG]->m_pFile, "%s", Msg2); + + /* In case it crashes write now to make sure you get the last messages. + Is this slower than caching it? */ + //fflush(m_Log[id]->m_pFile); + //fflush(m_Log[ver*100 + LogTypes::MASTER_LOG]->m_pFile); + + printf("%s", Msg2); // write to console screen + + // this limits the memory space used for the memory logs to MAX_MESSAGES rows + m_nextMessages[ver]++; + if (m_nextMessages[ver] >= MAX_MESSAGES) + m_nextMessages[ver] = 0; + // --------------- + } + else // write to separate files and structs + { + int id; + for (int i = VERBOSITY_LEVELS; i >= v ; i--) + { + // prepare the right id + id = i*100 + type; + + // write to memory + m_Messages[i][m_nextMessages[i]].Set((LogTypes::LOG_TYPE)id, v, Msg2); + + // ---------------------------------------------------------------------------------------- + // Write to file + // --------------- + if (m_Log[id]->m_pFile && m_Log[id]->m_bLogToFile) + fprintf(m_Log[id]->m_pFile, "%s", Msg2); + if (m_Log[i*100 + LogTypes::MASTER_LOG] && m_Log[i*100 + LogTypes::MASTER_LOG]->m_pFile + && m_LogSettings->bWriteMaster) + fprintf(m_Log[i*100 + LogTypes::MASTER_LOG]->m_pFile, "%s", Msg2); + + // Write now. Is this slower than caching it? + //fflush(m_Log[id]->m_pFile); + //fflush(m_Log[i*100 + LogTypes::MASTER_LOG]->m_pFile); + + printf("%s", Msg2); // write to console screen + + // this limits the memory space used for the memory logs to MAX_MESSAGES rows + m_nextMessages[i]++; + if (m_nextMessages[i] >= MAX_MESSAGES) + m_nextMessages[i] = 0; + // --------------- + } + } + m_bDirty = true; // tell LogWindow that the log has been updated +} + +bool IsLoggingActivated() +{ +#ifdef LOGGING + return true; +#else + return false; +#endif +} diff --git a/Source/Core/Core/Src/MemTools.cpp b/Source/Core/Core/Src/MemTools.cpp index bdf0cbc23a..052c8aed4b 100644 --- a/Source/Core/Core/Src/MemTools.cpp +++ b/Source/Core/Core/Src/MemTools.cpp @@ -1,220 +1,220 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - - -// TODO: create a working OS-neutral version of this file and put it in Common. - - -#ifdef _WIN32 - -#include -#include - -#include "Common.h" -#include "MemTools.h" -#include "HW/Memmap.h" -#include "PowerPC/PowerPC.h" -#include "PowerPC/Jit64/Jit.h" -#include "PowerPC/Jit64/JitBackpatch.h" -#include "x64Analyzer.h" - -namespace EMM -{ - -LONG NTAPI Handler(PEXCEPTION_POINTERS pPtrs) -{ - switch (pPtrs->ExceptionRecord->ExceptionCode) - { - case EXCEPTION_ACCESS_VIOLATION: - { - int accessType = (int)pPtrs->ExceptionRecord->ExceptionInformation[0]; - if (accessType == 8) //Rule out DEP - { - if(PowerPC::state == PowerPC::CPU_POWERDOWN) // Access violation during - // violent shutdown is fine - return EXCEPTION_CONTINUE_EXECUTION; - - MessageBox(0, _T("Tried to execute code that's not marked executable. This is likely a JIT bug.\n"), 0, 0); - return EXCEPTION_CONTINUE_SEARCH; - } - - //Where in the x86 code are we? - PVOID codeAddr = pPtrs->ExceptionRecord->ExceptionAddress; - unsigned char *codePtr = (unsigned char*)codeAddr; - - if (!Jit64::IsInJitCode(codePtr)) { - // Let's not prevent debugging. - return (DWORD)EXCEPTION_CONTINUE_SEARCH; - } - - //Figure out what address was hit - u64 badAddress = (u64)pPtrs->ExceptionRecord->ExceptionInformation[1]; - //TODO: First examine the address, make sure it's within the emulated memory space - u64 memspaceBottom = (u64)Memory::base; - if (badAddress < memspaceBottom) { - PanicAlert("Exception handler - access below memory space. %08x%08x", - badAddress >> 32, badAddress); - } - u32 emAddress = (u32)(badAddress - memspaceBottom); - - //Now we have the emulated address. - //Let's notify everyone who wants to be notified - //Notify(emAddress, accessType == 0 ? Read : Write); - - CONTEXT *ctx = pPtrs->ContextRecord; - //opportunity to play with the context - we can change the debug regs! - - //We could emulate the memory accesses here, but then they would still be around to take up - //execution resources. Instead, we backpatch into a generic memory call and retry. - u8 *new_rip = Jit64::BackPatch(codePtr, accessType, emAddress, ctx); - - // We no longer touch Rip, since we return back to the instruction, after overwriting it with a - // trampoline jump and some nops - if (new_rip) -#ifdef _M_X64 - ctx->Rip = (DWORD_PTR)new_rip; -#else - ctx->Eip = (DWORD_PTR)new_rip; -#endif - } - return (DWORD)EXCEPTION_CONTINUE_EXECUTION; - - case EXCEPTION_STACK_OVERFLOW: - MessageBox(0, _T("Stack overflow!"), 0,0); - return EXCEPTION_CONTINUE_SEARCH; - - case EXCEPTION_ILLEGAL_INSTRUCTION: - //No SSE support? Or simply bad codegen? - return EXCEPTION_CONTINUE_SEARCH; - - case EXCEPTION_PRIV_INSTRUCTION: - //okay, dynarec codegen is obviously broken. - return EXCEPTION_CONTINUE_SEARCH; - - case EXCEPTION_IN_PAGE_ERROR: - //okay, something went seriously wrong, out of memory? - return EXCEPTION_CONTINUE_SEARCH; - - case EXCEPTION_BREAKPOINT: - //might want to do something fun with this one day? - return EXCEPTION_CONTINUE_SEARCH; - - default: - return EXCEPTION_CONTINUE_SEARCH; - } -} - -void InstallExceptionHandler() -{ -#ifdef _M_X64 - // Make sure this is only called once per process execution - // Instead, could make a Uninstall function, but whatever.. - static bool handlerInstalled = false; - if (handlerInstalled) - return; - - AddVectoredExceptionHandler(TRUE, Handler); - handlerInstalled = true; -#endif -} - -} -#else - -namespace EMM { - -#if 0 -// -// backtrace useful function -// -void print_trace(const char * msg) -{ - void *array[100]; - size_t size; - char **strings; - size_t i; - - size = backtrace(array, 100); - strings = backtrace_symbols(array, size); - printf("%s Obtained %zd stack frames.\n", msg, size); - for (i = 0; i < size; i++) - printf("--> %s\n", strings[i]); - free(strings); -} - -void sigsegv_handler(int signal, int siginfo_t *info, void *raw_context) -{ - if (signal != SIGSEGV) - { - // We are not interested in other signals - handle it as usual. - return; - } - ucontext_t *context = (ucontext_t)raw_context; - int si_code = info->si_code; - if (si_code != SEGV_MAPERR) - { - // Huh? Return. - return; - } - mcontext_t *ctx = &context->uc_mcontext; - void *fault_memory_ptr = (void *)info->si_addr; - void *fault_instruction_ptr = (void *)ctx->mc_rip; - - if (!Jit64::IsInJitCode(fault_instruction_ptr)) { - // Let's not prevent debugging. - return; - } - - u64 memspaceBottom = (u64)Memory::base; - if (badAddress < memspaceBottom) { - PanicAlert("Exception handler - access below memory space. %08x%08x", - badAddress >> 32, badAddress); - } - u32 emAddress = (u32)(badAddress - memspaceBottom); - - // Backpatch time. - Jit64::BackPatch(fault_instruction_ptr, accessType, emAddress); -} - -#endif - -void InstallExceptionHandler() -{ -#ifdef _M_IX86 - PanicAlert("InstallExceptionHandler called, but this platform does not yet support it."); - return; -#endif - -#if 0 - sighandler_t old_signal_handler = signal(SIGSEGV , sigsegv_handler); - struct sigaction sa; - memset(&sa, 0, sizeof(sa)); - sa.sa_handler = sigsegv_handler; - sa.sa_flags = SA_SIGINFO; - sigemptyset(&sa.sa_mask); - sigaction(SIGSEGV, &sa, NULL); -#endif - - /* - * signal(xyz); - */ -} - -} - -#endif - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + + +// TODO: create a working OS-neutral version of this file and put it in Common. + + +#ifdef _WIN32 + +#include +#include + +#include "Common.h" +#include "MemTools.h" +#include "HW/Memmap.h" +#include "PowerPC/PowerPC.h" +#include "PowerPC/Jit64/Jit.h" +#include "PowerPC/Jit64/JitBackpatch.h" +#include "x64Analyzer.h" + +namespace EMM +{ + +LONG NTAPI Handler(PEXCEPTION_POINTERS pPtrs) +{ + switch (pPtrs->ExceptionRecord->ExceptionCode) + { + case EXCEPTION_ACCESS_VIOLATION: + { + int accessType = (int)pPtrs->ExceptionRecord->ExceptionInformation[0]; + if (accessType == 8) //Rule out DEP + { + if(PowerPC::state == PowerPC::CPU_POWERDOWN) // Access violation during + // violent shutdown is fine + return EXCEPTION_CONTINUE_EXECUTION; + + MessageBox(0, _T("Tried to execute code that's not marked executable. This is likely a JIT bug.\n"), 0, 0); + return EXCEPTION_CONTINUE_SEARCH; + } + + //Where in the x86 code are we? + PVOID codeAddr = pPtrs->ExceptionRecord->ExceptionAddress; + unsigned char *codePtr = (unsigned char*)codeAddr; + + if (!Jit64::IsInJitCode(codePtr)) { + // Let's not prevent debugging. + return (DWORD)EXCEPTION_CONTINUE_SEARCH; + } + + //Figure out what address was hit + u64 badAddress = (u64)pPtrs->ExceptionRecord->ExceptionInformation[1]; + //TODO: First examine the address, make sure it's within the emulated memory space + u64 memspaceBottom = (u64)Memory::base; + if (badAddress < memspaceBottom) { + PanicAlert("Exception handler - access below memory space. %08x%08x", + badAddress >> 32, badAddress); + } + u32 emAddress = (u32)(badAddress - memspaceBottom); + + //Now we have the emulated address. + //Let's notify everyone who wants to be notified + //Notify(emAddress, accessType == 0 ? Read : Write); + + CONTEXT *ctx = pPtrs->ContextRecord; + //opportunity to play with the context - we can change the debug regs! + + //We could emulate the memory accesses here, but then they would still be around to take up + //execution resources. Instead, we backpatch into a generic memory call and retry. + u8 *new_rip = Jit64::BackPatch(codePtr, accessType, emAddress, ctx); + + // We no longer touch Rip, since we return back to the instruction, after overwriting it with a + // trampoline jump and some nops + if (new_rip) +#ifdef _M_X64 + ctx->Rip = (DWORD_PTR)new_rip; +#else + ctx->Eip = (DWORD_PTR)new_rip; +#endif + } + return (DWORD)EXCEPTION_CONTINUE_EXECUTION; + + case EXCEPTION_STACK_OVERFLOW: + MessageBox(0, _T("Stack overflow!"), 0,0); + return EXCEPTION_CONTINUE_SEARCH; + + case EXCEPTION_ILLEGAL_INSTRUCTION: + //No SSE support? Or simply bad codegen? + return EXCEPTION_CONTINUE_SEARCH; + + case EXCEPTION_PRIV_INSTRUCTION: + //okay, dynarec codegen is obviously broken. + return EXCEPTION_CONTINUE_SEARCH; + + case EXCEPTION_IN_PAGE_ERROR: + //okay, something went seriously wrong, out of memory? + return EXCEPTION_CONTINUE_SEARCH; + + case EXCEPTION_BREAKPOINT: + //might want to do something fun with this one day? + return EXCEPTION_CONTINUE_SEARCH; + + default: + return EXCEPTION_CONTINUE_SEARCH; + } +} + +void InstallExceptionHandler() +{ +#ifdef _M_X64 + // Make sure this is only called once per process execution + // Instead, could make a Uninstall function, but whatever.. + static bool handlerInstalled = false; + if (handlerInstalled) + return; + + AddVectoredExceptionHandler(TRUE, Handler); + handlerInstalled = true; +#endif +} + +} +#else + +namespace EMM { + +#if 0 +// +// backtrace useful function +// +void print_trace(const char * msg) +{ + void *array[100]; + size_t size; + char **strings; + size_t i; + + size = backtrace(array, 100); + strings = backtrace_symbols(array, size); + printf("%s Obtained %zd stack frames.\n", msg, size); + for (i = 0; i < size; i++) + printf("--> %s\n", strings[i]); + free(strings); +} + +void sigsegv_handler(int signal, int siginfo_t *info, void *raw_context) +{ + if (signal != SIGSEGV) + { + // We are not interested in other signals - handle it as usual. + return; + } + ucontext_t *context = (ucontext_t)raw_context; + int si_code = info->si_code; + if (si_code != SEGV_MAPERR) + { + // Huh? Return. + return; + } + mcontext_t *ctx = &context->uc_mcontext; + void *fault_memory_ptr = (void *)info->si_addr; + void *fault_instruction_ptr = (void *)ctx->mc_rip; + + if (!Jit64::IsInJitCode(fault_instruction_ptr)) { + // Let's not prevent debugging. + return; + } + + u64 memspaceBottom = (u64)Memory::base; + if (badAddress < memspaceBottom) { + PanicAlert("Exception handler - access below memory space. %08x%08x", + badAddress >> 32, badAddress); + } + u32 emAddress = (u32)(badAddress - memspaceBottom); + + // Backpatch time. + Jit64::BackPatch(fault_instruction_ptr, accessType, emAddress); +} + +#endif + +void InstallExceptionHandler() +{ +#ifdef _M_IX86 + PanicAlert("InstallExceptionHandler called, but this platform does not yet support it."); + return; +#endif + +#if 0 + sighandler_t old_signal_handler = signal(SIGSEGV , sigsegv_handler); + struct sigaction sa; + memset(&sa, 0, sizeof(sa)); + sa.sa_handler = sigsegv_handler; + sa.sa_flags = SA_SIGINFO; + sigemptyset(&sa.sa_mask); + sigaction(SIGSEGV, &sa, NULL); +#endif + + /* + * signal(xyz); + */ +} + +} + +#endif + diff --git a/Source/Core/Core/Src/PatchEngine.cpp b/Source/Core/Core/Src/PatchEngine.cpp index 9f2b691c45..9abb493ad0 100644 --- a/Source/Core/Core/Src/PatchEngine.cpp +++ b/Source/Core/Core/Src/PatchEngine.cpp @@ -1,175 +1,175 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -// PatchEngine -// Supports simple memory patches, and has a partial Action Replay implementation -// in ActionReplay.cpp/h. - -// Zelda item hang fixes: -// [Tue Aug 21 2007] [18:30:40] 0x802904b4 in US released -// [Tue Aug 21 2007] [18:30:53] 0x80294d54 in EUR Demo version -// [Tue Aug 21 2007] [18:31:10] we just patch a blr on it (0x4E800020) -// [OnLoad] -// 0x80020394=dword,0x4e800020 - -#include -#include -#include -#include "StringUtil.h" -#include "PatchEngine.h" -#include "HW/Memmap.h" -#include "ActionReplay.h" - -using namespace Common; - -namespace PatchEngine -{ - -std::vector onFrame; -std::map speedHacks; - -void LoadPatchSection(const char *section, std::vector &patches, IniFile &ini) -{ - std::vector lines; - if (!ini.GetLines(section, lines)) - return; - - Patch currentPatch; - - for (std::vector::const_iterator iter = lines.begin(); iter != lines.end(); ++iter) - { - std::string line = *iter; - if (line.size()) - { - if (line[0] == '+' || line[0] == '$') - { - // Take care of the previous code - if (currentPatch.name.size()) patches.push_back(currentPatch); - currentPatch.entries.clear(); - - // Set active and name - currentPatch.active = (line[0] == '+') ? true : false; - if (currentPatch.active) - currentPatch.name = line.substr(2, line.size() - 2); - else - currentPatch.name = line.substr(1, line.size() - 1); - continue; - } - - std::string::size_type loc = line.find_first_of('=', 0); - if (loc != std::string::npos) - line.at(loc) = ':'; - - std::vector items; - SplitString(line, ":", items); - if (items.size() >= 3) { - PatchEntry pE; - bool success = true; - success = success && TryParseUInt(items[0], &pE.address); - success = success && TryParseUInt(items[2], &pE.value); - pE.type = (PatchType)ChooseStringFrom(items[1].c_str(), PatchTypeStrings); - success = success && (pE.type != (PatchType)-1); - if (success) - currentPatch.entries.push_back(pE); - } - } - } - if (currentPatch.name.size()) patches.push_back(currentPatch); -} - -static void LoadSpeedhacks(const char *section, std::map &hacks, IniFile &ini) { - std::vector keys; - ini.GetKeys(section, keys); - for (std::vector::const_iterator iter = keys.begin(); iter != keys.end(); ++iter) - { - std::string key = *iter; - std::string value; - ini.Get(section, key.c_str(), &value, "BOGUS"); - if (value != "BOGUS") - { - u32 address; - u32 cycles; - bool success = true; - success = success && TryParseUInt(std::string(key.c_str()), &address); - success = success && TryParseUInt(value, &cycles); - if (success) { - speedHacks[address] = (int)cycles; - } - } - } -} - -int GetSpeedhackCycles(u32 addr) -{ - std::map::const_iterator iter = speedHacks.find(addr); - if (iter == speedHacks.end()) - return 0; - else - return iter->second; -} - -void LoadPatches(const char *gameID) -{ - IniFile ini; - std::string filename = std::string(FULL_GAMECONFIG_DIR) + gameID + ".ini"; - if (ini.Load(filename.c_str())) { - LoadPatchSection("OnFrame", onFrame, ini); - LoadActionReplayCodes(ini); - LoadSpeedhacks("Speedhacks", speedHacks, ini); - } -} - -void ApplyPatches(const std::vector &patches) -{ - for (std::vector::const_iterator iter = patches.begin(); iter != patches.end(); ++iter) - { - if (iter->active) - { - for (std::vector::const_iterator iter2 = iter->entries.begin(); iter2 != iter->entries.end(); ++iter2) - { - u32 addr = iter2->address; - u32 value = iter2->value; - switch (iter2->type) - { - case PATCH_8BIT: - Memory::Write_U8((u8)value, addr); - break; - case PATCH_16BIT: - Memory::Write_U16((u16)value, addr); - break; - case PATCH_32BIT: - Memory::Write_U32(value, addr); - break; - default: - //unknown patchtype - break; - } - } - } - } -} - -void ApplyFramePatches() -{ - ApplyPatches(onFrame); -} - -void ApplyARPatches() -{ - ActionReplayRunAllActive(); -} -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// PatchEngine +// Supports simple memory patches, and has a partial Action Replay implementation +// in ActionReplay.cpp/h. + +// Zelda item hang fixes: +// [Tue Aug 21 2007] [18:30:40] 0x802904b4 in US released +// [Tue Aug 21 2007] [18:30:53] 0x80294d54 in EUR Demo version +// [Tue Aug 21 2007] [18:31:10] we just patch a blr on it (0x4E800020) +// [OnLoad] +// 0x80020394=dword,0x4e800020 + +#include +#include +#include +#include "StringUtil.h" +#include "PatchEngine.h" +#include "HW/Memmap.h" +#include "ActionReplay.h" + +using namespace Common; + +namespace PatchEngine +{ + +std::vector onFrame; +std::map speedHacks; + +void LoadPatchSection(const char *section, std::vector &patches, IniFile &ini) +{ + std::vector lines; + if (!ini.GetLines(section, lines)) + return; + + Patch currentPatch; + + for (std::vector::const_iterator iter = lines.begin(); iter != lines.end(); ++iter) + { + std::string line = *iter; + if (line.size()) + { + if (line[0] == '+' || line[0] == '$') + { + // Take care of the previous code + if (currentPatch.name.size()) patches.push_back(currentPatch); + currentPatch.entries.clear(); + + // Set active and name + currentPatch.active = (line[0] == '+') ? true : false; + if (currentPatch.active) + currentPatch.name = line.substr(2, line.size() - 2); + else + currentPatch.name = line.substr(1, line.size() - 1); + continue; + } + + std::string::size_type loc = line.find_first_of('=', 0); + if (loc != std::string::npos) + line.at(loc) = ':'; + + std::vector items; + SplitString(line, ":", items); + if (items.size() >= 3) { + PatchEntry pE; + bool success = true; + success = success && TryParseUInt(items[0], &pE.address); + success = success && TryParseUInt(items[2], &pE.value); + pE.type = (PatchType)ChooseStringFrom(items[1].c_str(), PatchTypeStrings); + success = success && (pE.type != (PatchType)-1); + if (success) + currentPatch.entries.push_back(pE); + } + } + } + if (currentPatch.name.size()) patches.push_back(currentPatch); +} + +static void LoadSpeedhacks(const char *section, std::map &hacks, IniFile &ini) { + std::vector keys; + ini.GetKeys(section, keys); + for (std::vector::const_iterator iter = keys.begin(); iter != keys.end(); ++iter) + { + std::string key = *iter; + std::string value; + ini.Get(section, key.c_str(), &value, "BOGUS"); + if (value != "BOGUS") + { + u32 address; + u32 cycles; + bool success = true; + success = success && TryParseUInt(std::string(key.c_str()), &address); + success = success && TryParseUInt(value, &cycles); + if (success) { + speedHacks[address] = (int)cycles; + } + } + } +} + +int GetSpeedhackCycles(u32 addr) +{ + std::map::const_iterator iter = speedHacks.find(addr); + if (iter == speedHacks.end()) + return 0; + else + return iter->second; +} + +void LoadPatches(const char *gameID) +{ + IniFile ini; + std::string filename = std::string(FULL_GAMECONFIG_DIR) + gameID + ".ini"; + if (ini.Load(filename.c_str())) { + LoadPatchSection("OnFrame", onFrame, ini); + LoadActionReplayCodes(ini); + LoadSpeedhacks("Speedhacks", speedHacks, ini); + } +} + +void ApplyPatches(const std::vector &patches) +{ + for (std::vector::const_iterator iter = patches.begin(); iter != patches.end(); ++iter) + { + if (iter->active) + { + for (std::vector::const_iterator iter2 = iter->entries.begin(); iter2 != iter->entries.end(); ++iter2) + { + u32 addr = iter2->address; + u32 value = iter2->value; + switch (iter2->type) + { + case PATCH_8BIT: + Memory::Write_U8((u8)value, addr); + break; + case PATCH_16BIT: + Memory::Write_U16((u16)value, addr); + break; + case PATCH_32BIT: + Memory::Write_U32(value, addr); + break; + default: + //unknown patchtype + break; + } + } + } + } +} + +void ApplyFramePatches() +{ + ApplyPatches(onFrame); +} + +void ApplyARPatches() +{ + ActionReplayRunAllActive(); +} +} // namespace diff --git a/Source/Core/Core/Src/Plugins/Plugin_DSP.cpp b/Source/Core/Core/Src/Plugins/Plugin_DSP.cpp index d12f906705..d68a743806 100644 --- a/Source/Core/Core/Src/Plugins/Plugin_DSP.cpp +++ b/Source/Core/Core/Src/Plugins/Plugin_DSP.cpp @@ -1,129 +1,129 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "DynamicLibrary.h" -#include "Plugin_DSP.h" - -namespace PluginDSP -{ - -// Function Pointer -TGetDllInfo GetDllInfo = 0; -TDllConfig DllConfig = 0; -TDllDebugger DllDebugger = 0; -TDSP_Initialize DSP_Initialize = 0; -TDSP_Shutdown DSP_Shutdown = 0; -TDSP_ReadMailBox DSP_ReadMailboxHigh = 0; -TDSP_ReadMailBox DSP_ReadMailboxLow = 0; -TDSP_WriteMailBox DSP_WriteMailboxHigh = 0; -TDSP_WriteMailBox DSP_WriteMailboxLow = 0; -TDSP_ReadControlRegister DSP_ReadControlRegister = 0; -TDSP_WriteControlRegister DSP_WriteControlRegister = 0; -TDSP_Update DSP_Update = 0; -TDSP_SendAIBuffer DSP_SendAIBuffer = 0; -TDSP_DoState DSP_DoState = 0; - -//! Library Instance -DynamicLibrary plugin; - -bool IsLoaded() -{ - return plugin.IsLoaded(); -} - -void Debug(HWND _hwnd, bool Show) -{ - DllDebugger(_hwnd, Show); -} - -void UnloadPlugin() -{ - plugin.Unload(); - - // Set Functions to NULL - GetDllInfo = 0; - DllConfig = 0; - DllDebugger = 0; - DSP_Initialize = 0; - DSP_Shutdown = 0; - DSP_ReadMailboxHigh = 0; - DSP_ReadMailboxLow = 0; - DSP_WriteMailboxHigh = 0; - DSP_WriteMailboxLow = 0; - DSP_ReadControlRegister = 0; - DSP_WriteControlRegister = 0; - DSP_Update = 0; - DSP_SendAIBuffer = 0; - DSP_DoState = 0; -} - -bool LoadPlugin(const char *_Filename) -{ - int ret = plugin.Load(_Filename); // we may have alredy loaded this to open the debugger - - if (ret == 1) - { - GetDllInfo = reinterpret_cast (plugin.Get("GetDllInfo")); - DllConfig = reinterpret_cast (plugin.Get("DllConfig")); - DllDebugger = reinterpret_cast (plugin.Get("DllDebugger")); - DSP_Initialize = reinterpret_cast (plugin.Get("DSP_Initialize")); - DSP_Shutdown = reinterpret_cast (plugin.Get("DSP_Shutdown")); - DSP_ReadMailboxHigh = reinterpret_cast (plugin.Get("DSP_ReadMailboxHigh")); - DSP_ReadMailboxLow = reinterpret_cast (plugin.Get("DSP_ReadMailboxLow")); - DSP_WriteMailboxHigh = reinterpret_cast (plugin.Get("DSP_WriteMailboxHigh")); - DSP_WriteMailboxLow = reinterpret_cast (plugin.Get("DSP_WriteMailboxLow")); - DSP_ReadControlRegister = reinterpret_cast (plugin.Get("DSP_ReadControlRegister")); - DSP_WriteControlRegister = reinterpret_cast (plugin.Get("DSP_WriteControlRegister")); - DSP_Update = reinterpret_cast (plugin.Get("DSP_Update")); - DSP_SendAIBuffer = reinterpret_cast (plugin.Get("DSP_SendAIBuffer")); - DSP_DoState = reinterpret_cast (plugin.Get("DSP_DoState")); - - if ((GetDllInfo != 0) && - (DSP_Initialize != 0) && - (DSP_Shutdown != 0) && - (DSP_ReadMailboxHigh != 0) && - (DSP_ReadMailboxLow != 0) && - (DSP_WriteMailboxHigh != 0) && - (DSP_WriteMailboxLow != 0) && - (DSP_ReadControlRegister != 0) && - (DSP_WriteControlRegister != 0) && - (DSP_Update != 0) && - (DSP_SendAIBuffer != 0) && - (DSP_DoState != 0)) - { - //PanicAlert("return true: %i", ret); - return true; - } - else - { - UnloadPlugin(); - return false; - } - } - else if (ret == 2) - { - //PanicAlert("return true: %i", ret); - return true; - } - else if (ret == 0) - return false; - - return false; -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "DynamicLibrary.h" +#include "Plugin_DSP.h" + +namespace PluginDSP +{ + +// Function Pointer +TGetDllInfo GetDllInfo = 0; +TDllConfig DllConfig = 0; +TDllDebugger DllDebugger = 0; +TDSP_Initialize DSP_Initialize = 0; +TDSP_Shutdown DSP_Shutdown = 0; +TDSP_ReadMailBox DSP_ReadMailboxHigh = 0; +TDSP_ReadMailBox DSP_ReadMailboxLow = 0; +TDSP_WriteMailBox DSP_WriteMailboxHigh = 0; +TDSP_WriteMailBox DSP_WriteMailboxLow = 0; +TDSP_ReadControlRegister DSP_ReadControlRegister = 0; +TDSP_WriteControlRegister DSP_WriteControlRegister = 0; +TDSP_Update DSP_Update = 0; +TDSP_SendAIBuffer DSP_SendAIBuffer = 0; +TDSP_DoState DSP_DoState = 0; + +//! Library Instance +DynamicLibrary plugin; + +bool IsLoaded() +{ + return plugin.IsLoaded(); +} + +void Debug(HWND _hwnd, bool Show) +{ + DllDebugger(_hwnd, Show); +} + +void UnloadPlugin() +{ + plugin.Unload(); + + // Set Functions to NULL + GetDllInfo = 0; + DllConfig = 0; + DllDebugger = 0; + DSP_Initialize = 0; + DSP_Shutdown = 0; + DSP_ReadMailboxHigh = 0; + DSP_ReadMailboxLow = 0; + DSP_WriteMailboxHigh = 0; + DSP_WriteMailboxLow = 0; + DSP_ReadControlRegister = 0; + DSP_WriteControlRegister = 0; + DSP_Update = 0; + DSP_SendAIBuffer = 0; + DSP_DoState = 0; +} + +bool LoadPlugin(const char *_Filename) +{ + int ret = plugin.Load(_Filename); // we may have alredy loaded this to open the debugger + + if (ret == 1) + { + GetDllInfo = reinterpret_cast (plugin.Get("GetDllInfo")); + DllConfig = reinterpret_cast (plugin.Get("DllConfig")); + DllDebugger = reinterpret_cast (plugin.Get("DllDebugger")); + DSP_Initialize = reinterpret_cast (plugin.Get("DSP_Initialize")); + DSP_Shutdown = reinterpret_cast (plugin.Get("DSP_Shutdown")); + DSP_ReadMailboxHigh = reinterpret_cast (plugin.Get("DSP_ReadMailboxHigh")); + DSP_ReadMailboxLow = reinterpret_cast (plugin.Get("DSP_ReadMailboxLow")); + DSP_WriteMailboxHigh = reinterpret_cast (plugin.Get("DSP_WriteMailboxHigh")); + DSP_WriteMailboxLow = reinterpret_cast (plugin.Get("DSP_WriteMailboxLow")); + DSP_ReadControlRegister = reinterpret_cast (plugin.Get("DSP_ReadControlRegister")); + DSP_WriteControlRegister = reinterpret_cast (plugin.Get("DSP_WriteControlRegister")); + DSP_Update = reinterpret_cast (plugin.Get("DSP_Update")); + DSP_SendAIBuffer = reinterpret_cast (plugin.Get("DSP_SendAIBuffer")); + DSP_DoState = reinterpret_cast (plugin.Get("DSP_DoState")); + + if ((GetDllInfo != 0) && + (DSP_Initialize != 0) && + (DSP_Shutdown != 0) && + (DSP_ReadMailboxHigh != 0) && + (DSP_ReadMailboxLow != 0) && + (DSP_WriteMailboxHigh != 0) && + (DSP_WriteMailboxLow != 0) && + (DSP_ReadControlRegister != 0) && + (DSP_WriteControlRegister != 0) && + (DSP_Update != 0) && + (DSP_SendAIBuffer != 0) && + (DSP_DoState != 0)) + { + //PanicAlert("return true: %i", ret); + return true; + } + else + { + UnloadPlugin(); + return false; + } + } + else if (ret == 2) + { + //PanicAlert("return true: %i", ret); + return true; + } + else if (ret == 0) + return false; + + return false; +} + +} // namespace diff --git a/Source/Core/Core/Src/Plugins/Plugin_DVD.cpp b/Source/Core/Core/Src/Plugins/Plugin_DVD.cpp index 29e4c315c1..e907d2ee6e 100644 --- a/Source/Core/Core/Src/Plugins/Plugin_DVD.cpp +++ b/Source/Core/Core/Src/Plugins/Plugin_DVD.cpp @@ -1,163 +1,163 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "stdafx.h" -#include "Plugin_DVD.h" - - -namespace PluginDVD -{ - -//! Function Types -typedef void (__cdecl* TGetDllInfo) (PLUGIN_INFO*); -typedef void (__cdecl* TDllConfig) (HWND); -typedef void (__cdecl* TDVD_Initialize) (SDVDInitialize); -typedef void (__cdecl* TDVD_Shutdown) (); -typedef void (__cdecl* TDVD_SetISOFile) (const char*); -typedef BOOL (__cdecl* TDVD_GetISOName) (TCHAR*, int); -typedef BOOL (__cdecl* TDVD_ReadToPtr) (LPBYTE, u64, u64); -typedef BOOL (__cdecl* TDVD_IsValid) (); -typedef u32 (__cdecl* TDVD_Read32) (u64); - -//! Function Pointer -TGetDllInfo g_GetDllInfo = NULL; -TDllConfig g_DllConfig = NULL; -TDVD_Initialize g_DVD_Initialize = NULL; -TDVD_Shutdown g_DVD_Shutdown = NULL; -TDVD_SetISOFile g_DVD_SetISOFile = NULL; -TDVD_GetISOName g_DVD_GetISOName = NULL; -TDVD_ReadToPtr g_DVD_ReadToPtr = NULL; -TDVD_Read32 g_DVD_Read32 = NULL; -TDVD_IsValid g_DVD_IsValid = NULL; - -//! Library Instance -HINSTANCE g_hLibraryInstance = NULL; - -bool IsLoaded() -{ - return (g_hLibraryInstance != NULL); -} - -bool LoadPlugin(const char *_strFilename) -{ - UnloadPlugin(); - g_hLibraryInstance = LoadLibrary(_strFilename); - - if (g_hLibraryInstance) - { - g_GetDllInfo = reinterpret_cast (GetProcAddress(g_hLibraryInstance, "GetDllInfo")); - g_DllConfig = reinterpret_cast (GetProcAddress(g_hLibraryInstance, "DllConfig")); - g_DVD_Initialize = reinterpret_cast (GetProcAddress(g_hLibraryInstance, "DVD_Initialize")); - g_DVD_Shutdown = reinterpret_cast (GetProcAddress(g_hLibraryInstance, "DVD_Shutdown")); - g_DVD_SetISOFile = reinterpret_cast (GetProcAddress(g_hLibraryInstance, "DVD_SetISOFile")); - g_DVD_GetISOName = reinterpret_cast (GetProcAddress(g_hLibraryInstance, "DVD_GetISOName")); - g_DVD_ReadToPtr = reinterpret_cast (GetProcAddress(g_hLibraryInstance, "DVD_ReadToPtr")); - g_DVD_Read32 = reinterpret_cast (GetProcAddress(g_hLibraryInstance, "DVD_Read32")); - g_DVD_IsValid = reinterpret_cast (GetProcAddress(g_hLibraryInstance, "DVD_IsValid")); - - if ((g_GetDllInfo != NULL) && - (g_DllConfig != NULL) && - (g_DVD_Initialize != NULL) && - (g_DVD_Shutdown != NULL) && - (g_DVD_SetISOFile != NULL) && - (g_DVD_GetISOName != NULL) && - (g_DVD_ReadToPtr != NULL) && - (g_DVD_IsValid != NULL) && - (g_DVD_Read32 != NULL)) - { - return true; - } - else - { - UnloadPlugin(); - return false; - } - } - - return false; -} - -void UnloadPlugin() -{ - // Set Functions to NULL - g_GetDllInfo = NULL; - g_DllConfig = NULL; - g_DVD_Initialize = NULL; - g_DVD_Shutdown = NULL; - g_DVD_SetISOFile = NULL; - g_DVD_Read32 = NULL; - g_DVD_ReadToPtr = NULL; - g_DVD_IsValid = NULL; - - // Unload library - if (g_hLibraryInstance != NULL) - { - FreeLibrary(g_hLibraryInstance); - g_hLibraryInstance = NULL; - } -} - -// -// --- Plugin Functions --- -// - -void GetDllInfo(PLUGIN_INFO* _PluginInfo) -{ - g_GetDllInfo(_PluginInfo); -} - -void DllConfig(HWND _hParent) -{ - g_DllConfig(_hParent); -} - -void DVD_Initialize(SDVDInitialize _DVDInitialize) -{ - g_DVD_Initialize(_DVDInitialize); -} - -void DVD_Shutdown() -{ - g_DVD_Shutdown(); -} - -bool DVD_ReadToPtr(LPBYTE ptr, u64 _dwOffset, u64 _dwLength) -{ - return (g_DVD_ReadToPtr(ptr, _dwOffset, _dwLength) == TRUE) ? true : false; -} - -bool DVD_IsValid() -{ - return (g_DVD_IsValid() == TRUE) ? true : false; -} - -u32 DVD_Read32(u64 _dwOffset) -{ - return g_DVD_Read32(_dwOffset); -} - -void DVD_SetISOFile(const char* _szFilename) -{ - g_DVD_SetISOFile(_szFilename); -} - -BOOL DVD_GetISOName(TCHAR * _szFilename, int maxlen) -{ - return g_DVD_GetISOName(_szFilename, maxlen); -} - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "stdafx.h" +#include "Plugin_DVD.h" + + +namespace PluginDVD +{ + +//! Function Types +typedef void (__cdecl* TGetDllInfo) (PLUGIN_INFO*); +typedef void (__cdecl* TDllConfig) (HWND); +typedef void (__cdecl* TDVD_Initialize) (SDVDInitialize); +typedef void (__cdecl* TDVD_Shutdown) (); +typedef void (__cdecl* TDVD_SetISOFile) (const char*); +typedef BOOL (__cdecl* TDVD_GetISOName) (TCHAR*, int); +typedef BOOL (__cdecl* TDVD_ReadToPtr) (LPBYTE, u64, u64); +typedef BOOL (__cdecl* TDVD_IsValid) (); +typedef u32 (__cdecl* TDVD_Read32) (u64); + +//! Function Pointer +TGetDllInfo g_GetDllInfo = NULL; +TDllConfig g_DllConfig = NULL; +TDVD_Initialize g_DVD_Initialize = NULL; +TDVD_Shutdown g_DVD_Shutdown = NULL; +TDVD_SetISOFile g_DVD_SetISOFile = NULL; +TDVD_GetISOName g_DVD_GetISOName = NULL; +TDVD_ReadToPtr g_DVD_ReadToPtr = NULL; +TDVD_Read32 g_DVD_Read32 = NULL; +TDVD_IsValid g_DVD_IsValid = NULL; + +//! Library Instance +HINSTANCE g_hLibraryInstance = NULL; + +bool IsLoaded() +{ + return (g_hLibraryInstance != NULL); +} + +bool LoadPlugin(const char *_strFilename) +{ + UnloadPlugin(); + g_hLibraryInstance = LoadLibrary(_strFilename); + + if (g_hLibraryInstance) + { + g_GetDllInfo = reinterpret_cast (GetProcAddress(g_hLibraryInstance, "GetDllInfo")); + g_DllConfig = reinterpret_cast (GetProcAddress(g_hLibraryInstance, "DllConfig")); + g_DVD_Initialize = reinterpret_cast (GetProcAddress(g_hLibraryInstance, "DVD_Initialize")); + g_DVD_Shutdown = reinterpret_cast (GetProcAddress(g_hLibraryInstance, "DVD_Shutdown")); + g_DVD_SetISOFile = reinterpret_cast (GetProcAddress(g_hLibraryInstance, "DVD_SetISOFile")); + g_DVD_GetISOName = reinterpret_cast (GetProcAddress(g_hLibraryInstance, "DVD_GetISOName")); + g_DVD_ReadToPtr = reinterpret_cast (GetProcAddress(g_hLibraryInstance, "DVD_ReadToPtr")); + g_DVD_Read32 = reinterpret_cast (GetProcAddress(g_hLibraryInstance, "DVD_Read32")); + g_DVD_IsValid = reinterpret_cast (GetProcAddress(g_hLibraryInstance, "DVD_IsValid")); + + if ((g_GetDllInfo != NULL) && + (g_DllConfig != NULL) && + (g_DVD_Initialize != NULL) && + (g_DVD_Shutdown != NULL) && + (g_DVD_SetISOFile != NULL) && + (g_DVD_GetISOName != NULL) && + (g_DVD_ReadToPtr != NULL) && + (g_DVD_IsValid != NULL) && + (g_DVD_Read32 != NULL)) + { + return true; + } + else + { + UnloadPlugin(); + return false; + } + } + + return false; +} + +void UnloadPlugin() +{ + // Set Functions to NULL + g_GetDllInfo = NULL; + g_DllConfig = NULL; + g_DVD_Initialize = NULL; + g_DVD_Shutdown = NULL; + g_DVD_SetISOFile = NULL; + g_DVD_Read32 = NULL; + g_DVD_ReadToPtr = NULL; + g_DVD_IsValid = NULL; + + // Unload library + if (g_hLibraryInstance != NULL) + { + FreeLibrary(g_hLibraryInstance); + g_hLibraryInstance = NULL; + } +} + +// +// --- Plugin Functions --- +// + +void GetDllInfo(PLUGIN_INFO* _PluginInfo) +{ + g_GetDllInfo(_PluginInfo); +} + +void DllConfig(HWND _hParent) +{ + g_DllConfig(_hParent); +} + +void DVD_Initialize(SDVDInitialize _DVDInitialize) +{ + g_DVD_Initialize(_DVDInitialize); +} + +void DVD_Shutdown() +{ + g_DVD_Shutdown(); +} + +bool DVD_ReadToPtr(LPBYTE ptr, u64 _dwOffset, u64 _dwLength) +{ + return (g_DVD_ReadToPtr(ptr, _dwOffset, _dwLength) == TRUE) ? true : false; +} + +bool DVD_IsValid() +{ + return (g_DVD_IsValid() == TRUE) ? true : false; +} + +u32 DVD_Read32(u64 _dwOffset) +{ + return g_DVD_Read32(_dwOffset); +} + +void DVD_SetISOFile(const char* _szFilename) +{ + g_DVD_SetISOFile(_szFilename); +} + +BOOL DVD_GetISOName(TCHAR * _szFilename, int maxlen) +{ + return g_DVD_GetISOName(_szFilename, maxlen); +} + } // end of namespace PluginDVD \ No newline at end of file diff --git a/Source/Core/Core/Src/Plugins/Plugin_PAD.cpp b/Source/Core/Core/Src/Plugins/Plugin_PAD.cpp index 00d6e10efb..2402d002cf 100644 --- a/Source/Core/Core/Src/Plugins/Plugin_PAD.cpp +++ b/Source/Core/Core/Src/Plugins/Plugin_PAD.cpp @@ -1,83 +1,83 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "DynamicLibrary.h" -#include "Plugin_PAD.h" - -namespace PluginPAD -{ - -// Function Pointers -TGetDllInfo GetDllInfo = 0; -TPAD_Shutdown PAD_Shutdown = 0; -TDllConfig DllConfig = 0; -TPAD_Initialize PAD_Initialize = 0; -TPAD_GetStatus PAD_GetStatus = 0; -TPAD_Rumble PAD_Rumble = 0; -TPAD_GetAttachedPads PAD_GetAttachedPads = 0; - -// Library Instance -DynamicLibrary plugin; - -bool IsLoaded() -{ - return plugin.IsLoaded(); -} - -void UnloadPlugin() -{ - plugin.Unload(); - // Set Functions to 0 - GetDllInfo = 0; - PAD_Shutdown = 0; - DllConfig = 0; - PAD_Initialize = 0; - PAD_GetStatus = 0; - PAD_Rumble = 0; -} - -bool LoadPlugin(const char *_Filename) -{ - if (plugin.Load(_Filename)) - { - GetDllInfo = reinterpret_cast (plugin.Get("GetDllInfo")); - DllConfig = reinterpret_cast (plugin.Get("DllConfig")); - PAD_Initialize = reinterpret_cast (plugin.Get("PAD_Initialize")); - PAD_Shutdown = reinterpret_cast (plugin.Get("PAD_Shutdown")); - PAD_GetStatus = reinterpret_cast (plugin.Get("PAD_GetStatus")); - PAD_Rumble = reinterpret_cast (plugin.Get("PAD_Rumble")); - PAD_GetAttachedPads = reinterpret_cast(plugin.Get("PAD_GetAttachedPads")); - - if ((GetDllInfo != 0) && - (DllConfig != 0) && - (PAD_Initialize != 0) && - (PAD_Shutdown != 0) && - (PAD_GetStatus != 0)) - { - return true; - } - else - { - UnloadPlugin(); - return false; - } - } - - return false; -} - -} // end of namespace PluginPAD +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "DynamicLibrary.h" +#include "Plugin_PAD.h" + +namespace PluginPAD +{ + +// Function Pointers +TGetDllInfo GetDllInfo = 0; +TPAD_Shutdown PAD_Shutdown = 0; +TDllConfig DllConfig = 0; +TPAD_Initialize PAD_Initialize = 0; +TPAD_GetStatus PAD_GetStatus = 0; +TPAD_Rumble PAD_Rumble = 0; +TPAD_GetAttachedPads PAD_GetAttachedPads = 0; + +// Library Instance +DynamicLibrary plugin; + +bool IsLoaded() +{ + return plugin.IsLoaded(); +} + +void UnloadPlugin() +{ + plugin.Unload(); + // Set Functions to 0 + GetDllInfo = 0; + PAD_Shutdown = 0; + DllConfig = 0; + PAD_Initialize = 0; + PAD_GetStatus = 0; + PAD_Rumble = 0; +} + +bool LoadPlugin(const char *_Filename) +{ + if (plugin.Load(_Filename)) + { + GetDllInfo = reinterpret_cast (plugin.Get("GetDllInfo")); + DllConfig = reinterpret_cast (plugin.Get("DllConfig")); + PAD_Initialize = reinterpret_cast (plugin.Get("PAD_Initialize")); + PAD_Shutdown = reinterpret_cast (plugin.Get("PAD_Shutdown")); + PAD_GetStatus = reinterpret_cast (plugin.Get("PAD_GetStatus")); + PAD_Rumble = reinterpret_cast (plugin.Get("PAD_Rumble")); + PAD_GetAttachedPads = reinterpret_cast(plugin.Get("PAD_GetAttachedPads")); + + if ((GetDllInfo != 0) && + (DllConfig != 0) && + (PAD_Initialize != 0) && + (PAD_Shutdown != 0) && + (PAD_GetStatus != 0)) + { + return true; + } + else + { + UnloadPlugin(); + return false; + } + } + + return false; +} + +} // end of namespace PluginPAD diff --git a/Source/Core/Core/Src/Plugins/Plugin_Video.cpp b/Source/Core/Core/Src/Plugins/Plugin_Video.cpp index f76b3fb5b9..a9d09f6fa5 100644 --- a/Source/Core/Core/Src/Plugins/Plugin_Video.cpp +++ b/Source/Core/Core/Src/Plugins/Plugin_Video.cpp @@ -1,143 +1,143 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "DynamicLibrary.h" -#include "Plugin_Video.h" -#include "Plugin.h" - -extern DynamicLibrary Common::CPlugin; - -namespace PluginVideo -{ - -// Function Pointer -TGetDllInfo GetDllInfo = 0; -TDllConfig DllConfig = 0; -TDllDebugger DllDebugger = 0; -TVideo_Initialize Video_Initialize = 0; -TVideo_Prepare Video_Prepare = 0; -TVideo_Shutdown Video_Shutdown = 0; -TVideo_SendFifoData Video_SendFifoData = 0; -TVideo_UpdateXFB Video_UpdateXFB = 0; -TVideo_Screenshot Video_Screenshot = 0; -TVideo_EnterLoop Video_EnterLoop = 0; -TVideo_AddMessage Video_AddMessage = 0; -TVideo_DoState Video_DoState = 0; -TVideo_Stop Video_Stop = 0; - -// Library Instance -DynamicLibrary plugin; - -void Debug(HWND _hwnd, bool Show) -{ - DllDebugger(_hwnd, Show); -} - -bool IsLoaded() -{ - return plugin.IsLoaded(); -} - -void UnloadPlugin() -{ - //PanicAlert("Video UnloadPlugin"); - - // set Functions to 0 - GetDllInfo = 0; - DllConfig = 0; - DllDebugger = 0; - Video_Initialize = 0; - Video_Prepare = 0; - Video_Shutdown = 0; - Video_SendFifoData = 0; - Video_UpdateXFB = 0; - Video_AddMessage = 0; - Video_DoState = 0; - Video_Stop = 0; - - plugin.Unload(); -} - -// ============================================== -/* Load the plugin, but first check if we have already loaded the plugin for - the sake of showing the debugger. - - ret values: - 0 = failed - 1 = loaded successfully - 2 = already loaded from PluginManager.cpp, use it as it is */ -// ------------ -bool LoadPlugin(const char *_Filename) -{ - int ret = plugin.Load(_Filename); - - if (ret == 1) - { - GetDllInfo = reinterpret_cast (plugin.Get("GetDllInfo")); - DllConfig = reinterpret_cast (plugin.Get("DllConfig")); - DllDebugger = reinterpret_cast (plugin.Get("DllDebugger")); - Video_Initialize = reinterpret_cast (plugin.Get("Video_Initialize")); - Video_Prepare = reinterpret_cast (plugin.Get("Video_Prepare")); - Video_Shutdown = reinterpret_cast (plugin.Get("Video_Shutdown")); - Video_SendFifoData = reinterpret_cast (plugin.Get("Video_SendFifoData")); - Video_UpdateXFB = reinterpret_cast (plugin.Get("Video_UpdateXFB")); - Video_Screenshot = reinterpret_cast (plugin.Get("Video_Screenshot")); - Video_EnterLoop = reinterpret_cast (plugin.Get("Video_EnterLoop")); - Video_AddMessage = reinterpret_cast (plugin.Get("Video_AddMessage")); - Video_DoState = reinterpret_cast (plugin.Get("Video_DoState")); - Video_Stop = reinterpret_cast (plugin.Get("Video_Stop")); - if ((GetDllInfo != 0) && - (DllConfig != 0) && - (DllDebugger != 0) && - (Video_Initialize != 0) && - (Video_Prepare != 0) && - (Video_Shutdown != 0) && - (Video_SendFifoData != 0) && - (Video_UpdateXFB != 0) && - (Video_EnterLoop != 0) && - (Video_Screenshot != 0) && - (Video_AddMessage != 0) && - (Video_DoState != 0) && - (Video_Stop != 0)) - { - //PanicAlert("return true: %i", ret); - return true; - } - else - { - UnloadPlugin(); - return false; - } - } - else if(ret == 2) - { - //PanicAlert("return true: %i", ret); - return true; - } - else if(ret == 0) - { - //PanicAlert("return false: %i", ret); - return false; - } - - return false; -} -// ============ - - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "DynamicLibrary.h" +#include "Plugin_Video.h" +#include "Plugin.h" + +extern DynamicLibrary Common::CPlugin; + +namespace PluginVideo +{ + +// Function Pointer +TGetDllInfo GetDllInfo = 0; +TDllConfig DllConfig = 0; +TDllDebugger DllDebugger = 0; +TVideo_Initialize Video_Initialize = 0; +TVideo_Prepare Video_Prepare = 0; +TVideo_Shutdown Video_Shutdown = 0; +TVideo_SendFifoData Video_SendFifoData = 0; +TVideo_UpdateXFB Video_UpdateXFB = 0; +TVideo_Screenshot Video_Screenshot = 0; +TVideo_EnterLoop Video_EnterLoop = 0; +TVideo_AddMessage Video_AddMessage = 0; +TVideo_DoState Video_DoState = 0; +TVideo_Stop Video_Stop = 0; + +// Library Instance +DynamicLibrary plugin; + +void Debug(HWND _hwnd, bool Show) +{ + DllDebugger(_hwnd, Show); +} + +bool IsLoaded() +{ + return plugin.IsLoaded(); +} + +void UnloadPlugin() +{ + //PanicAlert("Video UnloadPlugin"); + + // set Functions to 0 + GetDllInfo = 0; + DllConfig = 0; + DllDebugger = 0; + Video_Initialize = 0; + Video_Prepare = 0; + Video_Shutdown = 0; + Video_SendFifoData = 0; + Video_UpdateXFB = 0; + Video_AddMessage = 0; + Video_DoState = 0; + Video_Stop = 0; + + plugin.Unload(); +} + +// ============================================== +/* Load the plugin, but first check if we have already loaded the plugin for + the sake of showing the debugger. + + ret values: + 0 = failed + 1 = loaded successfully + 2 = already loaded from PluginManager.cpp, use it as it is */ +// ------------ +bool LoadPlugin(const char *_Filename) +{ + int ret = plugin.Load(_Filename); + + if (ret == 1) + { + GetDllInfo = reinterpret_cast (plugin.Get("GetDllInfo")); + DllConfig = reinterpret_cast (plugin.Get("DllConfig")); + DllDebugger = reinterpret_cast (plugin.Get("DllDebugger")); + Video_Initialize = reinterpret_cast (plugin.Get("Video_Initialize")); + Video_Prepare = reinterpret_cast (plugin.Get("Video_Prepare")); + Video_Shutdown = reinterpret_cast (plugin.Get("Video_Shutdown")); + Video_SendFifoData = reinterpret_cast (plugin.Get("Video_SendFifoData")); + Video_UpdateXFB = reinterpret_cast (plugin.Get("Video_UpdateXFB")); + Video_Screenshot = reinterpret_cast (plugin.Get("Video_Screenshot")); + Video_EnterLoop = reinterpret_cast (plugin.Get("Video_EnterLoop")); + Video_AddMessage = reinterpret_cast (plugin.Get("Video_AddMessage")); + Video_DoState = reinterpret_cast (plugin.Get("Video_DoState")); + Video_Stop = reinterpret_cast (plugin.Get("Video_Stop")); + if ((GetDllInfo != 0) && + (DllConfig != 0) && + (DllDebugger != 0) && + (Video_Initialize != 0) && + (Video_Prepare != 0) && + (Video_Shutdown != 0) && + (Video_SendFifoData != 0) && + (Video_UpdateXFB != 0) && + (Video_EnterLoop != 0) && + (Video_Screenshot != 0) && + (Video_AddMessage != 0) && + (Video_DoState != 0) && + (Video_Stop != 0)) + { + //PanicAlert("return true: %i", ret); + return true; + } + else + { + UnloadPlugin(); + return false; + } + } + else if(ret == 2) + { + //PanicAlert("return true: %i", ret); + return true; + } + else if(ret == 0) + { + //PanicAlert("return false: %i", ret); + return false; + } + + return false; +} +// ============ + + +} // namespace diff --git a/Source/Core/Core/Src/Plugins/Plugin_Wiimote.cpp b/Source/Core/Core/Src/Plugins/Plugin_Wiimote.cpp index 8adff3313e..976fb9801b 100644 --- a/Source/Core/Core/Src/Plugins/Plugin_Wiimote.cpp +++ b/Source/Core/Core/Src/Plugins/Plugin_Wiimote.cpp @@ -1,105 +1,105 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "DynamicLibrary.h" -#include "Plugin_Wiimote.h" - -namespace PluginWiimote -{ - - // Function Pointer - TGetDllInfo GetDllInfo = 0; - TDllConfig DllConfig = 0; - TWiimote_Initialize Wiimote_Initialize = 0; - TWiimote_Shutdown Wiimote_Shutdown = 0; - TWiimote_Output Wiimote_ControlChannel = 0; - TWiimote_Input Wiimote_InterruptChannel = 0; - TWiimote_Update Wiimote_Update = 0; - TWiimote_GetAttachedControllers Wiimote_GetAttachedControllers = 0; - TWiimote_DoState Wiimote_DoState = 0; - - //! Library Instance - DynamicLibrary plugin; - - bool IsLoaded() - { - return plugin.IsLoaded(); - } - - void UnloadPlugin() - { - plugin.Unload(); - - // Set Functions to NULL - GetDllInfo = 0; - DllConfig = 0; - Wiimote_Initialize = 0; - Wiimote_Shutdown = 0; - Wiimote_ControlChannel = 0; - Wiimote_InterruptChannel = 0; - Wiimote_Update = 0; - Wiimote_GetAttachedControllers = 0; - Wiimote_DoState = 0; - } - - bool LoadPlugin(const char *_Filename) - { - if (plugin.Load(_Filename)) - { - LOG(MASTER_LOG, "getting Wiimote Plugin function pointers..."); - GetDllInfo = reinterpret_cast (plugin.Get("GetDllInfo")); - DllConfig = reinterpret_cast (plugin.Get("DllConfig")); - Wiimote_Initialize = reinterpret_cast (plugin.Get("Wiimote_Initialize")); - Wiimote_Shutdown = reinterpret_cast (plugin.Get("Wiimote_Shutdown")); - Wiimote_ControlChannel = reinterpret_cast (plugin.Get("Wiimote_ControlChannel")); - Wiimote_InterruptChannel = reinterpret_cast (plugin.Get("Wiimote_InterruptChannel")); - Wiimote_Update = reinterpret_cast (plugin.Get("Wiimote_Update")); - Wiimote_GetAttachedControllers = reinterpret_cast (plugin.Get("Wiimote_GetAttachedControllers")); - Wiimote_DoState = reinterpret_cast (plugin.Get("Wiimote_DoState")); - - LOG(MASTER_LOG, "%s: 0x%p", "GetDllInfo", GetDllInfo); - LOG(MASTER_LOG, "%s: 0x%p", "DllConfig", DllConfig); - LOG(MASTER_LOG, "%s: 0x%p", "Wiimote_Initialize", Wiimote_Initialize); - LOG(MASTER_LOG, "%s: 0x%p", "Wiimote_Shutdown", Wiimote_Shutdown); - LOG(MASTER_LOG, "%s: 0x%p", "Wiimote_ControlChannel", Wiimote_ControlChannel); - LOG(MASTER_LOG, "%s: 0x%p", "Wiimote_InterruptChannel", Wiimote_InterruptChannel); - LOG(MASTER_LOG, "%s: 0x%p", "Wiimote_Update", Wiimote_Update); - LOG(MASTER_LOG, "%s: 0x%p", "Wiimote_GetAttachedControllers", Wiimote_GetAttachedControllers); - LOG(MASTER_LOG, "%s: 0x%p", "Wiimote_DoState", Wiimote_DoState); - if ((GetDllInfo != 0) && - (Wiimote_Initialize != 0) && - (Wiimote_Shutdown != 0) && - (Wiimote_ControlChannel != 0) && - (Wiimote_InterruptChannel != 0) && - (Wiimote_Update != 0) && - (Wiimote_GetAttachedControllers != 0) && - (Wiimote_DoState != 0)) - { - return true; - } - else - { - UnloadPlugin(); - return false; - } - } - - return false; - } - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "DynamicLibrary.h" +#include "Plugin_Wiimote.h" + +namespace PluginWiimote +{ + + // Function Pointer + TGetDllInfo GetDllInfo = 0; + TDllConfig DllConfig = 0; + TWiimote_Initialize Wiimote_Initialize = 0; + TWiimote_Shutdown Wiimote_Shutdown = 0; + TWiimote_Output Wiimote_ControlChannel = 0; + TWiimote_Input Wiimote_InterruptChannel = 0; + TWiimote_Update Wiimote_Update = 0; + TWiimote_GetAttachedControllers Wiimote_GetAttachedControllers = 0; + TWiimote_DoState Wiimote_DoState = 0; + + //! Library Instance + DynamicLibrary plugin; + + bool IsLoaded() + { + return plugin.IsLoaded(); + } + + void UnloadPlugin() + { + plugin.Unload(); + + // Set Functions to NULL + GetDllInfo = 0; + DllConfig = 0; + Wiimote_Initialize = 0; + Wiimote_Shutdown = 0; + Wiimote_ControlChannel = 0; + Wiimote_InterruptChannel = 0; + Wiimote_Update = 0; + Wiimote_GetAttachedControllers = 0; + Wiimote_DoState = 0; + } + + bool LoadPlugin(const char *_Filename) + { + if (plugin.Load(_Filename)) + { + LOG(MASTER_LOG, "getting Wiimote Plugin function pointers..."); + GetDllInfo = reinterpret_cast (plugin.Get("GetDllInfo")); + DllConfig = reinterpret_cast (plugin.Get("DllConfig")); + Wiimote_Initialize = reinterpret_cast (plugin.Get("Wiimote_Initialize")); + Wiimote_Shutdown = reinterpret_cast (plugin.Get("Wiimote_Shutdown")); + Wiimote_ControlChannel = reinterpret_cast (plugin.Get("Wiimote_ControlChannel")); + Wiimote_InterruptChannel = reinterpret_cast (plugin.Get("Wiimote_InterruptChannel")); + Wiimote_Update = reinterpret_cast (plugin.Get("Wiimote_Update")); + Wiimote_GetAttachedControllers = reinterpret_cast (plugin.Get("Wiimote_GetAttachedControllers")); + Wiimote_DoState = reinterpret_cast (plugin.Get("Wiimote_DoState")); + + LOG(MASTER_LOG, "%s: 0x%p", "GetDllInfo", GetDllInfo); + LOG(MASTER_LOG, "%s: 0x%p", "DllConfig", DllConfig); + LOG(MASTER_LOG, "%s: 0x%p", "Wiimote_Initialize", Wiimote_Initialize); + LOG(MASTER_LOG, "%s: 0x%p", "Wiimote_Shutdown", Wiimote_Shutdown); + LOG(MASTER_LOG, "%s: 0x%p", "Wiimote_ControlChannel", Wiimote_ControlChannel); + LOG(MASTER_LOG, "%s: 0x%p", "Wiimote_InterruptChannel", Wiimote_InterruptChannel); + LOG(MASTER_LOG, "%s: 0x%p", "Wiimote_Update", Wiimote_Update); + LOG(MASTER_LOG, "%s: 0x%p", "Wiimote_GetAttachedControllers", Wiimote_GetAttachedControllers); + LOG(MASTER_LOG, "%s: 0x%p", "Wiimote_DoState", Wiimote_DoState); + if ((GetDllInfo != 0) && + (Wiimote_Initialize != 0) && + (Wiimote_Shutdown != 0) && + (Wiimote_ControlChannel != 0) && + (Wiimote_InterruptChannel != 0) && + (Wiimote_Update != 0) && + (Wiimote_GetAttachedControllers != 0) && + (Wiimote_DoState != 0)) + { + return true; + } + else + { + UnloadPlugin(); + return false; + } + } + + return false; + } + +} // namespace diff --git a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter.cpp b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter.cpp index 9dbde16423..b226bbeb68 100644 --- a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter.cpp +++ b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter.cpp @@ -1,166 +1,166 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "../../HW/Memmap.h" -#include "../../HW/CPU.h" -#include "../PPCTables.h" -#include "Interpreter.h" -#include "../../Debugger/Debugger_SymbolMap.h" -#include "../../CoreTiming.h" -#include "../../Core.h" -#include "PowerPCDisasm.h" -#include "../../IPC_HLE/WII_IPC_HLE.h" - - -namespace { - u32 last_pc; -} - -// function tables - -namespace Interpreter -{ -// cpu register to keep the code readable -u32 *m_GPR = PowerPC::ppcState.gpr; -bool m_EndBlock = false; - -_interpreterInstruction m_opTable[64]; -_interpreterInstruction m_opTable4[1024]; -_interpreterInstruction m_opTable19[1024]; -_interpreterInstruction m_opTable31[1024]; -_interpreterInstruction m_opTable59[32]; -_interpreterInstruction m_opTable63[1024]; - -void RunTable4(UGeckoInstruction _inst) {m_opTable4 [_inst.SUBOP10](_inst);} -void RunTable19(UGeckoInstruction _inst) {m_opTable19[_inst.SUBOP10](_inst);} -void RunTable31(UGeckoInstruction _inst) {m_opTable31[_inst.SUBOP10](_inst);} -void RunTable59(UGeckoInstruction _inst) {m_opTable59[_inst.SUBOP5 ](_inst);} -void RunTable63(UGeckoInstruction _inst) {m_opTable63[_inst.SUBOP10](_inst);} - -void Init() -{ -} - -void Shutdown() -{ -} - -void patches() -{ -/* if (Memory::Read_U16(0x90000880) == 0x130b) - { - PanicAlert("Memory::Read_U16(0x900008800) == 0x130b"); - } -*/ -/* if (PC == 0x80074cd4) - { - u16 command = Common::swap16(Memory::Read_U16(PowerPC::ppcState.gpr[3] + 8)); - if (command == 0x0b13) - { - PanicAlert("command: %x", command); - CCPU::Break(); - } - }*/ -} - -void SingleStepInner(void) -{ - static UGeckoInstruction instCode; - - NPC = PC + sizeof(UGeckoInstruction); - 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 - { - // check if we have to generate a FPU unavailable exception - if (!PPCTables::UsesFPU(instCode)) - m_opTable[instCode.OPCD](instCode); - else - { - PowerPC::ppcState.Exceptions |= EXCEPTION_FPU_UNAVAILABLE; - PowerPC::CheckExceptions(); - m_EndBlock = true; - } - - } - last_pc = PC; - PC = NPC; - - if (PowerPC::ppcState.gpr[1] == 0) { - printf("%i Corrupt stack", PowerPC::ppcState.DebugCount); -// CCPU::Break(); - } -#if defined(_DEBUG) || defined(DEBUGFAST) - PowerPC::ppcState.DebugCount++; -#endif - patches(); -} - -void SingleStep() -{ - SingleStepInner(); - - CoreTiming::slicelength = 1; - CoreTiming::downcount = 0; - CoreTiming::Advance(); - - if (PowerPC::ppcState.Exceptions) - { - PowerPC::CheckExceptions(); - PC = NPC; - } -} - -// sFastRun - inspired by GCemu -void Run() -{ - while (!PowerPC::state) - { - //we have to check exceptions at branches apparently (or maybe just rfi?) - while (CoreTiming::downcount > 0) - { - m_EndBlock = false; - int i; - for (i = 0; !m_EndBlock; i++) - { - SingleStepInner(); - } - CoreTiming::downcount -= i; - } - - CoreTiming::Advance(); - - if (PowerPC::ppcState.Exceptions) - { - PowerPC::CheckExceptions(); - PC = NPC; - } - } -} - -void unknown_instruction(UGeckoInstruction _inst) -{ - CCPU::Break(); - printf("Last PC = %08x : %s\n", last_pc, DisassembleGekko(Memory::ReadUnchecked_U32(last_pc), last_pc)); - Debugger::PrintCallstack(); - _dbg_assert_msg_(GEKKO, 0, "\nIntCPU: Unknown instr %08x at PC = %08x last_PC = %08x LR = %08x\n", _inst.hex, PC, last_pc, LR); -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "../../HW/Memmap.h" +#include "../../HW/CPU.h" +#include "../PPCTables.h" +#include "Interpreter.h" +#include "../../Debugger/Debugger_SymbolMap.h" +#include "../../CoreTiming.h" +#include "../../Core.h" +#include "PowerPCDisasm.h" +#include "../../IPC_HLE/WII_IPC_HLE.h" + + +namespace { + u32 last_pc; +} + +// function tables + +namespace Interpreter +{ +// cpu register to keep the code readable +u32 *m_GPR = PowerPC::ppcState.gpr; +bool m_EndBlock = false; + +_interpreterInstruction m_opTable[64]; +_interpreterInstruction m_opTable4[1024]; +_interpreterInstruction m_opTable19[1024]; +_interpreterInstruction m_opTable31[1024]; +_interpreterInstruction m_opTable59[32]; +_interpreterInstruction m_opTable63[1024]; + +void RunTable4(UGeckoInstruction _inst) {m_opTable4 [_inst.SUBOP10](_inst);} +void RunTable19(UGeckoInstruction _inst) {m_opTable19[_inst.SUBOP10](_inst);} +void RunTable31(UGeckoInstruction _inst) {m_opTable31[_inst.SUBOP10](_inst);} +void RunTable59(UGeckoInstruction _inst) {m_opTable59[_inst.SUBOP5 ](_inst);} +void RunTable63(UGeckoInstruction _inst) {m_opTable63[_inst.SUBOP10](_inst);} + +void Init() +{ +} + +void Shutdown() +{ +} + +void patches() +{ +/* if (Memory::Read_U16(0x90000880) == 0x130b) + { + PanicAlert("Memory::Read_U16(0x900008800) == 0x130b"); + } +*/ +/* if (PC == 0x80074cd4) + { + u16 command = Common::swap16(Memory::Read_U16(PowerPC::ppcState.gpr[3] + 8)); + if (command == 0x0b13) + { + PanicAlert("command: %x", command); + CCPU::Break(); + } + }*/ +} + +void SingleStepInner(void) +{ + static UGeckoInstruction instCode; + + NPC = PC + sizeof(UGeckoInstruction); + 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 + { + // check if we have to generate a FPU unavailable exception + if (!PPCTables::UsesFPU(instCode)) + m_opTable[instCode.OPCD](instCode); + else + { + PowerPC::ppcState.Exceptions |= EXCEPTION_FPU_UNAVAILABLE; + PowerPC::CheckExceptions(); + m_EndBlock = true; + } + + } + last_pc = PC; + PC = NPC; + + if (PowerPC::ppcState.gpr[1] == 0) { + printf("%i Corrupt stack", PowerPC::ppcState.DebugCount); +// CCPU::Break(); + } +#if defined(_DEBUG) || defined(DEBUGFAST) + PowerPC::ppcState.DebugCount++; +#endif + patches(); +} + +void SingleStep() +{ + SingleStepInner(); + + CoreTiming::slicelength = 1; + CoreTiming::downcount = 0; + CoreTiming::Advance(); + + if (PowerPC::ppcState.Exceptions) + { + PowerPC::CheckExceptions(); + PC = NPC; + } +} + +// sFastRun - inspired by GCemu +void Run() +{ + while (!PowerPC::state) + { + //we have to check exceptions at branches apparently (or maybe just rfi?) + while (CoreTiming::downcount > 0) + { + m_EndBlock = false; + int i; + for (i = 0; !m_EndBlock; i++) + { + SingleStepInner(); + } + CoreTiming::downcount -= i; + } + + CoreTiming::Advance(); + + if (PowerPC::ppcState.Exceptions) + { + PowerPC::CheckExceptions(); + PC = NPC; + } + } +} + +void unknown_instruction(UGeckoInstruction _inst) +{ + CCPU::Break(); + printf("Last PC = %08x : %s\n", last_pc, DisassembleGekko(Memory::ReadUnchecked_U32(last_pc), last_pc)); + Debugger::PrintCallstack(); + _dbg_assert_msg_(GEKKO, 0, "\nIntCPU: Unknown instr %08x at PC = %08x last_PC = %08x LR = %08x\n", _inst.hex, PC, last_pc, LR); +} + +} // namespace diff --git a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_Branch.cpp b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_Branch.cpp index f26135032d..3ae22f197f 100644 --- a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_Branch.cpp +++ b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_Branch.cpp @@ -1,143 +1,143 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Interpreter.h" -#include "../../HW/CPU.h" -#include "../../HLE/HLE.h" -#include "../PPCAnalyst.h" - -namespace Interpreter -{ - -void bx(UGeckoInstruction _inst) -{ - if (_inst.LK) - LR = PC + 4; - if (_inst.AA) - NPC = SignExt26(_inst.LI << 2); - else - NPC = PC+SignExt26(_inst.LI << 2); -/* -#ifdef _DEBUG - if (_inst.LK) - { - PPCAnalyst::LogFunctionCall(NPC); - } -#endif*/ - - m_EndBlock = true; -} - -// bcx - ugly, straight from PPC manual equations :) -void bcx(UGeckoInstruction _inst) -{ - if ((_inst.BO & BO_DONT_DECREMENT_FLAG) == 0) - CTR--; - - const bool true_false = ((_inst.BO >> 3) & 1); - const bool only_counter_check = ((_inst.BO >> 4) & 1); - const bool only_condition_check = ((_inst.BO >> 2) & 1); - int ctr_check = ((CTR != 0) ^ (_inst.BO >> 1)) & 1; - bool counter = only_condition_check || ctr_check; - bool condition = only_counter_check || (GetCRBit(_inst.BI) == u32(true_false)); - - if (counter && condition) - { - if (_inst.LK) - LR = PC + 4; - if (_inst.AA) - NPC = SignExt16(_inst.BD << 2); - else - NPC = PC + SignExt16(_inst.BD << 2); - } - m_EndBlock = true; -} - -void bcctrx(UGeckoInstruction _inst) -{ - if ((_inst.BO & BO_DONT_DECREMENT_FLAG) == 0) - CTR--; - - int condition = ((_inst.BO>>4) | (GetCRBit(_inst.BI) == ((_inst.BO>>3) & 1))) & 1; - - if (condition) - { - if (_inst.LK) - LR = PC + 4; - NPC = CTR & (~3); - } - m_EndBlock = true; -} - -void bclrx(UGeckoInstruction _inst) -{ - if ((_inst.BO & BO_DONT_DECREMENT_FLAG) == 0) - CTR--; - - int counter = ((_inst.BO >> 2) | ((CTR != 0) ^ (_inst.BO >> 1)))&1; - int condition = ((_inst.BO >> 4) | (GetCRBit(_inst.BI) == ((_inst.BO >> 3) & 1))) & 1; - - if (counter & condition) - { - NPC = LR & (~3); - if (_inst.LK) - LR = PC+4; - } - m_EndBlock = true; -} - -void HLEFunction(UGeckoInstruction _inst) -{ - m_EndBlock = true; - HLE::Execute(PC, _inst.hex); -} - -void CompiledBlock(UGeckoInstruction _inst) -{ - _assert_msg_(GEKKO, 0, "CompiledBlock - shouldn't be here!"); -} - -void rfi(UGeckoInstruction _inst) -{ - //Bits SRR1[0,5-9,16�23, 25�27, 30�31] are placed into the corresponding bits of the MSR. - //MSR[13] is set to 0. - const int mask = 0x87C0FF73; - MSR = (MSR & ~mask) | (SRR1 & mask); - MSR &= 0xFFFDFFFF; //TODO: VERIFY - NPC = SRR0; // TODO: VERIFY - m_EndBlock = true; - // After an RFI, exceptions should be checked IMMEDIATELY without going back into - // other code! TODO(ector): fix this properly - // PowerPC::CheckExceptions(); -} - -void rfid(UGeckoInstruction _inst) -{ - _dbg_assert_msg_(GEKKO,0,"Instruction unimplemented (does this instruction even exist?)","rfid"); - m_EndBlock = true; -} - -// sc isn't really used for anything important in gc games (just for a write barrier) so we really don't have to emulate it. -// We do it anyway, though :P -void sc(UGeckoInstruction _inst) -{ - PowerPC::ppcState.Exceptions |= EXCEPTION_SYSCALL; - PowerPC::CheckExceptions(); - m_EndBlock = true; -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Interpreter.h" +#include "../../HW/CPU.h" +#include "../../HLE/HLE.h" +#include "../PPCAnalyst.h" + +namespace Interpreter +{ + +void bx(UGeckoInstruction _inst) +{ + if (_inst.LK) + LR = PC + 4; + if (_inst.AA) + NPC = SignExt26(_inst.LI << 2); + else + NPC = PC+SignExt26(_inst.LI << 2); +/* +#ifdef _DEBUG + if (_inst.LK) + { + PPCAnalyst::LogFunctionCall(NPC); + } +#endif*/ + + m_EndBlock = true; +} + +// bcx - ugly, straight from PPC manual equations :) +void bcx(UGeckoInstruction _inst) +{ + if ((_inst.BO & BO_DONT_DECREMENT_FLAG) == 0) + CTR--; + + const bool true_false = ((_inst.BO >> 3) & 1); + const bool only_counter_check = ((_inst.BO >> 4) & 1); + const bool only_condition_check = ((_inst.BO >> 2) & 1); + int ctr_check = ((CTR != 0) ^ (_inst.BO >> 1)) & 1; + bool counter = only_condition_check || ctr_check; + bool condition = only_counter_check || (GetCRBit(_inst.BI) == u32(true_false)); + + if (counter && condition) + { + if (_inst.LK) + LR = PC + 4; + if (_inst.AA) + NPC = SignExt16(_inst.BD << 2); + else + NPC = PC + SignExt16(_inst.BD << 2); + } + m_EndBlock = true; +} + +void bcctrx(UGeckoInstruction _inst) +{ + if ((_inst.BO & BO_DONT_DECREMENT_FLAG) == 0) + CTR--; + + int condition = ((_inst.BO>>4) | (GetCRBit(_inst.BI) == ((_inst.BO>>3) & 1))) & 1; + + if (condition) + { + if (_inst.LK) + LR = PC + 4; + NPC = CTR & (~3); + } + m_EndBlock = true; +} + +void bclrx(UGeckoInstruction _inst) +{ + if ((_inst.BO & BO_DONT_DECREMENT_FLAG) == 0) + CTR--; + + int counter = ((_inst.BO >> 2) | ((CTR != 0) ^ (_inst.BO >> 1)))&1; + int condition = ((_inst.BO >> 4) | (GetCRBit(_inst.BI) == ((_inst.BO >> 3) & 1))) & 1; + + if (counter & condition) + { + NPC = LR & (~3); + if (_inst.LK) + LR = PC+4; + } + m_EndBlock = true; +} + +void HLEFunction(UGeckoInstruction _inst) +{ + m_EndBlock = true; + HLE::Execute(PC, _inst.hex); +} + +void CompiledBlock(UGeckoInstruction _inst) +{ + _assert_msg_(GEKKO, 0, "CompiledBlock - shouldn't be here!"); +} + +void rfi(UGeckoInstruction _inst) +{ + //Bits SRR1[0,5-9,16�23, 25�27, 30�31] are placed into the corresponding bits of the MSR. + //MSR[13] is set to 0. + const int mask = 0x87C0FF73; + MSR = (MSR & ~mask) | (SRR1 & mask); + MSR &= 0xFFFDFFFF; //TODO: VERIFY + NPC = SRR0; // TODO: VERIFY + m_EndBlock = true; + // After an RFI, exceptions should be checked IMMEDIATELY without going back into + // other code! TODO(ector): fix this properly + // PowerPC::CheckExceptions(); +} + +void rfid(UGeckoInstruction _inst) +{ + _dbg_assert_msg_(GEKKO,0,"Instruction unimplemented (does this instruction even exist?)","rfid"); + m_EndBlock = true; +} + +// sc isn't really used for anything important in gc games (just for a write barrier) so we really don't have to emulate it. +// We do it anyway, though :P +void sc(UGeckoInstruction _inst) +{ + PowerPC::ppcState.Exceptions |= EXCEPTION_SYSCALL; + PowerPC::CheckExceptions(); + m_EndBlock = true; +} + +} // namespace diff --git a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp index 72942868dc..3dbdcbec80 100644 --- a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp +++ b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_FloatingPoint.cpp @@ -1,537 +1,537 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include -#include - -#ifdef _WIN32 -#define _interlockedbittestandset workaround_ms_header_bug_platform_sdk6_set -#define _interlockedbittestandreset workaround_ms_header_bug_platform_sdk6_reset -#define _interlockedbittestandset64 workaround_ms_header_bug_platform_sdk6_set64 -#define _interlockedbittestandreset64 workaround_ms_header_bug_platform_sdk6_reset64 -#include -#undef _interlockedbittestandset -#undef _interlockedbittestandreset -#undef _interlockedbittestandset64 -#undef _interlockedbittestandreset64 -#else -#include -#endif - -#include "../../Core.h" -#include "Interpreter.h" - -// SUPER MONKEY BALL IS BEING A ROYAL PAIN -// We are missing the caller of 800070ec -// POSSIBLE APPROACHES: -// * Full SW FPU. Urgh. -// * Partial SW FPU, emulate just as much as necessary for monkey ball. Feasible but a lot of work. -// * HLE hacking. Figure out what all the evil functions really do and fake them. DONE (well, works okay-ish) - -// Interesting places in Super Monkey Ball: -// 80036654: fctwixz stuff -// 80007e08: -// -98: Various entry points that loads various odd fp values into f1 -// 800070b0: Estimate inverse square root. -// 800070ec: Examine f1. Reads a value out of locked cache into f2 (fixed address). Some cases causes us to call the above thing. -// If all goes well, jump to 70b0, which estimates the inverse square root. -// Then multiply the loaded variable with the original value of f1. Result should be the square root. (1 / sqrt(x)) * x = x / sqrt(x) = sqrt(x) -// 8000712c: Similar, but does not do the multiply at the end, just an frspx. -// 8000716c: Sort of similar, but has extra junk at the end. -// -// -// 800072a4 - nightmare of nightmares -// Fun stuff used: -// bso+ -// mcrfs (ARGH pulls stuff out of .. FPSCR). it uses this to check the result of frsp mostly (!!!!) -// crclr -// crset -// crxor -// fnabs -// Super Monkey Ball reads FPRF & friends after fmadds, fmuls, frspx -// WHY do the FR & FI flags affect it so much? - -namespace Interpreter -{ - -void UpdateFPSCR(UReg_FPSCR fp); -void UpdateSSEState(); - -void UpdateFPRF(double value) -{ - u64 ivalue = *((u64*)&value); - // 5 bits (C, <, >, =, ?) - // top: class descriptor - FPSCR.FPRF = 4; - // easy cases first - if (ivalue == 0) { - // positive zero - FPSCR.FPRF = 0x2; - } else if (ivalue == 0x8000000000000000ULL) { - // negative zero - FPSCR.FPRF = 0x12; - } else if (ivalue == 0x7FF0000000000000ULL) { - // positive inf - FPSCR.FPRF = 0x5; - } else if (ivalue == 0xFFF0000000000000ULL) { - // negative inf - FPSCR.FPRF = 0x9; - } else { - // OK let's dissect this thing. - int sign = (int)(ivalue >> 63); - int exp = (int)((ivalue >> 52) & 0x7FF); - if (exp >= 1 && exp <= 2046) { - // Nice normalized number. - if (sign) { - FPSCR.FPRF = 0x8; // negative - } else { - FPSCR.FPRF = 0x4; // positive - } - return; - } - u64 mantissa = ivalue & 0x000FFFFFFFFFFFFFULL; - // int mantissa_top = (int)(mantissa >> 51); - if (exp == 0 && mantissa) { - // Denormalized number. - if (sign) { - FPSCR.FPRF = 0x18; - } else { - FPSCR.FPRF = 0x14; - } - } else if (exp == 0x7FF && mantissa /* && mantissa_top*/) { - FPSCR.FPRF = 0x11; // Quiet NAN - return; - } - } -} - - -// extremely rare -void Helper_UpdateCR1(double _fValue) -{ - FPSCR.FPRF = 0; - if (_fValue == 0.0 || _fValue == -0.0) - FPSCR.FPRF |= 2; - if (_fValue > 0.0) - FPSCR.FPRF |= 4; - if (_fValue < 0.0) - FPSCR.FPRF |= 8; - SetCRField(1, (FPSCR.Hex & 0x0000F000) >> 12); - - PanicAlert("CR1"); -} - -bool IsNAN(double _dValue) -{ - return _dValue != _dValue; -} - -void fcmpo(UGeckoInstruction _inst) -{ - double fa = rPS0(_inst.FA); - double fb = rPS0(_inst.FB); - u32 compareResult; - if (IsNAN(fa) || IsNAN(fb)) compareResult = 1; - else if (fa < fb) compareResult = 8; - else if (fa > fb) compareResult = 4; - else compareResult = 2; - - FPSCR.FPRF = compareResult; - SetCRField(_inst.CRFD, compareResult); - -/* missing part - if ((frA) is an SNaN or (frB) is an SNaN ) - then VXSNAN ¬ 1 - if VE = 0 - then VXVC ¬ 1 - else if ((frA) is a QNaN or (frB) is a QNaN ) - then VXVC ¬ 1 */ -} - -void fcmpu(UGeckoInstruction _inst) -{ - double fa = rPS0(_inst.FA); - double fb = rPS0(_inst.FB); - - u32 compareResult; - if (IsNAN(fa) || IsNAN(fb)) compareResult = 1; - else if (fa < fb) compareResult = 8; - else if (fa > fb) compareResult = 4; - else compareResult = 2; - - FPSCR.FPRF = compareResult; - SetCRField(_inst.CRFD, compareResult); - -/* missing part - if ((frA) is an SNaN or (frB) is an SNaN) - then VXSNAN ¬ 1 */ -} - -// Apply current rounding mode -void fctiwx(UGeckoInstruction _inst) -{ - UpdateSSEState(); - const double b = rPS0(_inst.FB); - u32 value; - if (b > (double)0x7fffffff) - { - value = 0x7fffffff; - FPSCR.VXCVI = 1; - } - else if (b < -(double)0x7fffffff) - { - value = 0x80000000; - FPSCR.VXCVI = 1; - } - else - { - value = (u32)(s32)_mm_cvtsd_si32(_mm_set_sd(b)); // obey current rounding mode -// double d_value = (double)value; -// bool inexact = (d_value != b); -// FPSCR.FI = inexact ? 1 : 0; -// FPSCR.XX |= FPSCR.FI; -// FPSCR.FR = fabs(d_value) > fabs(b); - } - - //TODO: FR - //FPRF undefined - - riPS0(_inst.FD) = (u64)value; // zero extend - if (_inst.Rc) - Helper_UpdateCR1(rPS0(_inst.FD)); -} - -/* -In the float -> int direction, floating point input values larger than the largest -representable int result in 0x80000000 (a very negative number) rather than the -largest representable int on PowerPC. */ - -// Always round toward zero -void fctiwzx(UGeckoInstruction _inst) -{ - //UpdateFPSCR(FPSCR); - const double b = rPS0(_inst.FB); - u32 value; - if (b > (double)0x7fffffff) - { - value = 0x7fffffff; - FPSCR.VXCVI = 1; - } - else if (b < -(double)0x7fffffff) - { - value = 0x80000000; - FPSCR.VXCVI = 1; - } - else - { - value = (u32)(s32)_mm_cvttsd_si32(_mm_set_sd(b)); // truncate -// double d_value = (double)value; -// bool inexact = (d_value != b); -// FPSCR.FI = inexact ? 1 : 0; -// FPSCR.XX |= FPSCR.FI; -// FPSCR.FR = 1; //fabs(d_value) > fabs(b); - } - //FPRF undefined - - riPS0(_inst.FD) = (u64)value; - if (_inst.Rc) - Helper_UpdateCR1(rPS0(_inst.FD)); -} - -void fmrx(UGeckoInstruction _inst) -{ - riPS0(_inst.FD) = riPS0(_inst.FB); - // This is a binary instruction. Does not alter FPSCR - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} - -void fabsx(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = fabs(rPS0(_inst.FB)); - // This is a binary instruction. Does not alter FPSCR - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} - -void fnabsx(UGeckoInstruction _inst) -{ - riPS0(_inst.FD) = riPS0(_inst.FB) | (1ULL << 63); - // This is a binary instruction. Does not alter FPSCR - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} - -void fnegx(UGeckoInstruction _inst) -{ - riPS0(_inst.FD) = riPS0(_inst.FB) ^ (1ULL << 63); - // This is a binary instruction. Does not alter FPSCR - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} - -void fselx(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = (rPS0(_inst.FA) >= -0.0) ? rPS0(_inst.FC) : rPS0(_inst.FB); - // This is a binary instruction. Does not alter FPSCR - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} - - -// !!! warning !!! -// PS1 must be set to the value of PS0 or DragonballZ will be f**ked up -// PS1 is said to be undefined -// Super Monkey Ball is using this to do wacky tricks so we need 100% correct emulation. -void frspx(UGeckoInstruction _inst) // round to single -{ - if (true || FPSCR.RN != 0) - { - // Not used in Super Monkey Ball - UpdateSSEState(); - double b = rPS0(_inst.FB); - double rounded = (double)(float)b; - FPSCR.FI = b != rounded; // changing both of these affect Super Monkey Ball behaviour greatly. - FPSCR.FR = 1; // WHY? fabs(rounded) > fabs(b); - rPS0(_inst.FD) = rPS1(_inst.FD) = rounded; - return; - // PanicAlert("frspx: FPSCR.RN=%i", FPSCR.RN); - } - - // OK, let's try it in 100% software! Not yet working right. - union { - double d; - u64 i; - } in, out; - in.d = rPS0(_inst.FB); - out = in; - int sign = (int)(in.i >> 63); - int exp = (int)((in.i >> 52) & 0x7FF); - u64 mantissa = in.i & 0x000FFFFFFFFFFFFFULL; - u64 mantissa_single = mantissa & 0x000FFFFFE0000000ULL; - u64 leftover_single = mantissa & 0x000000001FFFFFFFULL; - - // OK. First make sure that we have a "normal" number. - if (exp >= 1 && exp <= 2046) { - // OK. Check for overflow. TODO - - FPSCR.FI = leftover_single != 0; // Inexact - if (leftover_single >= 0x10000000ULL) { - //PanicAlert("rounding up"); - FPSCR.FR = 1; - mantissa_single += 0x20000000; - if (mantissa_single & 0x0010000000000000ULL) { - // PanicAlert("renormalizing"); - mantissa_single >>= 1; - exp += 1; - // if (exp > 2046) { OVERFLOW } - } - } - out.i = ((u64)sign << 63) | ((u64)exp << 52) | mantissa_single; - } else { - if (!exp && !mantissa) { - // Positive or negative Zero. All is well. - FPSCR.FI = 0; - FPSCR.FR = 0; - } else if (exp == 0 && mantissa) { - // Denormalized number. - PanicAlert("denorm"); - } else if (exp == 2047 && !mantissa) { - // Infinite. - //PanicAlert("infinite"); - FPSCR.FI = 1; - FPSCR.FR = 1; -// FPSCR.OX = 1; - } else { - //PanicAlert("NAN %08x %08x", in.i >> 32, in.i); - } - } - UpdateFPRF(out.d); - FPSCR.FR = 1; // SUPER MONKEY BALL HACK - rPS0(_inst.FD) = rPS1(_inst.FD) = out.d; - - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} - - -void fmulx(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = rPS0(_inst.FA) * rPS0(_inst.FC); - FPSCR.FI = 0; - FPSCR.FR = 1; - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} -void fmulsx(UGeckoInstruction _inst) -{ - double d_value = rPS0(_inst.FA) * rPS0(_inst.FC); - rPS0(_inst.FD) = rPS1(_inst.FD) = static_cast(d_value); - FPSCR.FI = d_value != rPS0(_inst.FD); - FPSCR.FR = rand()&1; - UpdateFPRF(rPS0(_inst.FD)); - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} - - -void fmaddx(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = (rPS0(_inst.FA) * rPS0(_inst.FC)) + rPS0(_inst.FB); - FPSCR.FI = 0; - FPSCR.FR = 0; - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} -void fmaddsx(UGeckoInstruction _inst) -{ - double d_value = (rPS0(_inst.FA) * rPS0(_inst.FC)) + rPS0(_inst.FB); - rPS0(_inst.FD) = rPS1(_inst.FD) = - static_cast(d_value); - FPSCR.FI = d_value != rPS0(_inst.FD); - FPSCR.FR = 0; - UpdateFPRF(rPS0(_inst.FD)); - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} - - -void faddx(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = rPS0(_inst.FA) + rPS0(_inst.FB); -// FPSCR.FI = 0; -// FPSCR.FR = 1; - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} -void faddsx(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = rPS1(_inst.FD) = static_cast(rPS0(_inst.FA) + rPS0(_inst.FB)); -// FPSCR.FI = 0; -// FPSCR.FR = 1; -// FPSCR.Hex = (rand() ^ (rand() << 8) ^ (rand() << 16)) & ~(0x000000F8); - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} - - -void fdivx(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = rPS0(_inst.FA) / rPS0(_inst.FB); -// FPSCR.FI = 0; -// FPSCR.FR = 1; - if (fabs(rPS0(_inst.FB)) == 0.0) { - FPSCR.ZX = 1; - } - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} -void fdivsx(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = rPS1(_inst.FD) = static_cast(rPS0(_inst.FA) / rPS0(_inst.FB)); -// FPSCR.FI = 0; -// FPSCR.FR = 1; - if (fabs(rPS0(_inst.FB)) == 0.0) { - FPSCR.ZX = 1; - } - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} -void fresx(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = rPS1(_inst.FD) = static_cast(1.0f / rPS0(_inst.FB)); -// FPSCR.FI = 0; -// FPSCR.FR = 1; - if (fabs(rPS0(_inst.FB)) == 0.0) { - FPSCR.ZX = 1; - } - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} - - -void fmsubx(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = (rPS0(_inst.FA) * rPS0(_inst.FC)) - rPS0(_inst.FB); -// FPSCR.FI = 0; -// FPSCR.FR = 0; - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} - -void fmsubsx(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = rPS1(_inst.FD) = - static_cast((rPS0(_inst.FA) * rPS0(_inst.FC)) - rPS0(_inst.FB)); -// FPSCR.FI = 0; -// FPSCR.FR = 0; - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} - - -void fnmaddx(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = -((rPS0(_inst.FA) * rPS0(_inst.FC)) + rPS0(_inst.FB)); -// FPSCR.FI = 0; -// FPSCR.FR = 0; - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} -void fnmaddsx(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = rPS1(_inst.FD) = - static_cast(-((rPS0(_inst.FA) * rPS0(_inst.FC)) + rPS0(_inst.FB))); -// FPSCR.FI = 0; -// FPSCR.FR = 0; - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} - - -void fnmsubx(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = -((rPS0(_inst.FA) * rPS0(_inst.FC)) - rPS0(_inst.FB)); -// FPSCR.FI = 0; -// FPSCR.FR = 0; - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} -void fnmsubsx(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = rPS1(_inst.FD) = - static_cast(-((rPS0(_inst.FA) * rPS0(_inst.FC)) - rPS0(_inst.FB))); -// FPSCR.FI = 0; -// FPSCR.FR = 0; - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} - - -void fsubx(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = rPS0(_inst.FA) - rPS0(_inst.FB); -// FPSCR.FI = 0; -// FPSCR.FR = 0; - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} -void fsubsx(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = rPS1(_inst.FD) = static_cast(rPS0(_inst.FA) - rPS0(_inst.FB)); -// FPSCR.FI = 0; -// FPSCR.FR = 0; - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} - - -void frsqrtex(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = 1.0f / (sqrt(rPS0(_inst.FB))); -// FPSCR.FI = 0; -// FPSCR.FR = 0; - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} - -void fsqrtx(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = sqrt(rPS0(_inst.FB)); -// FPSCR.FI = 0; -// FPSCR.FR = 0; - - if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include + +#ifdef _WIN32 +#define _interlockedbittestandset workaround_ms_header_bug_platform_sdk6_set +#define _interlockedbittestandreset workaround_ms_header_bug_platform_sdk6_reset +#define _interlockedbittestandset64 workaround_ms_header_bug_platform_sdk6_set64 +#define _interlockedbittestandreset64 workaround_ms_header_bug_platform_sdk6_reset64 +#include +#undef _interlockedbittestandset +#undef _interlockedbittestandreset +#undef _interlockedbittestandset64 +#undef _interlockedbittestandreset64 +#else +#include +#endif + +#include "../../Core.h" +#include "Interpreter.h" + +// SUPER MONKEY BALL IS BEING A ROYAL PAIN +// We are missing the caller of 800070ec +// POSSIBLE APPROACHES: +// * Full SW FPU. Urgh. +// * Partial SW FPU, emulate just as much as necessary for monkey ball. Feasible but a lot of work. +// * HLE hacking. Figure out what all the evil functions really do and fake them. DONE (well, works okay-ish) + +// Interesting places in Super Monkey Ball: +// 80036654: fctwixz stuff +// 80007e08: +// -98: Various entry points that loads various odd fp values into f1 +// 800070b0: Estimate inverse square root. +// 800070ec: Examine f1. Reads a value out of locked cache into f2 (fixed address). Some cases causes us to call the above thing. +// If all goes well, jump to 70b0, which estimates the inverse square root. +// Then multiply the loaded variable with the original value of f1. Result should be the square root. (1 / sqrt(x)) * x = x / sqrt(x) = sqrt(x) +// 8000712c: Similar, but does not do the multiply at the end, just an frspx. +// 8000716c: Sort of similar, but has extra junk at the end. +// +// +// 800072a4 - nightmare of nightmares +// Fun stuff used: +// bso+ +// mcrfs (ARGH pulls stuff out of .. FPSCR). it uses this to check the result of frsp mostly (!!!!) +// crclr +// crset +// crxor +// fnabs +// Super Monkey Ball reads FPRF & friends after fmadds, fmuls, frspx +// WHY do the FR & FI flags affect it so much? + +namespace Interpreter +{ + +void UpdateFPSCR(UReg_FPSCR fp); +void UpdateSSEState(); + +void UpdateFPRF(double value) +{ + u64 ivalue = *((u64*)&value); + // 5 bits (C, <, >, =, ?) + // top: class descriptor + FPSCR.FPRF = 4; + // easy cases first + if (ivalue == 0) { + // positive zero + FPSCR.FPRF = 0x2; + } else if (ivalue == 0x8000000000000000ULL) { + // negative zero + FPSCR.FPRF = 0x12; + } else if (ivalue == 0x7FF0000000000000ULL) { + // positive inf + FPSCR.FPRF = 0x5; + } else if (ivalue == 0xFFF0000000000000ULL) { + // negative inf + FPSCR.FPRF = 0x9; + } else { + // OK let's dissect this thing. + int sign = (int)(ivalue >> 63); + int exp = (int)((ivalue >> 52) & 0x7FF); + if (exp >= 1 && exp <= 2046) { + // Nice normalized number. + if (sign) { + FPSCR.FPRF = 0x8; // negative + } else { + FPSCR.FPRF = 0x4; // positive + } + return; + } + u64 mantissa = ivalue & 0x000FFFFFFFFFFFFFULL; + // int mantissa_top = (int)(mantissa >> 51); + if (exp == 0 && mantissa) { + // Denormalized number. + if (sign) { + FPSCR.FPRF = 0x18; + } else { + FPSCR.FPRF = 0x14; + } + } else if (exp == 0x7FF && mantissa /* && mantissa_top*/) { + FPSCR.FPRF = 0x11; // Quiet NAN + return; + } + } +} + + +// extremely rare +void Helper_UpdateCR1(double _fValue) +{ + FPSCR.FPRF = 0; + if (_fValue == 0.0 || _fValue == -0.0) + FPSCR.FPRF |= 2; + if (_fValue > 0.0) + FPSCR.FPRF |= 4; + if (_fValue < 0.0) + FPSCR.FPRF |= 8; + SetCRField(1, (FPSCR.Hex & 0x0000F000) >> 12); + + PanicAlert("CR1"); +} + +bool IsNAN(double _dValue) +{ + return _dValue != _dValue; +} + +void fcmpo(UGeckoInstruction _inst) +{ + double fa = rPS0(_inst.FA); + double fb = rPS0(_inst.FB); + u32 compareResult; + if (IsNAN(fa) || IsNAN(fb)) compareResult = 1; + else if (fa < fb) compareResult = 8; + else if (fa > fb) compareResult = 4; + else compareResult = 2; + + FPSCR.FPRF = compareResult; + SetCRField(_inst.CRFD, compareResult); + +/* missing part + if ((frA) is an SNaN or (frB) is an SNaN ) + then VXSNAN ¬ 1 + if VE = 0 + then VXVC ¬ 1 + else if ((frA) is a QNaN or (frB) is a QNaN ) + then VXVC ¬ 1 */ +} + +void fcmpu(UGeckoInstruction _inst) +{ + double fa = rPS0(_inst.FA); + double fb = rPS0(_inst.FB); + + u32 compareResult; + if (IsNAN(fa) || IsNAN(fb)) compareResult = 1; + else if (fa < fb) compareResult = 8; + else if (fa > fb) compareResult = 4; + else compareResult = 2; + + FPSCR.FPRF = compareResult; + SetCRField(_inst.CRFD, compareResult); + +/* missing part + if ((frA) is an SNaN or (frB) is an SNaN) + then VXSNAN ¬ 1 */ +} + +// Apply current rounding mode +void fctiwx(UGeckoInstruction _inst) +{ + UpdateSSEState(); + const double b = rPS0(_inst.FB); + u32 value; + if (b > (double)0x7fffffff) + { + value = 0x7fffffff; + FPSCR.VXCVI = 1; + } + else if (b < -(double)0x7fffffff) + { + value = 0x80000000; + FPSCR.VXCVI = 1; + } + else + { + value = (u32)(s32)_mm_cvtsd_si32(_mm_set_sd(b)); // obey current rounding mode +// double d_value = (double)value; +// bool inexact = (d_value != b); +// FPSCR.FI = inexact ? 1 : 0; +// FPSCR.XX |= FPSCR.FI; +// FPSCR.FR = fabs(d_value) > fabs(b); + } + + //TODO: FR + //FPRF undefined + + riPS0(_inst.FD) = (u64)value; // zero extend + if (_inst.Rc) + Helper_UpdateCR1(rPS0(_inst.FD)); +} + +/* +In the float -> int direction, floating point input values larger than the largest +representable int result in 0x80000000 (a very negative number) rather than the +largest representable int on PowerPC. */ + +// Always round toward zero +void fctiwzx(UGeckoInstruction _inst) +{ + //UpdateFPSCR(FPSCR); + const double b = rPS0(_inst.FB); + u32 value; + if (b > (double)0x7fffffff) + { + value = 0x7fffffff; + FPSCR.VXCVI = 1; + } + else if (b < -(double)0x7fffffff) + { + value = 0x80000000; + FPSCR.VXCVI = 1; + } + else + { + value = (u32)(s32)_mm_cvttsd_si32(_mm_set_sd(b)); // truncate +// double d_value = (double)value; +// bool inexact = (d_value != b); +// FPSCR.FI = inexact ? 1 : 0; +// FPSCR.XX |= FPSCR.FI; +// FPSCR.FR = 1; //fabs(d_value) > fabs(b); + } + //FPRF undefined + + riPS0(_inst.FD) = (u64)value; + if (_inst.Rc) + Helper_UpdateCR1(rPS0(_inst.FD)); +} + +void fmrx(UGeckoInstruction _inst) +{ + riPS0(_inst.FD) = riPS0(_inst.FB); + // This is a binary instruction. Does not alter FPSCR + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} + +void fabsx(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = fabs(rPS0(_inst.FB)); + // This is a binary instruction. Does not alter FPSCR + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} + +void fnabsx(UGeckoInstruction _inst) +{ + riPS0(_inst.FD) = riPS0(_inst.FB) | (1ULL << 63); + // This is a binary instruction. Does not alter FPSCR + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} + +void fnegx(UGeckoInstruction _inst) +{ + riPS0(_inst.FD) = riPS0(_inst.FB) ^ (1ULL << 63); + // This is a binary instruction. Does not alter FPSCR + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} + +void fselx(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = (rPS0(_inst.FA) >= -0.0) ? rPS0(_inst.FC) : rPS0(_inst.FB); + // This is a binary instruction. Does not alter FPSCR + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} + + +// !!! warning !!! +// PS1 must be set to the value of PS0 or DragonballZ will be f**ked up +// PS1 is said to be undefined +// Super Monkey Ball is using this to do wacky tricks so we need 100% correct emulation. +void frspx(UGeckoInstruction _inst) // round to single +{ + if (true || FPSCR.RN != 0) + { + // Not used in Super Monkey Ball + UpdateSSEState(); + double b = rPS0(_inst.FB); + double rounded = (double)(float)b; + FPSCR.FI = b != rounded; // changing both of these affect Super Monkey Ball behaviour greatly. + FPSCR.FR = 1; // WHY? fabs(rounded) > fabs(b); + rPS0(_inst.FD) = rPS1(_inst.FD) = rounded; + return; + // PanicAlert("frspx: FPSCR.RN=%i", FPSCR.RN); + } + + // OK, let's try it in 100% software! Not yet working right. + union { + double d; + u64 i; + } in, out; + in.d = rPS0(_inst.FB); + out = in; + int sign = (int)(in.i >> 63); + int exp = (int)((in.i >> 52) & 0x7FF); + u64 mantissa = in.i & 0x000FFFFFFFFFFFFFULL; + u64 mantissa_single = mantissa & 0x000FFFFFE0000000ULL; + u64 leftover_single = mantissa & 0x000000001FFFFFFFULL; + + // OK. First make sure that we have a "normal" number. + if (exp >= 1 && exp <= 2046) { + // OK. Check for overflow. TODO + + FPSCR.FI = leftover_single != 0; // Inexact + if (leftover_single >= 0x10000000ULL) { + //PanicAlert("rounding up"); + FPSCR.FR = 1; + mantissa_single += 0x20000000; + if (mantissa_single & 0x0010000000000000ULL) { + // PanicAlert("renormalizing"); + mantissa_single >>= 1; + exp += 1; + // if (exp > 2046) { OVERFLOW } + } + } + out.i = ((u64)sign << 63) | ((u64)exp << 52) | mantissa_single; + } else { + if (!exp && !mantissa) { + // Positive or negative Zero. All is well. + FPSCR.FI = 0; + FPSCR.FR = 0; + } else if (exp == 0 && mantissa) { + // Denormalized number. + PanicAlert("denorm"); + } else if (exp == 2047 && !mantissa) { + // Infinite. + //PanicAlert("infinite"); + FPSCR.FI = 1; + FPSCR.FR = 1; +// FPSCR.OX = 1; + } else { + //PanicAlert("NAN %08x %08x", in.i >> 32, in.i); + } + } + UpdateFPRF(out.d); + FPSCR.FR = 1; // SUPER MONKEY BALL HACK + rPS0(_inst.FD) = rPS1(_inst.FD) = out.d; + + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} + + +void fmulx(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = rPS0(_inst.FA) * rPS0(_inst.FC); + FPSCR.FI = 0; + FPSCR.FR = 1; + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} +void fmulsx(UGeckoInstruction _inst) +{ + double d_value = rPS0(_inst.FA) * rPS0(_inst.FC); + rPS0(_inst.FD) = rPS1(_inst.FD) = static_cast(d_value); + FPSCR.FI = d_value != rPS0(_inst.FD); + FPSCR.FR = rand()&1; + UpdateFPRF(rPS0(_inst.FD)); + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} + + +void fmaddx(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = (rPS0(_inst.FA) * rPS0(_inst.FC)) + rPS0(_inst.FB); + FPSCR.FI = 0; + FPSCR.FR = 0; + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} +void fmaddsx(UGeckoInstruction _inst) +{ + double d_value = (rPS0(_inst.FA) * rPS0(_inst.FC)) + rPS0(_inst.FB); + rPS0(_inst.FD) = rPS1(_inst.FD) = + static_cast(d_value); + FPSCR.FI = d_value != rPS0(_inst.FD); + FPSCR.FR = 0; + UpdateFPRF(rPS0(_inst.FD)); + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} + + +void faddx(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = rPS0(_inst.FA) + rPS0(_inst.FB); +// FPSCR.FI = 0; +// FPSCR.FR = 1; + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} +void faddsx(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = rPS1(_inst.FD) = static_cast(rPS0(_inst.FA) + rPS0(_inst.FB)); +// FPSCR.FI = 0; +// FPSCR.FR = 1; +// FPSCR.Hex = (rand() ^ (rand() << 8) ^ (rand() << 16)) & ~(0x000000F8); + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} + + +void fdivx(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = rPS0(_inst.FA) / rPS0(_inst.FB); +// FPSCR.FI = 0; +// FPSCR.FR = 1; + if (fabs(rPS0(_inst.FB)) == 0.0) { + FPSCR.ZX = 1; + } + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} +void fdivsx(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = rPS1(_inst.FD) = static_cast(rPS0(_inst.FA) / rPS0(_inst.FB)); +// FPSCR.FI = 0; +// FPSCR.FR = 1; + if (fabs(rPS0(_inst.FB)) == 0.0) { + FPSCR.ZX = 1; + } + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} +void fresx(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = rPS1(_inst.FD) = static_cast(1.0f / rPS0(_inst.FB)); +// FPSCR.FI = 0; +// FPSCR.FR = 1; + if (fabs(rPS0(_inst.FB)) == 0.0) { + FPSCR.ZX = 1; + } + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} + + +void fmsubx(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = (rPS0(_inst.FA) * rPS0(_inst.FC)) - rPS0(_inst.FB); +// FPSCR.FI = 0; +// FPSCR.FR = 0; + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} + +void fmsubsx(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = rPS1(_inst.FD) = + static_cast((rPS0(_inst.FA) * rPS0(_inst.FC)) - rPS0(_inst.FB)); +// FPSCR.FI = 0; +// FPSCR.FR = 0; + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} + + +void fnmaddx(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = -((rPS0(_inst.FA) * rPS0(_inst.FC)) + rPS0(_inst.FB)); +// FPSCR.FI = 0; +// FPSCR.FR = 0; + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} +void fnmaddsx(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = rPS1(_inst.FD) = + static_cast(-((rPS0(_inst.FA) * rPS0(_inst.FC)) + rPS0(_inst.FB))); +// FPSCR.FI = 0; +// FPSCR.FR = 0; + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} + + +void fnmsubx(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = -((rPS0(_inst.FA) * rPS0(_inst.FC)) - rPS0(_inst.FB)); +// FPSCR.FI = 0; +// FPSCR.FR = 0; + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} +void fnmsubsx(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = rPS1(_inst.FD) = + static_cast(-((rPS0(_inst.FA) * rPS0(_inst.FC)) - rPS0(_inst.FB))); +// FPSCR.FI = 0; +// FPSCR.FR = 0; + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} + + +void fsubx(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = rPS0(_inst.FA) - rPS0(_inst.FB); +// FPSCR.FI = 0; +// FPSCR.FR = 0; + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} +void fsubsx(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = rPS1(_inst.FD) = static_cast(rPS0(_inst.FA) - rPS0(_inst.FB)); +// FPSCR.FI = 0; +// FPSCR.FR = 0; + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} + + +void frsqrtex(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = 1.0f / (sqrt(rPS0(_inst.FB))); +// FPSCR.FI = 0; +// FPSCR.FR = 0; + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} + +void fsqrtx(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = sqrt(rPS0(_inst.FB)); +// FPSCR.FI = 0; +// FPSCR.FR = 0; + + if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD)); +} + +} // namespace diff --git a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_Integer.cpp b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_Integer.cpp index 65aa4da7c0..6e30a0d5e9 100644 --- a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_Integer.cpp +++ b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_Integer.cpp @@ -1,585 +1,585 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Interpreter.h" -#include "../../Core.h" - -namespace Interpreter -{ - -void Helper_UpdateCR0(u32 _uValue) -{ - u32 Flags = 0; - int sValue = (int)_uValue; - if (sValue > 0) - Flags |= 0x40000000; - else if (sValue < 0) - Flags |= 0x80000000; - else - Flags |= 0x20000000; - Flags |= PowerPC::ppcState.spr[SPR_XER*4] & 0x10000000; - PowerPC::ppcState.cr = (PowerPC::ppcState.cr & 0xFFFFFFF) | Flags; -} - -void Helper_UpdateCRx(int _x, u32 _uValue) -{ - int shiftamount = _x*4; - int crmask = 0xFFFFFFFF ^ (0xF0000000 >> shiftamount); - - u32 Flags = 0; - int sValue = (int)_uValue; - if (sValue > 0) - Flags |= 0x40000000; - else if (sValue < 0) - Flags |= 0x80000000; - else - Flags |= 0x20000000; - Flags |= PowerPC::ppcState.spr[SPR_XER*4] & 0x10000000; - PowerPC::ppcState.cr = (PowerPC::ppcState.cr & crmask) | (Flags >> shiftamount); -} - -u32 Helper_Carry(u32 _uValue1, u32 _uValue2) -{ - return _uValue2 > (~_uValue1); -} - -u32 Helper_Mask(int mb, int me) -{ - //first make 001111111111111 part - u32 begin = 0xFFFFFFFF >> mb; - //then make 000000000001111 part, which is used to flip the bits of the first one - u32 end = me < 31 ? (0xFFFFFFFF >> (me + 1)) : 0; - //do the bitflip - u32 mask = begin ^ end; - //and invert if backwards - if (me < mb) - return ~mask; - else - return mask; -} - -void addi(UGeckoInstruction _inst) -{ - if (_inst.RA) - m_GPR[_inst.RD] = m_GPR[_inst.RA] + _inst.SIMM_16; - else - m_GPR[_inst.RD] = _inst.SIMM_16; -} - -void addic(UGeckoInstruction _inst) -{ - u32 a = m_GPR[_inst.RA]; - u32 imm = (u32)(s32)_inst.SIMM_16; - // TODO(ector): verify this thing - m_GPR[_inst.RD] = a + imm; - SetCarry(Helper_Carry(a, imm)); -} - -void addic_rc(UGeckoInstruction _inst) -{ - addic(_inst); - Helper_UpdateCR0(m_GPR[_inst.RD]); -} - -void addis(UGeckoInstruction _inst) -{ - if (_inst.RA) - m_GPR[_inst.RD] = m_GPR[_inst.RA] + (_inst.SIMM_16 << 16); - else - m_GPR[_inst.RD] = (_inst.SIMM_16 << 16); -} - -void andi_rc(UGeckoInstruction _inst) -{ - m_GPR[_inst.RA] = m_GPR[_inst.RS] & _inst.UIMM; - Helper_UpdateCR0(m_GPR[_inst.RA]); -} - -void andis_rc(UGeckoInstruction _inst) -{ - m_GPR[_inst.RA] = m_GPR[_inst.RS] & ((u32)_inst.UIMM<<16); - Helper_UpdateCR0(m_GPR[_inst.RA]); -} - -void cmpi(UGeckoInstruction _inst) -{ - Helper_UpdateCRx(_inst.CRFD, m_GPR[_inst.RA]-_inst.SIMM_16); -} - -void cmpli(UGeckoInstruction _inst) -{ - u32 a = m_GPR[_inst.RA]; - u32 b = _inst.UIMM; - int f; - if (a < b) f = 0x8; - else if (a > b) f = 0x4; - else f = 0x2; //equals - if (XER.SO) f = 0x1; - SetCRField(_inst.CRFD, f); -} - -void mulli(UGeckoInstruction _inst) -{ - m_GPR[_inst.RD] = (s32)m_GPR[_inst.RA] * _inst.SIMM_16; -} - -void ori(UGeckoInstruction _inst) -{ - m_GPR[_inst.RA] = m_GPR[_inst.RS] | _inst.UIMM; -} - -void oris(UGeckoInstruction _inst) -{ - m_GPR[_inst.RA] = m_GPR[_inst.RS] | (_inst.UIMM << 16); -} - -void subfic(UGeckoInstruction _inst) -{ -/* u32 rra = ~m_GPR[_inst.RA]; - s32 immediate = (s16)_inst.SIMM_16 + 1; - - -// #define CALC_XER_CA(X,Y) (((X) + (Y) < X) ? SET_XER_CA : CLEAR_XER_CA) - if ((rra + immediate) < rra) - XER.CA = 1; - else - XER.CA = 0; - - m_GPR[_inst.RD] = rra - immediate; -*/ - - s32 immediate = _inst.SIMM_16; - m_GPR[_inst.RD] = immediate - (signed)m_GPR[_inst.RA]; - SetCarry((m_GPR[_inst.RA] == 0) || (Helper_Carry(0-m_GPR[_inst.RA], immediate))); -} - -void twi(UGeckoInstruction _inst) -{ - bool bFirst = true; - if (bFirst) - PanicAlert("twi - Instruction unimplemented"); - - bFirst = false; -} - -void xori(UGeckoInstruction _inst) -{ - m_GPR[_inst.RA] = m_GPR[_inst.RS] ^ _inst.UIMM; -} - -void xoris(UGeckoInstruction _inst) -{ - m_GPR[_inst.RA] = m_GPR[_inst.RS] ^ (_inst.UIMM << 16); -} - -void rlwimix(UGeckoInstruction _inst) -{ - u32 mask = Helper_Mask(_inst.MB,_inst.ME); - m_GPR[_inst.RA] = (m_GPR[_inst.RA] & ~mask) | (_rotl(m_GPR[_inst.RS],_inst.SH) & mask); - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); -} - -void rlwinmx(UGeckoInstruction _inst) -{ - u32 mask = Helper_Mask(_inst.MB,_inst.ME); - m_GPR[_inst.RA] = _rotl(m_GPR[_inst.RS],_inst.SH) & mask; - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); -} - -void rlwnmx(UGeckoInstruction _inst) -{ - u32 mask = Helper_Mask(_inst.MB,_inst.ME); - m_GPR[_inst.RA] = _rotl(m_GPR[_inst.RS], m_GPR[_inst.RB] & 0x1F) & mask; - - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); -} - -void andx(UGeckoInstruction _inst) -{ - m_GPR[_inst.RA] = m_GPR[_inst.RS] & m_GPR[_inst.RB]; - - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); -} - -void andcx(UGeckoInstruction _inst) -{ - m_GPR[_inst.RA] = m_GPR[_inst.RS] & ~m_GPR[_inst.RB]; - - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); -} - -void cmp(UGeckoInstruction _inst) -{ - s32 a = (s32)m_GPR[_inst.RA]; - s32 b = (s32)m_GPR[_inst.RB]; - int fTemp = 0x8; // a < b - - // if (a < b) fTemp = 0x8; else - if (a > b) fTemp = 0x4; - else if (a == b) fTemp = 0x2; - if (XER.SO) PanicAlert("cmp getting overflow flag"); // fTemp |= 0x1 - SetCRField(_inst.CRFD, fTemp); -} - -void cmpl(UGeckoInstruction _inst) -{ - u32 a = m_GPR[_inst.RA]; - u32 b = m_GPR[_inst.RB]; - u32 fTemp = 0x8; // a < b - - // if (a < b) fTemp = 0x8;else - if (a > b) fTemp = 0x4; - else if (a == b) fTemp = 0x2; - if (XER.SO) PanicAlert("cmpl getting overflow flag"); // fTemp |= 0x1; - SetCRField(_inst.CRFD, fTemp); -} - -void cntlzwx(UGeckoInstruction _inst) -{ - u32 val = m_GPR[_inst.RS]; - u32 mask = 0x80000000; - int i = 0; - for (; i < 32; i++, mask >>= 1) - if (val & mask) - break; - m_GPR[_inst.RA] = i; - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); -} - -void eqvx(UGeckoInstruction _inst) -{ - m_GPR[_inst.RA] = ~(m_GPR[_inst.RS] ^ m_GPR[_inst.RB]); - - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); -} - -void extsbx(UGeckoInstruction _inst) -{ - m_GPR[_inst.RA] = (u32)(s32)(s8)m_GPR[_inst.RS]; - - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); -} - -void extshx(UGeckoInstruction _inst) -{ - m_GPR[_inst.RA] = (u32)(s32)(s16)m_GPR[_inst.RS]; - - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); -} - -void nandx(UGeckoInstruction _inst) -{ - m_GPR[_inst.RA] = ~(m_GPR[_inst.RS] & m_GPR[_inst.RB]); - - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); -} - -void norx(UGeckoInstruction _inst) -{ - m_GPR[_inst.RA] = ~(m_GPR[_inst.RS] | m_GPR[_inst.RB]); - - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); -} - -void orx(UGeckoInstruction _inst) -{ - m_GPR[_inst.RA] = m_GPR[_inst.RS] | m_GPR[_inst.RB]; - - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); -} - -void orcx(UGeckoInstruction _inst) -{ - m_GPR[_inst.RA] = m_GPR[_inst.RS] | (~m_GPR[_inst.RB]); - - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); -} - -void slwx(UGeckoInstruction _inst) -{ - u32 amount = m_GPR[_inst.RB]; - m_GPR[_inst.RA] = (amount & 0x20) ? 0 : m_GPR[_inst.RS] << amount; - - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); -} - -void srawx(UGeckoInstruction _inst) -{ - int rb = m_GPR[_inst.RB]; - if (rb & 0x20) - { - if (m_GPR[_inst.RS] & 0x80000000) - { - m_GPR[_inst.RA] = 0xFFFFFFFF; - SetCarry(1); - } - else - { - m_GPR[_inst.RA] = 0x00000000; - SetCarry(0); - } - } - else - { - int amount = rb & 0x1f; - if (amount == 0) - { - m_GPR[_inst.RA] = m_GPR[_inst.RS]; - SetCarry(0); - } - else - { - m_GPR[_inst.RA] = (u32)((s32)m_GPR[_inst.RS] >> amount); - if (m_GPR[_inst.RS] & 0x80000000) - SetCarry(1); - else - SetCarry(0); - } - } - - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); -} - -void srawix(UGeckoInstruction _inst) -{ - int amount = _inst.SH; - - if (amount != 0) - { - s32 rrs = m_GPR[_inst.RS]; - m_GPR[_inst.RA] = rrs >> amount; - - if ((rrs < 0) && (rrs << (32 - amount))) - SetCarry(1); - else - SetCarry(0); - } - else - { - SetCarry(0); - m_GPR[_inst.RA] = m_GPR[_inst.RS]; - } - - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); -} - -void srwx(UGeckoInstruction _inst) -{ - u32 amount = m_GPR[_inst.RB]; - m_GPR[_inst.RA] = (amount & 0x20) ? 0 : (m_GPR[_inst.RS] >> (amount & 0x1f)); - - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); -} - -void tw(UGeckoInstruction _inst) -{ - static bool bFirst = true; - if (bFirst) - PanicAlert("tw - Instruction unimplemented"); - bFirst = false; -} - -void xorx(UGeckoInstruction _inst) -{ - m_GPR[_inst.RA] = m_GPR[_inst.RS] ^ m_GPR[_inst.RB]; - - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); -} - -void addx(UGeckoInstruction _inst) -{ - m_GPR[_inst.RD] = m_GPR[_inst.RA] + m_GPR[_inst.RB]; - - if (_inst.OE) PanicAlert("OE: addx"); - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); -} - -void addcx(UGeckoInstruction _inst) -{ - u32 a = m_GPR[_inst.RA]; - u32 b = m_GPR[_inst.RB]; - m_GPR[_inst.RD] = a + b; - SetCarry(Helper_Carry(a,b)); - - if (_inst.OE) PanicAlert("OE: addcx"); - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); -} - -void addex(UGeckoInstruction _inst) -{ - int carry = GetCarry(); - int a = m_GPR[_inst.RA]; - int b = m_GPR[_inst.RB]; - m_GPR[_inst.RD] = a + b + carry; - SetCarry(Helper_Carry(a, b) || (carry != 0 && Helper_Carry(a + b, carry))); - - if (_inst.OE) PanicAlert("OE: addex"); - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); -} - -void addmex(UGeckoInstruction _inst) -{ - int carry = GetCarry(); - int a = m_GPR[_inst.RA]; - m_GPR[_inst.RD] = a + carry - 1; - SetCarry(Helper_Carry(a, carry - 1)); - - if (_inst.OE) PanicAlert("OE: addmex"); - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); -} - -void addzex(UGeckoInstruction _inst) -{ - int carry = GetCarry(); - int a = m_GPR[_inst.RA]; - m_GPR[_inst.RD] = a + carry; - SetCarry(Helper_Carry(a, carry)); - - if (_inst.OE) PanicAlert("OE: addzex"); - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); -} - -void divwx(UGeckoInstruction _inst) -{ - s32 a = m_GPR[_inst.RA]; - s32 b = m_GPR[_inst.RB]; - if (b == 0 || ((u32)a == 0x80000000 && b == -1)) - { - if (_inst.OE) - PanicAlert("OE: divwx"); - //else PanicAlert("Div by zero", "divwux"); - } - else - m_GPR[_inst.RD] = (u32)(a / b); - - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); -} - - -void divwux(UGeckoInstruction _inst) -{ - u32 a = m_GPR[_inst.RA]; - u32 b = m_GPR[_inst.RB]; - - if (b == 0 || (a == 0x80000000 && b == 0xFFFFFFFF)) - { - if (_inst.OE) - PanicAlert("OE: divwux"); - //else PanicAlert("Div by zero", "divwux"); - } - else - { - m_GPR[_inst.RD] = a / b; - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); - } -} - -void mulhwx(UGeckoInstruction _inst) -{ - u32 a = m_GPR[_inst.RA]; - u32 b = m_GPR[_inst.RB]; - u32 d = (u32)((u64)(((s64)(s32)a * (s64)(s32)b) ) >> 32); // This can be done better. Not in plain C/C++ though. - m_GPR[_inst.RD] = d; - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); -} - -void mulhwux(UGeckoInstruction _inst) -{ - u32 a = m_GPR[_inst.RA]; - u32 b = m_GPR[_inst.RB]; - u32 d = (u32)(((u64)a * (u64)b) >> 32); - m_GPR[_inst.RD] = d; - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); -} - -void mullwx(UGeckoInstruction _inst) -{ - u32 a = m_GPR[_inst.RA]; - u32 b = m_GPR[_inst.RB]; - u32 d = (u32)((s32)a * (s32)b); - m_GPR[_inst.RD] = d; - - if (_inst.OE) PanicAlert("OE: mullwx"); - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); -} - -void negx(UGeckoInstruction _inst) -{ - m_GPR[_inst.RD] = (~m_GPR[_inst.RA]) + 1; - if (m_GPR[_inst.RD] == 0x80000000) - { - if (_inst.OE) PanicAlert("OE: negx"); - } - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); -} - -void subfx(UGeckoInstruction _inst) -{ - m_GPR[_inst.RD] = m_GPR[_inst.RB] - m_GPR[_inst.RA]; - - if (_inst.OE) PanicAlert("OE: subfx"); - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); -} - -void subfcx(UGeckoInstruction _inst) -{ - u32 a = m_GPR[_inst.RA]; - u32 b = m_GPR[_inst.RB]; - m_GPR[_inst.RD] = b - a; - SetCarry(a == 0 || Helper_Carry(b, 0-a)); - - if (_inst.OE) PanicAlert("OE: subfcx"); - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); -} - -void subfex(UGeckoInstruction _inst) -{ - u32 a = m_GPR[_inst.RA]; - u32 b = m_GPR[_inst.RB]; - int carry = GetCarry(); - m_GPR[_inst.RD] = (~a) + b + carry; - SetCarry(Helper_Carry(~a, b) || Helper_Carry((~a) + b, carry)); - - if (_inst.OE) PanicAlert("OE: subfcx"); - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); -} - -// sub from minus one -void subfmex(UGeckoInstruction _inst) -{ - u32 a = m_GPR[_inst.RA]; - int carry = GetCarry(); - m_GPR[_inst.RD] = (~a) + carry - 1; - SetCarry(Helper_Carry(~a, carry - 1)); - - if (_inst.OE) PanicAlert("OE: subfmex"); - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); -} - -// sub from zero -void subfzex(UGeckoInstruction _inst) -{ - u32 a = m_GPR[_inst.RA]; - int carry = GetCarry(); - m_GPR[_inst.RD] = (~a) + carry; - SetCarry(Helper_Carry(~a, carry)); - - if (_inst.OE) PanicAlert("OE: subfzex"); - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Interpreter.h" +#include "../../Core.h" + +namespace Interpreter +{ + +void Helper_UpdateCR0(u32 _uValue) +{ + u32 Flags = 0; + int sValue = (int)_uValue; + if (sValue > 0) + Flags |= 0x40000000; + else if (sValue < 0) + Flags |= 0x80000000; + else + Flags |= 0x20000000; + Flags |= PowerPC::ppcState.spr[SPR_XER*4] & 0x10000000; + PowerPC::ppcState.cr = (PowerPC::ppcState.cr & 0xFFFFFFF) | Flags; +} + +void Helper_UpdateCRx(int _x, u32 _uValue) +{ + int shiftamount = _x*4; + int crmask = 0xFFFFFFFF ^ (0xF0000000 >> shiftamount); + + u32 Flags = 0; + int sValue = (int)_uValue; + if (sValue > 0) + Flags |= 0x40000000; + else if (sValue < 0) + Flags |= 0x80000000; + else + Flags |= 0x20000000; + Flags |= PowerPC::ppcState.spr[SPR_XER*4] & 0x10000000; + PowerPC::ppcState.cr = (PowerPC::ppcState.cr & crmask) | (Flags >> shiftamount); +} + +u32 Helper_Carry(u32 _uValue1, u32 _uValue2) +{ + return _uValue2 > (~_uValue1); +} + +u32 Helper_Mask(int mb, int me) +{ + //first make 001111111111111 part + u32 begin = 0xFFFFFFFF >> mb; + //then make 000000000001111 part, which is used to flip the bits of the first one + u32 end = me < 31 ? (0xFFFFFFFF >> (me + 1)) : 0; + //do the bitflip + u32 mask = begin ^ end; + //and invert if backwards + if (me < mb) + return ~mask; + else + return mask; +} + +void addi(UGeckoInstruction _inst) +{ + if (_inst.RA) + m_GPR[_inst.RD] = m_GPR[_inst.RA] + _inst.SIMM_16; + else + m_GPR[_inst.RD] = _inst.SIMM_16; +} + +void addic(UGeckoInstruction _inst) +{ + u32 a = m_GPR[_inst.RA]; + u32 imm = (u32)(s32)_inst.SIMM_16; + // TODO(ector): verify this thing + m_GPR[_inst.RD] = a + imm; + SetCarry(Helper_Carry(a, imm)); +} + +void addic_rc(UGeckoInstruction _inst) +{ + addic(_inst); + Helper_UpdateCR0(m_GPR[_inst.RD]); +} + +void addis(UGeckoInstruction _inst) +{ + if (_inst.RA) + m_GPR[_inst.RD] = m_GPR[_inst.RA] + (_inst.SIMM_16 << 16); + else + m_GPR[_inst.RD] = (_inst.SIMM_16 << 16); +} + +void andi_rc(UGeckoInstruction _inst) +{ + m_GPR[_inst.RA] = m_GPR[_inst.RS] & _inst.UIMM; + Helper_UpdateCR0(m_GPR[_inst.RA]); +} + +void andis_rc(UGeckoInstruction _inst) +{ + m_GPR[_inst.RA] = m_GPR[_inst.RS] & ((u32)_inst.UIMM<<16); + Helper_UpdateCR0(m_GPR[_inst.RA]); +} + +void cmpi(UGeckoInstruction _inst) +{ + Helper_UpdateCRx(_inst.CRFD, m_GPR[_inst.RA]-_inst.SIMM_16); +} + +void cmpli(UGeckoInstruction _inst) +{ + u32 a = m_GPR[_inst.RA]; + u32 b = _inst.UIMM; + int f; + if (a < b) f = 0x8; + else if (a > b) f = 0x4; + else f = 0x2; //equals + if (XER.SO) f = 0x1; + SetCRField(_inst.CRFD, f); +} + +void mulli(UGeckoInstruction _inst) +{ + m_GPR[_inst.RD] = (s32)m_GPR[_inst.RA] * _inst.SIMM_16; +} + +void ori(UGeckoInstruction _inst) +{ + m_GPR[_inst.RA] = m_GPR[_inst.RS] | _inst.UIMM; +} + +void oris(UGeckoInstruction _inst) +{ + m_GPR[_inst.RA] = m_GPR[_inst.RS] | (_inst.UIMM << 16); +} + +void subfic(UGeckoInstruction _inst) +{ +/* u32 rra = ~m_GPR[_inst.RA]; + s32 immediate = (s16)_inst.SIMM_16 + 1; + + +// #define CALC_XER_CA(X,Y) (((X) + (Y) < X) ? SET_XER_CA : CLEAR_XER_CA) + if ((rra + immediate) < rra) + XER.CA = 1; + else + XER.CA = 0; + + m_GPR[_inst.RD] = rra - immediate; +*/ + + s32 immediate = _inst.SIMM_16; + m_GPR[_inst.RD] = immediate - (signed)m_GPR[_inst.RA]; + SetCarry((m_GPR[_inst.RA] == 0) || (Helper_Carry(0-m_GPR[_inst.RA], immediate))); +} + +void twi(UGeckoInstruction _inst) +{ + bool bFirst = true; + if (bFirst) + PanicAlert("twi - Instruction unimplemented"); + + bFirst = false; +} + +void xori(UGeckoInstruction _inst) +{ + m_GPR[_inst.RA] = m_GPR[_inst.RS] ^ _inst.UIMM; +} + +void xoris(UGeckoInstruction _inst) +{ + m_GPR[_inst.RA] = m_GPR[_inst.RS] ^ (_inst.UIMM << 16); +} + +void rlwimix(UGeckoInstruction _inst) +{ + u32 mask = Helper_Mask(_inst.MB,_inst.ME); + m_GPR[_inst.RA] = (m_GPR[_inst.RA] & ~mask) | (_rotl(m_GPR[_inst.RS],_inst.SH) & mask); + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); +} + +void rlwinmx(UGeckoInstruction _inst) +{ + u32 mask = Helper_Mask(_inst.MB,_inst.ME); + m_GPR[_inst.RA] = _rotl(m_GPR[_inst.RS],_inst.SH) & mask; + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); +} + +void rlwnmx(UGeckoInstruction _inst) +{ + u32 mask = Helper_Mask(_inst.MB,_inst.ME); + m_GPR[_inst.RA] = _rotl(m_GPR[_inst.RS], m_GPR[_inst.RB] & 0x1F) & mask; + + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); +} + +void andx(UGeckoInstruction _inst) +{ + m_GPR[_inst.RA] = m_GPR[_inst.RS] & m_GPR[_inst.RB]; + + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); +} + +void andcx(UGeckoInstruction _inst) +{ + m_GPR[_inst.RA] = m_GPR[_inst.RS] & ~m_GPR[_inst.RB]; + + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); +} + +void cmp(UGeckoInstruction _inst) +{ + s32 a = (s32)m_GPR[_inst.RA]; + s32 b = (s32)m_GPR[_inst.RB]; + int fTemp = 0x8; // a < b + + // if (a < b) fTemp = 0x8; else + if (a > b) fTemp = 0x4; + else if (a == b) fTemp = 0x2; + if (XER.SO) PanicAlert("cmp getting overflow flag"); // fTemp |= 0x1 + SetCRField(_inst.CRFD, fTemp); +} + +void cmpl(UGeckoInstruction _inst) +{ + u32 a = m_GPR[_inst.RA]; + u32 b = m_GPR[_inst.RB]; + u32 fTemp = 0x8; // a < b + + // if (a < b) fTemp = 0x8;else + if (a > b) fTemp = 0x4; + else if (a == b) fTemp = 0x2; + if (XER.SO) PanicAlert("cmpl getting overflow flag"); // fTemp |= 0x1; + SetCRField(_inst.CRFD, fTemp); +} + +void cntlzwx(UGeckoInstruction _inst) +{ + u32 val = m_GPR[_inst.RS]; + u32 mask = 0x80000000; + int i = 0; + for (; i < 32; i++, mask >>= 1) + if (val & mask) + break; + m_GPR[_inst.RA] = i; + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); +} + +void eqvx(UGeckoInstruction _inst) +{ + m_GPR[_inst.RA] = ~(m_GPR[_inst.RS] ^ m_GPR[_inst.RB]); + + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); +} + +void extsbx(UGeckoInstruction _inst) +{ + m_GPR[_inst.RA] = (u32)(s32)(s8)m_GPR[_inst.RS]; + + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); +} + +void extshx(UGeckoInstruction _inst) +{ + m_GPR[_inst.RA] = (u32)(s32)(s16)m_GPR[_inst.RS]; + + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); +} + +void nandx(UGeckoInstruction _inst) +{ + m_GPR[_inst.RA] = ~(m_GPR[_inst.RS] & m_GPR[_inst.RB]); + + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); +} + +void norx(UGeckoInstruction _inst) +{ + m_GPR[_inst.RA] = ~(m_GPR[_inst.RS] | m_GPR[_inst.RB]); + + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); +} + +void orx(UGeckoInstruction _inst) +{ + m_GPR[_inst.RA] = m_GPR[_inst.RS] | m_GPR[_inst.RB]; + + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); +} + +void orcx(UGeckoInstruction _inst) +{ + m_GPR[_inst.RA] = m_GPR[_inst.RS] | (~m_GPR[_inst.RB]); + + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); +} + +void slwx(UGeckoInstruction _inst) +{ + u32 amount = m_GPR[_inst.RB]; + m_GPR[_inst.RA] = (amount & 0x20) ? 0 : m_GPR[_inst.RS] << amount; + + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); +} + +void srawx(UGeckoInstruction _inst) +{ + int rb = m_GPR[_inst.RB]; + if (rb & 0x20) + { + if (m_GPR[_inst.RS] & 0x80000000) + { + m_GPR[_inst.RA] = 0xFFFFFFFF; + SetCarry(1); + } + else + { + m_GPR[_inst.RA] = 0x00000000; + SetCarry(0); + } + } + else + { + int amount = rb & 0x1f; + if (amount == 0) + { + m_GPR[_inst.RA] = m_GPR[_inst.RS]; + SetCarry(0); + } + else + { + m_GPR[_inst.RA] = (u32)((s32)m_GPR[_inst.RS] >> amount); + if (m_GPR[_inst.RS] & 0x80000000) + SetCarry(1); + else + SetCarry(0); + } + } + + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); +} + +void srawix(UGeckoInstruction _inst) +{ + int amount = _inst.SH; + + if (amount != 0) + { + s32 rrs = m_GPR[_inst.RS]; + m_GPR[_inst.RA] = rrs >> amount; + + if ((rrs < 0) && (rrs << (32 - amount))) + SetCarry(1); + else + SetCarry(0); + } + else + { + SetCarry(0); + m_GPR[_inst.RA] = m_GPR[_inst.RS]; + } + + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); +} + +void srwx(UGeckoInstruction _inst) +{ + u32 amount = m_GPR[_inst.RB]; + m_GPR[_inst.RA] = (amount & 0x20) ? 0 : (m_GPR[_inst.RS] >> (amount & 0x1f)); + + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); +} + +void tw(UGeckoInstruction _inst) +{ + static bool bFirst = true; + if (bFirst) + PanicAlert("tw - Instruction unimplemented"); + bFirst = false; +} + +void xorx(UGeckoInstruction _inst) +{ + m_GPR[_inst.RA] = m_GPR[_inst.RS] ^ m_GPR[_inst.RB]; + + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RA]); +} + +void addx(UGeckoInstruction _inst) +{ + m_GPR[_inst.RD] = m_GPR[_inst.RA] + m_GPR[_inst.RB]; + + if (_inst.OE) PanicAlert("OE: addx"); + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); +} + +void addcx(UGeckoInstruction _inst) +{ + u32 a = m_GPR[_inst.RA]; + u32 b = m_GPR[_inst.RB]; + m_GPR[_inst.RD] = a + b; + SetCarry(Helper_Carry(a,b)); + + if (_inst.OE) PanicAlert("OE: addcx"); + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); +} + +void addex(UGeckoInstruction _inst) +{ + int carry = GetCarry(); + int a = m_GPR[_inst.RA]; + int b = m_GPR[_inst.RB]; + m_GPR[_inst.RD] = a + b + carry; + SetCarry(Helper_Carry(a, b) || (carry != 0 && Helper_Carry(a + b, carry))); + + if (_inst.OE) PanicAlert("OE: addex"); + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); +} + +void addmex(UGeckoInstruction _inst) +{ + int carry = GetCarry(); + int a = m_GPR[_inst.RA]; + m_GPR[_inst.RD] = a + carry - 1; + SetCarry(Helper_Carry(a, carry - 1)); + + if (_inst.OE) PanicAlert("OE: addmex"); + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); +} + +void addzex(UGeckoInstruction _inst) +{ + int carry = GetCarry(); + int a = m_GPR[_inst.RA]; + m_GPR[_inst.RD] = a + carry; + SetCarry(Helper_Carry(a, carry)); + + if (_inst.OE) PanicAlert("OE: addzex"); + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); +} + +void divwx(UGeckoInstruction _inst) +{ + s32 a = m_GPR[_inst.RA]; + s32 b = m_GPR[_inst.RB]; + if (b == 0 || ((u32)a == 0x80000000 && b == -1)) + { + if (_inst.OE) + PanicAlert("OE: divwx"); + //else PanicAlert("Div by zero", "divwux"); + } + else + m_GPR[_inst.RD] = (u32)(a / b); + + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); +} + + +void divwux(UGeckoInstruction _inst) +{ + u32 a = m_GPR[_inst.RA]; + u32 b = m_GPR[_inst.RB]; + + if (b == 0 || (a == 0x80000000 && b == 0xFFFFFFFF)) + { + if (_inst.OE) + PanicAlert("OE: divwux"); + //else PanicAlert("Div by zero", "divwux"); + } + else + { + m_GPR[_inst.RD] = a / b; + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); + } +} + +void mulhwx(UGeckoInstruction _inst) +{ + u32 a = m_GPR[_inst.RA]; + u32 b = m_GPR[_inst.RB]; + u32 d = (u32)((u64)(((s64)(s32)a * (s64)(s32)b) ) >> 32); // This can be done better. Not in plain C/C++ though. + m_GPR[_inst.RD] = d; + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); +} + +void mulhwux(UGeckoInstruction _inst) +{ + u32 a = m_GPR[_inst.RA]; + u32 b = m_GPR[_inst.RB]; + u32 d = (u32)(((u64)a * (u64)b) >> 32); + m_GPR[_inst.RD] = d; + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); +} + +void mullwx(UGeckoInstruction _inst) +{ + u32 a = m_GPR[_inst.RA]; + u32 b = m_GPR[_inst.RB]; + u32 d = (u32)((s32)a * (s32)b); + m_GPR[_inst.RD] = d; + + if (_inst.OE) PanicAlert("OE: mullwx"); + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); +} + +void negx(UGeckoInstruction _inst) +{ + m_GPR[_inst.RD] = (~m_GPR[_inst.RA]) + 1; + if (m_GPR[_inst.RD] == 0x80000000) + { + if (_inst.OE) PanicAlert("OE: negx"); + } + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); +} + +void subfx(UGeckoInstruction _inst) +{ + m_GPR[_inst.RD] = m_GPR[_inst.RB] - m_GPR[_inst.RA]; + + if (_inst.OE) PanicAlert("OE: subfx"); + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); +} + +void subfcx(UGeckoInstruction _inst) +{ + u32 a = m_GPR[_inst.RA]; + u32 b = m_GPR[_inst.RB]; + m_GPR[_inst.RD] = b - a; + SetCarry(a == 0 || Helper_Carry(b, 0-a)); + + if (_inst.OE) PanicAlert("OE: subfcx"); + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); +} + +void subfex(UGeckoInstruction _inst) +{ + u32 a = m_GPR[_inst.RA]; + u32 b = m_GPR[_inst.RB]; + int carry = GetCarry(); + m_GPR[_inst.RD] = (~a) + b + carry; + SetCarry(Helper_Carry(~a, b) || Helper_Carry((~a) + b, carry)); + + if (_inst.OE) PanicAlert("OE: subfcx"); + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); +} + +// sub from minus one +void subfmex(UGeckoInstruction _inst) +{ + u32 a = m_GPR[_inst.RA]; + int carry = GetCarry(); + m_GPR[_inst.RD] = (~a) + carry - 1; + SetCarry(Helper_Carry(~a, carry - 1)); + + if (_inst.OE) PanicAlert("OE: subfmex"); + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); +} + +// sub from zero +void subfzex(UGeckoInstruction _inst) +{ + u32 a = m_GPR[_inst.RA]; + int carry = GetCarry(); + m_GPR[_inst.RD] = (~a) + carry; + SetCarry(Helper_Carry(~a, carry)); + + if (_inst.OE) PanicAlert("OE: subfzex"); + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); +} + +} // namespace diff --git a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_LoadStore.cpp b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_LoadStore.cpp index 9a4fc71026..3eab82e23f 100644 --- a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_LoadStore.cpp +++ b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_LoadStore.cpp @@ -1,656 +1,656 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "../../HW/Memmap.h" -#include "../../HW/CommandProcessor.h" -#include "../../HW/PixelEngine.h" - -#include "Interpreter.h" -#include "../../Core.h" - -#include "../Jit64/Jit.h" -#include "../Jit64/JitCache.h" - -namespace Interpreter -{ - -u32 Helper_Get_EA(const UGeckoInstruction _inst) -{ - return _inst.RA ? (m_GPR[_inst.RA] + _inst.SIMM_16) : _inst.SIMM_16; -} - -u32 Helper_Get_EA_U(const UGeckoInstruction _inst) -{ - return (m_GPR[_inst.RA] + _inst.SIMM_16); -} - -u32 Helper_Get_EA_X(const UGeckoInstruction _inst) -{ - return _inst.RA ? (m_GPR[_inst.RA] + m_GPR[_inst.RB]) : m_GPR[_inst.RB]; -} - -u32 Helper_Get_EA_UX(const UGeckoInstruction _inst) -{ - return (m_GPR[_inst.RA] + m_GPR[_inst.RB]); -} - -void lbz(UGeckoInstruction _inst) -{ - m_GPR[_inst.RD] = (u32)Memory::Read_U8(Helper_Get_EA(_inst)); -} - -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; -} - -void lfd(UGeckoInstruction _inst) -{ - riPS0(_inst.FD) = Memory::Read_U64(Helper_Get_EA(_inst)); -} - -void lfdu(UGeckoInstruction _inst) -{ - u32 uAddress = Helper_Get_EA_U(_inst); - riPS0(_inst.FD) = Memory::Read_U64(uAddress); - 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; -} - -void lfdx(UGeckoInstruction _inst) -{ - riPS0(_inst.FD) = Memory::Read_U64(Helper_Get_EA_X(_inst)); -} - -void lfs(UGeckoInstruction _inst) -{ - u32 uTemp = Memory::Read_U32(Helper_Get_EA(_inst)); - rPS0(_inst.FD) = *(float*)&uTemp; - rPS1(_inst.FD) = rPS0(_inst.FD); -} - -void lfsu(UGeckoInstruction _inst) -{ - u32 uAddress = Helper_Get_EA_U(_inst); - u32 uTemp = Memory::Read_U32(uAddress); - rPS0(_inst.FD) = *(float*)&uTemp; - rPS1(_inst.FD) = rPS0(_inst.FD); - m_GPR[_inst.RA] = uAddress; -} - -void lfsux(UGeckoInstruction _inst) -{ - u32 uAddress = Helper_Get_EA_UX(_inst); - u32 uTemp = Memory::Read_U32(uAddress); - rPS0(_inst.FD) = *(float*)&uTemp; - rPS1(_inst.FD) = rPS0(_inst.FD); - m_GPR[_inst.RA] = uAddress; -} - -void lfsx(UGeckoInstruction _inst) -{ - u32 uTemp = Memory::Read_U32(Helper_Get_EA_X(_inst)); - rPS0(_inst.FD) = *(float*)&uTemp; - rPS1(_inst.FD) = rPS0(_inst.FD); -} - -void lha(UGeckoInstruction _inst) -{ - m_GPR[_inst.RD] = (u32)(s32)(s16)Memory::Read_U16(Helper_Get_EA(_inst)); -} - -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; -} - -void lhz(UGeckoInstruction _inst) -{ - m_GPR[_inst.RD] = (u32)(u16)Memory::Read_U16(Helper_Get_EA(_inst)); -} - -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; -} - -void lmw(UGeckoInstruction _inst) -{ - u32 uAddress = Helper_Get_EA(_inst); - for (int iReg = _inst.RD; iReg <= 31; iReg++, uAddress += 4) - { - u32 TempReg = Memory::Read_U32(uAddress); - if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) - { - PanicAlert("DSI exception in lmv."); - return; - } - - m_GPR[iReg] = TempReg; - } -} - -void stmw(UGeckoInstruction _inst) -{ - u32 uAddress = Helper_Get_EA(_inst); - for (int iReg = _inst.RS; iReg <= 31; iReg++, uAddress+=4) - { - Memory::Write_U32(m_GPR[iReg], uAddress); - if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) - return; - } -} - -void lwz(UGeckoInstruction _inst) -{ - u32 uAddress = Helper_Get_EA(_inst); - m_GPR[_inst.RD] = Memory::Read_U32(uAddress); - - // hack to detect SelectThread loop - // should probably run a pass through memory instead before execution - // but that would be dangerous - - // Enable idle skipping? - /* - if ((_inst.hex & 0xFFFF0000)==0x800D0000 && - Memory::ReadUnchecked_U32(PC+4)==0x28000000 && - Memory::ReadUnchecked_U32(PC+8)==0x4182fff8) - { - if (CommandProcessor::AllowIdleSkipping() && PixelEngine::AllowIdleSkipping()) - { - CoreTiming::Idle(); - } - }*/ -} - -void lwzu(UGeckoInstruction _inst) -{ - u32 uAddress = Helper_Get_EA_U(_inst); - m_GPR[_inst.RD] = Memory::Read_U32(uAddress); - m_GPR[_inst.RA] = uAddress; -} - -void stb(UGeckoInstruction _inst) -{ - Memory::Write_U8((u8)m_GPR[_inst.RS], Helper_Get_EA(_inst)); -} - -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; -} - -void stfd(UGeckoInstruction _inst) -{ - Memory::Write_U64(riPS0(_inst.FS), Helper_Get_EA(_inst)); -} - -void stfdu(UGeckoInstruction _inst) -{ - u32 uAddress = Helper_Get_EA_U(_inst); - Memory::Write_U64(riPS0(_inst.FS), uAddress); - m_GPR[_inst.RA] = uAddress; -} - -void stfs(UGeckoInstruction _inst) -{ - float fTemp = (float)rPS0(_inst.FS); - Memory::Write_U32(*(u32*)&fTemp, Helper_Get_EA(_inst)); -} - -void stfsu(UGeckoInstruction _inst) -{ - float fTemp = (float)rPS0(_inst.FS); - u32 uAddress = Helper_Get_EA_U(_inst); - Memory::Write_U32(*(u32*)&fTemp, uAddress); - m_GPR[_inst.RA] = uAddress; -} - -void sth(UGeckoInstruction _inst) -{ - Memory::Write_U16((u16)m_GPR[_inst.RS], Helper_Get_EA(_inst)); -} - -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; -} - -void stw(UGeckoInstruction _inst) -{ - Memory::Write_U32(m_GPR[_inst.RS], Helper_Get_EA(_inst)); -} - -void stwu(UGeckoInstruction _inst) -{ - u32 uAddress = Helper_Get_EA_U(_inst); - Memory::Write_U32(m_GPR[_inst.RS], uAddress); - m_GPR[_inst.RA] = uAddress; -} - -void dcba(UGeckoInstruction _inst) -{ - _assert_msg_(GEKKO,0,"dcba - Not implemented - not a Gekko instruction"); -} - -void dcbf(UGeckoInstruction _inst) -{ - //This should tell GFX plugin to throw out any cached data here - // !!! SPEEDUP HACK for OSProtectRange !!! -/* u32 tmp1 = Memory::Read_U32(PC+4); - u32 tmp2 = Memory::Read_U32(PC+8); - - if ((tmp1 == 0x38630020) && - (tmp2 == 0x4200fff8)) - { - NPC = PC + 12; - }*/ -} - -void dcbi(UGeckoInstruction _inst) -{ - //Used during initialization - //_assert_msg_(GEKKO,0,"dcbi - Not implemented"); -} - -void dcbst(UGeckoInstruction _inst) -{ - //_assert_msg_(GEKKO,0,"dcbst - Not implemented"); -} - -void dcbt(UGeckoInstruction _inst) -{ - //This should tell GFX plugin to throw out any cached data here - //Used by Ikaruga - //_assert_msg_(GEKKO,0,"dcbt - Not implemented"); -} - -void dcbtst(UGeckoInstruction _inst) -{ - _assert_msg_(GEKKO,0,"dcbtst - Not implemented"); -} - -// __________________________________________________________________________________________________ -// dcbz -// TODO(ector) check docs -void dcbz(UGeckoInstruction _inst) -{ - // HACK but works... we think - Memory::Memset(Helper_Get_EA_X(_inst) & (~31), 0, 32); -} - -void eciwx(UGeckoInstruction _inst) -{ - _assert_msg_(GEKKO,0,"eciwx - Not implemented"); -} - -void ecowx(UGeckoInstruction _inst) -{ - _assert_msg_(GEKKO,0,"ecowx - Not implemented"); -} - -void eieio(UGeckoInstruction _inst) -{ - _assert_msg_(GEKKO,0,"eieio - Not implemented"); -} - -void icbi(UGeckoInstruction _inst) -{ - u32 address = Helper_Get_EA_X(_inst); - // block size seems to be 0x20 - address &= ~0x1f; - - // this comment is slightly outdated but still relevant: - // Inform the JIT to kill off this area of code NOW - // VERY IMPORTANT when we start linking blocks - // There are a TON of these so hopefully we can make this mechanism - // fast in the JIT - Jit64::InvalidateCodeRange(address, 0x20); -} - -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; -} - -void lbzx(UGeckoInstruction _inst) -{ - m_GPR[_inst.RD] = (u32)Memory::Read_U8(Helper_Get_EA_X(_inst)); -} - -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; -} - -void lhax(UGeckoInstruction _inst) -{ - m_GPR[_inst.RD] = (s32)(s16)Memory::Read_U16(Helper_Get_EA_X(_inst)); -} - -void lhbrx(UGeckoInstruction _inst) -{ - m_GPR[_inst.RD] = (u32)Common::swap16(Memory::Read_U16(Helper_Get_EA_X(_inst))); -} - -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; -} - -void lhzx(UGeckoInstruction _inst) -{ - m_GPR[_inst.RD] = (u32)Memory::Read_U16(Helper_Get_EA_X(_inst)); -} - -void lswx(UGeckoInstruction _inst) -{ - static bool bFirst = true; - if (bFirst) - PanicAlert("lswx - Instruction unimplemented"); - bFirst = false; -} - -void lwbrx(UGeckoInstruction _inst) -{ - m_GPR[_inst.RD] = Common::swap32(Memory::Read_U32(Helper_Get_EA_X(_inst))); -} - -void lwzux(UGeckoInstruction _inst) -{ - u32 uAddress = Helper_Get_EA_UX(_inst); - m_GPR[_inst.RD] = Memory::Read_U32(uAddress); - m_GPR[_inst.RA] = uAddress; -} - -void lwzx(UGeckoInstruction _inst) -{ - u32 uAddress = Helper_Get_EA_X(_inst); - m_GPR[_inst.RD] = Memory::Read_U32(uAddress); -} - -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; -} - -void stbx(UGeckoInstruction _inst) -{ - Memory::Write_U8((u8)m_GPR[_inst.RS], Helper_Get_EA_X(_inst)); -} - -void stfdux(UGeckoInstruction _inst) -{ - u32 uAddress = Helper_Get_EA_UX(_inst); - Memory::Write_U64(riPS0(_inst.FS), uAddress); - m_GPR[_inst.RA] = uAddress; -} - -void stfdx(UGeckoInstruction _inst) -{ - Memory::Write_U64(riPS0(_inst.FS), Helper_Get_EA_X(_inst)); -} - -// __________________________________________________________________________________________________ -// stfiwx -// TODO - examine what this really does -// Stores Floating points into Integers indeXed -void stfiwx(UGeckoInstruction _inst) -{ - u32 uAddress = Helper_Get_EA_X(_inst); - - Memory::Write_U32((u32)riPS0(_inst.FS), uAddress); -} - -// __________________________________________________________________________________________________ -// stfsux -// -// no paired ?? -// -void stfsux(UGeckoInstruction _inst) -{ - float fTemp = (float)rPS0(_inst.FS); - u32 uAddress = Helper_Get_EA_UX(_inst); - Memory::Write_U32(*(u32*)&fTemp, uAddress); - m_GPR[_inst.RA] = uAddress; -} - -// __________________________________________________________________________________________________ -// stfsx -// -// no paired ?? -// -void stfsx(UGeckoInstruction _inst) -{ - float fTemp = (float)rPS0(_inst.FS); - Memory::Write_U32(*(u32 *)&fTemp, Helper_Get_EA_X(_inst)); -} - -void sthbrx(UGeckoInstruction _inst) -{ - Memory::Write_U16(Common::swap16((u16)m_GPR[_inst.RS]), Helper_Get_EA_X(_inst)); -} - -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; -} - -void sthx(UGeckoInstruction _inst) -{ - Memory::Write_U16((u16)m_GPR[_inst.RS], Helper_Get_EA_X(_inst)); -} - -// __________________________________________________________________________________________________ -// lswi - bizarro string instruction -// -void lswi(UGeckoInstruction _inst) -{ - u32 EA; - if (_inst.RA == 0) - EA = 0; - else - EA = m_GPR[_inst.RA]; - - u32 n; - if (_inst.NB == 0) - n = 32; - else - n = _inst.NB; - - int r = _inst.RD - 1; - int i = 0; - while (n>0) - { - if (i==0) - { - r++; - r &= 31; - m_GPR[r] = 0; - } - - u32 TempValue = Memory::Read_U8(EA) << (24 - i); - if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) - { - PanicAlert("DSI exception in lsw."); - return; - } - - m_GPR[r] |= TempValue; - - i += 8; - if (i == 32) - i = 0; - EA++; - n--; - } -} - -// todo : optimize ? -// __________________________________________________________________________________________________ -// stswi - bizarro string instruction -// -void stswi(UGeckoInstruction _inst) -{ - u32 EA; - if (_inst.RA == 0) - EA = 0; - else - EA = m_GPR[_inst.RA]; - - u32 n; - if (_inst.NB == 0) - n = 32; - else - n = _inst.NB; - - int r = _inst.RS - 1; - int i = 0; - while (n > 0) - { - if (i == 0) - { - r++; - r &= 31; - } - Memory::Write_U8((m_GPR[r] >> (24 - i)) & 0xFF, EA); - if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) - return; - - i += 8; - if (i == 32) - i = 0; - EA++; - n--; - } -} - -void stswx(UGeckoInstruction _inst) -{ - static bool bFirst = true; - if (bFirst) - PanicAlert("stswx - Instruction unimplemented"); - bFirst = false; -} - -void stwbrx(UGeckoInstruction _inst) -{ - u32 uAddress = Helper_Get_EA_X(_inst); - Memory::Write_U32(Common::swap32(m_GPR[_inst.RS]), uAddress); -} - - -// The following two instructions are for SMP communications. On a single -// CPU, they cannot fail unless an interrupt happens in between, which usually -// won't happen with the JIT. -bool g_bReserve = false; -u32 g_reserveAddr; - -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; -} - -void stwcxd(UGeckoInstruction _inst) -{ - // Stores Word Conditional indeXed - - u32 uAddress; - - if(g_bReserve) { - uAddress = Helper_Get_EA_X(_inst); - if(uAddress == g_reserveAddr) { - Memory::Write_U32(m_GPR[_inst.RS], uAddress); - g_bReserve = false; - SetCRField(0, 2 | XER.SO); - return; - } - } - - SetCRField(0, XER.SO); -} - -void stwux(UGeckoInstruction _inst) -{ - u32 uAddress = Helper_Get_EA_UX(_inst); - Memory::Write_U32(m_GPR[_inst.RS], uAddress); - m_GPR[_inst.RA] = uAddress; -} - -void stwx(UGeckoInstruction _inst) -{ - u32 uAddress = Helper_Get_EA_X(_inst); - Memory::Write_U32(m_GPR[_inst.RS], uAddress); -} - -void sync(UGeckoInstruction _inst) -{ - //ignored -} - -void tlbia(UGeckoInstruction _inst) -{ - // Gekko does not support this instructions. - PanicAlert("The GC CPU does not support tlbia"); - // invalid the whole TLB - //MessageBox(0,"TLBIA","TLBIA",0); -} - -void tlbie(UGeckoInstruction _inst) -{ - // invalid entry - // int entry = _inst.RB; - - //MessageBox(0,"TLBIE","TLBIE",0); -} - -void tlbsync(UGeckoInstruction _inst) -{ - //MessageBox(0,"TLBsync","TLBsyncE",0); -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "../../HW/Memmap.h" +#include "../../HW/CommandProcessor.h" +#include "../../HW/PixelEngine.h" + +#include "Interpreter.h" +#include "../../Core.h" + +#include "../Jit64/Jit.h" +#include "../Jit64/JitCache.h" + +namespace Interpreter +{ + +u32 Helper_Get_EA(const UGeckoInstruction _inst) +{ + return _inst.RA ? (m_GPR[_inst.RA] + _inst.SIMM_16) : _inst.SIMM_16; +} + +u32 Helper_Get_EA_U(const UGeckoInstruction _inst) +{ + return (m_GPR[_inst.RA] + _inst.SIMM_16); +} + +u32 Helper_Get_EA_X(const UGeckoInstruction _inst) +{ + return _inst.RA ? (m_GPR[_inst.RA] + m_GPR[_inst.RB]) : m_GPR[_inst.RB]; +} + +u32 Helper_Get_EA_UX(const UGeckoInstruction _inst) +{ + return (m_GPR[_inst.RA] + m_GPR[_inst.RB]); +} + +void lbz(UGeckoInstruction _inst) +{ + m_GPR[_inst.RD] = (u32)Memory::Read_U8(Helper_Get_EA(_inst)); +} + +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; +} + +void lfd(UGeckoInstruction _inst) +{ + riPS0(_inst.FD) = Memory::Read_U64(Helper_Get_EA(_inst)); +} + +void lfdu(UGeckoInstruction _inst) +{ + u32 uAddress = Helper_Get_EA_U(_inst); + riPS0(_inst.FD) = Memory::Read_U64(uAddress); + 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; +} + +void lfdx(UGeckoInstruction _inst) +{ + riPS0(_inst.FD) = Memory::Read_U64(Helper_Get_EA_X(_inst)); +} + +void lfs(UGeckoInstruction _inst) +{ + u32 uTemp = Memory::Read_U32(Helper_Get_EA(_inst)); + rPS0(_inst.FD) = *(float*)&uTemp; + rPS1(_inst.FD) = rPS0(_inst.FD); +} + +void lfsu(UGeckoInstruction _inst) +{ + u32 uAddress = Helper_Get_EA_U(_inst); + u32 uTemp = Memory::Read_U32(uAddress); + rPS0(_inst.FD) = *(float*)&uTemp; + rPS1(_inst.FD) = rPS0(_inst.FD); + m_GPR[_inst.RA] = uAddress; +} + +void lfsux(UGeckoInstruction _inst) +{ + u32 uAddress = Helper_Get_EA_UX(_inst); + u32 uTemp = Memory::Read_U32(uAddress); + rPS0(_inst.FD) = *(float*)&uTemp; + rPS1(_inst.FD) = rPS0(_inst.FD); + m_GPR[_inst.RA] = uAddress; +} + +void lfsx(UGeckoInstruction _inst) +{ + u32 uTemp = Memory::Read_U32(Helper_Get_EA_X(_inst)); + rPS0(_inst.FD) = *(float*)&uTemp; + rPS1(_inst.FD) = rPS0(_inst.FD); +} + +void lha(UGeckoInstruction _inst) +{ + m_GPR[_inst.RD] = (u32)(s32)(s16)Memory::Read_U16(Helper_Get_EA(_inst)); +} + +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; +} + +void lhz(UGeckoInstruction _inst) +{ + m_GPR[_inst.RD] = (u32)(u16)Memory::Read_U16(Helper_Get_EA(_inst)); +} + +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; +} + +void lmw(UGeckoInstruction _inst) +{ + u32 uAddress = Helper_Get_EA(_inst); + for (int iReg = _inst.RD; iReg <= 31; iReg++, uAddress += 4) + { + u32 TempReg = Memory::Read_U32(uAddress); + if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) + { + PanicAlert("DSI exception in lmv."); + return; + } + + m_GPR[iReg] = TempReg; + } +} + +void stmw(UGeckoInstruction _inst) +{ + u32 uAddress = Helper_Get_EA(_inst); + for (int iReg = _inst.RS; iReg <= 31; iReg++, uAddress+=4) + { + Memory::Write_U32(m_GPR[iReg], uAddress); + if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) + return; + } +} + +void lwz(UGeckoInstruction _inst) +{ + u32 uAddress = Helper_Get_EA(_inst); + m_GPR[_inst.RD] = Memory::Read_U32(uAddress); + + // hack to detect SelectThread loop + // should probably run a pass through memory instead before execution + // but that would be dangerous + + // Enable idle skipping? + /* + if ((_inst.hex & 0xFFFF0000)==0x800D0000 && + Memory::ReadUnchecked_U32(PC+4)==0x28000000 && + Memory::ReadUnchecked_U32(PC+8)==0x4182fff8) + { + if (CommandProcessor::AllowIdleSkipping() && PixelEngine::AllowIdleSkipping()) + { + CoreTiming::Idle(); + } + }*/ +} + +void lwzu(UGeckoInstruction _inst) +{ + u32 uAddress = Helper_Get_EA_U(_inst); + m_GPR[_inst.RD] = Memory::Read_U32(uAddress); + m_GPR[_inst.RA] = uAddress; +} + +void stb(UGeckoInstruction _inst) +{ + Memory::Write_U8((u8)m_GPR[_inst.RS], Helper_Get_EA(_inst)); +} + +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; +} + +void stfd(UGeckoInstruction _inst) +{ + Memory::Write_U64(riPS0(_inst.FS), Helper_Get_EA(_inst)); +} + +void stfdu(UGeckoInstruction _inst) +{ + u32 uAddress = Helper_Get_EA_U(_inst); + Memory::Write_U64(riPS0(_inst.FS), uAddress); + m_GPR[_inst.RA] = uAddress; +} + +void stfs(UGeckoInstruction _inst) +{ + float fTemp = (float)rPS0(_inst.FS); + Memory::Write_U32(*(u32*)&fTemp, Helper_Get_EA(_inst)); +} + +void stfsu(UGeckoInstruction _inst) +{ + float fTemp = (float)rPS0(_inst.FS); + u32 uAddress = Helper_Get_EA_U(_inst); + Memory::Write_U32(*(u32*)&fTemp, uAddress); + m_GPR[_inst.RA] = uAddress; +} + +void sth(UGeckoInstruction _inst) +{ + Memory::Write_U16((u16)m_GPR[_inst.RS], Helper_Get_EA(_inst)); +} + +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; +} + +void stw(UGeckoInstruction _inst) +{ + Memory::Write_U32(m_GPR[_inst.RS], Helper_Get_EA(_inst)); +} + +void stwu(UGeckoInstruction _inst) +{ + u32 uAddress = Helper_Get_EA_U(_inst); + Memory::Write_U32(m_GPR[_inst.RS], uAddress); + m_GPR[_inst.RA] = uAddress; +} + +void dcba(UGeckoInstruction _inst) +{ + _assert_msg_(GEKKO,0,"dcba - Not implemented - not a Gekko instruction"); +} + +void dcbf(UGeckoInstruction _inst) +{ + //This should tell GFX plugin to throw out any cached data here + // !!! SPEEDUP HACK for OSProtectRange !!! +/* u32 tmp1 = Memory::Read_U32(PC+4); + u32 tmp2 = Memory::Read_U32(PC+8); + + if ((tmp1 == 0x38630020) && + (tmp2 == 0x4200fff8)) + { + NPC = PC + 12; + }*/ +} + +void dcbi(UGeckoInstruction _inst) +{ + //Used during initialization + //_assert_msg_(GEKKO,0,"dcbi - Not implemented"); +} + +void dcbst(UGeckoInstruction _inst) +{ + //_assert_msg_(GEKKO,0,"dcbst - Not implemented"); +} + +void dcbt(UGeckoInstruction _inst) +{ + //This should tell GFX plugin to throw out any cached data here + //Used by Ikaruga + //_assert_msg_(GEKKO,0,"dcbt - Not implemented"); +} + +void dcbtst(UGeckoInstruction _inst) +{ + _assert_msg_(GEKKO,0,"dcbtst - Not implemented"); +} + +// __________________________________________________________________________________________________ +// dcbz +// TODO(ector) check docs +void dcbz(UGeckoInstruction _inst) +{ + // HACK but works... we think + Memory::Memset(Helper_Get_EA_X(_inst) & (~31), 0, 32); +} + +void eciwx(UGeckoInstruction _inst) +{ + _assert_msg_(GEKKO,0,"eciwx - Not implemented"); +} + +void ecowx(UGeckoInstruction _inst) +{ + _assert_msg_(GEKKO,0,"ecowx - Not implemented"); +} + +void eieio(UGeckoInstruction _inst) +{ + _assert_msg_(GEKKO,0,"eieio - Not implemented"); +} + +void icbi(UGeckoInstruction _inst) +{ + u32 address = Helper_Get_EA_X(_inst); + // block size seems to be 0x20 + address &= ~0x1f; + + // this comment is slightly outdated but still relevant: + // Inform the JIT to kill off this area of code NOW + // VERY IMPORTANT when we start linking blocks + // There are a TON of these so hopefully we can make this mechanism + // fast in the JIT + Jit64::InvalidateCodeRange(address, 0x20); +} + +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; +} + +void lbzx(UGeckoInstruction _inst) +{ + m_GPR[_inst.RD] = (u32)Memory::Read_U8(Helper_Get_EA_X(_inst)); +} + +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; +} + +void lhax(UGeckoInstruction _inst) +{ + m_GPR[_inst.RD] = (s32)(s16)Memory::Read_U16(Helper_Get_EA_X(_inst)); +} + +void lhbrx(UGeckoInstruction _inst) +{ + m_GPR[_inst.RD] = (u32)Common::swap16(Memory::Read_U16(Helper_Get_EA_X(_inst))); +} + +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; +} + +void lhzx(UGeckoInstruction _inst) +{ + m_GPR[_inst.RD] = (u32)Memory::Read_U16(Helper_Get_EA_X(_inst)); +} + +void lswx(UGeckoInstruction _inst) +{ + static bool bFirst = true; + if (bFirst) + PanicAlert("lswx - Instruction unimplemented"); + bFirst = false; +} + +void lwbrx(UGeckoInstruction _inst) +{ + m_GPR[_inst.RD] = Common::swap32(Memory::Read_U32(Helper_Get_EA_X(_inst))); +} + +void lwzux(UGeckoInstruction _inst) +{ + u32 uAddress = Helper_Get_EA_UX(_inst); + m_GPR[_inst.RD] = Memory::Read_U32(uAddress); + m_GPR[_inst.RA] = uAddress; +} + +void lwzx(UGeckoInstruction _inst) +{ + u32 uAddress = Helper_Get_EA_X(_inst); + m_GPR[_inst.RD] = Memory::Read_U32(uAddress); +} + +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; +} + +void stbx(UGeckoInstruction _inst) +{ + Memory::Write_U8((u8)m_GPR[_inst.RS], Helper_Get_EA_X(_inst)); +} + +void stfdux(UGeckoInstruction _inst) +{ + u32 uAddress = Helper_Get_EA_UX(_inst); + Memory::Write_U64(riPS0(_inst.FS), uAddress); + m_GPR[_inst.RA] = uAddress; +} + +void stfdx(UGeckoInstruction _inst) +{ + Memory::Write_U64(riPS0(_inst.FS), Helper_Get_EA_X(_inst)); +} + +// __________________________________________________________________________________________________ +// stfiwx +// TODO - examine what this really does +// Stores Floating points into Integers indeXed +void stfiwx(UGeckoInstruction _inst) +{ + u32 uAddress = Helper_Get_EA_X(_inst); + + Memory::Write_U32((u32)riPS0(_inst.FS), uAddress); +} + +// __________________________________________________________________________________________________ +// stfsux +// +// no paired ?? +// +void stfsux(UGeckoInstruction _inst) +{ + float fTemp = (float)rPS0(_inst.FS); + u32 uAddress = Helper_Get_EA_UX(_inst); + Memory::Write_U32(*(u32*)&fTemp, uAddress); + m_GPR[_inst.RA] = uAddress; +} + +// __________________________________________________________________________________________________ +// stfsx +// +// no paired ?? +// +void stfsx(UGeckoInstruction _inst) +{ + float fTemp = (float)rPS0(_inst.FS); + Memory::Write_U32(*(u32 *)&fTemp, Helper_Get_EA_X(_inst)); +} + +void sthbrx(UGeckoInstruction _inst) +{ + Memory::Write_U16(Common::swap16((u16)m_GPR[_inst.RS]), Helper_Get_EA_X(_inst)); +} + +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; +} + +void sthx(UGeckoInstruction _inst) +{ + Memory::Write_U16((u16)m_GPR[_inst.RS], Helper_Get_EA_X(_inst)); +} + +// __________________________________________________________________________________________________ +// lswi - bizarro string instruction +// +void lswi(UGeckoInstruction _inst) +{ + u32 EA; + if (_inst.RA == 0) + EA = 0; + else + EA = m_GPR[_inst.RA]; + + u32 n; + if (_inst.NB == 0) + n = 32; + else + n = _inst.NB; + + int r = _inst.RD - 1; + int i = 0; + while (n>0) + { + if (i==0) + { + r++; + r &= 31; + m_GPR[r] = 0; + } + + u32 TempValue = Memory::Read_U8(EA) << (24 - i); + if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) + { + PanicAlert("DSI exception in lsw."); + return; + } + + m_GPR[r] |= TempValue; + + i += 8; + if (i == 32) + i = 0; + EA++; + n--; + } +} + +// todo : optimize ? +// __________________________________________________________________________________________________ +// stswi - bizarro string instruction +// +void stswi(UGeckoInstruction _inst) +{ + u32 EA; + if (_inst.RA == 0) + EA = 0; + else + EA = m_GPR[_inst.RA]; + + u32 n; + if (_inst.NB == 0) + n = 32; + else + n = _inst.NB; + + int r = _inst.RS - 1; + int i = 0; + while (n > 0) + { + if (i == 0) + { + r++; + r &= 31; + } + Memory::Write_U8((m_GPR[r] >> (24 - i)) & 0xFF, EA); + if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) + return; + + i += 8; + if (i == 32) + i = 0; + EA++; + n--; + } +} + +void stswx(UGeckoInstruction _inst) +{ + static bool bFirst = true; + if (bFirst) + PanicAlert("stswx - Instruction unimplemented"); + bFirst = false; +} + +void stwbrx(UGeckoInstruction _inst) +{ + u32 uAddress = Helper_Get_EA_X(_inst); + Memory::Write_U32(Common::swap32(m_GPR[_inst.RS]), uAddress); +} + + +// The following two instructions are for SMP communications. On a single +// CPU, they cannot fail unless an interrupt happens in between, which usually +// won't happen with the JIT. +bool g_bReserve = false; +u32 g_reserveAddr; + +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; +} + +void stwcxd(UGeckoInstruction _inst) +{ + // Stores Word Conditional indeXed + + u32 uAddress; + + if(g_bReserve) { + uAddress = Helper_Get_EA_X(_inst); + if(uAddress == g_reserveAddr) { + Memory::Write_U32(m_GPR[_inst.RS], uAddress); + g_bReserve = false; + SetCRField(0, 2 | XER.SO); + return; + } + } + + SetCRField(0, XER.SO); +} + +void stwux(UGeckoInstruction _inst) +{ + u32 uAddress = Helper_Get_EA_UX(_inst); + Memory::Write_U32(m_GPR[_inst.RS], uAddress); + m_GPR[_inst.RA] = uAddress; +} + +void stwx(UGeckoInstruction _inst) +{ + u32 uAddress = Helper_Get_EA_X(_inst); + Memory::Write_U32(m_GPR[_inst.RS], uAddress); +} + +void sync(UGeckoInstruction _inst) +{ + //ignored +} + +void tlbia(UGeckoInstruction _inst) +{ + // Gekko does not support this instructions. + PanicAlert("The GC CPU does not support tlbia"); + // invalid the whole TLB + //MessageBox(0,"TLBIA","TLBIA",0); +} + +void tlbie(UGeckoInstruction _inst) +{ + // invalid entry + // int entry = _inst.RB; + + //MessageBox(0,"TLBIE","TLBIE",0); +} + +void tlbsync(UGeckoInstruction _inst) +{ + //MessageBox(0,"TLBsync","TLBsyncE",0); +} + +} // namespace diff --git a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_LoadStorePaired.cpp b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_LoadStorePaired.cpp index 5aec44ae13..795e6727df 100644 --- a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_LoadStorePaired.cpp +++ b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_LoadStorePaired.cpp @@ -1,342 +1,342 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include -#include "Interpreter.h" -#include "../../HW/Memmap.h" - -namespace Interpreter -{ - -// dequantize table -const float m_dequantizeTable[] = -{ - 1.0 / (1 << 0), 1.0 / (1 << 1), 1.0 / (1 << 2), 1.0 / (1 << 3), - 1.0 / (1 << 4), 1.0 / (1 << 5), 1.0 / (1 << 6), 1.0 / (1 << 7), - 1.0 / (1 << 8), 1.0 / (1 << 9), 1.0 / (1 << 10), 1.0 / (1 << 11), - 1.0 / (1 << 12), 1.0 / (1 << 13), 1.0 / (1 << 14), 1.0 / (1 << 15), - 1.0 / (1 << 16), 1.0 / (1 << 17), 1.0 / (1 << 18), 1.0 / (1 << 19), - 1.0 / (1 << 20), 1.0 / (1 << 21), 1.0 / (1 << 22), 1.0 / (1 << 23), - 1.0 / (1 << 24), 1.0 / (1 << 25), 1.0 / (1 << 26), 1.0 / (1 << 27), - 1.0 / (1 << 28), 1.0 / (1 << 29), 1.0 / (1 << 30), 1.0 / (1 << 31), - (1ULL << 32), (1 << 31), (1 << 30), (1 << 29), - (1 << 28), (1 << 27), (1 << 26), (1 << 25), - (1 << 24), (1 << 23), (1 << 22), (1 << 21), - (1 << 20), (1 << 19), (1 << 18), (1 << 17), - (1 << 16), (1 << 15), (1 << 14), (1 << 13), - (1 << 12), (1 << 11), (1 << 10), (1 << 9), - (1 << 8), (1 << 7), (1 << 6), (1 << 5), - (1 << 4), (1 << 3), (1 << 2), (1 << 1), -}; - -// quantize table -const float m_quantizeTable[] = -{ - (1 << 0), (1 << 1), (1 << 2), (1 << 3), - (1 << 4), (1 << 5), (1 << 6), (1 << 7), - (1 << 8), (1 << 9), (1 << 10), (1 << 11), - (1 << 12), (1 << 13), (1 << 14), (1 << 15), - (1 << 16), (1 << 17), (1 << 18), (1 << 19), - (1 << 20), (1 << 21), (1 << 22), (1 << 23), - (1 << 24), (1 << 25), (1 << 26), (1 << 27), - (1 << 28), (1 << 29), (1 << 30), (1 << 31), - 1.0 / (1ULL << 32), 1.0 / (1 << 31), 1.0 / (1 << 30), 1.0 / (1 << 29), - 1.0 / (1 << 28), 1.0 / (1 << 27), 1.0 / (1 << 26), 1.0 / (1 << 25), - 1.0 / (1 << 24), 1.0 / (1 << 23), 1.0 / (1 << 22), 1.0 / (1 << 21), - 1.0 / (1 << 20), 1.0 / (1 << 19), 1.0 / (1 << 18), 1.0 / (1 << 17), - 1.0 / (1 << 16), 1.0 / (1 << 15), 1.0 / (1 << 14), 1.0 / (1 << 13), - 1.0 / (1 << 12), 1.0 / (1 << 11), 1.0 / (1 << 10), 1.0 / (1 << 9), - 1.0 / (1 << 8), 1.0 / (1 << 7), 1.0 / (1 << 6), 1.0 / (1 << 5), - 1.0 / (1 << 4), 1.0 / (1 << 3), 1.0 / (1 << 2), 1.0 / (1 << 1), -}; - -template -inline T CLAMP(T a, T bottom, T top) { - if (a > top) return top; - if (a < bottom) return bottom; - return a; -} - -void Helper_Quantize(const u32 _Addr, const float _fValue, - const EQuantizeType _quantizeType, const unsigned int _uScale) -{ - switch(_quantizeType) - { - case QUANTIZE_FLOAT: - Memory::Write_U32(*(u32*)&_fValue,_Addr); - break; - - // used for THP player - case QUANTIZE_U8: - { - float fResult = CLAMP(_fValue * m_quantizeTable[_uScale], 0.0f, 255.0f); - Memory::Write_U8((u8)fResult, _Addr); - } - break; - - case QUANTIZE_U16: - { - float fResult = CLAMP(_fValue * m_quantizeTable[_uScale], 0.0f, 65535.0f); - Memory::Write_U16((u16)fResult, _Addr); - } - break; - - case QUANTIZE_S8: - { - float fResult = CLAMP(_fValue * m_quantizeTable[_uScale], -128.0f, 127.0f); - Memory::Write_U8((u8)(s8)fResult, _Addr); - } - break; - - case QUANTIZE_S16: - { - float fResult = CLAMP(_fValue * m_quantizeTable[_uScale], -32768.0f, 32767.0f); - Memory::Write_U16((u16)(s16)fResult, _Addr); - } - break; - - default: - _dbg_assert_msg_(GEKKO,0,"PS dequantize","Unknown type to read"); - break; - } -} - -float Helper_Dequantize(const u32 _Addr, const EQuantizeType _quantizeType, - const unsigned int _uScale) -{ - // dequantize the value - float fResult; - switch(_quantizeType) - { - case QUANTIZE_FLOAT: - { - u32 dwValue = Memory::Read_U32(_Addr); - fResult = *(float*)&dwValue; - } - break; - - case QUANTIZE_U8: - fResult = static_cast(Memory::Read_U8(_Addr)) * m_dequantizeTable[_uScale]; - break; - - case QUANTIZE_U16: - fResult = static_cast(Memory::Read_U16(_Addr)) * m_dequantizeTable[_uScale]; - break; - - case QUANTIZE_S8: - fResult = static_cast((s8)Memory::Read_U8(_Addr)) * m_dequantizeTable[_uScale]; - break; - - // used for THP player - case QUANTIZE_S16: - fResult = static_cast((s16)Memory::Read_U16(_Addr)) * m_dequantizeTable[_uScale]; - break; - - default: - _dbg_assert_msg_(GEKKO,0,"PS dequantize","Unknown type to read"); - fResult = 0; - break; - } - - return fResult; -} - -void psq_l(UGeckoInstruction _inst) -{ - const UGQR gqr(rSPR(SPR_GQR0 + _inst.I)); - const EQuantizeType ldType = static_cast(gqr.LD_TYPE); - const unsigned int ldScale = gqr.LD_SCALE; - const u32 EA = _inst.RA ? (m_GPR[_inst.RA] + _inst.SIMM_12) : _inst.SIMM_12; - - int c = 4; - if ((ldType == QUANTIZE_U8) || (ldType == QUANTIZE_S8)) c = 0x1; - if ((ldType == QUANTIZE_U16) || (ldType == QUANTIZE_S16)) c = 0x2; - - if (_inst.W == 0) - { - rPS0(_inst.RS) = Helper_Dequantize(EA, ldType, ldScale); - rPS1(_inst.RS) = Helper_Dequantize(EA+c, ldType, ldScale); - } - else - { - rPS0(_inst.RS) = Helper_Dequantize(EA, ldType, ldScale); - rPS1(_inst.RS) = 1.0f; - } -} - -void psq_lu(UGeckoInstruction _inst) -{ - const UGQR gqr(rSPR(SPR_GQR0 + _inst.I)); - const EQuantizeType ldType = static_cast(gqr.LD_TYPE); - const unsigned int ldScale = gqr.LD_SCALE; - const u32 EA = m_GPR[_inst.RA] + _inst.SIMM_12; - - int c = 4; - if ((ldType == 4) || (ldType == 6)) c = 0x1; - if ((ldType == 5) || (ldType == 7)) c = 0x2; - - if (_inst.W == 0) - { - rPS0(_inst.RS) = Helper_Dequantize( EA, ldType, ldScale ); - rPS1(_inst.RS) = Helper_Dequantize( EA+c, ldType, ldScale ); - } - else - { - rPS0(_inst.RS) = Helper_Dequantize( EA, ldType, ldScale ); - rPS1(_inst.RS) = 1.0f; - } - m_GPR[_inst.RA] = EA; -} - -void psq_st(UGeckoInstruction _inst) -{ - const UGQR gqr(rSPR(SPR_GQR0 + _inst.I)); - const EQuantizeType stType = static_cast(gqr.ST_TYPE); - const unsigned int stScale = gqr.ST_SCALE; - const u32 EA = _inst.RA ? (m_GPR[_inst.RA] + _inst.SIMM_12) : _inst.SIMM_12; - - int c = 4; - if ((stType == 4) || (stType == 6)) c = 0x1; - if ((stType == 5) || (stType == 7)) c = 0x2; - - if (_inst.W == 0) - { - Helper_Quantize( EA, (float)rPS0(_inst.RS), stType, stScale ); - Helper_Quantize( EA+c, (float)rPS1(_inst.RS), stType, stScale ); - } - else - { - Helper_Quantize( EA, (float)rPS0(_inst.RS), stType, stScale ); - } -} - -void psq_stu(UGeckoInstruction _inst) -{ - const UGQR gqr(rSPR(SPR_GQR0 + _inst.I)); - const EQuantizeType stType = static_cast(gqr.ST_TYPE); - const unsigned int stScale = gqr.ST_SCALE; - const u32 EA = m_GPR[_inst.RA] + _inst.SIMM_12; - - int c = 4; - if ((stType == 4) || (stType == 6)) c = 0x1; - if ((stType == 5) || (stType == 7)) c = 0x2; - - if (_inst.W == 0) - { - Helper_Quantize(EA, (float)rPS0(_inst.RS), stType, stScale); - Helper_Quantize(EA+c, (float)rPS1(_inst.RS), stType, stScale); - } - else - { - Helper_Quantize(EA, (float)rPS0(_inst.RS), stType, stScale); - } - m_GPR[_inst.RA] = EA; -} - -void psq_lx(UGeckoInstruction _inst) -{ - const UGQR gqr(rSPR(SPR_GQR0 + _inst.Ix)); - const EQuantizeType ldType = static_cast(gqr.LD_TYPE); - const unsigned int ldScale = gqr.LD_SCALE; - const u32 EA = _inst.RA ? (m_GPR[_inst.RA] + m_GPR[_inst.RB]) : m_GPR[_inst.RB]; - - int c = 4; - if ((ldType == 4) || (ldType == 6)) c = 0x1; - if ((ldType == 5) || (ldType == 7)) c = 0x2; - - if (_inst.Wx == 0) - { - rPS0(_inst.RS) = Helper_Dequantize( EA, ldType, ldScale ); - rPS1(_inst.RS) = Helper_Dequantize( EA+c, ldType, ldScale ); - } - else - { - rPS0(_inst.RS) = Helper_Dequantize( EA, ldType, ldScale ); - rPS1(_inst.RS) = 1.0f; - } -} - -void psq_stx(UGeckoInstruction _inst) -{ - const UGQR gqr(rSPR(SPR_GQR0 + _inst.Ix)); - const EQuantizeType stType = static_cast(gqr.ST_TYPE); - const unsigned int stScale = gqr.ST_SCALE; - const u32 EA = _inst.RA ? (m_GPR[_inst.RA] + m_GPR[_inst.RB]) : m_GPR[_inst.RB]; - - int c = 4; - if ((stType == 4) || (stType == 6)) c = 0x1; - if ((stType == 5) || (stType == 7)) c = 0x2; - - if (_inst.Wx == 0) - { - Helper_Quantize(EA, (float)rPS0(_inst.RS), stType, stScale); - Helper_Quantize(EA+c, (float)rPS1(_inst.RS), stType, stScale); - } - else - { - Helper_Quantize(EA, (float)rPS0(_inst.RS), stType, stScale); - } -} - -void psq_lux(UGeckoInstruction _inst) -{ - const UGQR gqr(rSPR(SPR_GQR0 + _inst.Ix)); - const EQuantizeType ldType = static_cast(gqr.LD_TYPE); - const unsigned int ldScale = gqr.LD_SCALE; - const u32 EA = m_GPR[_inst.RA] + m_GPR[_inst.RB]; - - int c = 4; - if ((ldType == 4) || (ldType == 6)) c = 0x1; - if ((ldType == 5) || (ldType == 7)) c = 0x2; - - if (_inst.Wx == 0) - { - rPS0(_inst.RS) = Helper_Dequantize( EA, ldType, ldScale ); - rPS1(_inst.RS) = Helper_Dequantize( EA+c, ldType, ldScale ); - } - else - { - rPS0(_inst.RS) = Helper_Dequantize( EA, ldType, ldScale ); - rPS1(_inst.RS) = 1.0f; - } - m_GPR[_inst.RA] = EA; -} - -void psq_stux(UGeckoInstruction _inst) -{ - const UGQR gqr(rSPR(SPR_GQR0 + _inst.Ix)); - const EQuantizeType stType = static_cast(gqr.ST_TYPE); - const unsigned int stScale = gqr.ST_SCALE; - const u32 EA = m_GPR[_inst.RA] + m_GPR[_inst.RB]; - - int c = 4; - if ((stType == 4) || (stType == 6)) c = 0x1; - if ((stType == 5) || (stType == 7)) c = 0x2; - - if (_inst.Wx == 0) - { - Helper_Quantize(EA, (float)rPS0(_inst.RS), stType, stScale); - Helper_Quantize(EA+c, (float)rPS1(_inst.RS), stType, stScale); - } - else - { - Helper_Quantize(EA, (float)rPS0(_inst.RS), stType, stScale); - } - m_GPR[_inst.RA] = EA; - -} // namespace======= +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include "Interpreter.h" +#include "../../HW/Memmap.h" + +namespace Interpreter +{ + +// dequantize table +const float m_dequantizeTable[] = +{ + 1.0 / (1 << 0), 1.0 / (1 << 1), 1.0 / (1 << 2), 1.0 / (1 << 3), + 1.0 / (1 << 4), 1.0 / (1 << 5), 1.0 / (1 << 6), 1.0 / (1 << 7), + 1.0 / (1 << 8), 1.0 / (1 << 9), 1.0 / (1 << 10), 1.0 / (1 << 11), + 1.0 / (1 << 12), 1.0 / (1 << 13), 1.0 / (1 << 14), 1.0 / (1 << 15), + 1.0 / (1 << 16), 1.0 / (1 << 17), 1.0 / (1 << 18), 1.0 / (1 << 19), + 1.0 / (1 << 20), 1.0 / (1 << 21), 1.0 / (1 << 22), 1.0 / (1 << 23), + 1.0 / (1 << 24), 1.0 / (1 << 25), 1.0 / (1 << 26), 1.0 / (1 << 27), + 1.0 / (1 << 28), 1.0 / (1 << 29), 1.0 / (1 << 30), 1.0 / (1 << 31), + (1ULL << 32), (1 << 31), (1 << 30), (1 << 29), + (1 << 28), (1 << 27), (1 << 26), (1 << 25), + (1 << 24), (1 << 23), (1 << 22), (1 << 21), + (1 << 20), (1 << 19), (1 << 18), (1 << 17), + (1 << 16), (1 << 15), (1 << 14), (1 << 13), + (1 << 12), (1 << 11), (1 << 10), (1 << 9), + (1 << 8), (1 << 7), (1 << 6), (1 << 5), + (1 << 4), (1 << 3), (1 << 2), (1 << 1), +}; + +// quantize table +const float m_quantizeTable[] = +{ + (1 << 0), (1 << 1), (1 << 2), (1 << 3), + (1 << 4), (1 << 5), (1 << 6), (1 << 7), + (1 << 8), (1 << 9), (1 << 10), (1 << 11), + (1 << 12), (1 << 13), (1 << 14), (1 << 15), + (1 << 16), (1 << 17), (1 << 18), (1 << 19), + (1 << 20), (1 << 21), (1 << 22), (1 << 23), + (1 << 24), (1 << 25), (1 << 26), (1 << 27), + (1 << 28), (1 << 29), (1 << 30), (1 << 31), + 1.0 / (1ULL << 32), 1.0 / (1 << 31), 1.0 / (1 << 30), 1.0 / (1 << 29), + 1.0 / (1 << 28), 1.0 / (1 << 27), 1.0 / (1 << 26), 1.0 / (1 << 25), + 1.0 / (1 << 24), 1.0 / (1 << 23), 1.0 / (1 << 22), 1.0 / (1 << 21), + 1.0 / (1 << 20), 1.0 / (1 << 19), 1.0 / (1 << 18), 1.0 / (1 << 17), + 1.0 / (1 << 16), 1.0 / (1 << 15), 1.0 / (1 << 14), 1.0 / (1 << 13), + 1.0 / (1 << 12), 1.0 / (1 << 11), 1.0 / (1 << 10), 1.0 / (1 << 9), + 1.0 / (1 << 8), 1.0 / (1 << 7), 1.0 / (1 << 6), 1.0 / (1 << 5), + 1.0 / (1 << 4), 1.0 / (1 << 3), 1.0 / (1 << 2), 1.0 / (1 << 1), +}; + +template +inline T CLAMP(T a, T bottom, T top) { + if (a > top) return top; + if (a < bottom) return bottom; + return a; +} + +void Helper_Quantize(const u32 _Addr, const float _fValue, + const EQuantizeType _quantizeType, const unsigned int _uScale) +{ + switch(_quantizeType) + { + case QUANTIZE_FLOAT: + Memory::Write_U32(*(u32*)&_fValue,_Addr); + break; + + // used for THP player + case QUANTIZE_U8: + { + float fResult = CLAMP(_fValue * m_quantizeTable[_uScale], 0.0f, 255.0f); + Memory::Write_U8((u8)fResult, _Addr); + } + break; + + case QUANTIZE_U16: + { + float fResult = CLAMP(_fValue * m_quantizeTable[_uScale], 0.0f, 65535.0f); + Memory::Write_U16((u16)fResult, _Addr); + } + break; + + case QUANTIZE_S8: + { + float fResult = CLAMP(_fValue * m_quantizeTable[_uScale], -128.0f, 127.0f); + Memory::Write_U8((u8)(s8)fResult, _Addr); + } + break; + + case QUANTIZE_S16: + { + float fResult = CLAMP(_fValue * m_quantizeTable[_uScale], -32768.0f, 32767.0f); + Memory::Write_U16((u16)(s16)fResult, _Addr); + } + break; + + default: + _dbg_assert_msg_(GEKKO,0,"PS dequantize","Unknown type to read"); + break; + } +} + +float Helper_Dequantize(const u32 _Addr, const EQuantizeType _quantizeType, + const unsigned int _uScale) +{ + // dequantize the value + float fResult; + switch(_quantizeType) + { + case QUANTIZE_FLOAT: + { + u32 dwValue = Memory::Read_U32(_Addr); + fResult = *(float*)&dwValue; + } + break; + + case QUANTIZE_U8: + fResult = static_cast(Memory::Read_U8(_Addr)) * m_dequantizeTable[_uScale]; + break; + + case QUANTIZE_U16: + fResult = static_cast(Memory::Read_U16(_Addr)) * m_dequantizeTable[_uScale]; + break; + + case QUANTIZE_S8: + fResult = static_cast((s8)Memory::Read_U8(_Addr)) * m_dequantizeTable[_uScale]; + break; + + // used for THP player + case QUANTIZE_S16: + fResult = static_cast((s16)Memory::Read_U16(_Addr)) * m_dequantizeTable[_uScale]; + break; + + default: + _dbg_assert_msg_(GEKKO,0,"PS dequantize","Unknown type to read"); + fResult = 0; + break; + } + + return fResult; +} + +void psq_l(UGeckoInstruction _inst) +{ + const UGQR gqr(rSPR(SPR_GQR0 + _inst.I)); + const EQuantizeType ldType = static_cast(gqr.LD_TYPE); + const unsigned int ldScale = gqr.LD_SCALE; + const u32 EA = _inst.RA ? (m_GPR[_inst.RA] + _inst.SIMM_12) : _inst.SIMM_12; + + int c = 4; + if ((ldType == QUANTIZE_U8) || (ldType == QUANTIZE_S8)) c = 0x1; + if ((ldType == QUANTIZE_U16) || (ldType == QUANTIZE_S16)) c = 0x2; + + if (_inst.W == 0) + { + rPS0(_inst.RS) = Helper_Dequantize(EA, ldType, ldScale); + rPS1(_inst.RS) = Helper_Dequantize(EA+c, ldType, ldScale); + } + else + { + rPS0(_inst.RS) = Helper_Dequantize(EA, ldType, ldScale); + rPS1(_inst.RS) = 1.0f; + } +} + +void psq_lu(UGeckoInstruction _inst) +{ + const UGQR gqr(rSPR(SPR_GQR0 + _inst.I)); + const EQuantizeType ldType = static_cast(gqr.LD_TYPE); + const unsigned int ldScale = gqr.LD_SCALE; + const u32 EA = m_GPR[_inst.RA] + _inst.SIMM_12; + + int c = 4; + if ((ldType == 4) || (ldType == 6)) c = 0x1; + if ((ldType == 5) || (ldType == 7)) c = 0x2; + + if (_inst.W == 0) + { + rPS0(_inst.RS) = Helper_Dequantize( EA, ldType, ldScale ); + rPS1(_inst.RS) = Helper_Dequantize( EA+c, ldType, ldScale ); + } + else + { + rPS0(_inst.RS) = Helper_Dequantize( EA, ldType, ldScale ); + rPS1(_inst.RS) = 1.0f; + } + m_GPR[_inst.RA] = EA; +} + +void psq_st(UGeckoInstruction _inst) +{ + const UGQR gqr(rSPR(SPR_GQR0 + _inst.I)); + const EQuantizeType stType = static_cast(gqr.ST_TYPE); + const unsigned int stScale = gqr.ST_SCALE; + const u32 EA = _inst.RA ? (m_GPR[_inst.RA] + _inst.SIMM_12) : _inst.SIMM_12; + + int c = 4; + if ((stType == 4) || (stType == 6)) c = 0x1; + if ((stType == 5) || (stType == 7)) c = 0x2; + + if (_inst.W == 0) + { + Helper_Quantize( EA, (float)rPS0(_inst.RS), stType, stScale ); + Helper_Quantize( EA+c, (float)rPS1(_inst.RS), stType, stScale ); + } + else + { + Helper_Quantize( EA, (float)rPS0(_inst.RS), stType, stScale ); + } +} + +void psq_stu(UGeckoInstruction _inst) +{ + const UGQR gqr(rSPR(SPR_GQR0 + _inst.I)); + const EQuantizeType stType = static_cast(gqr.ST_TYPE); + const unsigned int stScale = gqr.ST_SCALE; + const u32 EA = m_GPR[_inst.RA] + _inst.SIMM_12; + + int c = 4; + if ((stType == 4) || (stType == 6)) c = 0x1; + if ((stType == 5) || (stType == 7)) c = 0x2; + + if (_inst.W == 0) + { + Helper_Quantize(EA, (float)rPS0(_inst.RS), stType, stScale); + Helper_Quantize(EA+c, (float)rPS1(_inst.RS), stType, stScale); + } + else + { + Helper_Quantize(EA, (float)rPS0(_inst.RS), stType, stScale); + } + m_GPR[_inst.RA] = EA; +} + +void psq_lx(UGeckoInstruction _inst) +{ + const UGQR gqr(rSPR(SPR_GQR0 + _inst.Ix)); + const EQuantizeType ldType = static_cast(gqr.LD_TYPE); + const unsigned int ldScale = gqr.LD_SCALE; + const u32 EA = _inst.RA ? (m_GPR[_inst.RA] + m_GPR[_inst.RB]) : m_GPR[_inst.RB]; + + int c = 4; + if ((ldType == 4) || (ldType == 6)) c = 0x1; + if ((ldType == 5) || (ldType == 7)) c = 0x2; + + if (_inst.Wx == 0) + { + rPS0(_inst.RS) = Helper_Dequantize( EA, ldType, ldScale ); + rPS1(_inst.RS) = Helper_Dequantize( EA+c, ldType, ldScale ); + } + else + { + rPS0(_inst.RS) = Helper_Dequantize( EA, ldType, ldScale ); + rPS1(_inst.RS) = 1.0f; + } +} + +void psq_stx(UGeckoInstruction _inst) +{ + const UGQR gqr(rSPR(SPR_GQR0 + _inst.Ix)); + const EQuantizeType stType = static_cast(gqr.ST_TYPE); + const unsigned int stScale = gqr.ST_SCALE; + const u32 EA = _inst.RA ? (m_GPR[_inst.RA] + m_GPR[_inst.RB]) : m_GPR[_inst.RB]; + + int c = 4; + if ((stType == 4) || (stType == 6)) c = 0x1; + if ((stType == 5) || (stType == 7)) c = 0x2; + + if (_inst.Wx == 0) + { + Helper_Quantize(EA, (float)rPS0(_inst.RS), stType, stScale); + Helper_Quantize(EA+c, (float)rPS1(_inst.RS), stType, stScale); + } + else + { + Helper_Quantize(EA, (float)rPS0(_inst.RS), stType, stScale); + } +} + +void psq_lux(UGeckoInstruction _inst) +{ + const UGQR gqr(rSPR(SPR_GQR0 + _inst.Ix)); + const EQuantizeType ldType = static_cast(gqr.LD_TYPE); + const unsigned int ldScale = gqr.LD_SCALE; + const u32 EA = m_GPR[_inst.RA] + m_GPR[_inst.RB]; + + int c = 4; + if ((ldType == 4) || (ldType == 6)) c = 0x1; + if ((ldType == 5) || (ldType == 7)) c = 0x2; + + if (_inst.Wx == 0) + { + rPS0(_inst.RS) = Helper_Dequantize( EA, ldType, ldScale ); + rPS1(_inst.RS) = Helper_Dequantize( EA+c, ldType, ldScale ); + } + else + { + rPS0(_inst.RS) = Helper_Dequantize( EA, ldType, ldScale ); + rPS1(_inst.RS) = 1.0f; + } + m_GPR[_inst.RA] = EA; +} + +void psq_stux(UGeckoInstruction _inst) +{ + const UGQR gqr(rSPR(SPR_GQR0 + _inst.Ix)); + const EQuantizeType stType = static_cast(gqr.ST_TYPE); + const unsigned int stScale = gqr.ST_SCALE; + const u32 EA = m_GPR[_inst.RA] + m_GPR[_inst.RB]; + + int c = 4; + if ((stType == 4) || (stType == 6)) c = 0x1; + if ((stType == 5) || (stType == 7)) c = 0x2; + + if (_inst.Wx == 0) + { + Helper_Quantize(EA, (float)rPS0(_inst.RS), stType, stScale); + Helper_Quantize(EA+c, (float)rPS1(_inst.RS), stType, stScale); + } + else + { + Helper_Quantize(EA, (float)rPS0(_inst.RS), stType, stScale); + } + m_GPR[_inst.RA] = EA; + +} // namespace======= } diff --git a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_Paired.cpp b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_Paired.cpp index 89235d53ad..46ff9f45c3 100644 --- a/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_Paired.cpp +++ b/Source/Core/Core/Src/PowerPC/Interpreter/Interpreter_Paired.cpp @@ -1,261 +1,261 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include -#include "Interpreter.h" -#include "../../HW/Memmap.h" - -namespace Interpreter -{ - -// These "binary instructions" do not alter FPSCR. -void ps_sel(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = static_cast((rPS0(_inst.FA) >= -0.0) ? rPS0(_inst.FC) : rPS0(_inst.FB)); - rPS1(_inst.FD) = static_cast((rPS1(_inst.FA) >= -0.0) ? rPS1(_inst.FC) : rPS1(_inst.FB)); -} - -void ps_neg(UGeckoInstruction _inst) -{ - riPS0(_inst.FD) = riPS0(_inst.FB) ^ (1ULL << 63); - riPS1(_inst.FD) = riPS1(_inst.FB) ^ (1ULL << 63); -} - -void ps_mr(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = rPS0(_inst.FB); - rPS1(_inst.FD) = rPS1(_inst.FB); -} - -void ps_nabs(UGeckoInstruction _inst) -{ - riPS0(_inst.FD) = riPS0(_inst.FB) | (1ULL << 63); - riPS1(_inst.FD) = riPS1(_inst.FB) | (1ULL << 63); -} - -void ps_abs(UGeckoInstruction _inst) -{ - riPS0(_inst.FD) = riPS0(_inst.FB) &~ (1ULL << 63); - riPS1(_inst.FD) = riPS1(_inst.FB) &~ (1ULL << 63); -} - -// These are just moves, double is OK. -void ps_merge00(UGeckoInstruction _inst) -{ - double p0 = rPS0(_inst.FA); - double p1 = rPS0(_inst.FB); - rPS0(_inst.FD) = p0; - rPS1(_inst.FD) = p1; -} - -void ps_merge01(UGeckoInstruction _inst) -{ - double p0 = rPS0(_inst.FA); - double p1 = rPS1(_inst.FB); - rPS0(_inst.FD) = p0; - rPS1(_inst.FD) = p1; -} - -void ps_merge10(UGeckoInstruction _inst) -{ - double p0 = rPS1(_inst.FA); - double p1 = rPS0(_inst.FB); - rPS0(_inst.FD) = p0; - rPS1(_inst.FD) = p1; -} - -void ps_merge11(UGeckoInstruction _inst) -{ - double p0 = rPS1(_inst.FA); - double p1 = rPS1(_inst.FB); - rPS0(_inst.FD) = p0; - rPS1(_inst.FD) = p1; -} - - -// From here on, the real deal. - -void ps_div(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = static_cast(rPS0(_inst.FA) / rPS0(_inst.FB)); - rPS1(_inst.FD) = static_cast(rPS1(_inst.FA) / rPS1(_inst.FB)); - FPSCR.FI = 0; - if (fabs(rPS0(_inst.FB)) == 0.0) { - FPSCR.ZX = 1; - } -} - -void ps_sub(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = static_cast(rPS0(_inst.FA) - rPS0(_inst.FB)); - rPS1(_inst.FD) = static_cast(rPS1(_inst.FA) - rPS1(_inst.FB)); -} - -void ps_add(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = static_cast(rPS0(_inst.FA) + rPS0(_inst.FB)); - rPS1(_inst.FD) = static_cast(rPS1(_inst.FA) + rPS1(_inst.FB)); -} - -void ps_res(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = 1.0f / static_cast(rPS0(_inst.FB)); - rPS1(_inst.FD) = 1.0f / static_cast(rPS1(_inst.FB)); -} - -void ps_mul(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = static_cast(rPS0(_inst.FA) * rPS0(_inst.FC)); - rPS1(_inst.FD) = static_cast(rPS1(_inst.FA) * rPS1(_inst.FC)); -} - -void ps_rsqrte(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = static_cast(1.0f / sqrtf((float)rPS0(_inst.FB))); - rPS1(_inst.FD) = static_cast(1.0f / sqrtf((float)rPS1(_inst.FB))); - if (fabs(rPS0(_inst.FB)) == 0.0) { - FPSCR.ZX = 1; - } -} - -void ps_msub(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = static_cast((rPS0(_inst.FA) * rPS0(_inst.FC)) - rPS0(_inst.FB)); - rPS1(_inst.FD) = static_cast((rPS1(_inst.FA) * rPS1(_inst.FC)) - rPS1(_inst.FB)); -} - -void ps_madd(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = static_cast((rPS0(_inst.FA) * rPS0(_inst.FC)) + rPS0(_inst.FB)); - rPS1(_inst.FD) = static_cast((rPS1(_inst.FA) * rPS1(_inst.FC)) + rPS1(_inst.FB)); -} - -void ps_nmsub(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = static_cast(-(rPS0(_inst.FA) * rPS0(_inst.FC) - rPS0(_inst.FB))); - rPS1(_inst.FD) = static_cast(-(rPS1(_inst.FA) * rPS1(_inst.FC) - rPS1(_inst.FB))); -} - -void ps_nmadd(UGeckoInstruction _inst) -{ - rPS0(_inst.FD) = static_cast(-(rPS0(_inst.FA) * rPS0(_inst.FC) + rPS0(_inst.FB))); - rPS1(_inst.FD) = static_cast(-(rPS1(_inst.FA) * rPS1(_inst.FC) + rPS1(_inst.FB))); -} - -void ps_sum0(UGeckoInstruction _inst) -{ - double p0 = (float)(rPS0(_inst.FA) + rPS1(_inst.FB)); - double p1 = (float)(rPS1(_inst.FC)); - rPS0(_inst.FD) = p0; - rPS1(_inst.FD) = p1; -} - -void ps_sum1(UGeckoInstruction _inst) -{ - double p0 = rPS0(_inst.FC); - double p1 = rPS0(_inst.FA) + rPS1(_inst.FB); - rPS0(_inst.FD) = p0; - rPS1(_inst.FD) = p1; -} - -void ps_muls0(UGeckoInstruction _inst) -{ - double p0 = rPS0(_inst.FA) * rPS0(_inst.FC); - double p1 = rPS1(_inst.FA) * rPS0(_inst.FC); - rPS0(_inst.FD) = p0; - rPS1(_inst.FD) = p1; -} - -void ps_muls1(UGeckoInstruction _inst) -{ - double p0 = rPS0(_inst.FA) * rPS1(_inst.FC); - double p1 = rPS1(_inst.FA) * rPS1(_inst.FC); - rPS0(_inst.FD) = p0; - rPS1(_inst.FD) = p1; -} - -void ps_madds0(UGeckoInstruction _inst) -{ - double p0 = (rPS0(_inst.FA) * rPS0(_inst.FC)) + rPS0(_inst.FB); - double p1 = (rPS1(_inst.FA) * rPS0(_inst.FC)) + rPS1(_inst.FB); - rPS0(_inst.FD) = p0; - rPS1(_inst.FD) = p1; -} - -void ps_madds1(UGeckoInstruction _inst) -{ - double p0 = (rPS0(_inst.FA) * rPS1(_inst.FC)) + rPS0(_inst.FB); - double p1 = (rPS1(_inst.FA) * rPS1(_inst.FC)) + rPS1(_inst.FB); - rPS0(_inst.FD) = p0; - rPS1(_inst.FD) = p1; -} - -void ps_cmpu0(UGeckoInstruction _inst) -{ - double fa = rPS0(_inst.FA); - double fb = rPS0(_inst.FB); - int compareResult; - if (fa < fb) compareResult = 8; - else if (fa > fb) compareResult = 4; - else compareResult = 2; - SetCRField(_inst.CRFD, compareResult); -} - -void ps_cmpo0(UGeckoInstruction _inst) -{ - // for now HACK - ps_cmpu0(_inst); -} - -void ps_cmpu1(UGeckoInstruction _inst) -{ - double fa = rPS1(_inst.FA); - double fb = rPS1(_inst.FB); - int compareResult; - if (fa < fb) compareResult = 8; - else if (fa > fb) compareResult = 4; - else compareResult = 2; - - SetCRField(_inst.CRFD, compareResult); -} - -void ps_cmpo1(UGeckoInstruction _inst) -{ - // for now HACK - ps_cmpu1(_inst); -} - -// __________________________________________________________________________________________________ -// dcbz_l -// TODO(ector) check docs -void dcbz_l(UGeckoInstruction _inst) -{ - // This is supposed to allocate a cache line in the locked cache. Not entirely sure how - // this is visible to the rest of the world. For now, we ignore it. - /* - addr_t ea = Helper_Get_EA(_inst); - - u32 blockStart = ea & (~(CACHEBLOCKSIZE-1)); - u32 blockEnd = blockStart + CACHEBLOCKSIZE; - - //FAKE: clear memory instead of clearing the cache block - for (int i=blockStart; i +#include "Interpreter.h" +#include "../../HW/Memmap.h" + +namespace Interpreter +{ + +// These "binary instructions" do not alter FPSCR. +void ps_sel(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = static_cast((rPS0(_inst.FA) >= -0.0) ? rPS0(_inst.FC) : rPS0(_inst.FB)); + rPS1(_inst.FD) = static_cast((rPS1(_inst.FA) >= -0.0) ? rPS1(_inst.FC) : rPS1(_inst.FB)); +} + +void ps_neg(UGeckoInstruction _inst) +{ + riPS0(_inst.FD) = riPS0(_inst.FB) ^ (1ULL << 63); + riPS1(_inst.FD) = riPS1(_inst.FB) ^ (1ULL << 63); +} + +void ps_mr(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = rPS0(_inst.FB); + rPS1(_inst.FD) = rPS1(_inst.FB); +} + +void ps_nabs(UGeckoInstruction _inst) +{ + riPS0(_inst.FD) = riPS0(_inst.FB) | (1ULL << 63); + riPS1(_inst.FD) = riPS1(_inst.FB) | (1ULL << 63); +} + +void ps_abs(UGeckoInstruction _inst) +{ + riPS0(_inst.FD) = riPS0(_inst.FB) &~ (1ULL << 63); + riPS1(_inst.FD) = riPS1(_inst.FB) &~ (1ULL << 63); +} + +// These are just moves, double is OK. +void ps_merge00(UGeckoInstruction _inst) +{ + double p0 = rPS0(_inst.FA); + double p1 = rPS0(_inst.FB); + rPS0(_inst.FD) = p0; + rPS1(_inst.FD) = p1; +} + +void ps_merge01(UGeckoInstruction _inst) +{ + double p0 = rPS0(_inst.FA); + double p1 = rPS1(_inst.FB); + rPS0(_inst.FD) = p0; + rPS1(_inst.FD) = p1; +} + +void ps_merge10(UGeckoInstruction _inst) +{ + double p0 = rPS1(_inst.FA); + double p1 = rPS0(_inst.FB); + rPS0(_inst.FD) = p0; + rPS1(_inst.FD) = p1; +} + +void ps_merge11(UGeckoInstruction _inst) +{ + double p0 = rPS1(_inst.FA); + double p1 = rPS1(_inst.FB); + rPS0(_inst.FD) = p0; + rPS1(_inst.FD) = p1; +} + + +// From here on, the real deal. + +void ps_div(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = static_cast(rPS0(_inst.FA) / rPS0(_inst.FB)); + rPS1(_inst.FD) = static_cast(rPS1(_inst.FA) / rPS1(_inst.FB)); + FPSCR.FI = 0; + if (fabs(rPS0(_inst.FB)) == 0.0) { + FPSCR.ZX = 1; + } +} + +void ps_sub(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = static_cast(rPS0(_inst.FA) - rPS0(_inst.FB)); + rPS1(_inst.FD) = static_cast(rPS1(_inst.FA) - rPS1(_inst.FB)); +} + +void ps_add(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = static_cast(rPS0(_inst.FA) + rPS0(_inst.FB)); + rPS1(_inst.FD) = static_cast(rPS1(_inst.FA) + rPS1(_inst.FB)); +} + +void ps_res(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = 1.0f / static_cast(rPS0(_inst.FB)); + rPS1(_inst.FD) = 1.0f / static_cast(rPS1(_inst.FB)); +} + +void ps_mul(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = static_cast(rPS0(_inst.FA) * rPS0(_inst.FC)); + rPS1(_inst.FD) = static_cast(rPS1(_inst.FA) * rPS1(_inst.FC)); +} + +void ps_rsqrte(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = static_cast(1.0f / sqrtf((float)rPS0(_inst.FB))); + rPS1(_inst.FD) = static_cast(1.0f / sqrtf((float)rPS1(_inst.FB))); + if (fabs(rPS0(_inst.FB)) == 0.0) { + FPSCR.ZX = 1; + } +} + +void ps_msub(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = static_cast((rPS0(_inst.FA) * rPS0(_inst.FC)) - rPS0(_inst.FB)); + rPS1(_inst.FD) = static_cast((rPS1(_inst.FA) * rPS1(_inst.FC)) - rPS1(_inst.FB)); +} + +void ps_madd(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = static_cast((rPS0(_inst.FA) * rPS0(_inst.FC)) + rPS0(_inst.FB)); + rPS1(_inst.FD) = static_cast((rPS1(_inst.FA) * rPS1(_inst.FC)) + rPS1(_inst.FB)); +} + +void ps_nmsub(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = static_cast(-(rPS0(_inst.FA) * rPS0(_inst.FC) - rPS0(_inst.FB))); + rPS1(_inst.FD) = static_cast(-(rPS1(_inst.FA) * rPS1(_inst.FC) - rPS1(_inst.FB))); +} + +void ps_nmadd(UGeckoInstruction _inst) +{ + rPS0(_inst.FD) = static_cast(-(rPS0(_inst.FA) * rPS0(_inst.FC) + rPS0(_inst.FB))); + rPS1(_inst.FD) = static_cast(-(rPS1(_inst.FA) * rPS1(_inst.FC) + rPS1(_inst.FB))); +} + +void ps_sum0(UGeckoInstruction _inst) +{ + double p0 = (float)(rPS0(_inst.FA) + rPS1(_inst.FB)); + double p1 = (float)(rPS1(_inst.FC)); + rPS0(_inst.FD) = p0; + rPS1(_inst.FD) = p1; +} + +void ps_sum1(UGeckoInstruction _inst) +{ + double p0 = rPS0(_inst.FC); + double p1 = rPS0(_inst.FA) + rPS1(_inst.FB); + rPS0(_inst.FD) = p0; + rPS1(_inst.FD) = p1; +} + +void ps_muls0(UGeckoInstruction _inst) +{ + double p0 = rPS0(_inst.FA) * rPS0(_inst.FC); + double p1 = rPS1(_inst.FA) * rPS0(_inst.FC); + rPS0(_inst.FD) = p0; + rPS1(_inst.FD) = p1; +} + +void ps_muls1(UGeckoInstruction _inst) +{ + double p0 = rPS0(_inst.FA) * rPS1(_inst.FC); + double p1 = rPS1(_inst.FA) * rPS1(_inst.FC); + rPS0(_inst.FD) = p0; + rPS1(_inst.FD) = p1; +} + +void ps_madds0(UGeckoInstruction _inst) +{ + double p0 = (rPS0(_inst.FA) * rPS0(_inst.FC)) + rPS0(_inst.FB); + double p1 = (rPS1(_inst.FA) * rPS0(_inst.FC)) + rPS1(_inst.FB); + rPS0(_inst.FD) = p0; + rPS1(_inst.FD) = p1; +} + +void ps_madds1(UGeckoInstruction _inst) +{ + double p0 = (rPS0(_inst.FA) * rPS1(_inst.FC)) + rPS0(_inst.FB); + double p1 = (rPS1(_inst.FA) * rPS1(_inst.FC)) + rPS1(_inst.FB); + rPS0(_inst.FD) = p0; + rPS1(_inst.FD) = p1; +} + +void ps_cmpu0(UGeckoInstruction _inst) +{ + double fa = rPS0(_inst.FA); + double fb = rPS0(_inst.FB); + int compareResult; + if (fa < fb) compareResult = 8; + else if (fa > fb) compareResult = 4; + else compareResult = 2; + SetCRField(_inst.CRFD, compareResult); +} + +void ps_cmpo0(UGeckoInstruction _inst) +{ + // for now HACK + ps_cmpu0(_inst); +} + +void ps_cmpu1(UGeckoInstruction _inst) +{ + double fa = rPS1(_inst.FA); + double fb = rPS1(_inst.FB); + int compareResult; + if (fa < fb) compareResult = 8; + else if (fa > fb) compareResult = 4; + else compareResult = 2; + + SetCRField(_inst.CRFD, compareResult); +} + +void ps_cmpo1(UGeckoInstruction _inst) +{ + // for now HACK + ps_cmpu1(_inst); +} + +// __________________________________________________________________________________________________ +// dcbz_l +// TODO(ector) check docs +void dcbz_l(UGeckoInstruction _inst) +{ + // This is supposed to allocate a cache line in the locked cache. Not entirely sure how + // this is visible to the rest of the world. For now, we ignore it. + /* + addr_t ea = Helper_Get_EA(_inst); + + u32 blockStart = ea & (~(CACHEBLOCKSIZE-1)); + u32 blockEnd = blockStart + CACHEBLOCKSIZE; + + //FAKE: clear memory instead of clearing the cache block + for (int i=blockStart; i -#ifdef _WIN32 -#define _interlockedbittestandset workaround_ms_header_bug_platform_sdk6_set -#define _interlockedbittestandreset workaround_ms_header_bug_platform_sdk6_reset -#define _interlockedbittestandset64 workaround_ms_header_bug_platform_sdk6_set64 -#define _interlockedbittestandreset64 workaround_ms_header_bug_platform_sdk6_reset64 -#include -#undef _interlockedbittestandset -#undef _interlockedbittestandreset -#undef _interlockedbittestandset64 -#undef _interlockedbittestandreset64 -#else -static const unsigned short FPU_ROUND_NEAR = 0 << 10; -static const unsigned short FPU_ROUND_DOWN = 1 << 10; -static const unsigned short FPU_ROUND_UP = 2 << 10; -static const unsigned short FPU_ROUND_CHOP = 3 << 10; -static const unsigned short FPU_ROUND_MASK = 3 << 10; -#include -#endif - -#include "../../CoreTiming.h" -#include "../../HW/Memmap.h" -#include "../../HW/GPFifo.h" -#include "../../HW/SystemTimers.h" -#include "../../Core.h" -#include "Interpreter.h" - -/* - -Most of these are together with fctiwx -mffsx: 800c3624 -mffsx: 80043c98 -mffsx: 8003dd48 -mffsx: 8003dd9c -mffsx: 80036608 -mffsx: 80036650 (huh?) - -*/ -// TODO(ector): More proper handling of SSE state. -// That is, set rounding mode etc when entering jit code or the interpreter loop -// Restore rounding mode when calling anything external - -namespace Interpreter -{ - -void UpdateSSEState() -{ - u32 csr = _mm_getcsr(); - - const int ssetable[4] = - { - 0, - 3, - 2, - 1, - }; - csr = csr & 0x9FFF; - csr |= ssetable[FPSCR.RN] << 13; - - // Also handle denormals as zero (FZ + DAZ) - csr &= ~0x8020; - - // SETTING FTZ+DAZ KILLS BEYOND GOOD AND EVIL - //if (daz) - // csr |= 0x20; // Only set DAZ //0x8020; - - _mm_setcsr(csr); -} - -void RestoreSSEState() -{ - // A reasonable default - _mm_setcsr(0x1fa0); -} - -void UpdateFPSCR(UReg_FPSCR fp) -{ - // Set FPU rounding mode to mimic the PowerPC's -#ifdef _M_IX86 - // This shouldn't really be needed anymore since we use SSE -#ifdef _WIN32 - const int table[4] = - { - _RC_NEAR, - _RC_CHOP, - _RC_UP, - _RC_DOWN - }; - _set_controlfp(_MCW_RC, table[fp.RN]); -#else - const unsigned short table[4] = - { - FPU_ROUND_NEAR, - FPU_ROUND_CHOP, - FPU_ROUND_UP, - FPU_ROUND_DOWN - }; - unsigned short mode; - asm ("fstcw %0" : : "m" (mode)); - mode = (mode & ~FPU_ROUND_MASK) | table[fp.RN]; - asm ("fldcw %0" : : "m" (mode)); -#endif -#endif - if (fp.VE || fp.OE || fp.UE || fp.ZE || fp.XE) - { - // PanicAlert("FPSCR - exceptions enabled. Please report."); - // Pokemon Colosseum does this. Gah. - } - - // Also corresponding SSE rounding mode setting - UpdateSSEState(); -} - -void mcrfs(UGeckoInstruction _inst) -{ - u32 fpflags = ((FPSCR.Hex >> (4*(_inst.CRFS))) & 0xF); - switch (_inst.CRFS) { - case 0: - FPSCR.FX = 0; - FPSCR.OX = 0; - break; - case 1: - FPSCR.UX = 0; - FPSCR.ZX = 0; - FPSCR.XX = 0; - FPSCR.VXSNAN = 0; - break; - case 2: - FPSCR.VXISI = 0; - FPSCR.VXIDI = 0; - FPSCR.VXZDZ = 0; - FPSCR.VXIMZ = 0; - break; - case 3: - FPSCR.VXVC = 0; - break; - case 5: - FPSCR.VXSOFT = 0; - FPSCR.VXSQRT = 0; - FPSCR.VXCVI = 0; - break; - } - SetCRField(_inst.CRFD, fpflags); - UpdateFPSCR(FPSCR); -} - -#define MXCSR_IE 1 -#define MXCSR_DE 2 // denormal -#define MXCSR_ZE 4 // divide by zero, sticky -#define MXCSR_OE 8 // overflow -#define MXCSR_UE 16 // underflow -#define MXCSR_PE 32 // precision -#define MXCSR_DAZ 64 -#define MXCSR_IM 128 -#define MXCSR_DM 256 -#define MXCSR_ZM 512 -#define MXCSR_OM 1024 -#define MXCSR_UM 2048 -#define MXCSR_PM 4096 -#define MXCSR_ROUND (16384|8192) -#define MXCSR_FLUSH 32768 - -void mffsx(UGeckoInstruction _inst) -{ - // load from FPSCR - // This may or may not be accurate - but better than nothing, I guess - // TODO(ector): grab all overflow flags etc and set them in FPSCR - - riPS0(_inst.FD) = (u64)FPSCR.Hex; - if (_inst.Rc) PanicAlert("mffsx: inst_.Rc"); -} - -void mtfsb0x(UGeckoInstruction _inst) -{ - FPSCR.Hex &= (~(0x80000000 >> _inst.CRBD)); - UpdateFPSCR(FPSCR); - if (_inst.Rc) PanicAlert("mtfsb0x: inst_.Rc"); -} - -void mtfsb1x(UGeckoInstruction _inst) -{ - FPSCR.Hex |= 0x80000000 >> _inst.CRBD; - UpdateFPSCR(FPSCR); - if (_inst.Rc) PanicAlert("mtfsb1x: inst_.Rc"); -} - -void mtfsfix(UGeckoInstruction _inst) -{ - u32 mask = (0xF0000000 >> (4 * _inst.CRFD)); - u32 imm = (_inst.hex << 16) & 0xF0000000; - FPSCR.Hex = (FPSCR.Hex & ~mask) | (imm >> (4 * _inst.CRFD)); - UpdateFPSCR(FPSCR); - if (_inst.Rc) PanicAlert("mtfsfix: inst_.Rc"); -} - -void mtfsfx(UGeckoInstruction _inst) -{ - u32 fm = _inst.FM; - u32 m = 0; - for (int i = 0; i < 8; i++) { //7?? todo check - if (fm & (1 << i)) - m |= (0xf << (i*4)); - } - - FPSCR.Hex = (FPSCR.Hex & ~m) | ((u32)(riPS0(_inst.FB)) & m); - UpdateFPSCR(FPSCR); - if (_inst.Rc) PanicAlert("mtfsfx: inst_.Rc"); -} - -void mcrxr(UGeckoInstruction _inst) -{ - SetCRField(_inst.CRFD, XER.Hex >> 28); - XER.Hex &= ~0xF0000000; // clear 0-3 -} - -void mfcr(UGeckoInstruction _inst) -{ - m_GPR[_inst.RD] = GetCR(); -} - -void mtcrf(UGeckoInstruction _inst) -{ - u32 mask = 0; - u32 crm = _inst.CRM; - if (crm == 0xFF) { - SetCR(m_GPR[_inst.RS]); - } else { - //TODO: use lookup table? probably not worth it - for (int i = 0; i < 8; i++) { - if (crm & (1 << i)) - mask |= 0xF << (i*4); - } - SetCR((GetCR() & ~mask) | (m_GPR[_inst.RS] & mask)); - } -} - - -void mfmsr(UGeckoInstruction _inst) -{ - //Privileged? - m_GPR[_inst.RD] = MSR; -} - -// segment register -// We can probably ignore all this junk -void mfsr(UGeckoInstruction _inst) -{ - m_GPR[_inst.RD] = PowerPC::ppcState.sr[_inst.SR]; -} - -// segment register -void mfsrin(UGeckoInstruction _inst) -{ - int index = m_GPR[_inst.RB] & 0xF; - m_GPR[_inst.RD] = PowerPC::ppcState.sr[index]; -} - -void mtmsr(UGeckoInstruction _inst) -{ - //Privileged? - MSR = m_GPR[_inst.RS]; -} - -// segment register -void mtsr(UGeckoInstruction _inst) -{ - PowerPC::ppcState.sr[_inst.SR] = m_GPR[_inst.RS]; -} - -// segment register -void mtsrin(UGeckoInstruction _inst) -{ - int index = m_GPR[_inst.RB] & 0xF; - PowerPC::ppcState.sr[index] = m_GPR[_inst.RS]; -} - -void mftb(UGeckoInstruction _inst) -{ - int iIndex = (_inst.TBR >> 5) | ((_inst.TBR & 0x1F) << 5); - if (iIndex == 268) m_GPR[_inst.RD] = TL; - else if (iIndex == 269) m_GPR[_inst.RD] = TU; - else _dbg_assert_(GEKKO,0); -} - - -void mfspr(UGeckoInstruction _inst) -{ - u32 iIndex = ((_inst.SPR & 0x1F) << 5) + ((_inst.SPR >> 5) & 0x1F); - - //TODO - check processor privilege level - many of these require privilege - //XER LR CTR are the only ones available in user mode, time base can be read too. - //Gamecube games always run in superuser mode, but hey.... - - switch (iIndex) - { - //case SPR_DEC: - // MessageBox(NULL, "Read from DEC", "????", MB_OK); - // break; - case SPR_WPAR: - { - // If wpar_empty ever is false, Paper Mario hangs. Strange. - bool wpar_empty = true; //GPFifo::IsEmpty(); - if (!wpar_empty) - rSPR(iIndex) |= 1; // BNE = buffer not empty - else - rSPR(iIndex) &= ~1; - } - break; - } - m_GPR[_inst.RD] = rSPR(iIndex); -} - -void mtspr(UGeckoInstruction _inst) -{ - u32 iIndex = (_inst.SPRU << 5) | (_inst.SPRL & 0x1F); - u32 oldValue = rSPR(iIndex); - rSPR(iIndex) = m_GPR[_inst.RD]; - - //TODO - check processor privilege level - many of these require privilege - //XER LR CTR are the only ones available in user mode, time base can be read too. - //Gamecube games always run in superuser mode, but hey.... - - //Our DMA emulation is highly inaccurate - instead of properly emulating the queue - //and so on, we simply make all DMA:s complete instantaneously. - - switch(iIndex) - { - case SPR_TL: - case SPR_TU: - PanicAlert("Illegal Write to TL/TU"); - break; - - case SPR_TL_W: - TL = m_GPR[_inst.RD]; - break; - - case SPR_TU_W: - TU = m_GPR[_inst.RD]; - break; - - case SPR_HID2: // HID2 - { - UReg_HID2 old_hid2; - old_hid2.Hex = oldValue; - - if (HID2.PSE == 0) - PanicAlert("WARNING: PSE in HID2 isnt set"); - - // bool WriteGatherPipeEnable = (bool)HID2.WPE; //TODO? - // bool LockedCacheEnable = (bool)HID2.LCE; - // int DMAQueueLength = HID2.DMAQL; // Ignore - our DMA:s are instantaneous - // bool PairedSingleEnable = HID2.PSE; - // bool QuantizeEnable = HID2.LSQE; - //TODO(ector): Protect LC memory if LCE is false. - //TODO(ector): Honor PSE. - - //_assert_msg_(GEKKO, WriteGatherPipeEnable, "Write gather pipe not enabled!"); - //if ((HID2.PSE == 0)) - // MessageBox(NULL, "PSE in HID2 is set", "Warning", MB_OK); - } - break; - - case SPR_WPAR: - _assert_msg_(GEKKO, m_GPR[_inst.RD] == 0x0C008000, "Gather pipe @ %08x"); - GPFifo::ResetGatherPipe(); - break; - - case SPR_GQR0: - case SPR_GQR0 + 1: - case SPR_GQR0 + 2: - case SPR_GQR0 + 3: - case SPR_GQR0 + 4: - case SPR_GQR0 + 5: - case SPR_GQR0 + 6: - case SPR_GQR0 + 7: - break; - - case SPR_DMAL: - // Locked cache<->Memory DMA - // Total fake, we ignore that DMAs take time. - if (DMAL.DMA_T) - { - u32 dwMemAddress = DMAU.MEM_ADDR << 5; - u32 dwCacheAddress = DMAL.LC_ADDR << 5; - u32 iLength = ((DMAU.DMA_LEN_U << 2) | DMAL.DMA_LEN_L); - if (iLength == 0) - iLength = 128; - if (DMAL.DMA_LD) - Memory::DMA_MemoryToLC(dwCacheAddress, dwMemAddress, iLength); - else - Memory::DMA_LCToMemory(dwMemAddress, dwCacheAddress, iLength); - } - break; - - case SPR_DEC: - if (!(oldValue >> 31) && (m_GPR[_inst.RD]>>31)) //top bit from 0 to 1 - { - PanicAlert("Interesting - Software triggered Decrementer exception"); - PowerPC::ppcState.Exceptions |= EXCEPTION_DECREMENTER; - } - else - { - SystemTimers::DecrementerSet(); - } - break; - - case SPR_SDR: - Memory::SDRUpdated(); - break; - } -} - -void crand(UGeckoInstruction _inst) -{ - u32 cr = GetCR(); - u32 a = cr << _inst.CRBA; - u32 b = cr << _inst.CRBB; - u32 d = ((a & b) & 0x80000000) >> _inst.CRBD; - SetCR(d | (cr & ~(0x80000000 >> _inst.CRBD))); -} - -void crandc(UGeckoInstruction _inst) -{ - u32 cr = GetCR(); - u32 a = cr << _inst.CRBA; - u32 b = cr << _inst.CRBB; - u32 d = ((a & ~b) & 0x80000000) >> _inst.CRBD; - SetCR(d | (cr & ~(0x80000000 >> _inst.CRBD))); -} - - -void creqv(UGeckoInstruction _inst) -{ - u32 cr = GetCR(); - u32 a = cr << _inst.CRBA; - u32 b = cr << _inst.CRBB; - u32 d = ((~(a ^ b)) & 0x80000000) >> _inst.CRBD; - SetCR(d | (cr & ~(0x80000000 >> _inst.CRBD))); -} - -void crnand(UGeckoInstruction _inst) -{ - u32 cr = GetCR(); - u32 a = cr << _inst.CRBA; - u32 b = cr << _inst.CRBB; - u32 d = ((~(a & b)) & 0x80000000) >>_inst.CRBD; - SetCR(d | (cr & ~(0x80000000 >> _inst.CRBD))); -} - -void crnor(UGeckoInstruction _inst) -{ - u32 cr = GetCR(); - u32 a = cr << _inst.CRBA; - u32 b = cr << _inst.CRBB; - u32 d = ((~(a | b)) & 0x80000000) >> _inst.CRBD; - SetCR(d | (cr & ~(0x80000000 >> _inst.CRBD))); -} - -void cror(UGeckoInstruction _inst) -{ - u32 cr = GetCR(); - u32 a = cr << _inst.CRBA; - u32 b = cr << _inst.CRBB; - u32 d = ((a | b) & 0x80000000) >> _inst.CRBD; - SetCR(d | (cr & ~(0x80000000 >> _inst.CRBD))); -} - -void crorc(UGeckoInstruction _inst) -{ - u32 cr = GetCR(); - u32 a = cr << _inst.CRBA; - u32 b = cr << _inst.CRBB; - u32 d = ((a | ~b) & 0x80000000) >> _inst.CRBD; - SetCR(d | (cr & ~(0x80000000 >> _inst.CRBD))); -} - -void crxor(UGeckoInstruction _inst) -{ - u32 cr = GetCR(); - u32 a = cr << _inst.CRBA; - u32 b = cr << _inst.CRBB; - u32 d = ((a ^ b) & 0x80000000) >> _inst.CRBD; - SetCR(d | (cr & ~(0x80000000 >> _inst.CRBD))); -} - -void mcrf(UGeckoInstruction _inst) -{ - u32 cr = GetCR(); - u32 crmask = ~(0xF0000000 >> (4*_inst.CRFD)); - u32 flags = ((cr << (4*_inst.CRFS)) & 0xF0000000) >> (4*_inst.CRFD); - SetCR((cr & crmask) | flags); -} - -void isync(UGeckoInstruction _inst) -{ - //shouldnt do anything -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#ifdef _WIN32 +#define _interlockedbittestandset workaround_ms_header_bug_platform_sdk6_set +#define _interlockedbittestandreset workaround_ms_header_bug_platform_sdk6_reset +#define _interlockedbittestandset64 workaround_ms_header_bug_platform_sdk6_set64 +#define _interlockedbittestandreset64 workaround_ms_header_bug_platform_sdk6_reset64 +#include +#undef _interlockedbittestandset +#undef _interlockedbittestandreset +#undef _interlockedbittestandset64 +#undef _interlockedbittestandreset64 +#else +static const unsigned short FPU_ROUND_NEAR = 0 << 10; +static const unsigned short FPU_ROUND_DOWN = 1 << 10; +static const unsigned short FPU_ROUND_UP = 2 << 10; +static const unsigned short FPU_ROUND_CHOP = 3 << 10; +static const unsigned short FPU_ROUND_MASK = 3 << 10; +#include +#endif + +#include "../../CoreTiming.h" +#include "../../HW/Memmap.h" +#include "../../HW/GPFifo.h" +#include "../../HW/SystemTimers.h" +#include "../../Core.h" +#include "Interpreter.h" + +/* + +Most of these are together with fctiwx +mffsx: 800c3624 +mffsx: 80043c98 +mffsx: 8003dd48 +mffsx: 8003dd9c +mffsx: 80036608 +mffsx: 80036650 (huh?) + +*/ +// TODO(ector): More proper handling of SSE state. +// That is, set rounding mode etc when entering jit code or the interpreter loop +// Restore rounding mode when calling anything external + +namespace Interpreter +{ + +void UpdateSSEState() +{ + u32 csr = _mm_getcsr(); + + const int ssetable[4] = + { + 0, + 3, + 2, + 1, + }; + csr = csr & 0x9FFF; + csr |= ssetable[FPSCR.RN] << 13; + + // Also handle denormals as zero (FZ + DAZ) + csr &= ~0x8020; + + // SETTING FTZ+DAZ KILLS BEYOND GOOD AND EVIL + //if (daz) + // csr |= 0x20; // Only set DAZ //0x8020; + + _mm_setcsr(csr); +} + +void RestoreSSEState() +{ + // A reasonable default + _mm_setcsr(0x1fa0); +} + +void UpdateFPSCR(UReg_FPSCR fp) +{ + // Set FPU rounding mode to mimic the PowerPC's +#ifdef _M_IX86 + // This shouldn't really be needed anymore since we use SSE +#ifdef _WIN32 + const int table[4] = + { + _RC_NEAR, + _RC_CHOP, + _RC_UP, + _RC_DOWN + }; + _set_controlfp(_MCW_RC, table[fp.RN]); +#else + const unsigned short table[4] = + { + FPU_ROUND_NEAR, + FPU_ROUND_CHOP, + FPU_ROUND_UP, + FPU_ROUND_DOWN + }; + unsigned short mode; + asm ("fstcw %0" : : "m" (mode)); + mode = (mode & ~FPU_ROUND_MASK) | table[fp.RN]; + asm ("fldcw %0" : : "m" (mode)); +#endif +#endif + if (fp.VE || fp.OE || fp.UE || fp.ZE || fp.XE) + { + // PanicAlert("FPSCR - exceptions enabled. Please report."); + // Pokemon Colosseum does this. Gah. + } + + // Also corresponding SSE rounding mode setting + UpdateSSEState(); +} + +void mcrfs(UGeckoInstruction _inst) +{ + u32 fpflags = ((FPSCR.Hex >> (4*(_inst.CRFS))) & 0xF); + switch (_inst.CRFS) { + case 0: + FPSCR.FX = 0; + FPSCR.OX = 0; + break; + case 1: + FPSCR.UX = 0; + FPSCR.ZX = 0; + FPSCR.XX = 0; + FPSCR.VXSNAN = 0; + break; + case 2: + FPSCR.VXISI = 0; + FPSCR.VXIDI = 0; + FPSCR.VXZDZ = 0; + FPSCR.VXIMZ = 0; + break; + case 3: + FPSCR.VXVC = 0; + break; + case 5: + FPSCR.VXSOFT = 0; + FPSCR.VXSQRT = 0; + FPSCR.VXCVI = 0; + break; + } + SetCRField(_inst.CRFD, fpflags); + UpdateFPSCR(FPSCR); +} + +#define MXCSR_IE 1 +#define MXCSR_DE 2 // denormal +#define MXCSR_ZE 4 // divide by zero, sticky +#define MXCSR_OE 8 // overflow +#define MXCSR_UE 16 // underflow +#define MXCSR_PE 32 // precision +#define MXCSR_DAZ 64 +#define MXCSR_IM 128 +#define MXCSR_DM 256 +#define MXCSR_ZM 512 +#define MXCSR_OM 1024 +#define MXCSR_UM 2048 +#define MXCSR_PM 4096 +#define MXCSR_ROUND (16384|8192) +#define MXCSR_FLUSH 32768 + +void mffsx(UGeckoInstruction _inst) +{ + // load from FPSCR + // This may or may not be accurate - but better than nothing, I guess + // TODO(ector): grab all overflow flags etc and set them in FPSCR + + riPS0(_inst.FD) = (u64)FPSCR.Hex; + if (_inst.Rc) PanicAlert("mffsx: inst_.Rc"); +} + +void mtfsb0x(UGeckoInstruction _inst) +{ + FPSCR.Hex &= (~(0x80000000 >> _inst.CRBD)); + UpdateFPSCR(FPSCR); + if (_inst.Rc) PanicAlert("mtfsb0x: inst_.Rc"); +} + +void mtfsb1x(UGeckoInstruction _inst) +{ + FPSCR.Hex |= 0x80000000 >> _inst.CRBD; + UpdateFPSCR(FPSCR); + if (_inst.Rc) PanicAlert("mtfsb1x: inst_.Rc"); +} + +void mtfsfix(UGeckoInstruction _inst) +{ + u32 mask = (0xF0000000 >> (4 * _inst.CRFD)); + u32 imm = (_inst.hex << 16) & 0xF0000000; + FPSCR.Hex = (FPSCR.Hex & ~mask) | (imm >> (4 * _inst.CRFD)); + UpdateFPSCR(FPSCR); + if (_inst.Rc) PanicAlert("mtfsfix: inst_.Rc"); +} + +void mtfsfx(UGeckoInstruction _inst) +{ + u32 fm = _inst.FM; + u32 m = 0; + for (int i = 0; i < 8; i++) { //7?? todo check + if (fm & (1 << i)) + m |= (0xf << (i*4)); + } + + FPSCR.Hex = (FPSCR.Hex & ~m) | ((u32)(riPS0(_inst.FB)) & m); + UpdateFPSCR(FPSCR); + if (_inst.Rc) PanicAlert("mtfsfx: inst_.Rc"); +} + +void mcrxr(UGeckoInstruction _inst) +{ + SetCRField(_inst.CRFD, XER.Hex >> 28); + XER.Hex &= ~0xF0000000; // clear 0-3 +} + +void mfcr(UGeckoInstruction _inst) +{ + m_GPR[_inst.RD] = GetCR(); +} + +void mtcrf(UGeckoInstruction _inst) +{ + u32 mask = 0; + u32 crm = _inst.CRM; + if (crm == 0xFF) { + SetCR(m_GPR[_inst.RS]); + } else { + //TODO: use lookup table? probably not worth it + for (int i = 0; i < 8; i++) { + if (crm & (1 << i)) + mask |= 0xF << (i*4); + } + SetCR((GetCR() & ~mask) | (m_GPR[_inst.RS] & mask)); + } +} + + +void mfmsr(UGeckoInstruction _inst) +{ + //Privileged? + m_GPR[_inst.RD] = MSR; +} + +// segment register +// We can probably ignore all this junk +void mfsr(UGeckoInstruction _inst) +{ + m_GPR[_inst.RD] = PowerPC::ppcState.sr[_inst.SR]; +} + +// segment register +void mfsrin(UGeckoInstruction _inst) +{ + int index = m_GPR[_inst.RB] & 0xF; + m_GPR[_inst.RD] = PowerPC::ppcState.sr[index]; +} + +void mtmsr(UGeckoInstruction _inst) +{ + //Privileged? + MSR = m_GPR[_inst.RS]; +} + +// segment register +void mtsr(UGeckoInstruction _inst) +{ + PowerPC::ppcState.sr[_inst.SR] = m_GPR[_inst.RS]; +} + +// segment register +void mtsrin(UGeckoInstruction _inst) +{ + int index = m_GPR[_inst.RB] & 0xF; + PowerPC::ppcState.sr[index] = m_GPR[_inst.RS]; +} + +void mftb(UGeckoInstruction _inst) +{ + int iIndex = (_inst.TBR >> 5) | ((_inst.TBR & 0x1F) << 5); + if (iIndex == 268) m_GPR[_inst.RD] = TL; + else if (iIndex == 269) m_GPR[_inst.RD] = TU; + else _dbg_assert_(GEKKO,0); +} + + +void mfspr(UGeckoInstruction _inst) +{ + u32 iIndex = ((_inst.SPR & 0x1F) << 5) + ((_inst.SPR >> 5) & 0x1F); + + //TODO - check processor privilege level - many of these require privilege + //XER LR CTR are the only ones available in user mode, time base can be read too. + //Gamecube games always run in superuser mode, but hey.... + + switch (iIndex) + { + //case SPR_DEC: + // MessageBox(NULL, "Read from DEC", "????", MB_OK); + // break; + case SPR_WPAR: + { + // If wpar_empty ever is false, Paper Mario hangs. Strange. + bool wpar_empty = true; //GPFifo::IsEmpty(); + if (!wpar_empty) + rSPR(iIndex) |= 1; // BNE = buffer not empty + else + rSPR(iIndex) &= ~1; + } + break; + } + m_GPR[_inst.RD] = rSPR(iIndex); +} + +void mtspr(UGeckoInstruction _inst) +{ + u32 iIndex = (_inst.SPRU << 5) | (_inst.SPRL & 0x1F); + u32 oldValue = rSPR(iIndex); + rSPR(iIndex) = m_GPR[_inst.RD]; + + //TODO - check processor privilege level - many of these require privilege + //XER LR CTR are the only ones available in user mode, time base can be read too. + //Gamecube games always run in superuser mode, but hey.... + + //Our DMA emulation is highly inaccurate - instead of properly emulating the queue + //and so on, we simply make all DMA:s complete instantaneously. + + switch(iIndex) + { + case SPR_TL: + case SPR_TU: + PanicAlert("Illegal Write to TL/TU"); + break; + + case SPR_TL_W: + TL = m_GPR[_inst.RD]; + break; + + case SPR_TU_W: + TU = m_GPR[_inst.RD]; + break; + + case SPR_HID2: // HID2 + { + UReg_HID2 old_hid2; + old_hid2.Hex = oldValue; + + if (HID2.PSE == 0) + PanicAlert("WARNING: PSE in HID2 isnt set"); + + // bool WriteGatherPipeEnable = (bool)HID2.WPE; //TODO? + // bool LockedCacheEnable = (bool)HID2.LCE; + // int DMAQueueLength = HID2.DMAQL; // Ignore - our DMA:s are instantaneous + // bool PairedSingleEnable = HID2.PSE; + // bool QuantizeEnable = HID2.LSQE; + //TODO(ector): Protect LC memory if LCE is false. + //TODO(ector): Honor PSE. + + //_assert_msg_(GEKKO, WriteGatherPipeEnable, "Write gather pipe not enabled!"); + //if ((HID2.PSE == 0)) + // MessageBox(NULL, "PSE in HID2 is set", "Warning", MB_OK); + } + break; + + case SPR_WPAR: + _assert_msg_(GEKKO, m_GPR[_inst.RD] == 0x0C008000, "Gather pipe @ %08x"); + GPFifo::ResetGatherPipe(); + break; + + case SPR_GQR0: + case SPR_GQR0 + 1: + case SPR_GQR0 + 2: + case SPR_GQR0 + 3: + case SPR_GQR0 + 4: + case SPR_GQR0 + 5: + case SPR_GQR0 + 6: + case SPR_GQR0 + 7: + break; + + case SPR_DMAL: + // Locked cache<->Memory DMA + // Total fake, we ignore that DMAs take time. + if (DMAL.DMA_T) + { + u32 dwMemAddress = DMAU.MEM_ADDR << 5; + u32 dwCacheAddress = DMAL.LC_ADDR << 5; + u32 iLength = ((DMAU.DMA_LEN_U << 2) | DMAL.DMA_LEN_L); + if (iLength == 0) + iLength = 128; + if (DMAL.DMA_LD) + Memory::DMA_MemoryToLC(dwCacheAddress, dwMemAddress, iLength); + else + Memory::DMA_LCToMemory(dwMemAddress, dwCacheAddress, iLength); + } + break; + + case SPR_DEC: + if (!(oldValue >> 31) && (m_GPR[_inst.RD]>>31)) //top bit from 0 to 1 + { + PanicAlert("Interesting - Software triggered Decrementer exception"); + PowerPC::ppcState.Exceptions |= EXCEPTION_DECREMENTER; + } + else + { + SystemTimers::DecrementerSet(); + } + break; + + case SPR_SDR: + Memory::SDRUpdated(); + break; + } +} + +void crand(UGeckoInstruction _inst) +{ + u32 cr = GetCR(); + u32 a = cr << _inst.CRBA; + u32 b = cr << _inst.CRBB; + u32 d = ((a & b) & 0x80000000) >> _inst.CRBD; + SetCR(d | (cr & ~(0x80000000 >> _inst.CRBD))); +} + +void crandc(UGeckoInstruction _inst) +{ + u32 cr = GetCR(); + u32 a = cr << _inst.CRBA; + u32 b = cr << _inst.CRBB; + u32 d = ((a & ~b) & 0x80000000) >> _inst.CRBD; + SetCR(d | (cr & ~(0x80000000 >> _inst.CRBD))); +} + + +void creqv(UGeckoInstruction _inst) +{ + u32 cr = GetCR(); + u32 a = cr << _inst.CRBA; + u32 b = cr << _inst.CRBB; + u32 d = ((~(a ^ b)) & 0x80000000) >> _inst.CRBD; + SetCR(d | (cr & ~(0x80000000 >> _inst.CRBD))); +} + +void crnand(UGeckoInstruction _inst) +{ + u32 cr = GetCR(); + u32 a = cr << _inst.CRBA; + u32 b = cr << _inst.CRBB; + u32 d = ((~(a & b)) & 0x80000000) >>_inst.CRBD; + SetCR(d | (cr & ~(0x80000000 >> _inst.CRBD))); +} + +void crnor(UGeckoInstruction _inst) +{ + u32 cr = GetCR(); + u32 a = cr << _inst.CRBA; + u32 b = cr << _inst.CRBB; + u32 d = ((~(a | b)) & 0x80000000) >> _inst.CRBD; + SetCR(d | (cr & ~(0x80000000 >> _inst.CRBD))); +} + +void cror(UGeckoInstruction _inst) +{ + u32 cr = GetCR(); + u32 a = cr << _inst.CRBA; + u32 b = cr << _inst.CRBB; + u32 d = ((a | b) & 0x80000000) >> _inst.CRBD; + SetCR(d | (cr & ~(0x80000000 >> _inst.CRBD))); +} + +void crorc(UGeckoInstruction _inst) +{ + u32 cr = GetCR(); + u32 a = cr << _inst.CRBA; + u32 b = cr << _inst.CRBB; + u32 d = ((a | ~b) & 0x80000000) >> _inst.CRBD; + SetCR(d | (cr & ~(0x80000000 >> _inst.CRBD))); +} + +void crxor(UGeckoInstruction _inst) +{ + u32 cr = GetCR(); + u32 a = cr << _inst.CRBA; + u32 b = cr << _inst.CRBB; + u32 d = ((a ^ b) & 0x80000000) >> _inst.CRBD; + SetCR(d | (cr & ~(0x80000000 >> _inst.CRBD))); +} + +void mcrf(UGeckoInstruction _inst) +{ + u32 cr = GetCR(); + u32 crmask = ~(0xF0000000 >> (4*_inst.CRFD)); + u32 flags = ((cr << (4*_inst.CRFS)) & 0xF0000000) >> (4*_inst.CRFD); + SetCR((cr & crmask) | flags); +} + +void isync(UGeckoInstruction _inst) +{ + //shouldnt do anything +} + +} // namespace diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp index 08bedae23f..8d01a65b04 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit.cpp @@ -1,420 +1,420 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include - -#include "Common.h" -#include "x64Emitter.h" -#include "ABI.h" -#include "Thunk.h" -#include "../../HLE/HLE.h" -#include "../../Core.h" -#include "../../PatchEngine.h" -#include "../../CoreTiming.h" -#include "../PowerPC.h" -#include "../Profiler.h" -#include "../PPCTables.h" -#include "../PPCAnalyst.h" -#include "../../HW/Memmap.h" -#include "../../HW/GPFifo.h" -#include "Jit.h" -#include "JitAsm.h" -#include "JitCache.h" -#include "JitRegCache.h" - -using namespace Gen; -using namespace PowerPC; - -extern int blocksExecuted; - -// Dolphin's PowerPC->x86 JIT dynamic recompiler -// All code by ector (hrydgard) -// Features: -// * x86 & x64 support, lots of shared code. -// * Basic block linking -// * Fast dispatcher - -// Unfeatures: -// * Does not recompile all instructions. Often falls back to inserting a CALL to the corresponding JIT function. - - -// Various notes below - -// Register allocation -// RAX - Generic quicktemp register -// RBX - point to base of memory map -// RSI RDI R12 R13 R14 R15 - free for allocation -// RCX RDX R8 R9 R10 R11 - allocate in emergencies. These need to be flushed before functions are called. -// RSP - stack pointer, do not generally use, very dangerous -// RBP - ? - -// IMPORTANT: -// Make sure that all generated code and all emulator state sits under the 2GB boundary so that -// RIP addressing can be used easily. Windows will always allocate static code under the 2GB boundary. -// Also make sure to use VirtualAlloc and specify EXECUTE permission. - -// Open questions -// * Should there be any statically allocated registers? r3, r4, r5, r8, r0 come to mind.. maybe sp -// * Does it make sense to finish off the remaining non-jitted instructions? Seems we are hitting diminishing returns. -// * Why is the FPU exception handling not working 100%? Several games still get corrupted floating point state. -// This can even be seen in one homebrew Wii demo - RayTracer.elf - -// Other considerations - -//Many instructions have shorter forms for EAX. However, I believe their performance boost -//will be as small to be negligble, so I haven't dirtied up the code with that. AMD recommends it in their -//optimization manuals, though. - -// We support block linking. Reserve space at the exits of every block for a full 5-byte jmp. Save 16-bit offsets -// from the starts of each block, marking the exits so that they can be nicely patched at any time. - -// * Blocks do NOT use call/ret, they only jmp to each other and to the dispatcher when necessary. - -// All blocks that can be precompiled will be precompiled. Code will be memory protected - any write will mark -// the region as non-compilable, and all links to the page will be torn out and replaced with dispatcher jmps. - -// Alternatively, icbi instruction SHOULD mark where we can't compile - -// Seldom-happening events will be handled by adding a decrement of a counter to all blr instructions (which are -// expensive anyway since we need to return to dispatcher, except when they can be predicted). - -// TODO: SERIOUS synchronization problem with the video plugin setting tokens and breakpoints in dual core mode!!! -// Somewhat fixed by disabling idle skipping when certain interrupts are enabled -// This is no permantent reliable fix -// TODO: Zeldas go whacko when you hang the gfx thread - -// Idea - Accurate exception handling -// Compute register state at a certain instruction by running the JIT in "dry mode", and stopping at the right place. -// Not likely to be done :P - - -// Optimization Ideas - -/* - * Assume SP is in main RAM (in Wii mode too?) - partly done - * Assume all floating point loads and double precision loads+stores are to/from main ram - (single precision can be used in write gather pipe, specialized fast check added) - * AMD only - use movaps instead of movapd when loading ps from memory? - * HLE functions like floorf, sin, memcpy, etc - they can be much faster - * ABI optimizations - drop F0-F13 on blr, for example. Watch out for context switching. - CR2-CR4 are non-volatile, rest of CR is volatile -> dropped on blr. - R5-R12 are volatile -> dropped on blr. - * classic inlining across calls. - -Metroid wants -subc -subfe - -Low hanging fruit: -stfd -- guaranteed in memory -cmpl -mulli -stfs -stwu -lb/stzx - -bcx - optimize! -bcctr -stfs -psq_st -addx -orx -rlwimix -fcmpo -DSP_UpdateARAMDMA -lfd -stwu -cntlzwx -bcctrx -WriteBigEData - -TODO -lha -srawx -addic_rc -addex -subfcx -subfex - -fmaddx -fmulx -faddx -fnegx -frspx -frsqrtex -ps_sum0 -ps_muls0 -ps_adds1 - -*/ - - -namespace CPUCompare -{ - extern u32 m_BlockStart; -} - - -namespace Jit64 -{ - JitState js; - JitOptions jo; - - void Init() - { - jo.optimizeStack = true; - jo.enableBlocklink = true; // Speed boost, but not 100% safe -#ifdef _M_X64 - jo.enableFastMem = Core::GetStartupParameter().bUseFastMem; -#else - jo.enableFastMem = false; -#endif - jo.assumeFPLoadFromMem = true; - jo.fpAccurateFlags = true; - jo.optimizeGatherPipe = true; - jo.interpretFPU = false; - jo.fastInterrupts = false; - } - - void WriteCallInterpreter(UGeckoInstruction _inst) - { - gpr.Flush(FLUSH_ALL); - fpr.Flush(FLUSH_ALL); - if (js.isLastInstruction) - { - MOV(32, M(&PC), Imm32(js.compilerPC)); - MOV(32, M(&NPC), Imm32(js.compilerPC + 4)); - } - Interpreter::_interpreterInstruction instr = GetInterpreterOp(_inst); - ABI_CallFunctionC((void*)instr, _inst.hex); - } - - void Default(UGeckoInstruction _inst) - { - WriteCallInterpreter(_inst.hex); - } - - void HLEFunction(UGeckoInstruction _inst) - { - gpr.Flush(FLUSH_ALL); - fpr.Flush(FLUSH_ALL); - ABI_CallFunctionCC((void*)&HLE::Execute, js.compilerPC, _inst.hex); - MOV(32, R(EAX), M(&NPC)); - WriteExitDestInEAX(0); - } - - void DoNothing(UGeckoInstruction _inst) - { - // Yup, just don't do anything. - } - - static const bool ImHereDebug = false; - static const bool ImHereLog = false; - static std::map been_here; - - void ImHere() - { - static FILE *f = 0; - if (ImHereLog) { - if (!f) - { -#ifdef _M_X64 - f = fopen("log64.txt", "w"); -#else - f = fopen("log32.txt", "w"); -#endif - } - fprintf(f, "%08x\n", PC); - } - if (been_here.find(PC) != been_here.end()) { - been_here.find(PC)->second++; - if ((been_here.find(PC)->second) & 1023) - return; - } - LOG(DYNA_REC, "I'm here - PC = %08x , LR = %08x", PC, LR); - printf("I'm here - PC = %08x , LR = %08x", PC, LR); - been_here[PC] = 1; - } - - void Cleanup() - { - if (jo.optimizeGatherPipe && js.fifoBytesThisBlock > 0) - CALL((void *)&GPFifo::CheckGatherPipe); - } - - void WriteExit(u32 destination, int exit_num) - { - Cleanup(); - SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount)); - - //If nobody has taken care of this yet (this can be removed when all branches are done) - JitBlock *b = js.curBlock; - b->exitAddress[exit_num] = destination; - b->exitPtrs[exit_num] = GetWritableCodePtr(); - - // Link opportunity! - int block = GetBlockNumberFromAddress(destination); - if (block >= 0 && jo.enableBlocklink) - { - // It exists! Joy of joy! - JMP(GetBlock(block)->checkedEntry, true); - b->linkStatus[exit_num] = true; - } - else - { - MOV(32, M(&PC), Imm32(destination)); - JMP(Asm::dispatcher, true); - } - } - - void WriteExitDestInEAX(int exit_num) - { - MOV(32, M(&PC), R(EAX)); - Cleanup(); - SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount)); - JMP(Asm::dispatcher, true); - } - - void WriteRfiExitDestInEAX() - { - MOV(32, M(&PC), R(EAX)); - Cleanup(); - SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount)); - JMP(Asm::testExceptions, true); - } - - void WriteExceptionExit(u32 exception) - { - Cleanup(); - OR(32, M(&PowerPC::ppcState.Exceptions), Imm32(exception)); - MOV(32, M(&PC), Imm32(js.compilerPC + 4)); - JMP(Asm::testExceptions, true); - } - - const u8* DoJit(u32 emaddress, JitBlock &b) - { - if (emaddress == 0) - PanicAlert("ERROR : Trying to compile at 0. LR=%08x", LR); - - u32 size; - js.isLastInstruction = false; - js.blockStart = emaddress; - js.fifoBytesThisBlock = 0; - js.curBlock = &b; - js.blockSetsQuantizers = false; - js.block_flags = 0; - - //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. - PPCAnalyst::CodeOp *ops = PPCAnalyst::Flatten(emaddress, size, js.st, js.gpa, js.fpa); - const u8 *start = AlignCode4(); //TODO: Test if this or AlignCode16 make a difference from GetCodePtr - b.checkedEntry = start; - b.runCount = 0; - - // Downcount flag check. The last block decremented downcounter, and the flag should still be available. - FixupBranch skip = J_CC(CC_NBE); - MOV(32, M(&PC), Imm32(js.blockStart)); - JMP(Asm::doTiming, true); // downcount hit zero - go doTiming. - SetJumpTarget(skip); - - const u8 *normalEntry = GetCodePtr(); - if (ImHereDebug) CALL((void *)&ImHere); //Used to get a trace of the last few blocks before a crash, sometimes VERY useful - - if (js.fpa.any) - { - //This block 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); - MOV(32, M(&PC), Imm32(js.blockStart)); - JMP(Asm::fpException, true); - 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::testExceptions, true); - SetJumpTarget(b1); - } - - // Conditionally add profiling code. - if (Profiler::g_ProfileBlocks) { - ADD(32, M(&b.runCount), Imm8(1)); -#ifdef _WIN32 - b.ticCounter.QuadPart = 0; - b.ticStart.QuadPart = 0; - b.ticStop.QuadPart = 0; -#else -//TODO -#endif - // get start tic - PROFILER_QUERY_PERFORMACE_COUNTER(&b.ticStart); - } - - //Start up the register allocators - //They use the information in gpa/fpa to preload commonly used registers. - gpr.Start(js.gpa); - fpr.Start(js.fpa); - - js.downcountAmount = js.st.numCycles + PatchEngine::GetSpeedhackCycles(emaddress); - js.blockSize = size; - // Translate instructions - for (int i = 0; i < (int)size; i++) - { - // gpr.Flush(FLUSH_ALL); - // if (PPCTables::UsesFPU(_inst)) - // fpr.Flush(FLUSH_ALL); - js.compilerPC = ops[i].address; - js.op = &ops[i]; - js.instructionNumber = i; - if (i == (int)size - 1) { - js.isLastInstruction = true; - if (Profiler::g_ProfileBlocks) { - // CAUTION!!! push on stack regs you use, do your stuff, then pop - PROFILER_VPUSH; - // get end tic - PROFILER_QUERY_PERFORMACE_COUNTER(&b.ticStop); - // tic counter += (end tic - start tic) - PROFILER_ADD_DIFF_LARGE_INTEGER(&b.ticCounter, &b.ticStop, &b.ticStart); - PROFILER_VPOP; - } - } - - // const GekkoOpInfo *info = GetOpInfo(); - if (jo.interpretFPU && PPCTables::UsesFPU(ops[i].inst)) - Default(ops[i].inst); - else - PPCTables::CompileInstruction(ops[i].inst); - - gpr.SanityCheck(); - fpr.SanityCheck(); - if (jo.optimizeGatherPipe && js.fifoBytesThisBlock >= 32) - { - js.fifoBytesThisBlock -= 32; - CALL(ProtectFunction((void *)&GPFifo::CheckGatherPipe, 0)); - } - } - js.compilerPC += 4; - - b.flags = js.block_flags; - b.codeSize = (u32)(GetCodePtr() - start); - b.originalSize = js.compilerPC - emaddress; - return normalEntry; - } -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "Common.h" +#include "x64Emitter.h" +#include "ABI.h" +#include "Thunk.h" +#include "../../HLE/HLE.h" +#include "../../Core.h" +#include "../../PatchEngine.h" +#include "../../CoreTiming.h" +#include "../PowerPC.h" +#include "../Profiler.h" +#include "../PPCTables.h" +#include "../PPCAnalyst.h" +#include "../../HW/Memmap.h" +#include "../../HW/GPFifo.h" +#include "Jit.h" +#include "JitAsm.h" +#include "JitCache.h" +#include "JitRegCache.h" + +using namespace Gen; +using namespace PowerPC; + +extern int blocksExecuted; + +// Dolphin's PowerPC->x86 JIT dynamic recompiler +// All code by ector (hrydgard) +// Features: +// * x86 & x64 support, lots of shared code. +// * Basic block linking +// * Fast dispatcher + +// Unfeatures: +// * Does not recompile all instructions. Often falls back to inserting a CALL to the corresponding JIT function. + + +// Various notes below + +// Register allocation +// RAX - Generic quicktemp register +// RBX - point to base of memory map +// RSI RDI R12 R13 R14 R15 - free for allocation +// RCX RDX R8 R9 R10 R11 - allocate in emergencies. These need to be flushed before functions are called. +// RSP - stack pointer, do not generally use, very dangerous +// RBP - ? + +// IMPORTANT: +// Make sure that all generated code and all emulator state sits under the 2GB boundary so that +// RIP addressing can be used easily. Windows will always allocate static code under the 2GB boundary. +// Also make sure to use VirtualAlloc and specify EXECUTE permission. + +// Open questions +// * Should there be any statically allocated registers? r3, r4, r5, r8, r0 come to mind.. maybe sp +// * Does it make sense to finish off the remaining non-jitted instructions? Seems we are hitting diminishing returns. +// * Why is the FPU exception handling not working 100%? Several games still get corrupted floating point state. +// This can even be seen in one homebrew Wii demo - RayTracer.elf + +// Other considerations + +//Many instructions have shorter forms for EAX. However, I believe their performance boost +//will be as small to be negligble, so I haven't dirtied up the code with that. AMD recommends it in their +//optimization manuals, though. + +// We support block linking. Reserve space at the exits of every block for a full 5-byte jmp. Save 16-bit offsets +// from the starts of each block, marking the exits so that they can be nicely patched at any time. + +// * Blocks do NOT use call/ret, they only jmp to each other and to the dispatcher when necessary. + +// All blocks that can be precompiled will be precompiled. Code will be memory protected - any write will mark +// the region as non-compilable, and all links to the page will be torn out and replaced with dispatcher jmps. + +// Alternatively, icbi instruction SHOULD mark where we can't compile + +// Seldom-happening events will be handled by adding a decrement of a counter to all blr instructions (which are +// expensive anyway since we need to return to dispatcher, except when they can be predicted). + +// TODO: SERIOUS synchronization problem with the video plugin setting tokens and breakpoints in dual core mode!!! +// Somewhat fixed by disabling idle skipping when certain interrupts are enabled +// This is no permantent reliable fix +// TODO: Zeldas go whacko when you hang the gfx thread + +// Idea - Accurate exception handling +// Compute register state at a certain instruction by running the JIT in "dry mode", and stopping at the right place. +// Not likely to be done :P + + +// Optimization Ideas - +/* + * Assume SP is in main RAM (in Wii mode too?) - partly done + * Assume all floating point loads and double precision loads+stores are to/from main ram + (single precision can be used in write gather pipe, specialized fast check added) + * AMD only - use movaps instead of movapd when loading ps from memory? + * HLE functions like floorf, sin, memcpy, etc - they can be much faster + * ABI optimizations - drop F0-F13 on blr, for example. Watch out for context switching. + CR2-CR4 are non-volatile, rest of CR is volatile -> dropped on blr. + R5-R12 are volatile -> dropped on blr. + * classic inlining across calls. + +Metroid wants +subc +subfe + +Low hanging fruit: +stfd -- guaranteed in memory +cmpl +mulli +stfs +stwu +lb/stzx + +bcx - optimize! +bcctr +stfs +psq_st +addx +orx +rlwimix +fcmpo +DSP_UpdateARAMDMA +lfd +stwu +cntlzwx +bcctrx +WriteBigEData + +TODO +lha +srawx +addic_rc +addex +subfcx +subfex + +fmaddx +fmulx +faddx +fnegx +frspx +frsqrtex +ps_sum0 +ps_muls0 +ps_adds1 + +*/ + + +namespace CPUCompare +{ + extern u32 m_BlockStart; +} + + +namespace Jit64 +{ + JitState js; + JitOptions jo; + + void Init() + { + jo.optimizeStack = true; + jo.enableBlocklink = true; // Speed boost, but not 100% safe +#ifdef _M_X64 + jo.enableFastMem = Core::GetStartupParameter().bUseFastMem; +#else + jo.enableFastMem = false; +#endif + jo.assumeFPLoadFromMem = true; + jo.fpAccurateFlags = true; + jo.optimizeGatherPipe = true; + jo.interpretFPU = false; + jo.fastInterrupts = false; + } + + void WriteCallInterpreter(UGeckoInstruction _inst) + { + gpr.Flush(FLUSH_ALL); + fpr.Flush(FLUSH_ALL); + if (js.isLastInstruction) + { + MOV(32, M(&PC), Imm32(js.compilerPC)); + MOV(32, M(&NPC), Imm32(js.compilerPC + 4)); + } + Interpreter::_interpreterInstruction instr = GetInterpreterOp(_inst); + ABI_CallFunctionC((void*)instr, _inst.hex); + } + + void Default(UGeckoInstruction _inst) + { + WriteCallInterpreter(_inst.hex); + } + + void HLEFunction(UGeckoInstruction _inst) + { + gpr.Flush(FLUSH_ALL); + fpr.Flush(FLUSH_ALL); + ABI_CallFunctionCC((void*)&HLE::Execute, js.compilerPC, _inst.hex); + MOV(32, R(EAX), M(&NPC)); + WriteExitDestInEAX(0); + } + + void DoNothing(UGeckoInstruction _inst) + { + // Yup, just don't do anything. + } + + static const bool ImHereDebug = false; + static const bool ImHereLog = false; + static std::map been_here; + + void ImHere() + { + static FILE *f = 0; + if (ImHereLog) { + if (!f) + { +#ifdef _M_X64 + f = fopen("log64.txt", "w"); +#else + f = fopen("log32.txt", "w"); +#endif + } + fprintf(f, "%08x\n", PC); + } + if (been_here.find(PC) != been_here.end()) { + been_here.find(PC)->second++; + if ((been_here.find(PC)->second) & 1023) + return; + } + LOG(DYNA_REC, "I'm here - PC = %08x , LR = %08x", PC, LR); + printf("I'm here - PC = %08x , LR = %08x", PC, LR); + been_here[PC] = 1; + } + + void Cleanup() + { + if (jo.optimizeGatherPipe && js.fifoBytesThisBlock > 0) + CALL((void *)&GPFifo::CheckGatherPipe); + } + + void WriteExit(u32 destination, int exit_num) + { + Cleanup(); + SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount)); + + //If nobody has taken care of this yet (this can be removed when all branches are done) + JitBlock *b = js.curBlock; + b->exitAddress[exit_num] = destination; + b->exitPtrs[exit_num] = GetWritableCodePtr(); + + // Link opportunity! + int block = GetBlockNumberFromAddress(destination); + if (block >= 0 && jo.enableBlocklink) + { + // It exists! Joy of joy! + JMP(GetBlock(block)->checkedEntry, true); + b->linkStatus[exit_num] = true; + } + else + { + MOV(32, M(&PC), Imm32(destination)); + JMP(Asm::dispatcher, true); + } + } + + void WriteExitDestInEAX(int exit_num) + { + MOV(32, M(&PC), R(EAX)); + Cleanup(); + SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount)); + JMP(Asm::dispatcher, true); + } + + void WriteRfiExitDestInEAX() + { + MOV(32, M(&PC), R(EAX)); + Cleanup(); + SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount)); + JMP(Asm::testExceptions, true); + } + + void WriteExceptionExit(u32 exception) + { + Cleanup(); + OR(32, M(&PowerPC::ppcState.Exceptions), Imm32(exception)); + MOV(32, M(&PC), Imm32(js.compilerPC + 4)); + JMP(Asm::testExceptions, true); + } + + const u8* DoJit(u32 emaddress, JitBlock &b) + { + if (emaddress == 0) + PanicAlert("ERROR : Trying to compile at 0. LR=%08x", LR); + + u32 size; + js.isLastInstruction = false; + js.blockStart = emaddress; + js.fifoBytesThisBlock = 0; + js.curBlock = &b; + js.blockSetsQuantizers = false; + js.block_flags = 0; + + //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. + PPCAnalyst::CodeOp *ops = PPCAnalyst::Flatten(emaddress, size, js.st, js.gpa, js.fpa); + const u8 *start = AlignCode4(); //TODO: Test if this or AlignCode16 make a difference from GetCodePtr + b.checkedEntry = start; + b.runCount = 0; + + // Downcount flag check. The last block decremented downcounter, and the flag should still be available. + FixupBranch skip = J_CC(CC_NBE); + MOV(32, M(&PC), Imm32(js.blockStart)); + JMP(Asm::doTiming, true); // downcount hit zero - go doTiming. + SetJumpTarget(skip); + + const u8 *normalEntry = GetCodePtr(); + if (ImHereDebug) CALL((void *)&ImHere); //Used to get a trace of the last few blocks before a crash, sometimes VERY useful + + if (js.fpa.any) + { + //This block 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); + MOV(32, M(&PC), Imm32(js.blockStart)); + JMP(Asm::fpException, true); + 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::testExceptions, true); + SetJumpTarget(b1); + } + + // Conditionally add profiling code. + if (Profiler::g_ProfileBlocks) { + ADD(32, M(&b.runCount), Imm8(1)); +#ifdef _WIN32 + b.ticCounter.QuadPart = 0; + b.ticStart.QuadPart = 0; + b.ticStop.QuadPart = 0; +#else +//TODO +#endif + // get start tic + PROFILER_QUERY_PERFORMACE_COUNTER(&b.ticStart); + } + + //Start up the register allocators + //They use the information in gpa/fpa to preload commonly used registers. + gpr.Start(js.gpa); + fpr.Start(js.fpa); + + js.downcountAmount = js.st.numCycles + PatchEngine::GetSpeedhackCycles(emaddress); + js.blockSize = size; + // Translate instructions + for (int i = 0; i < (int)size; i++) + { + // gpr.Flush(FLUSH_ALL); + // if (PPCTables::UsesFPU(_inst)) + // fpr.Flush(FLUSH_ALL); + js.compilerPC = ops[i].address; + js.op = &ops[i]; + js.instructionNumber = i; + if (i == (int)size - 1) { + js.isLastInstruction = true; + if (Profiler::g_ProfileBlocks) { + // CAUTION!!! push on stack regs you use, do your stuff, then pop + PROFILER_VPUSH; + // get end tic + PROFILER_QUERY_PERFORMACE_COUNTER(&b.ticStop); + // tic counter += (end tic - start tic) + PROFILER_ADD_DIFF_LARGE_INTEGER(&b.ticCounter, &b.ticStop, &b.ticStart); + PROFILER_VPOP; + } + } + + // const GekkoOpInfo *info = GetOpInfo(); + if (jo.interpretFPU && PPCTables::UsesFPU(ops[i].inst)) + Default(ops[i].inst); + else + PPCTables::CompileInstruction(ops[i].inst); + + gpr.SanityCheck(); + fpr.SanityCheck(); + if (jo.optimizeGatherPipe && js.fifoBytesThisBlock >= 32) + { + js.fifoBytesThisBlock -= 32; + CALL(ProtectFunction((void *)&GPFifo::CheckGatherPipe, 0)); + } + } + js.compilerPC += 4; + + b.flags = js.block_flags; + b.codeSize = (u32)(GetCodePtr() - start); + b.originalSize = js.compilerPC - emaddress; + return normalEntry; + } +} diff --git a/Source/Core/Core/Src/PowerPC/Jit64/JitAsm.cpp b/Source/Core/Core/Src/PowerPC/Jit64/JitAsm.cpp index 4916272786..d43a7edd1d 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/JitAsm.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/JitAsm.cpp @@ -1,374 +1,374 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "ABI.h" -#include "x64Emitter.h" - -#include "../../HW/Memmap.h" - -#include "../PowerPC.h" -#include "../../CoreTiming.h" -#include "MemoryUtil.h" - -#include "ABI.h" -#include "Jit.h" -#include "JitCache.h" - -#include "../../HW/CPUCompare.h" -#include "../../HW/GPFifo.h" -#include "../../Core.h" - -using namespace Gen; -int blocksExecuted; - -namespace Jit64 -{ - -namespace Asm -{ -const u8 *enterCode; -const u8 *testExceptions; -const u8 *fpException; -const u8 *doTiming; -const u8 *dispatcher; -const u8 *dispatcherNoCheck; -const u8 *dispatcherPcInEAX; -const u8 *computeRc; -const u8 *computeRcFp; - -const u8 *fifoDirectWrite8; -const u8 *fifoDirectWrite16; -const u8 *fifoDirectWrite32; -const u8 *fifoDirectWriteFloat; -const u8 *fifoDirectWriteXmm64; - -bool compareEnabled = false; - -//TODO - make an option -//#if _DEBUG -static bool enableDebug = false; -//#else -// bool enableDebug = false; -//#endif - -static bool enableStatistics = false; - -//GLOBAL STATIC ALLOCATIONS x86 -//EAX - ubiquitous scratch register - EVERYBODY scratches this - -//GLOBAL STATIC ALLOCATIONS x64 -//EAX - ubiquitous scratch register - EVERYBODY scratches this -//RBX - Base pointer of memory -//R15 - Pointer to array of block pointers - - -// PLAN: no more block numbers - crazy opcodes just contain offset within -// dynarec buffer -// At this offset - 4, there is an int specifying the block number. - - -void GenerateCommon(); - -#ifdef _M_IX86 -void Generate() -{ - enterCode = AlignCode16(); - PUSH(EBP); - PUSH(EBX); - PUSH(ESI); - PUSH(EDI); - - //MOV(32, R(EBX), Imm32((u32)&Memory::base)); - const u8 *outerLoop = GetCodePtr(); - CALL(reinterpret_cast(&CoreTiming::Advance)); - FixupBranch skipToRealDispatch = J(); //skip the sync and compare first time - - dispatcher = GetCodePtr(); - //This is the place for CPUCompare! - - //The result of slice decrementation should be in flags if somebody jumped here - //Jump on negative, not carry!!! - FixupBranch bail = J_CC(CC_BE); - if (Core::bReadTrace || Core::bWriteTrace) - { - CALL(reinterpret_cast(&Core::SyncTrace)); - // CMP(32, R(EAX),Imm32(0)); - // bail2 = J_CC(); - } - SetJumpTarget(skipToRealDispatch); - //TEST(32,M(&PowerPC::ppcState.Exceptions), Imm32(0xFFFFFFFF)); - //FixupBranch bail2 = J_CC(CC_NZ); - dispatcherNoCheck = GetCodePtr(); - MOV(32, R(EAX), M(&PowerPC::ppcState.pc)); - dispatcherPcInEAX = GetCodePtr(); - - AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); - MOV(32, R(EBX), Imm32((u32)Memory::base)); - MOV(32, R(EAX), MComplex(EBX, EAX, SCALE_1, 0)); - TEST(32, R(EAX), Imm32(0xFC)); - FixupBranch notfound = J_CC(CC_NZ); - BSWAP(32, EAX); - //IDEA - we have 26 bits, why not just use offsets from base of code? - if (enableStatistics) - { - ADD(32, M(&blocksExecuted), Imm8(1)); - } - if (enableDebug) - { - ADD(32, M(&PowerPC::ppcState.DebugCount), Imm8(1)); - } - //grab from list and jump to it - //INT3(); - MOV(32, R(EDX), ImmPtr(GetCodePointers())); - JMPptr(MComplex(EDX, EAX, 4, 0)); - SetJumpTarget(notfound); - - //Ok, no block, let's jit - ABI_AlignStack(4); - PUSH(32, M(&PowerPC::ppcState.pc)); - CALL(reinterpret_cast(&Jit)); - ABI_RestoreStack(4); - JMP(dispatcherNoCheck); // no point in special casing this - - //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)); - CALL(reinterpret_cast(&PowerPC::CheckExceptions)); - MOV(32, R(EAX), M(&NPC)); - MOV(32, M(&PC), R(EAX)); - JMP(dispatcher); - - SetJumpTarget(bail); - doTiming = GetCodePtr(); - - CALL(reinterpret_cast(&CoreTiming::Advance)); - - testExceptions = GetCodePtr(); - TEST(32, M(&PowerPC::ppcState.Exceptions), Imm32(0xFFFFFFFF)); - FixupBranch skipExceptions = J_CC(CC_Z); - MOV(32, R(EAX), M(&PC)); - MOV(32, M(&NPC), R(EAX)); - CALL(reinterpret_cast(&PowerPC::CheckExceptions)); - MOV(32, R(EAX), M(&NPC)); - MOV(32, M(&PC), R(EAX)); - SetJumpTarget(skipExceptions); - - TEST(32, M((void*)&PowerPC::state), Imm32(0xFFFFFFFF)); - J_CC(CC_Z, outerLoop, true); - - POP(EDI); - POP(ESI); - POP(EBX); - POP(EBP); - RET(); - - GenerateCommon(); -} - -#elif defined(_M_X64) - -void Generate() -{ - enterCode = AlignCode16(); - - ABI_PushAllCalleeSavedRegsAndAdjustStack(); - - MOV(64, R(RBX), Imm64((u64)Memory::base)); - MOV(64, R(R15), Imm64((u64)GetCodePointers())); //It's below 2GB so 32 bits are good enough - const u8 *outerLoop = GetCodePtr(); - - CALL((void *)&CoreTiming::Advance); - FixupBranch skipToRealDispatch = J(); //skip the sync and compare first time - - dispatcher = GetCodePtr(); - //The result of slice decrementation should be in flags if somebody jumped here - //Jump on negative, not carry!!! - FixupBranch bail = J_CC(CC_BE); - SetJumpTarget(skipToRealDispatch); - - dispatcherNoCheck = GetCodePtr(); - MOV(32, R(EAX), M(&PowerPC::ppcState.pc)); - dispatcherPcInEAX = GetCodePtr(); - MOV(32, R(EAX), MComplex(RBX, RAX, SCALE_1, 0)); - TEST(32, R(EAX), Imm32(0xFC)); - - FixupBranch notfound = J_CC(CC_NZ); - BSWAP(32, EAX); - //IDEA - we have 26 bits, why not just use offsets from base of code? - if (enableStatistics) - { - ADD(32, M(&blocksExecuted), Imm8(1)); - } - if (enableDebug) - { - ADD(32, M(&PowerPC::ppcState.DebugCount), Imm8(1)); - } - //grab from list and jump to it - JMPptr(MComplex(R15, RAX, 8, 0)); - SetJumpTarget(notfound); - - //Ok, no block, let's jit - MOV(32, R(ABI_PARAM1), M(&PowerPC::ppcState.pc)); - CALL((void *)&Jit); - JMP(dispatcherNoCheck); // no point in special casing this, not the "fast path" - - //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)); - CALL((void *)&PowerPC::CheckExceptions); - MOV(32, R(EAX), M(&NPC)); - MOV(32, M(&PC), R(EAX)); - JMP(dispatcherNoCheck); - - SetJumpTarget(bail); - doTiming = GetCodePtr(); - TEST(32, M(&PC), Imm32(0xFFFFFFFF)); - FixupBranch mojs = J_CC(CC_NZ); - INT3(); // if you hit this, PC == 0 - no good - SetJumpTarget(mojs); - CALL((void *)&CoreTiming::Advance); - - testExceptions = GetCodePtr(); - TEST(32, M(&PowerPC::ppcState.Exceptions), Imm32(0xFFFFFFFF)); - FixupBranch skipExceptions = J_CC(CC_Z); - MOV(32, R(EAX), M(&PC)); - MOV(32, M(&NPC), R(EAX)); - CALL((void *)&PowerPC::CheckExceptions); - MOV(32, R(EAX), M(&NPC)); - MOV(32, M(&PC), R(EAX)); - SetJumpTarget(skipExceptions); - - TEST(32, M((void*)&PowerPC::state), Imm32(0xFFFFFFFF)); - J_CC(CC_Z, outerLoop, true); - - //Landing pad for drec space - ABI_PopAllCalleeSavedRegsAndAdjustStack(); - RET(); - - GenerateCommon(); -} -#endif - -void GenFifoWrite(int size) -{ - // Assume value in ABI_PARAM1 - PUSH(ESI); - if (size != 32) - PUSH(EDX); - BSWAP(size, ABI_PARAM1); - MOV(32, R(EAX), Imm32((u32)(u64)GPFifo::m_gatherPipe)); - MOV(32, R(ESI), M(&GPFifo::m_gatherPipeCount)); - if (size != 32) { - MOV(32, R(EDX), R(ABI_PARAM1)); - MOV(size, MComplex(RAX, RSI, 1, 0), R(EDX)); - } else { - MOV(size, MComplex(RAX, RSI, 1, 0), R(ABI_PARAM1)); - } - ADD(32, R(ESI), Imm8(size >> 3)); - MOV(32, M(&GPFifo::m_gatherPipeCount), R(ESI)); - if (size != 32) - POP(EDX); - POP(ESI); - RET(); -} - -static int temp32; -void GenFifoFloatWrite() -{ - // Assume value in XMM0 - PUSH(ESI); - PUSH(EDX); - MOVSS(M(&temp32), XMM0); - MOV(32, R(EDX), M(&temp32)); - BSWAP(32, EDX); - MOV(32, R(EAX), Imm32((u32)(u64)GPFifo::m_gatherPipe)); - MOV(32, R(ESI), M(&GPFifo::m_gatherPipeCount)); - MOV(32, MComplex(RAX, RSI, 1, 0), R(EDX)); - ADD(32, R(ESI), Imm8(4)); - MOV(32, M(&GPFifo::m_gatherPipeCount), R(ESI)); - POP(EDX); - POP(ESI); - RET(); -} - -void GenFifoXmm64Write() -{ - // Assume value in XMM0. Assume pre-byteswapped (unlike the others here!) - PUSH(ESI); - MOV(32, R(EAX), Imm32((u32)(u64)GPFifo::m_gatherPipe)); - MOV(32, R(ESI), M(&GPFifo::m_gatherPipeCount)); - MOVQ_xmm(MComplex(RAX, RSI, 1, 0), XMM0); - ADD(32, R(ESI), Imm8(8)); - MOV(32, M(&GPFifo::m_gatherPipeCount), R(ESI)); - POP(ESI); - RET(); -} - -void GenerateCommon() -{ - computeRc = AlignCode16(); - AND(32, M(&CR), Imm32(0x0FFFFFFF)); - CMP(32, R(EAX), Imm8(0)); - FixupBranch pLesser = J_CC(CC_L); - FixupBranch pGreater = J_CC(CC_G); - OR(32, M(&CR), Imm32(0x20000000)); // _x86Reg == 0 - RET(); - SetJumpTarget(pGreater); - OR(32, M(&CR), Imm32(0x40000000)); // _x86Reg > 0 - RET(); - SetJumpTarget(pLesser); - OR(32, M(&CR), Imm32(0x80000000)); // _x86Reg < 0 - RET(); - - fifoDirectWrite8 = AlignCode4(); - GenFifoWrite(8); - fifoDirectWrite16 = AlignCode4(); - GenFifoWrite(16); - fifoDirectWrite32 = AlignCode4(); - GenFifoWrite(32); - fifoDirectWriteFloat = AlignCode4(); - GenFifoFloatWrite(); - fifoDirectWriteXmm64 = AlignCode4(); - GenFifoXmm64Write(); - - computeRcFp = AlignCode16(); - //CMPSD(R(XMM0), M(&zero), - // TODO - - // Fast write routines - special case the most common hardware write - // TODO: use this. - // Even in x86, the param values will be in the right registers. - /* - const u8 *fastMemWrite8 = AlignCode16(); - CMP(32, R(ABI_PARAM2), Imm32(0xCC008000)); - FixupBranch skip_fast_write = J_CC(CC_NE, false); - MOV(32, EAX, M(&m_gatherPipeCount)); - MOV(8, MDisp(EAX, (u32)&m_gatherPipe), ABI_PARAM1); - ADD(32, 1, M(&m_gatherPipeCount)); - RET(); - SetJumpTarget(skip_fast_write); - CALL((void *)&Memory::Write_U8);*/ -} - -} // namespace Asm - -} // namespace Jit64 - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "ABI.h" +#include "x64Emitter.h" + +#include "../../HW/Memmap.h" + +#include "../PowerPC.h" +#include "../../CoreTiming.h" +#include "MemoryUtil.h" + +#include "ABI.h" +#include "Jit.h" +#include "JitCache.h" + +#include "../../HW/CPUCompare.h" +#include "../../HW/GPFifo.h" +#include "../../Core.h" + +using namespace Gen; +int blocksExecuted; + +namespace Jit64 +{ + +namespace Asm +{ +const u8 *enterCode; +const u8 *testExceptions; +const u8 *fpException; +const u8 *doTiming; +const u8 *dispatcher; +const u8 *dispatcherNoCheck; +const u8 *dispatcherPcInEAX; +const u8 *computeRc; +const u8 *computeRcFp; + +const u8 *fifoDirectWrite8; +const u8 *fifoDirectWrite16; +const u8 *fifoDirectWrite32; +const u8 *fifoDirectWriteFloat; +const u8 *fifoDirectWriteXmm64; + +bool compareEnabled = false; + +//TODO - make an option +//#if _DEBUG +static bool enableDebug = false; +//#else +// bool enableDebug = false; +//#endif + +static bool enableStatistics = false; + +//GLOBAL STATIC ALLOCATIONS x86 +//EAX - ubiquitous scratch register - EVERYBODY scratches this + +//GLOBAL STATIC ALLOCATIONS x64 +//EAX - ubiquitous scratch register - EVERYBODY scratches this +//RBX - Base pointer of memory +//R15 - Pointer to array of block pointers + + +// PLAN: no more block numbers - crazy opcodes just contain offset within +// dynarec buffer +// At this offset - 4, there is an int specifying the block number. + + +void GenerateCommon(); + +#ifdef _M_IX86 +void Generate() +{ + enterCode = AlignCode16(); + PUSH(EBP); + PUSH(EBX); + PUSH(ESI); + PUSH(EDI); + + //MOV(32, R(EBX), Imm32((u32)&Memory::base)); + const u8 *outerLoop = GetCodePtr(); + CALL(reinterpret_cast(&CoreTiming::Advance)); + FixupBranch skipToRealDispatch = J(); //skip the sync and compare first time + + dispatcher = GetCodePtr(); + //This is the place for CPUCompare! + + //The result of slice decrementation should be in flags if somebody jumped here + //Jump on negative, not carry!!! + FixupBranch bail = J_CC(CC_BE); + if (Core::bReadTrace || Core::bWriteTrace) + { + CALL(reinterpret_cast(&Core::SyncTrace)); + // CMP(32, R(EAX),Imm32(0)); + // bail2 = J_CC(); + } + SetJumpTarget(skipToRealDispatch); + //TEST(32,M(&PowerPC::ppcState.Exceptions), Imm32(0xFFFFFFFF)); + //FixupBranch bail2 = J_CC(CC_NZ); + dispatcherNoCheck = GetCodePtr(); + MOV(32, R(EAX), M(&PowerPC::ppcState.pc)); + dispatcherPcInEAX = GetCodePtr(); + + AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); + MOV(32, R(EBX), Imm32((u32)Memory::base)); + MOV(32, R(EAX), MComplex(EBX, EAX, SCALE_1, 0)); + TEST(32, R(EAX), Imm32(0xFC)); + FixupBranch notfound = J_CC(CC_NZ); + BSWAP(32, EAX); + //IDEA - we have 26 bits, why not just use offsets from base of code? + if (enableStatistics) + { + ADD(32, M(&blocksExecuted), Imm8(1)); + } + if (enableDebug) + { + ADD(32, M(&PowerPC::ppcState.DebugCount), Imm8(1)); + } + //grab from list and jump to it + //INT3(); + MOV(32, R(EDX), ImmPtr(GetCodePointers())); + JMPptr(MComplex(EDX, EAX, 4, 0)); + SetJumpTarget(notfound); + + //Ok, no block, let's jit + ABI_AlignStack(4); + PUSH(32, M(&PowerPC::ppcState.pc)); + CALL(reinterpret_cast(&Jit)); + ABI_RestoreStack(4); + JMP(dispatcherNoCheck); // no point in special casing this + + //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)); + CALL(reinterpret_cast(&PowerPC::CheckExceptions)); + MOV(32, R(EAX), M(&NPC)); + MOV(32, M(&PC), R(EAX)); + JMP(dispatcher); + + SetJumpTarget(bail); + doTiming = GetCodePtr(); + + CALL(reinterpret_cast(&CoreTiming::Advance)); + + testExceptions = GetCodePtr(); + TEST(32, M(&PowerPC::ppcState.Exceptions), Imm32(0xFFFFFFFF)); + FixupBranch skipExceptions = J_CC(CC_Z); + MOV(32, R(EAX), M(&PC)); + MOV(32, M(&NPC), R(EAX)); + CALL(reinterpret_cast(&PowerPC::CheckExceptions)); + MOV(32, R(EAX), M(&NPC)); + MOV(32, M(&PC), R(EAX)); + SetJumpTarget(skipExceptions); + + TEST(32, M((void*)&PowerPC::state), Imm32(0xFFFFFFFF)); + J_CC(CC_Z, outerLoop, true); + + POP(EDI); + POP(ESI); + POP(EBX); + POP(EBP); + RET(); + + GenerateCommon(); +} + +#elif defined(_M_X64) + +void Generate() +{ + enterCode = AlignCode16(); + + ABI_PushAllCalleeSavedRegsAndAdjustStack(); + + MOV(64, R(RBX), Imm64((u64)Memory::base)); + MOV(64, R(R15), Imm64((u64)GetCodePointers())); //It's below 2GB so 32 bits are good enough + const u8 *outerLoop = GetCodePtr(); + + CALL((void *)&CoreTiming::Advance); + FixupBranch skipToRealDispatch = J(); //skip the sync and compare first time + + dispatcher = GetCodePtr(); + //The result of slice decrementation should be in flags if somebody jumped here + //Jump on negative, not carry!!! + FixupBranch bail = J_CC(CC_BE); + SetJumpTarget(skipToRealDispatch); + + dispatcherNoCheck = GetCodePtr(); + MOV(32, R(EAX), M(&PowerPC::ppcState.pc)); + dispatcherPcInEAX = GetCodePtr(); + MOV(32, R(EAX), MComplex(RBX, RAX, SCALE_1, 0)); + TEST(32, R(EAX), Imm32(0xFC)); + + FixupBranch notfound = J_CC(CC_NZ); + BSWAP(32, EAX); + //IDEA - we have 26 bits, why not just use offsets from base of code? + if (enableStatistics) + { + ADD(32, M(&blocksExecuted), Imm8(1)); + } + if (enableDebug) + { + ADD(32, M(&PowerPC::ppcState.DebugCount), Imm8(1)); + } + //grab from list and jump to it + JMPptr(MComplex(R15, RAX, 8, 0)); + SetJumpTarget(notfound); + + //Ok, no block, let's jit + MOV(32, R(ABI_PARAM1), M(&PowerPC::ppcState.pc)); + CALL((void *)&Jit); + JMP(dispatcherNoCheck); // no point in special casing this, not the "fast path" + + //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)); + CALL((void *)&PowerPC::CheckExceptions); + MOV(32, R(EAX), M(&NPC)); + MOV(32, M(&PC), R(EAX)); + JMP(dispatcherNoCheck); + + SetJumpTarget(bail); + doTiming = GetCodePtr(); + TEST(32, M(&PC), Imm32(0xFFFFFFFF)); + FixupBranch mojs = J_CC(CC_NZ); + INT3(); // if you hit this, PC == 0 - no good + SetJumpTarget(mojs); + CALL((void *)&CoreTiming::Advance); + + testExceptions = GetCodePtr(); + TEST(32, M(&PowerPC::ppcState.Exceptions), Imm32(0xFFFFFFFF)); + FixupBranch skipExceptions = J_CC(CC_Z); + MOV(32, R(EAX), M(&PC)); + MOV(32, M(&NPC), R(EAX)); + CALL((void *)&PowerPC::CheckExceptions); + MOV(32, R(EAX), M(&NPC)); + MOV(32, M(&PC), R(EAX)); + SetJumpTarget(skipExceptions); + + TEST(32, M((void*)&PowerPC::state), Imm32(0xFFFFFFFF)); + J_CC(CC_Z, outerLoop, true); + + //Landing pad for drec space + ABI_PopAllCalleeSavedRegsAndAdjustStack(); + RET(); + + GenerateCommon(); +} +#endif + +void GenFifoWrite(int size) +{ + // Assume value in ABI_PARAM1 + PUSH(ESI); + if (size != 32) + PUSH(EDX); + BSWAP(size, ABI_PARAM1); + MOV(32, R(EAX), Imm32((u32)(u64)GPFifo::m_gatherPipe)); + MOV(32, R(ESI), M(&GPFifo::m_gatherPipeCount)); + if (size != 32) { + MOV(32, R(EDX), R(ABI_PARAM1)); + MOV(size, MComplex(RAX, RSI, 1, 0), R(EDX)); + } else { + MOV(size, MComplex(RAX, RSI, 1, 0), R(ABI_PARAM1)); + } + ADD(32, R(ESI), Imm8(size >> 3)); + MOV(32, M(&GPFifo::m_gatherPipeCount), R(ESI)); + if (size != 32) + POP(EDX); + POP(ESI); + RET(); +} + +static int temp32; +void GenFifoFloatWrite() +{ + // Assume value in XMM0 + PUSH(ESI); + PUSH(EDX); + MOVSS(M(&temp32), XMM0); + MOV(32, R(EDX), M(&temp32)); + BSWAP(32, EDX); + MOV(32, R(EAX), Imm32((u32)(u64)GPFifo::m_gatherPipe)); + MOV(32, R(ESI), M(&GPFifo::m_gatherPipeCount)); + MOV(32, MComplex(RAX, RSI, 1, 0), R(EDX)); + ADD(32, R(ESI), Imm8(4)); + MOV(32, M(&GPFifo::m_gatherPipeCount), R(ESI)); + POP(EDX); + POP(ESI); + RET(); +} + +void GenFifoXmm64Write() +{ + // Assume value in XMM0. Assume pre-byteswapped (unlike the others here!) + PUSH(ESI); + MOV(32, R(EAX), Imm32((u32)(u64)GPFifo::m_gatherPipe)); + MOV(32, R(ESI), M(&GPFifo::m_gatherPipeCount)); + MOVQ_xmm(MComplex(RAX, RSI, 1, 0), XMM0); + ADD(32, R(ESI), Imm8(8)); + MOV(32, M(&GPFifo::m_gatherPipeCount), R(ESI)); + POP(ESI); + RET(); +} + +void GenerateCommon() +{ + computeRc = AlignCode16(); + AND(32, M(&CR), Imm32(0x0FFFFFFF)); + CMP(32, R(EAX), Imm8(0)); + FixupBranch pLesser = J_CC(CC_L); + FixupBranch pGreater = J_CC(CC_G); + OR(32, M(&CR), Imm32(0x20000000)); // _x86Reg == 0 + RET(); + SetJumpTarget(pGreater); + OR(32, M(&CR), Imm32(0x40000000)); // _x86Reg > 0 + RET(); + SetJumpTarget(pLesser); + OR(32, M(&CR), Imm32(0x80000000)); // _x86Reg < 0 + RET(); + + fifoDirectWrite8 = AlignCode4(); + GenFifoWrite(8); + fifoDirectWrite16 = AlignCode4(); + GenFifoWrite(16); + fifoDirectWrite32 = AlignCode4(); + GenFifoWrite(32); + fifoDirectWriteFloat = AlignCode4(); + GenFifoFloatWrite(); + fifoDirectWriteXmm64 = AlignCode4(); + GenFifoXmm64Write(); + + computeRcFp = AlignCode16(); + //CMPSD(R(XMM0), M(&zero), + // TODO + + // Fast write routines - special case the most common hardware write + // TODO: use this. + // Even in x86, the param values will be in the right registers. + /* + const u8 *fastMemWrite8 = AlignCode16(); + CMP(32, R(ABI_PARAM2), Imm32(0xCC008000)); + FixupBranch skip_fast_write = J_CC(CC_NE, false); + MOV(32, EAX, M(&m_gatherPipeCount)); + MOV(8, MDisp(EAX, (u32)&m_gatherPipe), ABI_PARAM1); + ADD(32, 1, M(&m_gatherPipeCount)); + RET(); + SetJumpTarget(skip_fast_write); + CALL((void *)&Memory::Write_U8);*/ +} + +} // namespace Asm + +} // namespace Jit64 + diff --git a/Source/Core/Core/Src/PowerPC/Jit64/JitBackpatch.cpp b/Source/Core/Core/Src/PowerPC/Jit64/JitBackpatch.cpp index b0d7fa30c4..58759ba621 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/JitBackpatch.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/JitBackpatch.cpp @@ -1,197 +1,197 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include - -#include "Common.h" -#include "disasm.h" -#include "JitAsm.h" -#include "JitBackpatch.h" -#include "../../HW/Memmap.h" - -#include "x64Emitter.h" -#include "ABI.h" -#include "Thunk.h" -#include "x64Analyzer.h" - -#include "StringUtil.h" -#include "Jit.h" - -using namespace Gen; - -namespace Jit64 { - -extern u8 *trampolineCodePtr; - -void BackPatchError(const std::string &text, u8 *codePtr, u32 emAddress) { - u64 code_addr = (u64)codePtr; - disassembler disasm; - char disbuf[256]; - memset(disbuf, 0, 256); -#ifdef _M_IX86 - disasm.disasm32 -#else - disasm.disasm64 -#endif - (0, code_addr, codePtr, disbuf); - PanicAlert("%s\n\n" - "Error encountered accessing emulated address %08x.\n" - "Culprit instruction: \n%s\nat %08x%08x", - text.c_str(), emAddress, disbuf, code_addr>>32, code_addr); - return; -} - -// This generates some fairly heavy trampolines, but: -// 1) It's really necessary. We don't know anything about the context. -// 2) It doesn't really hurt. Only instructions that access I/O will get these, and there won't be -// that many of them in a typical program/game. -u8 *BackPatch(u8 *codePtr, int accessType, u32 emAddress, CONTEXT *ctx) -{ -#ifdef _M_X64 - if (!IsInJitCode(codePtr)) - return 0; // this will become a regular crash real soon after this - - u8 *oldCodePtr = GetWritableCodePtr(); - InstructionInfo info; - if (!DisassembleMov(codePtr, info, accessType)) { - BackPatchError("BackPatch - failed to disassemble MOV instruction", codePtr, emAddress); - } - - /* - if (info.isMemoryWrite) { - if (!Memory::IsRAMAddress(emAddress, true)) { - PanicAlert("Exception: Caught write to invalid address %08x", emAddress); - return; - } - BackPatchError("BackPatch - determined that MOV is write, not yet supported and should have been caught before", - codePtr, emAddress); - }*/ - - if (info.operandSize != 4) { - BackPatchError(StringFromFormat("BackPatch - no support for operand size %i", info.operandSize), codePtr, emAddress); - } - u64 code_addr = (u64)codePtr; - X64Reg addrReg = (X64Reg)info.scaledReg; - X64Reg dataReg = (X64Reg)info.regOperandReg; - if (info.otherReg != RBX) - PanicAlert("BackPatch : Base reg not RBX." - "\n\nAttempted to access %08x.", emAddress); - //if (accessType == OP_ACCESS_WRITE) - // PanicAlert("BackPatch : Currently only supporting reads." - // "\n\nAttempted to write to %08x.", emAddress); - - // OK, let's write a trampoline, and a jump to it. - // Later, let's share trampolines. - - // In the first iteration, we assume that all accesses are 32-bit. We also only deal with reads. - // Next step - support writes, special case FIFO writes. Also, support 32-bit mode. - u8 *trampoline = trampolineCodePtr; - SetCodePtr(trampolineCodePtr); - - if (accessType == 0) - { - // It's a read. Easy. - ABI_PushAllCallerSavedRegsAndAdjustStack(); - MOV(32, R(ABI_PARAM1), R((X64Reg)addrReg)); - if (info.displacement) { - ADD(32, R(ABI_PARAM1), Imm32(info.displacement)); - } - switch (info.operandSize) { - case 4: - CALL(ProtectFunction((void *)&Memory::Read_U32, 1)); - break; - default: - BackPatchError(StringFromFormat("We don't handle the size %i yet in backpatch", info.operandSize), codePtr, emAddress); - break; - } - ABI_PopAllCallerSavedRegsAndAdjustStack(); - MOV(32, R(dataReg), R(EAX)); - RET(); - trampolineCodePtr = GetWritableCodePtr(); - - SetCodePtr(codePtr); - int bswapNopCount; - // Check the following BSWAP for REX byte - if ((GetCodePtr()[info.instructionSize] & 0xF0) == 0x40) - bswapNopCount = 3; - else - bswapNopCount = 2; - CALL(trampoline); - NOP((int)info.instructionSize + bswapNopCount - 5); - SetCodePtr(oldCodePtr); - - return codePtr; - } - else if (accessType == 1) - { - // It's a write. Yay. Remember that we don't have to be super efficient since it's "just" a - // hardware access - we can take shortcuts. - //if (emAddress == 0xCC008000) - // PanicAlert("caught a fifo write"); - if (dataReg != EAX) - PanicAlert("Backpatch write - not through EAX"); - CMP(32, R(addrReg), Imm32(0xCC008000)); - FixupBranch skip_fast = J_CC(CC_NE, false); - MOV(32, R(ABI_PARAM1), R((X64Reg)dataReg)); - CALL((void*)Asm::fifoDirectWrite32); - RET(); - SetJumpTarget(skip_fast); - ABI_PushAllCallerSavedRegsAndAdjustStack(); - if (addrReg != ABI_PARAM1) { - //INT3(); - MOV(32, R(ABI_PARAM1), R((X64Reg)dataReg)); - MOV(32, R(ABI_PARAM2), R((X64Reg)addrReg)); - } else { - MOV(32, R(ABI_PARAM2), R((X64Reg)addrReg)); - MOV(32, R(ABI_PARAM1), R((X64Reg)dataReg)); - } - if (info.displacement) { - ADD(32, R(ABI_PARAM2), Imm32(info.displacement)); - } - switch (info.operandSize) { - case 4: - CALL(ProtectFunction((void *)&Memory::Write_U32, 2)); - break; - default: - BackPatchError(StringFromFormat("We don't handle the size %i yet in backpatch", info.operandSize), codePtr, emAddress); - break; - } - ABI_PopAllCallerSavedRegsAndAdjustStack(); - RET(); - - trampolineCodePtr = GetWritableCodePtr(); - - // We know it's EAX so the BSWAP before will be two byte. Overwrite it. - SetCodePtr(codePtr - 2); - CALL(trampoline); - NOP((int)info.instructionSize - 3); - if (info.instructionSize < 3) - PanicAlert("instruction too small"); - SetCodePtr(oldCodePtr); - - // We entered here with a BSWAP-ed EAX. We'll have to swap it back. - ctx->Rax = _byteswap_ulong(ctx->Rax); - - return codePtr - 2; - } - return 0; -#else - return 0; -#endif -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "Common.h" +#include "disasm.h" +#include "JitAsm.h" +#include "JitBackpatch.h" +#include "../../HW/Memmap.h" + +#include "x64Emitter.h" +#include "ABI.h" +#include "Thunk.h" +#include "x64Analyzer.h" + +#include "StringUtil.h" +#include "Jit.h" + +using namespace Gen; + +namespace Jit64 { + +extern u8 *trampolineCodePtr; + +void BackPatchError(const std::string &text, u8 *codePtr, u32 emAddress) { + u64 code_addr = (u64)codePtr; + disassembler disasm; + char disbuf[256]; + memset(disbuf, 0, 256); +#ifdef _M_IX86 + disasm.disasm32 +#else + disasm.disasm64 +#endif + (0, code_addr, codePtr, disbuf); + PanicAlert("%s\n\n" + "Error encountered accessing emulated address %08x.\n" + "Culprit instruction: \n%s\nat %08x%08x", + text.c_str(), emAddress, disbuf, code_addr>>32, code_addr); + return; +} + +// This generates some fairly heavy trampolines, but: +// 1) It's really necessary. We don't know anything about the context. +// 2) It doesn't really hurt. Only instructions that access I/O will get these, and there won't be +// that many of them in a typical program/game. +u8 *BackPatch(u8 *codePtr, int accessType, u32 emAddress, CONTEXT *ctx) +{ +#ifdef _M_X64 + if (!IsInJitCode(codePtr)) + return 0; // this will become a regular crash real soon after this + + u8 *oldCodePtr = GetWritableCodePtr(); + InstructionInfo info; + if (!DisassembleMov(codePtr, info, accessType)) { + BackPatchError("BackPatch - failed to disassemble MOV instruction", codePtr, emAddress); + } + + /* + if (info.isMemoryWrite) { + if (!Memory::IsRAMAddress(emAddress, true)) { + PanicAlert("Exception: Caught write to invalid address %08x", emAddress); + return; + } + BackPatchError("BackPatch - determined that MOV is write, not yet supported and should have been caught before", + codePtr, emAddress); + }*/ + + if (info.operandSize != 4) { + BackPatchError(StringFromFormat("BackPatch - no support for operand size %i", info.operandSize), codePtr, emAddress); + } + u64 code_addr = (u64)codePtr; + X64Reg addrReg = (X64Reg)info.scaledReg; + X64Reg dataReg = (X64Reg)info.regOperandReg; + if (info.otherReg != RBX) + PanicAlert("BackPatch : Base reg not RBX." + "\n\nAttempted to access %08x.", emAddress); + //if (accessType == OP_ACCESS_WRITE) + // PanicAlert("BackPatch : Currently only supporting reads." + // "\n\nAttempted to write to %08x.", emAddress); + + // OK, let's write a trampoline, and a jump to it. + // Later, let's share trampolines. + + // In the first iteration, we assume that all accesses are 32-bit. We also only deal with reads. + // Next step - support writes, special case FIFO writes. Also, support 32-bit mode. + u8 *trampoline = trampolineCodePtr; + SetCodePtr(trampolineCodePtr); + + if (accessType == 0) + { + // It's a read. Easy. + ABI_PushAllCallerSavedRegsAndAdjustStack(); + MOV(32, R(ABI_PARAM1), R((X64Reg)addrReg)); + if (info.displacement) { + ADD(32, R(ABI_PARAM1), Imm32(info.displacement)); + } + switch (info.operandSize) { + case 4: + CALL(ProtectFunction((void *)&Memory::Read_U32, 1)); + break; + default: + BackPatchError(StringFromFormat("We don't handle the size %i yet in backpatch", info.operandSize), codePtr, emAddress); + break; + } + ABI_PopAllCallerSavedRegsAndAdjustStack(); + MOV(32, R(dataReg), R(EAX)); + RET(); + trampolineCodePtr = GetWritableCodePtr(); + + SetCodePtr(codePtr); + int bswapNopCount; + // Check the following BSWAP for REX byte + if ((GetCodePtr()[info.instructionSize] & 0xF0) == 0x40) + bswapNopCount = 3; + else + bswapNopCount = 2; + CALL(trampoline); + NOP((int)info.instructionSize + bswapNopCount - 5); + SetCodePtr(oldCodePtr); + + return codePtr; + } + else if (accessType == 1) + { + // It's a write. Yay. Remember that we don't have to be super efficient since it's "just" a + // hardware access - we can take shortcuts. + //if (emAddress == 0xCC008000) + // PanicAlert("caught a fifo write"); + if (dataReg != EAX) + PanicAlert("Backpatch write - not through EAX"); + CMP(32, R(addrReg), Imm32(0xCC008000)); + FixupBranch skip_fast = J_CC(CC_NE, false); + MOV(32, R(ABI_PARAM1), R((X64Reg)dataReg)); + CALL((void*)Asm::fifoDirectWrite32); + RET(); + SetJumpTarget(skip_fast); + ABI_PushAllCallerSavedRegsAndAdjustStack(); + if (addrReg != ABI_PARAM1) { + //INT3(); + MOV(32, R(ABI_PARAM1), R((X64Reg)dataReg)); + MOV(32, R(ABI_PARAM2), R((X64Reg)addrReg)); + } else { + MOV(32, R(ABI_PARAM2), R((X64Reg)addrReg)); + MOV(32, R(ABI_PARAM1), R((X64Reg)dataReg)); + } + if (info.displacement) { + ADD(32, R(ABI_PARAM2), Imm32(info.displacement)); + } + switch (info.operandSize) { + case 4: + CALL(ProtectFunction((void *)&Memory::Write_U32, 2)); + break; + default: + BackPatchError(StringFromFormat("We don't handle the size %i yet in backpatch", info.operandSize), codePtr, emAddress); + break; + } + ABI_PopAllCallerSavedRegsAndAdjustStack(); + RET(); + + trampolineCodePtr = GetWritableCodePtr(); + + // We know it's EAX so the BSWAP before will be two byte. Overwrite it. + SetCodePtr(codePtr - 2); + CALL(trampoline); + NOP((int)info.instructionSize - 3); + if (info.instructionSize < 3) + PanicAlert("instruction too small"); + SetCodePtr(oldCodePtr); + + // We entered here with a BSWAP-ed EAX. We'll have to swap it back. + ctx->Rax = _byteswap_ulong(ctx->Rax); + + return codePtr - 2; + } + return 0; +#else + return 0; +#endif +} + +} // namespace diff --git a/Source/Core/Core/Src/PowerPC/Jit64/JitCache.cpp b/Source/Core/Core/Src/PowerPC/Jit64/JitCache.cpp index 88c8ddf6a8..fefc435e3b 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/JitCache.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/JitCache.cpp @@ -1,429 +1,429 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -// Enable define below to enable oprofile integration. For this to work, -// it requires at least oprofile version 0.9.4, and changing the build -// system to link the Dolphin executable against libopagent. Since the -// dependency is a little inconvenient and this is possibly a slight -// performance hit, it's not enabled by default, but it's useful for -// locating performance issues. -//#define OPROFILE_REPORT - -#include - -#include "Common.h" -#include "../../Core.h" -#include "MemoryUtil.h" - -#include "../../HW/Memmap.h" -#include "../../CoreTiming.h" - -#include "../PowerPC.h" -#include "../PPCTables.h" -#include "../PPCAnalyst.h" - -#include "x64Emitter.h" -#include "x64Analyzer.h" - -#include "Jit.h" -#include "JitCache.h" -#include "JitAsm.h" - -#include "disasm.h" - -#ifdef OPROFILE_REPORT -#include -#endif - -using namespace Gen; - -namespace Jit64 -{ -#ifdef OPROFILE_REPORT - op_agent_t agent; -#endif - static u8 *codeCache; - static u8 *genFunctions; - static u8 *trampolineCache; - u8 *trampolineCodePtr; -#define INVALID_EXIT 0xFFFFFFFF - void LinkBlockExits(int i); - void LinkBlock(int i); - - enum - { - //CODE_SIZE = 1024*1024*8, - GEN_SIZE = 4096, - TRAMPOLINE_SIZE = 1024*1024, - //MAX_NUM_BLOCKS = 65536, - }; - int CODE_SIZE = 1024*1024*16; // nonconstant to be able to have an option for it - int MAX_NUM_BLOCKS = 65536*2; - - static u8 **blockCodePointers; // cut these in half and force below 2GB? - - static std::multimap links_to; - - static JitBlock *blocks; - static int numBlocks; - - void DestroyBlock(int blocknum, bool invalidate); - - void PrintStats() - { - LOG(DYNA_REC, "JIT Statistics ======================="); - LOG(DYNA_REC, "Number of blocks currently: %i", numBlocks); - LOG(DYNA_REC, "Code cache size: %i b", GetCodePtr() - codeCache); - LOG(DYNA_REC, "======================================"); - } - - void InitCache() - { - if(Core::g_CoreStartupParameter.bJITUnlimitedCache) - { - CODE_SIZE = 1024*1024*8*8; - MAX_NUM_BLOCKS = 65536*8; - } - - codeCache = (u8*)AllocateExecutableMemory(CODE_SIZE); - genFunctions = (u8*)AllocateExecutableMemory(GEN_SIZE); - trampolineCache = (u8*)AllocateExecutableMemory(TRAMPOLINE_SIZE); - trampolineCodePtr = trampolineCache; - -#ifdef OPROFILE_REPORT - agent = op_open_agent(); -#endif - blocks = new JitBlock[MAX_NUM_BLOCKS]; - blockCodePointers = new u8*[MAX_NUM_BLOCKS]; - ClearCache(); - SetCodePtr(genFunctions); - Asm::Generate(); - // Protect the generated functions - WriteProtectMemory(genFunctions, GEN_SIZE, true); - SetCodePtr(codeCache); - } - - void ShutdownCache() - { - UnWriteProtectMemory(genFunctions, GEN_SIZE, true); - FreeMemoryPages(codeCache, CODE_SIZE); - FreeMemoryPages(genFunctions, GEN_SIZE); - FreeMemoryPages(trampolineCache, TRAMPOLINE_SIZE); - delete [] blocks; - delete [] blockCodePointers; - blocks = 0; - blockCodePointers = 0; - numBlocks = 0; -#ifdef OPROFILE_REPORT - op_close_agent(agent); -#endif - } - - /* This clears the JIT cache. It's called from JitCache.cpp when the JIT cache - is full and when saving and loading states */ - void ClearCache() - { - Core::DisplayMessage("Cleared code cache.", 3000); - // Is destroying the blocks really necessary? - for (int i = 0; i < numBlocks; i++) { - DestroyBlock(i, false); - } - links_to.clear(); - trampolineCodePtr = trampolineCache; - numBlocks = 0; - memset(blockCodePointers, 0, sizeof(u8*)*MAX_NUM_BLOCKS); - memset(codeCache, 0xCC, CODE_SIZE); - SetCodePtr(codeCache); - } - - void DestroyBlocksWithFlag(BlockFlag death_flag) - { - for (int i = 0; i < numBlocks; i++) { - if (blocks[i].flags & death_flag) { - DestroyBlock(i, false); - } - } - } - - void ResetCache() - { - ShutdownCache(); - InitCache(); - } - - JitBlock *CurBlock() - { - return &blocks[numBlocks]; - } - - JitBlock *GetBlock(int no) - { - return &blocks[no]; - } - - int GetNumBlocks() - { - return numBlocks; - } - - bool RangeIntersect(int s1, int e1, int s2, int e2) - { - // check if any endpoint is inside the other range - if ( (s1 >= s2 && s1 <= e2) || - (e1 >= s2 && e1 <= e2) || - (s2 >= s1 && s2 <= e1) || - (e2 >= s1 && e2 <= e1)) - return true; - else - return false; - } - - u8 *Jit(u32 emAddress) - { - if (GetCodePtr() >= codeCache + CODE_SIZE - 0x10000 || numBlocks >= MAX_NUM_BLOCKS - 1) - { - LOG(DYNA_REC, "JIT cache full - clearing.") - if(Core::g_CoreStartupParameter.bJITUnlimitedCache) - { - PanicAlert("What? JIT cache still full - clearing."); - } - ClearCache(); - } - - JitBlock &b = blocks[numBlocks]; - b.invalid = false; - b.originalAddress = emAddress; - b.originalFirstOpcode = Memory::ReadFast32(emAddress); - b.exitAddress[0] = INVALID_EXIT; - b.exitAddress[1] = INVALID_EXIT; - b.exitPtrs[0] = 0; - b.exitPtrs[1] = 0; - b.linkStatus[0] = false; - b.linkStatus[1] = false; - - blockCodePointers[numBlocks] = (u8*)DoJit(emAddress, b); //cast away const - Memory::WriteUnchecked_U32((JIT_OPCODE << 26) | numBlocks, emAddress); - - if (jo.enableBlocklink) { - for (int i = 0; i < 2; i++) { - if (b.exitAddress[i] != INVALID_EXIT) { - links_to.insert(std::pair(b.exitAddress[i], numBlocks)); - } - } - - u8 *oldCodePtr = GetWritableCodePtr(); - LinkBlock(numBlocks); - LinkBlockExits(numBlocks); - SetCodePtr(oldCodePtr); - } - -#ifdef OPROFILE_REPORT - char buf[100]; - sprintf(buf, "EmuCode%x", emAddress); - u8* blockStart = blockCodePointers[numBlocks], *blockEnd = GetWritableCodePtr(); - op_write_native_code(agent, buf, (uint64_t)blockStart, - blockStart, blockEnd - blockStart); -#endif - - numBlocks++; //commit the current block - return 0; - } - - void unknown_instruction(UGeckoInstruction _inst) - { - // CCPU::Break(); - PanicAlert("unknown_instruction Jit64 - Fix me ;)"); - _dbg_assert_(DYNA_REC, 0); - } - - u8 **GetCodePointers() - { - return blockCodePointers; - } - - bool IsInJitCode(const u8 *codePtr) { - return codePtr >= codeCache && codePtr <= GetCodePtr(); - } - - void EnterFastRun() - { - CompiledCode pExecAddr = (CompiledCode)Asm::enterCode; - pExecAddr(); - //Will return when PowerPC::state changes - } - - int GetBlockNumberFromAddress(u32 addr) - { - if (!blocks) - return -1; - u32 code = Memory::ReadFast32(addr); - if ((code >> 26) == JIT_OPCODE) - { - //jitted code - unsigned int blockNum = code & 0x03FFFFFF; - if (blockNum >= (unsigned int)numBlocks) { - return -1; - } - - if (blocks[blockNum].originalAddress != addr) - { - //_assert_msg_(DYNA_REC, 0, "GetBlockFromAddress %08x - No match - This is BAD", addr); - return -1; - } - return blockNum; - } - else - { - return -1; - } - } - - u32 GetOriginalCode(u32 address) - { - int num = GetBlockNumberFromAddress(address); - if (num == -1) - return Memory::ReadUnchecked_U32(address); - else - return blocks[num].originalFirstOpcode; - } - - CompiledCode GetCompiledCode(u32 address) - { - int num = GetBlockNumberFromAddress(address); - if (num == -1) - return 0; - else - return (CompiledCode)blockCodePointers[num]; - } - - CompiledCode GetCompiledCodeFromBlock(int blockNumber) - { - return (CompiledCode)blockCodePointers[blockNumber]; - } - - int GetCodeSize() { - return (int)(GetCodePtr() - codeCache); - } - - //Block linker - //Make sure to have as many blocks as possible compiled before calling this - //It's O(N), so it's fast :) - //Can be faster by doing a queue for blocks to link up, and only process those - //Should probably be done - - void LinkBlockExits(int i) - { - JitBlock &b = blocks[i]; - if (b.invalid) - { - // This block is dead. Don't relink it. - return; - } - for (int e = 0; e < 2; e++) - { - if (b.exitAddress[e] != INVALID_EXIT && !b.linkStatus[e]) - { - int destinationBlock = GetBlockNumberFromAddress(b.exitAddress[e]); - if (destinationBlock != -1) - { - SetCodePtr(b.exitPtrs[e]); - JMP(blocks[destinationBlock].checkedEntry, true); - b.linkStatus[e] = true; - } - } - } - } - - /* - if ((b.exitAddress[0] == INVALID_EXIT || b.linkStatus[0]) && - (b.exitAddress[1] == INVALID_EXIT || b.linkStatus[1])) { - unlinked.erase(iter); - if (unlinked.size() > 4000) PanicAlert("Removed from unlinked. Size = %i", unlinked.size()); - } -*/ - using namespace std; - void LinkBlock(int i) - { - LinkBlockExits(i); - JitBlock &b = blocks[i]; - std::map::iterator iter; - pair::iterator, multimap::iterator> ppp; - // equal_range(b) returns pair representing the range - // of element with key b - ppp = links_to.equal_range(b.originalAddress); - if (ppp.first == ppp.second) - return; - for (multimap::iterator iter2 = ppp.first; iter2 != ppp.second; ++iter2) { - // PanicAlert("Linking block %i to block %i", iter2->second, i); - LinkBlockExits(iter2->second); - } - } - - void DestroyBlock(int blocknum, bool invalidate) - { - u32 codebytes = (JIT_OPCODE << 26) | blocknum; //generate from i - JitBlock &b = blocks[blocknum]; - b.invalid = 1; - if (codebytes == Memory::ReadFast32(b.originalAddress)) - { - //nobody has changed it, good - Memory::WriteUnchecked_U32(b.originalFirstOpcode, b.originalAddress); - } - else if (!invalidate) - { - //PanicAlert("Detected code overwrite"); - //else, we may be in trouble, since we apparently know of this block but it's been - //overwritten. We should have thrown it out before, on instruction cache invalidate or something. - //Not ne cessarily bad though , if a game has simply thrown away a lot of code and is now using the space - //for something else, then it's fine. - LOG(MASTER_LOG, "WARNING - ClearCache detected code overwrite @ %08x", blocks[blocknum].originalAddress); - } - - // We don't unlink blocks, we just send anyone who tries to run them back to the dispatcher. - // Not entirely ideal, but .. pretty good. - - // TODO - make sure that the below stuff really is safe. - u8 *prev_code = GetWritableCodePtr(); - // Spurious entrances from previously linked blocks can only come through checkedEntry - SetCodePtr((u8*)b.checkedEntry); - MOV(32, M(&PC), Imm32(b.originalAddress)); - JMP(Asm::dispatcher, true); - SetCodePtr(blockCodePointers[blocknum]); - MOV(32, M(&PC), Imm32(b.originalAddress)); - JMP(Asm::dispatcher, true); - SetCodePtr(prev_code); // reset code pointer - } - -#define BLR_OP 0x4e800020 - - void InvalidateCodeRange(u32 address, u32 length) - { - if (!jo.enableBlocklink) - return; - return; - //This is slow but should be safe (zelda needs it for block linking) - for (int i = 0; i < numBlocks; i++) - { - if (RangeIntersect(blocks[i].originalAddress, blocks[i].originalAddress+blocks[i].originalSize, - address, address + length)) - { - DestroyBlock(i, true); - } - } - } - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// Enable define below to enable oprofile integration. For this to work, +// it requires at least oprofile version 0.9.4, and changing the build +// system to link the Dolphin executable against libopagent. Since the +// dependency is a little inconvenient and this is possibly a slight +// performance hit, it's not enabled by default, but it's useful for +// locating performance issues. +//#define OPROFILE_REPORT + +#include + +#include "Common.h" +#include "../../Core.h" +#include "MemoryUtil.h" + +#include "../../HW/Memmap.h" +#include "../../CoreTiming.h" + +#include "../PowerPC.h" +#include "../PPCTables.h" +#include "../PPCAnalyst.h" + +#include "x64Emitter.h" +#include "x64Analyzer.h" + +#include "Jit.h" +#include "JitCache.h" +#include "JitAsm.h" + +#include "disasm.h" + +#ifdef OPROFILE_REPORT +#include +#endif + +using namespace Gen; + +namespace Jit64 +{ +#ifdef OPROFILE_REPORT + op_agent_t agent; +#endif + static u8 *codeCache; + static u8 *genFunctions; + static u8 *trampolineCache; + u8 *trampolineCodePtr; +#define INVALID_EXIT 0xFFFFFFFF + void LinkBlockExits(int i); + void LinkBlock(int i); + + enum + { + //CODE_SIZE = 1024*1024*8, + GEN_SIZE = 4096, + TRAMPOLINE_SIZE = 1024*1024, + //MAX_NUM_BLOCKS = 65536, + }; + int CODE_SIZE = 1024*1024*16; // nonconstant to be able to have an option for it + int MAX_NUM_BLOCKS = 65536*2; + + static u8 **blockCodePointers; // cut these in half and force below 2GB? + + static std::multimap links_to; + + static JitBlock *blocks; + static int numBlocks; + + void DestroyBlock(int blocknum, bool invalidate); + + void PrintStats() + { + LOG(DYNA_REC, "JIT Statistics ======================="); + LOG(DYNA_REC, "Number of blocks currently: %i", numBlocks); + LOG(DYNA_REC, "Code cache size: %i b", GetCodePtr() - codeCache); + LOG(DYNA_REC, "======================================"); + } + + void InitCache() + { + if(Core::g_CoreStartupParameter.bJITUnlimitedCache) + { + CODE_SIZE = 1024*1024*8*8; + MAX_NUM_BLOCKS = 65536*8; + } + + codeCache = (u8*)AllocateExecutableMemory(CODE_SIZE); + genFunctions = (u8*)AllocateExecutableMemory(GEN_SIZE); + trampolineCache = (u8*)AllocateExecutableMemory(TRAMPOLINE_SIZE); + trampolineCodePtr = trampolineCache; + +#ifdef OPROFILE_REPORT + agent = op_open_agent(); +#endif + blocks = new JitBlock[MAX_NUM_BLOCKS]; + blockCodePointers = new u8*[MAX_NUM_BLOCKS]; + ClearCache(); + SetCodePtr(genFunctions); + Asm::Generate(); + // Protect the generated functions + WriteProtectMemory(genFunctions, GEN_SIZE, true); + SetCodePtr(codeCache); + } + + void ShutdownCache() + { + UnWriteProtectMemory(genFunctions, GEN_SIZE, true); + FreeMemoryPages(codeCache, CODE_SIZE); + FreeMemoryPages(genFunctions, GEN_SIZE); + FreeMemoryPages(trampolineCache, TRAMPOLINE_SIZE); + delete [] blocks; + delete [] blockCodePointers; + blocks = 0; + blockCodePointers = 0; + numBlocks = 0; +#ifdef OPROFILE_REPORT + op_close_agent(agent); +#endif + } + + /* This clears the JIT cache. It's called from JitCache.cpp when the JIT cache + is full and when saving and loading states */ + void ClearCache() + { + Core::DisplayMessage("Cleared code cache.", 3000); + // Is destroying the blocks really necessary? + for (int i = 0; i < numBlocks; i++) { + DestroyBlock(i, false); + } + links_to.clear(); + trampolineCodePtr = trampolineCache; + numBlocks = 0; + memset(blockCodePointers, 0, sizeof(u8*)*MAX_NUM_BLOCKS); + memset(codeCache, 0xCC, CODE_SIZE); + SetCodePtr(codeCache); + } + + void DestroyBlocksWithFlag(BlockFlag death_flag) + { + for (int i = 0; i < numBlocks; i++) { + if (blocks[i].flags & death_flag) { + DestroyBlock(i, false); + } + } + } + + void ResetCache() + { + ShutdownCache(); + InitCache(); + } + + JitBlock *CurBlock() + { + return &blocks[numBlocks]; + } + + JitBlock *GetBlock(int no) + { + return &blocks[no]; + } + + int GetNumBlocks() + { + return numBlocks; + } + + bool RangeIntersect(int s1, int e1, int s2, int e2) + { + // check if any endpoint is inside the other range + if ( (s1 >= s2 && s1 <= e2) || + (e1 >= s2 && e1 <= e2) || + (s2 >= s1 && s2 <= e1) || + (e2 >= s1 && e2 <= e1)) + return true; + else + return false; + } + + u8 *Jit(u32 emAddress) + { + if (GetCodePtr() >= codeCache + CODE_SIZE - 0x10000 || numBlocks >= MAX_NUM_BLOCKS - 1) + { + LOG(DYNA_REC, "JIT cache full - clearing.") + if(Core::g_CoreStartupParameter.bJITUnlimitedCache) + { + PanicAlert("What? JIT cache still full - clearing."); + } + ClearCache(); + } + + JitBlock &b = blocks[numBlocks]; + b.invalid = false; + b.originalAddress = emAddress; + b.originalFirstOpcode = Memory::ReadFast32(emAddress); + b.exitAddress[0] = INVALID_EXIT; + b.exitAddress[1] = INVALID_EXIT; + b.exitPtrs[0] = 0; + b.exitPtrs[1] = 0; + b.linkStatus[0] = false; + b.linkStatus[1] = false; + + blockCodePointers[numBlocks] = (u8*)DoJit(emAddress, b); //cast away const + Memory::WriteUnchecked_U32((JIT_OPCODE << 26) | numBlocks, emAddress); + + if (jo.enableBlocklink) { + for (int i = 0; i < 2; i++) { + if (b.exitAddress[i] != INVALID_EXIT) { + links_to.insert(std::pair(b.exitAddress[i], numBlocks)); + } + } + + u8 *oldCodePtr = GetWritableCodePtr(); + LinkBlock(numBlocks); + LinkBlockExits(numBlocks); + SetCodePtr(oldCodePtr); + } + +#ifdef OPROFILE_REPORT + char buf[100]; + sprintf(buf, "EmuCode%x", emAddress); + u8* blockStart = blockCodePointers[numBlocks], *blockEnd = GetWritableCodePtr(); + op_write_native_code(agent, buf, (uint64_t)blockStart, + blockStart, blockEnd - blockStart); +#endif + + numBlocks++; //commit the current block + return 0; + } + + void unknown_instruction(UGeckoInstruction _inst) + { + // CCPU::Break(); + PanicAlert("unknown_instruction Jit64 - Fix me ;)"); + _dbg_assert_(DYNA_REC, 0); + } + + u8 **GetCodePointers() + { + return blockCodePointers; + } + + bool IsInJitCode(const u8 *codePtr) { + return codePtr >= codeCache && codePtr <= GetCodePtr(); + } + + void EnterFastRun() + { + CompiledCode pExecAddr = (CompiledCode)Asm::enterCode; + pExecAddr(); + //Will return when PowerPC::state changes + } + + int GetBlockNumberFromAddress(u32 addr) + { + if (!blocks) + return -1; + u32 code = Memory::ReadFast32(addr); + if ((code >> 26) == JIT_OPCODE) + { + //jitted code + unsigned int blockNum = code & 0x03FFFFFF; + if (blockNum >= (unsigned int)numBlocks) { + return -1; + } + + if (blocks[blockNum].originalAddress != addr) + { + //_assert_msg_(DYNA_REC, 0, "GetBlockFromAddress %08x - No match - This is BAD", addr); + return -1; + } + return blockNum; + } + else + { + return -1; + } + } + + u32 GetOriginalCode(u32 address) + { + int num = GetBlockNumberFromAddress(address); + if (num == -1) + return Memory::ReadUnchecked_U32(address); + else + return blocks[num].originalFirstOpcode; + } + + CompiledCode GetCompiledCode(u32 address) + { + int num = GetBlockNumberFromAddress(address); + if (num == -1) + return 0; + else + return (CompiledCode)blockCodePointers[num]; + } + + CompiledCode GetCompiledCodeFromBlock(int blockNumber) + { + return (CompiledCode)blockCodePointers[blockNumber]; + } + + int GetCodeSize() { + return (int)(GetCodePtr() - codeCache); + } + + //Block linker + //Make sure to have as many blocks as possible compiled before calling this + //It's O(N), so it's fast :) + //Can be faster by doing a queue for blocks to link up, and only process those + //Should probably be done + + void LinkBlockExits(int i) + { + JitBlock &b = blocks[i]; + if (b.invalid) + { + // This block is dead. Don't relink it. + return; + } + for (int e = 0; e < 2; e++) + { + if (b.exitAddress[e] != INVALID_EXIT && !b.linkStatus[e]) + { + int destinationBlock = GetBlockNumberFromAddress(b.exitAddress[e]); + if (destinationBlock != -1) + { + SetCodePtr(b.exitPtrs[e]); + JMP(blocks[destinationBlock].checkedEntry, true); + b.linkStatus[e] = true; + } + } + } + } + + /* + if ((b.exitAddress[0] == INVALID_EXIT || b.linkStatus[0]) && + (b.exitAddress[1] == INVALID_EXIT || b.linkStatus[1])) { + unlinked.erase(iter); + if (unlinked.size() > 4000) PanicAlert("Removed from unlinked. Size = %i", unlinked.size()); + } +*/ + using namespace std; + void LinkBlock(int i) + { + LinkBlockExits(i); + JitBlock &b = blocks[i]; + std::map::iterator iter; + pair::iterator, multimap::iterator> ppp; + // equal_range(b) returns pair representing the range + // of element with key b + ppp = links_to.equal_range(b.originalAddress); + if (ppp.first == ppp.second) + return; + for (multimap::iterator iter2 = ppp.first; iter2 != ppp.second; ++iter2) { + // PanicAlert("Linking block %i to block %i", iter2->second, i); + LinkBlockExits(iter2->second); + } + } + + void DestroyBlock(int blocknum, bool invalidate) + { + u32 codebytes = (JIT_OPCODE << 26) | blocknum; //generate from i + JitBlock &b = blocks[blocknum]; + b.invalid = 1; + if (codebytes == Memory::ReadFast32(b.originalAddress)) + { + //nobody has changed it, good + Memory::WriteUnchecked_U32(b.originalFirstOpcode, b.originalAddress); + } + else if (!invalidate) + { + //PanicAlert("Detected code overwrite"); + //else, we may be in trouble, since we apparently know of this block but it's been + //overwritten. We should have thrown it out before, on instruction cache invalidate or something. + //Not ne cessarily bad though , if a game has simply thrown away a lot of code and is now using the space + //for something else, then it's fine. + LOG(MASTER_LOG, "WARNING - ClearCache detected code overwrite @ %08x", blocks[blocknum].originalAddress); + } + + // We don't unlink blocks, we just send anyone who tries to run them back to the dispatcher. + // Not entirely ideal, but .. pretty good. + + // TODO - make sure that the below stuff really is safe. + u8 *prev_code = GetWritableCodePtr(); + // Spurious entrances from previously linked blocks can only come through checkedEntry + SetCodePtr((u8*)b.checkedEntry); + MOV(32, M(&PC), Imm32(b.originalAddress)); + JMP(Asm::dispatcher, true); + SetCodePtr(blockCodePointers[blocknum]); + MOV(32, M(&PC), Imm32(b.originalAddress)); + JMP(Asm::dispatcher, true); + SetCodePtr(prev_code); // reset code pointer + } + +#define BLR_OP 0x4e800020 + + void InvalidateCodeRange(u32 address, u32 length) + { + if (!jo.enableBlocklink) + return; + return; + //This is slow but should be safe (zelda needs it for block linking) + for (int i = 0; i < numBlocks; i++) + { + if (RangeIntersect(blocks[i].originalAddress, blocks[i].originalAddress+blocks[i].originalSize, + address, address + length)) + { + DestroyBlock(i, true); + } + } + } + +} // namespace diff --git a/Source/Core/Core/Src/PowerPC/Jit64/JitCore.cpp b/Source/Core/Core/Src/PowerPC/Jit64/JitCore.cpp index 5192bbd031..fe39963dc2 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/JitCore.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/JitCore.cpp @@ -1,59 +1,59 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ -#include "JitCore.h" -#include "JitCache.h" -#include "JitAsm.h" -#include "Jit.h" - -#include "../../HW/Memmap.h" -#include "../../HW/CPU.h" -#include "../../HW/DSP.h" -#include "../../HW/GPFifo.h" - -#include "../../HW/VideoInterface.h" -#include "../../HW/SerialInterface.h" -#include "../../Core.h" - -namespace Jit64 -{ -namespace Core -{ - -void Init() -{ - ::Jit64::Init(); - InitCache(); - Asm::compareEnabled = ::Core::g_CoreStartupParameter.bRunCompareClient; -} - -void Shutdown() -{ - ShutdownCache(); -} - -void SingleStep() -{ - Run(); -} - -void Run() -{ - EnterFastRun(); -} - -} // namespace -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +#include "JitCore.h" +#include "JitCache.h" +#include "JitAsm.h" +#include "Jit.h" + +#include "../../HW/Memmap.h" +#include "../../HW/CPU.h" +#include "../../HW/DSP.h" +#include "../../HW/GPFifo.h" + +#include "../../HW/VideoInterface.h" +#include "../../HW/SerialInterface.h" +#include "../../Core.h" + +namespace Jit64 +{ +namespace Core +{ + +void Init() +{ + ::Jit64::Init(); + InitCache(); + Asm::compareEnabled = ::Core::g_CoreStartupParameter.bRunCompareClient; +} + +void Shutdown() +{ + ShutdownCache(); +} + +void SingleStep() +{ + Run(); +} + +void Run() +{ + EnterFastRun(); +} + +} // namespace +} // namespace diff --git a/Source/Core/Core/Src/PowerPC/Jit64/JitRegCache.cpp b/Source/Core/Core/Src/PowerPC/Jit64/JitRegCache.cpp index 84ba14f2fd..ad6b6c76fa 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/JitRegCache.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/JitRegCache.cpp @@ -1,397 +1,397 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ -#include "../PowerPC.h" -#include "../PPCTables.h" -#include "../PPCAnalyst.h" -#include "Jit.h" -#include "JitCache.h" -#include "JitAsm.h" -#include "JitRegCache.h" - - -using namespace Gen; -using namespace PowerPC; - -namespace Jit64 -{ - GPRRegCache gpr; - FPURegCache fpr; - - void RegCache::Start(PPCAnalyst::BlockRegStats &stats) - { - for (int i = 0; i < NUMXREGS; i++) - { - xregs[i].free = true; - xregs[i].dirty = false; - xlocks[i] = false; - } - for (int i = 0; i < 32; i++) - { - regs[i].location = GetDefaultLocation(i); - regs[i].away = false; - } - - // todo: sort to find the most popular regs - /* - int maxPreload = 2; - for (int i = 0; i < 32; i++) - { - if (stats.numReads[i] > 2 || stats.numWrites[i] >= 2) - { - LoadToX64(i, true, false); //stats.firstRead[i] <= stats.firstWrite[i], false); - maxPreload--; - if (!maxPreload) - break; - } - }*/ - //Find top regs - preload them (load bursts ain't bad) - //But only preload IF written OR reads >= 3 - } - - // these are powerpc reg indices - void RegCache::Lock(int p1, int p2, int p3, int p4) - { - locks[p1] = true; - if (p2 != 0xFF) locks[p2] = true; - if (p3 != 0xFF) locks[p3] = true; - if (p4 != 0xFF) locks[p4] = true; - } - - // these are x64 reg indices - void RegCache::LockX(int x1, int x2, int x3, int x4) - { - if (xlocks[x1]) { - PanicAlert("RegCache: x %i already locked!"); - } - xlocks[x1] = true; - if (x2 != 0xFF) xlocks[x2] = true; - if (x3 != 0xFF) xlocks[x3] = true; - if (x4 != 0xFF) xlocks[x4] = true; - } - - bool RegCache::IsFreeX(int xreg) const - { - return xregs[xreg].free && !xlocks[xreg]; - } - - void RegCache::UnlockAll() - { - for (int i = 0; i < 32; i++) - locks[i] = false; - } - - void RegCache::UnlockAllX() - { - for (int i = 0; i < NUMXREGS; i++) - xlocks[i] = false; - } - - X64Reg RegCache::GetFreeXReg() - { - int aCount; - const int *aOrder = GetAllocationOrder(aCount); - for (int i = 0; i < aCount; i++) - { - X64Reg xr = (X64Reg)aOrder[i]; - if (!xlocks[xr] && xregs[xr].free) - { - return (X64Reg)xr; - } - } - //Okay, not found :( Force grab one - - //TODO - add a pass to grab xregs whose ppcreg is not used in the next 3 instructions - for (int i = 0; i < aCount; i++) - { - X64Reg xr = (X64Reg)aOrder[i]; - if (xlocks[xr]) - continue; - int preg = xregs[xr].ppcReg; - if (!locks[preg]) - { - StoreFromX64(preg); - return xr; - } - } - //Still no dice? Die! - _assert_msg_(DYNA_REC, 0, "Regcache ran out of regs"); - return (X64Reg) -1; - } - - void RegCache::SaveState() - { - memcpy(saved_locks, locks, sizeof(locks)); - memcpy(saved_xlocks, xlocks, sizeof(xlocks)); - memcpy(saved_regs, regs, sizeof(regs)); - memcpy(saved_xregs, xregs, sizeof(xregs)); - } - - void RegCache::LoadState() - { - memcpy(xlocks, saved_xlocks, sizeof(xlocks)); - memcpy(locks, saved_locks, sizeof(locks)); - memcpy(regs, saved_regs, sizeof(regs)); - memcpy(xregs, saved_xregs, sizeof(xregs)); - } - - void RegCache::FlushR(X64Reg reg) - { - if (reg >= NUMXREGS) - PanicAlert("Flushing non existent reg"); - if (!xregs[reg].free) - { - StoreFromX64(xregs[reg].ppcReg); - } - } - - void RegCache::SanityCheck() const - { - for (int i = 0; i < 32; i++) { - if (regs[i].away) { - if (regs[i].location.IsSimpleReg()) { - Gen::X64Reg simple = regs[i].location.GetSimpleReg(); - if (xlocks[simple]) { - PanicAlert("%08x : PPC Reg %i is in locked x64 register %i", js.compilerPC, i, regs[i].location.GetSimpleReg()); - } - if (xregs[simple].ppcReg != i) { - PanicAlert("%08x : Xreg/ppcreg mismatch"); - } - } - } - } - } - - void RegCache::DiscardRegContentsIfCached(int preg) - { - if (regs[preg].away && regs[preg].location.IsSimpleReg()) - { - xregs[regs[preg].location.GetSimpleReg()].free = true; - xregs[regs[preg].location.GetSimpleReg()].dirty = false; - regs[preg].away = false; - } - } - - - void GPRRegCache::SetImmediate32(int preg, u32 immValue) - { - DiscardRegContentsIfCached(preg); - regs[preg].away = true; - regs[preg].location = Imm32(immValue); - } - - void GPRRegCache::Start(PPCAnalyst::BlockRegStats &stats) - { - RegCache::Start(stats); - } - - void FPURegCache::Start(PPCAnalyst::BlockRegStats &stats) - { - RegCache::Start(stats); - } - - const int *GPRRegCache::GetAllocationOrder(int &count) - { - static const int allocationOrder[] = - { -#ifdef _M_X64 -#ifdef _WIN32 - RSI, RDI, R12, R13, R14, R8, R9, R10, R11 //, RCX -#else - RBP, R12, R13, R14, R8, R9, R10, R11, //, RCX -#endif -#elif _M_IX86 - ESI, EDI, EBX, EBP, EDX, ECX, -#endif - }; - count = sizeof(allocationOrder) / sizeof(const int); - return allocationOrder; - } - - const int *FPURegCache::GetAllocationOrder(int &count) - { - static const int allocationOrder[] = - { -#ifdef _M_X64 - XMM6, XMM7, XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15, XMM2, XMM3, XMM4, XMM5 -#elif _M_IX86 - XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, -#endif - }; - count = sizeof(allocationOrder) / sizeof(int); - return allocationOrder; - } - - OpArg GPRRegCache::GetDefaultLocation(int reg) const - { - return M(&ppcState.gpr[reg]); - } - - OpArg FPURegCache::GetDefaultLocation(int reg) const - { - return M(&ppcState.ps[reg][0]); - } - - void RegCache::KillImmediate(int preg) - { - if (regs[preg].away && regs[preg].location.IsImm()) - { - LoadToX64(preg, true, true); - } - } - - void GPRRegCache::LoadToX64(int i, bool doLoad, bool makeDirty) - { - if (!regs[i].away && regs[i].location.IsImm()) - PanicAlert("Bad immedaite"); - - if (!regs[i].away || (regs[i].away && regs[i].location.IsImm())) - { - X64Reg xr = GetFreeXReg(); - if (xregs[xr].dirty) PanicAlert("Xreg already dirty"); - if (xlocks[xr]) PanicAlert("GetFreeXReg returned locked register"); - xregs[xr].free = false; - xregs[xr].ppcReg = i; - xregs[xr].dirty = makeDirty || regs[i].location.IsImm(); - OpArg newloc = ::Gen::R(xr); - if (doLoad || regs[i].location.IsImm()) - MOV(32, newloc, regs[i].location); - for (int j = 0; j < 32; j++) - { - if (i != j && regs[j].location.IsSimpleReg() && regs[j].location.GetSimpleReg() == xr) - { - Crash(); - } - } - regs[i].away = true; - regs[i].location = newloc; - } - else - { - // reg location must be simplereg; memory locations - // and immediates are taken care of above. - xregs[RX(i)].dirty |= makeDirty; - } - if (xlocks[RX(i)]) { - PanicAlert("Seriously WTF, this reg should have been flushed"); - } - } - - void GPRRegCache::StoreFromX64(int i) - { - if (regs[i].away) - { - bool doStore; - if (regs[i].location.IsSimpleReg()) - { - X64Reg xr = RX(i); - xregs[xr].free = true; - xregs[xr].ppcReg = -1; - doStore = xregs[xr].dirty; - xregs[xr].dirty = false; - } - else - { - //must be immediate - do nothing - doStore = true; - } - OpArg newLoc = GetDefaultLocation(i); - // if (doStore) //<-- Breaks JIT compilation - MOV(32, newLoc, regs[i].location); - regs[i].location = newLoc; - regs[i].away = false; - } - } - - void FPURegCache::LoadToX64(int i, bool doLoad, bool makeDirty) - { - _assert_msg_(DYNA_REC, !regs[i].location.IsImm(), "WTF - load - imm"); - if (!regs[i].away) - { - // Reg is at home in the memory register file. Let's pull it out. - X64Reg xr = GetFreeXReg(); - _assert_msg_(DYNA_REC, xr >= 0 && xr < NUMXREGS, "WTF - load - invalid reg"); - xregs[xr].ppcReg = i; - xregs[xr].free = false; - xregs[xr].dirty = makeDirty; - OpArg newloc = ::Gen::R(xr); - if (doLoad) { - if (!regs[i].location.IsImm() && (regs[i].location.offset & 0xF)) { - PanicAlert("WARNING - misaligned fp register location %i", i); - } - MOVAPD(xr, regs[i].location); - } - regs[i].location = newloc; - regs[i].away = true; - } else { - // There are no immediates in the FPR reg file, so we already had this in a register. Make dirty as necessary. - xregs[RX(i)].dirty |= makeDirty; - } - } - - void FPURegCache::StoreFromX64(int i) - { - _assert_msg_(DYNA_REC, !regs[i].location.IsImm(), "WTF - store - imm"); - if (regs[i].away) - { - X64Reg xr = regs[i].location.GetSimpleReg(); - _assert_msg_(DYNA_REC, xr >= 0 && xr < NUMXREGS, "WTF - store - invalid reg"); - xregs[xr].free = true; - xregs[xr].dirty = false; - xregs[xr].ppcReg = -1; - OpArg newLoc = GetDefaultLocation(i); - MOVAPD(newLoc, xr); - regs[i].location = newLoc; - regs[i].away = false; - } - else - { - // _assert_msg_(DYNA_REC,0,"already stored"); - } - } - - void RegCache::Flush(FlushMode mode) - { - for (int i = 0; i < NUMXREGS; i++) { - if (xlocks[i]) - PanicAlert("Somone forgot to unlock X64 reg %i.", i); - } - for (int i = 0; i < 32; i++) - { - if (locks[i]) - { - PanicAlert("Somebody forgot to unlock PPC reg %i.", i); - } - if (regs[i].away) - { - if (regs[i].location.IsSimpleReg()) - { - X64Reg xr = RX(i); - StoreFromX64(i); - xregs[xr].dirty = false; - } - else if (regs[i].location.IsImm()) - { - StoreFromX64(i); - } - else - { - _assert_msg_(DYNA_REC,0,"Jit64 - Flush unhandled case, reg %i", i); - } - } - } - } -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +#include "../PowerPC.h" +#include "../PPCTables.h" +#include "../PPCAnalyst.h" +#include "Jit.h" +#include "JitCache.h" +#include "JitAsm.h" +#include "JitRegCache.h" + + +using namespace Gen; +using namespace PowerPC; + +namespace Jit64 +{ + GPRRegCache gpr; + FPURegCache fpr; + + void RegCache::Start(PPCAnalyst::BlockRegStats &stats) + { + for (int i = 0; i < NUMXREGS; i++) + { + xregs[i].free = true; + xregs[i].dirty = false; + xlocks[i] = false; + } + for (int i = 0; i < 32; i++) + { + regs[i].location = GetDefaultLocation(i); + regs[i].away = false; + } + + // todo: sort to find the most popular regs + /* + int maxPreload = 2; + for (int i = 0; i < 32; i++) + { + if (stats.numReads[i] > 2 || stats.numWrites[i] >= 2) + { + LoadToX64(i, true, false); //stats.firstRead[i] <= stats.firstWrite[i], false); + maxPreload--; + if (!maxPreload) + break; + } + }*/ + //Find top regs - preload them (load bursts ain't bad) + //But only preload IF written OR reads >= 3 + } + + // these are powerpc reg indices + void RegCache::Lock(int p1, int p2, int p3, int p4) + { + locks[p1] = true; + if (p2 != 0xFF) locks[p2] = true; + if (p3 != 0xFF) locks[p3] = true; + if (p4 != 0xFF) locks[p4] = true; + } + + // these are x64 reg indices + void RegCache::LockX(int x1, int x2, int x3, int x4) + { + if (xlocks[x1]) { + PanicAlert("RegCache: x %i already locked!"); + } + xlocks[x1] = true; + if (x2 != 0xFF) xlocks[x2] = true; + if (x3 != 0xFF) xlocks[x3] = true; + if (x4 != 0xFF) xlocks[x4] = true; + } + + bool RegCache::IsFreeX(int xreg) const + { + return xregs[xreg].free && !xlocks[xreg]; + } + + void RegCache::UnlockAll() + { + for (int i = 0; i < 32; i++) + locks[i] = false; + } + + void RegCache::UnlockAllX() + { + for (int i = 0; i < NUMXREGS; i++) + xlocks[i] = false; + } + + X64Reg RegCache::GetFreeXReg() + { + int aCount; + const int *aOrder = GetAllocationOrder(aCount); + for (int i = 0; i < aCount; i++) + { + X64Reg xr = (X64Reg)aOrder[i]; + if (!xlocks[xr] && xregs[xr].free) + { + return (X64Reg)xr; + } + } + //Okay, not found :( Force grab one + + //TODO - add a pass to grab xregs whose ppcreg is not used in the next 3 instructions + for (int i = 0; i < aCount; i++) + { + X64Reg xr = (X64Reg)aOrder[i]; + if (xlocks[xr]) + continue; + int preg = xregs[xr].ppcReg; + if (!locks[preg]) + { + StoreFromX64(preg); + return xr; + } + } + //Still no dice? Die! + _assert_msg_(DYNA_REC, 0, "Regcache ran out of regs"); + return (X64Reg) -1; + } + + void RegCache::SaveState() + { + memcpy(saved_locks, locks, sizeof(locks)); + memcpy(saved_xlocks, xlocks, sizeof(xlocks)); + memcpy(saved_regs, regs, sizeof(regs)); + memcpy(saved_xregs, xregs, sizeof(xregs)); + } + + void RegCache::LoadState() + { + memcpy(xlocks, saved_xlocks, sizeof(xlocks)); + memcpy(locks, saved_locks, sizeof(locks)); + memcpy(regs, saved_regs, sizeof(regs)); + memcpy(xregs, saved_xregs, sizeof(xregs)); + } + + void RegCache::FlushR(X64Reg reg) + { + if (reg >= NUMXREGS) + PanicAlert("Flushing non existent reg"); + if (!xregs[reg].free) + { + StoreFromX64(xregs[reg].ppcReg); + } + } + + void RegCache::SanityCheck() const + { + for (int i = 0; i < 32; i++) { + if (regs[i].away) { + if (regs[i].location.IsSimpleReg()) { + Gen::X64Reg simple = regs[i].location.GetSimpleReg(); + if (xlocks[simple]) { + PanicAlert("%08x : PPC Reg %i is in locked x64 register %i", js.compilerPC, i, regs[i].location.GetSimpleReg()); + } + if (xregs[simple].ppcReg != i) { + PanicAlert("%08x : Xreg/ppcreg mismatch"); + } + } + } + } + } + + void RegCache::DiscardRegContentsIfCached(int preg) + { + if (regs[preg].away && regs[preg].location.IsSimpleReg()) + { + xregs[regs[preg].location.GetSimpleReg()].free = true; + xregs[regs[preg].location.GetSimpleReg()].dirty = false; + regs[preg].away = false; + } + } + + + void GPRRegCache::SetImmediate32(int preg, u32 immValue) + { + DiscardRegContentsIfCached(preg); + regs[preg].away = true; + regs[preg].location = Imm32(immValue); + } + + void GPRRegCache::Start(PPCAnalyst::BlockRegStats &stats) + { + RegCache::Start(stats); + } + + void FPURegCache::Start(PPCAnalyst::BlockRegStats &stats) + { + RegCache::Start(stats); + } + + const int *GPRRegCache::GetAllocationOrder(int &count) + { + static const int allocationOrder[] = + { +#ifdef _M_X64 +#ifdef _WIN32 + RSI, RDI, R12, R13, R14, R8, R9, R10, R11 //, RCX +#else + RBP, R12, R13, R14, R8, R9, R10, R11, //, RCX +#endif +#elif _M_IX86 + ESI, EDI, EBX, EBP, EDX, ECX, +#endif + }; + count = sizeof(allocationOrder) / sizeof(const int); + return allocationOrder; + } + + const int *FPURegCache::GetAllocationOrder(int &count) + { + static const int allocationOrder[] = + { +#ifdef _M_X64 + XMM6, XMM7, XMM8, XMM9, XMM10, XMM11, XMM12, XMM13, XMM14, XMM15, XMM2, XMM3, XMM4, XMM5 +#elif _M_IX86 + XMM2, XMM3, XMM4, XMM5, XMM6, XMM7, +#endif + }; + count = sizeof(allocationOrder) / sizeof(int); + return allocationOrder; + } + + OpArg GPRRegCache::GetDefaultLocation(int reg) const + { + return M(&ppcState.gpr[reg]); + } + + OpArg FPURegCache::GetDefaultLocation(int reg) const + { + return M(&ppcState.ps[reg][0]); + } + + void RegCache::KillImmediate(int preg) + { + if (regs[preg].away && regs[preg].location.IsImm()) + { + LoadToX64(preg, true, true); + } + } + + void GPRRegCache::LoadToX64(int i, bool doLoad, bool makeDirty) + { + if (!regs[i].away && regs[i].location.IsImm()) + PanicAlert("Bad immedaite"); + + if (!regs[i].away || (regs[i].away && regs[i].location.IsImm())) + { + X64Reg xr = GetFreeXReg(); + if (xregs[xr].dirty) PanicAlert("Xreg already dirty"); + if (xlocks[xr]) PanicAlert("GetFreeXReg returned locked register"); + xregs[xr].free = false; + xregs[xr].ppcReg = i; + xregs[xr].dirty = makeDirty || regs[i].location.IsImm(); + OpArg newloc = ::Gen::R(xr); + if (doLoad || regs[i].location.IsImm()) + MOV(32, newloc, regs[i].location); + for (int j = 0; j < 32; j++) + { + if (i != j && regs[j].location.IsSimpleReg() && regs[j].location.GetSimpleReg() == xr) + { + Crash(); + } + } + regs[i].away = true; + regs[i].location = newloc; + } + else + { + // reg location must be simplereg; memory locations + // and immediates are taken care of above. + xregs[RX(i)].dirty |= makeDirty; + } + if (xlocks[RX(i)]) { + PanicAlert("Seriously WTF, this reg should have been flushed"); + } + } + + void GPRRegCache::StoreFromX64(int i) + { + if (regs[i].away) + { + bool doStore; + if (regs[i].location.IsSimpleReg()) + { + X64Reg xr = RX(i); + xregs[xr].free = true; + xregs[xr].ppcReg = -1; + doStore = xregs[xr].dirty; + xregs[xr].dirty = false; + } + else + { + //must be immediate - do nothing + doStore = true; + } + OpArg newLoc = GetDefaultLocation(i); + // if (doStore) //<-- Breaks JIT compilation + MOV(32, newLoc, regs[i].location); + regs[i].location = newLoc; + regs[i].away = false; + } + } + + void FPURegCache::LoadToX64(int i, bool doLoad, bool makeDirty) + { + _assert_msg_(DYNA_REC, !regs[i].location.IsImm(), "WTF - load - imm"); + if (!regs[i].away) + { + // Reg is at home in the memory register file. Let's pull it out. + X64Reg xr = GetFreeXReg(); + _assert_msg_(DYNA_REC, xr >= 0 && xr < NUMXREGS, "WTF - load - invalid reg"); + xregs[xr].ppcReg = i; + xregs[xr].free = false; + xregs[xr].dirty = makeDirty; + OpArg newloc = ::Gen::R(xr); + if (doLoad) { + if (!regs[i].location.IsImm() && (regs[i].location.offset & 0xF)) { + PanicAlert("WARNING - misaligned fp register location %i", i); + } + MOVAPD(xr, regs[i].location); + } + regs[i].location = newloc; + regs[i].away = true; + } else { + // There are no immediates in the FPR reg file, so we already had this in a register. Make dirty as necessary. + xregs[RX(i)].dirty |= makeDirty; + } + } + + void FPURegCache::StoreFromX64(int i) + { + _assert_msg_(DYNA_REC, !regs[i].location.IsImm(), "WTF - store - imm"); + if (regs[i].away) + { + X64Reg xr = regs[i].location.GetSimpleReg(); + _assert_msg_(DYNA_REC, xr >= 0 && xr < NUMXREGS, "WTF - store - invalid reg"); + xregs[xr].free = true; + xregs[xr].dirty = false; + xregs[xr].ppcReg = -1; + OpArg newLoc = GetDefaultLocation(i); + MOVAPD(newLoc, xr); + regs[i].location = newLoc; + regs[i].away = false; + } + else + { + // _assert_msg_(DYNA_REC,0,"already stored"); + } + } + + void RegCache::Flush(FlushMode mode) + { + for (int i = 0; i < NUMXREGS; i++) { + if (xlocks[i]) + PanicAlert("Somone forgot to unlock X64 reg %i.", i); + } + for (int i = 0; i < 32; i++) + { + if (locks[i]) + { + PanicAlert("Somebody forgot to unlock PPC reg %i.", i); + } + if (regs[i].away) + { + if (regs[i].location.IsSimpleReg()) + { + X64Reg xr = RX(i); + StoreFromX64(i); + xregs[xr].dirty = false; + } + else if (regs[i].location.IsImm()) + { + StoreFromX64(i); + } + else + { + _assert_msg_(DYNA_REC,0,"Jit64 - Flush unhandled case, reg %i", i); + } + } + } + } +} diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit_Branch.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit_Branch.cpp index 483097498b..6ea665b68f 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit_Branch.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit_Branch.cpp @@ -1,262 +1,262 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ -#include "Common.h" -#include "Thunk.h" - -#include "../PowerPC.h" -#include "../../CoreTiming.h" -#include "../PPCTables.h" -#include "x64Emitter.h" - -#include "Jit.h" -#include "JitRegCache.h" -#include "JitCache.h" -#include "JitAsm.h" - -// The branches are known good, or at least reasonably good. -// No need for a disable-mechanism. - -// If defined, clears CR0 at blr and bl-s. If the assumption that -// flags never carry over between functions holds, then the task for -// an optimizer becomes much easier. - -// #define ACID_TEST - -// Zelda and many more games seem to pass the Acid Test. - -using namespace Gen; -namespace Jit64 -{ - void sc(UGeckoInstruction _inst) - { - gpr.Flush(FLUSH_ALL); - fpr.Flush(FLUSH_ALL); - WriteExceptionExit(EXCEPTION_SYSCALL); - } - - void rfi(UGeckoInstruction _inst) - { - gpr.Flush(FLUSH_ALL); - fpr.Flush(FLUSH_ALL); - //Bits SRR1[0, 5-9, 16-23, 25-27, 30-31] are placed into the corresponding bits of the MSR. - //MSR[13] is set to 0. - const u32 mask = 0x87C0FF73; - // MSR = (MSR & ~mask) | (SRR1 & mask); - MOV(32, R(EAX), M(&MSR)); - MOV(32, R(ECX), M(&SRR1)); - AND(32, R(EAX), Imm32(~mask)); - AND(32, R(ECX), Imm32(mask)); - OR(32, R(EAX), R(ECX)); - // MSR &= 0xFFFDFFFF; //TODO: VERIFY - AND(32, R(EAX), Imm32(0xFFFDFFFF)); - MOV(32, M(&MSR), R(EAX)); - // NPC = SRR0; - MOV(32, R(EAX), M(&SRR0)); - WriteRfiExitDestInEAX(); - } - - void bx(UGeckoInstruction inst) - { - if (inst.LK) - MOV(32, M(&LR), Imm32(js.compilerPC + 4)); - gpr.Flush(FLUSH_ALL); - fpr.Flush(FLUSH_ALL); - - if (js.isLastInstruction) - { - u32 destination; - if (inst.AA) - destination = SignExt26(inst.LI << 2); - else - destination = js.compilerPC + SignExt26(inst.LI << 2); -#ifdef ACID_TEST - if (inst.LK) - AND(32, M(&CR), Imm32(~(0xFF000000))); -#endif - if (destination == js.compilerPC) - { - //PanicAlert("Idle loop detected at %08x", destination); - // CALL(ProtectFunction(&CoreTiming::Idle, 0)); - // JMP(Asm::testExceptions, true); - // make idle loops go faster - js.downcountAmount += 8; - } - WriteExit(destination, 0); - } - else { - // TODO: investigate the good old method of merging blocks here. - PanicAlert("bx not last instruction of block"); // this should not happen - } - } - - // TODO - optimize to hell and beyond - // TODO - make nice easy to optimize special cases for the most common - // variants of this instruction. - void bcx(UGeckoInstruction inst) - { - _assert_msg_(DYNA_REC, js.isLastInstruction, "bcx not last instruction of block"); - - gpr.Flush(FLUSH_ALL); - fpr.Flush(FLUSH_ALL); - - CCFlags branch = CC_Z; - - //const bool only_counter_check = (inst.BO & 16) ? true : false; - //const bool only_condition_check = (inst.BO & 4) ? true : false; - //if (only_condition_check && only_counter_check) - // PanicAlert("Bizarre bcx encountered. Likely bad or corrupt code."); - bool doFullTest = (inst.BO & 16) == 0 && (inst.BO & 4) == 0; - bool ctrDecremented = false; - - if ((inst.BO & 16) == 0) // Test a CR bit - { - TEST(32, M(&CR), Imm32(0x80000000 >> inst.BI)); - if (inst.BO & 8) // Conditional branch - branch = CC_NZ; - else - branch = CC_Z; - - if (doFullTest) - SETcc(branch, R(EAX)); - } - else - { - if (doFullTest) - MOV(32, R(EAX), Imm32(1)); - } - - if ((inst.BO & 4) == 0) // Decrement and test CTR - { - // Decrement CTR - SUB(32, M(&CTR), Imm8(1)); - ctrDecremented = true; - // Test whether to branch if CTR is zero or not - if (inst.BO & 2) - branch = CC_Z; - else - branch = CC_NZ; - - if (doFullTest) - SETcc(branch, R(ECX)); - } - else - { - if (doFullTest) - MOV(32, R(ECX), Imm32(1)); - } - - if (doFullTest) - { - TEST(32, R(EAX), R(ECX)); - branch = CC_Z; - } - else - { - if (branch == CC_Z) - branch = CC_NZ; - else - branch = CC_Z; - } - - if (!ctrDecremented && (inst.BO & BO_DONT_DECREMENT_FLAG) == 0) - { - SUB(32, M(&CTR), Imm8(1)); - } - FixupBranch skip; - if (inst.BO != 20) - { - skip = J_CC(branch); - } - u32 destination; - if (inst.LK) - MOV(32, M(&LR), Imm32(js.compilerPC + 4)); - if(inst.AA) - destination = SignExt16(inst.BD << 2); - else - destination = js.compilerPC + SignExt16(inst.BD << 2); - WriteExit(destination, 0); - if (inst.BO != 20) - { - SetJumpTarget(skip); - WriteExit(js.compilerPC + 4, 1); - } - } - - void bcctrx(UGeckoInstruction inst) - { - gpr.Flush(FLUSH_ALL); - fpr.Flush(FLUSH_ALL); - - // bool fastway = true; - - if ((inst.BO & 16) == 0) - { - PanicAlert("Bizarro bcctrx %08x, not supported.", inst.hex); - _assert_msg_(DYNA_REC, 0, "Bizarro bcctrx"); - /* - fastway = false; - MOV(32, M(&PC), Imm32(js.compilerPC+4)); - MOV(32, R(EAX), M(&CR)); - XOR(32, R(ECX), R(ECX)); - AND(32, R(EAX), Imm32(0x80000000 >> inst.BI)); - - CCFlags branch; - if(inst.BO & 8) - branch = CC_NZ; - else - branch = CC_Z; - */ - // TODO(ector): Why is this commented out? - //SETcc(branch, R(ECX)); - // check for EBX - //TEST(32, R(ECX), R(ECX)); - //linkEnd = J_CC(branch); - } - // NPC = CTR & 0xfffffffc; - MOV(32, R(EAX), M(&CTR)); - if (inst.LK) - MOV(32, M(&LR), Imm32(js.compilerPC + 4)); // LR = PC + 4; - AND(32, R(EAX), Imm32(0xFFFFFFFC)); - WriteExitDestInEAX(0); - } - - - void bclrx(UGeckoInstruction inst) - { - gpr.Flush(FLUSH_ALL); - fpr.Flush(FLUSH_ALL); - //Special case BLR - if (inst.hex == 0x4e800020) - { - //CDynaRegCache::Flush(); - // This below line can be used to prove that blr "eats flags" in practice. - // This observation will let us do a lot of fun observations. -#ifdef ACID_TEST - AND(32, M(&CR), Imm32(~(0xFF000000))); -#endif - MOV(32, R(EAX), M(&LR)); - MOV(32, M(&PC), R(EAX)); - WriteExitDestInEAX(0); - return; - } - // Call interpreter - Default(inst); - MOV(32, R(EAX), M(&NPC)); - WriteExitDestInEAX(0); - } - -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +#include "Common.h" +#include "Thunk.h" + +#include "../PowerPC.h" +#include "../../CoreTiming.h" +#include "../PPCTables.h" +#include "x64Emitter.h" + +#include "Jit.h" +#include "JitRegCache.h" +#include "JitCache.h" +#include "JitAsm.h" + +// The branches are known good, or at least reasonably good. +// No need for a disable-mechanism. + +// If defined, clears CR0 at blr and bl-s. If the assumption that +// flags never carry over between functions holds, then the task for +// an optimizer becomes much easier. + +// #define ACID_TEST + +// Zelda and many more games seem to pass the Acid Test. + +using namespace Gen; +namespace Jit64 +{ + void sc(UGeckoInstruction _inst) + { + gpr.Flush(FLUSH_ALL); + fpr.Flush(FLUSH_ALL); + WriteExceptionExit(EXCEPTION_SYSCALL); + } + + void rfi(UGeckoInstruction _inst) + { + gpr.Flush(FLUSH_ALL); + fpr.Flush(FLUSH_ALL); + //Bits SRR1[0, 5-9, 16-23, 25-27, 30-31] are placed into the corresponding bits of the MSR. + //MSR[13] is set to 0. + const u32 mask = 0x87C0FF73; + // MSR = (MSR & ~mask) | (SRR1 & mask); + MOV(32, R(EAX), M(&MSR)); + MOV(32, R(ECX), M(&SRR1)); + AND(32, R(EAX), Imm32(~mask)); + AND(32, R(ECX), Imm32(mask)); + OR(32, R(EAX), R(ECX)); + // MSR &= 0xFFFDFFFF; //TODO: VERIFY + AND(32, R(EAX), Imm32(0xFFFDFFFF)); + MOV(32, M(&MSR), R(EAX)); + // NPC = SRR0; + MOV(32, R(EAX), M(&SRR0)); + WriteRfiExitDestInEAX(); + } + + void bx(UGeckoInstruction inst) + { + if (inst.LK) + MOV(32, M(&LR), Imm32(js.compilerPC + 4)); + gpr.Flush(FLUSH_ALL); + fpr.Flush(FLUSH_ALL); + + if (js.isLastInstruction) + { + u32 destination; + if (inst.AA) + destination = SignExt26(inst.LI << 2); + else + destination = js.compilerPC + SignExt26(inst.LI << 2); +#ifdef ACID_TEST + if (inst.LK) + AND(32, M(&CR), Imm32(~(0xFF000000))); +#endif + if (destination == js.compilerPC) + { + //PanicAlert("Idle loop detected at %08x", destination); + // CALL(ProtectFunction(&CoreTiming::Idle, 0)); + // JMP(Asm::testExceptions, true); + // make idle loops go faster + js.downcountAmount += 8; + } + WriteExit(destination, 0); + } + else { + // TODO: investigate the good old method of merging blocks here. + PanicAlert("bx not last instruction of block"); // this should not happen + } + } + + // TODO - optimize to hell and beyond + // TODO - make nice easy to optimize special cases for the most common + // variants of this instruction. + void bcx(UGeckoInstruction inst) + { + _assert_msg_(DYNA_REC, js.isLastInstruction, "bcx not last instruction of block"); + + gpr.Flush(FLUSH_ALL); + fpr.Flush(FLUSH_ALL); + + CCFlags branch = CC_Z; + + //const bool only_counter_check = (inst.BO & 16) ? true : false; + //const bool only_condition_check = (inst.BO & 4) ? true : false; + //if (only_condition_check && only_counter_check) + // PanicAlert("Bizarre bcx encountered. Likely bad or corrupt code."); + bool doFullTest = (inst.BO & 16) == 0 && (inst.BO & 4) == 0; + bool ctrDecremented = false; + + if ((inst.BO & 16) == 0) // Test a CR bit + { + TEST(32, M(&CR), Imm32(0x80000000 >> inst.BI)); + if (inst.BO & 8) // Conditional branch + branch = CC_NZ; + else + branch = CC_Z; + + if (doFullTest) + SETcc(branch, R(EAX)); + } + else + { + if (doFullTest) + MOV(32, R(EAX), Imm32(1)); + } + + if ((inst.BO & 4) == 0) // Decrement and test CTR + { + // Decrement CTR + SUB(32, M(&CTR), Imm8(1)); + ctrDecremented = true; + // Test whether to branch if CTR is zero or not + if (inst.BO & 2) + branch = CC_Z; + else + branch = CC_NZ; + + if (doFullTest) + SETcc(branch, R(ECX)); + } + else + { + if (doFullTest) + MOV(32, R(ECX), Imm32(1)); + } + + if (doFullTest) + { + TEST(32, R(EAX), R(ECX)); + branch = CC_Z; + } + else + { + if (branch == CC_Z) + branch = CC_NZ; + else + branch = CC_Z; + } + + if (!ctrDecremented && (inst.BO & BO_DONT_DECREMENT_FLAG) == 0) + { + SUB(32, M(&CTR), Imm8(1)); + } + FixupBranch skip; + if (inst.BO != 20) + { + skip = J_CC(branch); + } + u32 destination; + if (inst.LK) + MOV(32, M(&LR), Imm32(js.compilerPC + 4)); + if(inst.AA) + destination = SignExt16(inst.BD << 2); + else + destination = js.compilerPC + SignExt16(inst.BD << 2); + WriteExit(destination, 0); + if (inst.BO != 20) + { + SetJumpTarget(skip); + WriteExit(js.compilerPC + 4, 1); + } + } + + void bcctrx(UGeckoInstruction inst) + { + gpr.Flush(FLUSH_ALL); + fpr.Flush(FLUSH_ALL); + + // bool fastway = true; + + if ((inst.BO & 16) == 0) + { + PanicAlert("Bizarro bcctrx %08x, not supported.", inst.hex); + _assert_msg_(DYNA_REC, 0, "Bizarro bcctrx"); + /* + fastway = false; + MOV(32, M(&PC), Imm32(js.compilerPC+4)); + MOV(32, R(EAX), M(&CR)); + XOR(32, R(ECX), R(ECX)); + AND(32, R(EAX), Imm32(0x80000000 >> inst.BI)); + + CCFlags branch; + if(inst.BO & 8) + branch = CC_NZ; + else + branch = CC_Z; + */ + // TODO(ector): Why is this commented out? + //SETcc(branch, R(ECX)); + // check for EBX + //TEST(32, R(ECX), R(ECX)); + //linkEnd = J_CC(branch); + } + // NPC = CTR & 0xfffffffc; + MOV(32, R(EAX), M(&CTR)); + if (inst.LK) + MOV(32, M(&LR), Imm32(js.compilerPC + 4)); // LR = PC + 4; + AND(32, R(EAX), Imm32(0xFFFFFFFC)); + WriteExitDestInEAX(0); + } + + + void bclrx(UGeckoInstruction inst) + { + gpr.Flush(FLUSH_ALL); + fpr.Flush(FLUSH_ALL); + //Special case BLR + if (inst.hex == 0x4e800020) + { + //CDynaRegCache::Flush(); + // This below line can be used to prove that blr "eats flags" in practice. + // This observation will let us do a lot of fun observations. +#ifdef ACID_TEST + AND(32, M(&CR), Imm32(~(0xFF000000))); +#endif + MOV(32, R(EAX), M(&LR)); + MOV(32, M(&PC), R(EAX)); + WriteExitDestInEAX(0); + return; + } + // Call interpreter + Default(inst); + MOV(32, R(EAX), M(&NPC)); + WriteExitDestInEAX(0); + } + +} diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit_FloatingPoint.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit_FloatingPoint.cpp index 59c814f8bf..e056d8e2e3 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit_FloatingPoint.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit_FloatingPoint.cpp @@ -1,234 +1,234 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" - -#include "../../Core.h" -#include "../PowerPC.h" -#include "../PPCTables.h" -#include "x64Emitter.h" - -#include "Jit.h" -#include "JitCache.h" -#include "JitRegCache.h" -#include "Jit_Util.h" - -#define INSTRUCTION_START -// #define INSTRUCTION_START Default(inst); return; - -namespace Jit64 -{ - const u64 GC_ALIGNED16(psSignBits2[2]) = {0x8000000000000000ULL, 0x8000000000000000ULL}; - const u64 GC_ALIGNED16(psAbsMask2[2]) = {0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL}; - const double GC_ALIGNED16(psOneOne2[2]) = {1.0, 1.0}; - - void fp_tri_op(int d, int a, int b, bool reversible, bool dupe, void (*op)(X64Reg, OpArg)) - { - fpr.Lock(d, a, b); - if (d == a) - { - fpr.LoadToX64(d, true); - op(fpr.RX(d), fpr.R(b)); - } - else if (d == b && reversible) - { - fpr.LoadToX64(d, true); - op(fpr.RX(d), fpr.R(a)); - } - else if (a != d && b != d) - { - // Sources different from d, can use rather quick solution - fpr.LoadToX64(d, !dupe); - MOVSD(fpr.RX(d), fpr.R(a)); - op(fpr.RX(d), fpr.R(b)); - } - else if (b != d) - { - fpr.LoadToX64(d, !dupe); - MOVSD(XMM0, fpr.R(b)); - MOVSD(fpr.RX(d), fpr.R(a)); - op(fpr.RX(d), Gen::R(XMM0)); - } - else // Other combo, must use two temps :( - { - MOVSD(XMM0, fpr.R(a)); - MOVSD(XMM1, fpr.R(b)); - fpr.LoadToX64(d, !dupe); - op(XMM0, Gen::R(XMM1)); - MOVSD(fpr.RX(d), Gen::R(XMM0)); - } - if (dupe) { - ForceSinglePrecisionS(fpr.RX(d)); - MOVDDUP(fpr.RX(d), fpr.R(d)); - } - fpr.UnlockAll(); - } - - void fp_arith_s(UGeckoInstruction inst) - { - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITFloatingPointOff) - {Default(inst); return;} // turn off from debugger - INSTRUCTION_START; - if (inst.Rc) { - Default(inst); return; - } - bool dupe = inst.OPCD == 59; - switch (inst.SUBOP5) - { - case 18: fp_tri_op(inst.FD, inst.FA, inst.FB, false, dupe, &DIVSD); break; //div - case 20: fp_tri_op(inst.FD, inst.FA, inst.FB, false, dupe, &SUBSD); break; //sub - case 21: fp_tri_op(inst.FD, inst.FA, inst.FB, true, dupe, &ADDSD); break; //add - case 23: //sel - Default(inst); - break; - case 24: //res - Default(inst); - break; - case 25: fp_tri_op(inst.FD, inst.FA, inst.FC, true, dupe, &MULSD); break; //mul - default: - _assert_msg_(DYNA_REC, 0, "fp_arith_s WTF!!!"); - } - } - - void fmaddXX(UGeckoInstruction inst) - { - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITFloatingPointOff) - {Default(inst); return;} // turn off from debugger - INSTRUCTION_START; - if (inst.Rc) { - Default(inst); return; - } - - bool single_precision = inst.OPCD == 59; - - int a = inst.FA; - int b = inst.FB; - int c = inst.FC; - int d = inst.FD; - - fpr.Lock(a, b, c, d); - MOVSD(XMM0, fpr.R(a)); - switch (inst.SUBOP5) - { - case 28: //msub - MULSD(XMM0, fpr.R(c)); - SUBSD(XMM0, fpr.R(b)); - break; - case 29: //madd - MULSD(XMM0, fpr.R(c)); - ADDSD(XMM0, fpr.R(b)); - break; - case 30: //nmsub - MULSD(XMM0, fpr.R(c)); - SUBSD(XMM0, fpr.R(b)); - XORPD(XMM0, M((void*)&psSignBits2)); - break; - case 31: //nmadd - MULSD(XMM0, fpr.R(c)); - ADDSD(XMM0, fpr.R(b)); - XORPD(XMM0, M((void*)&psSignBits2)); - break; - } - fpr.LoadToX64(d, false); - //YES it is necessary to dupe the result :( - //TODO : analysis - does the top reg get used? If so, dupe, if not, don't. - if (single_precision) { - ForceSinglePrecisionS(XMM0); - MOVDDUP(fpr.RX(d), R(XMM0)); - } else { - MOVSD(fpr.RX(d), R(XMM0)); - } - fpr.UnlockAll(); - } - - void fmrx(UGeckoInstruction inst) - { - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITFloatingPointOff) - {Default(inst); return;} // turn off from debugger - INSTRUCTION_START; - if (inst.Rc) { - Default(inst); return; - } - int d = inst.FD; - int b = inst.FB; - fpr.LoadToX64(d, true); // we don't want to destroy the high bit - MOVSD(fpr.RX(d), fpr.R(b)); - } - - void fcmpx(UGeckoInstruction inst) - { - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITFloatingPointOff) - {Default(inst); return;} // turn off from debugger - INSTRUCTION_START; - if (jo.fpAccurateFlags) - { - Default(inst); - return; - } - bool ordered = inst.SUBOP10 == 32; - /* - double fa = rPS0(_inst.FA); - double fb = rPS0(_inst.FB); - u32 compareResult; - - if(IsNAN(fa) || IsNAN(fb)) compareResult = 1; - else if(fa < fb) compareResult = 8; - else if(fa > fb) compareResult = 4; - else compareResult = 2; - - FPSCR.FPRF = compareResult; - CR = (CR & (~(0xf0000000 >> (_inst.CRFD * 4)))) | (compareResult << ((7 - _inst.CRFD) * 4)); -*/ - int a = inst.FA; - int b = inst.FB; - int crf = inst.CRFD; - int shift = crf * 4; - //FPSCR - //XOR(32,R(EAX),R(EAX)); - - fpr.Lock(a,b); - if (a != b) - { - fpr.LoadToX64(a, true); - } - - AND(32, M(&CR), Imm32(~(0xF0000000 >> shift))); - if (ordered) - COMISD(fpr.R(a).GetSimpleReg(), fpr.R(b)); - else - UCOMISD(fpr.R(a).GetSimpleReg(), fpr.R(b)); - FixupBranch pLesser = J_CC(CC_B); - FixupBranch pGreater = J_CC(CC_A); - // _x86Reg == 0 - MOV(32, R(EAX), Imm32(0x20000000)); - FixupBranch continue1 = J(); - // _x86Reg > 0 - SetJumpTarget(pGreater); - MOV(32, R(EAX), Imm32(0x40000000)); - FixupBranch continue2 = J(); - // _x86Reg < 0 - SetJumpTarget(pLesser); - MOV(32, R(EAX), Imm32(0x80000000)); - SetJumpTarget(continue1); - SetJumpTarget(continue2); - SHR(32, R(EAX), Imm8(shift)); - OR(32, M(&CR), R(EAX)); - fpr.UnlockAll(); - } - -} - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" + +#include "../../Core.h" +#include "../PowerPC.h" +#include "../PPCTables.h" +#include "x64Emitter.h" + +#include "Jit.h" +#include "JitCache.h" +#include "JitRegCache.h" +#include "Jit_Util.h" + +#define INSTRUCTION_START +// #define INSTRUCTION_START Default(inst); return; + +namespace Jit64 +{ + const u64 GC_ALIGNED16(psSignBits2[2]) = {0x8000000000000000ULL, 0x8000000000000000ULL}; + const u64 GC_ALIGNED16(psAbsMask2[2]) = {0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL}; + const double GC_ALIGNED16(psOneOne2[2]) = {1.0, 1.0}; + + void fp_tri_op(int d, int a, int b, bool reversible, bool dupe, void (*op)(X64Reg, OpArg)) + { + fpr.Lock(d, a, b); + if (d == a) + { + fpr.LoadToX64(d, true); + op(fpr.RX(d), fpr.R(b)); + } + else if (d == b && reversible) + { + fpr.LoadToX64(d, true); + op(fpr.RX(d), fpr.R(a)); + } + else if (a != d && b != d) + { + // Sources different from d, can use rather quick solution + fpr.LoadToX64(d, !dupe); + MOVSD(fpr.RX(d), fpr.R(a)); + op(fpr.RX(d), fpr.R(b)); + } + else if (b != d) + { + fpr.LoadToX64(d, !dupe); + MOVSD(XMM0, fpr.R(b)); + MOVSD(fpr.RX(d), fpr.R(a)); + op(fpr.RX(d), Gen::R(XMM0)); + } + else // Other combo, must use two temps :( + { + MOVSD(XMM0, fpr.R(a)); + MOVSD(XMM1, fpr.R(b)); + fpr.LoadToX64(d, !dupe); + op(XMM0, Gen::R(XMM1)); + MOVSD(fpr.RX(d), Gen::R(XMM0)); + } + if (dupe) { + ForceSinglePrecisionS(fpr.RX(d)); + MOVDDUP(fpr.RX(d), fpr.R(d)); + } + fpr.UnlockAll(); + } + + void fp_arith_s(UGeckoInstruction inst) + { + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITFloatingPointOff) + {Default(inst); return;} // turn off from debugger + INSTRUCTION_START; + if (inst.Rc) { + Default(inst); return; + } + bool dupe = inst.OPCD == 59; + switch (inst.SUBOP5) + { + case 18: fp_tri_op(inst.FD, inst.FA, inst.FB, false, dupe, &DIVSD); break; //div + case 20: fp_tri_op(inst.FD, inst.FA, inst.FB, false, dupe, &SUBSD); break; //sub + case 21: fp_tri_op(inst.FD, inst.FA, inst.FB, true, dupe, &ADDSD); break; //add + case 23: //sel + Default(inst); + break; + case 24: //res + Default(inst); + break; + case 25: fp_tri_op(inst.FD, inst.FA, inst.FC, true, dupe, &MULSD); break; //mul + default: + _assert_msg_(DYNA_REC, 0, "fp_arith_s WTF!!!"); + } + } + + void fmaddXX(UGeckoInstruction inst) + { + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITFloatingPointOff) + {Default(inst); return;} // turn off from debugger + INSTRUCTION_START; + if (inst.Rc) { + Default(inst); return; + } + + bool single_precision = inst.OPCD == 59; + + int a = inst.FA; + int b = inst.FB; + int c = inst.FC; + int d = inst.FD; + + fpr.Lock(a, b, c, d); + MOVSD(XMM0, fpr.R(a)); + switch (inst.SUBOP5) + { + case 28: //msub + MULSD(XMM0, fpr.R(c)); + SUBSD(XMM0, fpr.R(b)); + break; + case 29: //madd + MULSD(XMM0, fpr.R(c)); + ADDSD(XMM0, fpr.R(b)); + break; + case 30: //nmsub + MULSD(XMM0, fpr.R(c)); + SUBSD(XMM0, fpr.R(b)); + XORPD(XMM0, M((void*)&psSignBits2)); + break; + case 31: //nmadd + MULSD(XMM0, fpr.R(c)); + ADDSD(XMM0, fpr.R(b)); + XORPD(XMM0, M((void*)&psSignBits2)); + break; + } + fpr.LoadToX64(d, false); + //YES it is necessary to dupe the result :( + //TODO : analysis - does the top reg get used? If so, dupe, if not, don't. + if (single_precision) { + ForceSinglePrecisionS(XMM0); + MOVDDUP(fpr.RX(d), R(XMM0)); + } else { + MOVSD(fpr.RX(d), R(XMM0)); + } + fpr.UnlockAll(); + } + + void fmrx(UGeckoInstruction inst) + { + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITFloatingPointOff) + {Default(inst); return;} // turn off from debugger + INSTRUCTION_START; + if (inst.Rc) { + Default(inst); return; + } + int d = inst.FD; + int b = inst.FB; + fpr.LoadToX64(d, true); // we don't want to destroy the high bit + MOVSD(fpr.RX(d), fpr.R(b)); + } + + void fcmpx(UGeckoInstruction inst) + { + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITFloatingPointOff) + {Default(inst); return;} // turn off from debugger + INSTRUCTION_START; + if (jo.fpAccurateFlags) + { + Default(inst); + return; + } + bool ordered = inst.SUBOP10 == 32; + /* + double fa = rPS0(_inst.FA); + double fb = rPS0(_inst.FB); + u32 compareResult; + + if(IsNAN(fa) || IsNAN(fb)) compareResult = 1; + else if(fa < fb) compareResult = 8; + else if(fa > fb) compareResult = 4; + else compareResult = 2; + + FPSCR.FPRF = compareResult; + CR = (CR & (~(0xf0000000 >> (_inst.CRFD * 4)))) | (compareResult << ((7 - _inst.CRFD) * 4)); +*/ + int a = inst.FA; + int b = inst.FB; + int crf = inst.CRFD; + int shift = crf * 4; + //FPSCR + //XOR(32,R(EAX),R(EAX)); + + fpr.Lock(a,b); + if (a != b) + { + fpr.LoadToX64(a, true); + } + + AND(32, M(&CR), Imm32(~(0xF0000000 >> shift))); + if (ordered) + COMISD(fpr.R(a).GetSimpleReg(), fpr.R(b)); + else + UCOMISD(fpr.R(a).GetSimpleReg(), fpr.R(b)); + FixupBranch pLesser = J_CC(CC_B); + FixupBranch pGreater = J_CC(CC_A); + // _x86Reg == 0 + MOV(32, R(EAX), Imm32(0x20000000)); + FixupBranch continue1 = J(); + // _x86Reg > 0 + SetJumpTarget(pGreater); + MOV(32, R(EAX), Imm32(0x40000000)); + FixupBranch continue2 = J(); + // _x86Reg < 0 + SetJumpTarget(pLesser); + MOV(32, R(EAX), Imm32(0x80000000)); + SetJumpTarget(continue1); + SetJumpTarget(continue2); + SHR(32, R(EAX), Imm8(shift)); + OR(32, M(&CR), R(EAX)); + fpr.UnlockAll(); + } + +} + diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit_Integer.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit_Integer.cpp index 63f44b0e89..fa37c26a32 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit_Integer.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit_Integer.cpp @@ -1,1018 +1,1018 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "../../Core.h" // include "Common.h", "CoreParameter.h", SCoreStartupParameter -#include "../PowerPC.h" -#include "../PPCTables.h" -#include "x64Emitter.h" - -#include "Jit.h" -#include "JitCache.h" -#include "JitRegCache.h" -#include "JitAsm.h" - -// #define INSTRUCTION_START Default(inst); return; -#define INSTRUCTION_START - -namespace Jit64 -{ - // Assumes that the flags were just set through an addition. - void GenerateCarry(X64Reg temp_reg) { - SETcc(CC_C, R(temp_reg)); - AND(32, M(&XER), Imm32(~(1 << 29))); - SHL(32, R(temp_reg), Imm8(29)); - OR(32, M(&XER), R(temp_reg)); - } - - typedef u32 (*Operation)(u32 a, u32 b); - u32 Add(u32 a, u32 b) {return a + b;} - u32 Or (u32 a, u32 b) {return a | b;} - u32 And(u32 a, u32 b) {return a & b;} - u32 Xor(u32 a, u32 b) {return a ^ b;} - - void regimmop(int d, int a, bool binary, u32 value, Operation doop, void(*op)(int, const OpArg&, const OpArg&), bool Rc = false, bool carry = false) - { - gpr.Lock(d, a); - if (a || binary || carry) // yeh nasty special case addic - { - if (a == d) - { - if (gpr.R(d).IsImm() && !carry) - { - gpr.SetImmediate32(d, doop((u32)gpr.R(d).offset, value)); - } - else - { - if (gpr.R(d).IsImm()) - gpr.LoadToX64(d, false); - op(32, gpr.R(d), Imm32(value)); //m_GPR[d] = m_GPR[_inst.RA] + _inst.SIMM_16; - if (carry) - GenerateCarry(EAX); - } - } - else - { - gpr.LoadToX64(d, false); - MOV(32, gpr.R(d), gpr.R(a)); - op(32, gpr.R(d), Imm32(value)); //m_GPR[d] = m_GPR[_inst.RA] + _inst.SIMM_16; - if (carry) - GenerateCarry(EAX); - } - } - else if (doop == Add) - { - // a == 0, which for these instructions imply value = 0 - gpr.SetImmediate32(d, value); - } - else - { - _assert_msg_(DYNA_REC, 0, "WTF regimmop"); - } - if (Rc) - { - // Todo - special case immediates. - MOV(32, R(EAX), gpr.R(d)); - CALL((u8*)Asm::computeRc); - } - gpr.UnlockAll(); - } - - void reg_imm(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int d = inst.RD, a = inst.RA, s = inst.RS; - switch (inst.OPCD) - { - case 14: // addi - // occasionally used as MOV - emulate, with immediate propagation - if (gpr.R(a).IsImm() && d != a && a != 0) { - gpr.SetImmediate32(d, (u32)gpr.R(a).offset + (u32)(s32)(s16)inst.SIMM_16); - } else if (inst.SIMM_16 == 0 && d != a && a != 0) { - gpr.Lock(a, d); - gpr.LoadToX64(d, false, true); - MOV(32, gpr.R(d), gpr.R(a)); - gpr.UnlockAll(); - } else { - regimmop(d, a, false, (u32)(s32)inst.SIMM_16, Add, ADD); //addi - } - break; - case 15: regimmop(d, a, false, (u32)inst.SIMM_16 << 16, Add, ADD); break; //addis - case 24: - if (a == 0 && s == 0 && inst.UIMM == 0 && !inst.Rc) //check for nop - {NOP(); return;} //make the nop visible in the generated code. not much use but interesting if we see one. - regimmop(a, s, true, inst.UIMM, Or, OR); - break; //ori - case 25: regimmop(a, s, true, inst.UIMM << 16, Or, OR, false); break;//oris - case 28: regimmop(a, s, true, inst.UIMM, And, AND, true); break; - case 29: regimmop(a, s, true, inst.UIMM << 16, And, AND, true); break; - case 26: regimmop(a, s, true, inst.UIMM, Xor, XOR, false); break; //xori - case 27: regimmop(a, s, true, inst.UIMM << 16, Xor, XOR, false); break; //xoris - case 12: //regimmop(d, a, false, (u32)(s32)inst.SIMM_16, Add, ADD, false, true); //addic - case 13: //regimmop(d, a, true, (u32)(s32)inst.SIMM_16, Add, ADD, true, true); //addic_rc - default: - Default(inst); - break; - } - } - - // unsigned - void cmpli(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA; - u32 uimm = inst.UIMM; - int crf = inst.CRFD; - int shift = crf * 4; - gpr.KillImmediate(a); // todo, optimize instead, but unlikely to make a difference - AND(32, M(&CR), Imm32(~(0xF0000000 >> (crf*4)))); - CMP(32, gpr.R(a), Imm32(uimm)); - FixupBranch pLesser = J_CC(CC_B); - FixupBranch pGreater = J_CC(CC_A); - - MOV(32, R(EAX), Imm32(0x20000000 >> shift)); // _x86Reg == 0 - FixupBranch continue1 = J(); - - SetJumpTarget(pGreater); - MOV(32, R(EAX), Imm32(0x40000000 >> shift)); // _x86Reg > 0 - FixupBranch continue2 = J(); - - SetJumpTarget(pLesser); - MOV(32, R(EAX), Imm32(0x80000000 >> shift));// _x86Reg < 0 - SetJumpTarget(continue1); - SetJumpTarget(continue2); - OR(32, M(&CR), R(EAX)); - } - - // signed - void cmpi(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA; - s32 simm = (s32)(s16)inst.UIMM; - int crf = inst.CRFD; - int shift = crf * 4; - gpr.KillImmediate(a); // todo, optimize instead, but unlikely to make a difference - AND(32, M(&CR), Imm32(~(0xF0000000 >> (crf*4)))); - CMP(32, gpr.R(a), Imm32(simm)); - FixupBranch pLesser = J_CC(CC_L); - FixupBranch pGreater = J_CC(CC_G); - // _x86Reg == 0 - MOV(32, R(EAX), Imm32(0x20000000 >> shift)); - FixupBranch continue1 = J(); - // _x86Reg > 0 - SetJumpTarget(pGreater); - MOV(32, R(EAX), Imm32(0x40000000 >> shift)); - FixupBranch continue2 = J(); - // _x86Reg < 0 - SetJumpTarget(pLesser); - MOV(32, R(EAX), Imm32(0x80000000 >> shift)); - SetJumpTarget(continue1); - SetJumpTarget(continue2); - OR(32, M(&CR), R(EAX)); - } - - // signed - void cmp(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA; - int b = inst.RB; - int crf = inst.CRFD; - int shift = crf * 4; - gpr.Lock(a, b); - gpr.LoadToX64(a, true, false); - AND(32, M(&CR), Imm32(~(0xF0000000 >> (crf*4)))); - CMP(32, gpr.R(a), gpr.R(b)); - FixupBranch pLesser = J_CC(CC_L); - FixupBranch pGreater = J_CC(CC_G); - // _x86Reg == 0 - MOV(32, R(EAX), Imm32(0x20000000 >> shift)); - FixupBranch continue1 = J(); - // _x86Reg > 0 - SetJumpTarget(pGreater); - MOV(32, R(EAX), Imm32(0x40000000 >> shift)); - FixupBranch continue2 = J(); - // _x86Reg < 0 - SetJumpTarget(pLesser); - MOV(32, R(EAX), Imm32(0x80000000 >> shift)); - SetJumpTarget(continue1); - SetJumpTarget(continue2); - OR(32, M(&CR), R(EAX)); - gpr.UnlockAll(); - } - - // unsigned - void cmpl(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA; - int b = inst.RB; - int crf = inst.CRFD; - int shift = crf * 4; - gpr.Lock(a, b); - gpr.LoadToX64(a, true, false); - AND(32, M(&CR), Imm32(~(0xF0000000 >> (crf*4)))); - CMP(32, gpr.R(a), gpr.R(b)); - FixupBranch pLesser = J_CC(CC_B); - FixupBranch pGreater = J_CC(CC_A); - // _x86Reg == 0 - MOV(32, R(EAX), Imm32(0x20000000 >> shift)); - FixupBranch continue1 = J(); - // _x86Reg > 0 - SetJumpTarget(pGreater); - MOV(32, R(EAX), Imm32(0x40000000 >> shift)); - FixupBranch continue2 = J(); - // _x86Reg < 0 - SetJumpTarget(pLesser); - MOV(32, R(EAX), Imm32(0x80000000 >> shift)); - SetJumpTarget(continue1); - SetJumpTarget(continue2); - OR(32, M(&CR), R(EAX)); - gpr.UnlockAll(); - } - - void orx(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA; - int s = inst.RS; - int b = inst.RB; - - if (s == b && s != a) - { - gpr.Lock(a,s); - gpr.LoadToX64(a, false); - MOV(32, gpr.R(a), gpr.R(s)); - gpr.UnlockAll(); - } - else - { - gpr.Lock(a, s, b); - gpr.LoadToX64(a, (a == s || a == b), true); - if (a == s) - OR(32, gpr.R(a), gpr.R(b)); - else if (a == b) - OR(32, gpr.R(a), gpr.R(s)); - else { - MOV(32, gpr.R(a), gpr.R(b)); - OR(32, gpr.R(a), gpr.R(s)); - } - gpr.UnlockAll(); - } - - if (inst.Rc) - { - MOV(32, R(EAX), gpr.R(a)); - CALL((u8*)Asm::computeRc); - } - } - - - // m_GPR[_inst.RA] = m_GPR[_inst.RS] ^ m_GPR[_inst.RB]; - void xorx(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA; - int s = inst.RS; - int b = inst.RB; - - if (s == b) { - gpr.SetImmediate32(a, 0); - } - else - { - gpr.LoadToX64(a, a == s || a == b, true); - gpr.Lock(a, s, b); - MOV(32, R(EAX), gpr.R(s)); - XOR(32, R(EAX), gpr.R(b)); - MOV(32, gpr.R(a), R(EAX)); - gpr.UnlockAll(); - } - - if (inst.Rc) - { - MOV(32, R(EAX), gpr.R(a)); - CALL((u8*)Asm::computeRc); - } - } - - void andx(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA, s = inst.RS, b = inst.RB; - if (a != s && a != b) { - gpr.LoadToX64(a, false, true); - } else { - gpr.LoadToX64(a, true, true); - } - gpr.Lock(a, s, b); - MOV(32, R(EAX), gpr.R(s)); - AND(32, R(EAX), gpr.R(b)); - MOV(32, gpr.R(a), R(EAX)); - gpr.UnlockAll(); - - if (inst.Rc) { - // result is already in eax - CALL((u8*)Asm::computeRc); - } - } - - void extsbx(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA, - s = inst.RS; - gpr.LoadToX64(a, a == s, true); - // Always force moving to EAX because it isn't possible - // to refer to the lowest byte of some registers, at least in - // 32-bit mode. - MOV(32, R(EAX), gpr.R(s)); - MOVSX(32, 8, gpr.RX(a), R(AL)); // watch out for ah and friends - if (inst.Rc) { - MOV(32, R(EAX), gpr.R(a)); - CALL((u8*)Asm::computeRc); - } - } - - void extshx(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA, s = inst.RS; - gpr.KillImmediate(s); - gpr.LoadToX64(a, a == s, true); - // This looks a little dangerous, but it's safe because - // every 32-bit register has a 16-bit half at the same index - // as the 32-bit register. - MOVSX(32, 16, gpr.RX(a), gpr.R(s)); - if (inst.Rc) { - MOV(32, R(EAX), gpr.R(a)); - CALL((u8*)Asm::computeRc); - } - } - - void subfic(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA, d = inst.RD; - gpr.FlushLockX(ECX); - gpr.Lock(a, d); - gpr.LoadToX64(d, a == d, true); - int imm = inst.SIMM_16; - MOV(32, R(EAX), gpr.R(a)); - NOT(32, R(EAX)); - ADD(32, R(EAX), Imm32(imm + 1)); - MOV(32, gpr.R(d), R(EAX)); - GenerateCarry(ECX); - gpr.UnlockAll(); - gpr.UnlockAllX(); - // This instruction has no RC flag - } - - void subfcx(UGeckoInstruction inst) - { - INSTRUCTION_START; - Default(inst); - return; - /* - u32 a = m_GPR[_inst.RA]; - u32 b = m_GPR[_inst.RB]; - m_GPR[_inst.RD] = b - a; - SetCarry(a == 0 || Helper_Carry(b, 0-a)); - - if (_inst.OE) PanicAlert("OE: subfcx"); - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); - */ - } - - void subfex(UGeckoInstruction inst) - { - INSTRUCTION_START; - Default(inst); - return; - /* - u32 a = m_GPR[_inst.RA]; - u32 b = m_GPR[_inst.RB]; - int carry = GetCarry(); - m_GPR[_inst.RD] = (~a) + b + carry; - SetCarry(Helper_Carry(~a, b) || Helper_Carry((~a) + b, carry)); - - if (_inst.OE) PanicAlert("OE: subfcx"); - if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); - */ - } - - void subfx(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA, b = inst.RB, d = inst.RD; - gpr.Lock(a, b, d); - if (d != a && d != b) { - gpr.LoadToX64(d, false, true); - } else { - gpr.LoadToX64(d, true, true); - } - MOV(32, R(EAX), gpr.R(b)); - SUB(32, R(EAX), gpr.R(a)); - MOV(32, gpr.R(d), R(EAX)); - gpr.UnlockAll(); - if (inst.OE) PanicAlert("OE: subfx"); - if (inst.Rc) { - // result is already in eax - CALL((u8*)Asm::computeRc); - } - } - - void mulli(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA, d = inst.RD; - gpr.Lock(a, d); - gpr.LoadToX64(d, (d == a), true); - gpr.KillImmediate(a); - IMUL(32, gpr.RX(d), gpr.R(a), Imm32((u32)(s32)inst.SIMM_16)); - gpr.UnlockAll(); - } - - void mullwx(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA, b = inst.RB, d = inst.RD; - gpr.Lock(a, b, d); - gpr.LoadToX64(d, (d == a || d == b), true); - if (d == a) { - IMUL(32, gpr.RX(d), gpr.R(b)); - } else if (d == b) { - IMUL(32, gpr.RX(d), gpr.R(a)); - } else { - MOV(32, gpr.R(d), gpr.R(b)); - IMUL(32, gpr.RX(d), gpr.R(a)); - } - gpr.UnlockAll(); - if (inst.Rc) { - MOV(32, R(EAX), gpr.R(d)); - CALL((u8*)Asm::computeRc); - } - } - - void mulhwux(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA, b = inst.RB, d = inst.RD; - gpr.FlushLockX(EDX); - gpr.Lock(a, b, d); - if (d != a && d != b) { - gpr.LoadToX64(d, false, true); - } else { - gpr.LoadToX64(d, true, true); - } - if (gpr.RX(d) == EDX) - PanicAlert("mulhwux : WTF"); - MOV(32, R(EAX), gpr.R(a)); - gpr.KillImmediate(b); - MUL(32, gpr.R(b)); - gpr.UnlockAll(); - gpr.UnlockAllX(); - if (inst.Rc) { - MOV(32, R(EAX), R(EDX)); - MOV(32, gpr.R(d), R(EDX)); - // result is already in eax - CALL((u8*)Asm::computeRc); - } else { - MOV(32, gpr.R(d), R(EDX)); - } - } - - // skipped some of the special handling in here - if we get crashes, let the interpreter handle this op - void divwux(UGeckoInstruction inst) { - Default(inst); return; - - int a = inst.RA, b = inst.RB, d = inst.RD; - gpr.FlushLockX(EDX); - gpr.Lock(a, b, d); - if (d != a && d != b) { - gpr.LoadToX64(d, false, true); - } else { - gpr.LoadToX64(d, true, true); - } - MOV(32, R(EAX), gpr.R(a)); - XOR(32, R(EDX), R(EDX)); - gpr.KillImmediate(b); - DIV(32, gpr.R(b)); - MOV(32, gpr.R(d), R(EAX)); - gpr.UnlockAll(); - gpr.UnlockAllX(); - if (inst.Rc) { - CALL((u8*)Asm::computeRc); - } - } - - u32 Helper_Mask(u8 mb, u8 me) - { - return (((mb > me) ? - ~(((u32)-1 >> mb) ^ ((me >= 31) ? 0 : (u32) -1 >> (me + 1))) - : - (((u32)-1 >> mb) ^ ((me >= 31) ? 0 : (u32) -1 >> (me + 1)))) - ); - } - - void addx(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA, b = inst.RB, d = inst.RD; - _assert_msg_(DYNA_REC, !inst.OE, "Add - OE enabled :("); - - if (a != d && b != d && a != b) - { - gpr.Lock(a, b, d); - gpr.LoadToX64(d, false); - if (gpr.R(a).IsSimpleReg() && gpr.R(b).IsSimpleReg()) { - LEA(32, gpr.RX(d), MComplex(gpr.RX(a), gpr.RX(b), 1, 0)); - } else { - MOV(32, gpr.R(d), gpr.R(a)); - ADD(32, gpr.R(d), gpr.R(b)); - } - if (inst.Rc) - { - MOV(32, R(EAX), gpr.R(d)); - CALL((u8*)Asm::computeRc); - } - gpr.UnlockAll(); - } - else if (d == a && d != b) - { - gpr.Lock(b, d); - gpr.LoadToX64(d, true); - ADD(32, gpr.R(d), gpr.R(b)); - if (inst.Rc) - { - MOV(32, R(EAX), gpr.R(d)); - CALL((u8*)Asm::computeRc); - } - gpr.UnlockAll(); - } - else if (d == b && d != a) - { - gpr.Lock(a, d); - gpr.LoadToX64(d, true); - ADD(32, gpr.R(d), gpr.R(a)); - if (inst.Rc) - { - MOV(32, R(EAX), gpr.R(d)); - CALL((u8*)Asm::computeRc); - } - gpr.UnlockAll(); - } - else - { - Default(inst); return; - } - } - - // This can be optimized - void addex(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA, b = inst.RB, d = inst.RD; - gpr.FlushLockX(ECX); - gpr.Lock(a, b, d); - if (d != a && d != b) - gpr.LoadToX64(d, false); - else - gpr.LoadToX64(d, true); - MOV(32, R(EAX), M(&XER)); - SHR(32, R(EAX), Imm8(30)); // shift the carry flag out into the x86 carry flag - MOV(32, R(EAX), gpr.R(a)); - ADC(32, R(EAX), gpr.R(b)); - MOV(32, gpr.R(d), R(EAX)); - GenerateCarry(ECX); - gpr.UnlockAll(); - gpr.UnlockAllX(); - if (inst.Rc) - { - CALL((u8*)Asm::computeRc); - } - } - - void rlwinmx(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA; - int s = inst.RS; - if (gpr.R(a).IsImm() || gpr.R(s).IsImm()) - { - if (gpr.R(s).IsImm()) - { - if (gpr.R(s).offset == 0 && !inst.Rc) { - // This is pretty common for some reason - gpr.LoadToX64(a, false); - XOR(32, gpr.R(a), gpr.R(a)); - return; - } - // This might also be worth doing. - } - Default(inst); - return; - } - - if (a != s) - { - gpr.Lock(a, s); - gpr.LoadToX64(a, false); - MOV(32, gpr.R(a), gpr.R(s)); - } - - if (inst.MB == 0 && inst.ME==31-inst.SH) - { - SHL(32, gpr.R(a), Imm8(inst.SH)); - } - else if (inst.ME == 31 && inst.MB == 32 - inst.SH) - { - SHR(32, gpr.R(a), Imm8(inst.MB)); - } - else - { - bool written = false; - if (inst.SH != 0) - { - ROL(32, gpr.R(a), Imm8(inst.SH)); - written = true; - } - if (!(inst.MB==0 && inst.ME==31)) - { - written = true; - AND(32, gpr.R(a), Imm32(Helper_Mask(inst.MB, inst.ME))); - } - _assert_msg_(DYNA_REC, written, "W T F!!!"); - } - gpr.UnlockAll(); - - if (inst.Rc) - { - MOV(32, R(EAX), gpr.R(a)); - CALL((u8*)Asm::computeRc); - } - } - - - void rlwimix(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA; - int s = inst.RS; - if (gpr.R(a).IsImm() || gpr.R(s).IsImm()) - { - Default(inst); - return; - } - - if (a != s) - { - gpr.Lock(a, s); - gpr.LoadToX64(a, true); - } - - u32 mask = Helper_Mask(inst.MB, inst.ME); - MOV(32, R(EAX), gpr.R(s)); - AND(32, gpr.R(a), Imm32(~mask)); - if (inst.SH) - ROL(32, R(EAX), Imm8(inst.SH)); - AND(32, R(EAX), Imm32(mask)); - OR(32, gpr.R(a), R(EAX)); - gpr.UnlockAll(); - if (inst.Rc) - { - MOV(32, R(EAX), gpr.R(a)); - CALL((u8*)Asm::computeRc); - } - } - - void rlwnmx(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA, b = inst.RB, s = inst.RS; - if (gpr.R(a).IsImm()) - { - Default(inst); - return; - } - - u32 mask = Helper_Mask(inst.MB, inst.ME); - gpr.FlushLockX(ECX); - gpr.Lock(a, b, s); - MOV(32, R(EAX), gpr.R(s)); - MOV(32, R(ECX), gpr.R(b)); - AND(32, R(ECX), Imm32(0x1f)); - ROL(32, R(EAX), R(ECX)); - AND(32, R(EAX), Imm32(mask)); - MOV(32, gpr.R(a), R(EAX)); - gpr.UnlockAll(); - gpr.UnlockAllX(); - if (inst.Rc) - { - MOV(32, R(EAX), gpr.R(a)); - CALL((u8*)Asm::computeRc); - } - } - - void negx(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA; - int d = inst.RD; - gpr.Lock(a, d); - gpr.LoadToX64(d, a == d, true); - if (a != d) - MOV(32, gpr.R(d), gpr.R(a)); - NEG(32, gpr.R(d)); - gpr.UnlockAll(); - if (inst.Rc) - { - MOV(32, R(EAX), gpr.R(a)); - CALL((u8*)Asm::computeRc); - } - } - - void srwx(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA; - int b = inst.RB; - int s = inst.RS; - gpr.FlushLockX(ECX); - gpr.Lock(a, b, s); - gpr.LoadToX64(a, a == s || a == b || s == b, true); - MOV(32, R(ECX), gpr.R(b)); - XOR(32, R(EAX), R(EAX)); - TEST(32, R(ECX), Imm32(32)); - FixupBranch branch = J_CC(CC_NZ); - MOV(32, R(EAX), gpr.R(s)); - SHR(32, R(EAX), R(ECX)); - SetJumpTarget(branch); - MOV(32, gpr.R(a), R(EAX)); - gpr.UnlockAll(); - gpr.UnlockAllX(); - if (inst.Rc) - { - MOV(32, R(EAX), gpr.R(a)); - CALL((u8*)Asm::computeRc); - } - } - - void slwx(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA; - int b = inst.RB; - int s = inst.RS; - gpr.FlushLockX(ECX); - gpr.Lock(a, b, s); - gpr.LoadToX64(a, a == s || a == b || s == b, true); - MOV(32, R(ECX), gpr.R(b)); - XOR(32, R(EAX), R(EAX)); - TEST(32, R(ECX), Imm32(32)); - FixupBranch branch = J_CC(CC_NZ); - MOV(32, R(EAX), gpr.R(s)); - SHL(32, R(EAX), R(ECX)); - SetJumpTarget(branch); - MOV(32, gpr.R(a), R(EAX)); - gpr.UnlockAll(); - gpr.UnlockAllX(); - if (inst.Rc) - { - MOV(32, R(EAX), gpr.R(a)); - CALL((u8*)Asm::computeRc); - } - } - - void srawx(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA; - int b = inst.RB; - int s = inst.RS; - gpr.Lock(a, s); - gpr.FlushLockX(ECX); - gpr.LoadToX64(a, a == s || a == b, true); - MOV(32, R(ECX), gpr.R(b)); - TEST(32, R(ECX), Imm32(32)); - FixupBranch topBitSet = J_CC(CC_NZ); - if (a != s) - MOV(32, gpr.R(a), gpr.R(s)); - MOV(32, R(EAX), Imm32(1)); - SHL(32, R(EAX), R(ECX)); - ADD(32, R(EAX), Imm32(0x7FFFFFFF)); - AND(32, R(EAX), gpr.R(a)); - ADD(32, R(EAX), Imm32(-1)); - CMP(32, R(EAX), Imm32(-1)); - SETcc(CC_L, R(EAX)); - SAR(32, gpr.R(a), R(ECX)); - AND(32, M(&XER), Imm32(~(1 << 29))); - SHL(32, R(EAX), Imm8(29)); - OR(32, M(&XER), R(EAX)); - FixupBranch end = J(); - SetJumpTarget(topBitSet); - MOV(32, R(EAX), gpr.R(s)); - SAR(32, R(EAX), Imm8(31)); - MOV(32, gpr.R(a), R(EAX)); - AND(32, M(&XER), Imm32(~(1 << 29))); - AND(32, R(EAX), Imm32(1<<29)); - OR(32, M(&XER), R(EAX)); - SetJumpTarget(end); - gpr.UnlockAll(); - gpr.UnlockAllX(); - - if (inst.Rc) { - MOV(32, R(EAX), gpr.R(a)); - CALL((u8*)Asm::computeRc); - } - } - - void srawix(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA; - int s = inst.RS; - int amount = inst.SH; - if (amount != 0) - { - gpr.Lock(a, s); - gpr.LoadToX64(a, a == s, true); - MOV(32, R(EAX), gpr.R(s)); - MOV(32, gpr.R(a), R(EAX)); - SAR(32, gpr.R(a), Imm8(amount)); - CMP(32, R(EAX), Imm8(0)); - FixupBranch nocarry1 = J_CC(CC_GE); - TEST(32, R(EAX), Imm32((u32)0xFFFFFFFF >> (32 - amount))); // were any 1s shifted out? - FixupBranch nocarry2 = J_CC(CC_Z); - OR(32, M(&XER), Imm32(XER_CA_MASK)); //XER.CA = 1 - FixupBranch carry = J(false); - SetJumpTarget(nocarry1); - SetJumpTarget(nocarry2); - AND(32, M(&XER), Imm32(~XER_CA_MASK)); //XER.CA = 0 - SetJumpTarget(carry); - gpr.UnlockAll(); - } - else - { - Default(inst); return; - gpr.Lock(a, s); - AND(32, M(&XER), Imm32(~XER_CA_MASK)); //XER.CA = 0 - gpr.LoadToX64(a, a == s, true); - if (a != s) - MOV(32, gpr.R(a), gpr.R(s)); - gpr.UnlockAll(); - } - - if (inst.Rc) { - MOV(32, R(EAX), gpr.R(a)); - CALL((u8*)Asm::computeRc); - } - } - - // count leading zeroes - void cntlzwx(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int a = inst.RA; - int s = inst.RS; - if (gpr.R(a).IsImm() || gpr.R(s).IsImm() || s == a) - { - Default(inst); - return; - } - gpr.Lock(a,s); - gpr.LoadToX64(a,false); - BSR(32, gpr.R(a).GetSimpleReg(), gpr.R(s)); - FixupBranch gotone = J_CC(CC_NZ); - MOV(32, gpr.R(a), Imm32(63)); - SetJumpTarget(gotone); - XOR(32, gpr.R(a), Imm8(0x1f)); // flip order - gpr.UnlockAll(); - - if (inst.Rc) - { - MOV(32, R(EAX), gpr.R(a)); - CALL((u8*)Asm::computeRc); - // TODO: Check PPC manual too - } - } - -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "../../Core.h" // include "Common.h", "CoreParameter.h", SCoreStartupParameter +#include "../PowerPC.h" +#include "../PPCTables.h" +#include "x64Emitter.h" + +#include "Jit.h" +#include "JitCache.h" +#include "JitRegCache.h" +#include "JitAsm.h" + +// #define INSTRUCTION_START Default(inst); return; +#define INSTRUCTION_START + +namespace Jit64 +{ + // Assumes that the flags were just set through an addition. + void GenerateCarry(X64Reg temp_reg) { + SETcc(CC_C, R(temp_reg)); + AND(32, M(&XER), Imm32(~(1 << 29))); + SHL(32, R(temp_reg), Imm8(29)); + OR(32, M(&XER), R(temp_reg)); + } + + typedef u32 (*Operation)(u32 a, u32 b); + u32 Add(u32 a, u32 b) {return a + b;} + u32 Or (u32 a, u32 b) {return a | b;} + u32 And(u32 a, u32 b) {return a & b;} + u32 Xor(u32 a, u32 b) {return a ^ b;} + + void regimmop(int d, int a, bool binary, u32 value, Operation doop, void(*op)(int, const OpArg&, const OpArg&), bool Rc = false, bool carry = false) + { + gpr.Lock(d, a); + if (a || binary || carry) // yeh nasty special case addic + { + if (a == d) + { + if (gpr.R(d).IsImm() && !carry) + { + gpr.SetImmediate32(d, doop((u32)gpr.R(d).offset, value)); + } + else + { + if (gpr.R(d).IsImm()) + gpr.LoadToX64(d, false); + op(32, gpr.R(d), Imm32(value)); //m_GPR[d] = m_GPR[_inst.RA] + _inst.SIMM_16; + if (carry) + GenerateCarry(EAX); + } + } + else + { + gpr.LoadToX64(d, false); + MOV(32, gpr.R(d), gpr.R(a)); + op(32, gpr.R(d), Imm32(value)); //m_GPR[d] = m_GPR[_inst.RA] + _inst.SIMM_16; + if (carry) + GenerateCarry(EAX); + } + } + else if (doop == Add) + { + // a == 0, which for these instructions imply value = 0 + gpr.SetImmediate32(d, value); + } + else + { + _assert_msg_(DYNA_REC, 0, "WTF regimmop"); + } + if (Rc) + { + // Todo - special case immediates. + MOV(32, R(EAX), gpr.R(d)); + CALL((u8*)Asm::computeRc); + } + gpr.UnlockAll(); + } + + void reg_imm(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int d = inst.RD, a = inst.RA, s = inst.RS; + switch (inst.OPCD) + { + case 14: // addi + // occasionally used as MOV - emulate, with immediate propagation + if (gpr.R(a).IsImm() && d != a && a != 0) { + gpr.SetImmediate32(d, (u32)gpr.R(a).offset + (u32)(s32)(s16)inst.SIMM_16); + } else if (inst.SIMM_16 == 0 && d != a && a != 0) { + gpr.Lock(a, d); + gpr.LoadToX64(d, false, true); + MOV(32, gpr.R(d), gpr.R(a)); + gpr.UnlockAll(); + } else { + regimmop(d, a, false, (u32)(s32)inst.SIMM_16, Add, ADD); //addi + } + break; + case 15: regimmop(d, a, false, (u32)inst.SIMM_16 << 16, Add, ADD); break; //addis + case 24: + if (a == 0 && s == 0 && inst.UIMM == 0 && !inst.Rc) //check for nop + {NOP(); return;} //make the nop visible in the generated code. not much use but interesting if we see one. + regimmop(a, s, true, inst.UIMM, Or, OR); + break; //ori + case 25: regimmop(a, s, true, inst.UIMM << 16, Or, OR, false); break;//oris + case 28: regimmop(a, s, true, inst.UIMM, And, AND, true); break; + case 29: regimmop(a, s, true, inst.UIMM << 16, And, AND, true); break; + case 26: regimmop(a, s, true, inst.UIMM, Xor, XOR, false); break; //xori + case 27: regimmop(a, s, true, inst.UIMM << 16, Xor, XOR, false); break; //xoris + case 12: //regimmop(d, a, false, (u32)(s32)inst.SIMM_16, Add, ADD, false, true); //addic + case 13: //regimmop(d, a, true, (u32)(s32)inst.SIMM_16, Add, ADD, true, true); //addic_rc + default: + Default(inst); + break; + } + } + + // unsigned + void cmpli(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA; + u32 uimm = inst.UIMM; + int crf = inst.CRFD; + int shift = crf * 4; + gpr.KillImmediate(a); // todo, optimize instead, but unlikely to make a difference + AND(32, M(&CR), Imm32(~(0xF0000000 >> (crf*4)))); + CMP(32, gpr.R(a), Imm32(uimm)); + FixupBranch pLesser = J_CC(CC_B); + FixupBranch pGreater = J_CC(CC_A); + + MOV(32, R(EAX), Imm32(0x20000000 >> shift)); // _x86Reg == 0 + FixupBranch continue1 = J(); + + SetJumpTarget(pGreater); + MOV(32, R(EAX), Imm32(0x40000000 >> shift)); // _x86Reg > 0 + FixupBranch continue2 = J(); + + SetJumpTarget(pLesser); + MOV(32, R(EAX), Imm32(0x80000000 >> shift));// _x86Reg < 0 + SetJumpTarget(continue1); + SetJumpTarget(continue2); + OR(32, M(&CR), R(EAX)); + } + + // signed + void cmpi(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA; + s32 simm = (s32)(s16)inst.UIMM; + int crf = inst.CRFD; + int shift = crf * 4; + gpr.KillImmediate(a); // todo, optimize instead, but unlikely to make a difference + AND(32, M(&CR), Imm32(~(0xF0000000 >> (crf*4)))); + CMP(32, gpr.R(a), Imm32(simm)); + FixupBranch pLesser = J_CC(CC_L); + FixupBranch pGreater = J_CC(CC_G); + // _x86Reg == 0 + MOV(32, R(EAX), Imm32(0x20000000 >> shift)); + FixupBranch continue1 = J(); + // _x86Reg > 0 + SetJumpTarget(pGreater); + MOV(32, R(EAX), Imm32(0x40000000 >> shift)); + FixupBranch continue2 = J(); + // _x86Reg < 0 + SetJumpTarget(pLesser); + MOV(32, R(EAX), Imm32(0x80000000 >> shift)); + SetJumpTarget(continue1); + SetJumpTarget(continue2); + OR(32, M(&CR), R(EAX)); + } + + // signed + void cmp(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA; + int b = inst.RB; + int crf = inst.CRFD; + int shift = crf * 4; + gpr.Lock(a, b); + gpr.LoadToX64(a, true, false); + AND(32, M(&CR), Imm32(~(0xF0000000 >> (crf*4)))); + CMP(32, gpr.R(a), gpr.R(b)); + FixupBranch pLesser = J_CC(CC_L); + FixupBranch pGreater = J_CC(CC_G); + // _x86Reg == 0 + MOV(32, R(EAX), Imm32(0x20000000 >> shift)); + FixupBranch continue1 = J(); + // _x86Reg > 0 + SetJumpTarget(pGreater); + MOV(32, R(EAX), Imm32(0x40000000 >> shift)); + FixupBranch continue2 = J(); + // _x86Reg < 0 + SetJumpTarget(pLesser); + MOV(32, R(EAX), Imm32(0x80000000 >> shift)); + SetJumpTarget(continue1); + SetJumpTarget(continue2); + OR(32, M(&CR), R(EAX)); + gpr.UnlockAll(); + } + + // unsigned + void cmpl(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA; + int b = inst.RB; + int crf = inst.CRFD; + int shift = crf * 4; + gpr.Lock(a, b); + gpr.LoadToX64(a, true, false); + AND(32, M(&CR), Imm32(~(0xF0000000 >> (crf*4)))); + CMP(32, gpr.R(a), gpr.R(b)); + FixupBranch pLesser = J_CC(CC_B); + FixupBranch pGreater = J_CC(CC_A); + // _x86Reg == 0 + MOV(32, R(EAX), Imm32(0x20000000 >> shift)); + FixupBranch continue1 = J(); + // _x86Reg > 0 + SetJumpTarget(pGreater); + MOV(32, R(EAX), Imm32(0x40000000 >> shift)); + FixupBranch continue2 = J(); + // _x86Reg < 0 + SetJumpTarget(pLesser); + MOV(32, R(EAX), Imm32(0x80000000 >> shift)); + SetJumpTarget(continue1); + SetJumpTarget(continue2); + OR(32, M(&CR), R(EAX)); + gpr.UnlockAll(); + } + + void orx(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA; + int s = inst.RS; + int b = inst.RB; + + if (s == b && s != a) + { + gpr.Lock(a,s); + gpr.LoadToX64(a, false); + MOV(32, gpr.R(a), gpr.R(s)); + gpr.UnlockAll(); + } + else + { + gpr.Lock(a, s, b); + gpr.LoadToX64(a, (a == s || a == b), true); + if (a == s) + OR(32, gpr.R(a), gpr.R(b)); + else if (a == b) + OR(32, gpr.R(a), gpr.R(s)); + else { + MOV(32, gpr.R(a), gpr.R(b)); + OR(32, gpr.R(a), gpr.R(s)); + } + gpr.UnlockAll(); + } + + if (inst.Rc) + { + MOV(32, R(EAX), gpr.R(a)); + CALL((u8*)Asm::computeRc); + } + } + + + // m_GPR[_inst.RA] = m_GPR[_inst.RS] ^ m_GPR[_inst.RB]; + void xorx(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA; + int s = inst.RS; + int b = inst.RB; + + if (s == b) { + gpr.SetImmediate32(a, 0); + } + else + { + gpr.LoadToX64(a, a == s || a == b, true); + gpr.Lock(a, s, b); + MOV(32, R(EAX), gpr.R(s)); + XOR(32, R(EAX), gpr.R(b)); + MOV(32, gpr.R(a), R(EAX)); + gpr.UnlockAll(); + } + + if (inst.Rc) + { + MOV(32, R(EAX), gpr.R(a)); + CALL((u8*)Asm::computeRc); + } + } + + void andx(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA, s = inst.RS, b = inst.RB; + if (a != s && a != b) { + gpr.LoadToX64(a, false, true); + } else { + gpr.LoadToX64(a, true, true); + } + gpr.Lock(a, s, b); + MOV(32, R(EAX), gpr.R(s)); + AND(32, R(EAX), gpr.R(b)); + MOV(32, gpr.R(a), R(EAX)); + gpr.UnlockAll(); + + if (inst.Rc) { + // result is already in eax + CALL((u8*)Asm::computeRc); + } + } + + void extsbx(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA, + s = inst.RS; + gpr.LoadToX64(a, a == s, true); + // Always force moving to EAX because it isn't possible + // to refer to the lowest byte of some registers, at least in + // 32-bit mode. + MOV(32, R(EAX), gpr.R(s)); + MOVSX(32, 8, gpr.RX(a), R(AL)); // watch out for ah and friends + if (inst.Rc) { + MOV(32, R(EAX), gpr.R(a)); + CALL((u8*)Asm::computeRc); + } + } + + void extshx(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA, s = inst.RS; + gpr.KillImmediate(s); + gpr.LoadToX64(a, a == s, true); + // This looks a little dangerous, but it's safe because + // every 32-bit register has a 16-bit half at the same index + // as the 32-bit register. + MOVSX(32, 16, gpr.RX(a), gpr.R(s)); + if (inst.Rc) { + MOV(32, R(EAX), gpr.R(a)); + CALL((u8*)Asm::computeRc); + } + } + + void subfic(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA, d = inst.RD; + gpr.FlushLockX(ECX); + gpr.Lock(a, d); + gpr.LoadToX64(d, a == d, true); + int imm = inst.SIMM_16; + MOV(32, R(EAX), gpr.R(a)); + NOT(32, R(EAX)); + ADD(32, R(EAX), Imm32(imm + 1)); + MOV(32, gpr.R(d), R(EAX)); + GenerateCarry(ECX); + gpr.UnlockAll(); + gpr.UnlockAllX(); + // This instruction has no RC flag + } + + void subfcx(UGeckoInstruction inst) + { + INSTRUCTION_START; + Default(inst); + return; + /* + u32 a = m_GPR[_inst.RA]; + u32 b = m_GPR[_inst.RB]; + m_GPR[_inst.RD] = b - a; + SetCarry(a == 0 || Helper_Carry(b, 0-a)); + + if (_inst.OE) PanicAlert("OE: subfcx"); + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); + */ + } + + void subfex(UGeckoInstruction inst) + { + INSTRUCTION_START; + Default(inst); + return; + /* + u32 a = m_GPR[_inst.RA]; + u32 b = m_GPR[_inst.RB]; + int carry = GetCarry(); + m_GPR[_inst.RD] = (~a) + b + carry; + SetCarry(Helper_Carry(~a, b) || Helper_Carry((~a) + b, carry)); + + if (_inst.OE) PanicAlert("OE: subfcx"); + if (_inst.Rc) Helper_UpdateCR0(m_GPR[_inst.RD]); + */ + } + + void subfx(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA, b = inst.RB, d = inst.RD; + gpr.Lock(a, b, d); + if (d != a && d != b) { + gpr.LoadToX64(d, false, true); + } else { + gpr.LoadToX64(d, true, true); + } + MOV(32, R(EAX), gpr.R(b)); + SUB(32, R(EAX), gpr.R(a)); + MOV(32, gpr.R(d), R(EAX)); + gpr.UnlockAll(); + if (inst.OE) PanicAlert("OE: subfx"); + if (inst.Rc) { + // result is already in eax + CALL((u8*)Asm::computeRc); + } + } + + void mulli(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA, d = inst.RD; + gpr.Lock(a, d); + gpr.LoadToX64(d, (d == a), true); + gpr.KillImmediate(a); + IMUL(32, gpr.RX(d), gpr.R(a), Imm32((u32)(s32)inst.SIMM_16)); + gpr.UnlockAll(); + } + + void mullwx(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA, b = inst.RB, d = inst.RD; + gpr.Lock(a, b, d); + gpr.LoadToX64(d, (d == a || d == b), true); + if (d == a) { + IMUL(32, gpr.RX(d), gpr.R(b)); + } else if (d == b) { + IMUL(32, gpr.RX(d), gpr.R(a)); + } else { + MOV(32, gpr.R(d), gpr.R(b)); + IMUL(32, gpr.RX(d), gpr.R(a)); + } + gpr.UnlockAll(); + if (inst.Rc) { + MOV(32, R(EAX), gpr.R(d)); + CALL((u8*)Asm::computeRc); + } + } + + void mulhwux(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA, b = inst.RB, d = inst.RD; + gpr.FlushLockX(EDX); + gpr.Lock(a, b, d); + if (d != a && d != b) { + gpr.LoadToX64(d, false, true); + } else { + gpr.LoadToX64(d, true, true); + } + if (gpr.RX(d) == EDX) + PanicAlert("mulhwux : WTF"); + MOV(32, R(EAX), gpr.R(a)); + gpr.KillImmediate(b); + MUL(32, gpr.R(b)); + gpr.UnlockAll(); + gpr.UnlockAllX(); + if (inst.Rc) { + MOV(32, R(EAX), R(EDX)); + MOV(32, gpr.R(d), R(EDX)); + // result is already in eax + CALL((u8*)Asm::computeRc); + } else { + MOV(32, gpr.R(d), R(EDX)); + } + } + + // skipped some of the special handling in here - if we get crashes, let the interpreter handle this op + void divwux(UGeckoInstruction inst) { + Default(inst); return; + + int a = inst.RA, b = inst.RB, d = inst.RD; + gpr.FlushLockX(EDX); + gpr.Lock(a, b, d); + if (d != a && d != b) { + gpr.LoadToX64(d, false, true); + } else { + gpr.LoadToX64(d, true, true); + } + MOV(32, R(EAX), gpr.R(a)); + XOR(32, R(EDX), R(EDX)); + gpr.KillImmediate(b); + DIV(32, gpr.R(b)); + MOV(32, gpr.R(d), R(EAX)); + gpr.UnlockAll(); + gpr.UnlockAllX(); + if (inst.Rc) { + CALL((u8*)Asm::computeRc); + } + } + + u32 Helper_Mask(u8 mb, u8 me) + { + return (((mb > me) ? + ~(((u32)-1 >> mb) ^ ((me >= 31) ? 0 : (u32) -1 >> (me + 1))) + : + (((u32)-1 >> mb) ^ ((me >= 31) ? 0 : (u32) -1 >> (me + 1)))) + ); + } + + void addx(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA, b = inst.RB, d = inst.RD; + _assert_msg_(DYNA_REC, !inst.OE, "Add - OE enabled :("); + + if (a != d && b != d && a != b) + { + gpr.Lock(a, b, d); + gpr.LoadToX64(d, false); + if (gpr.R(a).IsSimpleReg() && gpr.R(b).IsSimpleReg()) { + LEA(32, gpr.RX(d), MComplex(gpr.RX(a), gpr.RX(b), 1, 0)); + } else { + MOV(32, gpr.R(d), gpr.R(a)); + ADD(32, gpr.R(d), gpr.R(b)); + } + if (inst.Rc) + { + MOV(32, R(EAX), gpr.R(d)); + CALL((u8*)Asm::computeRc); + } + gpr.UnlockAll(); + } + else if (d == a && d != b) + { + gpr.Lock(b, d); + gpr.LoadToX64(d, true); + ADD(32, gpr.R(d), gpr.R(b)); + if (inst.Rc) + { + MOV(32, R(EAX), gpr.R(d)); + CALL((u8*)Asm::computeRc); + } + gpr.UnlockAll(); + } + else if (d == b && d != a) + { + gpr.Lock(a, d); + gpr.LoadToX64(d, true); + ADD(32, gpr.R(d), gpr.R(a)); + if (inst.Rc) + { + MOV(32, R(EAX), gpr.R(d)); + CALL((u8*)Asm::computeRc); + } + gpr.UnlockAll(); + } + else + { + Default(inst); return; + } + } + + // This can be optimized + void addex(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA, b = inst.RB, d = inst.RD; + gpr.FlushLockX(ECX); + gpr.Lock(a, b, d); + if (d != a && d != b) + gpr.LoadToX64(d, false); + else + gpr.LoadToX64(d, true); + MOV(32, R(EAX), M(&XER)); + SHR(32, R(EAX), Imm8(30)); // shift the carry flag out into the x86 carry flag + MOV(32, R(EAX), gpr.R(a)); + ADC(32, R(EAX), gpr.R(b)); + MOV(32, gpr.R(d), R(EAX)); + GenerateCarry(ECX); + gpr.UnlockAll(); + gpr.UnlockAllX(); + if (inst.Rc) + { + CALL((u8*)Asm::computeRc); + } + } + + void rlwinmx(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA; + int s = inst.RS; + if (gpr.R(a).IsImm() || gpr.R(s).IsImm()) + { + if (gpr.R(s).IsImm()) + { + if (gpr.R(s).offset == 0 && !inst.Rc) { + // This is pretty common for some reason + gpr.LoadToX64(a, false); + XOR(32, gpr.R(a), gpr.R(a)); + return; + } + // This might also be worth doing. + } + Default(inst); + return; + } + + if (a != s) + { + gpr.Lock(a, s); + gpr.LoadToX64(a, false); + MOV(32, gpr.R(a), gpr.R(s)); + } + + if (inst.MB == 0 && inst.ME==31-inst.SH) + { + SHL(32, gpr.R(a), Imm8(inst.SH)); + } + else if (inst.ME == 31 && inst.MB == 32 - inst.SH) + { + SHR(32, gpr.R(a), Imm8(inst.MB)); + } + else + { + bool written = false; + if (inst.SH != 0) + { + ROL(32, gpr.R(a), Imm8(inst.SH)); + written = true; + } + if (!(inst.MB==0 && inst.ME==31)) + { + written = true; + AND(32, gpr.R(a), Imm32(Helper_Mask(inst.MB, inst.ME))); + } + _assert_msg_(DYNA_REC, written, "W T F!!!"); + } + gpr.UnlockAll(); + + if (inst.Rc) + { + MOV(32, R(EAX), gpr.R(a)); + CALL((u8*)Asm::computeRc); + } + } + + + void rlwimix(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA; + int s = inst.RS; + if (gpr.R(a).IsImm() || gpr.R(s).IsImm()) + { + Default(inst); + return; + } + + if (a != s) + { + gpr.Lock(a, s); + gpr.LoadToX64(a, true); + } + + u32 mask = Helper_Mask(inst.MB, inst.ME); + MOV(32, R(EAX), gpr.R(s)); + AND(32, gpr.R(a), Imm32(~mask)); + if (inst.SH) + ROL(32, R(EAX), Imm8(inst.SH)); + AND(32, R(EAX), Imm32(mask)); + OR(32, gpr.R(a), R(EAX)); + gpr.UnlockAll(); + if (inst.Rc) + { + MOV(32, R(EAX), gpr.R(a)); + CALL((u8*)Asm::computeRc); + } + } + + void rlwnmx(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA, b = inst.RB, s = inst.RS; + if (gpr.R(a).IsImm()) + { + Default(inst); + return; + } + + u32 mask = Helper_Mask(inst.MB, inst.ME); + gpr.FlushLockX(ECX); + gpr.Lock(a, b, s); + MOV(32, R(EAX), gpr.R(s)); + MOV(32, R(ECX), gpr.R(b)); + AND(32, R(ECX), Imm32(0x1f)); + ROL(32, R(EAX), R(ECX)); + AND(32, R(EAX), Imm32(mask)); + MOV(32, gpr.R(a), R(EAX)); + gpr.UnlockAll(); + gpr.UnlockAllX(); + if (inst.Rc) + { + MOV(32, R(EAX), gpr.R(a)); + CALL((u8*)Asm::computeRc); + } + } + + void negx(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA; + int d = inst.RD; + gpr.Lock(a, d); + gpr.LoadToX64(d, a == d, true); + if (a != d) + MOV(32, gpr.R(d), gpr.R(a)); + NEG(32, gpr.R(d)); + gpr.UnlockAll(); + if (inst.Rc) + { + MOV(32, R(EAX), gpr.R(a)); + CALL((u8*)Asm::computeRc); + } + } + + void srwx(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA; + int b = inst.RB; + int s = inst.RS; + gpr.FlushLockX(ECX); + gpr.Lock(a, b, s); + gpr.LoadToX64(a, a == s || a == b || s == b, true); + MOV(32, R(ECX), gpr.R(b)); + XOR(32, R(EAX), R(EAX)); + TEST(32, R(ECX), Imm32(32)); + FixupBranch branch = J_CC(CC_NZ); + MOV(32, R(EAX), gpr.R(s)); + SHR(32, R(EAX), R(ECX)); + SetJumpTarget(branch); + MOV(32, gpr.R(a), R(EAX)); + gpr.UnlockAll(); + gpr.UnlockAllX(); + if (inst.Rc) + { + MOV(32, R(EAX), gpr.R(a)); + CALL((u8*)Asm::computeRc); + } + } + + void slwx(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA; + int b = inst.RB; + int s = inst.RS; + gpr.FlushLockX(ECX); + gpr.Lock(a, b, s); + gpr.LoadToX64(a, a == s || a == b || s == b, true); + MOV(32, R(ECX), gpr.R(b)); + XOR(32, R(EAX), R(EAX)); + TEST(32, R(ECX), Imm32(32)); + FixupBranch branch = J_CC(CC_NZ); + MOV(32, R(EAX), gpr.R(s)); + SHL(32, R(EAX), R(ECX)); + SetJumpTarget(branch); + MOV(32, gpr.R(a), R(EAX)); + gpr.UnlockAll(); + gpr.UnlockAllX(); + if (inst.Rc) + { + MOV(32, R(EAX), gpr.R(a)); + CALL((u8*)Asm::computeRc); + } + } + + void srawx(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA; + int b = inst.RB; + int s = inst.RS; + gpr.Lock(a, s); + gpr.FlushLockX(ECX); + gpr.LoadToX64(a, a == s || a == b, true); + MOV(32, R(ECX), gpr.R(b)); + TEST(32, R(ECX), Imm32(32)); + FixupBranch topBitSet = J_CC(CC_NZ); + if (a != s) + MOV(32, gpr.R(a), gpr.R(s)); + MOV(32, R(EAX), Imm32(1)); + SHL(32, R(EAX), R(ECX)); + ADD(32, R(EAX), Imm32(0x7FFFFFFF)); + AND(32, R(EAX), gpr.R(a)); + ADD(32, R(EAX), Imm32(-1)); + CMP(32, R(EAX), Imm32(-1)); + SETcc(CC_L, R(EAX)); + SAR(32, gpr.R(a), R(ECX)); + AND(32, M(&XER), Imm32(~(1 << 29))); + SHL(32, R(EAX), Imm8(29)); + OR(32, M(&XER), R(EAX)); + FixupBranch end = J(); + SetJumpTarget(topBitSet); + MOV(32, R(EAX), gpr.R(s)); + SAR(32, R(EAX), Imm8(31)); + MOV(32, gpr.R(a), R(EAX)); + AND(32, M(&XER), Imm32(~(1 << 29))); + AND(32, R(EAX), Imm32(1<<29)); + OR(32, M(&XER), R(EAX)); + SetJumpTarget(end); + gpr.UnlockAll(); + gpr.UnlockAllX(); + + if (inst.Rc) { + MOV(32, R(EAX), gpr.R(a)); + CALL((u8*)Asm::computeRc); + } + } + + void srawix(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA; + int s = inst.RS; + int amount = inst.SH; + if (amount != 0) + { + gpr.Lock(a, s); + gpr.LoadToX64(a, a == s, true); + MOV(32, R(EAX), gpr.R(s)); + MOV(32, gpr.R(a), R(EAX)); + SAR(32, gpr.R(a), Imm8(amount)); + CMP(32, R(EAX), Imm8(0)); + FixupBranch nocarry1 = J_CC(CC_GE); + TEST(32, R(EAX), Imm32((u32)0xFFFFFFFF >> (32 - amount))); // were any 1s shifted out? + FixupBranch nocarry2 = J_CC(CC_Z); + OR(32, M(&XER), Imm32(XER_CA_MASK)); //XER.CA = 1 + FixupBranch carry = J(false); + SetJumpTarget(nocarry1); + SetJumpTarget(nocarry2); + AND(32, M(&XER), Imm32(~XER_CA_MASK)); //XER.CA = 0 + SetJumpTarget(carry); + gpr.UnlockAll(); + } + else + { + Default(inst); return; + gpr.Lock(a, s); + AND(32, M(&XER), Imm32(~XER_CA_MASK)); //XER.CA = 0 + gpr.LoadToX64(a, a == s, true); + if (a != s) + MOV(32, gpr.R(a), gpr.R(s)); + gpr.UnlockAll(); + } + + if (inst.Rc) { + MOV(32, R(EAX), gpr.R(a)); + CALL((u8*)Asm::computeRc); + } + } + + // count leading zeroes + void cntlzwx(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITIntegerOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int a = inst.RA; + int s = inst.RS; + if (gpr.R(a).IsImm() || gpr.R(s).IsImm() || s == a) + { + Default(inst); + return; + } + gpr.Lock(a,s); + gpr.LoadToX64(a,false); + BSR(32, gpr.R(a).GetSimpleReg(), gpr.R(s)); + FixupBranch gotone = J_CC(CC_NZ); + MOV(32, gpr.R(a), Imm32(63)); + SetJumpTarget(gotone); + XOR(32, gpr.R(a), Imm8(0x1f)); // flip order + gpr.UnlockAll(); + + if (inst.Rc) + { + MOV(32, R(EAX), gpr.R(a)); + CALL((u8*)Asm::computeRc); + // TODO: Check PPC manual too + } + } + +} diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStore.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStore.cpp index d27096e775..ee30875361 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStore.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStore.cpp @@ -1,465 +1,465 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -// TODO(ector): Tons of pshufb optimization of the loads/stores, for SSSE3+, possibly SSE4, only. -// Should give a very noticable speed boost to paired single heavy code. - -#include "Common.h" -#include "Thunk.h" - -#include "../PowerPC.h" -#include "../../Core.h" -#include "../../HW/GPFifo.h" -#include "../../HW/CommandProcessor.h" -#include "../../HW/PixelEngine.h" -#include "../../HW/Memmap.h" -#include "../PPCTables.h" -#include "x64Emitter.h" -#include "ABI.h" - -#include "Jit.h" -#include "JitCache.h" -#include "JitAsm.h" -#include "JitRegCache.h" -#include "Jit_Util.h" - -// #define INSTRUCTION_START Default(inst); return; -#define INSTRUCTION_START - -#ifdef _M_IX86 -#define DISABLE_32BIT Default(inst); return; -#else -#define DISABLE_32BIT ; -#endif - -namespace Jit64 -{ - namespace { - // u64 GC_ALIGNED16(temp64); - // u32 GC_ALIGNED16(temp32); - } - void lbzx(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreOff - || Core::g_CoreStartupParameter.bJITLoadStorelbzxOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - - int a = inst.RA, b = inst.RB, d = inst.RD; - gpr.Lock(a, b, d); - gpr.FlushLockX(ABI_PARAM1); - if (b == d || a == d) - gpr.LoadToX64(d, true, true); - else - gpr.LoadToX64(d, false, true); - MOV(32, R(ABI_PARAM1), gpr.R(b)); - if (a) - ADD(32, R(ABI_PARAM1), gpr.R(a)); -#if 0 - SafeLoadRegToEAX(ABI_PARAM1, 8, 0); - MOV(32, gpr.R(d), R(EAX)); -#else - UnsafeLoadRegToReg(ABI_PARAM1, gpr.RX(d), 8, 0, false); -#endif - gpr.UnlockAll(); - gpr.UnlockAllX(); - } - - void lwzx(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - - int a = inst.RA, b = inst.RB, d = inst.RD; - gpr.Lock(a, b, d); - gpr.FlushLockX(ABI_PARAM1); - if (b == d || a == d) - gpr.LoadToX64(d, true, true); - else - gpr.LoadToX64(d, false, true); - MOV(32, R(ABI_PARAM1), gpr.R(b)); - if (a) - ADD(32, R(ABI_PARAM1), gpr.R(a)); -#if 1 - SafeLoadRegToEAX(ABI_PARAM1, 32, 0); - MOV(32, gpr.R(d), R(EAX)); -#else - UnsafeLoadRegToReg(ABI_PARAM1, gpr.RX(d), 32, 0, false); -#endif - gpr.UnlockAll(); - gpr.UnlockAllX(); - } - - void lhax(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - - int a = inst.RA, b = inst.RB, d = inst.RD; - gpr.Lock(a, b, d); - gpr.FlushLockX(ABI_PARAM1); - if (b == d || a == d) - gpr.LoadToX64(d, true, true); - else - gpr.LoadToX64(d, false, true); - MOV(32, R(ABI_PARAM1), gpr.R(b)); - if (a) - ADD(32, R(ABI_PARAM1), gpr.R(a)); -#if 1 - SafeLoadRegToEAX(ABI_PARAM1, 16, 0, true); - MOV(32, gpr.R(d), R(EAX)); -#else - UnsafeLoadRegToReg(ABI_PARAM1, gpr.RX(d), 16, 0, true); -#endif - gpr.UnlockAll(); - gpr.UnlockAllX(); - } - - void lXz(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreOff - || Core::g_CoreStartupParameter.bJITLoadStorelXzOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - - int d = inst.RD; - int a = inst.RA; - - // TODO(ector): Make it dynamically enable/disable idle skipping where appropriate - // Will give nice boost to dual core mode - // (mb2): I agree, - // IMHO those Idles should always be skipped and replaced by a more controllable "native" Idle methode - // ... maybe the throttle one already do that :p - // if (CommandProcessor::AllowIdleSkipping() && PixelEngine::AllowIdleSkipping()) - if (Core::GetStartupParameter().bSkipIdle && - inst.OPCD == 32 && - (inst.hex & 0xFFFF0000) == 0x800D0000 && - (Memory::ReadUnchecked_U32(js.compilerPC + 4) == 0x28000000 || - (Core::GetStartupParameter().bWii && Memory::ReadUnchecked_U32(js.compilerPC + 4) == 0x2C000000)) && - Memory::ReadUnchecked_U32(js.compilerPC + 8) == 0x4182fff8) - { - gpr.Flush(FLUSH_ALL); - fpr.Flush(FLUSH_ALL); - ABI_CallFunctionC((void *)&PowerPC::OnIdle, PowerPC::ppcState.gpr[a] + (s32)(s16)inst.SIMM_16); - MOV(32, M(&PowerPC::ppcState.pc), Imm32(js.compilerPC + 12)); - JMP(Asm::testExceptions, true); - js.compilerPC += 8; - return; - } - - // R2 always points to the small read-only data area. We could bake R2-relative loads into immediates. - // R13 always points to the small read/write data area. Not so exciting but at least could drop checks in 32-bit safe mode. - - s32 offset = (s32)(s16)inst.SIMM_16; - if (!a) - { - Default(inst); - return; - } - int accessSize; - switch (inst.OPCD) - { - case 32: - accessSize = 32; - if(Core::g_CoreStartupParameter.bJITLoadStorelwzOff) {Default(inst); return;} - break; //lwz - case 40: accessSize = 16; break; //lhz - case 34: accessSize = 8; break; //lbz - default: - //_assert_msg_(DYNA_REC, 0, "lXz: invalid access size"); - PanicAlert("lXz: invalid access size"); - return; - } - - //Still here? Do regular path. -#if defined(_M_X64) - if (accessSize == 8 || accessSize == 16 || !jo.enableFastMem) { -#else - if (true) { -#endif - // Safe and boring - gpr.FlushLockX(ABI_PARAM1); - gpr.Lock(d, a); - MOV(32, R(ABI_PARAM1), gpr.R(a)); - SafeLoadRegToEAX(ABI_PARAM1, accessSize, offset); - gpr.LoadToX64(d, false, true); - MOV(32, gpr.R(d), R(EAX)); - gpr.UnlockAll(); - gpr.UnlockAllX(); - return; - } - - // Fast and daring - gpr.Lock(a, d); - gpr.LoadToX64(a, true, false); - gpr.LoadToX64(d, a == d, true); - MOV(accessSize, gpr.R(d), MComplex(RBX, gpr.R(a).GetSimpleReg(), SCALE_1, offset)); - switch (accessSize) { - case 32: - BSWAP(32, gpr.R(d).GetSimpleReg()); - break; -// Careful in the backpatch - need to properly nop over first -// case 16: -// BSWAP(32, gpr.R(d).GetSimpleReg()); -// SHR(32, gpr.R(d), Imm8(16)); -// break; - } - gpr.UnlockAll(); - } - - void lha(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - - int d = inst.RD; - int a = inst.RA; - s32 offset = (s32)(s16)inst.SIMM_16; - // Safe and boring - gpr.FlushLockX(ABI_PARAM1); - gpr.Lock(d, a); - MOV(32, R(ABI_PARAM1), gpr.R(a)); - SafeLoadRegToEAX(ABI_PARAM1, 16, offset, true); - gpr.LoadToX64(d, d == a, true); - MOV(32, gpr.R(d), R(EAX)); - gpr.UnlockAll(); - gpr.UnlockAllX(); - return; - } - - // Zero cache line. - void dcbz(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - - MOV(32, R(EAX), gpr.R(inst.RB)); - if (inst.RA) - ADD(32, R(EAX), gpr.R(inst.RA)); - AND(32, R(EAX), Imm32(~31)); - XORPD(XMM0, R(XMM0)); -#ifdef _M_X64 - MOVAPS(MComplex(EBX, EAX, SCALE_1, 0), XMM0); - MOVAPS(MComplex(EBX, EAX, SCALE_1, 16), XMM0); -#else - AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); - MOVAPS(MDisp(EAX, (u32)Memory::base), XMM0); - MOVAPS(MDisp(EAX, (u32)Memory::base + 16), XMM0); -#endif - } - - void stX(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - - int s = inst.RS; - int a = inst.RA; - - bool update = inst.OPCD & 1; - - s32 offset = (s32)(s16)inst.SIMM_16; - if (a || update) - { - int accessSize; - switch (inst.OPCD & ~1) - { - case 36: accessSize = 32; break; //stw - case 44: accessSize = 16; break; //sth - case 38: accessSize = 8; break; //stb - default: _assert_msg_(DYNA_REC, 0, "AWETKLJASDLKF"); return; - } - - if (gpr.R(a).IsImm()) - { - // If we already know the address through constant folding, we can do some - // fun tricks... - u32 addr = (u32)gpr.R(a).offset; - addr += offset; - if ((addr & 0xFFFFF000) == 0xCC008000 && jo.optimizeGatherPipe) - { - if (offset && update) - gpr.SetImmediate32(a, addr); - gpr.FlushLockX(ABI_PARAM1); - MOV(32, R(ABI_PARAM1), gpr.R(s)); - // INT3(); - switch (accessSize) - { - // No need to protect these, they don't touch any state - // question - should we inline them instead? Pro: Lose a CALL Con: Code bloat - case 8: CALL((void *)Asm::fifoDirectWrite8); break; - case 16: CALL((void *)Asm::fifoDirectWrite16); break; - case 32: CALL((void *)Asm::fifoDirectWrite32); break; - } - js.fifoBytesThisBlock += accessSize >> 3; - gpr.UnlockAllX(); - return; - } - else if (Memory::IsRAMAddress(addr) && accessSize == 32) - { - if (offset && update) - gpr.SetImmediate32(a, addr); - MOV(accessSize, R(EAX), gpr.R(s)); - BSWAP(accessSize, EAX); - WriteToConstRamAddress(accessSize, R(EAX), addr); - return; - } - // Other IO not worth the trouble. - } - - // Optimized stack access? - if (accessSize == 32 && !gpr.R(a).IsImm() && a == 1 && js.st.isFirstBlockOfFunction && jo.optimizeStack) - { - gpr.FlushLockX(ABI_PARAM1); - MOV(32, R(ABI_PARAM1), gpr.R(a)); - MOV(32, R(EAX), gpr.R(s)); - BSWAP(32, EAX); -#ifdef _M_X64 - MOV(accessSize, MComplex(RBX, ABI_PARAM1, SCALE_1, (u32)offset), R(EAX)); -#elif _M_IX86 - AND(32, R(ABI_PARAM1), Imm32(Memory::MEMVIEW32_MASK)); - MOV(accessSize, MDisp(ABI_PARAM1, (u32)Memory::base + (u32)offset), R(EAX)); -#endif - if (update) - ADD(32, gpr.R(a), Imm32(offset)); - gpr.UnlockAllX(); - return; - } - - /* // TODO - figure out why Beyond Good and Evil hates this -#ifdef _M_X64 - if (accessSize == 32 && !update && jo.enableFastMem) - { - // Fast and daring - requires 64-bit - MOV(32, R(EAX), gpr.R(s)); - gpr.LoadToX64(a, true, false); - BSWAP(32, EAX); - MOV(accessSize, MComplex(RBX, gpr.RX(a), SCALE_1, (u32)offset), R(EAX)); - return; - } -#endif*/ - - //Still here? Do regular path. - - // NOTE: stb and stbu are broken under 64bit Linux - #ifndef _WIN32 - #ifdef _M_X64 - Default(inst); - return; - #endif - #endif - gpr.Lock(s, a); - gpr.FlushLockX(ABI_PARAM1, ABI_PARAM2); - MOV(32, R(ABI_PARAM2), gpr.R(a)); - MOV(32, R(ABI_PARAM1), gpr.R(s)); - if (offset) - ADD(32, R(ABI_PARAM2), Imm32((u32)offset)); - if (update && offset) - { - gpr.LoadToX64(a, true, true); - MOV(32, gpr.R(a), R(ABI_PARAM2)); - } - TEST(32, R(ABI_PARAM2), Imm32(0x0C000000)); - FixupBranch unsafe_addr = J_CC(CC_NZ); - BSWAP(accessSize, ABI_PARAM1); -#ifdef _M_X64 - MOV(accessSize, MComplex(RBX, ABI_PARAM2, SCALE_1, 0), R(ABI_PARAM1)); -#else - AND(32, R(ABI_PARAM2), Imm32(Memory::MEMVIEW32_MASK)); - MOV(accessSize, MDisp(ABI_PARAM2, (u32)Memory::base), R(ABI_PARAM1)); -#endif - FixupBranch skip_call = J(); - SetJumpTarget(unsafe_addr); - switch (accessSize) - { - case 32: ABI_CallFunctionRR(ProtectFunction((void *)&Memory::Write_U32, 2), ABI_PARAM1, ABI_PARAM2); break; - case 16: ABI_CallFunctionRR(ProtectFunction((void *)&Memory::Write_U16, 2), ABI_PARAM1, ABI_PARAM2); break; - case 8: ABI_CallFunctionRR(ProtectFunction((void *)&Memory::Write_U8, 2), ABI_PARAM1, ABI_PARAM2); break; - } - SetJumpTarget(skip_call); - gpr.UnlockAll(); - gpr.UnlockAllX(); - } - else - { - Default(inst); - } - } - - // A few games use these heavily in video codecs. - void lmw(UGeckoInstruction inst) - { - INSTRUCTION_START; - Default(inst); - return; - - /* - /// BUGGY - //return _inst.RA ? (m_GPR[_inst.RA] + _inst.SIMM_16) : _inst.SIMM_16; - gpr.FlushLockX(ECX, EDX); - gpr.FlushLockX(ESI); - //INT3(); - MOV(32, R(EAX), Imm32((u32)(s32)inst.SIMM_16)); - if (inst.RA) - ADD(32, R(EAX), gpr.R(inst.RA)); - MOV(32, R(ECX), Imm32(inst.RD)); - MOV(32, R(ESI), Imm32(static_cast((u64)&PowerPC::ppcState.gpr[0]))); - const u8 *loopPtr = GetCodePtr(); - MOV(32, R(EDX), MComplex(RBX, EAX, SCALE_1, 0)); - MOV(32, MComplex(ESI, ECX, SCALE_4, 0), R(EDX)); - ADD(32, R(EAX), Imm8(4)); - ADD(32, R(ESI), Imm8(4)); - ADD(32, R(ECX), Imm8(1)); - CMP(32, R(ECX), Imm8(32)); - gpr.UnlockAllX();*/ - } - - void stmw(UGeckoInstruction inst) - { - INSTRUCTION_START; - Default(inst); - return; - /* - u32 uAddress = Helper_Get_EA(_inst); - for (int iReg = _inst.RS; iReg <= 31; iReg++, uAddress+=4) - { - Memory::Write_U32(m_GPR[iReg], uAddress); - if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) - return; - }*/ - } -} - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// TODO(ector): Tons of pshufb optimization of the loads/stores, for SSSE3+, possibly SSE4, only. +// Should give a very noticable speed boost to paired single heavy code. + +#include "Common.h" +#include "Thunk.h" + +#include "../PowerPC.h" +#include "../../Core.h" +#include "../../HW/GPFifo.h" +#include "../../HW/CommandProcessor.h" +#include "../../HW/PixelEngine.h" +#include "../../HW/Memmap.h" +#include "../PPCTables.h" +#include "x64Emitter.h" +#include "ABI.h" + +#include "Jit.h" +#include "JitCache.h" +#include "JitAsm.h" +#include "JitRegCache.h" +#include "Jit_Util.h" + +// #define INSTRUCTION_START Default(inst); return; +#define INSTRUCTION_START + +#ifdef _M_IX86 +#define DISABLE_32BIT Default(inst); return; +#else +#define DISABLE_32BIT ; +#endif + +namespace Jit64 +{ + namespace { + // u64 GC_ALIGNED16(temp64); + // u32 GC_ALIGNED16(temp32); + } + void lbzx(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreOff + || Core::g_CoreStartupParameter.bJITLoadStorelbzxOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + + int a = inst.RA, b = inst.RB, d = inst.RD; + gpr.Lock(a, b, d); + gpr.FlushLockX(ABI_PARAM1); + if (b == d || a == d) + gpr.LoadToX64(d, true, true); + else + gpr.LoadToX64(d, false, true); + MOV(32, R(ABI_PARAM1), gpr.R(b)); + if (a) + ADD(32, R(ABI_PARAM1), gpr.R(a)); +#if 0 + SafeLoadRegToEAX(ABI_PARAM1, 8, 0); + MOV(32, gpr.R(d), R(EAX)); +#else + UnsafeLoadRegToReg(ABI_PARAM1, gpr.RX(d), 8, 0, false); +#endif + gpr.UnlockAll(); + gpr.UnlockAllX(); + } + + void lwzx(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + + int a = inst.RA, b = inst.RB, d = inst.RD; + gpr.Lock(a, b, d); + gpr.FlushLockX(ABI_PARAM1); + if (b == d || a == d) + gpr.LoadToX64(d, true, true); + else + gpr.LoadToX64(d, false, true); + MOV(32, R(ABI_PARAM1), gpr.R(b)); + if (a) + ADD(32, R(ABI_PARAM1), gpr.R(a)); +#if 1 + SafeLoadRegToEAX(ABI_PARAM1, 32, 0); + MOV(32, gpr.R(d), R(EAX)); +#else + UnsafeLoadRegToReg(ABI_PARAM1, gpr.RX(d), 32, 0, false); +#endif + gpr.UnlockAll(); + gpr.UnlockAllX(); + } + + void lhax(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + + int a = inst.RA, b = inst.RB, d = inst.RD; + gpr.Lock(a, b, d); + gpr.FlushLockX(ABI_PARAM1); + if (b == d || a == d) + gpr.LoadToX64(d, true, true); + else + gpr.LoadToX64(d, false, true); + MOV(32, R(ABI_PARAM1), gpr.R(b)); + if (a) + ADD(32, R(ABI_PARAM1), gpr.R(a)); +#if 1 + SafeLoadRegToEAX(ABI_PARAM1, 16, 0, true); + MOV(32, gpr.R(d), R(EAX)); +#else + UnsafeLoadRegToReg(ABI_PARAM1, gpr.RX(d), 16, 0, true); +#endif + gpr.UnlockAll(); + gpr.UnlockAllX(); + } + + void lXz(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreOff + || Core::g_CoreStartupParameter.bJITLoadStorelXzOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + + int d = inst.RD; + int a = inst.RA; + + // TODO(ector): Make it dynamically enable/disable idle skipping where appropriate + // Will give nice boost to dual core mode + // (mb2): I agree, + // IMHO those Idles should always be skipped and replaced by a more controllable "native" Idle methode + // ... maybe the throttle one already do that :p + // if (CommandProcessor::AllowIdleSkipping() && PixelEngine::AllowIdleSkipping()) + if (Core::GetStartupParameter().bSkipIdle && + inst.OPCD == 32 && + (inst.hex & 0xFFFF0000) == 0x800D0000 && + (Memory::ReadUnchecked_U32(js.compilerPC + 4) == 0x28000000 || + (Core::GetStartupParameter().bWii && Memory::ReadUnchecked_U32(js.compilerPC + 4) == 0x2C000000)) && + Memory::ReadUnchecked_U32(js.compilerPC + 8) == 0x4182fff8) + { + gpr.Flush(FLUSH_ALL); + fpr.Flush(FLUSH_ALL); + ABI_CallFunctionC((void *)&PowerPC::OnIdle, PowerPC::ppcState.gpr[a] + (s32)(s16)inst.SIMM_16); + MOV(32, M(&PowerPC::ppcState.pc), Imm32(js.compilerPC + 12)); + JMP(Asm::testExceptions, true); + js.compilerPC += 8; + return; + } + + // R2 always points to the small read-only data area. We could bake R2-relative loads into immediates. + // R13 always points to the small read/write data area. Not so exciting but at least could drop checks in 32-bit safe mode. + + s32 offset = (s32)(s16)inst.SIMM_16; + if (!a) + { + Default(inst); + return; + } + int accessSize; + switch (inst.OPCD) + { + case 32: + accessSize = 32; + if(Core::g_CoreStartupParameter.bJITLoadStorelwzOff) {Default(inst); return;} + break; //lwz + case 40: accessSize = 16; break; //lhz + case 34: accessSize = 8; break; //lbz + default: + //_assert_msg_(DYNA_REC, 0, "lXz: invalid access size"); + PanicAlert("lXz: invalid access size"); + return; + } + + //Still here? Do regular path. +#if defined(_M_X64) + if (accessSize == 8 || accessSize == 16 || !jo.enableFastMem) { +#else + if (true) { +#endif + // Safe and boring + gpr.FlushLockX(ABI_PARAM1); + gpr.Lock(d, a); + MOV(32, R(ABI_PARAM1), gpr.R(a)); + SafeLoadRegToEAX(ABI_PARAM1, accessSize, offset); + gpr.LoadToX64(d, false, true); + MOV(32, gpr.R(d), R(EAX)); + gpr.UnlockAll(); + gpr.UnlockAllX(); + return; + } + + // Fast and daring + gpr.Lock(a, d); + gpr.LoadToX64(a, true, false); + gpr.LoadToX64(d, a == d, true); + MOV(accessSize, gpr.R(d), MComplex(RBX, gpr.R(a).GetSimpleReg(), SCALE_1, offset)); + switch (accessSize) { + case 32: + BSWAP(32, gpr.R(d).GetSimpleReg()); + break; +// Careful in the backpatch - need to properly nop over first +// case 16: +// BSWAP(32, gpr.R(d).GetSimpleReg()); +// SHR(32, gpr.R(d), Imm8(16)); +// break; + } + gpr.UnlockAll(); + } + + void lha(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + + int d = inst.RD; + int a = inst.RA; + s32 offset = (s32)(s16)inst.SIMM_16; + // Safe and boring + gpr.FlushLockX(ABI_PARAM1); + gpr.Lock(d, a); + MOV(32, R(ABI_PARAM1), gpr.R(a)); + SafeLoadRegToEAX(ABI_PARAM1, 16, offset, true); + gpr.LoadToX64(d, d == a, true); + MOV(32, gpr.R(d), R(EAX)); + gpr.UnlockAll(); + gpr.UnlockAllX(); + return; + } + + // Zero cache line. + void dcbz(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + + MOV(32, R(EAX), gpr.R(inst.RB)); + if (inst.RA) + ADD(32, R(EAX), gpr.R(inst.RA)); + AND(32, R(EAX), Imm32(~31)); + XORPD(XMM0, R(XMM0)); +#ifdef _M_X64 + MOVAPS(MComplex(EBX, EAX, SCALE_1, 0), XMM0); + MOVAPS(MComplex(EBX, EAX, SCALE_1, 16), XMM0); +#else + AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); + MOVAPS(MDisp(EAX, (u32)Memory::base), XMM0); + MOVAPS(MDisp(EAX, (u32)Memory::base + 16), XMM0); +#endif + } + + void stX(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + + int s = inst.RS; + int a = inst.RA; + + bool update = inst.OPCD & 1; + + s32 offset = (s32)(s16)inst.SIMM_16; + if (a || update) + { + int accessSize; + switch (inst.OPCD & ~1) + { + case 36: accessSize = 32; break; //stw + case 44: accessSize = 16; break; //sth + case 38: accessSize = 8; break; //stb + default: _assert_msg_(DYNA_REC, 0, "AWETKLJASDLKF"); return; + } + + if (gpr.R(a).IsImm()) + { + // If we already know the address through constant folding, we can do some + // fun tricks... + u32 addr = (u32)gpr.R(a).offset; + addr += offset; + if ((addr & 0xFFFFF000) == 0xCC008000 && jo.optimizeGatherPipe) + { + if (offset && update) + gpr.SetImmediate32(a, addr); + gpr.FlushLockX(ABI_PARAM1); + MOV(32, R(ABI_PARAM1), gpr.R(s)); + // INT3(); + switch (accessSize) + { + // No need to protect these, they don't touch any state + // question - should we inline them instead? Pro: Lose a CALL Con: Code bloat + case 8: CALL((void *)Asm::fifoDirectWrite8); break; + case 16: CALL((void *)Asm::fifoDirectWrite16); break; + case 32: CALL((void *)Asm::fifoDirectWrite32); break; + } + js.fifoBytesThisBlock += accessSize >> 3; + gpr.UnlockAllX(); + return; + } + else if (Memory::IsRAMAddress(addr) && accessSize == 32) + { + if (offset && update) + gpr.SetImmediate32(a, addr); + MOV(accessSize, R(EAX), gpr.R(s)); + BSWAP(accessSize, EAX); + WriteToConstRamAddress(accessSize, R(EAX), addr); + return; + } + // Other IO not worth the trouble. + } + + // Optimized stack access? + if (accessSize == 32 && !gpr.R(a).IsImm() && a == 1 && js.st.isFirstBlockOfFunction && jo.optimizeStack) + { + gpr.FlushLockX(ABI_PARAM1); + MOV(32, R(ABI_PARAM1), gpr.R(a)); + MOV(32, R(EAX), gpr.R(s)); + BSWAP(32, EAX); +#ifdef _M_X64 + MOV(accessSize, MComplex(RBX, ABI_PARAM1, SCALE_1, (u32)offset), R(EAX)); +#elif _M_IX86 + AND(32, R(ABI_PARAM1), Imm32(Memory::MEMVIEW32_MASK)); + MOV(accessSize, MDisp(ABI_PARAM1, (u32)Memory::base + (u32)offset), R(EAX)); +#endif + if (update) + ADD(32, gpr.R(a), Imm32(offset)); + gpr.UnlockAllX(); + return; + } + + /* // TODO - figure out why Beyond Good and Evil hates this +#ifdef _M_X64 + if (accessSize == 32 && !update && jo.enableFastMem) + { + // Fast and daring - requires 64-bit + MOV(32, R(EAX), gpr.R(s)); + gpr.LoadToX64(a, true, false); + BSWAP(32, EAX); + MOV(accessSize, MComplex(RBX, gpr.RX(a), SCALE_1, (u32)offset), R(EAX)); + return; + } +#endif*/ + + //Still here? Do regular path. + + // NOTE: stb and stbu are broken under 64bit Linux + #ifndef _WIN32 + #ifdef _M_X64 + Default(inst); + return; + #endif + #endif + gpr.Lock(s, a); + gpr.FlushLockX(ABI_PARAM1, ABI_PARAM2); + MOV(32, R(ABI_PARAM2), gpr.R(a)); + MOV(32, R(ABI_PARAM1), gpr.R(s)); + if (offset) + ADD(32, R(ABI_PARAM2), Imm32((u32)offset)); + if (update && offset) + { + gpr.LoadToX64(a, true, true); + MOV(32, gpr.R(a), R(ABI_PARAM2)); + } + TEST(32, R(ABI_PARAM2), Imm32(0x0C000000)); + FixupBranch unsafe_addr = J_CC(CC_NZ); + BSWAP(accessSize, ABI_PARAM1); +#ifdef _M_X64 + MOV(accessSize, MComplex(RBX, ABI_PARAM2, SCALE_1, 0), R(ABI_PARAM1)); +#else + AND(32, R(ABI_PARAM2), Imm32(Memory::MEMVIEW32_MASK)); + MOV(accessSize, MDisp(ABI_PARAM2, (u32)Memory::base), R(ABI_PARAM1)); +#endif + FixupBranch skip_call = J(); + SetJumpTarget(unsafe_addr); + switch (accessSize) + { + case 32: ABI_CallFunctionRR(ProtectFunction((void *)&Memory::Write_U32, 2), ABI_PARAM1, ABI_PARAM2); break; + case 16: ABI_CallFunctionRR(ProtectFunction((void *)&Memory::Write_U16, 2), ABI_PARAM1, ABI_PARAM2); break; + case 8: ABI_CallFunctionRR(ProtectFunction((void *)&Memory::Write_U8, 2), ABI_PARAM1, ABI_PARAM2); break; + } + SetJumpTarget(skip_call); + gpr.UnlockAll(); + gpr.UnlockAllX(); + } + else + { + Default(inst); + } + } + + // A few games use these heavily in video codecs. + void lmw(UGeckoInstruction inst) + { + INSTRUCTION_START; + Default(inst); + return; + + /* + /// BUGGY + //return _inst.RA ? (m_GPR[_inst.RA] + _inst.SIMM_16) : _inst.SIMM_16; + gpr.FlushLockX(ECX, EDX); + gpr.FlushLockX(ESI); + //INT3(); + MOV(32, R(EAX), Imm32((u32)(s32)inst.SIMM_16)); + if (inst.RA) + ADD(32, R(EAX), gpr.R(inst.RA)); + MOV(32, R(ECX), Imm32(inst.RD)); + MOV(32, R(ESI), Imm32(static_cast((u64)&PowerPC::ppcState.gpr[0]))); + const u8 *loopPtr = GetCodePtr(); + MOV(32, R(EDX), MComplex(RBX, EAX, SCALE_1, 0)); + MOV(32, MComplex(ESI, ECX, SCALE_4, 0), R(EDX)); + ADD(32, R(EAX), Imm8(4)); + ADD(32, R(ESI), Imm8(4)); + ADD(32, R(ECX), Imm8(1)); + CMP(32, R(ECX), Imm8(32)); + gpr.UnlockAllX();*/ + } + + void stmw(UGeckoInstruction inst) + { + INSTRUCTION_START; + Default(inst); + return; + /* + u32 uAddress = Helper_Get_EA(_inst); + for (int iReg = _inst.RS; iReg <= 31; iReg++, uAddress+=4) + { + Memory::Write_U32(m_GPR[iReg], uAddress); + if (PowerPC::ppcState.Exceptions & EXCEPTION_DSI) + return; + }*/ + } +} + diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStoreFloating.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStoreFloating.cpp index b15a7c8806..a57cef8d2a 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStoreFloating.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStoreFloating.cpp @@ -1,303 +1,303 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -// TODO(ector): Tons of pshufb optimization of the loads/stores, for SSSE3+, possibly SSE4, only. -// Should give a very noticable speed boost to paired single heavy code. - -#include "Common.h" - -#include "../PowerPC.h" -#include "../../Core.h" // include "Common.h", "CoreParameter.h" -#include "../../HW/GPFifo.h" -#include "../../HW/CommandProcessor.h" -#include "../../HW/PixelEngine.h" -#include "../../HW/Memmap.h" -#include "../PPCTables.h" -#include "CPUDetect.h" -#include "x64Emitter.h" -#include "ABI.h" - -#include "Jit.h" -#include "JitCache.h" -#include "JitAsm.h" -#include "JitRegCache.h" -#include "Jit_Util.h" - -// #define INSTRUCTION_START Default(inst); return; -#define INSTRUCTION_START - -#ifdef _M_IX86 -#define DISABLE_32BIT Default(inst); return; -#else -#define DISABLE_32BIT ; -#endif - -namespace Jit64 -{ - -// pshufb todo: MOVQ -const u8 GC_ALIGNED16(bswapShuffle1x4[16]) = {3, 2, 1, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; -const u8 GC_ALIGNED16(bswapShuffle2x4[16]) = {3, 2, 1, 0, 7, 6, 5, 4, 8, 9, 10, 11, 12, 13, 14, 15}; -const u8 GC_ALIGNED16(bswapShuffle1x8[16]) = {7, 6, 5, 4, 3, 2, 1, 0, 8, 9, 10, 11, 12, 13, 14, 15}; -const u8 GC_ALIGNED16(bswapShuffle1x8Dupe[16]) = {7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0}; -const u8 GC_ALIGNED16(bswapShuffle2x8[16]) = {7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8}; - -namespace { -u64 GC_ALIGNED16(temp64); -u32 GC_ALIGNED16(temp32); -} -// TODO: Add peephole optimizations for multiple consecutive lfd/lfs/stfd/stfs since they are so common, -// and pshufb could help a lot. -// Also add hacks for things like lfs/stfs the same reg consecutively, that is, simple memory moves. - -void lfs(UGeckoInstruction inst) -{ -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreFloatingOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - int d = inst.RD; - int a = inst.RA; - if (!a) - { - Default(inst); - return; - } - s32 offset = (s32)(s16)inst.SIMM_16; - gpr.FlushLockX(ABI_PARAM1); - gpr.Lock(a); - MOV(32, R(ABI_PARAM1), gpr.R(a)); - if (jo.assumeFPLoadFromMem) - { - UnsafeLoadRegToReg(ABI_PARAM1, EAX, 32, offset, false); - } - else - { - SafeLoadRegToEAX(ABI_PARAM1, 32, offset); - } - - MOV(32, M(&temp32), R(EAX)); - fpr.Lock(d); - fpr.LoadToX64(d, false); - CVTSS2SD(fpr.RX(d), M(&temp32)); - MOVDDUP(fpr.RX(d), fpr.R(d)); - gpr.UnlockAll(); - gpr.UnlockAllX(); - fpr.UnlockAll(); -} - - -void lfd(UGeckoInstruction inst) -{ -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreFloatingOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - if (!cpu_info.bSSSE3) { - DISABLE_32BIT; - } - int d = inst.RD; - int a = inst.RA; - if (!a) - { - Default(inst); - return; - } - s32 offset = (s32)(s16)inst.SIMM_16; - gpr.FlushLockX(ABI_PARAM1); - gpr.Lock(a); - MOV(32, R(ABI_PARAM1), gpr.R(a)); - // TODO - optimize. This has to load the previous value - upper double should stay unmodified. - fpr.LoadToX64(d, true); - fpr.Lock(d); - if (cpu_info.bSSSE3) { - X64Reg xd = fpr.RX(d); -#ifdef _M_X64 - MOVQ_xmm(XMM0, MComplex(RBX, ABI_PARAM1, SCALE_1, offset)); -#else - MOV(32, R(EAX), R(ABI_PARAM1)); - AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); - MOVQ_xmm(XMM0, MDisp(EAX, (u32)Memory::base + offset)); -#endif - PSHUFB(XMM0, M((void *)bswapShuffle1x8Dupe)); - MOVSD(xd, R(XMM0)); - } else { -#ifndef _M_X64 - PanicAlert("lfd - wtf"); -#endif - X64Reg xd = fpr.RX(d); - MOV(64, R(EAX), MComplex(RBX, ABI_PARAM1, SCALE_1, offset)); - BSWAP(64, EAX); - MOV(64, M(&temp64), R(EAX)); - MOVSD(XMM0, M(&temp64)); - MOVSD(xd, R(XMM0)); - } - gpr.UnlockAll(); - gpr.UnlockAllX(); - fpr.UnlockAll(); -} - - -void stfd(UGeckoInstruction inst) -{ -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreFloatingOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - if (!cpu_info.bSSSE3) - { - DISABLE_32BIT; - } - int s = inst.RS; - int a = inst.RA; - if (!a) - { - Default(inst); - return; - } - s32 offset = (s32)(s16)inst.SIMM_16; - gpr.FlushLockX(ABI_PARAM1); - gpr.Lock(a); - fpr.Lock(s); - MOV(32, R(ABI_PARAM1), gpr.R(a)); -#ifdef _M_IX86 - AND(32, R(ABI_PARAM1), Imm32(Memory::MEMVIEW32_MASK)); -#endif - if (cpu_info.bSSSE3) { - MOVAPD(XMM0, fpr.R(s)); - PSHUFB(XMM0, M((void *)bswapShuffle1x8)); -#ifdef _M_X64 - MOVQ_xmm(MComplex(RBX, ABI_PARAM1, SCALE_1, offset), XMM0); -#else - MOVQ_xmm(MDisp(ABI_PARAM1, (u32)Memory::base + offset), XMM0); -#endif - } else { - fpr.LoadToX64(s, true, false); - MOVSD(M(&temp64), fpr.RX(s)); - MOV(64, R(EAX), M(&temp64)); - BSWAP(64, EAX); - MOV(64, MComplex(RBX, ABI_PARAM1, SCALE_1, offset), R(EAX)); - } - gpr.UnlockAll(); - gpr.UnlockAllX(); - fpr.UnlockAll(); -} - - -void stfs(UGeckoInstruction inst) -{ -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreFloatingOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - bool update = inst.OPCD & 1; - int s = inst.RS; - int a = inst.RA; - s32 offset = (s32)(s16)inst.SIMM_16; - if (!a || update) { - Default(inst); - return; - } - - if (gpr.R(a).IsImm()) - { - u32 addr = (u32)(gpr.R(a).offset + offset); - if (Memory::IsRAMAddress(addr)) - { - if (cpu_info.bSSSE3) { - CVTSD2SS(XMM0, fpr.R(s)); - PSHUFB(XMM0, M((void *)bswapShuffle1x4)); - WriteFloatToConstRamAddress(XMM0, addr); - return; - } - } - else if (addr == 0xCC008000) - { - // Float directly to write gather pipe! Fun! - CVTSD2SS(XMM0, fpr.R(s)); - CALL((void*)Asm::fifoDirectWriteFloat); - // TODO - js.fifoBytesThisBlock += 4; - return; - } - } - - gpr.FlushLockX(ABI_PARAM1, ABI_PARAM2); - gpr.Lock(a); - fpr.Lock(s); - MOV(32, R(ABI_PARAM2), gpr.R(a)); - ADD(32, R(ABI_PARAM2), Imm32(offset)); - if (update && offset) - { - MOV(32, gpr.R(a), R(ABI_PARAM2)); - } - CVTSD2SS(XMM0, fpr.R(s)); - MOVSS(M(&temp32), XMM0); - MOV(32, R(ABI_PARAM1), M(&temp32)); - SafeWriteRegToReg(ABI_PARAM1, ABI_PARAM2, 32, 0); - gpr.UnlockAll(); - gpr.UnlockAllX(); - fpr.UnlockAll(); -} - - -void stfsx(UGeckoInstruction inst) -{ - // We can take a shortcut here - it's not likely that a hardware access would use this instruction. - INSTRUCTION_START; - // TODO - Default(inst); return; -} - - -void lfsx(UGeckoInstruction inst) -{ -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreFloatingOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - fpr.Lock(inst.RS); - fpr.LoadToX64(inst.RS, false, true); - MOV(32, R(EAX), gpr.R(inst.RB)); - if (inst.RA) - ADD(32, R(EAX), gpr.R(inst.RA)); - if (cpu_info.bSSSE3) { - X64Reg r = fpr.R(inst.RS).GetSimpleReg(); -#ifdef _M_IX86 - AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); - MOVD_xmm(r, MDisp(EAX, (u32)Memory::base)); -#else - MOVD_xmm(r, MComplex(RBX, EAX, SCALE_1, 0)); -#endif - PSHUFB(r, M((void *)bswapShuffle1x4)); - CVTSS2SD(r, R(r)); - MOVDDUP(r, R(r)); - } else { - UnsafeLoadRegToReg(EAX, EAX, 32, false); - MOV(32, M(&temp32), R(EAX)); - CVTSS2SD(XMM0, M(&temp32)); - MOVDDUP(fpr.R(inst.RS).GetSimpleReg(), R(XMM0)); - } - fpr.UnlockAll(); -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// TODO(ector): Tons of pshufb optimization of the loads/stores, for SSSE3+, possibly SSE4, only. +// Should give a very noticable speed boost to paired single heavy code. + +#include "Common.h" + +#include "../PowerPC.h" +#include "../../Core.h" // include "Common.h", "CoreParameter.h" +#include "../../HW/GPFifo.h" +#include "../../HW/CommandProcessor.h" +#include "../../HW/PixelEngine.h" +#include "../../HW/Memmap.h" +#include "../PPCTables.h" +#include "CPUDetect.h" +#include "x64Emitter.h" +#include "ABI.h" + +#include "Jit.h" +#include "JitCache.h" +#include "JitAsm.h" +#include "JitRegCache.h" +#include "Jit_Util.h" + +// #define INSTRUCTION_START Default(inst); return; +#define INSTRUCTION_START + +#ifdef _M_IX86 +#define DISABLE_32BIT Default(inst); return; +#else +#define DISABLE_32BIT ; +#endif + +namespace Jit64 +{ + +// pshufb todo: MOVQ +const u8 GC_ALIGNED16(bswapShuffle1x4[16]) = {3, 2, 1, 0, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; +const u8 GC_ALIGNED16(bswapShuffle2x4[16]) = {3, 2, 1, 0, 7, 6, 5, 4, 8, 9, 10, 11, 12, 13, 14, 15}; +const u8 GC_ALIGNED16(bswapShuffle1x8[16]) = {7, 6, 5, 4, 3, 2, 1, 0, 8, 9, 10, 11, 12, 13, 14, 15}; +const u8 GC_ALIGNED16(bswapShuffle1x8Dupe[16]) = {7, 6, 5, 4, 3, 2, 1, 0, 7, 6, 5, 4, 3, 2, 1, 0}; +const u8 GC_ALIGNED16(bswapShuffle2x8[16]) = {7, 6, 5, 4, 3, 2, 1, 0, 15, 14, 13, 12, 11, 10, 9, 8}; + +namespace { +u64 GC_ALIGNED16(temp64); +u32 GC_ALIGNED16(temp32); +} +// TODO: Add peephole optimizations for multiple consecutive lfd/lfs/stfd/stfs since they are so common, +// and pshufb could help a lot. +// Also add hacks for things like lfs/stfs the same reg consecutively, that is, simple memory moves. + +void lfs(UGeckoInstruction inst) +{ +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreFloatingOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + int d = inst.RD; + int a = inst.RA; + if (!a) + { + Default(inst); + return; + } + s32 offset = (s32)(s16)inst.SIMM_16; + gpr.FlushLockX(ABI_PARAM1); + gpr.Lock(a); + MOV(32, R(ABI_PARAM1), gpr.R(a)); + if (jo.assumeFPLoadFromMem) + { + UnsafeLoadRegToReg(ABI_PARAM1, EAX, 32, offset, false); + } + else + { + SafeLoadRegToEAX(ABI_PARAM1, 32, offset); + } + + MOV(32, M(&temp32), R(EAX)); + fpr.Lock(d); + fpr.LoadToX64(d, false); + CVTSS2SD(fpr.RX(d), M(&temp32)); + MOVDDUP(fpr.RX(d), fpr.R(d)); + gpr.UnlockAll(); + gpr.UnlockAllX(); + fpr.UnlockAll(); +} + + +void lfd(UGeckoInstruction inst) +{ +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreFloatingOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + if (!cpu_info.bSSSE3) { + DISABLE_32BIT; + } + int d = inst.RD; + int a = inst.RA; + if (!a) + { + Default(inst); + return; + } + s32 offset = (s32)(s16)inst.SIMM_16; + gpr.FlushLockX(ABI_PARAM1); + gpr.Lock(a); + MOV(32, R(ABI_PARAM1), gpr.R(a)); + // TODO - optimize. This has to load the previous value - upper double should stay unmodified. + fpr.LoadToX64(d, true); + fpr.Lock(d); + if (cpu_info.bSSSE3) { + X64Reg xd = fpr.RX(d); +#ifdef _M_X64 + MOVQ_xmm(XMM0, MComplex(RBX, ABI_PARAM1, SCALE_1, offset)); +#else + MOV(32, R(EAX), R(ABI_PARAM1)); + AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); + MOVQ_xmm(XMM0, MDisp(EAX, (u32)Memory::base + offset)); +#endif + PSHUFB(XMM0, M((void *)bswapShuffle1x8Dupe)); + MOVSD(xd, R(XMM0)); + } else { +#ifndef _M_X64 + PanicAlert("lfd - wtf"); +#endif + X64Reg xd = fpr.RX(d); + MOV(64, R(EAX), MComplex(RBX, ABI_PARAM1, SCALE_1, offset)); + BSWAP(64, EAX); + MOV(64, M(&temp64), R(EAX)); + MOVSD(XMM0, M(&temp64)); + MOVSD(xd, R(XMM0)); + } + gpr.UnlockAll(); + gpr.UnlockAllX(); + fpr.UnlockAll(); +} + + +void stfd(UGeckoInstruction inst) +{ +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreFloatingOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + if (!cpu_info.bSSSE3) + { + DISABLE_32BIT; + } + int s = inst.RS; + int a = inst.RA; + if (!a) + { + Default(inst); + return; + } + s32 offset = (s32)(s16)inst.SIMM_16; + gpr.FlushLockX(ABI_PARAM1); + gpr.Lock(a); + fpr.Lock(s); + MOV(32, R(ABI_PARAM1), gpr.R(a)); +#ifdef _M_IX86 + AND(32, R(ABI_PARAM1), Imm32(Memory::MEMVIEW32_MASK)); +#endif + if (cpu_info.bSSSE3) { + MOVAPD(XMM0, fpr.R(s)); + PSHUFB(XMM0, M((void *)bswapShuffle1x8)); +#ifdef _M_X64 + MOVQ_xmm(MComplex(RBX, ABI_PARAM1, SCALE_1, offset), XMM0); +#else + MOVQ_xmm(MDisp(ABI_PARAM1, (u32)Memory::base + offset), XMM0); +#endif + } else { + fpr.LoadToX64(s, true, false); + MOVSD(M(&temp64), fpr.RX(s)); + MOV(64, R(EAX), M(&temp64)); + BSWAP(64, EAX); + MOV(64, MComplex(RBX, ABI_PARAM1, SCALE_1, offset), R(EAX)); + } + gpr.UnlockAll(); + gpr.UnlockAllX(); + fpr.UnlockAll(); +} + + +void stfs(UGeckoInstruction inst) +{ +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreFloatingOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + bool update = inst.OPCD & 1; + int s = inst.RS; + int a = inst.RA; + s32 offset = (s32)(s16)inst.SIMM_16; + if (!a || update) { + Default(inst); + return; + } + + if (gpr.R(a).IsImm()) + { + u32 addr = (u32)(gpr.R(a).offset + offset); + if (Memory::IsRAMAddress(addr)) + { + if (cpu_info.bSSSE3) { + CVTSD2SS(XMM0, fpr.R(s)); + PSHUFB(XMM0, M((void *)bswapShuffle1x4)); + WriteFloatToConstRamAddress(XMM0, addr); + return; + } + } + else if (addr == 0xCC008000) + { + // Float directly to write gather pipe! Fun! + CVTSD2SS(XMM0, fpr.R(s)); + CALL((void*)Asm::fifoDirectWriteFloat); + // TODO + js.fifoBytesThisBlock += 4; + return; + } + } + + gpr.FlushLockX(ABI_PARAM1, ABI_PARAM2); + gpr.Lock(a); + fpr.Lock(s); + MOV(32, R(ABI_PARAM2), gpr.R(a)); + ADD(32, R(ABI_PARAM2), Imm32(offset)); + if (update && offset) + { + MOV(32, gpr.R(a), R(ABI_PARAM2)); + } + CVTSD2SS(XMM0, fpr.R(s)); + MOVSS(M(&temp32), XMM0); + MOV(32, R(ABI_PARAM1), M(&temp32)); + SafeWriteRegToReg(ABI_PARAM1, ABI_PARAM2, 32, 0); + gpr.UnlockAll(); + gpr.UnlockAllX(); + fpr.UnlockAll(); +} + + +void stfsx(UGeckoInstruction inst) +{ + // We can take a shortcut here - it's not likely that a hardware access would use this instruction. + INSTRUCTION_START; + // TODO + Default(inst); return; +} + + +void lfsx(UGeckoInstruction inst) +{ +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStoreFloatingOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + fpr.Lock(inst.RS); + fpr.LoadToX64(inst.RS, false, true); + MOV(32, R(EAX), gpr.R(inst.RB)); + if (inst.RA) + ADD(32, R(EAX), gpr.R(inst.RA)); + if (cpu_info.bSSSE3) { + X64Reg r = fpr.R(inst.RS).GetSimpleReg(); +#ifdef _M_IX86 + AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); + MOVD_xmm(r, MDisp(EAX, (u32)Memory::base)); +#else + MOVD_xmm(r, MComplex(RBX, EAX, SCALE_1, 0)); +#endif + PSHUFB(r, M((void *)bswapShuffle1x4)); + CVTSS2SD(r, R(r)); + MOVDDUP(r, R(r)); + } else { + UnsafeLoadRegToReg(EAX, EAX, 32, false); + MOV(32, M(&temp32), R(EAX)); + CVTSS2SD(XMM0, M(&temp32)); + MOVDDUP(fpr.R(inst.RS).GetSimpleReg(), R(XMM0)); + } + fpr.UnlockAll(); +} + +} // namespace diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStorePaired.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStorePaired.cpp index 1aa9d8dccd..d59c44b137 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStorePaired.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit_LoadStorePaired.cpp @@ -1,465 +1,465 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -// TODO(ector): Tons of pshufb optimization of the loads/stores, for SSSE3+, possibly SSE4, only. -// Should give a very noticable speed boost to paired single heavy code. - -#include "Common.h" - -#include "Thunk.h" -#include "../PowerPC.h" -#include "../../Core.h" -#include "../../HW/GPFifo.h" -#include "../../HW/CommandProcessor.h" -#include "../../HW/PixelEngine.h" -#include "../../HW/Memmap.h" -#include "../PPCTables.h" -#include "CPUDetect.h" -#include "x64Emitter.h" -#include "ABI.h" - -#include "Jit.h" -#include "JitCache.h" -#include "JitAsm.h" -#include "JitRegCache.h" - -#define INSTRUCTION_START - -namespace Jit64 { - -const u8 GC_ALIGNED16(pbswapShuffle2x4[16]) = {3, 2, 1, 0, 7, 6, 5, 4, 8, 9, 10, 11, 12, 13, 14, 15}; -const u8 GC_ALIGNED16(pbswapShuffleNoop[16]) = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; - -static double GC_ALIGNED16(psTemp[2]) = {1.0, 1.0}; -static u64 GC_ALIGNED16(temp64); - -// TODO(ector): Improve 64-bit version -void WriteDual32(u64 value, u32 address) -{ - Memory::Write_U32((u32)(value >> 32), address); - Memory::Write_U32((u32)value, address + 4); -} - -const double GC_ALIGNED16(m_quantizeTableD[]) = -{ - (1 << 0), (1 << 1), (1 << 2), (1 << 3), - (1 << 4), (1 << 5), (1 << 6), (1 << 7), - (1 << 8), (1 << 9), (1 << 10), (1 << 11), - (1 << 12), (1 << 13), (1 << 14), (1 << 15), - (1 << 16), (1 << 17), (1 << 18), (1 << 19), - (1 << 20), (1 << 21), (1 << 22), (1 << 23), - (1 << 24), (1 << 25), (1 << 26), (1 << 27), - (1 << 28), (1 << 29), (1 << 30), (1 << 31), - 1.0 / (1ULL << 32), 1.0 / (1 << 31), 1.0 / (1 << 30), 1.0 / (1 << 29), - 1.0 / (1 << 28), 1.0 / (1 << 27), 1.0 / (1 << 26), 1.0 / (1 << 25), - 1.0 / (1 << 24), 1.0 / (1 << 23), 1.0 / (1 << 22), 1.0 / (1 << 21), - 1.0 / (1 << 20), 1.0 / (1 << 19), 1.0 / (1 << 18), 1.0 / (1 << 17), - 1.0 / (1 << 16), 1.0 / (1 << 15), 1.0 / (1 << 14), 1.0 / (1 << 13), - 1.0 / (1 << 12), 1.0 / (1 << 11), 1.0 / (1 << 10), 1.0 / (1 << 9), - 1.0 / (1 << 8), 1.0 / (1 << 7), 1.0 / (1 << 6), 1.0 / (1 << 5), - 1.0 / (1 << 4), 1.0 / (1 << 3), 1.0 / (1 << 2), 1.0 / (1 << 1), -}; - -const double GC_ALIGNED16(m_dequantizeTableD[]) = -{ - 1.0 / (1 << 0), 1.0 / (1 << 1), 1.0 / (1 << 2), 1.0 / (1 << 3), - 1.0 / (1 << 4), 1.0 / (1 << 5), 1.0 / (1 << 6), 1.0 / (1 << 7), - 1.0 / (1 << 8), 1.0 / (1 << 9), 1.0 / (1 << 10), 1.0 / (1 << 11), - 1.0 / (1 << 12), 1.0 / (1 << 13), 1.0 / (1 << 14), 1.0 / (1 << 15), - 1.0 / (1 << 16), 1.0 / (1 << 17), 1.0 / (1 << 18), 1.0 / (1 << 19), - 1.0 / (1 << 20), 1.0 / (1 << 21), 1.0 / (1 << 22), 1.0 / (1 << 23), - 1.0 / (1 << 24), 1.0 / (1 << 25), 1.0 / (1 << 26), 1.0 / (1 << 27), - 1.0 / (1 << 28), 1.0 / (1 << 29), 1.0 / (1 << 30), 1.0 / (1 << 31), - (1ULL << 32), (1 << 31), (1 << 30), (1 << 29), - (1 << 28), (1 << 27), (1 << 26), (1 << 25), - (1 << 24), (1 << 23), (1 << 22), (1 << 21), - (1 << 20), (1 << 19), (1 << 18), (1 << 17), - (1 << 16), (1 << 15), (1 << 14), (1 << 13), - (1 << 12), (1 << 11), (1 << 10), (1 << 9), - (1 << 8), (1 << 7), (1 << 6), (1 << 5), - (1 << 4), (1 << 3), (1 << 2), (1 << 1), -}; - -// The big problem is likely instructions that set the quantizers in the same block. -// We will have to break block after quantizers are written to. -void psq_st(UGeckoInstruction inst) -{ -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStorePairedOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - js.block_flags |= BLOCK_USE_GQR0 << inst.I; - - if (js.blockSetsQuantizers || !Core::GetStartupParameter().bOptimizeQuantizers) - { - Default(inst); - return; - } - if (!inst.RA) - { - // This really should never happen. Unless we change this to also support stwux - Default(inst); - return; - } - - const UGQR gqr(rSPR(SPR_GQR0 + inst.I)); - const EQuantizeType stType = static_cast(gqr.ST_TYPE); - int stScale = gqr.ST_SCALE; - bool update = inst.OPCD == 61; - - int offset = inst.SIMM_12; - int a = inst.RA; - int s = inst.RS; // Fp numbers - - if (inst.W) { - // PanicAlert("W=1: stType %i stScale %i update %i", (int)stType, (int)stScale, (int)update); - // It's fairly common that games write stuff to the pipe using this. Then, it's pretty much only - // floats so that's what we'll work on. - switch (stType) - { - case QUANTIZE_FLOAT: - { - // This one has quite a bit of optimization potential. - if (gpr.R(a).IsImm()) - { - PanicAlert("Imm: %08x", gpr.R(a).offset); - } - gpr.FlushLockX(ABI_PARAM1, ABI_PARAM2); - gpr.Lock(a); - fpr.Lock(s); - if (update) - gpr.LoadToX64(a, true, true); - MOV(32, R(ABI_PARAM2), gpr.R(a)); - if (offset) - ADD(32, R(ABI_PARAM2), Imm32((u32)offset)); - TEST(32, R(ABI_PARAM2), Imm32(0x0C000000)); - if (update && offset) - MOV(32, gpr.R(a), R(ABI_PARAM2)); - CVTSD2SS(XMM0, fpr.R(s)); - MOVD_xmm(M(&temp64), XMM0); - MOV(32, R(ABI_PARAM1), M(&temp64)); - FixupBranch argh = J_CC(CC_NZ); - BSWAP(32, ABI_PARAM1); -#ifdef _M_X64 - MOV(32, MComplex(RBX, ABI_PARAM2, SCALE_1, 0), R(ABI_PARAM1)); -#else - MOV(32, R(EAX), R(ABI_PARAM2)); - AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); - MOV(32, MDisp(EAX, (u32)Memory::base), R(ABI_PARAM1)); -#endif - FixupBranch skip_call = J(); - SetJumpTarget(argh); - ABI_CallFunctionRR(ProtectFunction((void *)&Memory::Write_U32, 2), ABI_PARAM1, ABI_PARAM2); - SetJumpTarget(skip_call); - gpr.UnlockAll(); - gpr.UnlockAllX(); - fpr.UnlockAll(); - return; - } - default: - Default(inst); - return; - } - return; - } - - if (stType == QUANTIZE_FLOAT) - { - if (gpr.R(a).IsImm() && !update && cpu_info.bSSSE3) - { - u32 addr = gpr.R(a).offset + offset; - if (addr == 0xCC008000) { - // Writing to FIFO. Let's do fast method. - CVTPD2PS(XMM0, fpr.R(s)); - PSHUFB(XMM0, M((void*)&pbswapShuffle2x4)); - CALL((void*)Asm::fifoDirectWriteXmm64); - js.fifoBytesThisBlock += 8; - return; - } - } - - gpr.FlushLockX(ABI_PARAM1, ABI_PARAM2); - gpr.Lock(a); - fpr.Lock(s); - if (update) - gpr.LoadToX64(a, true, true); - MOV(32, R(ABI_PARAM2), gpr.R(a)); - if (offset) - ADD(32, R(ABI_PARAM2), Imm32((u32)offset)); - TEST(32, R(ABI_PARAM2), Imm32(0x0C000000)); - if (update && offset) - MOV(32, gpr.R(a), R(ABI_PARAM2)); - CVTPD2PS(XMM0, fpr.R(s)); - SHUFPS(XMM0, R(XMM0), 1); - MOVQ_xmm(M(&temp64), XMM0); -#ifdef _M_X64 - MOV(64, R(ABI_PARAM1), M(&temp64)); - FixupBranch argh = J_CC(CC_NZ); - BSWAP(64, ABI_PARAM1); - MOV(64, MComplex(RBX, ABI_PARAM2, SCALE_1, 0), R(ABI_PARAM1)); - FixupBranch arg2 = J(); - SetJumpTarget(argh); - CALL(ProtectFunction((void *)&WriteDual32, 0)); -#else - FixupBranch argh = J_CC(CC_NZ); - MOV(32, R(ABI_PARAM1), M(((char*)&temp64) + 4)); - BSWAP(32, ABI_PARAM1); - AND(32, R(ABI_PARAM2), Imm32(Memory::MEMVIEW32_MASK)); - MOV(32, MDisp(ABI_PARAM2, (u32)Memory::base), R(ABI_PARAM1)); - MOV(32, R(ABI_PARAM1), M(&temp64)); - BSWAP(32, ABI_PARAM1); - MOV(32, MDisp(ABI_PARAM2, 4+(u32)Memory::base), R(ABI_PARAM1)); - FixupBranch arg2 = J(); - SetJumpTarget(argh); - MOV(32, R(ABI_PARAM1), M(((char*)&temp64) + 4)); - ABI_CallFunctionRR(ProtectFunction((void *)&Memory::Write_U32, 2), ABI_PARAM1, ABI_PARAM2); - MOV(32, R(ABI_PARAM1), M(((char*)&temp64))); - ADD(32, R(ABI_PARAM2), Imm32(4)); - ABI_CallFunctionRR(ProtectFunction((void *)&Memory::Write_U32, 2), ABI_PARAM1, ABI_PARAM2); -#endif - SetJumpTarget(arg2); - gpr.UnlockAll(); - gpr.UnlockAllX(); - fpr.UnlockAll(); - } - else if (stType == QUANTIZE_U8) - { - gpr.FlushLockX(ABI_PARAM1, ABI_PARAM2); - gpr.Lock(a); - fpr.Lock(s); - if (update) - gpr.LoadToX64(a, true, update); - MOV(32, R(ABI_PARAM2), gpr.R(a)); - if (offset) - ADD(32, R(ABI_PARAM2), Imm32((u32)offset)); - if (update && offset) - MOV(32, gpr.R(a), R(ABI_PARAM2)); - MOVAPD(XMM0, fpr.R(s)); - MOVDDUP(XMM1, M((void*)&m_quantizeTableD[stScale])); - MULPD(XMM0, R(XMM1)); - CVTPD2DQ(XMM0, R(XMM0)); - PACKSSDW(XMM0, R(XMM0)); - PACKUSWB(XMM0, R(XMM0)); - MOVD_xmm(M(&temp64), XMM0); - MOV(16, R(ABI_PARAM1), M(&temp64)); -#ifdef _M_X64 - MOV(16, MComplex(RBX, ABI_PARAM2, SCALE_1, 0), R(ABI_PARAM1)); -#else - MOV(32, R(EAX), R(ABI_PARAM2)); - AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); - MOV(16, MDisp(EAX, (u32)Memory::base), R(ABI_PARAM1)); -#endif - if (update) - MOV(32, gpr.R(a), R(ABI_PARAM2)); - gpr.UnlockAll(); - gpr.UnlockAllX(); - fpr.UnlockAll(); - } - else if (stType == QUANTIZE_S16) - { - gpr.FlushLockX(ABI_PARAM1, ABI_PARAM2); - gpr.Lock(a); - fpr.Lock(s); - if (update) - gpr.LoadToX64(a, true, update); - MOV(32, R(ABI_PARAM2), gpr.R(a)); - if (offset) - ADD(32, R(ABI_PARAM2), Imm32((u32)offset)); - if (update) - MOV(32, gpr.R(a), R(ABI_PARAM2)); - MOVAPD(XMM0, fpr.R(s)); - MOVDDUP(XMM1, M((void*)&m_quantizeTableD[stScale])); - MULPD(XMM0, R(XMM1)); - SHUFPD(XMM0, R(XMM0), 1); - CVTPD2DQ(XMM0, R(XMM0)); - PACKSSDW(XMM0, R(XMM0)); - MOVD_xmm(M(&temp64), XMM0); - MOV(32, R(ABI_PARAM1), M(&temp64)); - BSWAP(32, ABI_PARAM1); -#ifdef _M_X64 - MOV(32, MComplex(RBX, ABI_PARAM2, SCALE_1, 0), R(ABI_PARAM1)); -#else - MOV(32, R(EAX), R(ABI_PARAM2)); - AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); - MOV(32, MDisp(EAX, (u32)Memory::base), R(ABI_PARAM1)); -#endif - gpr.UnlockAll(); - gpr.UnlockAllX(); - fpr.UnlockAll(); - } - else { - // Dodger uses this. - // mario tennis - //PanicAlert("st %i:%i", stType, inst.W); - Default(inst); - } -} - -void psq_l(UGeckoInstruction inst) -{ -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStorePairedOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - js.block_flags |= BLOCK_USE_GQR0 << inst.I; - - if (js.blockSetsQuantizers || !Core::GetStartupParameter().bOptimizeQuantizers) - { - Default(inst); - return; - } - - const UGQR gqr(rSPR(SPR_GQR0 + inst.I)); - const EQuantizeType ldType = static_cast(gqr.LD_TYPE); - int ldScale = gqr.LD_SCALE; - bool update = inst.OPCD == 57; - if (!inst.RA || inst.W) - { - // 0 1 during load - //PanicAlert("ld:%i %i", ldType, (int)inst.W); - Default(inst); - return; - } - int offset = inst.SIMM_12; - switch (ldType) { - case QUANTIZE_FLOAT: // We know this is from RAM, so we don't need to check the address. - { -#ifdef _M_X64 - gpr.LoadToX64(inst.RA, true, update); - fpr.LoadToX64(inst.RS, false); - if (cpu_info.bSSSE3) { - X64Reg xd = fpr.R(inst.RS).GetSimpleReg(); - MOVQ_xmm(xd, MComplex(RBX, gpr.R(inst.RA).GetSimpleReg(), 1, offset)); - PSHUFB(xd, M((void *)pbswapShuffle2x4)); - CVTPS2PD(xd, R(xd)); - } else { - MOV(64, R(RAX), MComplex(RBX, gpr.R(inst.RA).GetSimpleReg(), 1, offset)); - BSWAP(64, RAX); - MOV(64, M(&psTemp[0]), R(RAX)); - X64Reg r = fpr.R(inst.RS).GetSimpleReg(); - CVTPS2PD(r, M(&psTemp[0])); - SHUFPD(r, R(r), 1); - } - if (update && offset != 0) - ADD(32, gpr.R(inst.RA), Imm32(offset)); - break; -#else - if (cpu_info.bSSSE3) { - gpr.LoadToX64(inst.RA, true, update); - fpr.LoadToX64(inst.RS, false); - X64Reg xd = fpr.R(inst.RS).GetSimpleReg(); - MOV(32, R(EAX), gpr.R(inst.RA)); - AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); - MOVQ_xmm(xd, MDisp(EAX, (u32)Memory::base + offset)); - PSHUFB(xd, M((void *)pbswapShuffle2x4)); - CVTPS2PD(xd, R(xd)); - } else { - gpr.FlushLockX(ECX); - gpr.LoadToX64(inst.RA, true, update); - // This can probably be optimized somewhat. - LEA(32, ECX, MDisp(gpr.R(inst.RA).GetSimpleReg(), offset)); - AND(32, R(ECX), Imm32(Memory::MEMVIEW32_MASK)); - MOV(32, R(EAX), MDisp(ECX, (u32)Memory::base)); - BSWAP(32, RAX); - MOV(32, M(&psTemp[0]), R(RAX)); - MOV(32, R(EAX), MDisp(ECX, (u32)Memory::base + 4)); - BSWAP(32, RAX); - MOV(32, M(((float *)&psTemp[0]) + 1), R(RAX)); - fpr.LoadToX64(inst.RS, false, true); - X64Reg r = fpr.R(inst.RS).GetSimpleReg(); - CVTPS2PD(r, M(&psTemp[0])); - gpr.UnlockAllX(); - } - if (update && offset != 0) - ADD(32, gpr.R(inst.RA), Imm32(offset)); - break; -#endif - } - case QUANTIZE_U8: - { - gpr.LoadToX64(inst.RA, true, update); -#ifdef _M_X64 - MOVZX(32, 16, EAX, MComplex(RBX, gpr.R(inst.RA).GetSimpleReg(), 1, offset)); -#else - LEA(32, EAX, MDisp(gpr.R(inst.RA).GetSimpleReg(), offset)); - AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); - MOVZX(32, 16, EAX, MDisp(EAX, (u32)Memory::base)); -#endif - MOV(32, M(&temp64), R(EAX)); - MOVD_xmm(XMM0, M(&temp64)); - // SSE4 optimization opportunity here. - PXOR(XMM1, R(XMM1)); - PUNPCKLBW(XMM0, R(XMM1)); - PUNPCKLWD(XMM0, R(XMM1)); - CVTDQ2PD(XMM0, R(XMM0)); - fpr.LoadToX64(inst.RS, false, true); - X64Reg r = fpr.R(inst.RS).GetSimpleReg(); - MOVDDUP(r, M((void *)&m_dequantizeTableD[ldScale])); - MULPD(r, R(XMM0)); - if (update && offset != 0) - ADD(32, gpr.R(inst.RA), Imm32(offset)); - } - break; - case QUANTIZE_S16: - { - gpr.LoadToX64(inst.RA, true, update); -#ifdef _M_X64 - MOV(32, R(EAX), MComplex(RBX, gpr.R(inst.RA).GetSimpleReg(), 1, offset)); -#else - LEA(32, EAX, MDisp(gpr.R(inst.RA).GetSimpleReg(), offset)); - AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); - MOV(32, R(EAX), MDisp(EAX, (u32)Memory::base)); -#endif - BSWAP(32, EAX); - MOV(32, M(&temp64), R(EAX)); - //INT3(); - fpr.LoadToX64(inst.RS, false, true); - X64Reg r = fpr.R(inst.RS).GetSimpleReg(); - MOVD_xmm(XMM0, M(&temp64)); - PUNPCKLWD(XMM0, R(XMM0)); // unpack to higher word in each dword.. - PSRAD(XMM0, 16); // then use this signed shift to sign extend. clever eh? :P - CVTDQ2PD(XMM0, R(XMM0)); - MOVDDUP(r, M((void*)&m_dequantizeTableD[ldScale])); - MULPD(r, R(XMM0)); - SHUFPD(r, R(r), 1); - if (update && offset != 0) - ADD(32, gpr.R(inst.RA), Imm32(offset)); - } - break; - - /* - Dynamic quantizer. Todo when we have a test set. - MOVZX(32, 8, EAX, M(((char *)&PowerPC::ppcState.spr[SPR_GQR0 + inst.I]) + 3)); // it's in the high byte. - AND(32, R(EAX), Imm8(0x3F)); - MOV(32, R(ECX), Imm32((u32)&m_dequantizeTableD)); - MOVDDUP(r, MComplex(RCX, EAX, 8, 0)); - */ - default: - // 4 0 - // 6 0 //power tennis - // 5 0 - // PanicAlert("ld:%i %i", ldType, (int)inst.W); - Default(inst); - return; - } - - //u32 EA = (m_GPR[_inst.RA] + _inst.SIMM_12) : _inst.SIMM_12; -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// TODO(ector): Tons of pshufb optimization of the loads/stores, for SSSE3+, possibly SSE4, only. +// Should give a very noticable speed boost to paired single heavy code. + +#include "Common.h" + +#include "Thunk.h" +#include "../PowerPC.h" +#include "../../Core.h" +#include "../../HW/GPFifo.h" +#include "../../HW/CommandProcessor.h" +#include "../../HW/PixelEngine.h" +#include "../../HW/Memmap.h" +#include "../PPCTables.h" +#include "CPUDetect.h" +#include "x64Emitter.h" +#include "ABI.h" + +#include "Jit.h" +#include "JitCache.h" +#include "JitAsm.h" +#include "JitRegCache.h" + +#define INSTRUCTION_START + +namespace Jit64 { + +const u8 GC_ALIGNED16(pbswapShuffle2x4[16]) = {3, 2, 1, 0, 7, 6, 5, 4, 8, 9, 10, 11, 12, 13, 14, 15}; +const u8 GC_ALIGNED16(pbswapShuffleNoop[16]) = {0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}; + +static double GC_ALIGNED16(psTemp[2]) = {1.0, 1.0}; +static u64 GC_ALIGNED16(temp64); + +// TODO(ector): Improve 64-bit version +void WriteDual32(u64 value, u32 address) +{ + Memory::Write_U32((u32)(value >> 32), address); + Memory::Write_U32((u32)value, address + 4); +} + +const double GC_ALIGNED16(m_quantizeTableD[]) = +{ + (1 << 0), (1 << 1), (1 << 2), (1 << 3), + (1 << 4), (1 << 5), (1 << 6), (1 << 7), + (1 << 8), (1 << 9), (1 << 10), (1 << 11), + (1 << 12), (1 << 13), (1 << 14), (1 << 15), + (1 << 16), (1 << 17), (1 << 18), (1 << 19), + (1 << 20), (1 << 21), (1 << 22), (1 << 23), + (1 << 24), (1 << 25), (1 << 26), (1 << 27), + (1 << 28), (1 << 29), (1 << 30), (1 << 31), + 1.0 / (1ULL << 32), 1.0 / (1 << 31), 1.0 / (1 << 30), 1.0 / (1 << 29), + 1.0 / (1 << 28), 1.0 / (1 << 27), 1.0 / (1 << 26), 1.0 / (1 << 25), + 1.0 / (1 << 24), 1.0 / (1 << 23), 1.0 / (1 << 22), 1.0 / (1 << 21), + 1.0 / (1 << 20), 1.0 / (1 << 19), 1.0 / (1 << 18), 1.0 / (1 << 17), + 1.0 / (1 << 16), 1.0 / (1 << 15), 1.0 / (1 << 14), 1.0 / (1 << 13), + 1.0 / (1 << 12), 1.0 / (1 << 11), 1.0 / (1 << 10), 1.0 / (1 << 9), + 1.0 / (1 << 8), 1.0 / (1 << 7), 1.0 / (1 << 6), 1.0 / (1 << 5), + 1.0 / (1 << 4), 1.0 / (1 << 3), 1.0 / (1 << 2), 1.0 / (1 << 1), +}; + +const double GC_ALIGNED16(m_dequantizeTableD[]) = +{ + 1.0 / (1 << 0), 1.0 / (1 << 1), 1.0 / (1 << 2), 1.0 / (1 << 3), + 1.0 / (1 << 4), 1.0 / (1 << 5), 1.0 / (1 << 6), 1.0 / (1 << 7), + 1.0 / (1 << 8), 1.0 / (1 << 9), 1.0 / (1 << 10), 1.0 / (1 << 11), + 1.0 / (1 << 12), 1.0 / (1 << 13), 1.0 / (1 << 14), 1.0 / (1 << 15), + 1.0 / (1 << 16), 1.0 / (1 << 17), 1.0 / (1 << 18), 1.0 / (1 << 19), + 1.0 / (1 << 20), 1.0 / (1 << 21), 1.0 / (1 << 22), 1.0 / (1 << 23), + 1.0 / (1 << 24), 1.0 / (1 << 25), 1.0 / (1 << 26), 1.0 / (1 << 27), + 1.0 / (1 << 28), 1.0 / (1 << 29), 1.0 / (1 << 30), 1.0 / (1 << 31), + (1ULL << 32), (1 << 31), (1 << 30), (1 << 29), + (1 << 28), (1 << 27), (1 << 26), (1 << 25), + (1 << 24), (1 << 23), (1 << 22), (1 << 21), + (1 << 20), (1 << 19), (1 << 18), (1 << 17), + (1 << 16), (1 << 15), (1 << 14), (1 << 13), + (1 << 12), (1 << 11), (1 << 10), (1 << 9), + (1 << 8), (1 << 7), (1 << 6), (1 << 5), + (1 << 4), (1 << 3), (1 << 2), (1 << 1), +}; + +// The big problem is likely instructions that set the quantizers in the same block. +// We will have to break block after quantizers are written to. +void psq_st(UGeckoInstruction inst) +{ +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStorePairedOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + js.block_flags |= BLOCK_USE_GQR0 << inst.I; + + if (js.blockSetsQuantizers || !Core::GetStartupParameter().bOptimizeQuantizers) + { + Default(inst); + return; + } + if (!inst.RA) + { + // This really should never happen. Unless we change this to also support stwux + Default(inst); + return; + } + + const UGQR gqr(rSPR(SPR_GQR0 + inst.I)); + const EQuantizeType stType = static_cast(gqr.ST_TYPE); + int stScale = gqr.ST_SCALE; + bool update = inst.OPCD == 61; + + int offset = inst.SIMM_12; + int a = inst.RA; + int s = inst.RS; // Fp numbers + + if (inst.W) { + // PanicAlert("W=1: stType %i stScale %i update %i", (int)stType, (int)stScale, (int)update); + // It's fairly common that games write stuff to the pipe using this. Then, it's pretty much only + // floats so that's what we'll work on. + switch (stType) + { + case QUANTIZE_FLOAT: + { + // This one has quite a bit of optimization potential. + if (gpr.R(a).IsImm()) + { + PanicAlert("Imm: %08x", gpr.R(a).offset); + } + gpr.FlushLockX(ABI_PARAM1, ABI_PARAM2); + gpr.Lock(a); + fpr.Lock(s); + if (update) + gpr.LoadToX64(a, true, true); + MOV(32, R(ABI_PARAM2), gpr.R(a)); + if (offset) + ADD(32, R(ABI_PARAM2), Imm32((u32)offset)); + TEST(32, R(ABI_PARAM2), Imm32(0x0C000000)); + if (update && offset) + MOV(32, gpr.R(a), R(ABI_PARAM2)); + CVTSD2SS(XMM0, fpr.R(s)); + MOVD_xmm(M(&temp64), XMM0); + MOV(32, R(ABI_PARAM1), M(&temp64)); + FixupBranch argh = J_CC(CC_NZ); + BSWAP(32, ABI_PARAM1); +#ifdef _M_X64 + MOV(32, MComplex(RBX, ABI_PARAM2, SCALE_1, 0), R(ABI_PARAM1)); +#else + MOV(32, R(EAX), R(ABI_PARAM2)); + AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); + MOV(32, MDisp(EAX, (u32)Memory::base), R(ABI_PARAM1)); +#endif + FixupBranch skip_call = J(); + SetJumpTarget(argh); + ABI_CallFunctionRR(ProtectFunction((void *)&Memory::Write_U32, 2), ABI_PARAM1, ABI_PARAM2); + SetJumpTarget(skip_call); + gpr.UnlockAll(); + gpr.UnlockAllX(); + fpr.UnlockAll(); + return; + } + default: + Default(inst); + return; + } + return; + } + + if (stType == QUANTIZE_FLOAT) + { + if (gpr.R(a).IsImm() && !update && cpu_info.bSSSE3) + { + u32 addr = gpr.R(a).offset + offset; + if (addr == 0xCC008000) { + // Writing to FIFO. Let's do fast method. + CVTPD2PS(XMM0, fpr.R(s)); + PSHUFB(XMM0, M((void*)&pbswapShuffle2x4)); + CALL((void*)Asm::fifoDirectWriteXmm64); + js.fifoBytesThisBlock += 8; + return; + } + } + + gpr.FlushLockX(ABI_PARAM1, ABI_PARAM2); + gpr.Lock(a); + fpr.Lock(s); + if (update) + gpr.LoadToX64(a, true, true); + MOV(32, R(ABI_PARAM2), gpr.R(a)); + if (offset) + ADD(32, R(ABI_PARAM2), Imm32((u32)offset)); + TEST(32, R(ABI_PARAM2), Imm32(0x0C000000)); + if (update && offset) + MOV(32, gpr.R(a), R(ABI_PARAM2)); + CVTPD2PS(XMM0, fpr.R(s)); + SHUFPS(XMM0, R(XMM0), 1); + MOVQ_xmm(M(&temp64), XMM0); +#ifdef _M_X64 + MOV(64, R(ABI_PARAM1), M(&temp64)); + FixupBranch argh = J_CC(CC_NZ); + BSWAP(64, ABI_PARAM1); + MOV(64, MComplex(RBX, ABI_PARAM2, SCALE_1, 0), R(ABI_PARAM1)); + FixupBranch arg2 = J(); + SetJumpTarget(argh); + CALL(ProtectFunction((void *)&WriteDual32, 0)); +#else + FixupBranch argh = J_CC(CC_NZ); + MOV(32, R(ABI_PARAM1), M(((char*)&temp64) + 4)); + BSWAP(32, ABI_PARAM1); + AND(32, R(ABI_PARAM2), Imm32(Memory::MEMVIEW32_MASK)); + MOV(32, MDisp(ABI_PARAM2, (u32)Memory::base), R(ABI_PARAM1)); + MOV(32, R(ABI_PARAM1), M(&temp64)); + BSWAP(32, ABI_PARAM1); + MOV(32, MDisp(ABI_PARAM2, 4+(u32)Memory::base), R(ABI_PARAM1)); + FixupBranch arg2 = J(); + SetJumpTarget(argh); + MOV(32, R(ABI_PARAM1), M(((char*)&temp64) + 4)); + ABI_CallFunctionRR(ProtectFunction((void *)&Memory::Write_U32, 2), ABI_PARAM1, ABI_PARAM2); + MOV(32, R(ABI_PARAM1), M(((char*)&temp64))); + ADD(32, R(ABI_PARAM2), Imm32(4)); + ABI_CallFunctionRR(ProtectFunction((void *)&Memory::Write_U32, 2), ABI_PARAM1, ABI_PARAM2); +#endif + SetJumpTarget(arg2); + gpr.UnlockAll(); + gpr.UnlockAllX(); + fpr.UnlockAll(); + } + else if (stType == QUANTIZE_U8) + { + gpr.FlushLockX(ABI_PARAM1, ABI_PARAM2); + gpr.Lock(a); + fpr.Lock(s); + if (update) + gpr.LoadToX64(a, true, update); + MOV(32, R(ABI_PARAM2), gpr.R(a)); + if (offset) + ADD(32, R(ABI_PARAM2), Imm32((u32)offset)); + if (update && offset) + MOV(32, gpr.R(a), R(ABI_PARAM2)); + MOVAPD(XMM0, fpr.R(s)); + MOVDDUP(XMM1, M((void*)&m_quantizeTableD[stScale])); + MULPD(XMM0, R(XMM1)); + CVTPD2DQ(XMM0, R(XMM0)); + PACKSSDW(XMM0, R(XMM0)); + PACKUSWB(XMM0, R(XMM0)); + MOVD_xmm(M(&temp64), XMM0); + MOV(16, R(ABI_PARAM1), M(&temp64)); +#ifdef _M_X64 + MOV(16, MComplex(RBX, ABI_PARAM2, SCALE_1, 0), R(ABI_PARAM1)); +#else + MOV(32, R(EAX), R(ABI_PARAM2)); + AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); + MOV(16, MDisp(EAX, (u32)Memory::base), R(ABI_PARAM1)); +#endif + if (update) + MOV(32, gpr.R(a), R(ABI_PARAM2)); + gpr.UnlockAll(); + gpr.UnlockAllX(); + fpr.UnlockAll(); + } + else if (stType == QUANTIZE_S16) + { + gpr.FlushLockX(ABI_PARAM1, ABI_PARAM2); + gpr.Lock(a); + fpr.Lock(s); + if (update) + gpr.LoadToX64(a, true, update); + MOV(32, R(ABI_PARAM2), gpr.R(a)); + if (offset) + ADD(32, R(ABI_PARAM2), Imm32((u32)offset)); + if (update) + MOV(32, gpr.R(a), R(ABI_PARAM2)); + MOVAPD(XMM0, fpr.R(s)); + MOVDDUP(XMM1, M((void*)&m_quantizeTableD[stScale])); + MULPD(XMM0, R(XMM1)); + SHUFPD(XMM0, R(XMM0), 1); + CVTPD2DQ(XMM0, R(XMM0)); + PACKSSDW(XMM0, R(XMM0)); + MOVD_xmm(M(&temp64), XMM0); + MOV(32, R(ABI_PARAM1), M(&temp64)); + BSWAP(32, ABI_PARAM1); +#ifdef _M_X64 + MOV(32, MComplex(RBX, ABI_PARAM2, SCALE_1, 0), R(ABI_PARAM1)); +#else + MOV(32, R(EAX), R(ABI_PARAM2)); + AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); + MOV(32, MDisp(EAX, (u32)Memory::base), R(ABI_PARAM1)); +#endif + gpr.UnlockAll(); + gpr.UnlockAllX(); + fpr.UnlockAll(); + } + else { + // Dodger uses this. + // mario tennis + //PanicAlert("st %i:%i", stType, inst.W); + Default(inst); + } +} + +void psq_l(UGeckoInstruction inst) +{ +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITLoadStorePairedOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + js.block_flags |= BLOCK_USE_GQR0 << inst.I; + + if (js.blockSetsQuantizers || !Core::GetStartupParameter().bOptimizeQuantizers) + { + Default(inst); + return; + } + + const UGQR gqr(rSPR(SPR_GQR0 + inst.I)); + const EQuantizeType ldType = static_cast(gqr.LD_TYPE); + int ldScale = gqr.LD_SCALE; + bool update = inst.OPCD == 57; + if (!inst.RA || inst.W) + { + // 0 1 during load + //PanicAlert("ld:%i %i", ldType, (int)inst.W); + Default(inst); + return; + } + int offset = inst.SIMM_12; + switch (ldType) { + case QUANTIZE_FLOAT: // We know this is from RAM, so we don't need to check the address. + { +#ifdef _M_X64 + gpr.LoadToX64(inst.RA, true, update); + fpr.LoadToX64(inst.RS, false); + if (cpu_info.bSSSE3) { + X64Reg xd = fpr.R(inst.RS).GetSimpleReg(); + MOVQ_xmm(xd, MComplex(RBX, gpr.R(inst.RA).GetSimpleReg(), 1, offset)); + PSHUFB(xd, M((void *)pbswapShuffle2x4)); + CVTPS2PD(xd, R(xd)); + } else { + MOV(64, R(RAX), MComplex(RBX, gpr.R(inst.RA).GetSimpleReg(), 1, offset)); + BSWAP(64, RAX); + MOV(64, M(&psTemp[0]), R(RAX)); + X64Reg r = fpr.R(inst.RS).GetSimpleReg(); + CVTPS2PD(r, M(&psTemp[0])); + SHUFPD(r, R(r), 1); + } + if (update && offset != 0) + ADD(32, gpr.R(inst.RA), Imm32(offset)); + break; +#else + if (cpu_info.bSSSE3) { + gpr.LoadToX64(inst.RA, true, update); + fpr.LoadToX64(inst.RS, false); + X64Reg xd = fpr.R(inst.RS).GetSimpleReg(); + MOV(32, R(EAX), gpr.R(inst.RA)); + AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); + MOVQ_xmm(xd, MDisp(EAX, (u32)Memory::base + offset)); + PSHUFB(xd, M((void *)pbswapShuffle2x4)); + CVTPS2PD(xd, R(xd)); + } else { + gpr.FlushLockX(ECX); + gpr.LoadToX64(inst.RA, true, update); + // This can probably be optimized somewhat. + LEA(32, ECX, MDisp(gpr.R(inst.RA).GetSimpleReg(), offset)); + AND(32, R(ECX), Imm32(Memory::MEMVIEW32_MASK)); + MOV(32, R(EAX), MDisp(ECX, (u32)Memory::base)); + BSWAP(32, RAX); + MOV(32, M(&psTemp[0]), R(RAX)); + MOV(32, R(EAX), MDisp(ECX, (u32)Memory::base + 4)); + BSWAP(32, RAX); + MOV(32, M(((float *)&psTemp[0]) + 1), R(RAX)); + fpr.LoadToX64(inst.RS, false, true); + X64Reg r = fpr.R(inst.RS).GetSimpleReg(); + CVTPS2PD(r, M(&psTemp[0])); + gpr.UnlockAllX(); + } + if (update && offset != 0) + ADD(32, gpr.R(inst.RA), Imm32(offset)); + break; +#endif + } + case QUANTIZE_U8: + { + gpr.LoadToX64(inst.RA, true, update); +#ifdef _M_X64 + MOVZX(32, 16, EAX, MComplex(RBX, gpr.R(inst.RA).GetSimpleReg(), 1, offset)); +#else + LEA(32, EAX, MDisp(gpr.R(inst.RA).GetSimpleReg(), offset)); + AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); + MOVZX(32, 16, EAX, MDisp(EAX, (u32)Memory::base)); +#endif + MOV(32, M(&temp64), R(EAX)); + MOVD_xmm(XMM0, M(&temp64)); + // SSE4 optimization opportunity here. + PXOR(XMM1, R(XMM1)); + PUNPCKLBW(XMM0, R(XMM1)); + PUNPCKLWD(XMM0, R(XMM1)); + CVTDQ2PD(XMM0, R(XMM0)); + fpr.LoadToX64(inst.RS, false, true); + X64Reg r = fpr.R(inst.RS).GetSimpleReg(); + MOVDDUP(r, M((void *)&m_dequantizeTableD[ldScale])); + MULPD(r, R(XMM0)); + if (update && offset != 0) + ADD(32, gpr.R(inst.RA), Imm32(offset)); + } + break; + case QUANTIZE_S16: + { + gpr.LoadToX64(inst.RA, true, update); +#ifdef _M_X64 + MOV(32, R(EAX), MComplex(RBX, gpr.R(inst.RA).GetSimpleReg(), 1, offset)); +#else + LEA(32, EAX, MDisp(gpr.R(inst.RA).GetSimpleReg(), offset)); + AND(32, R(EAX), Imm32(Memory::MEMVIEW32_MASK)); + MOV(32, R(EAX), MDisp(EAX, (u32)Memory::base)); +#endif + BSWAP(32, EAX); + MOV(32, M(&temp64), R(EAX)); + //INT3(); + fpr.LoadToX64(inst.RS, false, true); + X64Reg r = fpr.R(inst.RS).GetSimpleReg(); + MOVD_xmm(XMM0, M(&temp64)); + PUNPCKLWD(XMM0, R(XMM0)); // unpack to higher word in each dword.. + PSRAD(XMM0, 16); // then use this signed shift to sign extend. clever eh? :P + CVTDQ2PD(XMM0, R(XMM0)); + MOVDDUP(r, M((void*)&m_dequantizeTableD[ldScale])); + MULPD(r, R(XMM0)); + SHUFPD(r, R(r), 1); + if (update && offset != 0) + ADD(32, gpr.R(inst.RA), Imm32(offset)); + } + break; + + /* + Dynamic quantizer. Todo when we have a test set. + MOVZX(32, 8, EAX, M(((char *)&PowerPC::ppcState.spr[SPR_GQR0 + inst.I]) + 3)); // it's in the high byte. + AND(32, R(EAX), Imm8(0x3F)); + MOV(32, R(ECX), Imm32((u32)&m_dequantizeTableD)); + MOVDDUP(r, MComplex(RCX, EAX, 8, 0)); + */ + default: + // 4 0 + // 6 0 //power tennis + // 5 0 + // PanicAlert("ld:%i %i", ldType, (int)inst.W); + Default(inst); + return; + } + + //u32 EA = (m_GPR[_inst.RA] + _inst.SIMM_12) : _inst.SIMM_12; +} + +} // namespace diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit_Paired.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit_Paired.cpp index 7fdea7fb7a..0f3f1c9506 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit_Paired.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit_Paired.cpp @@ -1,451 +1,451 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" - -#include "../../Core.h" -#include "../PowerPC.h" -#include "../PPCTables.h" -#include "x64Emitter.h" -#include "../../HW/GPFifo.h" - -#include "Jit.h" -#include "JitCache.h" -#include "JitRegCache.h" -#include "Jit_Util.h" - -// TODO -// ps_madds0 -// ps_muls0 -// ps_madds1 -// ps_sel -// cmppd, andpd, andnpd, or -// lfsx, ps_merge01 etc - -// #define INSTRUCTION_START Default(inst); return; -#define INSTRUCTION_START - -#ifdef _M_IX86 -#define DISABLE_32BIT Default(inst); return; -#else -#define DISABLE_32BIT ; -#endif - -namespace Jit64 -{ - const u64 GC_ALIGNED16(psSignBits[2]) = {0x8000000000000000ULL, 0x8000000000000000ULL}; - const u64 GC_ALIGNED16(psAbsMask[2]) = {0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL}; - const double GC_ALIGNED16(psOneOne[2]) = {1.0, 1.0}; - const double GC_ALIGNED16(psZeroZero[2]) = {0.0, 0.0}; - - void ps_mr(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITPairedOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - if (inst.Rc) { - Default(inst); return; - } - int d = inst.FD; - int b = inst.FB; - if (d == b) - return; - fpr.LoadToX64(d, false); - MOVAPD(fpr.RX(d), fpr.R(b)); - } - - void ps_sel(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITPairedOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - Default(inst); - return; - - if (inst.Rc) { - Default(inst); return; - } - // GRR can't get this to work 100%. Getting artifacts in D.O.N. intro. - int d = inst.FD; - int a = inst.FA; - int b = inst.FB; - int c = inst.FC; - fpr.FlushLockX(XMM7); - fpr.FlushLockX(XMM6); - fpr.Lock(a, b, c, d); - fpr.LoadToX64(a, true, false); - fpr.LoadToX64(d, false, true); - // BLENDPD would have been nice... - MOVAPD(XMM7, fpr.R(a)); - CMPPD(XMM7, M((void*)psZeroZero), 1); //less-than = 111111 - MOVAPD(XMM6, R(XMM7)); - ANDPD(XMM7, fpr.R(d)); - ANDNPD(XMM6, fpr.R(c)); - MOVAPD(fpr.RX(d), R(XMM7)); - ORPD(fpr.RX(d), R(XMM6)); - fpr.UnlockAll(); - fpr.UnlockAllX(); - } - - void ps_sign(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITPairedOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - if (inst.Rc) { - Default(inst); return; - } - int d = inst.FD; - int b = inst.FB; - - fpr.Lock(d, b); - if (d != b) - { - fpr.LoadToX64(d, false); - MOVAPD(fpr.RX(d), fpr.R(b)); - } - else - { - fpr.LoadToX64(d, true); - } - - switch (inst.SUBOP10) - { - case 40: //neg - XORPD(fpr.RX(d), M((void*)&psSignBits)); - break; - case 136: //nabs - ORPD(fpr.RX(d), M((void*)&psSignBits)); - break; - case 264: //abs - ANDPD(fpr.RX(d), M((void*)&psAbsMask)); - break; - } - - fpr.UnlockAll(); - } - - void ps_rsqrte(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITPairedOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - if (inst.Rc) { - Default(inst); return; - } - int d = inst.FD; - int b = inst.FB; - fpr.Lock(d, b); - SQRTPD(XMM0, fpr.R(b)); - MOVAPD(XMM1, M((void*)&psOneOne)); - DIVPD(XMM1, R(XMM0)); - MOVAPD(fpr.R(d), XMM1); - fpr.UnlockAll(); - } - - //add a, b, c - - //mov a, b - //add a, c - //we need: - /* - psq_l - psq_stu - */ - - /* - add a,b,a - */ - - //There's still a little bit more optimization that can be squeezed out of this - void tri_op(int d, int a, int b, bool reversible, void (*op)(X64Reg, OpArg)) - { - fpr.Lock(d, a, b); - - if (d == a) - { - fpr.LoadToX64(d, true); - op(fpr.RX(d), fpr.R(b)); - } - else if (d == b && reversible) - { - fpr.LoadToX64(d, true); - op(fpr.RX(d), fpr.R(a)); - } - else if (a != d && b != d) - { - //sources different from d, can use rather quick solution - fpr.LoadToX64(d, false); - MOVAPD(fpr.RX(d), fpr.R(a)); - op(fpr.RX(d), fpr.R(b)); - } - else if (b != d) - { - fpr.LoadToX64(d, false); - MOVAPD(XMM0, fpr.R(b)); - MOVAPD(fpr.RX(d), fpr.R(a)); - op(fpr.RX(d), Gen::R(XMM0)); - } - else //Other combo, must use two temps :( - { - MOVAPD(XMM0, fpr.R(a)); - MOVAPD(XMM1, fpr.R(b)); - fpr.LoadToX64(d, false); - op(XMM0, Gen::R(XMM1)); - MOVAPD(fpr.RX(d), Gen::R(XMM0)); - } - ForceSinglePrecisionP(fpr.RX(d)); - fpr.UnlockAll(); - } - - void ps_arith(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITPairedOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - if (inst.Rc) { - Default(inst); return; - } - switch (inst.SUBOP5) - { - case 18: tri_op(inst.FD, inst.FA, inst.FB, false, &DIVPD); break; //div - case 20: tri_op(inst.FD, inst.FA, inst.FB, false, &SUBPD); break; //sub - case 21: tri_op(inst.FD, inst.FA, inst.FB, true, &ADDPD); break; //add - case 23://sel - Default(inst); - break; - case 24://res - Default(inst); - break; - case 25: tri_op(inst.FD, inst.FA, inst.FC, true, &MULPD); break; //mul - default: - _assert_msg_(DYNA_REC, 0, "ps_arith WTF!!!"); - } - } - - void ps_sum(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITPairedOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - if (inst.Rc) { - Default(inst); return; - } - int d = inst.FD; - int a = inst.FA; - int b = inst.FB; - int c = inst.FC; - fpr.Lock(a,b,c,d); - fpr.LoadToX64(d, d == a || d == b || d == c, true); - switch (inst.SUBOP5) - { - case 10: - // Do the sum in upper subregisters, merge uppers - MOVDDUP(XMM0, fpr.R(a)); - MOVAPD(XMM1, fpr.R(b)); - ADDPD(XMM0, R(XMM1)); - UNPCKHPD(XMM0, fpr.R(c)); //merge - MOVAPD(fpr.R(d), XMM0); - break; - case 11: - // Do the sum in lower subregisters, merge lowers - MOVAPD(XMM0, fpr.R(a)); - MOVAPD(XMM1, fpr.R(b)); - SHUFPD(XMM1, R(XMM1), 5); // copy higher to lower - ADDPD(XMM0, R(XMM1)); // sum lowers - MOVAPD(XMM1, fpr.R(c)); - UNPCKLPD(XMM1, R(XMM0)); // merge - MOVAPD(fpr.R(d), XMM1); - break; - default: - PanicAlert("ps_sum WTF!!!"); - } - ForceSinglePrecisionP(fpr.RX(d)); - fpr.UnlockAll(); - } - - - void ps_muls(UGeckoInstruction inst) - { - Default(inst); return; -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITPairedOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - if (inst.Rc) { - Default(inst); return; - } - int d = inst.FD; - int a = inst.FA; - int c = inst.FC; - fpr.Lock(a, c, d); - fpr.LoadToX64(d, d == a || d == c, true); - switch (inst.SUBOP5) - { - case 12: - // Single multiply scalar high - // TODO - faster version for when regs are different - MOVAPD(XMM0, fpr.R(c)); - MOVDDUP(XMM1, fpr.R(a)); - MULPS(XMM0, R(XMM1)); - MOVAPD(fpr.R(d), XMM0); - break; - case 13: - // TODO - faster version for when regs are different - MOVAPD(XMM0, fpr.R(c)); - MOVAPD(XMM1, fpr.R(a)); - SHUFPD(XMM1, R(XMM1), 5); // copy higher to lower - MULPD(XMM0, R(XMM1)); // sum lowers - MOVAPD(fpr.R(d), XMM0); - break; - default: - PanicAlert("ps_muls WTF!!!"); - } - ForceSinglePrecisionP(fpr.RX(d)); - fpr.UnlockAll(); - } - - - //TODO: find easy cases and optimize them, do a breakout like ps_arith - void ps_mergeXX(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITPairedOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - if (inst.Rc) { - Default(inst); return; - } - int d = inst.FD; - int a = inst.FA; - int b = inst.FB; - fpr.Lock(a,b,d); - - MOVAPD(XMM0, fpr.R(a)); - switch (inst.SUBOP10) - { - case 528: - UNPCKLPD(XMM0, fpr.R(b)); //unpck is faster than shuf - break; //00 - case 560: - SHUFPD(XMM0, fpr.R(b), 2); //must use shuf here - break; //01 - case 592: - SHUFPD(XMM0, fpr.R(b), 1); - break; //10 - case 624: - UNPCKHPD(XMM0, fpr.R(b)); - break; //11 - default: - _assert_msg_(DYNA_REC, 0, "ps_merge - invalid op"); - } - fpr.LoadToX64(d, false); - MOVAPD(fpr.RX(d), Gen::R(XMM0)); - fpr.UnlockAll(); - } - - //one op is assumed to be add - void quad_op(int d, int a, int b, int c, void (*op)(X64Reg, OpArg)) - { - - } - - //TODO: add optimized cases - void ps_maddXX(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITPairedOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - if (inst.Rc) { - Default(inst); return; - } - int a = inst.FA; - int b = inst.FB; - int c = inst.FC; - int d = inst.FD; - fpr.Lock(a,b,c,d); - - MOVAPD(XMM0, fpr.R(a)); - switch (inst.SUBOP5) - { - case 28: //msub - MULPD(XMM0, fpr.R(c)); - SUBPD(XMM0, fpr.R(b)); - break; - case 29: //madd - MULPD(XMM0, fpr.R(c)); - ADDPD(XMM0, fpr.R(b)); - break; - case 30: //nmsub - MULPD(XMM0, fpr.R(c)); - SUBPD(XMM0, fpr.R(b)); - XORPD(XMM0, M((void*)&psSignBits)); - break; - case 31: //nmadd - MULPD(XMM0, fpr.R(c)); - ADDPD(XMM0, fpr.R(b)); - XORPD(XMM0, M((void*)&psSignBits)); - break; - default: - _assert_msg_(DYNA_REC, 0, "ps_maddXX WTF!!!"); - //Default(inst); - //fpr.UnlockAll(); - return; - } - fpr.LoadToX64(d, false); - MOVAPD(fpr.RX(d), Gen::R(XMM0)); - ForceSinglePrecisionP(fpr.RX(d)); - fpr.UnlockAll(); - } - - void ps_mulsX(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITPairedOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - Default(inst); - return; - if (inst.Rc) { - Default(inst); return; - } - - switch (inst.SUBOP5) - { - case 12: - case 13: - break; - } - } -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" + +#include "../../Core.h" +#include "../PowerPC.h" +#include "../PPCTables.h" +#include "x64Emitter.h" +#include "../../HW/GPFifo.h" + +#include "Jit.h" +#include "JitCache.h" +#include "JitRegCache.h" +#include "Jit_Util.h" + +// TODO +// ps_madds0 +// ps_muls0 +// ps_madds1 +// ps_sel +// cmppd, andpd, andnpd, or +// lfsx, ps_merge01 etc + +// #define INSTRUCTION_START Default(inst); return; +#define INSTRUCTION_START + +#ifdef _M_IX86 +#define DISABLE_32BIT Default(inst); return; +#else +#define DISABLE_32BIT ; +#endif + +namespace Jit64 +{ + const u64 GC_ALIGNED16(psSignBits[2]) = {0x8000000000000000ULL, 0x8000000000000000ULL}; + const u64 GC_ALIGNED16(psAbsMask[2]) = {0x7FFFFFFFFFFFFFFFULL, 0x7FFFFFFFFFFFFFFFULL}; + const double GC_ALIGNED16(psOneOne[2]) = {1.0, 1.0}; + const double GC_ALIGNED16(psZeroZero[2]) = {0.0, 0.0}; + + void ps_mr(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITPairedOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + if (inst.Rc) { + Default(inst); return; + } + int d = inst.FD; + int b = inst.FB; + if (d == b) + return; + fpr.LoadToX64(d, false); + MOVAPD(fpr.RX(d), fpr.R(b)); + } + + void ps_sel(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITPairedOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + Default(inst); + return; + + if (inst.Rc) { + Default(inst); return; + } + // GRR can't get this to work 100%. Getting artifacts in D.O.N. intro. + int d = inst.FD; + int a = inst.FA; + int b = inst.FB; + int c = inst.FC; + fpr.FlushLockX(XMM7); + fpr.FlushLockX(XMM6); + fpr.Lock(a, b, c, d); + fpr.LoadToX64(a, true, false); + fpr.LoadToX64(d, false, true); + // BLENDPD would have been nice... + MOVAPD(XMM7, fpr.R(a)); + CMPPD(XMM7, M((void*)psZeroZero), 1); //less-than = 111111 + MOVAPD(XMM6, R(XMM7)); + ANDPD(XMM7, fpr.R(d)); + ANDNPD(XMM6, fpr.R(c)); + MOVAPD(fpr.RX(d), R(XMM7)); + ORPD(fpr.RX(d), R(XMM6)); + fpr.UnlockAll(); + fpr.UnlockAllX(); + } + + void ps_sign(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITPairedOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + if (inst.Rc) { + Default(inst); return; + } + int d = inst.FD; + int b = inst.FB; + + fpr.Lock(d, b); + if (d != b) + { + fpr.LoadToX64(d, false); + MOVAPD(fpr.RX(d), fpr.R(b)); + } + else + { + fpr.LoadToX64(d, true); + } + + switch (inst.SUBOP10) + { + case 40: //neg + XORPD(fpr.RX(d), M((void*)&psSignBits)); + break; + case 136: //nabs + ORPD(fpr.RX(d), M((void*)&psSignBits)); + break; + case 264: //abs + ANDPD(fpr.RX(d), M((void*)&psAbsMask)); + break; + } + + fpr.UnlockAll(); + } + + void ps_rsqrte(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITPairedOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + if (inst.Rc) { + Default(inst); return; + } + int d = inst.FD; + int b = inst.FB; + fpr.Lock(d, b); + SQRTPD(XMM0, fpr.R(b)); + MOVAPD(XMM1, M((void*)&psOneOne)); + DIVPD(XMM1, R(XMM0)); + MOVAPD(fpr.R(d), XMM1); + fpr.UnlockAll(); + } + + //add a, b, c + + //mov a, b + //add a, c + //we need: + /* + psq_l + psq_stu + */ + + /* + add a,b,a + */ + + //There's still a little bit more optimization that can be squeezed out of this + void tri_op(int d, int a, int b, bool reversible, void (*op)(X64Reg, OpArg)) + { + fpr.Lock(d, a, b); + + if (d == a) + { + fpr.LoadToX64(d, true); + op(fpr.RX(d), fpr.R(b)); + } + else if (d == b && reversible) + { + fpr.LoadToX64(d, true); + op(fpr.RX(d), fpr.R(a)); + } + else if (a != d && b != d) + { + //sources different from d, can use rather quick solution + fpr.LoadToX64(d, false); + MOVAPD(fpr.RX(d), fpr.R(a)); + op(fpr.RX(d), fpr.R(b)); + } + else if (b != d) + { + fpr.LoadToX64(d, false); + MOVAPD(XMM0, fpr.R(b)); + MOVAPD(fpr.RX(d), fpr.R(a)); + op(fpr.RX(d), Gen::R(XMM0)); + } + else //Other combo, must use two temps :( + { + MOVAPD(XMM0, fpr.R(a)); + MOVAPD(XMM1, fpr.R(b)); + fpr.LoadToX64(d, false); + op(XMM0, Gen::R(XMM1)); + MOVAPD(fpr.RX(d), Gen::R(XMM0)); + } + ForceSinglePrecisionP(fpr.RX(d)); + fpr.UnlockAll(); + } + + void ps_arith(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITPairedOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + if (inst.Rc) { + Default(inst); return; + } + switch (inst.SUBOP5) + { + case 18: tri_op(inst.FD, inst.FA, inst.FB, false, &DIVPD); break; //div + case 20: tri_op(inst.FD, inst.FA, inst.FB, false, &SUBPD); break; //sub + case 21: tri_op(inst.FD, inst.FA, inst.FB, true, &ADDPD); break; //add + case 23://sel + Default(inst); + break; + case 24://res + Default(inst); + break; + case 25: tri_op(inst.FD, inst.FA, inst.FC, true, &MULPD); break; //mul + default: + _assert_msg_(DYNA_REC, 0, "ps_arith WTF!!!"); + } + } + + void ps_sum(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITPairedOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + if (inst.Rc) { + Default(inst); return; + } + int d = inst.FD; + int a = inst.FA; + int b = inst.FB; + int c = inst.FC; + fpr.Lock(a,b,c,d); + fpr.LoadToX64(d, d == a || d == b || d == c, true); + switch (inst.SUBOP5) + { + case 10: + // Do the sum in upper subregisters, merge uppers + MOVDDUP(XMM0, fpr.R(a)); + MOVAPD(XMM1, fpr.R(b)); + ADDPD(XMM0, R(XMM1)); + UNPCKHPD(XMM0, fpr.R(c)); //merge + MOVAPD(fpr.R(d), XMM0); + break; + case 11: + // Do the sum in lower subregisters, merge lowers + MOVAPD(XMM0, fpr.R(a)); + MOVAPD(XMM1, fpr.R(b)); + SHUFPD(XMM1, R(XMM1), 5); // copy higher to lower + ADDPD(XMM0, R(XMM1)); // sum lowers + MOVAPD(XMM1, fpr.R(c)); + UNPCKLPD(XMM1, R(XMM0)); // merge + MOVAPD(fpr.R(d), XMM1); + break; + default: + PanicAlert("ps_sum WTF!!!"); + } + ForceSinglePrecisionP(fpr.RX(d)); + fpr.UnlockAll(); + } + + + void ps_muls(UGeckoInstruction inst) + { + Default(inst); return; +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITPairedOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + if (inst.Rc) { + Default(inst); return; + } + int d = inst.FD; + int a = inst.FA; + int c = inst.FC; + fpr.Lock(a, c, d); + fpr.LoadToX64(d, d == a || d == c, true); + switch (inst.SUBOP5) + { + case 12: + // Single multiply scalar high + // TODO - faster version for when regs are different + MOVAPD(XMM0, fpr.R(c)); + MOVDDUP(XMM1, fpr.R(a)); + MULPS(XMM0, R(XMM1)); + MOVAPD(fpr.R(d), XMM0); + break; + case 13: + // TODO - faster version for when regs are different + MOVAPD(XMM0, fpr.R(c)); + MOVAPD(XMM1, fpr.R(a)); + SHUFPD(XMM1, R(XMM1), 5); // copy higher to lower + MULPD(XMM0, R(XMM1)); // sum lowers + MOVAPD(fpr.R(d), XMM0); + break; + default: + PanicAlert("ps_muls WTF!!!"); + } + ForceSinglePrecisionP(fpr.RX(d)); + fpr.UnlockAll(); + } + + + //TODO: find easy cases and optimize them, do a breakout like ps_arith + void ps_mergeXX(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITPairedOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + if (inst.Rc) { + Default(inst); return; + } + int d = inst.FD; + int a = inst.FA; + int b = inst.FB; + fpr.Lock(a,b,d); + + MOVAPD(XMM0, fpr.R(a)); + switch (inst.SUBOP10) + { + case 528: + UNPCKLPD(XMM0, fpr.R(b)); //unpck is faster than shuf + break; //00 + case 560: + SHUFPD(XMM0, fpr.R(b), 2); //must use shuf here + break; //01 + case 592: + SHUFPD(XMM0, fpr.R(b), 1); + break; //10 + case 624: + UNPCKHPD(XMM0, fpr.R(b)); + break; //11 + default: + _assert_msg_(DYNA_REC, 0, "ps_merge - invalid op"); + } + fpr.LoadToX64(d, false); + MOVAPD(fpr.RX(d), Gen::R(XMM0)); + fpr.UnlockAll(); + } + + //one op is assumed to be add + void quad_op(int d, int a, int b, int c, void (*op)(X64Reg, OpArg)) + { + + } + + //TODO: add optimized cases + void ps_maddXX(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITPairedOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + if (inst.Rc) { + Default(inst); return; + } + int a = inst.FA; + int b = inst.FB; + int c = inst.FC; + int d = inst.FD; + fpr.Lock(a,b,c,d); + + MOVAPD(XMM0, fpr.R(a)); + switch (inst.SUBOP5) + { + case 28: //msub + MULPD(XMM0, fpr.R(c)); + SUBPD(XMM0, fpr.R(b)); + break; + case 29: //madd + MULPD(XMM0, fpr.R(c)); + ADDPD(XMM0, fpr.R(b)); + break; + case 30: //nmsub + MULPD(XMM0, fpr.R(c)); + SUBPD(XMM0, fpr.R(b)); + XORPD(XMM0, M((void*)&psSignBits)); + break; + case 31: //nmadd + MULPD(XMM0, fpr.R(c)); + ADDPD(XMM0, fpr.R(b)); + XORPD(XMM0, M((void*)&psSignBits)); + break; + default: + _assert_msg_(DYNA_REC, 0, "ps_maddXX WTF!!!"); + //Default(inst); + //fpr.UnlockAll(); + return; + } + fpr.LoadToX64(d, false); + MOVAPD(fpr.RX(d), Gen::R(XMM0)); + ForceSinglePrecisionP(fpr.RX(d)); + fpr.UnlockAll(); + } + + void ps_mulsX(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITPairedOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + Default(inst); + return; + if (inst.Rc) { + Default(inst); return; + } + + switch (inst.SUBOP5) + { + case 12: + case 13: + break; + } + } +} diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp index 8e0f2cfa2d..4a1cf30ab9 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit_SystemRegisters.cpp @@ -1,194 +1,194 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" - -#include "../../Core.h" -#include "../../CoreTiming.h" -#include "../../HW/SystemTimers.h" -#include "../PowerPC.h" -#include "../PPCTables.h" -#include "x64Emitter.h" -#include "ABI.h" -#include "Thunk.h" - -#include "Jit.h" -#include "JitCache.h" -#include "JitRegCache.h" - -#define INSTRUCTION_START -// #define INSTRUCTION_START Default(inst); return; - -namespace Jit64 -{ - void mtspr(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITSystemRegistersOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - u32 iIndex = (inst.SPRU << 5) | (inst.SPRL & 0x1F); - int d = inst.RD; - - switch (iIndex) - { - case SPR_LR: - case SPR_CTR: - case SPR_XER: - // These are safe to do the easy way, see the bottom of this function. - break; - - case SPR_GQR0: - case SPR_GQR0 + 1: - case SPR_GQR0 + 2: - case SPR_GQR0 + 3: - case SPR_GQR0 + 4: - case SPR_GQR0 + 5: - case SPR_GQR0 + 6: - case SPR_GQR0 + 7: - js.blockSetsQuantizers = true; - // Prevent recompiler from compiling in old quantizer values. - // If the value changed, destroy all blocks using this quantizer - // This will create a little bit of block churn, but hopefully not too bad. - { - /* - MOV(32, R(EAX), M(&PowerPC::ppcState.spr[iIndex])); // Load old value - CMP(32, R(EAX), gpr.R(inst.RD)); - FixupBranch skip_destroy = J_CC(CC_E, false); - int gqr = iIndex - SPR_GQR0; - ABI_CallFunctionC(ProtectFunction(&Jit64::DestroyBlocksWithFlag, 1), (u32)BLOCK_USE_GQR0 << gqr); - SetJumpTarget(skip_destroy);*/ - } - break; - // TODO - break block if quantizers are written to. - default: - Default(inst); - return; - } - - // OK, this is easy. - gpr.Lock(d); - gpr.LoadToX64(d, true); - MOV(32, M(&PowerPC::ppcState.spr[iIndex]), gpr.R(d)); - gpr.UnlockAll(); - } - - void mfspr(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITSystemRegistersOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - u32 iIndex = (inst.SPRU << 5) | (inst.SPRL & 0x1F); - int d = inst.RD; - switch (iIndex) - { - case SPR_WPAR: - Default(inst); - return; -// case SPR_DEC: - //MessageBox(NULL, "Read from DEC", "????", MB_OK); - //break; - case SPR_TL: - case SPR_TU: - //CALL((void *)&CoreTiming::Advance); - // fall through - default: - gpr.Lock(d); - gpr.LoadToX64(d, false); - MOV(32, gpr.R(d), M(&PowerPC::ppcState.spr[iIndex])); - gpr.UnlockAll(); - break; - } - } - - - // ======================================================================================= - // Don't interpret this, if we do we get thrown out - // -------------- - void mtmsr(UGeckoInstruction inst) - { - //if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITSystemRegistersOff) - // {Default(inst); return;} // turn off from debugger - INSTRUCTION_START; - gpr.LoadToX64(inst.RS, true, false); - MOV(32, M(&MSR), gpr.R(inst.RS)); - gpr.Flush(FLUSH_ALL); - fpr.Flush(FLUSH_ALL); - WriteExit(js.compilerPC + 4, 0); - } - // ============== - - - void mfmsr(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITSystemRegistersOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - //Privileged? - gpr.LoadToX64(inst.RD, false); - MOV(32, gpr.R(inst.RD), M(&MSR)); - } - - void mftb(UGeckoInstruction inst) - { -#ifdef JIT_OFF_OPTIONS - if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITSystemRegistersOff) - {Default(inst); return;} // turn off from debugger -#endif - INSTRUCTION_START; - mfspr(inst); - } - - void mfcr(UGeckoInstruction inst) - { - int d = inst.RD; - gpr.LoadToX64(d, false, true); - MOV(32, gpr.R(d), M(&PowerPC::ppcState.cr)); - } - - void mtcrf(UGeckoInstruction inst) - { - u32 mask = 0; - u32 crm = inst.CRM; - gpr.FlushLockX(ECX); - if (crm == 0xFF) { - MOV(32, R(EAX), gpr.R(inst.RS)); - MOV(32, M(&PowerPC::ppcState.cr), R(EAX)); - } else { - //TODO: use lookup table? probably not worth it - for (int i = 0; i < 8; i++) { - if (crm & (1 << i)) - mask |= 0xF << (i*4); - } - MOV(32, R(EAX), gpr.R(inst.RS)); - MOV(32, R(ECX), M(&PowerPC::ppcState.cr)); - AND(32, R(EAX), Imm32(mask)); - AND(32, R(ECX), Imm32(~mask)); - OR(32, R(EAX), R(ECX)); - MOV(32, M(&PowerPC::ppcState.cr), R(EAX)); - } - gpr.UnlockAllX(); - } - - -} - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" + +#include "../../Core.h" +#include "../../CoreTiming.h" +#include "../../HW/SystemTimers.h" +#include "../PowerPC.h" +#include "../PPCTables.h" +#include "x64Emitter.h" +#include "ABI.h" +#include "Thunk.h" + +#include "Jit.h" +#include "JitCache.h" +#include "JitRegCache.h" + +#define INSTRUCTION_START +// #define INSTRUCTION_START Default(inst); return; + +namespace Jit64 +{ + void mtspr(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITSystemRegistersOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + u32 iIndex = (inst.SPRU << 5) | (inst.SPRL & 0x1F); + int d = inst.RD; + + switch (iIndex) + { + case SPR_LR: + case SPR_CTR: + case SPR_XER: + // These are safe to do the easy way, see the bottom of this function. + break; + + case SPR_GQR0: + case SPR_GQR0 + 1: + case SPR_GQR0 + 2: + case SPR_GQR0 + 3: + case SPR_GQR0 + 4: + case SPR_GQR0 + 5: + case SPR_GQR0 + 6: + case SPR_GQR0 + 7: + js.blockSetsQuantizers = true; + // Prevent recompiler from compiling in old quantizer values. + // If the value changed, destroy all blocks using this quantizer + // This will create a little bit of block churn, but hopefully not too bad. + { + /* + MOV(32, R(EAX), M(&PowerPC::ppcState.spr[iIndex])); // Load old value + CMP(32, R(EAX), gpr.R(inst.RD)); + FixupBranch skip_destroy = J_CC(CC_E, false); + int gqr = iIndex - SPR_GQR0; + ABI_CallFunctionC(ProtectFunction(&Jit64::DestroyBlocksWithFlag, 1), (u32)BLOCK_USE_GQR0 << gqr); + SetJumpTarget(skip_destroy);*/ + } + break; + // TODO - break block if quantizers are written to. + default: + Default(inst); + return; + } + + // OK, this is easy. + gpr.Lock(d); + gpr.LoadToX64(d, true); + MOV(32, M(&PowerPC::ppcState.spr[iIndex]), gpr.R(d)); + gpr.UnlockAll(); + } + + void mfspr(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITSystemRegistersOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + u32 iIndex = (inst.SPRU << 5) | (inst.SPRL & 0x1F); + int d = inst.RD; + switch (iIndex) + { + case SPR_WPAR: + Default(inst); + return; +// case SPR_DEC: + //MessageBox(NULL, "Read from DEC", "????", MB_OK); + //break; + case SPR_TL: + case SPR_TU: + //CALL((void *)&CoreTiming::Advance); + // fall through + default: + gpr.Lock(d); + gpr.LoadToX64(d, false); + MOV(32, gpr.R(d), M(&PowerPC::ppcState.spr[iIndex])); + gpr.UnlockAll(); + break; + } + } + + + // ======================================================================================= + // Don't interpret this, if we do we get thrown out + // -------------- + void mtmsr(UGeckoInstruction inst) + { + //if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITSystemRegistersOff) + // {Default(inst); return;} // turn off from debugger + INSTRUCTION_START; + gpr.LoadToX64(inst.RS, true, false); + MOV(32, M(&MSR), gpr.R(inst.RS)); + gpr.Flush(FLUSH_ALL); + fpr.Flush(FLUSH_ALL); + WriteExit(js.compilerPC + 4, 0); + } + // ============== + + + void mfmsr(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITSystemRegistersOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + //Privileged? + gpr.LoadToX64(inst.RD, false); + MOV(32, gpr.R(inst.RD), M(&MSR)); + } + + void mftb(UGeckoInstruction inst) + { +#ifdef JIT_OFF_OPTIONS + if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITSystemRegistersOff) + {Default(inst); return;} // turn off from debugger +#endif + INSTRUCTION_START; + mfspr(inst); + } + + void mfcr(UGeckoInstruction inst) + { + int d = inst.RD; + gpr.LoadToX64(d, false, true); + MOV(32, gpr.R(d), M(&PowerPC::ppcState.cr)); + } + + void mtcrf(UGeckoInstruction inst) + { + u32 mask = 0; + u32 crm = inst.CRM; + gpr.FlushLockX(ECX); + if (crm == 0xFF) { + MOV(32, R(EAX), gpr.R(inst.RS)); + MOV(32, M(&PowerPC::ppcState.cr), R(EAX)); + } else { + //TODO: use lookup table? probably not worth it + for (int i = 0; i < 8; i++) { + if (crm & (1 << i)) + mask |= 0xF << (i*4); + } + MOV(32, R(EAX), gpr.R(inst.RS)); + MOV(32, R(ECX), M(&PowerPC::ppcState.cr)); + AND(32, R(EAX), Imm32(mask)); + AND(32, R(ECX), Imm32(~mask)); + OR(32, R(EAX), R(ECX)); + MOV(32, M(&PowerPC::ppcState.cr), R(EAX)); + } + gpr.UnlockAllX(); + } + + +} + diff --git a/Source/Core/Core/Src/PowerPC/Jit64/Jit_Util.cpp b/Source/Core/Core/Src/PowerPC/Jit64/Jit_Util.cpp index 2e290eb291..699ef09881 100644 --- a/Source/Core/Core/Src/PowerPC/Jit64/Jit_Util.cpp +++ b/Source/Core/Core/Src/PowerPC/Jit64/Jit_Util.cpp @@ -1,145 +1,145 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "Thunk.h" - -#include "../PowerPC.h" -#include "../../Core.h" -#include "../../HW/GPFifo.h" -#include "../../HW/CommandProcessor.h" -#include "../../HW/PixelEngine.h" -#include "../../HW/Memmap.h" -#include "../PPCTables.h" -#include "x64Emitter.h" -#include "ABI.h" - -#include "Jit.h" -#include "JitCache.h" -#include "JitAsm.h" -#include "JitRegCache.h" - - -namespace Jit64 -{ - -void UnsafeLoadRegToReg(X64Reg reg_addr, X64Reg reg_value, int accessSize, s32 offset, bool signExtend) -{ -#ifdef _M_IX86 - AND(32, R(reg_addr), Imm32(Memory::MEMVIEW32_MASK)); - MOVZX(32, accessSize, reg_value, MDisp(reg_addr, (u32)Memory::base + offset)); -#else - MOVZX(32, accessSize, reg_value, MComplex(RBX, reg_addr, SCALE_1, offset)); -#endif - if (accessSize == 32) - { - BSWAP(32, reg_value); - } - else if (accessSize == 16) - { - BSWAP(32, reg_value); - if (signExtend) - SAR(32, R(reg_value), Imm8(16)); - else - SHR(32, R(reg_value), Imm8(16)); - } else if (signExtend) { - // TODO: bake 8-bit into the original load. - MOVSX(32, accessSize, reg_value, R(reg_value)); - } -} - -void SafeLoadRegToEAX(X64Reg reg, int accessSize, s32 offset, bool signExtend) -{ - if (offset) - ADD(32, R(reg), Imm32((u32)offset)); - TEST(32, R(reg), Imm32(0x0C000000)); - FixupBranch argh = J_CC(CC_Z); - switch (accessSize) - { - case 32: ABI_CallFunctionR(ProtectFunction((void *)&Memory::Read_U32, 1), reg); break; - case 16: ABI_CallFunctionR(ProtectFunction((void *)&Memory::Read_U16, 1), reg); break; - case 8: ABI_CallFunctionR(ProtectFunction((void *)&Memory::Read_U8, 1), reg); break; - } - if (signExtend && accessSize < 32) { - // Need to sign extend values coming from the Read_U* functions. - MOVSX(32, accessSize, EAX, R(EAX)); - } - FixupBranch arg2 = J(); - SetJumpTarget(argh); - UnsafeLoadRegToReg(reg, EAX, accessSize, 0, signExtend); - SetJumpTarget(arg2); -} - -void UnsafeWriteRegToReg(X64Reg reg_value, X64Reg reg_addr, int accessSize, s32 offset) -{ - if (accessSize != 32) { - PanicAlert("UnsafeWriteRegToReg can't handle %i byte accesses", accessSize); - } - BSWAP(32, reg_value); -#ifdef _M_IX86 - AND(32, R(reg_addr), Imm32(Memory::MEMVIEW32_MASK)); - MOV(accessSize, MDisp(reg_addr, (u32)Memory::base + offset), R(reg_value)); -#else - MOV(accessSize, MComplex(RBX, reg_addr, SCALE_1, offset), R(reg_value)); -#endif -} - -// Destroys both arg registers -void SafeWriteRegToReg(X64Reg reg_value, X64Reg reg_addr, int accessSize, s32 offset) -{ - if (offset) - ADD(32, R(reg_addr), Imm32(offset)); - TEST(32, R(reg_addr), Imm32(0x0C000000)); - FixupBranch unsafe_addr = J_CC(CC_NZ); - UnsafeWriteRegToReg(reg_value, reg_addr, accessSize, 0); - FixupBranch skip_call = J(); - SetJumpTarget(unsafe_addr); - ABI_CallFunctionRR(ProtectFunction((void *)&Memory::Write_U32, 2), ABI_PARAM1, ABI_PARAM2); - SetJumpTarget(skip_call); -} - -void WriteToConstRamAddress(int accessSize, const Gen::OpArg& arg, u32 address) -{ -#ifdef _M_X64 - MOV(accessSize, MDisp(RBX, address & 0x3FFFFFFF), arg); -#else - MOV(accessSize, M((void*)(Memory::base + (address & Memory::MEMVIEW32_MASK))), arg); -#endif -} - -void WriteFloatToConstRamAddress(const Gen::X64Reg& xmm_reg, u32 address) -{ -#ifdef _M_X64 - MOV(32, R(RAX), Imm32(address)); - MOVSS(MComplex(RBX, RAX, 1, 0), xmm_reg); -#else - MOVSS(M((void*)((u32)Memory::base + (address & Memory::MEMVIEW32_MASK))), xmm_reg); -#endif -} - -void ForceSinglePrecisionS(X64Reg xmm) { - // Most games don't need these. Zelda requires it though - some platforms get stuck without them. - CVTSD2SS(xmm, R(xmm)); - CVTSS2SD(xmm, R(xmm)); -} -void ForceSinglePrecisionP(X64Reg xmm) { - // Most games don't need these. Zelda requires it though - some platforms get stuck without them. - CVTPD2PS(xmm, R(xmm)); - CVTPS2PD(xmm, R(xmm)); -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "Thunk.h" + +#include "../PowerPC.h" +#include "../../Core.h" +#include "../../HW/GPFifo.h" +#include "../../HW/CommandProcessor.h" +#include "../../HW/PixelEngine.h" +#include "../../HW/Memmap.h" +#include "../PPCTables.h" +#include "x64Emitter.h" +#include "ABI.h" + +#include "Jit.h" +#include "JitCache.h" +#include "JitAsm.h" +#include "JitRegCache.h" + + +namespace Jit64 +{ + +void UnsafeLoadRegToReg(X64Reg reg_addr, X64Reg reg_value, int accessSize, s32 offset, bool signExtend) +{ +#ifdef _M_IX86 + AND(32, R(reg_addr), Imm32(Memory::MEMVIEW32_MASK)); + MOVZX(32, accessSize, reg_value, MDisp(reg_addr, (u32)Memory::base + offset)); +#else + MOVZX(32, accessSize, reg_value, MComplex(RBX, reg_addr, SCALE_1, offset)); +#endif + if (accessSize == 32) + { + BSWAP(32, reg_value); + } + else if (accessSize == 16) + { + BSWAP(32, reg_value); + if (signExtend) + SAR(32, R(reg_value), Imm8(16)); + else + SHR(32, R(reg_value), Imm8(16)); + } else if (signExtend) { + // TODO: bake 8-bit into the original load. + MOVSX(32, accessSize, reg_value, R(reg_value)); + } +} + +void SafeLoadRegToEAX(X64Reg reg, int accessSize, s32 offset, bool signExtend) +{ + if (offset) + ADD(32, R(reg), Imm32((u32)offset)); + TEST(32, R(reg), Imm32(0x0C000000)); + FixupBranch argh = J_CC(CC_Z); + switch (accessSize) + { + case 32: ABI_CallFunctionR(ProtectFunction((void *)&Memory::Read_U32, 1), reg); break; + case 16: ABI_CallFunctionR(ProtectFunction((void *)&Memory::Read_U16, 1), reg); break; + case 8: ABI_CallFunctionR(ProtectFunction((void *)&Memory::Read_U8, 1), reg); break; + } + if (signExtend && accessSize < 32) { + // Need to sign extend values coming from the Read_U* functions. + MOVSX(32, accessSize, EAX, R(EAX)); + } + FixupBranch arg2 = J(); + SetJumpTarget(argh); + UnsafeLoadRegToReg(reg, EAX, accessSize, 0, signExtend); + SetJumpTarget(arg2); +} + +void UnsafeWriteRegToReg(X64Reg reg_value, X64Reg reg_addr, int accessSize, s32 offset) +{ + if (accessSize != 32) { + PanicAlert("UnsafeWriteRegToReg can't handle %i byte accesses", accessSize); + } + BSWAP(32, reg_value); +#ifdef _M_IX86 + AND(32, R(reg_addr), Imm32(Memory::MEMVIEW32_MASK)); + MOV(accessSize, MDisp(reg_addr, (u32)Memory::base + offset), R(reg_value)); +#else + MOV(accessSize, MComplex(RBX, reg_addr, SCALE_1, offset), R(reg_value)); +#endif +} + +// Destroys both arg registers +void SafeWriteRegToReg(X64Reg reg_value, X64Reg reg_addr, int accessSize, s32 offset) +{ + if (offset) + ADD(32, R(reg_addr), Imm32(offset)); + TEST(32, R(reg_addr), Imm32(0x0C000000)); + FixupBranch unsafe_addr = J_CC(CC_NZ); + UnsafeWriteRegToReg(reg_value, reg_addr, accessSize, 0); + FixupBranch skip_call = J(); + SetJumpTarget(unsafe_addr); + ABI_CallFunctionRR(ProtectFunction((void *)&Memory::Write_U32, 2), ABI_PARAM1, ABI_PARAM2); + SetJumpTarget(skip_call); +} + +void WriteToConstRamAddress(int accessSize, const Gen::OpArg& arg, u32 address) +{ +#ifdef _M_X64 + MOV(accessSize, MDisp(RBX, address & 0x3FFFFFFF), arg); +#else + MOV(accessSize, M((void*)(Memory::base + (address & Memory::MEMVIEW32_MASK))), arg); +#endif +} + +void WriteFloatToConstRamAddress(const Gen::X64Reg& xmm_reg, u32 address) +{ +#ifdef _M_X64 + MOV(32, R(RAX), Imm32(address)); + MOVSS(MComplex(RBX, RAX, 1, 0), xmm_reg); +#else + MOVSS(M((void*)((u32)Memory::base + (address & Memory::MEMVIEW32_MASK))), xmm_reg); +#endif +} + +void ForceSinglePrecisionS(X64Reg xmm) { + // Most games don't need these. Zelda requires it though - some platforms get stuck without them. + CVTSD2SS(xmm, R(xmm)); + CVTSS2SD(xmm, R(xmm)); +} +void ForceSinglePrecisionP(X64Reg xmm) { + // Most games don't need these. Zelda requires it though - some platforms get stuck without them. + CVTPD2PS(xmm, R(xmm)); + CVTPS2PD(xmm, R(xmm)); +} + +} // namespace diff --git a/Source/Core/Core/Src/PowerPC/PPCAnalyst.cpp b/Source/Core/Core/Src/PowerPC/PPCAnalyst.cpp index 649dcd6d78..480a3426a4 100644 --- a/Source/Core/Core/Src/PowerPC/PPCAnalyst.cpp +++ b/Source/Core/Core/Src/PowerPC/PPCAnalyst.cpp @@ -1,765 +1,765 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include - -#include "StringUtil.h" -#include "Interpreter/Interpreter.h" -#include "../HW/Memmap.h" -#include "PPCTables.h" -#include "SymbolDB.h" -#include "SignatureDB.h" -#include "PPCAnalyst.h" - -// Analyzes PowerPC code in memory to find functions -// After running, for each function we will know what functions it calls -// and what functions calls it. That is, we will have an incomplete call graph, -// but only missing indirect branches. - -// The results of this analysis is displayed in the code browsing sections at the bottom left -// of the disassembly window (debugger). - -// It is also useful for finding function boundaries so that we can find, fingerprint and detect library functions. -// We don't do this much currently. Only for the special case Super Monkey Ball. - -namespace PPCAnalyst { - -using namespace std; - -PPCAnalyst::CodeOp *codebuffer; - -enum -{ - CODEBUFFER_SIZE = 32000, -}; - -void Init() -{ - codebuffer = new PPCAnalyst::CodeOp[CODEBUFFER_SIZE]; -} - -void Shutdown() -{ - delete [] codebuffer; -} - - -void AnalyzeFunction2(Symbol &func); -u32 EvaluateBranchTarget(UGeckoInstruction instr, u32 pc); - -// void FixUpInternalBranches(CodeOp *code, int begin, int end); - - -#define INVALID_TARGET ((u32)-1) - -u32 EvaluateBranchTarget(UGeckoInstruction instr, u32 pc) -{ - switch (instr.OPCD) - { - case 18://branch instruction - { - u32 target = SignExt26(instr.LI<<2); - if (!instr.AA) - target += pc; - - return target; - } - default: - return INVALID_TARGET; - } -} - -//To find the size of each found function, scan -//forward until we hit blr. In the meantime, collect information -//about which functions this function calls. -//Also collect which internal branch goes the farthest -//If any one goes farther than the blr, assume that there is more than -//one blr, and keep scanning. - -bool AnalyzeFunction(u32 startAddr, Symbol &func, int max_size) -{ - if (!func.name.size()) - func.name = StringFromFormat("zz_%07x_", startAddr & 0x0FFFFFF); - if (func.analyzed >= 1) - return true; // No error, just already did it. - - func.calls.clear(); - func.callers.clear(); - func.size = 0; - func.flags = FFLAG_LEAF; - u32 addr = startAddr; - - u32 farthestInternalBranchTarget = startAddr; - int numInternalBranches = 0; - while (true) - { - func.size += 4; - if (func.size >= CODEBUFFER_SIZE * 4) //weird - return false; - - UGeckoInstruction instr = (UGeckoInstruction)Memory::ReadUnchecked_U32(addr); - if (max_size && func.size > max_size) - { - func.address = startAddr; - func.analyzed = 1; - func.hash = SignatureDB::ComputeCodeChecksum(startAddr, addr); - if (numInternalBranches == 0) - func.flags |= FFLAG_STRAIGHT; - return true; - } - if (PPCTables::IsValidInstruction(instr)) - { - if (instr.hex == 0x4e800020) //4e800021 is blrl, not the end of a function - { - //BLR - if (farthestInternalBranchTarget > addr) - { - //bah, not this one, continue.. - } - else - { - //a final blr! - //We're done! Looks like we have a neat valid function. Perfect. - //Let's calc the checksum and get outta here - func.address = startAddr; - func.analyzed = 1; - func.hash = SignatureDB::ComputeCodeChecksum(startAddr, addr); - if (numInternalBranches == 0) - func.flags |= FFLAG_STRAIGHT; - return true; - } - } - /* - else if ((instr.hex & 0xFC000000) == (0x4b000000 & 0xFC000000) && !instr.LK) { - u32 target = addr + SignExt26(instr.LI << 2); - if (target < startAddr || (max_size && target > max_size+startAddr)) - { - //block ends by branching away. We're done! - func.size *= 4; // into bytes - func.address = startAddr; - func.analyzed = 1; - func.hash = SignatureDB::ComputeCodeChecksum(startAddr, addr); - if (numInternalBranches == 0) - func.flags |= FFLAG_STRAIGHT; - return true; - } - }*/ - else if (instr.hex == 0x4e800021 || instr.hex == 0x4e800420 || instr.hex == 0x4e800421) - { - func.flags &= ~FFLAG_LEAF; - func.flags |= FFLAG_EVIL; - } - else if (instr.hex == 0x4c000064) - { - func.flags &= ~FFLAG_LEAF; - func.flags |= FFLAG_RFI; - } - else - { - if (instr.OPCD == 16) - { - u32 target = SignExt16(instr.BD << 2); - - if (!instr.AA) - target += addr; - - if (target > farthestInternalBranchTarget && !instr.LK) - { - farthestInternalBranchTarget = target; - } - numInternalBranches++; - } - else - { - u32 target = EvaluateBranchTarget(instr, addr); - if (target != INVALID_TARGET && instr.LK) - { - //we found a branch-n-link! - func.calls.push_back(SCall(target,addr)); - func.flags &= ~FFLAG_LEAF; - } - } - } - } - else - { - return false; - } - addr += 4; - } -} - - -// Second pass analysis, done after the first pass is done for all functions -// so we have more information to work with -void AnalyzeFunction2(Symbol &func) -{ - // u32 addr = func.address; - u32 flags = func.flags; - /* - for (int i = 0; i < func.size; i++) - { - UGeckoInstruction instr = (UGeckoInstruction)Memory::ReadUnchecked_U32(addr); - - GekkoOPInfo *info = GetOpInfo(instr); - if (!info) - { - LOG(HLE,"Seems function %s contains bad op %08x",func.name,instr); - } - else - { - if (info->flags & FL_TIMER) - { - flags |= FFLAG_TIMERINSTRUCTIONS; - } - } - addr+=4; - }*/ - - bool nonleafcall = false; - for (size_t i = 0; i < func.calls.size(); i++) - { - SCall c = func.calls[i]; - Symbol *called_func = g_symbolDB.GetSymbolFromAddr(c.function); - if (called_func && (called_func->flags & FFLAG_LEAF) == 0) - { - nonleafcall = true; - break; - } - } - - if (nonleafcall && !(flags & FFLAG_EVIL) && !(flags & FFLAG_RFI)) - flags |= FFLAG_ONLYCALLSNICELEAFS; - - func.flags = flags; -} - -// Currently not used -void FixUpInternalBranches(CodeOp *code, int begin, int end) -{ - for (int i = begin; i < end; i++) - { - if (code[i].branchTo != INVALID_TARGET) //check if this branch already processed - { - if (code[i].inst.OPCD == 16) - { - u32 target = SignExt16(code[i].inst.BD<<2); - if (!code[i].inst.AA) - target += code[i].address; - //local branch - code[i].branchTo = target; - } - else - code[i].branchTo = INVALID_TARGET; - } - } - - //brute force - for (int i = begin; i < end; i++) - { - if (code[i].branchTo != INVALID_TARGET) - { - bool found = false; - for (int j = begin; j < end; j++) - if (code[i].branchTo == code[j].address) - code[i].branchToIndex = j; - if (!found) - { - LOG(HLE, "ERROR: branch target missing"); - } - } - } -} - -void ShuffleUp(CodeOp *code, int first, int last) -{ - CodeOp temp = code[first]; - for (int i = first; i < last; i++) - code[i] = code[i + 1]; - code[last] = temp; -} - -CodeOp *Flatten(u32 address, u32 &realsize, BlockStats &st, BlockRegStats &gpa, BlockRegStats &fpa) -{ - int numCycles = 0; - u32 blockstart = address; - memset(&st, 0, sizeof(st)); - UGeckoInstruction previnst = Memory::Read_Instruction(address-4); - if (previnst.hex == 0x4e800020) - { - st.isFirstBlockOfFunction = true; - } - - gpa.any = true; - fpa.any = false; - - enum Todo - { - JustCopy = 0, Flatten = 1, Nothing = 2 - }; - Todo todo = Nothing; - - //Symbol *f = g_symbolDB.GetSymbolFromAddr(address); - int maxsize = CODEBUFFER_SIZE; - //for now, all will return JustCopy :P - /* - if (f) - { - if (f->flags & FFLAG_LEAF) - { - //no reason to flatten - todo = JustCopy; - } - else if (f->flags & FFLAG_ONLYCALLSNICELEAFS) - { - //inline calls if possible - //todo = Flatten; - todo = JustCopy; - } - else - { - todo = JustCopy; - } - todo = JustCopy; - - maxsize = f->size; - } - else*/ - todo = JustCopy; - - CodeOp *code = codebuffer; //new CodeOp[size]; - - if (todo == JustCopy) - { - realsize = 0; - bool foundExit = false; - int numFollows = 0; - for (int i = 0; i < maxsize; i++, realsize++) - { - memset(&code[i], 0, sizeof(CodeOp)); - code[i].address = address; - UGeckoInstruction inst = Memory::Read_Instruction(code[i].address); - _assert_msg_(GEKKO, inst.hex != 0, "Zero Op - Error flattening %08x op %08x",address+i*4,inst); - code[i].inst = inst; - code[i].branchTo = -1; - code[i].branchToIndex = -1; - code[i].x86ptr = 0; - GekkoOPInfo *opinfo = GetOpInfo(inst); - numCycles += opinfo->numCyclesMinusOne + 1; - _assert_msg_(GEKKO, opinfo != 0,"Invalid Op - Error flattening %08x op %08x",address+i*4,inst); - int flags = opinfo->flags; - - bool follow = false; - u32 destination; - if (inst.OPCD == 18) - { - //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 (flags & FL_ENDBLOCK) //right now we stop early - { - foundExit = true; - break; - } - address += 4; - } - else - { - address = destination; - } - } - _assert_msg_(GEKKO,foundExit,"Analyzer ERROR - Function %08x too big", blockstart); - realsize++; - st.numCycles = numCycles; - FixUpInternalBranches(code,0,realsize); - } - else if (todo == Flatten) - { - return 0; - } - else - { - return 0; - } - - // Do analysis of the code, look for dependencies etc - int numSystemInstructions = 0; - for (int i = 0; i < 32; i++) - { - gpa.firstRead[i] = -1; - gpa.firstWrite[i] = -1; - gpa.numReads[i] = 0; - gpa.numWrites[i] = 0; - } - - gpa.any = true; - for (size_t i = 0; i < realsize; i++) - { - UGeckoInstruction inst = code[i].inst; - if (PPCTables::UsesFPU(inst)) - fpa.any = true; - - const GekkoOPInfo *opinfo = GetOpInfo(code[i].inst); - _assert_msg_(GEKKO, opinfo != 0, "Invalid Op - Error scanning %08x op %08x",address+i*4,inst); - int flags = opinfo->flags; - - 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; - - for (int j = 0; j < 3; j++) - { - code[i].fregsIn[j] = -1; - code[i].regsIn[j] = -1; - } - for (int j = 0; j < 2; j++) - code[i].regsOut[j] = -1; - - code[i].fregOut = -1; - - int numOut = 0; - int numIn = 0; - switch (opinfo->type) - { - case OPTYPE_INTEGER: - case OPTYPE_LOAD: - case OPTYPE_STORE: - if (flags & FL_OUT_A) - { - code[i].regsOut[numOut++] = inst.RA; - gpa.numWrites[inst.RA]++; - } - if (flags & FL_OUT_D) - { - code[i].regsOut[numOut++] = inst.RD; - gpa.numWrites[inst.RD]++; - } - if ((flags & FL_IN_A) || ((flags & FL_IN_A0) && inst.RA != 0)) - { - code[i].regsIn[numIn++] = inst.RA; - gpa.numReads[inst.RA]++; - } - if (flags & FL_IN_B) - { - code[i].regsIn[numIn++] = inst.RB; - gpa.numReads[inst.RB]++; - } - if (flags & FL_IN_C) - { - code[i].regsIn[numIn++] = inst.RC; - gpa.numReads[inst.RC]++; - } - 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. - code[i].outputCR0 = true; - code[i].outputCR1 = true; - } - break; - case OPTYPE_SYSTEM: - case OPTYPE_SYSTEMFP: - numSystemInstructions++; - break; - } - - for (int j = 0; j < numIn; j++) - { - int r = code[i].regsIn[j]; - if (r < 0 || r > 31) - PanicAlert("wtf"); - if (gpa.firstRead[r] == -1) - gpa.firstRead[r] = (short)(i); - gpa.lastRead[r] = (short)(i); - gpa.numReads[r]++; - } - - for (int j = 0; j < numOut; j++) - { - int r = code[i].regsOut[j]; - if (r < 0 || r > 31) - PanicAlert("wtf"); - if (gpa.firstWrite[r] == -1) - gpa.firstWrite[r] = (short)(i); - gpa.lastWrite[r] = (short)(i); - gpa.numWrites[r]++; - } - } - - //Scan for CR0 dependency - //assume next block wants CR0 to be safe - bool wantsCR0 = true; - bool wantsCR1 = true; - bool wantsPS1 = true; - for (int i = realsize - 1; i; i--) - { - if (code[i].outputCR0) - wantsCR0 = false; - if (code[i].outputCR1) - wantsCR1 = false; - if (code[i].outputPS1) - wantsPS1 = false; - wantsCR0 |= code[i].wantsCR0; - wantsCR1 |= code[i].wantsCR1; - wantsPS1 |= code[i].wantsPS1; - code[i].wantsCR0 = wantsCR0; - code[i].wantsCR1 = wantsCR1; - code[i].wantsPS1 = wantsPS1; - } - - // Time for code shuffling, taking into account the above dependency analysis. - bool successful_shuffle = false; - //Move compares - // Try to push compares as close as possible to the following branch - // this way we can do neat stuff like combining compare and branch - // and avoid emitting any cr flags at all - /* - * - pseudo: - if (op is cmp and sets CR0) - { - scan forward for branch - if we hit any instruction that sets CR0, bail - if we hit any instruction that writes to any of the cmp input variables, bail - shuffleup(code, cmpaddr, branchaddr-1) - } - - - how to merge: - if (op is cmp and nextop is condbranch) - { - check if nextnextop wants cr - if it does, bail (or merge and write cr) - else merge! - } - - */ - if (successful_shuffle) { - // Disasm before and after, display side by side - } - // Decide what regs to potentially regcache - return code; -} - -// Most functions that are relevant to analyze should be -// called by another function. Therefore, let's scan the -// entire space for bl operations and find what functions -// get called. -void FindFunctionsFromBranches(u32 startAddr, u32 endAddr, SymbolDB *func_db) -{ - for (u32 addr = startAddr; addr < endAddr; addr+=4) - { - UGeckoInstruction instr = (UGeckoInstruction)Memory::ReadUnchecked_U32(addr); - - if (PPCTables::IsValidInstruction(instr)) - { - switch (instr.OPCD) - { - case 18://branch instruction - { - if (instr.LK) //bl - { - u32 target = SignExt26(instr.LI << 2); - if (!instr.AA) - target += addr; - if (Memory::IsRAMAddress(target)) - { - func_db->AddFunction(target); - } - } - } - break; - default: - break; - } - } - } -} - -void FindFunctionsAfterBLR(SymbolDB *func_db) -{ - vector funcAddrs; - - for (SymbolDB::XFuncMap::iterator iter = func_db->GetIterator(); iter != func_db->End(); iter++) - funcAddrs.push_back(iter->second.address + iter->second.size); - - for (vector::iterator iter = funcAddrs.begin(); iter != funcAddrs.end(); iter++) - { - u32 location = *iter; - while (true) - { - if (PPCTables::IsValidInstruction(Memory::Read_Instruction(location))) - { - //check if this function is already mapped - Symbol *f = func_db->AddFunction(location); - if (!f) - break; - else - location += f->size; - } - else - break; - } - } -} - -void FindFunctions(u32 startAddr, u32 endAddr, SymbolDB *func_db) -{ - //Step 1: Find all functions - FindFunctionsFromBranches(startAddr, endAddr, func_db); - FindFunctionsAfterBLR(func_db); - - //Step 2: - func_db->FillInCallers(); - - int numLeafs = 0, numNice = 0, numUnNice = 0; - int numTimer = 0, numRFI = 0, numStraightLeaf = 0; - int leafSize = 0, niceSize = 0, unniceSize = 0; - for (SymbolDB::XFuncMap::iterator iter = func_db->GetIterator(); iter != func_db->End(); iter++) - { - if (iter->second.address == 4) - { - LOG(HLE, "weird function"); - continue; - } - AnalyzeFunction2(iter->second); - Symbol &f = iter->second; - if (f.name.substr(0,3) == "zzz") - { - if (f.flags & FFLAG_LEAF) - f.name += "_leaf"; - if (f.flags & FFLAG_STRAIGHT) - f.name += "_straight"; - } - if (f.flags & FFLAG_LEAF) - { - numLeafs++; - leafSize += f.size; - } - else if (f.flags & FFLAG_ONLYCALLSNICELEAFS) - { - numNice++; - niceSize += f.size; - } - else - { - numUnNice++; - unniceSize += f.size; - } - - if (f.flags & FFLAG_TIMERINSTRUCTIONS) - numTimer++; - if (f.flags & FFLAG_RFI) - numRFI++; - if ((f.flags & FFLAG_STRAIGHT) && (f.flags & FFLAG_LEAF)) - numStraightLeaf++; - } - if (numLeafs == 0) - leafSize = 0; - else - leafSize /= numLeafs; - - if (numNice == 0) - niceSize = 0; - else - niceSize /= numNice; - - if (numUnNice == 0) - unniceSize = 0; - else - unniceSize /= numUnNice; - - LOG(HLE, "Functions analyzed. %i leafs, %i nice, %i unnice. %i timer, %i rfi. %i are branchless leafs.",numLeafs,numNice,numUnNice,numTimer,numRFI,numStraightLeaf); - LOG(HLE, "Average size: %i (leaf), %i (nice), %i(unnice)", leafSize, niceSize, unniceSize); -} - -/* -void AnalyzeBackwards() -{ -#ifndef BWLINKS - return; -#else - for (int i=0; i> 26) - { - case 18: - if (LK) //LK - { - u32 addr; - if(AA) - addr = SignExt26(LI << 2); - else - addr = ptr + SignExt26(LI << 2); - - int funNum = GetSymbolNum(addr); - if (funNum>=0) - entries[funNum].backwardLinks.push_back(ptr); - } - break; - default: - ; - } - ptr+=4; - } - } - } -#endif -} - -*/ - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "StringUtil.h" +#include "Interpreter/Interpreter.h" +#include "../HW/Memmap.h" +#include "PPCTables.h" +#include "SymbolDB.h" +#include "SignatureDB.h" +#include "PPCAnalyst.h" + +// Analyzes PowerPC code in memory to find functions +// After running, for each function we will know what functions it calls +// and what functions calls it. That is, we will have an incomplete call graph, +// but only missing indirect branches. + +// The results of this analysis is displayed in the code browsing sections at the bottom left +// of the disassembly window (debugger). + +// It is also useful for finding function boundaries so that we can find, fingerprint and detect library functions. +// We don't do this much currently. Only for the special case Super Monkey Ball. + +namespace PPCAnalyst { + +using namespace std; + +PPCAnalyst::CodeOp *codebuffer; + +enum +{ + CODEBUFFER_SIZE = 32000, +}; + +void Init() +{ + codebuffer = new PPCAnalyst::CodeOp[CODEBUFFER_SIZE]; +} + +void Shutdown() +{ + delete [] codebuffer; +} + + +void AnalyzeFunction2(Symbol &func); +u32 EvaluateBranchTarget(UGeckoInstruction instr, u32 pc); + +// void FixUpInternalBranches(CodeOp *code, int begin, int end); + + +#define INVALID_TARGET ((u32)-1) + +u32 EvaluateBranchTarget(UGeckoInstruction instr, u32 pc) +{ + switch (instr.OPCD) + { + case 18://branch instruction + { + u32 target = SignExt26(instr.LI<<2); + if (!instr.AA) + target += pc; + + return target; + } + default: + return INVALID_TARGET; + } +} + +//To find the size of each found function, scan +//forward until we hit blr. In the meantime, collect information +//about which functions this function calls. +//Also collect which internal branch goes the farthest +//If any one goes farther than the blr, assume that there is more than +//one blr, and keep scanning. + +bool AnalyzeFunction(u32 startAddr, Symbol &func, int max_size) +{ + if (!func.name.size()) + func.name = StringFromFormat("zz_%07x_", startAddr & 0x0FFFFFF); + if (func.analyzed >= 1) + return true; // No error, just already did it. + + func.calls.clear(); + func.callers.clear(); + func.size = 0; + func.flags = FFLAG_LEAF; + u32 addr = startAddr; + + u32 farthestInternalBranchTarget = startAddr; + int numInternalBranches = 0; + while (true) + { + func.size += 4; + if (func.size >= CODEBUFFER_SIZE * 4) //weird + return false; + + UGeckoInstruction instr = (UGeckoInstruction)Memory::ReadUnchecked_U32(addr); + if (max_size && func.size > max_size) + { + func.address = startAddr; + func.analyzed = 1; + func.hash = SignatureDB::ComputeCodeChecksum(startAddr, addr); + if (numInternalBranches == 0) + func.flags |= FFLAG_STRAIGHT; + return true; + } + if (PPCTables::IsValidInstruction(instr)) + { + if (instr.hex == 0x4e800020) //4e800021 is blrl, not the end of a function + { + //BLR + if (farthestInternalBranchTarget > addr) + { + //bah, not this one, continue.. + } + else + { + //a final blr! + //We're done! Looks like we have a neat valid function. Perfect. + //Let's calc the checksum and get outta here + func.address = startAddr; + func.analyzed = 1; + func.hash = SignatureDB::ComputeCodeChecksum(startAddr, addr); + if (numInternalBranches == 0) + func.flags |= FFLAG_STRAIGHT; + return true; + } + } + /* + else if ((instr.hex & 0xFC000000) == (0x4b000000 & 0xFC000000) && !instr.LK) { + u32 target = addr + SignExt26(instr.LI << 2); + if (target < startAddr || (max_size && target > max_size+startAddr)) + { + //block ends by branching away. We're done! + func.size *= 4; // into bytes + func.address = startAddr; + func.analyzed = 1; + func.hash = SignatureDB::ComputeCodeChecksum(startAddr, addr); + if (numInternalBranches == 0) + func.flags |= FFLAG_STRAIGHT; + return true; + } + }*/ + else if (instr.hex == 0x4e800021 || instr.hex == 0x4e800420 || instr.hex == 0x4e800421) + { + func.flags &= ~FFLAG_LEAF; + func.flags |= FFLAG_EVIL; + } + else if (instr.hex == 0x4c000064) + { + func.flags &= ~FFLAG_LEAF; + func.flags |= FFLAG_RFI; + } + else + { + if (instr.OPCD == 16) + { + u32 target = SignExt16(instr.BD << 2); + + if (!instr.AA) + target += addr; + + if (target > farthestInternalBranchTarget && !instr.LK) + { + farthestInternalBranchTarget = target; + } + numInternalBranches++; + } + else + { + u32 target = EvaluateBranchTarget(instr, addr); + if (target != INVALID_TARGET && instr.LK) + { + //we found a branch-n-link! + func.calls.push_back(SCall(target,addr)); + func.flags &= ~FFLAG_LEAF; + } + } + } + } + else + { + return false; + } + addr += 4; + } +} + + +// Second pass analysis, done after the first pass is done for all functions +// so we have more information to work with +void AnalyzeFunction2(Symbol &func) +{ + // u32 addr = func.address; + u32 flags = func.flags; + /* + for (int i = 0; i < func.size; i++) + { + UGeckoInstruction instr = (UGeckoInstruction)Memory::ReadUnchecked_U32(addr); + + GekkoOPInfo *info = GetOpInfo(instr); + if (!info) + { + LOG(HLE,"Seems function %s contains bad op %08x",func.name,instr); + } + else + { + if (info->flags & FL_TIMER) + { + flags |= FFLAG_TIMERINSTRUCTIONS; + } + } + addr+=4; + }*/ + + bool nonleafcall = false; + for (size_t i = 0; i < func.calls.size(); i++) + { + SCall c = func.calls[i]; + Symbol *called_func = g_symbolDB.GetSymbolFromAddr(c.function); + if (called_func && (called_func->flags & FFLAG_LEAF) == 0) + { + nonleafcall = true; + break; + } + } + + if (nonleafcall && !(flags & FFLAG_EVIL) && !(flags & FFLAG_RFI)) + flags |= FFLAG_ONLYCALLSNICELEAFS; + + func.flags = flags; +} + +// Currently not used +void FixUpInternalBranches(CodeOp *code, int begin, int end) +{ + for (int i = begin; i < end; i++) + { + if (code[i].branchTo != INVALID_TARGET) //check if this branch already processed + { + if (code[i].inst.OPCD == 16) + { + u32 target = SignExt16(code[i].inst.BD<<2); + if (!code[i].inst.AA) + target += code[i].address; + //local branch + code[i].branchTo = target; + } + else + code[i].branchTo = INVALID_TARGET; + } + } + + //brute force + for (int i = begin; i < end; i++) + { + if (code[i].branchTo != INVALID_TARGET) + { + bool found = false; + for (int j = begin; j < end; j++) + if (code[i].branchTo == code[j].address) + code[i].branchToIndex = j; + if (!found) + { + LOG(HLE, "ERROR: branch target missing"); + } + } + } +} + +void ShuffleUp(CodeOp *code, int first, int last) +{ + CodeOp temp = code[first]; + for (int i = first; i < last; i++) + code[i] = code[i + 1]; + code[last] = temp; +} + +CodeOp *Flatten(u32 address, u32 &realsize, BlockStats &st, BlockRegStats &gpa, BlockRegStats &fpa) +{ + int numCycles = 0; + u32 blockstart = address; + memset(&st, 0, sizeof(st)); + UGeckoInstruction previnst = Memory::Read_Instruction(address-4); + if (previnst.hex == 0x4e800020) + { + st.isFirstBlockOfFunction = true; + } + + gpa.any = true; + fpa.any = false; + + enum Todo + { + JustCopy = 0, Flatten = 1, Nothing = 2 + }; + Todo todo = Nothing; + + //Symbol *f = g_symbolDB.GetSymbolFromAddr(address); + int maxsize = CODEBUFFER_SIZE; + //for now, all will return JustCopy :P + /* + if (f) + { + if (f->flags & FFLAG_LEAF) + { + //no reason to flatten + todo = JustCopy; + } + else if (f->flags & FFLAG_ONLYCALLSNICELEAFS) + { + //inline calls if possible + //todo = Flatten; + todo = JustCopy; + } + else + { + todo = JustCopy; + } + todo = JustCopy; + + maxsize = f->size; + } + else*/ + todo = JustCopy; + + CodeOp *code = codebuffer; //new CodeOp[size]; + + if (todo == JustCopy) + { + realsize = 0; + bool foundExit = false; + int numFollows = 0; + for (int i = 0; i < maxsize; i++, realsize++) + { + memset(&code[i], 0, sizeof(CodeOp)); + code[i].address = address; + UGeckoInstruction inst = Memory::Read_Instruction(code[i].address); + _assert_msg_(GEKKO, inst.hex != 0, "Zero Op - Error flattening %08x op %08x",address+i*4,inst); + code[i].inst = inst; + code[i].branchTo = -1; + code[i].branchToIndex = -1; + code[i].x86ptr = 0; + GekkoOPInfo *opinfo = GetOpInfo(inst); + numCycles += opinfo->numCyclesMinusOne + 1; + _assert_msg_(GEKKO, opinfo != 0,"Invalid Op - Error flattening %08x op %08x",address+i*4,inst); + int flags = opinfo->flags; + + bool follow = false; + u32 destination; + if (inst.OPCD == 18) + { + //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 (flags & FL_ENDBLOCK) //right now we stop early + { + foundExit = true; + break; + } + address += 4; + } + else + { + address = destination; + } + } + _assert_msg_(GEKKO,foundExit,"Analyzer ERROR - Function %08x too big", blockstart); + realsize++; + st.numCycles = numCycles; + FixUpInternalBranches(code,0,realsize); + } + else if (todo == Flatten) + { + return 0; + } + else + { + return 0; + } + + // Do analysis of the code, look for dependencies etc + int numSystemInstructions = 0; + for (int i = 0; i < 32; i++) + { + gpa.firstRead[i] = -1; + gpa.firstWrite[i] = -1; + gpa.numReads[i] = 0; + gpa.numWrites[i] = 0; + } + + gpa.any = true; + for (size_t i = 0; i < realsize; i++) + { + UGeckoInstruction inst = code[i].inst; + if (PPCTables::UsesFPU(inst)) + fpa.any = true; + + const GekkoOPInfo *opinfo = GetOpInfo(code[i].inst); + _assert_msg_(GEKKO, opinfo != 0, "Invalid Op - Error scanning %08x op %08x",address+i*4,inst); + int flags = opinfo->flags; + + 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; + + for (int j = 0; j < 3; j++) + { + code[i].fregsIn[j] = -1; + code[i].regsIn[j] = -1; + } + for (int j = 0; j < 2; j++) + code[i].regsOut[j] = -1; + + code[i].fregOut = -1; + + int numOut = 0; + int numIn = 0; + switch (opinfo->type) + { + case OPTYPE_INTEGER: + case OPTYPE_LOAD: + case OPTYPE_STORE: + if (flags & FL_OUT_A) + { + code[i].regsOut[numOut++] = inst.RA; + gpa.numWrites[inst.RA]++; + } + if (flags & FL_OUT_D) + { + code[i].regsOut[numOut++] = inst.RD; + gpa.numWrites[inst.RD]++; + } + if ((flags & FL_IN_A) || ((flags & FL_IN_A0) && inst.RA != 0)) + { + code[i].regsIn[numIn++] = inst.RA; + gpa.numReads[inst.RA]++; + } + if (flags & FL_IN_B) + { + code[i].regsIn[numIn++] = inst.RB; + gpa.numReads[inst.RB]++; + } + if (flags & FL_IN_C) + { + code[i].regsIn[numIn++] = inst.RC; + gpa.numReads[inst.RC]++; + } + 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. + code[i].outputCR0 = true; + code[i].outputCR1 = true; + } + break; + case OPTYPE_SYSTEM: + case OPTYPE_SYSTEMFP: + numSystemInstructions++; + break; + } + + for (int j = 0; j < numIn; j++) + { + int r = code[i].regsIn[j]; + if (r < 0 || r > 31) + PanicAlert("wtf"); + if (gpa.firstRead[r] == -1) + gpa.firstRead[r] = (short)(i); + gpa.lastRead[r] = (short)(i); + gpa.numReads[r]++; + } + + for (int j = 0; j < numOut; j++) + { + int r = code[i].regsOut[j]; + if (r < 0 || r > 31) + PanicAlert("wtf"); + if (gpa.firstWrite[r] == -1) + gpa.firstWrite[r] = (short)(i); + gpa.lastWrite[r] = (short)(i); + gpa.numWrites[r]++; + } + } + + //Scan for CR0 dependency + //assume next block wants CR0 to be safe + bool wantsCR0 = true; + bool wantsCR1 = true; + bool wantsPS1 = true; + for (int i = realsize - 1; i; i--) + { + if (code[i].outputCR0) + wantsCR0 = false; + if (code[i].outputCR1) + wantsCR1 = false; + if (code[i].outputPS1) + wantsPS1 = false; + wantsCR0 |= code[i].wantsCR0; + wantsCR1 |= code[i].wantsCR1; + wantsPS1 |= code[i].wantsPS1; + code[i].wantsCR0 = wantsCR0; + code[i].wantsCR1 = wantsCR1; + code[i].wantsPS1 = wantsPS1; + } + + // Time for code shuffling, taking into account the above dependency analysis. + bool successful_shuffle = false; + //Move compares + // Try to push compares as close as possible to the following branch + // this way we can do neat stuff like combining compare and branch + // and avoid emitting any cr flags at all + /* + * + pseudo: + if (op is cmp and sets CR0) + { + scan forward for branch + if we hit any instruction that sets CR0, bail + if we hit any instruction that writes to any of the cmp input variables, bail + shuffleup(code, cmpaddr, branchaddr-1) + } + + + how to merge: + if (op is cmp and nextop is condbranch) + { + check if nextnextop wants cr + if it does, bail (or merge and write cr) + else merge! + } + + */ + if (successful_shuffle) { + // Disasm before and after, display side by side + } + // Decide what regs to potentially regcache + return code; +} + +// Most functions that are relevant to analyze should be +// called by another function. Therefore, let's scan the +// entire space for bl operations and find what functions +// get called. +void FindFunctionsFromBranches(u32 startAddr, u32 endAddr, SymbolDB *func_db) +{ + for (u32 addr = startAddr; addr < endAddr; addr+=4) + { + UGeckoInstruction instr = (UGeckoInstruction)Memory::ReadUnchecked_U32(addr); + + if (PPCTables::IsValidInstruction(instr)) + { + switch (instr.OPCD) + { + case 18://branch instruction + { + if (instr.LK) //bl + { + u32 target = SignExt26(instr.LI << 2); + if (!instr.AA) + target += addr; + if (Memory::IsRAMAddress(target)) + { + func_db->AddFunction(target); + } + } + } + break; + default: + break; + } + } + } +} + +void FindFunctionsAfterBLR(SymbolDB *func_db) +{ + vector funcAddrs; + + for (SymbolDB::XFuncMap::iterator iter = func_db->GetIterator(); iter != func_db->End(); iter++) + funcAddrs.push_back(iter->second.address + iter->second.size); + + for (vector::iterator iter = funcAddrs.begin(); iter != funcAddrs.end(); iter++) + { + u32 location = *iter; + while (true) + { + if (PPCTables::IsValidInstruction(Memory::Read_Instruction(location))) + { + //check if this function is already mapped + Symbol *f = func_db->AddFunction(location); + if (!f) + break; + else + location += f->size; + } + else + break; + } + } +} + +void FindFunctions(u32 startAddr, u32 endAddr, SymbolDB *func_db) +{ + //Step 1: Find all functions + FindFunctionsFromBranches(startAddr, endAddr, func_db); + FindFunctionsAfterBLR(func_db); + + //Step 2: + func_db->FillInCallers(); + + int numLeafs = 0, numNice = 0, numUnNice = 0; + int numTimer = 0, numRFI = 0, numStraightLeaf = 0; + int leafSize = 0, niceSize = 0, unniceSize = 0; + for (SymbolDB::XFuncMap::iterator iter = func_db->GetIterator(); iter != func_db->End(); iter++) + { + if (iter->second.address == 4) + { + LOG(HLE, "weird function"); + continue; + } + AnalyzeFunction2(iter->second); + Symbol &f = iter->second; + if (f.name.substr(0,3) == "zzz") + { + if (f.flags & FFLAG_LEAF) + f.name += "_leaf"; + if (f.flags & FFLAG_STRAIGHT) + f.name += "_straight"; + } + if (f.flags & FFLAG_LEAF) + { + numLeafs++; + leafSize += f.size; + } + else if (f.flags & FFLAG_ONLYCALLSNICELEAFS) + { + numNice++; + niceSize += f.size; + } + else + { + numUnNice++; + unniceSize += f.size; + } + + if (f.flags & FFLAG_TIMERINSTRUCTIONS) + numTimer++; + if (f.flags & FFLAG_RFI) + numRFI++; + if ((f.flags & FFLAG_STRAIGHT) && (f.flags & FFLAG_LEAF)) + numStraightLeaf++; + } + if (numLeafs == 0) + leafSize = 0; + else + leafSize /= numLeafs; + + if (numNice == 0) + niceSize = 0; + else + niceSize /= numNice; + + if (numUnNice == 0) + unniceSize = 0; + else + unniceSize /= numUnNice; + + LOG(HLE, "Functions analyzed. %i leafs, %i nice, %i unnice. %i timer, %i rfi. %i are branchless leafs.",numLeafs,numNice,numUnNice,numTimer,numRFI,numStraightLeaf); + LOG(HLE, "Average size: %i (leaf), %i (nice), %i(unnice)", leafSize, niceSize, unniceSize); +} + +/* +void AnalyzeBackwards() +{ +#ifndef BWLINKS + return; +#else + for (int i=0; i> 26) + { + case 18: + if (LK) //LK + { + u32 addr; + if(AA) + addr = SignExt26(LI << 2); + else + addr = ptr + SignExt26(LI << 2); + + int funNum = GetSymbolNum(addr); + if (funNum>=0) + entries[funNum].backwardLinks.push_back(ptr); + } + break; + default: + ; + } + ptr+=4; + } + } + } +#endif +} + +*/ + +} // namespace diff --git a/Source/Core/Core/Src/PowerPC/PPCTables.cpp b/Source/Core/Core/Src/PowerPC/PPCTables.cpp index 2392a64898..1b6bce027b 100644 --- a/Source/Core/Core/Src/PowerPC/PPCTables.cpp +++ b/Source/Core/Core/Src/PowerPC/PPCTables.cpp @@ -1,733 +1,733 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include - -#include "Common.h" -#include "PPCTables.h" -#include "StringUtil.h" -#include "Interpreter/Interpreter.h" - -#if defined(_M_IX86) || defined(_M_X64) -#include "Jit64/Jit.h" -#include "Jit64/JitCache.h" -#else -#error Unknown architecture! -#endif - -typedef void (*_recompilerInstruction) (UGeckoInstruction instCode); - -struct GekkoOPTemplate -{ - int opcode; - PPCTables::_interpreterInstruction interpret; - PPCTables::_recompilerInstruction recompile; - - GekkoOPInfo opinfo; - int runCount; -}; - -static GekkoOPInfo *m_infoTable[64]; -static GekkoOPInfo *m_infoTable4[1024]; -static GekkoOPInfo *m_infoTable19[1024]; -static GekkoOPInfo *m_infoTable31[1024]; -static GekkoOPInfo *m_infoTable59[32]; -static GekkoOPInfo *m_infoTable63[1024]; - -static _recompilerInstruction dynaOpTable[64]; -static _recompilerInstruction dynaOpTable4[1024]; -static _recompilerInstruction dynaOpTable19[1024]; -static _recompilerInstruction dynaOpTable31[1024]; -static _recompilerInstruction dynaOpTable59[32]; -static _recompilerInstruction dynaOpTable63[1024]; - -void DynaRunTable4(UGeckoInstruction _inst) {dynaOpTable4 [_inst.SUBOP10](_inst);} -void DynaRunTable19(UGeckoInstruction _inst) {dynaOpTable19[_inst.SUBOP10](_inst);} -void DynaRunTable31(UGeckoInstruction _inst) {dynaOpTable31[_inst.SUBOP10](_inst);} -void DynaRunTable59(UGeckoInstruction _inst) {dynaOpTable59[_inst.SUBOP5 ](_inst);} -void DynaRunTable63(UGeckoInstruction _inst) {dynaOpTable63[_inst.SUBOP10](_inst);} - -static GekkoOPInfo *m_allInstructions[2048]; -static int m_numInstructions; - -GekkoOPInfo *GetOpInfo(UGeckoInstruction _inst) -{ - GekkoOPInfo *info = m_infoTable[_inst.OPCD]; - if ((info->type & 0xFFFFFF) == OPTYPE_SUBTABLE) - { - int table = info->type>>24; - switch(table) - { - case 4: return m_infoTable4[_inst.SUBOP10]; - case 19: return m_infoTable19[_inst.SUBOP10]; - case 31: return m_infoTable31[_inst.SUBOP10]; - case 59: return m_infoTable59[_inst.SUBOP5]; - case 63: return m_infoTable63[_inst.SUBOP10]; - default: - _assert_msg_(GEKKO,0,"GetOpInfo - invalid subtable op %08x @ %08x", _inst, PC); - return 0; - } - } - else - { - if ((info->type & 0xFFFFFF) == OPTYPE_INVALID) - { - _assert_msg_(GEKKO,0,"GetOpInfo - invalid op %08x @ %08x", _inst, PC); - return 0; - } - return m_infoTable[_inst.OPCD]; - } -} - -Interpreter::_interpreterInstruction GetInterpreterOp(UGeckoInstruction _inst) -{ - const GekkoOPInfo *info = m_infoTable[_inst.OPCD]; - if ((info->type & 0xFFFFFF) == OPTYPE_SUBTABLE) - { - int table = info->type>>24; - switch(table) - { - case 4: return Interpreter::m_opTable4[_inst.SUBOP10]; - case 19: return Interpreter::m_opTable19[_inst.SUBOP10]; - case 31: return Interpreter::m_opTable31[_inst.SUBOP10]; - case 59: return Interpreter::m_opTable59[_inst.SUBOP5]; - case 63: return Interpreter::m_opTable63[_inst.SUBOP10]; - default: - _assert_msg_(GEKKO,0,"GetInterpreterOp - invalid subtable op %08x @ %08x", _inst, PC); - return 0; - } - } - else - { - if ((info->type & 0xFFFFFF) == OPTYPE_INVALID) - { - _assert_msg_(GEKKO,0,"GetInterpreterOp - invalid op %08x @ %08x", _inst, PC); - return 0; - } - return Interpreter::m_opTable[_inst.OPCD]; - } -} - - -GekkoOPTemplate primarytable[] = -{ - {4, Interpreter::RunTable4, DynaRunTable4, {"RunTable4", OPTYPE_SUBTABLE | (4<<24), 0}}, - {19, Interpreter::RunTable19, DynaRunTable19, {"RunTable19", OPTYPE_SUBTABLE | (19<<24), 0}}, - {31, Interpreter::RunTable31, DynaRunTable31, {"RunTable31", OPTYPE_SUBTABLE | (31<<24), 0}}, - {59, Interpreter::RunTable59, DynaRunTable59, {"RunTable59", OPTYPE_SUBTABLE | (59<<24), 0}}, - {63, Interpreter::RunTable63, DynaRunTable63, {"RunTable63", OPTYPE_SUBTABLE | (63<<24), 0}}, - - {16, Interpreter::bcx, Jit64::bcx, {"bcx", OPTYPE_SYSTEM, FL_ENDBLOCK}}, - {18, Interpreter::bx, Jit64::bx, {"bx", OPTYPE_SYSTEM, FL_ENDBLOCK}}, - - {1, Interpreter::HLEFunction, Jit64::HLEFunction, {"HLEFunction", OPTYPE_SYSTEM, FL_ENDBLOCK}}, - {2, Interpreter::CompiledBlock, Jit64::Default, {"DynaBlock", OPTYPE_SYSTEM, 0}}, - {3, Interpreter::twi, Jit64::Default, {"twi", OPTYPE_SYSTEM, 0}}, - {17, Interpreter::sc, Jit64::sc, {"sc", OPTYPE_SYSTEM, FL_ENDBLOCK, 1}}, - - {7, Interpreter::mulli, Jit64::mulli, {"mulli", OPTYPE_INTEGER, FL_RC_BIT, 2}}, - {8, Interpreter::subfic, Jit64::subfic, {"subfic", OPTYPE_INTEGER, FL_SET_CA}}, - {10, Interpreter::cmpli, Jit64::cmpli, {"cmpli", OPTYPE_INTEGER, FL_SET_CRn}}, - {11, Interpreter::cmpi, Jit64::cmpi, {"cmpi", OPTYPE_INTEGER, FL_SET_CRn}}, - {12, Interpreter::addic, Jit64::reg_imm, {"addic", OPTYPE_INTEGER, FL_SET_CA}}, - {13, Interpreter::addic_rc, Jit64::reg_imm, {"addic_rc", OPTYPE_INTEGER, FL_SET_CR0}}, - {14, Interpreter::addi, Jit64::reg_imm, {"addi", OPTYPE_INTEGER, 0}}, - {15, Interpreter::addis, Jit64::reg_imm, {"addis", OPTYPE_INTEGER, 0}}, - - {20, Interpreter::rlwimix, Jit64::rlwimix, {"rlwimix", OPTYPE_INTEGER, FL_RC_BIT}}, - {21, Interpreter::rlwinmx, Jit64::rlwinmx, {"rlwinmx", OPTYPE_INTEGER, FL_RC_BIT}}, - {23, Interpreter::rlwnmx, Jit64::rlwnmx, {"rlwnmx", OPTYPE_INTEGER, FL_RC_BIT}}, - - {24, Interpreter::ori, Jit64::reg_imm, {"ori", OPTYPE_INTEGER, 0}}, - {25, Interpreter::oris, Jit64::reg_imm, {"oris", OPTYPE_INTEGER, 0}}, - {26, Interpreter::xori, Jit64::reg_imm, {"xori", OPTYPE_INTEGER, 0}}, - {27, Interpreter::xoris, Jit64::reg_imm, {"xoris", OPTYPE_INTEGER, 0}}, - {28, Interpreter::andi_rc, Jit64::reg_imm, {"andi_rc", OPTYPE_INTEGER, FL_SET_CR0}}, - {29, Interpreter::andis_rc, Jit64::reg_imm, {"andis_rc", OPTYPE_INTEGER, FL_SET_CR0}}, - - {32, Interpreter::lwz, Jit64::lXz, {"lwz", OPTYPE_LOAD, 0}}, - {33, Interpreter::lwzu, Jit64::Default, {"lwzu", OPTYPE_LOAD, 0}}, - {34, Interpreter::lbz, Jit64::lXz, {"lbz", OPTYPE_LOAD, 0}}, - {35, Interpreter::lbzu, Jit64::Default, {"lbzu", OPTYPE_LOAD, 0}}, - {40, Interpreter::lhz, Jit64::lXz, {"lhz", OPTYPE_LOAD, 0}}, - {41, Interpreter::lhzu, Jit64::Default, {"lhzu", OPTYPE_LOAD, 0}}, - {42, Interpreter::lha, Jit64::lha, {"lha", OPTYPE_LOAD, 0}}, - {43, Interpreter::lhau, Jit64::Default, {"lhau", OPTYPE_LOAD, 0}}, - - {44, Interpreter::sth, Jit64::stX, {"sth", OPTYPE_STORE, 0}}, - {45, Interpreter::sthu, Jit64::stX, {"sthu", OPTYPE_STORE, 0}}, - {36, Interpreter::stw, Jit64::stX, {"stw", OPTYPE_STORE, 0}}, - {37, Interpreter::stwu, Jit64::stX, {"stwu", OPTYPE_STORE, 0}}, - {38, Interpreter::stb, Jit64::stX, {"stb", OPTYPE_STORE, 0}}, - {39, Interpreter::stbu, Jit64::stX, {"stbu", OPTYPE_STORE, 0}}, - - {46, Interpreter::lmw, Jit64::lmw, {"lmw", OPTYPE_SYSTEM, 0, 10}}, - {47, Interpreter::stmw, Jit64::stmw, {"stmw", OPTYPE_SYSTEM, 0, 10}}, - - {48, Interpreter::lfs, Jit64::lfs, {"lfs", OPTYPE_LOADFP, 0}}, - {49, Interpreter::lfsu, Jit64::Default, {"lfsu", OPTYPE_LOADFP, 0}}, - {50, Interpreter::lfd, Jit64::lfd, {"lfd", OPTYPE_LOADFP, 0}}, - {51, Interpreter::lfdu, Jit64::Default, {"lfdu", OPTYPE_LOADFP, 0}}, - - {52, Interpreter::stfs, Jit64::stfs, {"stfs", OPTYPE_STOREFP, 0}}, - {53, Interpreter::stfsu, Jit64::stfs, {"stfsu", OPTYPE_STOREFP, 0}}, - {54, Interpreter::stfd, Jit64::stfd, {"stfd", OPTYPE_STOREFP, 0}}, - {55, Interpreter::stfdu, Jit64::Default, {"stfdu", OPTYPE_STOREFP, 0}}, - - {56, Interpreter::psq_l, Jit64::psq_l, {"psq_l", OPTYPE_PS, 0}}, - {57, Interpreter::psq_lu, Jit64::psq_l, {"psq_lu", OPTYPE_PS, 0}}, - {60, Interpreter::psq_st, Jit64::psq_st, {"psq_st", OPTYPE_PS, 0}}, - {61, Interpreter::psq_stu, Jit64::psq_st, {"psq_stu", OPTYPE_PS, 0}}, - - //missing: 0, 5, 6, 9, 22, 30, 62, 58 - {0, Interpreter::unknown_instruction, Jit64::Default, {"unknown_instruction", OPTYPE_UNKNOWN, 0}}, - {5, Interpreter::unknown_instruction, Jit64::Default, {"unknown_instruction", OPTYPE_UNKNOWN, 0}}, - {6, Interpreter::unknown_instruction, Jit64::Default, {"unknown_instruction", OPTYPE_UNKNOWN, 0}}, - {9, Interpreter::unknown_instruction, Jit64::Default, {"unknown_instruction", OPTYPE_UNKNOWN, 0}}, - {22, Interpreter::unknown_instruction, Jit64::Default, {"unknown_instruction", OPTYPE_UNKNOWN, 0}}, - {30, Interpreter::unknown_instruction, Jit64::Default, {"unknown_instruction", OPTYPE_UNKNOWN, 0}}, - {62, Interpreter::unknown_instruction, Jit64::Default, {"unknown_instruction", OPTYPE_UNKNOWN, 0}}, - {58, Interpreter::unknown_instruction, Jit64::Default, {"unknown_instruction", OPTYPE_UNKNOWN, 0}}, -}; - -GekkoOPTemplate table4[] = -{ - {0, Interpreter::ps_cmpu0, Jit64::Default, {"ps_cmpu0", OPTYPE_PS, FL_SET_CRn}}, - {32, Interpreter::ps_cmpo0, Jit64::Default, {"ps_cmpo0", OPTYPE_PS, FL_SET_CRn}}, - {40, Interpreter::ps_neg, Jit64::ps_sign, {"ps_neg", OPTYPE_PS, FL_RC_BIT}}, - {136, Interpreter::ps_nabs, Jit64::ps_sign, {"ps_nabs", OPTYPE_PS, FL_RC_BIT}}, - {264, Interpreter::ps_abs, Jit64::ps_sign, {"ps_abs", OPTYPE_PS, FL_RC_BIT}}, - {64, Interpreter::ps_cmpu1, Jit64::Default, {"ps_cmpu1", OPTYPE_PS, FL_RC_BIT}}, - {72, Interpreter::ps_mr, Jit64::ps_mr, {"ps_mr", OPTYPE_PS, FL_RC_BIT}}, - {96, Interpreter::ps_cmpo1, Jit64::Default, {"ps_cmpo1", OPTYPE_PS, FL_RC_BIT}}, - {528, Interpreter::ps_merge00, Jit64::ps_mergeXX, {"ps_merge00", OPTYPE_PS, FL_RC_BIT}}, - {560, Interpreter::ps_merge01, Jit64::ps_mergeXX, {"ps_merge01", OPTYPE_PS, FL_RC_BIT}}, - {592, Interpreter::ps_merge10, Jit64::ps_mergeXX, {"ps_merge10", OPTYPE_PS, FL_RC_BIT}}, - {624, Interpreter::ps_merge11, Jit64::ps_mergeXX, {"ps_merge11", OPTYPE_PS, FL_RC_BIT}}, - - {1014, Interpreter::dcbz_l, Jit64::Default, {"dcbz_l", OPTYPE_SYSTEM, 0}}, -}; - -GekkoOPTemplate table4_2[] = -{ - {10, Interpreter::ps_sum0, Jit64::ps_sum, {"ps_sum0", OPTYPE_PS, 0}}, - {11, Interpreter::ps_sum1, Jit64::ps_sum, {"ps_sum1", OPTYPE_PS, 0}}, - {12, Interpreter::ps_muls0, Jit64::ps_muls, {"ps_muls0", OPTYPE_PS, 0}}, - {13, Interpreter::ps_muls1, Jit64::ps_muls, {"ps_muls1", OPTYPE_PS, 0}}, - {14, Interpreter::ps_madds0, Jit64::Default, {"ps_madds0", OPTYPE_PS, 0}}, - {15, Interpreter::ps_madds1, Jit64::Default, {"ps_madds1", OPTYPE_PS, 0}}, - {18, Interpreter::ps_div, Jit64::ps_arith, {"ps_div", OPTYPE_PS, 0, 16}}, - {20, Interpreter::ps_sub, Jit64::ps_arith, {"ps_sub", OPTYPE_PS, 0}}, - {21, Interpreter::ps_add, Jit64::ps_arith, {"ps_add", OPTYPE_PS, 0}}, - {23, Interpreter::ps_sel, Jit64::ps_sel, {"ps_sel", OPTYPE_PS, 0}}, - {24, Interpreter::ps_res, Jit64::Default, {"ps_res", OPTYPE_PS, 0}}, - {25, Interpreter::ps_mul, Jit64::ps_arith, {"ps_mul", OPTYPE_PS, 0}}, - {26, Interpreter::ps_rsqrte, Jit64::ps_rsqrte, {"ps_rsqrte", OPTYPE_PS, 0, 1}}, - {28, Interpreter::ps_msub, Jit64::ps_maddXX, {"ps_msub", OPTYPE_PS, 0}}, - {29, Interpreter::ps_madd, Jit64::ps_maddXX, {"ps_madd", OPTYPE_PS, 0}}, - {30, Interpreter::ps_nmsub, Jit64::ps_maddXX, {"ps_nmsub", OPTYPE_PS, 0}}, - {31, Interpreter::ps_nmadd, Jit64::ps_maddXX, {"ps_nmadd", OPTYPE_PS, 0}}, -}; - -GekkoOPTemplate table4_3[] = -{ - {6, Interpreter::psq_lx, Jit64::Default, {"psq_lx", OPTYPE_PS, 0}}, - {7, Interpreter::psq_stx, Jit64::Default, {"psq_stx", OPTYPE_PS, 0}}, - {38, Interpreter::psq_lux, Jit64::Default, {"psq_lux", OPTYPE_PS, 0}}, - {39, Interpreter::psq_stux, Jit64::Default, {"psq_stux", OPTYPE_PS, 0}}, -}; - -GekkoOPTemplate table19[] = -{ - {528, Interpreter::bcctrx, Jit64::bcctrx, {"bcctrx", OPTYPE_BRANCH, FL_ENDBLOCK}}, - {16, Interpreter::bclrx, Jit64::bclrx, {"bclrx", OPTYPE_BRANCH, FL_ENDBLOCK}}, - {257, Interpreter::crand, Jit64::Default, {"crand", OPTYPE_CR, 0}}, - {129, Interpreter::crandc, Jit64::Default, {"crandc", OPTYPE_CR, 0}}, - {289, Interpreter::creqv, Jit64::Default, {"creqv", OPTYPE_CR, 0}}, - {225, Interpreter::crnand, Jit64::Default, {"crnand", OPTYPE_CR, 0}}, - {33, Interpreter::crnor, Jit64::Default, {"crnor", OPTYPE_CR, 0}}, - {449, Interpreter::cror, Jit64::Default, {"cror", OPTYPE_CR, 0}}, - {417, Interpreter::crorc, Jit64::Default, {"crorc", OPTYPE_CR, 0}}, - {193, Interpreter::crxor, Jit64::Default, {"crxor", OPTYPE_CR, 0}}, - - {150, Interpreter::isync, Jit64::DoNothing, {"isync", OPTYPE_ICACHE, 0 }}, - {0, Interpreter::mcrf, Jit64::Default, {"mcrf", OPTYPE_SYSTEM, 0}}, - - {50, Interpreter::rfi, Jit64::rfi, {"rfi", OPTYPE_SYSTEM, FL_ENDBLOCK | FL_CHECKEXCEPTIONS, 1}}, - {18, Interpreter::rfid, Jit64::Default, {"rfid", OPTYPE_SYSTEM, FL_ENDBLOCK | FL_CHECKEXCEPTIONS}} -}; - - -GekkoOPTemplate table31[] = -{ - {28, Interpreter::andx, Jit64::andx, {"andx", OPTYPE_INTEGER, FL_IN_AB | FL_OUT_S | FL_RC_BIT}}, - {60, Interpreter::andcx, Jit64::Default, {"andcx", OPTYPE_INTEGER, FL_IN_AB | FL_OUT_S | FL_RC_BIT}}, - {444, Interpreter::orx, Jit64::orx, {"orx", OPTYPE_INTEGER, FL_IN_AB | FL_OUT_S | FL_RC_BIT}}, - {124, Interpreter::norx, Jit64::Default, {"norx", OPTYPE_INTEGER, FL_IN_AB | FL_OUT_S | FL_RC_BIT}}, - {316, Interpreter::xorx, Jit64::xorx, {"xorx", OPTYPE_INTEGER, FL_IN_AB | FL_OUT_S | FL_RC_BIT}}, - {412, Interpreter::orcx, Jit64::Default, {"orcx", OPTYPE_INTEGER, FL_IN_AB | FL_OUT_S | FL_RC_BIT}}, - {476, Interpreter::nandx, Jit64::Default, {"nandx", OPTYPE_INTEGER, FL_IN_AB | FL_OUT_S | FL_RC_BIT}}, - {284, Interpreter::eqvx, Jit64::Default, {"eqvx", OPTYPE_INTEGER, FL_IN_AB | FL_OUT_S | FL_RC_BIT}}, - {0, Interpreter::cmp, Jit64::cmp, {"cmp", OPTYPE_INTEGER, FL_IN_AB | FL_SET_CRn}}, - {32, Interpreter::cmpl, Jit64::cmpl, {"cmpl", OPTYPE_INTEGER, FL_IN_AB | FL_SET_CRn}}, - {26, Interpreter::cntlzwx, Jit64::cntlzwx, {"cntlzwx",OPTYPE_INTEGER, FL_IN_A | FL_OUT_S | FL_RC_BIT}}, - {922, Interpreter::extshx, Jit64::extshx, {"extshx", OPTYPE_INTEGER, FL_IN_A | FL_OUT_S | FL_RC_BIT}}, - {954, Interpreter::extsbx, Jit64::extsbx, {"extsbx", OPTYPE_INTEGER, FL_IN_A | FL_OUT_S | FL_RC_BIT}}, - {536, Interpreter::srwx, Jit64::srwx, {"srwx", OPTYPE_INTEGER, FL_RC_BIT}}, - {792, Interpreter::srawx, Jit64::srawx, {"srawx", OPTYPE_INTEGER, FL_RC_BIT}}, - {824, Interpreter::srawix, Jit64::srawix, {"srawix", OPTYPE_INTEGER, FL_RC_BIT}}, - {24, Interpreter::slwx, Jit64::slwx, {"slwx", OPTYPE_INTEGER, FL_RC_BIT}}, - - {54, Interpreter::dcbst, Jit64::Default, {"dcbst", OPTYPE_DCACHE, 0, 4}}, - {86, Interpreter::dcbf, Jit64::Default, {"dcbf", OPTYPE_DCACHE, 0, 4}}, - {246, Interpreter::dcbtst, Jit64::Default, {"dcbtst", OPTYPE_DCACHE, 0, 1}}, - {278, Interpreter::dcbt, Jit64::Default, {"dcbt", OPTYPE_DCACHE, 0, 1}}, - {470, Interpreter::dcbi, Jit64::Default, {"dcbi", OPTYPE_DCACHE, 0, 4}}, - {758, Interpreter::dcba, Jit64::Default, {"dcba", OPTYPE_DCACHE, 0, 4}}, - {1014, Interpreter::dcbz, Jit64::dcbz, {"dcbz", OPTYPE_DCACHE, 0, 4}}, - - //load word - {23, Interpreter::lwzx, Jit64::lwzx, {"lwzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, - {55, Interpreter::lwzux, Jit64::Default, {"lwzux", OPTYPE_LOAD, FL_OUT_D | FL_IN_A | FL_IN_B}}, - - //load halfword - {279, Interpreter::lhzx, Jit64::Default, {"lhzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, - {311, Interpreter::lhzux, Jit64::Default, {"lhzux", OPTYPE_LOAD, FL_OUT_D | FL_IN_A | FL_IN_B}}, - - //load halfword signextend - {343, Interpreter::lhax, Jit64::lhax, {"lhax", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, - {375, Interpreter::lhaux, Jit64::Default, {"lhaux", OPTYPE_LOAD, FL_OUT_D | FL_IN_A | FL_IN_B}}, - - //load byte - {87, Interpreter::lbzx, Jit64::lbzx, {"lbzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, - {119, Interpreter::lbzux, Jit64::Default, {"lbzux", OPTYPE_LOAD, FL_OUT_D | FL_IN_A | FL_IN_B}}, - - //load byte reverse - {534, Interpreter::lwbrx, Jit64::Default, {"lwbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, - {790, Interpreter::lhbrx, Jit64::Default, {"lhbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, - - // Conditional load/store (Wii SMP) - {150, Interpreter::stwcxd, Jit64::Default, {"stwcxd", OPTYPE_STORE, 0}}, - {20, Interpreter::lwarx, Jit64::Default, {"lwarx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0B}}, - - //load string (interpret these) - {533, Interpreter::lswx, Jit64::Default, {"lswx", OPTYPE_LOAD, FL_IN_A | FL_OUT_D}}, - {597, Interpreter::lswi, Jit64::Default, {"lswi", OPTYPE_LOAD, FL_IN_AB | FL_OUT_D}}, - - //store word - {151, Interpreter::stwx, Jit64::Default, {"stwx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}}, - {183, Interpreter::stwux, Jit64::Default, {"stwux", OPTYPE_STORE, FL_IN_A | FL_IN_B}}, - - //store halfword - {407, Interpreter::sthx, Jit64::Default, {"sthx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}}, - {439, Interpreter::sthux, Jit64::Default, {"sthux", OPTYPE_STORE, FL_IN_A | FL_IN_B}}, - - //store byte - {215, Interpreter::stbx, Jit64::Default, {"stbx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}}, - {247, Interpreter::stbux, Jit64::Default, {"stbux", OPTYPE_STORE, FL_IN_A | FL_IN_B}}, - - //store bytereverse - {662, Interpreter::stwbrx, Jit64::Default, {"stwbrx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}}, - {918, Interpreter::sthbrx, Jit64::Default, {"sthbrx", OPTYPE_STORE, FL_IN_A | FL_IN_B}}, - - {661, Interpreter::stswx, Jit64::Default, {"stswx", OPTYPE_STORE, 0}}, - {725, Interpreter::stswi, Jit64::Default, {"stswi", OPTYPE_STORE, 0}}, - - // fp load/store - {535, Interpreter::lfsx, Jit64::lfsx, {"lfsx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B}}, - {567, Interpreter::lfsux, Jit64::Default, {"lfsux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B}}, - {599, Interpreter::lfdx, Jit64::Default, {"lfdx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B}}, - {631, Interpreter::lfdux, Jit64::Default, {"lfdux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B}}, - - {663, Interpreter::stfsx, Jit64::Default, {"stfsx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}}, - {695, Interpreter::stfsux, Jit64::Default, {"stfsux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B}}, - {727, Interpreter::stfdx, Jit64::Default, {"stfdx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}}, - {759, Interpreter::stfdux, Jit64::Default, {"stfdux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B}}, - {983, Interpreter::stfiwx, Jit64::Default, {"stfiwx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}}, - - {19, Interpreter::mfcr, Jit64::mfcr, {"mfcr", OPTYPE_SYSTEM, 0}}, - {83, Interpreter::mfmsr, Jit64::mfmsr, {"mfmsr", OPTYPE_SYSTEM, 0}}, - {144, Interpreter::mtcrf, Jit64::mtcrf, {"mtcrf", OPTYPE_SYSTEM, 0}}, - {146, Interpreter::mtmsr, Jit64::mtmsr, {"mtmsr", OPTYPE_SYSTEM, FL_ENDBLOCK}}, - {210, Interpreter::mtsr, Jit64::Default, {"mtsr", OPTYPE_SYSTEM, 0}}, - {242, Interpreter::mtsrin, Jit64::Default, {"mtsrin", OPTYPE_SYSTEM, 0}}, - {339, Interpreter::mfspr, Jit64::mfspr, {"mfspr", OPTYPE_SPR, 0}}, - {467, Interpreter::mtspr, Jit64::mtspr, {"mtspr", OPTYPE_SPR, 0, 2}}, - {371, Interpreter::mftb, Jit64::mftb, {"mftb", OPTYPE_SYSTEM, FL_TIMER}}, - {512, Interpreter::mcrxr, Jit64::Default, {"mcrxr", OPTYPE_SYSTEM, 0}}, - {595, Interpreter::mfsr, Jit64::Default, {"mfsr", OPTYPE_SYSTEM, 0, 2}}, - {659, Interpreter::mfsrin, Jit64::Default, {"mfsrin", OPTYPE_SYSTEM, 0, 2}}, - - {4, Interpreter::tw, Jit64::Default, {"tw", OPTYPE_SYSTEM, 0, 1}}, - {598, Interpreter::sync, Jit64::DoNothing, {"sync", OPTYPE_SYSTEM, 0, 2}}, - {982, Interpreter::icbi, Jit64::Default, {"icbi", OPTYPE_SYSTEM, 0, 3}}, - - // Unused instructions on GC - {310, Interpreter::eciwx, Jit64::Default, {"eciwx", OPTYPE_INTEGER, FL_RC_BIT}}, - {438, Interpreter::ecowx, Jit64::Default, {"ecowx", OPTYPE_INTEGER, FL_RC_BIT}}, - {854, Interpreter::eieio, Jit64::Default, {"eieio", OPTYPE_INTEGER, FL_RC_BIT}}, - {306, Interpreter::tlbie, Jit64::Default, {"tlbie", OPTYPE_SYSTEM, 0}}, - {370, Interpreter::tlbia, Jit64::Default, {"tlbia", OPTYPE_SYSTEM, 0}}, - {566, Interpreter::tlbsync, Jit64::Default, {"tlbsync", OPTYPE_SYSTEM, 0}}, -}; - -GekkoOPTemplate table31_2[] = -{ - {266, Interpreter::addx, Jit64::addx, {"addx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_RC_BIT}}, - {10, Interpreter::addcx, Jit64::Default, {"addcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_SET_CA | FL_RC_BIT}}, - {138, Interpreter::addex, Jit64::addex, {"addex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, - {234, Interpreter::addmex, Jit64::Default, {"addmex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, - {202, Interpreter::addzex, Jit64::Default, {"addzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, - {491, Interpreter::divwx, Jit64::Default, {"divwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_RC_BIT, 39}}, - {459, Interpreter::divwux, Jit64::divwux, {"divwux", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_RC_BIT, 39}}, - {75, Interpreter::mulhwx, Jit64::Default, {"mulhwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_RC_BIT, 4}}, - {11, Interpreter::mulhwux, Jit64::mulhwux, {"mulhwux", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_RC_BIT, 4}}, - {235, Interpreter::mullwx, Jit64::mullwx, {"mullwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_RC_BIT, 4}}, - {104, Interpreter::negx, Jit64::negx, {"negx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_RC_BIT}}, - {40, Interpreter::subfx, Jit64::subfx, {"subfx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_RC_BIT}}, - {8, Interpreter::subfcx, Jit64::subfcx, {"subfcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_SET_CA | FL_RC_BIT}}, - {136, Interpreter::subfex, Jit64::subfex, {"subfex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, - {232, Interpreter::subfmex, Jit64::Default, {"subfmex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, - {200, Interpreter::subfzex, Jit64::Default, {"subfzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, -}; - -GekkoOPTemplate table59[] = -{ - {18, Interpreter::fdivsx, Jit64::fp_arith_s, {"fdivsx", OPTYPE_FPU, FL_RC_BIT_F, 16}}, - {20, Interpreter::fsubsx, Jit64::fp_arith_s, {"fsubsx", OPTYPE_FPU, FL_RC_BIT_F}}, - {21, Interpreter::faddsx, Jit64::fp_arith_s, {"faddsx", OPTYPE_FPU, FL_RC_BIT_F}}, -// {22, Interpreter::fsqrtsx, Jit64::Default, {"fsqrtsx", OPTYPE_FPU, FL_RC_BIT_F}}, // Not implemented on gekko - {24, Interpreter::fresx, Jit64::Default, {"fresx", OPTYPE_FPU, FL_RC_BIT_F}}, - {25, Interpreter::fmulsx, Jit64::fp_arith_s, {"fmulsx", OPTYPE_FPU, FL_RC_BIT_F}}, - {28, Interpreter::fmsubsx, Jit64::fmaddXX, {"fmsubsx", OPTYPE_FPU, FL_RC_BIT_F}}, - {29, Interpreter::fmaddsx, Jit64::fmaddXX, {"fmaddsx", OPTYPE_FPU, FL_RC_BIT_F}}, - {30, Interpreter::fnmsubsx, Jit64::fmaddXX, {"fnmsubsx", OPTYPE_FPU, FL_RC_BIT_F}}, - {31, Interpreter::fnmaddsx, Jit64::fmaddXX, {"fnmaddsx", OPTYPE_FPU, FL_RC_BIT_F}}, -}; - -GekkoOPTemplate table63[] = -{ - {264, Interpreter::fabsx, Jit64::Default, {"fabsx", OPTYPE_FPU, FL_RC_BIT_F}}, - {32, Interpreter::fcmpo, Jit64::fcmpx, {"fcmpo", OPTYPE_FPU, FL_RC_BIT_F}}, - {0, Interpreter::fcmpu, Jit64::fcmpx, {"fcmpu", OPTYPE_FPU, FL_RC_BIT_F}}, - {14, Interpreter::fctiwx, Jit64::Default, {"fctiwx", OPTYPE_FPU, FL_RC_BIT_F}}, - {15, Interpreter::fctiwzx, Jit64::Default, {"fctiwzx", OPTYPE_FPU, FL_RC_BIT_F}}, - {72, Interpreter::fmrx, Jit64::fmrx, {"fmrx", OPTYPE_FPU, FL_RC_BIT_F}}, - {136, Interpreter::fnabsx, Jit64::Default, {"fnabsx", OPTYPE_FPU, FL_RC_BIT_F}}, - {40, Interpreter::fnegx, Jit64::Default, {"fnegx", OPTYPE_FPU, FL_RC_BIT_F}}, - {12, Interpreter::frspx, Jit64::Default, {"frspx", OPTYPE_FPU, FL_RC_BIT_F}}, - - {64, Interpreter::mcrfs, Jit64::Default, {"mcrfs", OPTYPE_SYSTEMFP, 0}}, - {583, Interpreter::mffsx, Jit64::Default, {"mffsx", OPTYPE_SYSTEMFP, 0}}, - {70, Interpreter::mtfsb0x, Jit64::Default, {"mtfsb0x", OPTYPE_SYSTEMFP, 0, 2}}, - {38, Interpreter::mtfsb1x, Jit64::Default, {"mtfsb1x", OPTYPE_SYSTEMFP, 0, 2}}, - {134, Interpreter::mtfsfix, Jit64::Default, {"mtfsfix", OPTYPE_SYSTEMFP, 0, 2}}, - {711, Interpreter::mtfsfx, Jit64::Default, {"mtfsfx", OPTYPE_SYSTEMFP, 0, 2}}, -}; - -GekkoOPTemplate table63_2[] = -{ - {18, Interpreter::fdivx, Jit64::Default, {"fdivx", OPTYPE_FPU, FL_RC_BIT_F, 30}}, - {20, Interpreter::fsubx, Jit64::Default, {"fsubx", OPTYPE_FPU, FL_RC_BIT_F}}, - {21, Interpreter::faddx, Jit64::Default, {"faddx", OPTYPE_FPU, FL_RC_BIT_F}}, - {22, Interpreter::fsqrtx, Jit64::Default, {"fsqrtx", OPTYPE_FPU, FL_RC_BIT_F}}, - {23, Interpreter::fselx, Jit64::Default, {"fselx", OPTYPE_FPU, FL_RC_BIT_F}}, - {25, Interpreter::fmulx, Jit64::Default, {"fmulx", OPTYPE_FPU, FL_RC_BIT_F}}, - {26, Interpreter::frsqrtex,Jit64::Default, {"frsqrtex", OPTYPE_FPU, FL_RC_BIT_F}}, - {28, Interpreter::fmsubx, Jit64::Default, {"fmsubx", OPTYPE_FPU, FL_RC_BIT_F}}, - {29, Interpreter::fmaddx, Jit64::Default, {"fmaddx", OPTYPE_FPU, FL_RC_BIT_F}}, - {30, Interpreter::fnmsubx, Jit64::Default, {"fnmsubx", OPTYPE_FPU, FL_RC_BIT_F}}, - {31, Interpreter::fnmaddx, Jit64::Default, {"fnmaddx", OPTYPE_FPU, FL_RC_BIT_F}}, -}; - -bool PPCTables::UsesFPU(UGeckoInstruction _inst) -{ - switch (_inst.OPCD) - { - case 04: // PS - return _inst.SUBOP10 != 1014; - - case 48: // lfs - case 49: // lfsu - case 50: // lfd - case 51: // lfdu - case 52: // stfs - case 53: // stfsu - case 54: // stfd - case 55: // stfdu - case 56: // psq_l - case 57: // psq_lu - - case 59: // FPU-sgl - case 60: // psq_st - case 61: // psq_stu - case 63: // FPU-dbl - return true; - - case 31: - switch (_inst.SUBOP10) - { - case 535: - case 567: - case 599: - case 631: - case 663: - case 695: - case 727: - case 759: - case 983: - return true; - default: - return false; - } - default: - return false; - } -} - -void PPCTables::InitTables() -{ - //clear - for (int i = 0; i < 32; i++) - { - Interpreter::m_opTable59[i] = Interpreter::unknown_instruction; - dynaOpTable59[i] = Jit64::unknown_instruction; - m_infoTable59[i] = 0; - } - - for (int i = 0; i < 1024; i++) - { - Interpreter::m_opTable4 [i] = Interpreter::unknown_instruction; - Interpreter::m_opTable19[i] = Interpreter::unknown_instruction; - Interpreter::m_opTable31[i] = Interpreter::unknown_instruction; - Interpreter::m_opTable63[i] = Interpreter::unknown_instruction; - dynaOpTable4 [i] = Jit64::unknown_instruction; - dynaOpTable19[i] = Jit64::unknown_instruction; - dynaOpTable31[i] = Jit64::unknown_instruction; - dynaOpTable63[i] = Jit64::unknown_instruction; - m_infoTable4[i] = 0; - m_infoTable19[i] = 0; - m_infoTable31[i] = 0; - m_infoTable63[i] = 0; - } - - for (int i = 0; i < (int)(sizeof(primarytable) / sizeof(GekkoOPTemplate)); i++) - { - Interpreter::m_opTable[primarytable[i].opcode] = primarytable[i].interpret; - dynaOpTable[primarytable[i].opcode] = primarytable[i].recompile; - m_infoTable[primarytable[i].opcode] = &primarytable[i].opinfo; - } - - for (int i = 0; i < 32; i++) - { - int fill = i << 5; - for (int j = 0; j < (int)(sizeof(table4_2) / sizeof(GekkoOPTemplate)); j++) - { - int op = fill+table4_2[j].opcode; - Interpreter::m_opTable4[op] = table4_2[j].interpret; - dynaOpTable4[op] = table4_2[j].recompile; - m_infoTable4[op] = &table4_2[j].opinfo; - } - } - - for (int i = 0; i < 16; i++) - { - int fill = i << 6; - for (int j = 0; j < (int)(sizeof(table4_3) / sizeof(GekkoOPTemplate)); j++) - { - int op = fill+table4_3[j].opcode; - Interpreter::m_opTable4[op] = table4_3[j].interpret; - dynaOpTable4[op] = table4_3[j].recompile; - m_infoTable4[op] = &table4_3[j].opinfo; - } - } - - for (int i = 0; i < (int)(sizeof(table4) / sizeof(GekkoOPTemplate)); i++) - { - int op = table4[i].opcode; - Interpreter::m_opTable4[op] = table4[i].interpret; - dynaOpTable4[op] = table4[i].recompile; - m_infoTable4[op] = &table4[i].opinfo; - } - - for (int i = 0; i < (int)(sizeof(table31) / sizeof(GekkoOPTemplate)); i++) - { - int op = table31[i].opcode; - Interpreter::m_opTable31[op] = table31[i].interpret; - dynaOpTable31[op] = table31[i].recompile; - m_infoTable31[op] = &table31[i].opinfo; - } - - for (int i = 0; i < 1; i++) - { - int fill = i << 9; - for (int j = 0; j < (int)(sizeof(table31_2) / sizeof(GekkoOPTemplate)); j++) - { - int op = fill + table31_2[j].opcode; - Interpreter::m_opTable31[op] = table31_2[j].interpret; - dynaOpTable31[op] = table31_2[j].recompile; - m_infoTable31[op] = &table31_2[j].opinfo; - } - } - - for (int i = 0; i < (int)(sizeof(table19) / sizeof(GekkoOPTemplate)); i++) - { - int op = table19[i].opcode; - Interpreter::m_opTable19[op] = table19[i].interpret; - dynaOpTable19[op] = table19[i].recompile; - m_infoTable19[op] = &table19[i].opinfo; - } - - for (int i = 0; i < (int)(sizeof(table59) / sizeof(GekkoOPTemplate)); i++) - { - int op = table59[i].opcode; - Interpreter::m_opTable59[op] = table59[i].interpret; - dynaOpTable59[op] = table59[i].recompile; - m_infoTable59[op] = &table59[i].opinfo; - } - - for (int i = 0; i < (int)(sizeof(table63) / sizeof(GekkoOPTemplate)); i++) - { - int op = table63[i].opcode; - Interpreter::m_opTable63[op] = table63[i].interpret; - dynaOpTable63[op] = table63[i].recompile; - m_infoTable63[op] = &table63[i].opinfo; - } - - for (int i = 0; i < 32; i++) - { - int fill = i << 5; - for (int j = 0; j < (int)(sizeof(table63_2) / sizeof(GekkoOPTemplate)); j++) - { - int op = fill + table63_2[j].opcode; - Interpreter::m_opTable63[op] = table63_2[j].interpret; - dynaOpTable63[op] = table63_2[j].recompile; - m_infoTable63[op] = &table63_2[j].opinfo; - } - } - - for (int i = 0; i < (int)(sizeof(primarytable) / sizeof(GekkoOPTemplate)); i++) - m_allInstructions[m_numInstructions++] = &primarytable[i].opinfo; - for (int i = 0; i < (int)(sizeof(table4_2) / sizeof(GekkoOPTemplate)); i++) - m_allInstructions[m_numInstructions++] = &table4_2[i].opinfo; - for (int i = 0; i < (int)(sizeof(table4_3) / sizeof(GekkoOPTemplate)); i++) - m_allInstructions[m_numInstructions++] = &table4_3[i].opinfo; - for (int i = 0; i < (int)(sizeof(table4) / sizeof(GekkoOPTemplate)); i++) - m_allInstructions[m_numInstructions++] = &table4[i].opinfo; - for (int i = 0; i < (int)(sizeof(table31) / sizeof(GekkoOPTemplate)); i++) - m_allInstructions[m_numInstructions++] = &table31[i].opinfo; - for (int i = 0; i < (int)(sizeof(table31_2) / sizeof(GekkoOPTemplate)); i++) - m_allInstructions[m_numInstructions++] = &table31_2[i].opinfo; - for (int i = 0; i < (int)(sizeof(table19) / sizeof(GekkoOPTemplate)); i++) - m_allInstructions[m_numInstructions++] = &table19[i].opinfo; - for (int i = 0; i < (int)(sizeof(table59) / sizeof(GekkoOPTemplate)); i++) - m_allInstructions[m_numInstructions++] = &table59[i].opinfo; - for (int i = 0; i < (int)(sizeof(table63) / sizeof(GekkoOPTemplate)); i++) - m_allInstructions[m_numInstructions++] = &table63[i].opinfo; - for (int i = 0; i < (int)(sizeof(table63_2) / sizeof(GekkoOPTemplate)); i++) - m_allInstructions[m_numInstructions++] = &table63_2[i].opinfo; - if (m_numInstructions >= 2048) { - PanicAlert("m_allInstructions underdimensioned"); - } -} - -namespace { - std::vector rsplocations; -} - -void PPCTables::CompileInstruction(UGeckoInstruction _inst) -{ - dynaOpTable[_inst.OPCD](_inst); - GekkoOPInfo *info = GetOpInfo(_inst); - if (info) { - if (!strcmp(info->opname, "mcrfs")) { - rsplocations.push_back(Jit64::js.compilerPC); - } - info->compileCount++; - info->lastUse = Jit64::js.compilerPC; - } -} - -bool PPCTables::IsValidInstruction(UGeckoInstruction _instCode) -{ - const GekkoOPInfo *info = GetOpInfo(_instCode); - return info != 0; -} - -void PPCTables::CountInstruction(UGeckoInstruction _inst) -{ - GekkoOPInfo *info = GetOpInfo(_inst); - if (info) - info->runCount++; -} - -struct inf -{ - const char *name; - int count; - bool operator < (const inf &o) const - { - return count > o.count; - } -}; - -void PPCTables::PrintInstructionRunCounts() -{ - std::vector temp; - - for (int i = 0; i < m_numInstructions; i++) - { - inf x; - x.name = m_allInstructions[i]->opname; - x.count = m_allInstructions[i]->runCount; - temp.push_back(x); - } - std::sort(temp.begin(), temp.end()); - - for (int i = 0; i < m_numInstructions; i++) - { - LOG(GEKKO, "%s : %i", temp[i].name,temp[i].count); - } -} - -//TODO move to LogManager -void PPCTables::LogCompiledInstructions() -{ - static int time = 0; - FILE *f = fopen(StringFromFormat(FULL_LOGS_DIR "inst_log%i.txt", time).c_str(), "w"); - for (int i = 0; i < m_numInstructions; i++) - { - if (m_allInstructions[i]->compileCount > 0) { - fprintf(f, "%s\t%i\t%i\t%08x\n", m_allInstructions[i]->opname, m_allInstructions[i]->compileCount, m_allInstructions[i]->runCount, m_allInstructions[i]->lastUse); - } - } - fclose(f); - f = fopen(StringFromFormat(FULL_LOGS_DIR "inst_not%i.txt", time).c_str(), "w"); - for (int i = 0; i < m_numInstructions; i++) - { - if (m_allInstructions[i]->compileCount == 0) { - fprintf(f, "%s\t%i\t%i\n", m_allInstructions[i]->opname, m_allInstructions[i]->compileCount, m_allInstructions[i]->runCount); - } - } - fclose(f); - f = fopen(StringFromFormat(FULL_LOGS_DIR "mcrfs_at.txt", time).c_str(), "w"); - for (size_t i = 0; i < rsplocations.size(); i++) { - fprintf(f, "mcrfs: %08x\n", rsplocations[i]); - } - fclose(f); - time++; -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "Common.h" +#include "PPCTables.h" +#include "StringUtil.h" +#include "Interpreter/Interpreter.h" + +#if defined(_M_IX86) || defined(_M_X64) +#include "Jit64/Jit.h" +#include "Jit64/JitCache.h" +#else +#error Unknown architecture! +#endif + +typedef void (*_recompilerInstruction) (UGeckoInstruction instCode); + +struct GekkoOPTemplate +{ + int opcode; + PPCTables::_interpreterInstruction interpret; + PPCTables::_recompilerInstruction recompile; + + GekkoOPInfo opinfo; + int runCount; +}; + +static GekkoOPInfo *m_infoTable[64]; +static GekkoOPInfo *m_infoTable4[1024]; +static GekkoOPInfo *m_infoTable19[1024]; +static GekkoOPInfo *m_infoTable31[1024]; +static GekkoOPInfo *m_infoTable59[32]; +static GekkoOPInfo *m_infoTable63[1024]; + +static _recompilerInstruction dynaOpTable[64]; +static _recompilerInstruction dynaOpTable4[1024]; +static _recompilerInstruction dynaOpTable19[1024]; +static _recompilerInstruction dynaOpTable31[1024]; +static _recompilerInstruction dynaOpTable59[32]; +static _recompilerInstruction dynaOpTable63[1024]; + +void DynaRunTable4(UGeckoInstruction _inst) {dynaOpTable4 [_inst.SUBOP10](_inst);} +void DynaRunTable19(UGeckoInstruction _inst) {dynaOpTable19[_inst.SUBOP10](_inst);} +void DynaRunTable31(UGeckoInstruction _inst) {dynaOpTable31[_inst.SUBOP10](_inst);} +void DynaRunTable59(UGeckoInstruction _inst) {dynaOpTable59[_inst.SUBOP5 ](_inst);} +void DynaRunTable63(UGeckoInstruction _inst) {dynaOpTable63[_inst.SUBOP10](_inst);} + +static GekkoOPInfo *m_allInstructions[2048]; +static int m_numInstructions; + +GekkoOPInfo *GetOpInfo(UGeckoInstruction _inst) +{ + GekkoOPInfo *info = m_infoTable[_inst.OPCD]; + if ((info->type & 0xFFFFFF) == OPTYPE_SUBTABLE) + { + int table = info->type>>24; + switch(table) + { + case 4: return m_infoTable4[_inst.SUBOP10]; + case 19: return m_infoTable19[_inst.SUBOP10]; + case 31: return m_infoTable31[_inst.SUBOP10]; + case 59: return m_infoTable59[_inst.SUBOP5]; + case 63: return m_infoTable63[_inst.SUBOP10]; + default: + _assert_msg_(GEKKO,0,"GetOpInfo - invalid subtable op %08x @ %08x", _inst, PC); + return 0; + } + } + else + { + if ((info->type & 0xFFFFFF) == OPTYPE_INVALID) + { + _assert_msg_(GEKKO,0,"GetOpInfo - invalid op %08x @ %08x", _inst, PC); + return 0; + } + return m_infoTable[_inst.OPCD]; + } +} + +Interpreter::_interpreterInstruction GetInterpreterOp(UGeckoInstruction _inst) +{ + const GekkoOPInfo *info = m_infoTable[_inst.OPCD]; + if ((info->type & 0xFFFFFF) == OPTYPE_SUBTABLE) + { + int table = info->type>>24; + switch(table) + { + case 4: return Interpreter::m_opTable4[_inst.SUBOP10]; + case 19: return Interpreter::m_opTable19[_inst.SUBOP10]; + case 31: return Interpreter::m_opTable31[_inst.SUBOP10]; + case 59: return Interpreter::m_opTable59[_inst.SUBOP5]; + case 63: return Interpreter::m_opTable63[_inst.SUBOP10]; + default: + _assert_msg_(GEKKO,0,"GetInterpreterOp - invalid subtable op %08x @ %08x", _inst, PC); + return 0; + } + } + else + { + if ((info->type & 0xFFFFFF) == OPTYPE_INVALID) + { + _assert_msg_(GEKKO,0,"GetInterpreterOp - invalid op %08x @ %08x", _inst, PC); + return 0; + } + return Interpreter::m_opTable[_inst.OPCD]; + } +} + + +GekkoOPTemplate primarytable[] = +{ + {4, Interpreter::RunTable4, DynaRunTable4, {"RunTable4", OPTYPE_SUBTABLE | (4<<24), 0}}, + {19, Interpreter::RunTable19, DynaRunTable19, {"RunTable19", OPTYPE_SUBTABLE | (19<<24), 0}}, + {31, Interpreter::RunTable31, DynaRunTable31, {"RunTable31", OPTYPE_SUBTABLE | (31<<24), 0}}, + {59, Interpreter::RunTable59, DynaRunTable59, {"RunTable59", OPTYPE_SUBTABLE | (59<<24), 0}}, + {63, Interpreter::RunTable63, DynaRunTable63, {"RunTable63", OPTYPE_SUBTABLE | (63<<24), 0}}, + + {16, Interpreter::bcx, Jit64::bcx, {"bcx", OPTYPE_SYSTEM, FL_ENDBLOCK}}, + {18, Interpreter::bx, Jit64::bx, {"bx", OPTYPE_SYSTEM, FL_ENDBLOCK}}, + + {1, Interpreter::HLEFunction, Jit64::HLEFunction, {"HLEFunction", OPTYPE_SYSTEM, FL_ENDBLOCK}}, + {2, Interpreter::CompiledBlock, Jit64::Default, {"DynaBlock", OPTYPE_SYSTEM, 0}}, + {3, Interpreter::twi, Jit64::Default, {"twi", OPTYPE_SYSTEM, 0}}, + {17, Interpreter::sc, Jit64::sc, {"sc", OPTYPE_SYSTEM, FL_ENDBLOCK, 1}}, + + {7, Interpreter::mulli, Jit64::mulli, {"mulli", OPTYPE_INTEGER, FL_RC_BIT, 2}}, + {8, Interpreter::subfic, Jit64::subfic, {"subfic", OPTYPE_INTEGER, FL_SET_CA}}, + {10, Interpreter::cmpli, Jit64::cmpli, {"cmpli", OPTYPE_INTEGER, FL_SET_CRn}}, + {11, Interpreter::cmpi, Jit64::cmpi, {"cmpi", OPTYPE_INTEGER, FL_SET_CRn}}, + {12, Interpreter::addic, Jit64::reg_imm, {"addic", OPTYPE_INTEGER, FL_SET_CA}}, + {13, Interpreter::addic_rc, Jit64::reg_imm, {"addic_rc", OPTYPE_INTEGER, FL_SET_CR0}}, + {14, Interpreter::addi, Jit64::reg_imm, {"addi", OPTYPE_INTEGER, 0}}, + {15, Interpreter::addis, Jit64::reg_imm, {"addis", OPTYPE_INTEGER, 0}}, + + {20, Interpreter::rlwimix, Jit64::rlwimix, {"rlwimix", OPTYPE_INTEGER, FL_RC_BIT}}, + {21, Interpreter::rlwinmx, Jit64::rlwinmx, {"rlwinmx", OPTYPE_INTEGER, FL_RC_BIT}}, + {23, Interpreter::rlwnmx, Jit64::rlwnmx, {"rlwnmx", OPTYPE_INTEGER, FL_RC_BIT}}, + + {24, Interpreter::ori, Jit64::reg_imm, {"ori", OPTYPE_INTEGER, 0}}, + {25, Interpreter::oris, Jit64::reg_imm, {"oris", OPTYPE_INTEGER, 0}}, + {26, Interpreter::xori, Jit64::reg_imm, {"xori", OPTYPE_INTEGER, 0}}, + {27, Interpreter::xoris, Jit64::reg_imm, {"xoris", OPTYPE_INTEGER, 0}}, + {28, Interpreter::andi_rc, Jit64::reg_imm, {"andi_rc", OPTYPE_INTEGER, FL_SET_CR0}}, + {29, Interpreter::andis_rc, Jit64::reg_imm, {"andis_rc", OPTYPE_INTEGER, FL_SET_CR0}}, + + {32, Interpreter::lwz, Jit64::lXz, {"lwz", OPTYPE_LOAD, 0}}, + {33, Interpreter::lwzu, Jit64::Default, {"lwzu", OPTYPE_LOAD, 0}}, + {34, Interpreter::lbz, Jit64::lXz, {"lbz", OPTYPE_LOAD, 0}}, + {35, Interpreter::lbzu, Jit64::Default, {"lbzu", OPTYPE_LOAD, 0}}, + {40, Interpreter::lhz, Jit64::lXz, {"lhz", OPTYPE_LOAD, 0}}, + {41, Interpreter::lhzu, Jit64::Default, {"lhzu", OPTYPE_LOAD, 0}}, + {42, Interpreter::lha, Jit64::lha, {"lha", OPTYPE_LOAD, 0}}, + {43, Interpreter::lhau, Jit64::Default, {"lhau", OPTYPE_LOAD, 0}}, + + {44, Interpreter::sth, Jit64::stX, {"sth", OPTYPE_STORE, 0}}, + {45, Interpreter::sthu, Jit64::stX, {"sthu", OPTYPE_STORE, 0}}, + {36, Interpreter::stw, Jit64::stX, {"stw", OPTYPE_STORE, 0}}, + {37, Interpreter::stwu, Jit64::stX, {"stwu", OPTYPE_STORE, 0}}, + {38, Interpreter::stb, Jit64::stX, {"stb", OPTYPE_STORE, 0}}, + {39, Interpreter::stbu, Jit64::stX, {"stbu", OPTYPE_STORE, 0}}, + + {46, Interpreter::lmw, Jit64::lmw, {"lmw", OPTYPE_SYSTEM, 0, 10}}, + {47, Interpreter::stmw, Jit64::stmw, {"stmw", OPTYPE_SYSTEM, 0, 10}}, + + {48, Interpreter::lfs, Jit64::lfs, {"lfs", OPTYPE_LOADFP, 0}}, + {49, Interpreter::lfsu, Jit64::Default, {"lfsu", OPTYPE_LOADFP, 0}}, + {50, Interpreter::lfd, Jit64::lfd, {"lfd", OPTYPE_LOADFP, 0}}, + {51, Interpreter::lfdu, Jit64::Default, {"lfdu", OPTYPE_LOADFP, 0}}, + + {52, Interpreter::stfs, Jit64::stfs, {"stfs", OPTYPE_STOREFP, 0}}, + {53, Interpreter::stfsu, Jit64::stfs, {"stfsu", OPTYPE_STOREFP, 0}}, + {54, Interpreter::stfd, Jit64::stfd, {"stfd", OPTYPE_STOREFP, 0}}, + {55, Interpreter::stfdu, Jit64::Default, {"stfdu", OPTYPE_STOREFP, 0}}, + + {56, Interpreter::psq_l, Jit64::psq_l, {"psq_l", OPTYPE_PS, 0}}, + {57, Interpreter::psq_lu, Jit64::psq_l, {"psq_lu", OPTYPE_PS, 0}}, + {60, Interpreter::psq_st, Jit64::psq_st, {"psq_st", OPTYPE_PS, 0}}, + {61, Interpreter::psq_stu, Jit64::psq_st, {"psq_stu", OPTYPE_PS, 0}}, + + //missing: 0, 5, 6, 9, 22, 30, 62, 58 + {0, Interpreter::unknown_instruction, Jit64::Default, {"unknown_instruction", OPTYPE_UNKNOWN, 0}}, + {5, Interpreter::unknown_instruction, Jit64::Default, {"unknown_instruction", OPTYPE_UNKNOWN, 0}}, + {6, Interpreter::unknown_instruction, Jit64::Default, {"unknown_instruction", OPTYPE_UNKNOWN, 0}}, + {9, Interpreter::unknown_instruction, Jit64::Default, {"unknown_instruction", OPTYPE_UNKNOWN, 0}}, + {22, Interpreter::unknown_instruction, Jit64::Default, {"unknown_instruction", OPTYPE_UNKNOWN, 0}}, + {30, Interpreter::unknown_instruction, Jit64::Default, {"unknown_instruction", OPTYPE_UNKNOWN, 0}}, + {62, Interpreter::unknown_instruction, Jit64::Default, {"unknown_instruction", OPTYPE_UNKNOWN, 0}}, + {58, Interpreter::unknown_instruction, Jit64::Default, {"unknown_instruction", OPTYPE_UNKNOWN, 0}}, +}; + +GekkoOPTemplate table4[] = +{ + {0, Interpreter::ps_cmpu0, Jit64::Default, {"ps_cmpu0", OPTYPE_PS, FL_SET_CRn}}, + {32, Interpreter::ps_cmpo0, Jit64::Default, {"ps_cmpo0", OPTYPE_PS, FL_SET_CRn}}, + {40, Interpreter::ps_neg, Jit64::ps_sign, {"ps_neg", OPTYPE_PS, FL_RC_BIT}}, + {136, Interpreter::ps_nabs, Jit64::ps_sign, {"ps_nabs", OPTYPE_PS, FL_RC_BIT}}, + {264, Interpreter::ps_abs, Jit64::ps_sign, {"ps_abs", OPTYPE_PS, FL_RC_BIT}}, + {64, Interpreter::ps_cmpu1, Jit64::Default, {"ps_cmpu1", OPTYPE_PS, FL_RC_BIT}}, + {72, Interpreter::ps_mr, Jit64::ps_mr, {"ps_mr", OPTYPE_PS, FL_RC_BIT}}, + {96, Interpreter::ps_cmpo1, Jit64::Default, {"ps_cmpo1", OPTYPE_PS, FL_RC_BIT}}, + {528, Interpreter::ps_merge00, Jit64::ps_mergeXX, {"ps_merge00", OPTYPE_PS, FL_RC_BIT}}, + {560, Interpreter::ps_merge01, Jit64::ps_mergeXX, {"ps_merge01", OPTYPE_PS, FL_RC_BIT}}, + {592, Interpreter::ps_merge10, Jit64::ps_mergeXX, {"ps_merge10", OPTYPE_PS, FL_RC_BIT}}, + {624, Interpreter::ps_merge11, Jit64::ps_mergeXX, {"ps_merge11", OPTYPE_PS, FL_RC_BIT}}, + + {1014, Interpreter::dcbz_l, Jit64::Default, {"dcbz_l", OPTYPE_SYSTEM, 0}}, +}; + +GekkoOPTemplate table4_2[] = +{ + {10, Interpreter::ps_sum0, Jit64::ps_sum, {"ps_sum0", OPTYPE_PS, 0}}, + {11, Interpreter::ps_sum1, Jit64::ps_sum, {"ps_sum1", OPTYPE_PS, 0}}, + {12, Interpreter::ps_muls0, Jit64::ps_muls, {"ps_muls0", OPTYPE_PS, 0}}, + {13, Interpreter::ps_muls1, Jit64::ps_muls, {"ps_muls1", OPTYPE_PS, 0}}, + {14, Interpreter::ps_madds0, Jit64::Default, {"ps_madds0", OPTYPE_PS, 0}}, + {15, Interpreter::ps_madds1, Jit64::Default, {"ps_madds1", OPTYPE_PS, 0}}, + {18, Interpreter::ps_div, Jit64::ps_arith, {"ps_div", OPTYPE_PS, 0, 16}}, + {20, Interpreter::ps_sub, Jit64::ps_arith, {"ps_sub", OPTYPE_PS, 0}}, + {21, Interpreter::ps_add, Jit64::ps_arith, {"ps_add", OPTYPE_PS, 0}}, + {23, Interpreter::ps_sel, Jit64::ps_sel, {"ps_sel", OPTYPE_PS, 0}}, + {24, Interpreter::ps_res, Jit64::Default, {"ps_res", OPTYPE_PS, 0}}, + {25, Interpreter::ps_mul, Jit64::ps_arith, {"ps_mul", OPTYPE_PS, 0}}, + {26, Interpreter::ps_rsqrte, Jit64::ps_rsqrte, {"ps_rsqrte", OPTYPE_PS, 0, 1}}, + {28, Interpreter::ps_msub, Jit64::ps_maddXX, {"ps_msub", OPTYPE_PS, 0}}, + {29, Interpreter::ps_madd, Jit64::ps_maddXX, {"ps_madd", OPTYPE_PS, 0}}, + {30, Interpreter::ps_nmsub, Jit64::ps_maddXX, {"ps_nmsub", OPTYPE_PS, 0}}, + {31, Interpreter::ps_nmadd, Jit64::ps_maddXX, {"ps_nmadd", OPTYPE_PS, 0}}, +}; + +GekkoOPTemplate table4_3[] = +{ + {6, Interpreter::psq_lx, Jit64::Default, {"psq_lx", OPTYPE_PS, 0}}, + {7, Interpreter::psq_stx, Jit64::Default, {"psq_stx", OPTYPE_PS, 0}}, + {38, Interpreter::psq_lux, Jit64::Default, {"psq_lux", OPTYPE_PS, 0}}, + {39, Interpreter::psq_stux, Jit64::Default, {"psq_stux", OPTYPE_PS, 0}}, +}; + +GekkoOPTemplate table19[] = +{ + {528, Interpreter::bcctrx, Jit64::bcctrx, {"bcctrx", OPTYPE_BRANCH, FL_ENDBLOCK}}, + {16, Interpreter::bclrx, Jit64::bclrx, {"bclrx", OPTYPE_BRANCH, FL_ENDBLOCK}}, + {257, Interpreter::crand, Jit64::Default, {"crand", OPTYPE_CR, 0}}, + {129, Interpreter::crandc, Jit64::Default, {"crandc", OPTYPE_CR, 0}}, + {289, Interpreter::creqv, Jit64::Default, {"creqv", OPTYPE_CR, 0}}, + {225, Interpreter::crnand, Jit64::Default, {"crnand", OPTYPE_CR, 0}}, + {33, Interpreter::crnor, Jit64::Default, {"crnor", OPTYPE_CR, 0}}, + {449, Interpreter::cror, Jit64::Default, {"cror", OPTYPE_CR, 0}}, + {417, Interpreter::crorc, Jit64::Default, {"crorc", OPTYPE_CR, 0}}, + {193, Interpreter::crxor, Jit64::Default, {"crxor", OPTYPE_CR, 0}}, + + {150, Interpreter::isync, Jit64::DoNothing, {"isync", OPTYPE_ICACHE, 0 }}, + {0, Interpreter::mcrf, Jit64::Default, {"mcrf", OPTYPE_SYSTEM, 0}}, + + {50, Interpreter::rfi, Jit64::rfi, {"rfi", OPTYPE_SYSTEM, FL_ENDBLOCK | FL_CHECKEXCEPTIONS, 1}}, + {18, Interpreter::rfid, Jit64::Default, {"rfid", OPTYPE_SYSTEM, FL_ENDBLOCK | FL_CHECKEXCEPTIONS}} +}; + + +GekkoOPTemplate table31[] = +{ + {28, Interpreter::andx, Jit64::andx, {"andx", OPTYPE_INTEGER, FL_IN_AB | FL_OUT_S | FL_RC_BIT}}, + {60, Interpreter::andcx, Jit64::Default, {"andcx", OPTYPE_INTEGER, FL_IN_AB | FL_OUT_S | FL_RC_BIT}}, + {444, Interpreter::orx, Jit64::orx, {"orx", OPTYPE_INTEGER, FL_IN_AB | FL_OUT_S | FL_RC_BIT}}, + {124, Interpreter::norx, Jit64::Default, {"norx", OPTYPE_INTEGER, FL_IN_AB | FL_OUT_S | FL_RC_BIT}}, + {316, Interpreter::xorx, Jit64::xorx, {"xorx", OPTYPE_INTEGER, FL_IN_AB | FL_OUT_S | FL_RC_BIT}}, + {412, Interpreter::orcx, Jit64::Default, {"orcx", OPTYPE_INTEGER, FL_IN_AB | FL_OUT_S | FL_RC_BIT}}, + {476, Interpreter::nandx, Jit64::Default, {"nandx", OPTYPE_INTEGER, FL_IN_AB | FL_OUT_S | FL_RC_BIT}}, + {284, Interpreter::eqvx, Jit64::Default, {"eqvx", OPTYPE_INTEGER, FL_IN_AB | FL_OUT_S | FL_RC_BIT}}, + {0, Interpreter::cmp, Jit64::cmp, {"cmp", OPTYPE_INTEGER, FL_IN_AB | FL_SET_CRn}}, + {32, Interpreter::cmpl, Jit64::cmpl, {"cmpl", OPTYPE_INTEGER, FL_IN_AB | FL_SET_CRn}}, + {26, Interpreter::cntlzwx, Jit64::cntlzwx, {"cntlzwx",OPTYPE_INTEGER, FL_IN_A | FL_OUT_S | FL_RC_BIT}}, + {922, Interpreter::extshx, Jit64::extshx, {"extshx", OPTYPE_INTEGER, FL_IN_A | FL_OUT_S | FL_RC_BIT}}, + {954, Interpreter::extsbx, Jit64::extsbx, {"extsbx", OPTYPE_INTEGER, FL_IN_A | FL_OUT_S | FL_RC_BIT}}, + {536, Interpreter::srwx, Jit64::srwx, {"srwx", OPTYPE_INTEGER, FL_RC_BIT}}, + {792, Interpreter::srawx, Jit64::srawx, {"srawx", OPTYPE_INTEGER, FL_RC_BIT}}, + {824, Interpreter::srawix, Jit64::srawix, {"srawix", OPTYPE_INTEGER, FL_RC_BIT}}, + {24, Interpreter::slwx, Jit64::slwx, {"slwx", OPTYPE_INTEGER, FL_RC_BIT}}, + + {54, Interpreter::dcbst, Jit64::Default, {"dcbst", OPTYPE_DCACHE, 0, 4}}, + {86, Interpreter::dcbf, Jit64::Default, {"dcbf", OPTYPE_DCACHE, 0, 4}}, + {246, Interpreter::dcbtst, Jit64::Default, {"dcbtst", OPTYPE_DCACHE, 0, 1}}, + {278, Interpreter::dcbt, Jit64::Default, {"dcbt", OPTYPE_DCACHE, 0, 1}}, + {470, Interpreter::dcbi, Jit64::Default, {"dcbi", OPTYPE_DCACHE, 0, 4}}, + {758, Interpreter::dcba, Jit64::Default, {"dcba", OPTYPE_DCACHE, 0, 4}}, + {1014, Interpreter::dcbz, Jit64::dcbz, {"dcbz", OPTYPE_DCACHE, 0, 4}}, + + //load word + {23, Interpreter::lwzx, Jit64::lwzx, {"lwzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, + {55, Interpreter::lwzux, Jit64::Default, {"lwzux", OPTYPE_LOAD, FL_OUT_D | FL_IN_A | FL_IN_B}}, + + //load halfword + {279, Interpreter::lhzx, Jit64::Default, {"lhzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, + {311, Interpreter::lhzux, Jit64::Default, {"lhzux", OPTYPE_LOAD, FL_OUT_D | FL_IN_A | FL_IN_B}}, + + //load halfword signextend + {343, Interpreter::lhax, Jit64::lhax, {"lhax", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, + {375, Interpreter::lhaux, Jit64::Default, {"lhaux", OPTYPE_LOAD, FL_OUT_D | FL_IN_A | FL_IN_B}}, + + //load byte + {87, Interpreter::lbzx, Jit64::lbzx, {"lbzx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, + {119, Interpreter::lbzux, Jit64::Default, {"lbzux", OPTYPE_LOAD, FL_OUT_D | FL_IN_A | FL_IN_B}}, + + //load byte reverse + {534, Interpreter::lwbrx, Jit64::Default, {"lwbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, + {790, Interpreter::lhbrx, Jit64::Default, {"lhbrx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0 | FL_IN_B}}, + + // Conditional load/store (Wii SMP) + {150, Interpreter::stwcxd, Jit64::Default, {"stwcxd", OPTYPE_STORE, 0}}, + {20, Interpreter::lwarx, Jit64::Default, {"lwarx", OPTYPE_LOAD, FL_OUT_D | FL_IN_A0B}}, + + //load string (interpret these) + {533, Interpreter::lswx, Jit64::Default, {"lswx", OPTYPE_LOAD, FL_IN_A | FL_OUT_D}}, + {597, Interpreter::lswi, Jit64::Default, {"lswi", OPTYPE_LOAD, FL_IN_AB | FL_OUT_D}}, + + //store word + {151, Interpreter::stwx, Jit64::Default, {"stwx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}}, + {183, Interpreter::stwux, Jit64::Default, {"stwux", OPTYPE_STORE, FL_IN_A | FL_IN_B}}, + + //store halfword + {407, Interpreter::sthx, Jit64::Default, {"sthx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}}, + {439, Interpreter::sthux, Jit64::Default, {"sthux", OPTYPE_STORE, FL_IN_A | FL_IN_B}}, + + //store byte + {215, Interpreter::stbx, Jit64::Default, {"stbx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}}, + {247, Interpreter::stbux, Jit64::Default, {"stbux", OPTYPE_STORE, FL_IN_A | FL_IN_B}}, + + //store bytereverse + {662, Interpreter::stwbrx, Jit64::Default, {"stwbrx", OPTYPE_STORE, FL_IN_A0 | FL_IN_B}}, + {918, Interpreter::sthbrx, Jit64::Default, {"sthbrx", OPTYPE_STORE, FL_IN_A | FL_IN_B}}, + + {661, Interpreter::stswx, Jit64::Default, {"stswx", OPTYPE_STORE, 0}}, + {725, Interpreter::stswi, Jit64::Default, {"stswi", OPTYPE_STORE, 0}}, + + // fp load/store + {535, Interpreter::lfsx, Jit64::lfsx, {"lfsx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B}}, + {567, Interpreter::lfsux, Jit64::Default, {"lfsux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B}}, + {599, Interpreter::lfdx, Jit64::Default, {"lfdx", OPTYPE_LOADFP, FL_IN_A0 | FL_IN_B}}, + {631, Interpreter::lfdux, Jit64::Default, {"lfdux", OPTYPE_LOADFP, FL_IN_A | FL_IN_B}}, + + {663, Interpreter::stfsx, Jit64::Default, {"stfsx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}}, + {695, Interpreter::stfsux, Jit64::Default, {"stfsux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B}}, + {727, Interpreter::stfdx, Jit64::Default, {"stfdx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}}, + {759, Interpreter::stfdux, Jit64::Default, {"stfdux", OPTYPE_STOREFP, FL_IN_A | FL_IN_B}}, + {983, Interpreter::stfiwx, Jit64::Default, {"stfiwx", OPTYPE_STOREFP, FL_IN_A0 | FL_IN_B}}, + + {19, Interpreter::mfcr, Jit64::mfcr, {"mfcr", OPTYPE_SYSTEM, 0}}, + {83, Interpreter::mfmsr, Jit64::mfmsr, {"mfmsr", OPTYPE_SYSTEM, 0}}, + {144, Interpreter::mtcrf, Jit64::mtcrf, {"mtcrf", OPTYPE_SYSTEM, 0}}, + {146, Interpreter::mtmsr, Jit64::mtmsr, {"mtmsr", OPTYPE_SYSTEM, FL_ENDBLOCK}}, + {210, Interpreter::mtsr, Jit64::Default, {"mtsr", OPTYPE_SYSTEM, 0}}, + {242, Interpreter::mtsrin, Jit64::Default, {"mtsrin", OPTYPE_SYSTEM, 0}}, + {339, Interpreter::mfspr, Jit64::mfspr, {"mfspr", OPTYPE_SPR, 0}}, + {467, Interpreter::mtspr, Jit64::mtspr, {"mtspr", OPTYPE_SPR, 0, 2}}, + {371, Interpreter::mftb, Jit64::mftb, {"mftb", OPTYPE_SYSTEM, FL_TIMER}}, + {512, Interpreter::mcrxr, Jit64::Default, {"mcrxr", OPTYPE_SYSTEM, 0}}, + {595, Interpreter::mfsr, Jit64::Default, {"mfsr", OPTYPE_SYSTEM, 0, 2}}, + {659, Interpreter::mfsrin, Jit64::Default, {"mfsrin", OPTYPE_SYSTEM, 0, 2}}, + + {4, Interpreter::tw, Jit64::Default, {"tw", OPTYPE_SYSTEM, 0, 1}}, + {598, Interpreter::sync, Jit64::DoNothing, {"sync", OPTYPE_SYSTEM, 0, 2}}, + {982, Interpreter::icbi, Jit64::Default, {"icbi", OPTYPE_SYSTEM, 0, 3}}, + + // Unused instructions on GC + {310, Interpreter::eciwx, Jit64::Default, {"eciwx", OPTYPE_INTEGER, FL_RC_BIT}}, + {438, Interpreter::ecowx, Jit64::Default, {"ecowx", OPTYPE_INTEGER, FL_RC_BIT}}, + {854, Interpreter::eieio, Jit64::Default, {"eieio", OPTYPE_INTEGER, FL_RC_BIT}}, + {306, Interpreter::tlbie, Jit64::Default, {"tlbie", OPTYPE_SYSTEM, 0}}, + {370, Interpreter::tlbia, Jit64::Default, {"tlbia", OPTYPE_SYSTEM, 0}}, + {566, Interpreter::tlbsync, Jit64::Default, {"tlbsync", OPTYPE_SYSTEM, 0}}, +}; + +GekkoOPTemplate table31_2[] = +{ + {266, Interpreter::addx, Jit64::addx, {"addx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_RC_BIT}}, + {10, Interpreter::addcx, Jit64::Default, {"addcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_SET_CA | FL_RC_BIT}}, + {138, Interpreter::addex, Jit64::addex, {"addex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, + {234, Interpreter::addmex, Jit64::Default, {"addmex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, + {202, Interpreter::addzex, Jit64::Default, {"addzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, + {491, Interpreter::divwx, Jit64::Default, {"divwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_RC_BIT, 39}}, + {459, Interpreter::divwux, Jit64::divwux, {"divwux", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_RC_BIT, 39}}, + {75, Interpreter::mulhwx, Jit64::Default, {"mulhwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_RC_BIT, 4}}, + {11, Interpreter::mulhwux, Jit64::mulhwux, {"mulhwux", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_RC_BIT, 4}}, + {235, Interpreter::mullwx, Jit64::mullwx, {"mullwx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_RC_BIT, 4}}, + {104, Interpreter::negx, Jit64::negx, {"negx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_RC_BIT}}, + {40, Interpreter::subfx, Jit64::subfx, {"subfx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_RC_BIT}}, + {8, Interpreter::subfcx, Jit64::subfcx, {"subfcx", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_SET_CA | FL_RC_BIT}}, + {136, Interpreter::subfex, Jit64::subfex, {"subfex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, + {232, Interpreter::subfmex, Jit64::Default, {"subfmex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, + {200, Interpreter::subfzex, Jit64::Default, {"subfzex", OPTYPE_INTEGER, FL_OUT_D | FL_IN_A | FL_IN_B | FL_READ_CA | FL_SET_CA | FL_RC_BIT}}, +}; + +GekkoOPTemplate table59[] = +{ + {18, Interpreter::fdivsx, Jit64::fp_arith_s, {"fdivsx", OPTYPE_FPU, FL_RC_BIT_F, 16}}, + {20, Interpreter::fsubsx, Jit64::fp_arith_s, {"fsubsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {21, Interpreter::faddsx, Jit64::fp_arith_s, {"faddsx", OPTYPE_FPU, FL_RC_BIT_F}}, +// {22, Interpreter::fsqrtsx, Jit64::Default, {"fsqrtsx", OPTYPE_FPU, FL_RC_BIT_F}}, // Not implemented on gekko + {24, Interpreter::fresx, Jit64::Default, {"fresx", OPTYPE_FPU, FL_RC_BIT_F}}, + {25, Interpreter::fmulsx, Jit64::fp_arith_s, {"fmulsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {28, Interpreter::fmsubsx, Jit64::fmaddXX, {"fmsubsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {29, Interpreter::fmaddsx, Jit64::fmaddXX, {"fmaddsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {30, Interpreter::fnmsubsx, Jit64::fmaddXX, {"fnmsubsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {31, Interpreter::fnmaddsx, Jit64::fmaddXX, {"fnmaddsx", OPTYPE_FPU, FL_RC_BIT_F}}, +}; + +GekkoOPTemplate table63[] = +{ + {264, Interpreter::fabsx, Jit64::Default, {"fabsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {32, Interpreter::fcmpo, Jit64::fcmpx, {"fcmpo", OPTYPE_FPU, FL_RC_BIT_F}}, + {0, Interpreter::fcmpu, Jit64::fcmpx, {"fcmpu", OPTYPE_FPU, FL_RC_BIT_F}}, + {14, Interpreter::fctiwx, Jit64::Default, {"fctiwx", OPTYPE_FPU, FL_RC_BIT_F}}, + {15, Interpreter::fctiwzx, Jit64::Default, {"fctiwzx", OPTYPE_FPU, FL_RC_BIT_F}}, + {72, Interpreter::fmrx, Jit64::fmrx, {"fmrx", OPTYPE_FPU, FL_RC_BIT_F}}, + {136, Interpreter::fnabsx, Jit64::Default, {"fnabsx", OPTYPE_FPU, FL_RC_BIT_F}}, + {40, Interpreter::fnegx, Jit64::Default, {"fnegx", OPTYPE_FPU, FL_RC_BIT_F}}, + {12, Interpreter::frspx, Jit64::Default, {"frspx", OPTYPE_FPU, FL_RC_BIT_F}}, + + {64, Interpreter::mcrfs, Jit64::Default, {"mcrfs", OPTYPE_SYSTEMFP, 0}}, + {583, Interpreter::mffsx, Jit64::Default, {"mffsx", OPTYPE_SYSTEMFP, 0}}, + {70, Interpreter::mtfsb0x, Jit64::Default, {"mtfsb0x", OPTYPE_SYSTEMFP, 0, 2}}, + {38, Interpreter::mtfsb1x, Jit64::Default, {"mtfsb1x", OPTYPE_SYSTEMFP, 0, 2}}, + {134, Interpreter::mtfsfix, Jit64::Default, {"mtfsfix", OPTYPE_SYSTEMFP, 0, 2}}, + {711, Interpreter::mtfsfx, Jit64::Default, {"mtfsfx", OPTYPE_SYSTEMFP, 0, 2}}, +}; + +GekkoOPTemplate table63_2[] = +{ + {18, Interpreter::fdivx, Jit64::Default, {"fdivx", OPTYPE_FPU, FL_RC_BIT_F, 30}}, + {20, Interpreter::fsubx, Jit64::Default, {"fsubx", OPTYPE_FPU, FL_RC_BIT_F}}, + {21, Interpreter::faddx, Jit64::Default, {"faddx", OPTYPE_FPU, FL_RC_BIT_F}}, + {22, Interpreter::fsqrtx, Jit64::Default, {"fsqrtx", OPTYPE_FPU, FL_RC_BIT_F}}, + {23, Interpreter::fselx, Jit64::Default, {"fselx", OPTYPE_FPU, FL_RC_BIT_F}}, + {25, Interpreter::fmulx, Jit64::Default, {"fmulx", OPTYPE_FPU, FL_RC_BIT_F}}, + {26, Interpreter::frsqrtex,Jit64::Default, {"frsqrtex", OPTYPE_FPU, FL_RC_BIT_F}}, + {28, Interpreter::fmsubx, Jit64::Default, {"fmsubx", OPTYPE_FPU, FL_RC_BIT_F}}, + {29, Interpreter::fmaddx, Jit64::Default, {"fmaddx", OPTYPE_FPU, FL_RC_BIT_F}}, + {30, Interpreter::fnmsubx, Jit64::Default, {"fnmsubx", OPTYPE_FPU, FL_RC_BIT_F}}, + {31, Interpreter::fnmaddx, Jit64::Default, {"fnmaddx", OPTYPE_FPU, FL_RC_BIT_F}}, +}; + +bool PPCTables::UsesFPU(UGeckoInstruction _inst) +{ + switch (_inst.OPCD) + { + case 04: // PS + return _inst.SUBOP10 != 1014; + + case 48: // lfs + case 49: // lfsu + case 50: // lfd + case 51: // lfdu + case 52: // stfs + case 53: // stfsu + case 54: // stfd + case 55: // stfdu + case 56: // psq_l + case 57: // psq_lu + + case 59: // FPU-sgl + case 60: // psq_st + case 61: // psq_stu + case 63: // FPU-dbl + return true; + + case 31: + switch (_inst.SUBOP10) + { + case 535: + case 567: + case 599: + case 631: + case 663: + case 695: + case 727: + case 759: + case 983: + return true; + default: + return false; + } + default: + return false; + } +} + +void PPCTables::InitTables() +{ + //clear + for (int i = 0; i < 32; i++) + { + Interpreter::m_opTable59[i] = Interpreter::unknown_instruction; + dynaOpTable59[i] = Jit64::unknown_instruction; + m_infoTable59[i] = 0; + } + + for (int i = 0; i < 1024; i++) + { + Interpreter::m_opTable4 [i] = Interpreter::unknown_instruction; + Interpreter::m_opTable19[i] = Interpreter::unknown_instruction; + Interpreter::m_opTable31[i] = Interpreter::unknown_instruction; + Interpreter::m_opTable63[i] = Interpreter::unknown_instruction; + dynaOpTable4 [i] = Jit64::unknown_instruction; + dynaOpTable19[i] = Jit64::unknown_instruction; + dynaOpTable31[i] = Jit64::unknown_instruction; + dynaOpTable63[i] = Jit64::unknown_instruction; + m_infoTable4[i] = 0; + m_infoTable19[i] = 0; + m_infoTable31[i] = 0; + m_infoTable63[i] = 0; + } + + for (int i = 0; i < (int)(sizeof(primarytable) / sizeof(GekkoOPTemplate)); i++) + { + Interpreter::m_opTable[primarytable[i].opcode] = primarytable[i].interpret; + dynaOpTable[primarytable[i].opcode] = primarytable[i].recompile; + m_infoTable[primarytable[i].opcode] = &primarytable[i].opinfo; + } + + for (int i = 0; i < 32; i++) + { + int fill = i << 5; + for (int j = 0; j < (int)(sizeof(table4_2) / sizeof(GekkoOPTemplate)); j++) + { + int op = fill+table4_2[j].opcode; + Interpreter::m_opTable4[op] = table4_2[j].interpret; + dynaOpTable4[op] = table4_2[j].recompile; + m_infoTable4[op] = &table4_2[j].opinfo; + } + } + + for (int i = 0; i < 16; i++) + { + int fill = i << 6; + for (int j = 0; j < (int)(sizeof(table4_3) / sizeof(GekkoOPTemplate)); j++) + { + int op = fill+table4_3[j].opcode; + Interpreter::m_opTable4[op] = table4_3[j].interpret; + dynaOpTable4[op] = table4_3[j].recompile; + m_infoTable4[op] = &table4_3[j].opinfo; + } + } + + for (int i = 0; i < (int)(sizeof(table4) / sizeof(GekkoOPTemplate)); i++) + { + int op = table4[i].opcode; + Interpreter::m_opTable4[op] = table4[i].interpret; + dynaOpTable4[op] = table4[i].recompile; + m_infoTable4[op] = &table4[i].opinfo; + } + + for (int i = 0; i < (int)(sizeof(table31) / sizeof(GekkoOPTemplate)); i++) + { + int op = table31[i].opcode; + Interpreter::m_opTable31[op] = table31[i].interpret; + dynaOpTable31[op] = table31[i].recompile; + m_infoTable31[op] = &table31[i].opinfo; + } + + for (int i = 0; i < 1; i++) + { + int fill = i << 9; + for (int j = 0; j < (int)(sizeof(table31_2) / sizeof(GekkoOPTemplate)); j++) + { + int op = fill + table31_2[j].opcode; + Interpreter::m_opTable31[op] = table31_2[j].interpret; + dynaOpTable31[op] = table31_2[j].recompile; + m_infoTable31[op] = &table31_2[j].opinfo; + } + } + + for (int i = 0; i < (int)(sizeof(table19) / sizeof(GekkoOPTemplate)); i++) + { + int op = table19[i].opcode; + Interpreter::m_opTable19[op] = table19[i].interpret; + dynaOpTable19[op] = table19[i].recompile; + m_infoTable19[op] = &table19[i].opinfo; + } + + for (int i = 0; i < (int)(sizeof(table59) / sizeof(GekkoOPTemplate)); i++) + { + int op = table59[i].opcode; + Interpreter::m_opTable59[op] = table59[i].interpret; + dynaOpTable59[op] = table59[i].recompile; + m_infoTable59[op] = &table59[i].opinfo; + } + + for (int i = 0; i < (int)(sizeof(table63) / sizeof(GekkoOPTemplate)); i++) + { + int op = table63[i].opcode; + Interpreter::m_opTable63[op] = table63[i].interpret; + dynaOpTable63[op] = table63[i].recompile; + m_infoTable63[op] = &table63[i].opinfo; + } + + for (int i = 0; i < 32; i++) + { + int fill = i << 5; + for (int j = 0; j < (int)(sizeof(table63_2) / sizeof(GekkoOPTemplate)); j++) + { + int op = fill + table63_2[j].opcode; + Interpreter::m_opTable63[op] = table63_2[j].interpret; + dynaOpTable63[op] = table63_2[j].recompile; + m_infoTable63[op] = &table63_2[j].opinfo; + } + } + + for (int i = 0; i < (int)(sizeof(primarytable) / sizeof(GekkoOPTemplate)); i++) + m_allInstructions[m_numInstructions++] = &primarytable[i].opinfo; + for (int i = 0; i < (int)(sizeof(table4_2) / sizeof(GekkoOPTemplate)); i++) + m_allInstructions[m_numInstructions++] = &table4_2[i].opinfo; + for (int i = 0; i < (int)(sizeof(table4_3) / sizeof(GekkoOPTemplate)); i++) + m_allInstructions[m_numInstructions++] = &table4_3[i].opinfo; + for (int i = 0; i < (int)(sizeof(table4) / sizeof(GekkoOPTemplate)); i++) + m_allInstructions[m_numInstructions++] = &table4[i].opinfo; + for (int i = 0; i < (int)(sizeof(table31) / sizeof(GekkoOPTemplate)); i++) + m_allInstructions[m_numInstructions++] = &table31[i].opinfo; + for (int i = 0; i < (int)(sizeof(table31_2) / sizeof(GekkoOPTemplate)); i++) + m_allInstructions[m_numInstructions++] = &table31_2[i].opinfo; + for (int i = 0; i < (int)(sizeof(table19) / sizeof(GekkoOPTemplate)); i++) + m_allInstructions[m_numInstructions++] = &table19[i].opinfo; + for (int i = 0; i < (int)(sizeof(table59) / sizeof(GekkoOPTemplate)); i++) + m_allInstructions[m_numInstructions++] = &table59[i].opinfo; + for (int i = 0; i < (int)(sizeof(table63) / sizeof(GekkoOPTemplate)); i++) + m_allInstructions[m_numInstructions++] = &table63[i].opinfo; + for (int i = 0; i < (int)(sizeof(table63_2) / sizeof(GekkoOPTemplate)); i++) + m_allInstructions[m_numInstructions++] = &table63_2[i].opinfo; + if (m_numInstructions >= 2048) { + PanicAlert("m_allInstructions underdimensioned"); + } +} + +namespace { + std::vector rsplocations; +} + +void PPCTables::CompileInstruction(UGeckoInstruction _inst) +{ + dynaOpTable[_inst.OPCD](_inst); + GekkoOPInfo *info = GetOpInfo(_inst); + if (info) { + if (!strcmp(info->opname, "mcrfs")) { + rsplocations.push_back(Jit64::js.compilerPC); + } + info->compileCount++; + info->lastUse = Jit64::js.compilerPC; + } +} + +bool PPCTables::IsValidInstruction(UGeckoInstruction _instCode) +{ + const GekkoOPInfo *info = GetOpInfo(_instCode); + return info != 0; +} + +void PPCTables::CountInstruction(UGeckoInstruction _inst) +{ + GekkoOPInfo *info = GetOpInfo(_inst); + if (info) + info->runCount++; +} + +struct inf +{ + const char *name; + int count; + bool operator < (const inf &o) const + { + return count > o.count; + } +}; + +void PPCTables::PrintInstructionRunCounts() +{ + std::vector temp; + + for (int i = 0; i < m_numInstructions; i++) + { + inf x; + x.name = m_allInstructions[i]->opname; + x.count = m_allInstructions[i]->runCount; + temp.push_back(x); + } + std::sort(temp.begin(), temp.end()); + + for (int i = 0; i < m_numInstructions; i++) + { + LOG(GEKKO, "%s : %i", temp[i].name,temp[i].count); + } +} + +//TODO move to LogManager +void PPCTables::LogCompiledInstructions() +{ + static int time = 0; + FILE *f = fopen(StringFromFormat(FULL_LOGS_DIR "inst_log%i.txt", time).c_str(), "w"); + for (int i = 0; i < m_numInstructions; i++) + { + if (m_allInstructions[i]->compileCount > 0) { + fprintf(f, "%s\t%i\t%i\t%08x\n", m_allInstructions[i]->opname, m_allInstructions[i]->compileCount, m_allInstructions[i]->runCount, m_allInstructions[i]->lastUse); + } + } + fclose(f); + f = fopen(StringFromFormat(FULL_LOGS_DIR "inst_not%i.txt", time).c_str(), "w"); + for (int i = 0; i < m_numInstructions; i++) + { + if (m_allInstructions[i]->compileCount == 0) { + fprintf(f, "%s\t%i\t%i\n", m_allInstructions[i]->opname, m_allInstructions[i]->compileCount, m_allInstructions[i]->runCount); + } + } + fclose(f); + f = fopen(StringFromFormat(FULL_LOGS_DIR "mcrfs_at.txt", time).c_str(), "w"); + for (size_t i = 0; i < rsplocations.size(); i++) { + fprintf(f, "mcrfs: %08x\n", rsplocations[i]); + } + fclose(f); + time++; +} diff --git a/Source/Core/Core/Src/PowerPC/PowerPC.cpp b/Source/Core/Core/Src/PowerPC/PowerPC.cpp index 9e11780cec..fe44e31597 100644 --- a/Source/Core/Core/Src/PowerPC/PowerPC.cpp +++ b/Source/Core/Core/Src/PowerPC/PowerPC.cpp @@ -1,294 +1,294 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include - -#include "Common.h" -#include "ChunkFile.h" - -#include "../HW/Memmap.h" -#include "../HW/CPU.h" -#include "../Core.h" -#include "../CoreTiming.h" - -#include "Interpreter/Interpreter.h" -#include "Jit64/JitCore.h" -#include "Jit64/JitCache.h" -#include "PowerPC.h" -#include "PPCTables.h" - -#include "../Host.h" - -namespace PowerPC -{ - -// STATE_TO_SAVE -PowerPCState GC_ALIGNED16(ppcState); -volatile CPUState state = CPU_STEPPING; - -static CoreMode mode; - -void DoState(PointerWrap &p) -{ - p.Do(ppcState); -} - -void ResetRegisters() -{ - for (int i = 0; i < 32; i++) - { - ppcState.gpr[i] = 0; - riPS0(i) = 0; - riPS1(i) = 0; - } - - memset(ppcState.spr, 0, sizeof(ppcState.spr)); - - ppcState.cr = 0; - ppcState.fpscr = 0; - ppcState.pc = 0; - ppcState.npc = 0; - ppcState.Exceptions = 0; - - TL = 0; - TU = 0; - - ppcState.msr = 0; - rDEC = 0xFFFFFFFF; -} - -void Init() -{ - enum { - FPU_PREC_24 = 0 << 8, - FPU_PREC_53 = 2 << 8, - FPU_PREC_64 = 3 << 8, - FPU_PREC_MASK = 3 << 8, - }; - #ifdef _M_IX86 - // sets the floating-point lib to 53-bit - // PowerPC has a 53bit floating pipeline only - // eg: sscanf is very sensitive -#ifdef _WIN32 - _control87(_PC_53, MCW_PC); -#else - unsigned short _mode; - asm ("fstcw %0" : : "m" (_mode)); - _mode = (_mode & ~FPU_PREC_MASK) | FPU_PREC_53; - asm ("fldcw %0" : : "m" (_mode)); -#endif -#else - //x64 doesn't need this - fpu is done with SSE - //but still - set any useful sse options here -#endif - - ResetRegisters(); - PPCTables::InitTables(); - - // Initialize both execution engines ... - Interpreter::Init(); - Jit64::Core::Init(); - // ... but start as interpreter by default. - mode = MODE_INTERPRETER; - state = CPU_STEPPING; -} - -void Shutdown() -{ - // Shutdown both execution engines. Doesn't matter which one is active. - Jit64::Core::Shutdown(); - Interpreter::Shutdown(); -} - -void SetMode(CoreMode new_mode) -{ - if (new_mode == mode) - return; // We don't need to do anything. - - mode = new_mode; - switch (mode) - { - case MODE_INTERPRETER: // Switching from JIT to interpreter - Jit64::ClearCache(); // Remove all those nasty JIT patches. - break; - - case MODE_JIT: // Switching from interpreter to JIT. - // Don't really need to do much. It'll work, the cache will refill itself. - break; - } -} - -void SingleStep() -{ - switch (mode) - { - case MODE_INTERPRETER: - Interpreter::SingleStep(); - break; - case MODE_JIT: - Jit64::Core::SingleStep(); - break; - } -} - -void RunLoop() -{ - state = CPU_RUNNING; - switch (mode) - { - case MODE_INTERPRETER: - Interpreter::Run(); - break; - case MODE_JIT: - Jit64::Core::Run(); - break; - } - Host_UpdateDisasmDialog(); -} - -void Start() -{ - // Select running mode for CPU.cpp - state = Core::g_CoreStartupParameter.bEnableDebugging ? CPU_RUNNINGDEBUG : CPU_RUNNING; - if (Core::bReadTrace || Core::bWriteTrace) - { - state = CPU_RUNNING; - } - Host_UpdateDisasmDialog(); -} - -void Pause() -{ - state = CPU_STEPPING; - Host_UpdateDisasmDialog(); -} - -void Stop() -{ - state = CPU_POWERDOWN; - Host_UpdateDisasmDialog(); -} - -void CheckExceptions() -{ - // This check is unnecessary in JIT mode. However, it probably doesn't really hurt. - if (!ppcState.Exceptions) - return; - - // TODO(ector): - // gcemu uses the mask 0x87C0FFFF instead of 0x0780FF77 - // Investigate! - - if (ppcState.Exceptions & EXCEPTION_FPU_UNAVAILABLE) - { - //This happens a lot - Gamecube OS uses deferred FPU context switching - SRR0 = PC; // re-execute the instruction - SRR1 = MSR & 0x0780FF77; - NPC = 0x80000800; - - LOGV(GEKKO, 1, "EXCEPTION_FPU_UNAVAILABLE"); - ppcState.Exceptions &= ~EXCEPTION_FPU_UNAVAILABLE; - SRR1 |= 0x02; //recoverable - } - else if (ppcState.Exceptions & EXCEPTION_SYSCALL) - { - SRR0 = NPC; // execute next instruction when we come back from handler - SRR1 = MSR & 0x0780FF77; - NPC = 0x80000C00; - - LOGV(GEKKO, 1, "EXCEPTION_SYSCALL (PC=%08x)",PC); - ppcState.Exceptions &= ~EXCEPTION_SYSCALL; - SRR1 |= 0x02; //recoverable - } - else if (ppcState.Exceptions & EXCEPTION_DSI) - { - SRR0 = PC; // re-execute the instruction - SRR1 = MSR & 0x0780FF77; - NPC = 0x80000300; - - LOGV(GEKKO, 1, "EXCEPTION_DSI"); - ppcState.Exceptions &= ~EXCEPTION_DSI; - //SRR1 |= 0x02; //make recoverable ? - } - else if (ppcState.Exceptions & EXCEPTION_ISI) - { - SRR0 = PC; - SRR1 = (MSR & 0x0780FF77) | 0x40000000; - NPC = 0x80000400; - - LOG(GEKKO, "EXCEPTION_ISI"); - ppcState.Exceptions &= ~EXCEPTION_ISI; - //SRR1 |= 0x02; //make recoverable ? - } - else if (ppcState.Exceptions & EXCEPTION_ALIGNMENT) - { - //This never happens ATM - SRR0 = NPC; - SRR1 = MSR & 0x0780FF77; - NPC = 0x80000600; - - LOG(GEKKO, "EXCEPTION_ALIGNMENT"); - ppcState.Exceptions &= ~EXCEPTION_ALIGNMENT; - //SRR1 |= 0x02; //make recoverable ? - } - - // EXTERNAL INTTERUPT - else if (MSR & 0x0008000) - { - if (ppcState.Exceptions & EXCEPTION_EXTERNAL_INT) - { - // Pokemon gets this "too early", it hasn't a handler yet - ppcState.Exceptions &= ~EXCEPTION_EXTERNAL_INT; // clear exception - - SRR0 = NPC; - NPC = 0x80000500; - SRR1 = (MSR & 0x0780FF77); - - LOGV(GEKKO, 1, "EXCEPTION_EXTERNAL_INT"); - - SRR1 |= 0x02; //set it to recoverable - _dbg_assert_msg_(GEKKO, (SRR1 & 0x02) != 0, "GEKKO", "EXTERNAL_INT unrecoverable???"); // unrecoverable exception !?! - } - else if (ppcState.Exceptions & EXCEPTION_DECREMENTER) - { - SRR0 = NPC; - SRR1 = MSR & 0x0000FF77; - NPC = 0x80000900; - - ppcState.Exceptions &= ~EXCEPTION_DECREMENTER; - - LOGV(GEKKO, 1, "EXCEPTION_DECREMENTER"); - SRR1 |= 0x02; //make recoverable - } - else - { - _dbg_assert_msg_(GEKKO, 0, "Unknown EXT interrupt: Exceptions == %08x", ppcState.Exceptions); - LOG(GEKKO, "Unknown EXTERNAL INTERRUPT exception: Exceptions == %08x", ppcState.Exceptions); - } - } - MSR &= ~0x0008000; // clear EE-bit so interrupts aren't possible anymore -} - -void OnIdle(u32 _uThreadAddr) -{ - u32 nextThread = Memory::Read_U32(_uThreadAddr); - //do idle skipping - if (nextThread == 0) - CoreTiming::Idle(); -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "Common.h" +#include "ChunkFile.h" + +#include "../HW/Memmap.h" +#include "../HW/CPU.h" +#include "../Core.h" +#include "../CoreTiming.h" + +#include "Interpreter/Interpreter.h" +#include "Jit64/JitCore.h" +#include "Jit64/JitCache.h" +#include "PowerPC.h" +#include "PPCTables.h" + +#include "../Host.h" + +namespace PowerPC +{ + +// STATE_TO_SAVE +PowerPCState GC_ALIGNED16(ppcState); +volatile CPUState state = CPU_STEPPING; + +static CoreMode mode; + +void DoState(PointerWrap &p) +{ + p.Do(ppcState); +} + +void ResetRegisters() +{ + for (int i = 0; i < 32; i++) + { + ppcState.gpr[i] = 0; + riPS0(i) = 0; + riPS1(i) = 0; + } + + memset(ppcState.spr, 0, sizeof(ppcState.spr)); + + ppcState.cr = 0; + ppcState.fpscr = 0; + ppcState.pc = 0; + ppcState.npc = 0; + ppcState.Exceptions = 0; + + TL = 0; + TU = 0; + + ppcState.msr = 0; + rDEC = 0xFFFFFFFF; +} + +void Init() +{ + enum { + FPU_PREC_24 = 0 << 8, + FPU_PREC_53 = 2 << 8, + FPU_PREC_64 = 3 << 8, + FPU_PREC_MASK = 3 << 8, + }; + #ifdef _M_IX86 + // sets the floating-point lib to 53-bit + // PowerPC has a 53bit floating pipeline only + // eg: sscanf is very sensitive +#ifdef _WIN32 + _control87(_PC_53, MCW_PC); +#else + unsigned short _mode; + asm ("fstcw %0" : : "m" (_mode)); + _mode = (_mode & ~FPU_PREC_MASK) | FPU_PREC_53; + asm ("fldcw %0" : : "m" (_mode)); +#endif +#else + //x64 doesn't need this - fpu is done with SSE + //but still - set any useful sse options here +#endif + + ResetRegisters(); + PPCTables::InitTables(); + + // Initialize both execution engines ... + Interpreter::Init(); + Jit64::Core::Init(); + // ... but start as interpreter by default. + mode = MODE_INTERPRETER; + state = CPU_STEPPING; +} + +void Shutdown() +{ + // Shutdown both execution engines. Doesn't matter which one is active. + Jit64::Core::Shutdown(); + Interpreter::Shutdown(); +} + +void SetMode(CoreMode new_mode) +{ + if (new_mode == mode) + return; // We don't need to do anything. + + mode = new_mode; + switch (mode) + { + case MODE_INTERPRETER: // Switching from JIT to interpreter + Jit64::ClearCache(); // Remove all those nasty JIT patches. + break; + + case MODE_JIT: // Switching from interpreter to JIT. + // Don't really need to do much. It'll work, the cache will refill itself. + break; + } +} + +void SingleStep() +{ + switch (mode) + { + case MODE_INTERPRETER: + Interpreter::SingleStep(); + break; + case MODE_JIT: + Jit64::Core::SingleStep(); + break; + } +} + +void RunLoop() +{ + state = CPU_RUNNING; + switch (mode) + { + case MODE_INTERPRETER: + Interpreter::Run(); + break; + case MODE_JIT: + Jit64::Core::Run(); + break; + } + Host_UpdateDisasmDialog(); +} + +void Start() +{ + // Select running mode for CPU.cpp + state = Core::g_CoreStartupParameter.bEnableDebugging ? CPU_RUNNINGDEBUG : CPU_RUNNING; + if (Core::bReadTrace || Core::bWriteTrace) + { + state = CPU_RUNNING; + } + Host_UpdateDisasmDialog(); +} + +void Pause() +{ + state = CPU_STEPPING; + Host_UpdateDisasmDialog(); +} + +void Stop() +{ + state = CPU_POWERDOWN; + Host_UpdateDisasmDialog(); +} + +void CheckExceptions() +{ + // This check is unnecessary in JIT mode. However, it probably doesn't really hurt. + if (!ppcState.Exceptions) + return; + + // TODO(ector): + // gcemu uses the mask 0x87C0FFFF instead of 0x0780FF77 + // Investigate! + + if (ppcState.Exceptions & EXCEPTION_FPU_UNAVAILABLE) + { + //This happens a lot - Gamecube OS uses deferred FPU context switching + SRR0 = PC; // re-execute the instruction + SRR1 = MSR & 0x0780FF77; + NPC = 0x80000800; + + LOGV(GEKKO, 1, "EXCEPTION_FPU_UNAVAILABLE"); + ppcState.Exceptions &= ~EXCEPTION_FPU_UNAVAILABLE; + SRR1 |= 0x02; //recoverable + } + else if (ppcState.Exceptions & EXCEPTION_SYSCALL) + { + SRR0 = NPC; // execute next instruction when we come back from handler + SRR1 = MSR & 0x0780FF77; + NPC = 0x80000C00; + + LOGV(GEKKO, 1, "EXCEPTION_SYSCALL (PC=%08x)",PC); + ppcState.Exceptions &= ~EXCEPTION_SYSCALL; + SRR1 |= 0x02; //recoverable + } + else if (ppcState.Exceptions & EXCEPTION_DSI) + { + SRR0 = PC; // re-execute the instruction + SRR1 = MSR & 0x0780FF77; + NPC = 0x80000300; + + LOGV(GEKKO, 1, "EXCEPTION_DSI"); + ppcState.Exceptions &= ~EXCEPTION_DSI; + //SRR1 |= 0x02; //make recoverable ? + } + else if (ppcState.Exceptions & EXCEPTION_ISI) + { + SRR0 = PC; + SRR1 = (MSR & 0x0780FF77) | 0x40000000; + NPC = 0x80000400; + + LOG(GEKKO, "EXCEPTION_ISI"); + ppcState.Exceptions &= ~EXCEPTION_ISI; + //SRR1 |= 0x02; //make recoverable ? + } + else if (ppcState.Exceptions & EXCEPTION_ALIGNMENT) + { + //This never happens ATM + SRR0 = NPC; + SRR1 = MSR & 0x0780FF77; + NPC = 0x80000600; + + LOG(GEKKO, "EXCEPTION_ALIGNMENT"); + ppcState.Exceptions &= ~EXCEPTION_ALIGNMENT; + //SRR1 |= 0x02; //make recoverable ? + } + + // EXTERNAL INTTERUPT + else if (MSR & 0x0008000) + { + if (ppcState.Exceptions & EXCEPTION_EXTERNAL_INT) + { + // Pokemon gets this "too early", it hasn't a handler yet + ppcState.Exceptions &= ~EXCEPTION_EXTERNAL_INT; // clear exception + + SRR0 = NPC; + NPC = 0x80000500; + SRR1 = (MSR & 0x0780FF77); + + LOGV(GEKKO, 1, "EXCEPTION_EXTERNAL_INT"); + + SRR1 |= 0x02; //set it to recoverable + _dbg_assert_msg_(GEKKO, (SRR1 & 0x02) != 0, "GEKKO", "EXTERNAL_INT unrecoverable???"); // unrecoverable exception !?! + } + else if (ppcState.Exceptions & EXCEPTION_DECREMENTER) + { + SRR0 = NPC; + SRR1 = MSR & 0x0000FF77; + NPC = 0x80000900; + + ppcState.Exceptions &= ~EXCEPTION_DECREMENTER; + + LOGV(GEKKO, 1, "EXCEPTION_DECREMENTER"); + SRR1 |= 0x02; //make recoverable + } + else + { + _dbg_assert_msg_(GEKKO, 0, "Unknown EXT interrupt: Exceptions == %08x", ppcState.Exceptions); + LOG(GEKKO, "Unknown EXTERNAL INTERRUPT exception: Exceptions == %08x", ppcState.Exceptions); + } + } + MSR &= ~0x0008000; // clear EE-bit so interrupts aren't possible anymore +} + +void OnIdle(u32 _uThreadAddr) +{ + u32 nextThread = Memory::Read_U32(_uThreadAddr); + //do idle skipping + if (nextThread == 0) + CoreTiming::Idle(); +} + +} // namespace diff --git a/Source/Core/Core/Src/PowerPC/Profiler.cpp b/Source/Core/Core/Src/PowerPC/Profiler.cpp index fd9e8c2366..18e1fc1d12 100644 --- a/Source/Core/Core/Src/PowerPC/Profiler.cpp +++ b/Source/Core/Core/Src/PowerPC/Profiler.cpp @@ -1,93 +1,93 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Jit64/Jit.h" - -#include -#include - -#include "SymbolDB.h" - -namespace Profiler -{ - -bool g_ProfileBlocks; -bool g_ProfileInstructions; - -struct BlockStat -{ - BlockStat(int bn, u64 c) : blockNum(bn), cost(c) {} - int blockNum; - u64 cost; - - bool operator <(const BlockStat &other) const { - return cost > other.cost; - } -}; - -void WriteProfileResults(const char *filename) { - std::vector stats; - stats.reserve(Jit64::GetNumBlocks()); - u64 cost_sum = 0; -#ifdef _WIN32 - u64 timecost_sum = 0; - LARGE_INTEGER countsPerSec; - QueryPerformanceFrequency(&countsPerSec); -#endif - for (int i = 0; i < Jit64::GetNumBlocks(); i++) - { - const Jit64::JitBlock *block = Jit64::GetBlock(i); - u64 cost = (block->originalSize / 4) * block->runCount; // rough heuristic. mem instructions should cost more. -#ifdef _WIN32 - u64 timecost = block->ticCounter.QuadPart; // Indeed ;) -#endif - if (block->runCount >= 1) { // Todo: tweak. - stats.push_back(BlockStat(i, cost)); - } - cost_sum += cost; -#ifdef _WIN32 - timecost_sum += timecost; -#endif - } - - sort(stats.begin(), stats.end()); - FILE *f = fopen(filename, "w"); - if (!f) { - PanicAlert("failed to open %s", filename); - return; - } - fprintf(f, "origAddr\tblkName\tcost\ttimeCost\tpercent\ttimePercent\tOvAllinBlkTime(ms)\tblkCodeSize\n"); - for (unsigned int i = 0; i < stats.size(); i++) - { - const Jit64::JitBlock *block = Jit64::GetBlock(stats[i].blockNum); - if (block) { - std::string name = g_symbolDB.GetDescription(block->originalAddress); - double percent = 100.0 * (double)stats[i].cost / (double)cost_sum; -#ifdef _WIN32 - double timePercent = 100.0 * (double)block->ticCounter.QuadPart / (double)timecost_sum; - fprintf(f, "%08x\t%s\t%llu\t%llu\t%.2lf\t%llf\t%lf\t%i\n", - block->originalAddress, name.c_str(), stats[i].cost, block->ticCounter.QuadPart, percent, timePercent, (double)block->ticCounter.QuadPart*1000.0/(double)countsPerSec.QuadPart, block->codeSize); -#else - fprintf(f, "%08x\t%s\t%llu\t???\t%.2lf\t???\t???\t%i\n", - block->originalAddress, name.c_str(), stats[i].cost, /*block->ticCounter.QuadPart,*/ percent, /*timePercent, (double)block->ticCounter.QuadPart*1000.0/(double)countsPerSec.QuadPart,*/ block->codeSize); -#endif - } - } - fclose(f); -} - -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Jit64/Jit.h" + +#include +#include + +#include "SymbolDB.h" + +namespace Profiler +{ + +bool g_ProfileBlocks; +bool g_ProfileInstructions; + +struct BlockStat +{ + BlockStat(int bn, u64 c) : blockNum(bn), cost(c) {} + int blockNum; + u64 cost; + + bool operator <(const BlockStat &other) const { + return cost > other.cost; + } +}; + +void WriteProfileResults(const char *filename) { + std::vector stats; + stats.reserve(Jit64::GetNumBlocks()); + u64 cost_sum = 0; +#ifdef _WIN32 + u64 timecost_sum = 0; + LARGE_INTEGER countsPerSec; + QueryPerformanceFrequency(&countsPerSec); +#endif + for (int i = 0; i < Jit64::GetNumBlocks(); i++) + { + const Jit64::JitBlock *block = Jit64::GetBlock(i); + u64 cost = (block->originalSize / 4) * block->runCount; // rough heuristic. mem instructions should cost more. +#ifdef _WIN32 + u64 timecost = block->ticCounter.QuadPart; // Indeed ;) +#endif + if (block->runCount >= 1) { // Todo: tweak. + stats.push_back(BlockStat(i, cost)); + } + cost_sum += cost; +#ifdef _WIN32 + timecost_sum += timecost; +#endif + } + + sort(stats.begin(), stats.end()); + FILE *f = fopen(filename, "w"); + if (!f) { + PanicAlert("failed to open %s", filename); + return; + } + fprintf(f, "origAddr\tblkName\tcost\ttimeCost\tpercent\ttimePercent\tOvAllinBlkTime(ms)\tblkCodeSize\n"); + for (unsigned int i = 0; i < stats.size(); i++) + { + const Jit64::JitBlock *block = Jit64::GetBlock(stats[i].blockNum); + if (block) { + std::string name = g_symbolDB.GetDescription(block->originalAddress); + double percent = 100.0 * (double)stats[i].cost / (double)cost_sum; +#ifdef _WIN32 + double timePercent = 100.0 * (double)block->ticCounter.QuadPart / (double)timecost_sum; + fprintf(f, "%08x\t%s\t%llu\t%llu\t%.2lf\t%llf\t%lf\t%i\n", + block->originalAddress, name.c_str(), stats[i].cost, block->ticCounter.QuadPart, percent, timePercent, (double)block->ticCounter.QuadPart*1000.0/(double)countsPerSec.QuadPart, block->codeSize); +#else + fprintf(f, "%08x\t%s\t%llu\t???\t%.2lf\t???\t???\t%i\n", + block->originalAddress, name.c_str(), stats[i].cost, /*block->ticCounter.QuadPart,*/ percent, /*timePercent, (double)block->ticCounter.QuadPart*1000.0/(double)countsPerSec.QuadPart,*/ block->codeSize); +#endif + } + } + fclose(f); +} + +} diff --git a/Source/Core/Core/Src/PowerPC/SignatureDB.cpp b/Source/Core/Core/Src/PowerPC/SignatureDB.cpp index 275caa378a..91089203d7 100644 --- a/Source/Core/Core/Src/PowerPC/SignatureDB.cpp +++ b/Source/Core/Core/Src/PowerPC/SignatureDB.cpp @@ -1,211 +1,211 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "PPCAnalyst.h" -#include "../HW/Memmap.h" - -#include "SignatureDB.h" -#include "SymbolDB.h" - -namespace { - -// On-disk format for SignatureDB entries. -struct FuncDesc -{ - u32 checkSum; - u32 size; - char name[128]; -}; - -} // namespace - -bool SignatureDB::Load(const char *filename) -{ - FILE *f = fopen(filename, "rb"); - if (!f) - return false; - u32 fcount = 0; - fread(&fcount, 4, 1, f); - for (size_t i = 0; i < fcount; i++) - { - FuncDesc temp; - memset(&temp, 0, sizeof(temp)); - - fread(&temp, sizeof(temp), 1, f); - temp.name[sizeof(temp.name)-1] = 0; - - DBFunc dbf; - dbf.name = temp.name; - dbf.size = temp.size; - database[temp.checkSum] = dbf; - } - fclose(f); - return true; -} - -bool SignatureDB::Save(const char *filename) -{ - FILE *f = fopen(filename,"wb"); - if (!f) - { - LOG(HLE, "Database save failed"); - return false; - } - int fcount = (int)database.size(); - fwrite(&fcount, 4, 1, f); - for (FuncDB::const_iterator iter = database.begin(); iter != database.end(); iter++) - { - FuncDesc temp; - memset(&temp, 0, sizeof(temp)); - temp.checkSum = iter->first; - temp.size = iter->second.size; - strncpy(temp.name, iter->second.name.c_str(), 127); - fwrite(&temp, sizeof(temp), 1, f); - } - fclose(f); - LOG(HLE,"Database save successful"); - return true; -} - -//Adds a known function to the hash database -u32 SignatureDB::Add(u32 startAddr, u32 size, const char *name) -{ - u32 hash = ComputeCodeChecksum(startAddr, startAddr + size); - - DBFunc temp_dbfunc; - temp_dbfunc.size = size; - temp_dbfunc.name = name; - - FuncDB::iterator iter = database.find(hash); - if (iter == database.end()) - database[hash] = temp_dbfunc; - return hash; -} - -void SignatureDB::List() -{ - for (FuncDB::iterator iter = database.begin(); iter != database.end(); iter++) - { - LOG(HLE,"%s : %i bytes, hash = %08x",iter->second.name.c_str(), iter->second.size, iter->first); - } - LOG(HLE, "%i functions known in current database.", database.size()); -} - -void SignatureDB::Clear() -{ - database.clear(); -} - -void SignatureDB::Apply(SymbolDB *symbol_db) -{ - for (FuncDB::const_iterator iter = database.begin(); iter != database.end(); iter++) - { - u32 hash = iter->first; - Symbol *function = symbol_db->GetSymbolFromHash(hash); - if (function) - { - // Found the function. Let's rename it according to the symbol file. - if (iter->second.size == (unsigned int)function->size) - { - function->name = iter->second.name; - LOGV(HLE, 1, "Found %s at %08x (size: %08x)!", iter->second.name.c_str(), function->address, function->size); - } - else - { - function->name = iter->second.name; - LOG(HLE, "Wrong sizzze! Found %s at %08x (size: %08x instead of %08x)!", iter->second.name.c_str(), function->address, function->size, iter->second.size); - } - } - } - symbol_db->Index(); -} - -void SignatureDB::Initialize(SymbolDB *symbol_db, const char *prefix) -{ - std::string prefix_str(prefix); - for (SymbolDB::XFuncMap::const_iterator iter = symbol_db->GetConstIterator(); iter != symbol_db->End(); iter++) - { - if (iter->second.name.substr(0, prefix_str.size()) == prefix_str) - { - DBFunc temp_dbfunc; - temp_dbfunc.name = iter->second.name; - temp_dbfunc.size = iter->second.size; - database[iter->second.hash] = temp_dbfunc; - } - } -} - -/*static*/ u32 SignatureDB::ComputeCodeChecksum(u32 offsetStart, u32 offsetEnd) -{ - u32 sum = 0; - for (u32 offset = offsetStart; offset <= offsetEnd; offset += 4) - { - u32 opcode = Memory::Read_Instruction(offset); - u32 op = opcode & 0xFC000000; - u32 op2 = 0; - u32 op3 = 0; - u32 auxop = op >> 26; - switch (auxop) - { - case 4: //PS instructions - op2 = opcode & 0x0000003F; - switch ( op2 ) - { - case 0: - case 8: - case 16: - case 21: - case 22: - op3 = opcode & 0x000007C0; - } - break; - - case 7: //addi muli etc - case 8: - case 10: - case 11: - case 12: - case 13: - case 14: - case 15: - op2 = opcode & 0x03FF0000; - break; - - case 19: // MCRF?? - case 31: //integer - case 63: //fpu - op2 = opcode & 0x000007FF; - break; - case 59: //fpu - op2 = opcode & 0x0000003F; - if (op2 < 16) - op3 = opcode & 0x000007C0; - break; - default: - if (auxop >= 32 && auxop < 56) - op2 = opcode & 0x03FF0000; - break; - } - // Checksum only uses opcode, not opcode data, because opcode data changes - // in all compilations, but opcodes dont! - sum = ( ( (sum << 17 ) & 0xFFFE0000 ) | ( (sum >> 15) & 0x0001FFFF ) ); - sum = sum ^ (op | op2 | op3); - } - return sum; -} - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "PPCAnalyst.h" +#include "../HW/Memmap.h" + +#include "SignatureDB.h" +#include "SymbolDB.h" + +namespace { + +// On-disk format for SignatureDB entries. +struct FuncDesc +{ + u32 checkSum; + u32 size; + char name[128]; +}; + +} // namespace + +bool SignatureDB::Load(const char *filename) +{ + FILE *f = fopen(filename, "rb"); + if (!f) + return false; + u32 fcount = 0; + fread(&fcount, 4, 1, f); + for (size_t i = 0; i < fcount; i++) + { + FuncDesc temp; + memset(&temp, 0, sizeof(temp)); + + fread(&temp, sizeof(temp), 1, f); + temp.name[sizeof(temp.name)-1] = 0; + + DBFunc dbf; + dbf.name = temp.name; + dbf.size = temp.size; + database[temp.checkSum] = dbf; + } + fclose(f); + return true; +} + +bool SignatureDB::Save(const char *filename) +{ + FILE *f = fopen(filename,"wb"); + if (!f) + { + LOG(HLE, "Database save failed"); + return false; + } + int fcount = (int)database.size(); + fwrite(&fcount, 4, 1, f); + for (FuncDB::const_iterator iter = database.begin(); iter != database.end(); iter++) + { + FuncDesc temp; + memset(&temp, 0, sizeof(temp)); + temp.checkSum = iter->first; + temp.size = iter->second.size; + strncpy(temp.name, iter->second.name.c_str(), 127); + fwrite(&temp, sizeof(temp), 1, f); + } + fclose(f); + LOG(HLE,"Database save successful"); + return true; +} + +//Adds a known function to the hash database +u32 SignatureDB::Add(u32 startAddr, u32 size, const char *name) +{ + u32 hash = ComputeCodeChecksum(startAddr, startAddr + size); + + DBFunc temp_dbfunc; + temp_dbfunc.size = size; + temp_dbfunc.name = name; + + FuncDB::iterator iter = database.find(hash); + if (iter == database.end()) + database[hash] = temp_dbfunc; + return hash; +} + +void SignatureDB::List() +{ + for (FuncDB::iterator iter = database.begin(); iter != database.end(); iter++) + { + LOG(HLE,"%s : %i bytes, hash = %08x",iter->second.name.c_str(), iter->second.size, iter->first); + } + LOG(HLE, "%i functions known in current database.", database.size()); +} + +void SignatureDB::Clear() +{ + database.clear(); +} + +void SignatureDB::Apply(SymbolDB *symbol_db) +{ + for (FuncDB::const_iterator iter = database.begin(); iter != database.end(); iter++) + { + u32 hash = iter->first; + Symbol *function = symbol_db->GetSymbolFromHash(hash); + if (function) + { + // Found the function. Let's rename it according to the symbol file. + if (iter->second.size == (unsigned int)function->size) + { + function->name = iter->second.name; + LOGV(HLE, 1, "Found %s at %08x (size: %08x)!", iter->second.name.c_str(), function->address, function->size); + } + else + { + function->name = iter->second.name; + LOG(HLE, "Wrong sizzze! Found %s at %08x (size: %08x instead of %08x)!", iter->second.name.c_str(), function->address, function->size, iter->second.size); + } + } + } + symbol_db->Index(); +} + +void SignatureDB::Initialize(SymbolDB *symbol_db, const char *prefix) +{ + std::string prefix_str(prefix); + for (SymbolDB::XFuncMap::const_iterator iter = symbol_db->GetConstIterator(); iter != symbol_db->End(); iter++) + { + if (iter->second.name.substr(0, prefix_str.size()) == prefix_str) + { + DBFunc temp_dbfunc; + temp_dbfunc.name = iter->second.name; + temp_dbfunc.size = iter->second.size; + database[iter->second.hash] = temp_dbfunc; + } + } +} + +/*static*/ u32 SignatureDB::ComputeCodeChecksum(u32 offsetStart, u32 offsetEnd) +{ + u32 sum = 0; + for (u32 offset = offsetStart; offset <= offsetEnd; offset += 4) + { + u32 opcode = Memory::Read_Instruction(offset); + u32 op = opcode & 0xFC000000; + u32 op2 = 0; + u32 op3 = 0; + u32 auxop = op >> 26; + switch (auxop) + { + case 4: //PS instructions + op2 = opcode & 0x0000003F; + switch ( op2 ) + { + case 0: + case 8: + case 16: + case 21: + case 22: + op3 = opcode & 0x000007C0; + } + break; + + case 7: //addi muli etc + case 8: + case 10: + case 11: + case 12: + case 13: + case 14: + case 15: + op2 = opcode & 0x03FF0000; + break; + + case 19: // MCRF?? + case 31: //integer + case 63: //fpu + op2 = opcode & 0x000007FF; + break; + case 59: //fpu + op2 = opcode & 0x0000003F; + if (op2 < 16) + op3 = opcode & 0x000007C0; + break; + default: + if (auxop >= 32 && auxop < 56) + op2 = opcode & 0x03FF0000; + break; + } + // Checksum only uses opcode, not opcode data, because opcode data changes + // in all compilations, but opcodes dont! + sum = ( ( (sum << 17 ) & 0xFFFE0000 ) | ( (sum >> 15) & 0x0001FFFF ) ); + sum = sum ^ (op | op2 | op3); + } + return sum; +} + diff --git a/Source/Core/Core/Src/PowerPC/SymbolDB.cpp b/Source/Core/Core/Src/PowerPC/SymbolDB.cpp index 967b26e6fe..6145f58606 100644 --- a/Source/Core/Core/Src/PowerPC/SymbolDB.cpp +++ b/Source/Core/Core/Src/PowerPC/SymbolDB.cpp @@ -1,383 +1,383 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" - -#include -#include -#include - -#include "../HW/Memmap.h" -#include "SymbolDB.h" -#include "SignatureDB.h" -#include "PPCAnalyst.h" - -#if defined(HAVE_WX) && HAVE_WX -#include -#include -#endif - -SymbolDB g_symbolDB; - -SymbolDB::SymbolDB() -{ - // Get access to the disasm() fgnction - debugger = new PPCDebugInterface(); -} - -SymbolDB::~SymbolDB() -{ -} - -void SymbolDB::List() -{ - for (XFuncMap::iterator iter = functions.begin(); iter != functions.end(); iter++) - { - LOG(HLE,"%s @ %08x: %i bytes (hash %08x) : %i calls", iter->second.name.c_str(), iter->second.address, iter->second.size, iter->second.hash,iter->second.numCalls); - } - LOG(HLE,"%i functions known in this program above.", functions.size()); -} - -void SymbolDB::Clear(const char *prefix) -{ - // TODO: honor prefix - functions.clear(); - checksumToFunction.clear(); -} - -void SymbolDB::Index() -{ - int i = 0; - for (XFuncMap::iterator iter = functions.begin(); iter != functions.end(); iter++) - { - iter->second.index = i++; - } -} - -// Adds the function to the list, unless it's already there -Symbol *SymbolDB::AddFunction(u32 startAddr) -{ - if (startAddr < 0x80000010) - return 0; - XFuncMap::iterator iter = functions.find(startAddr); - if (iter != functions.end()) - { - // it's already in the list - return 0; - } - else - { - Symbol tempFunc; //the current one we're working on - u32 targetEnd = PPCAnalyst::AnalyzeFunction(startAddr, tempFunc); - if (targetEnd == 0) - return 0; //found a dud :( - //LOG(HLE,"Symbol found at %08x",startAddr); - functions[startAddr] = tempFunc; - tempFunc.type = Symbol::SYMBOL_FUNCTION; - checksumToFunction[tempFunc.hash] = &(functions[startAddr]); - return &functions[startAddr]; - } -} - -void SymbolDB::AddKnownSymbol(u32 startAddr, u32 size, const char *name, int type) -{ - XFuncMap::iterator iter = functions.find(startAddr); - if (iter != functions.end()) - { - // already got it, let's just update name and checksum to be sure. - Symbol *tempfunc = &iter->second; - tempfunc->name = name; - tempfunc->hash = SignatureDB::ComputeCodeChecksum(startAddr, startAddr + size); - tempfunc->type = type; - } - else - { - // new symbol. run analyze. - Symbol tf; - tf.name = name; - tf.type = type; - tf.address = startAddr; - if (tf.type == Symbol::SYMBOL_FUNCTION) { - PPCAnalyst::AnalyzeFunction(startAddr, tf, size); - checksumToFunction[tf.hash] = &(functions[startAddr]); - } - functions[startAddr] = tf; - } -} - -Symbol *SymbolDB::GetSymbolFromAddr(u32 addr) -{ - if (!Memory::IsRAMAddress(addr)) - return 0; - XFuncMap::iterator it = functions.find(addr); - if (it != functions.end()) - return &it->second; - else - { - for (XFuncMap::iterator iter = functions.begin(); iter != functions.end(); iter++) - { - if (addr >= iter->second.address && addr < iter->second.address + iter->second.size) - return &iter->second; - } - } - return 0; -} - -Symbol *SymbolDB::GetSymbolFromName(const char *name) -{ - for (XFuncMap::iterator iter = functions.begin(); iter != functions.end(); iter++) - { - if (!strcmp(iter->second.name.c_str(), name)) - return &iter->second; - } - return 0; -} - -const char *SymbolDB::GetDescription(u32 addr) -{ - Symbol *symbol = GetSymbolFromAddr(addr); - if (symbol) - return symbol->name.c_str(); - else - return " --- "; -} - -void SymbolDB::FillInCallers() -{ - for (XFuncMap::iterator iter = functions.begin(); iter != functions.end(); iter++) - { - iter->second.callers.clear(); - } - - for (XFuncMap::iterator iter = functions.begin(); iter != functions.end(); iter++) - { - Symbol &f = iter->second; - for (size_t i = 0; i < f.calls.size(); i++) - { - SCall NewCall(iter->first, f.calls[i].callAddress); - u32 FunctionAddress = f.calls[i].function; - - XFuncMap::iterator FuncIterator = functions.find(FunctionAddress); - if (FuncIterator != functions.end()) - { - Symbol& rCalledFunction = FuncIterator->second; - rCalledFunction.callers.push_back(NewCall); - } - else - { - //LOG(HLE,"FillInCallers tries to fill data in an unknown function 0x%08x.", FunctionAddress); - // TODO - analyze the function instead. - } - } - } -} - -void SymbolDB::PrintCalls(u32 funcAddr) const -{ - XFuncMap::const_iterator iter = functions.find(funcAddr); - if (iter != functions.end()) - { - const Symbol &f = iter->second; - LOG(HLE, "The function %s at %08x calls:", f.name.c_str(), f.address); - for (std::vector::const_iterator fiter = f.calls.begin(); fiter!=f.calls.end(); fiter++) - { - XFuncMap::const_iterator n = functions.find(fiter->function); - if (n != functions.end()) - { - LOG(CONSOLE,"* %08x : %s", fiter->callAddress, n->second.name.c_str()); - } - } - } - else - { - LOG(CONSOLE, "Symbol does not exist"); - } -} - -void SymbolDB::PrintCallers(u32 funcAddr) const -{ - XFuncMap::const_iterator iter = functions.find(funcAddr); - if (iter != functions.end()) - { - const Symbol &f = iter->second; - LOG(CONSOLE,"The function %s at %08x is called by:",f.name.c_str(),f.address); - for (std::vector::const_iterator fiter = f.callers.begin(); fiter != f.callers.end(); fiter++) - { - XFuncMap::const_iterator n = functions.find(fiter->function); - if (n != functions.end()) - { - LOG(CONSOLE,"* %08x : %s", fiter->callAddress, n->second.name.c_str()); - } - } - } -} - -void SymbolDB::LogFunctionCall(u32 addr) -{ - //u32 from = PC; - XFuncMap::iterator iter = functions.find(addr); - if (iter != functions.end()) - { - Symbol &f = iter->second; - f.numCalls++; - } -} - -// This one can load both leftover map files on game discs (like Zelda), and mapfiles -// produced by SaveSymbolMap below. -bool SymbolDB::LoadMap(const char *filename) -{ - FILE *f = fopen(filename, "r"); - if (!f) - return false; - - bool started = false; - while (!feof(f)) - { - char line[512], temp[256]; - fgets(line, 511, f); - if (strlen(line) < 4) - continue; - - sscanf(line, "%s", temp); - if (strcmp(temp, "UNUSED")==0) continue; - if (strcmp(temp, ".text")==0) {started = true; continue;}; - if (strcmp(temp, ".init")==0) {started = true; continue;}; - if (strcmp(temp, "Starting")==0) continue; - if (strcmp(temp, "extab")==0) continue; - if (strcmp(temp, ".ctors")==0) break; //uh? - if (strcmp(temp, ".dtors")==0) break; - if (strcmp(temp, ".rodata")==0) continue; - if (strcmp(temp, ".data")==0) continue; - if (strcmp(temp, ".sbss")==0) continue; - if (strcmp(temp, ".sdata")==0) continue; - if (strcmp(temp, ".sdata2")==0) continue; - if (strcmp(temp, "address")==0) continue; - if (strcmp(temp, "-----------------------")==0) continue; - if (strcmp(temp, ".sbss2")==0) break; - if (temp[1] == ']') continue; - - if (!started) continue; - - u32 address, vaddress, size, unknown; - char name[512]; - sscanf(line, "%08x %08x %08x %i %s", &address, &size, &vaddress, &unknown, name); - - const char *namepos = strstr(line, name); - if (namepos != 0) //would be odd if not :P - strcpy(name, namepos); - name[strlen(name) - 1] = 0; - - // we want the function names only .... TODO: or do we really? aren't we wasting information here? - for (size_t i = 0; i < strlen(name); i++) - { - if (name[i] == ' ') name[i] = 0x00; - if (name[i] == '(') name[i] = 0x00; - } - - // Check if this is a valid entry. - if (strcmp(name, ".text") != 0 || strcmp(name, ".init") != 0 || strlen(name) > 0) - { - AddKnownSymbol(vaddress | 0x80000000, size, name); // ST_FUNCTION - } - } - - fclose(f); - Index(); - return true; -} - - -// =================================================== -/* Save the map file and save a code file */ -// ---------------- -bool SymbolDB::SaveMap(const char *filename, bool WithCodes) const -{ - // Format the name for the codes version - std::string mapFile = filename; - if(WithCodes) mapFile = mapFile.substr(0, mapFile.find_last_of(".")) + "_code.map"; - - // Make a file - FILE *f = fopen(mapFile.c_str(), "w"); - if (!f) return false; - - - // -------------------------------------------------------------------- - // Walk through every code row - // ------------------------- - fprintf(f, ".text\n"); // Write ".text" at the top - XFuncMap::const_iterator itr = functions.begin(); - u32 LastAddress = 0x80004000; - std::string LastSymbolName; - while (itr != functions.end()) - { - // Save a map file - const Symbol &rSymbol = itr->second; - if(!WithCodes) - { - fprintf(f,"%08x %08x %08x %i %s\n", rSymbol.address, rSymbol.size, rSymbol.address, - 0, rSymbol.name.c_str()); - itr++; - } - - // Save a code file - else - { - // Get the current and next address - LastAddress = rSymbol.address; - LastSymbolName = rSymbol.name; - itr++; - - /* To make nice straight lines we fill out the name with spaces, we also cut off - all names longer than 25 letters */ - std::string TempSym; - for(u32 i = 0; i < 25; i++) - { - if(i < LastSymbolName.size()) - TempSym += LastSymbolName[i]; - else - TempSym += " "; - } - - // We currently skip the last block because we don't know how long it goes - int space; - if(itr != functions.end()) - space = itr->second.address - LastAddress; - else - space = 0; - - for(int i = 0; i < space; i+=4) - { - int Address = LastAddress + i; - fprintf(f,"%08x %i %20s %s\n", Address, - 0, TempSym.c_str(), debugger->disasm(Address)); - } - fprintf(f, "\n"); // Write a blank line after each block - } - } - // --------------- -#if defined(HAVE_WX) && HAVE_WX - // Show success message - wxMessageBox(wxString::Format(wxT("Saved %s"), mapFile.c_str())); -#else - // Show message somewhere -#endif - // Close the file and return - fclose(f); - return true; -} -// =========== +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" + +#include +#include +#include + +#include "../HW/Memmap.h" +#include "SymbolDB.h" +#include "SignatureDB.h" +#include "PPCAnalyst.h" + +#if defined(HAVE_WX) && HAVE_WX +#include +#include +#endif + +SymbolDB g_symbolDB; + +SymbolDB::SymbolDB() +{ + // Get access to the disasm() fgnction + debugger = new PPCDebugInterface(); +} + +SymbolDB::~SymbolDB() +{ +} + +void SymbolDB::List() +{ + for (XFuncMap::iterator iter = functions.begin(); iter != functions.end(); iter++) + { + LOG(HLE,"%s @ %08x: %i bytes (hash %08x) : %i calls", iter->second.name.c_str(), iter->second.address, iter->second.size, iter->second.hash,iter->second.numCalls); + } + LOG(HLE,"%i functions known in this program above.", functions.size()); +} + +void SymbolDB::Clear(const char *prefix) +{ + // TODO: honor prefix + functions.clear(); + checksumToFunction.clear(); +} + +void SymbolDB::Index() +{ + int i = 0; + for (XFuncMap::iterator iter = functions.begin(); iter != functions.end(); iter++) + { + iter->second.index = i++; + } +} + +// Adds the function to the list, unless it's already there +Symbol *SymbolDB::AddFunction(u32 startAddr) +{ + if (startAddr < 0x80000010) + return 0; + XFuncMap::iterator iter = functions.find(startAddr); + if (iter != functions.end()) + { + // it's already in the list + return 0; + } + else + { + Symbol tempFunc; //the current one we're working on + u32 targetEnd = PPCAnalyst::AnalyzeFunction(startAddr, tempFunc); + if (targetEnd == 0) + return 0; //found a dud :( + //LOG(HLE,"Symbol found at %08x",startAddr); + functions[startAddr] = tempFunc; + tempFunc.type = Symbol::SYMBOL_FUNCTION; + checksumToFunction[tempFunc.hash] = &(functions[startAddr]); + return &functions[startAddr]; + } +} + +void SymbolDB::AddKnownSymbol(u32 startAddr, u32 size, const char *name, int type) +{ + XFuncMap::iterator iter = functions.find(startAddr); + if (iter != functions.end()) + { + // already got it, let's just update name and checksum to be sure. + Symbol *tempfunc = &iter->second; + tempfunc->name = name; + tempfunc->hash = SignatureDB::ComputeCodeChecksum(startAddr, startAddr + size); + tempfunc->type = type; + } + else + { + // new symbol. run analyze. + Symbol tf; + tf.name = name; + tf.type = type; + tf.address = startAddr; + if (tf.type == Symbol::SYMBOL_FUNCTION) { + PPCAnalyst::AnalyzeFunction(startAddr, tf, size); + checksumToFunction[tf.hash] = &(functions[startAddr]); + } + functions[startAddr] = tf; + } +} + +Symbol *SymbolDB::GetSymbolFromAddr(u32 addr) +{ + if (!Memory::IsRAMAddress(addr)) + return 0; + XFuncMap::iterator it = functions.find(addr); + if (it != functions.end()) + return &it->second; + else + { + for (XFuncMap::iterator iter = functions.begin(); iter != functions.end(); iter++) + { + if (addr >= iter->second.address && addr < iter->second.address + iter->second.size) + return &iter->second; + } + } + return 0; +} + +Symbol *SymbolDB::GetSymbolFromName(const char *name) +{ + for (XFuncMap::iterator iter = functions.begin(); iter != functions.end(); iter++) + { + if (!strcmp(iter->second.name.c_str(), name)) + return &iter->second; + } + return 0; +} + +const char *SymbolDB::GetDescription(u32 addr) +{ + Symbol *symbol = GetSymbolFromAddr(addr); + if (symbol) + return symbol->name.c_str(); + else + return " --- "; +} + +void SymbolDB::FillInCallers() +{ + for (XFuncMap::iterator iter = functions.begin(); iter != functions.end(); iter++) + { + iter->second.callers.clear(); + } + + for (XFuncMap::iterator iter = functions.begin(); iter != functions.end(); iter++) + { + Symbol &f = iter->second; + for (size_t i = 0; i < f.calls.size(); i++) + { + SCall NewCall(iter->first, f.calls[i].callAddress); + u32 FunctionAddress = f.calls[i].function; + + XFuncMap::iterator FuncIterator = functions.find(FunctionAddress); + if (FuncIterator != functions.end()) + { + Symbol& rCalledFunction = FuncIterator->second; + rCalledFunction.callers.push_back(NewCall); + } + else + { + //LOG(HLE,"FillInCallers tries to fill data in an unknown function 0x%08x.", FunctionAddress); + // TODO - analyze the function instead. + } + } + } +} + +void SymbolDB::PrintCalls(u32 funcAddr) const +{ + XFuncMap::const_iterator iter = functions.find(funcAddr); + if (iter != functions.end()) + { + const Symbol &f = iter->second; + LOG(HLE, "The function %s at %08x calls:", f.name.c_str(), f.address); + for (std::vector::const_iterator fiter = f.calls.begin(); fiter!=f.calls.end(); fiter++) + { + XFuncMap::const_iterator n = functions.find(fiter->function); + if (n != functions.end()) + { + LOG(CONSOLE,"* %08x : %s", fiter->callAddress, n->second.name.c_str()); + } + } + } + else + { + LOG(CONSOLE, "Symbol does not exist"); + } +} + +void SymbolDB::PrintCallers(u32 funcAddr) const +{ + XFuncMap::const_iterator iter = functions.find(funcAddr); + if (iter != functions.end()) + { + const Symbol &f = iter->second; + LOG(CONSOLE,"The function %s at %08x is called by:",f.name.c_str(),f.address); + for (std::vector::const_iterator fiter = f.callers.begin(); fiter != f.callers.end(); fiter++) + { + XFuncMap::const_iterator n = functions.find(fiter->function); + if (n != functions.end()) + { + LOG(CONSOLE,"* %08x : %s", fiter->callAddress, n->second.name.c_str()); + } + } + } +} + +void SymbolDB::LogFunctionCall(u32 addr) +{ + //u32 from = PC; + XFuncMap::iterator iter = functions.find(addr); + if (iter != functions.end()) + { + Symbol &f = iter->second; + f.numCalls++; + } +} + +// This one can load both leftover map files on game discs (like Zelda), and mapfiles +// produced by SaveSymbolMap below. +bool SymbolDB::LoadMap(const char *filename) +{ + FILE *f = fopen(filename, "r"); + if (!f) + return false; + + bool started = false; + while (!feof(f)) + { + char line[512], temp[256]; + fgets(line, 511, f); + if (strlen(line) < 4) + continue; + + sscanf(line, "%s", temp); + if (strcmp(temp, "UNUSED")==0) continue; + if (strcmp(temp, ".text")==0) {started = true; continue;}; + if (strcmp(temp, ".init")==0) {started = true; continue;}; + if (strcmp(temp, "Starting")==0) continue; + if (strcmp(temp, "extab")==0) continue; + if (strcmp(temp, ".ctors")==0) break; //uh? + if (strcmp(temp, ".dtors")==0) break; + if (strcmp(temp, ".rodata")==0) continue; + if (strcmp(temp, ".data")==0) continue; + if (strcmp(temp, ".sbss")==0) continue; + if (strcmp(temp, ".sdata")==0) continue; + if (strcmp(temp, ".sdata2")==0) continue; + if (strcmp(temp, "address")==0) continue; + if (strcmp(temp, "-----------------------")==0) continue; + if (strcmp(temp, ".sbss2")==0) break; + if (temp[1] == ']') continue; + + if (!started) continue; + + u32 address, vaddress, size, unknown; + char name[512]; + sscanf(line, "%08x %08x %08x %i %s", &address, &size, &vaddress, &unknown, name); + + const char *namepos = strstr(line, name); + if (namepos != 0) //would be odd if not :P + strcpy(name, namepos); + name[strlen(name) - 1] = 0; + + // we want the function names only .... TODO: or do we really? aren't we wasting information here? + for (size_t i = 0; i < strlen(name); i++) + { + if (name[i] == ' ') name[i] = 0x00; + if (name[i] == '(') name[i] = 0x00; + } + + // Check if this is a valid entry. + if (strcmp(name, ".text") != 0 || strcmp(name, ".init") != 0 || strlen(name) > 0) + { + AddKnownSymbol(vaddress | 0x80000000, size, name); // ST_FUNCTION + } + } + + fclose(f); + Index(); + return true; +} + + +// =================================================== +/* Save the map file and save a code file */ +// ---------------- +bool SymbolDB::SaveMap(const char *filename, bool WithCodes) const +{ + // Format the name for the codes version + std::string mapFile = filename; + if(WithCodes) mapFile = mapFile.substr(0, mapFile.find_last_of(".")) + "_code.map"; + + // Make a file + FILE *f = fopen(mapFile.c_str(), "w"); + if (!f) return false; + + + // -------------------------------------------------------------------- + // Walk through every code row + // ------------------------- + fprintf(f, ".text\n"); // Write ".text" at the top + XFuncMap::const_iterator itr = functions.begin(); + u32 LastAddress = 0x80004000; + std::string LastSymbolName; + while (itr != functions.end()) + { + // Save a map file + const Symbol &rSymbol = itr->second; + if(!WithCodes) + { + fprintf(f,"%08x %08x %08x %i %s\n", rSymbol.address, rSymbol.size, rSymbol.address, + 0, rSymbol.name.c_str()); + itr++; + } + + // Save a code file + else + { + // Get the current and next address + LastAddress = rSymbol.address; + LastSymbolName = rSymbol.name; + itr++; + + /* To make nice straight lines we fill out the name with spaces, we also cut off + all names longer than 25 letters */ + std::string TempSym; + for(u32 i = 0; i < 25; i++) + { + if(i < LastSymbolName.size()) + TempSym += LastSymbolName[i]; + else + TempSym += " "; + } + + // We currently skip the last block because we don't know how long it goes + int space; + if(itr != functions.end()) + space = itr->second.address - LastAddress; + else + space = 0; + + for(int i = 0; i < space; i+=4) + { + int Address = LastAddress + i; + fprintf(f,"%08x %i %20s %s\n", Address, + 0, TempSym.c_str(), debugger->disasm(Address)); + } + fprintf(f, "\n"); // Write a blank line after each block + } + } + // --------------- +#if defined(HAVE_WX) && HAVE_WX + // Show success message + wxMessageBox(wxString::Format(wxT("Saved %s"), mapFile.c_str())); +#else + // Show message somewhere +#endif + // Close the file and return + fclose(f); + return true; +} +// =========== diff --git a/Source/Core/Core/Src/State.cpp b/Source/Core/Core/Src/State.cpp index 12dbb7b043..11fd468f61 100644 --- a/Source/Core/Core/Src/State.cpp +++ b/Source/Core/Core/Src/State.cpp @@ -1,233 +1,233 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" - -#include "State.h" -#include "Core.h" -#include "StringUtil.h" -#include "CoreTiming.h" -#include "HW/HW.h" -#include "PowerPC/PowerPC.h" -#include "PowerPC/Jit64/JitCache.h" - -#include "Plugins/Plugin_Video.h" -#include "Plugins/Plugin_DSP.h" - -#include - -#include "minilzo.h" - - -#if defined(__LZO_STRICT_16BIT) -#define IN_LEN (8*1024u) -#elif defined(LZO_ARCH_I086) && !defined(LZO_HAVE_MM_HUGE_ARRAY) -#define IN_LEN (60*1024u) -#else -#define IN_LEN (128*1024ul) -#endif -#define OUT_LEN (IN_LEN + IN_LEN / 16 + 64 + 3) - -static unsigned char __LZO_MMODEL out [ OUT_LEN ]; - -#define HEAP_ALLOC(var,size) \ - lzo_align_t __LZO_MMODEL var [ ((size) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t) ] - -static HEAP_ALLOC(wrkmem,LZO1X_1_MEM_COMPRESS); - - -static int ev_Save; -static int ev_Load; - -static std::string cur_filename; - -static bool const bCompressed = true; - -enum { - version = 1 -}; - -void DoState(PointerWrap &p) -{ - u32 cookie = 0xBAADBABE + version; - p.Do(cookie); - if (cookie != 0xBAADBABE + version) { - PanicAlert("Can't load states from other versions."); - return; - } - // Begin with video plugin, so that it gets a chance to clear it's caches and writeback modified things to RAM - PluginVideo::Video_DoState(p.GetPPtr(), p.GetMode()); - PluginDSP::DSP_DoState(p.GetPPtr(), p.GetMode()); - PowerPC::DoState(p); - HW::DoState(p); - CoreTiming::DoState(p); -} - -void SaveStateCallback(u64 userdata, int cyclesLate) -{ - lzo_uint out_len = 0; - - FILE *f = fopen(cur_filename.c_str(), "wb"); - if(f == NULL) { - Core::DisplayMessage("Could not save state", 2000); - return; - } - - Jit64::ClearCache(); - u8 *ptr = 0; - PointerWrap p(&ptr, PointerWrap::MODE_MEASURE); - DoState(p); - size_t sz = (size_t)ptr; - u8 *buffer = new u8[sz]; - ptr = buffer; - p.SetMode(PointerWrap::MODE_WRITE); - DoState(p); - - if(bCompressed) { - fwrite(&sz, sizeof(int), 1, f); - } else { - int zero = 0; - fwrite(&zero, sizeof(int), 1, f); - } - - - if(bCompressed) { - if (lzo_init() != LZO_E_OK) - PanicAlert("Internal LZO Error - lzo_init() failed"); - else { - lzo_uint cur_len; - lzo_uint i = 0; - - for(;;) { - if((i + IN_LEN) >= sz) - cur_len = sz - i; - else - cur_len = IN_LEN; - - if(lzo1x_1_compress((buffer + i), cur_len, out, &out_len, wrkmem) != LZO_E_OK) - PanicAlert("Internal LZO Error - compression failed"); - - // The size of the data to write is 'out_len' - fwrite(&out_len, sizeof(int), 1, f); - fwrite(out, out_len, 1, f); - - if(cur_len != IN_LEN) - break; - i += cur_len; - } - } - } else - fwrite(buffer, sz, 1, f); - - fclose(f); - - delete [] buffer; - - Core::DisplayMessage(StringFromFormat("Saved State to %s", - cur_filename.c_str()).c_str(), 2000); -} - -void LoadStateCallback(u64 userdata, int cyclesLate) -{ - lzo_uint cur_len; - lzo_uint new_len; - - bool bCompressedState; - - FILE *f = fopen(cur_filename.c_str(), "rb"); - if (!f) { - Core::DisplayMessage("State not found", 2000); - return; - } - - Jit64::ClearCache(); - - u8 *buffer = NULL; - - int sz; - fread(&sz, sizeof(int), 1, f); - - bCompressedState = (sz != 0); - - if(bCompressedState) { - if (lzo_init() != LZO_E_OK) - PanicAlert("Internal LZO Error - lzo_init() failed"); - else { - lzo_uint i = 0; - buffer = new u8[sz]; - - for(;;) { - if(fread(&cur_len, 1, sizeof(int), f) == 0) - break; - fread(out, 1, cur_len, f); - - int res = lzo1x_decompress(out, cur_len, (buffer + i), &new_len, NULL); - if(res != LZO_E_OK) - PanicAlert("Internal LZO Error - decompression failed (%d)", res); - - // The size of the data to read to our buffer is 'new_len' - i += new_len; - } - } - } else { - fseek(f, 0, SEEK_END); - sz = (int)(ftell(f) - sizeof(int)); - fseek(f, sizeof(int), SEEK_SET); - - buffer = new u8[sz]; - - int x; - if ((x = (int)fread(buffer, 1, sz, f)) != sz) - PanicAlert("wtf? %d %d", x, sz); - } - - fclose(f); - - u8 *ptr = buffer; - PointerWrap p(&ptr, PointerWrap::MODE_READ); - DoState(p); - delete [] buffer; - - Core::DisplayMessage(StringFromFormat("Loaded state from %s", cur_filename.c_str()).c_str(), 2000); -} - -void State_Init() -{ - ev_Load = CoreTiming::RegisterEvent("LoadState", &LoadStateCallback); - ev_Save = CoreTiming::RegisterEvent("SaveState", &SaveStateCallback); -} - -void State_Shutdown() -{ - // nothing to do, here for consistency. -} - -std::string MakeStateFilename(int state_number) { - return StringFromFormat(FULL_STATESAVES_DIR "%s.s%02i", Core::GetStartupParameter().GetUniqueID().c_str(), state_number); -} - -void State_Save(int slot) -{ - cur_filename = MakeStateFilename(slot); - CoreTiming::ScheduleEvent_Threadsafe(0, ev_Save); -} - -void State_Load(int slot) -{ - cur_filename = MakeStateFilename(slot); - CoreTiming::ScheduleEvent_Threadsafe(0, ev_Load); -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" + +#include "State.h" +#include "Core.h" +#include "StringUtil.h" +#include "CoreTiming.h" +#include "HW/HW.h" +#include "PowerPC/PowerPC.h" +#include "PowerPC/Jit64/JitCache.h" + +#include "Plugins/Plugin_Video.h" +#include "Plugins/Plugin_DSP.h" + +#include + +#include "minilzo.h" + + +#if defined(__LZO_STRICT_16BIT) +#define IN_LEN (8*1024u) +#elif defined(LZO_ARCH_I086) && !defined(LZO_HAVE_MM_HUGE_ARRAY) +#define IN_LEN (60*1024u) +#else +#define IN_LEN (128*1024ul) +#endif +#define OUT_LEN (IN_LEN + IN_LEN / 16 + 64 + 3) + +static unsigned char __LZO_MMODEL out [ OUT_LEN ]; + +#define HEAP_ALLOC(var,size) \ + lzo_align_t __LZO_MMODEL var [ ((size) + (sizeof(lzo_align_t) - 1)) / sizeof(lzo_align_t) ] + +static HEAP_ALLOC(wrkmem,LZO1X_1_MEM_COMPRESS); + + +static int ev_Save; +static int ev_Load; + +static std::string cur_filename; + +static bool const bCompressed = true; + +enum { + version = 1 +}; + +void DoState(PointerWrap &p) +{ + u32 cookie = 0xBAADBABE + version; + p.Do(cookie); + if (cookie != 0xBAADBABE + version) { + PanicAlert("Can't load states from other versions."); + return; + } + // Begin with video plugin, so that it gets a chance to clear it's caches and writeback modified things to RAM + PluginVideo::Video_DoState(p.GetPPtr(), p.GetMode()); + PluginDSP::DSP_DoState(p.GetPPtr(), p.GetMode()); + PowerPC::DoState(p); + HW::DoState(p); + CoreTiming::DoState(p); +} + +void SaveStateCallback(u64 userdata, int cyclesLate) +{ + lzo_uint out_len = 0; + + FILE *f = fopen(cur_filename.c_str(), "wb"); + if(f == NULL) { + Core::DisplayMessage("Could not save state", 2000); + return; + } + + Jit64::ClearCache(); + u8 *ptr = 0; + PointerWrap p(&ptr, PointerWrap::MODE_MEASURE); + DoState(p); + size_t sz = (size_t)ptr; + u8 *buffer = new u8[sz]; + ptr = buffer; + p.SetMode(PointerWrap::MODE_WRITE); + DoState(p); + + if(bCompressed) { + fwrite(&sz, sizeof(int), 1, f); + } else { + int zero = 0; + fwrite(&zero, sizeof(int), 1, f); + } + + + if(bCompressed) { + if (lzo_init() != LZO_E_OK) + PanicAlert("Internal LZO Error - lzo_init() failed"); + else { + lzo_uint cur_len; + lzo_uint i = 0; + + for(;;) { + if((i + IN_LEN) >= sz) + cur_len = sz - i; + else + cur_len = IN_LEN; + + if(lzo1x_1_compress((buffer + i), cur_len, out, &out_len, wrkmem) != LZO_E_OK) + PanicAlert("Internal LZO Error - compression failed"); + + // The size of the data to write is 'out_len' + fwrite(&out_len, sizeof(int), 1, f); + fwrite(out, out_len, 1, f); + + if(cur_len != IN_LEN) + break; + i += cur_len; + } + } + } else + fwrite(buffer, sz, 1, f); + + fclose(f); + + delete [] buffer; + + Core::DisplayMessage(StringFromFormat("Saved State to %s", + cur_filename.c_str()).c_str(), 2000); +} + +void LoadStateCallback(u64 userdata, int cyclesLate) +{ + lzo_uint cur_len; + lzo_uint new_len; + + bool bCompressedState; + + FILE *f = fopen(cur_filename.c_str(), "rb"); + if (!f) { + Core::DisplayMessage("State not found", 2000); + return; + } + + Jit64::ClearCache(); + + u8 *buffer = NULL; + + int sz; + fread(&sz, sizeof(int), 1, f); + + bCompressedState = (sz != 0); + + if(bCompressedState) { + if (lzo_init() != LZO_E_OK) + PanicAlert("Internal LZO Error - lzo_init() failed"); + else { + lzo_uint i = 0; + buffer = new u8[sz]; + + for(;;) { + if(fread(&cur_len, 1, sizeof(int), f) == 0) + break; + fread(out, 1, cur_len, f); + + int res = lzo1x_decompress(out, cur_len, (buffer + i), &new_len, NULL); + if(res != LZO_E_OK) + PanicAlert("Internal LZO Error - decompression failed (%d)", res); + + // The size of the data to read to our buffer is 'new_len' + i += new_len; + } + } + } else { + fseek(f, 0, SEEK_END); + sz = (int)(ftell(f) - sizeof(int)); + fseek(f, sizeof(int), SEEK_SET); + + buffer = new u8[sz]; + + int x; + if ((x = (int)fread(buffer, 1, sz, f)) != sz) + PanicAlert("wtf? %d %d", x, sz); + } + + fclose(f); + + u8 *ptr = buffer; + PointerWrap p(&ptr, PointerWrap::MODE_READ); + DoState(p); + delete [] buffer; + + Core::DisplayMessage(StringFromFormat("Loaded state from %s", cur_filename.c_str()).c_str(), 2000); +} + +void State_Init() +{ + ev_Load = CoreTiming::RegisterEvent("LoadState", &LoadStateCallback); + ev_Save = CoreTiming::RegisterEvent("SaveState", &SaveStateCallback); +} + +void State_Shutdown() +{ + // nothing to do, here for consistency. +} + +std::string MakeStateFilename(int state_number) { + return StringFromFormat(FULL_STATESAVES_DIR "%s.s%02i", Core::GetStartupParameter().GetUniqueID().c_str(), state_number); +} + +void State_Save(int slot) +{ + cur_filename = MakeStateFilename(slot); + CoreTiming::ScheduleEvent_Threadsafe(0, ev_Save); +} + +void State_Load(int slot) +{ + cur_filename = MakeStateFilename(slot); + CoreTiming::ScheduleEvent_Threadsafe(0, ev_Load); +} diff --git a/Source/Core/Core/Src/Tracer.cpp b/Source/Core/Core/Src/Tracer.cpp index 8d487683aa..74bf3610d6 100644 --- a/Source/Core/Core/Src/Tracer.cpp +++ b/Source/Core/Core/Src/Tracer.cpp @@ -1,147 +1,147 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include -#include - -#include "Common.h" -#include "Tracer.h" - -#include "Host.h" - -#include "PowerPC/PowerPC.h" - -namespace Core { -FILE *tracefile; - -bool bReadTrace = false; -bool bWriteTrace = false; - -void StartTrace(bool write) -{ - if (write) - { - tracefile = fopen("L:\\trace.dat","wb"); - bReadTrace = false; - bWriteTrace = true; - } - else - { - tracefile = fopen("L:\\trace.dat","rb"); - bReadTrace = true; - bWriteTrace = false; - } -} - -void StopTrace() -{ - if (tracefile) - { - fclose(tracefile); - tracefile = 0; - } -} - -int stateSize = 32*4;// + 32*16 + 6*4; -u64 tb; - -int SyncTrace() -{ - if (bWriteTrace) - { - fwrite(&PowerPC::ppcState, stateSize, 1, tracefile); - fflush(tracefile); - return 1; - } - if (bReadTrace) - { - PowerPC::PowerPCState state; - if (feof(tracefile)) - { - return 1; - } - fread(&state, stateSize, 1, tracefile); - bool difference = false; - for (int i=0; i<32; i++) - { - if (PowerPC::ppcState.gpr[i] != state.gpr[i]) - { - LOG(GEKKO, "DIFFERENCE - r%i (local %08x, remote %08x)", i, PowerPC::ppcState.gpr[i], state.gpr[i]); - difference = true; - } - } -/* - for (int i=0; i<32; i++) - { - for (int j=0; j<2; j++) - { - if (PowerPC::ppcState.ps[i][j] != state.ps[i][j]) - { - LOG(GEKKO, "DIFFERENCE - ps%i_%i (local %f, remote %f)", i, j, PowerPC::ppcState.ps[i][j], state.ps[i][j]); - difference = true; - } - } - }*/ - /* - if (PowerPC::ppcState.cr != state.cr) - { - LOG(GEKKO, "DIFFERENCE - CR (local %08x, remote %08x)", PowerPC::ppcState.cr, state.cr); - difference = true; - } - if (PowerPC::ppcState.pc != state.pc) - { - LOG(GEKKO, "DIFFERENCE - PC (local %08x, remote %08x)", PowerPC::ppcState.pc, state.pc); - difference = true; - } - //if (PowerPC::ppcState.npc != state.npc) - ///{ - // LOG(GEKKO, "DIFFERENCE - NPC (local %08x, remote %08x)", PowerPC::ppcState.npc, state.npc); - // difference = true; - //} - if (PowerPC::ppcState.msr != state.msr) - { - LOG(GEKKO, "DIFFERENCE - MSR (local %08x, remote %08x)", PowerPC::ppcState.msr, state.msr); - difference = true; - } - if (PowerPC::ppcState.fpscr != state.fpscr) - { - LOG(GEKKO, "DIFFERENCE - FPSCR (local %08x, remote %08x)", PowerPC::ppcState.fpscr, state.fpscr); - difference = true; - } -*/ - if (difference) - { - Host_UpdateLogDisplay(); - //Also show drec compare window here - //CDynaViewDlg::Show(true); - //CDynaViewDlg::ViewAddr(m_BlockStart); - //CDynaViewDlg::Show(true); - //PanicAlert("Hang on"); - //Sleep(INFINITE); - return 0; - } - else - { - return 1; - //LOG(GEKKO, "No difference!"); - } - } - return 1; - -} -} - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include + +#include "Common.h" +#include "Tracer.h" + +#include "Host.h" + +#include "PowerPC/PowerPC.h" + +namespace Core { +FILE *tracefile; + +bool bReadTrace = false; +bool bWriteTrace = false; + +void StartTrace(bool write) +{ + if (write) + { + tracefile = fopen("L:\\trace.dat","wb"); + bReadTrace = false; + bWriteTrace = true; + } + else + { + tracefile = fopen("L:\\trace.dat","rb"); + bReadTrace = true; + bWriteTrace = false; + } +} + +void StopTrace() +{ + if (tracefile) + { + fclose(tracefile); + tracefile = 0; + } +} + +int stateSize = 32*4;// + 32*16 + 6*4; +u64 tb; + +int SyncTrace() +{ + if (bWriteTrace) + { + fwrite(&PowerPC::ppcState, stateSize, 1, tracefile); + fflush(tracefile); + return 1; + } + if (bReadTrace) + { + PowerPC::PowerPCState state; + if (feof(tracefile)) + { + return 1; + } + fread(&state, stateSize, 1, tracefile); + bool difference = false; + for (int i=0; i<32; i++) + { + if (PowerPC::ppcState.gpr[i] != state.gpr[i]) + { + LOG(GEKKO, "DIFFERENCE - r%i (local %08x, remote %08x)", i, PowerPC::ppcState.gpr[i], state.gpr[i]); + difference = true; + } + } +/* + for (int i=0; i<32; i++) + { + for (int j=0; j<2; j++) + { + if (PowerPC::ppcState.ps[i][j] != state.ps[i][j]) + { + LOG(GEKKO, "DIFFERENCE - ps%i_%i (local %f, remote %f)", i, j, PowerPC::ppcState.ps[i][j], state.ps[i][j]); + difference = true; + } + } + }*/ + /* + if (PowerPC::ppcState.cr != state.cr) + { + LOG(GEKKO, "DIFFERENCE - CR (local %08x, remote %08x)", PowerPC::ppcState.cr, state.cr); + difference = true; + } + if (PowerPC::ppcState.pc != state.pc) + { + LOG(GEKKO, "DIFFERENCE - PC (local %08x, remote %08x)", PowerPC::ppcState.pc, state.pc); + difference = true; + } + //if (PowerPC::ppcState.npc != state.npc) + ///{ + // LOG(GEKKO, "DIFFERENCE - NPC (local %08x, remote %08x)", PowerPC::ppcState.npc, state.npc); + // difference = true; + //} + if (PowerPC::ppcState.msr != state.msr) + { + LOG(GEKKO, "DIFFERENCE - MSR (local %08x, remote %08x)", PowerPC::ppcState.msr, state.msr); + difference = true; + } + if (PowerPC::ppcState.fpscr != state.fpscr) + { + LOG(GEKKO, "DIFFERENCE - FPSCR (local %08x, remote %08x)", PowerPC::ppcState.fpscr, state.fpscr); + difference = true; + } +*/ + if (difference) + { + Host_UpdateLogDisplay(); + //Also show drec compare window here + //CDynaViewDlg::Show(true); + //CDynaViewDlg::ViewAddr(m_BlockStart); + //CDynaViewDlg::Show(true); + //PanicAlert("Hang on"); + //Sleep(INFINITE); + return 0; + } + else + { + return 1; + //LOG(GEKKO, "No difference!"); + } + } + return 1; + +} +} + diff --git a/Source/Core/Core/Src/VolumeHandler.cpp b/Source/Core/Core/Src/VolumeHandler.cpp index ac3f035aa0..cca0ebcfef 100644 --- a/Source/Core/Core/Src/VolumeHandler.cpp +++ b/Source/Core/Core/Src/VolumeHandler.cpp @@ -1,91 +1,91 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "VolumeHandler.h" -#include "VolumeCreator.h" - -namespace VolumeHandler -{ - -DiscIO::IVolume* g_pVolume = NULL; - -DiscIO::IVolume *GetVolume() { - return g_pVolume; -} - -void SetVolumeName(const std::string& _rFullPath) -{ - if (g_pVolume) - { - // This code looks scary. Can the try/catch stuff be removed? - // This cause a "Unhandled exception ... Access violation - // reading location ..." after you have started and stopped two - // or three games - delete g_pVolume; - g_pVolume = NULL; - } - - g_pVolume = DiscIO::CreateVolumeFromFilename(_rFullPath); -} - -void SetVolumeDirectory(const std::string& _rFullPath, bool _bIsWii) -{ - if (g_pVolume) - { - delete g_pVolume; - g_pVolume = NULL; - } - - g_pVolume = DiscIO::CreateVolumeFromDirectory(_rFullPath, _bIsWii); -} - -u32 Read32(u64 _Offset) -{ - if (g_pVolume != NULL) - { - u32 Temp; - g_pVolume->Read(_Offset, 4, (u8*)&Temp); - return Common::swap32(Temp); - } - return 0; -} - -bool ReadToPtr(u8* ptr, u64 _dwOffset, u64 _dwLength) -{ - if (g_pVolume != NULL && ptr) - { - g_pVolume->Read(_dwOffset, _dwLength, ptr); - return true; - } - - return false; -} - -bool IsValid() -{ - return g_pVolume != NULL; -} - -bool IsWii() -{ - if (g_pVolume) - return IsVolumeWiiDisc(g_pVolume); - - return false; -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "VolumeHandler.h" +#include "VolumeCreator.h" + +namespace VolumeHandler +{ + +DiscIO::IVolume* g_pVolume = NULL; + +DiscIO::IVolume *GetVolume() { + return g_pVolume; +} + +void SetVolumeName(const std::string& _rFullPath) +{ + if (g_pVolume) + { + // This code looks scary. Can the try/catch stuff be removed? + // This cause a "Unhandled exception ... Access violation + // reading location ..." after you have started and stopped two + // or three games + delete g_pVolume; + g_pVolume = NULL; + } + + g_pVolume = DiscIO::CreateVolumeFromFilename(_rFullPath); +} + +void SetVolumeDirectory(const std::string& _rFullPath, bool _bIsWii) +{ + if (g_pVolume) + { + delete g_pVolume; + g_pVolume = NULL; + } + + g_pVolume = DiscIO::CreateVolumeFromDirectory(_rFullPath, _bIsWii); +} + +u32 Read32(u64 _Offset) +{ + if (g_pVolume != NULL) + { + u32 Temp; + g_pVolume->Read(_Offset, 4, (u8*)&Temp); + return Common::swap32(Temp); + } + return 0; +} + +bool ReadToPtr(u8* ptr, u64 _dwOffset, u64 _dwLength) +{ + if (g_pVolume != NULL && ptr) + { + g_pVolume->Read(_dwOffset, _dwLength, ptr); + return true; + } + + return false; +} + +bool IsValid() +{ + return g_pVolume != NULL; +} + +bool IsWii() +{ + if (g_pVolume) + return IsVolumeWiiDisc(g_pVolume); + + return false; +} + +} // namespace diff --git a/Source/Core/Core/Src/stdafx.cpp b/Source/Core/Core/Src/stdafx.cpp index 640544f585..d1a69e47c1 100644 --- a/Source/Core/Core/Src/stdafx.cpp +++ b/Source/Core/Core/Src/stdafx.cpp @@ -1,18 +1,18 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + #include "stdafx.h" \ No newline at end of file diff --git a/Source/Core/DebuggerWX/Src/BreakPointDlg.cpp b/Source/Core/DebuggerWX/Src/BreakPointDlg.cpp index 2a13bcb7a3..318b8225ef 100644 --- a/Source/Core/DebuggerWX/Src/BreakPointDlg.cpp +++ b/Source/Core/DebuggerWX/Src/BreakPointDlg.cpp @@ -1,88 +1,88 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "BreakPointDlg.h" -#include "Common.h" -#include "Debugger.h" -#include "StringUtil.h" -#include "Debugger/Debugger_BreakPoints.h" - - -BEGIN_EVENT_TABLE(BreakPointDlg,wxDialog) - EVT_CLOSE(BreakPointDlg::OnClose) - EVT_BUTTON(ID_OK, BreakPointDlg::OnOK) - EVT_BUTTON(ID_CANCEL, BreakPointDlg::OnCancel) -END_EVENT_TABLE() - - -BreakPointDlg::BreakPointDlg(wxWindow *parent, wxWindowID id, const wxString &title, const wxPoint &position, const wxSize& size, long style) -: wxDialog(parent, id, title, position, size, style) -{ - CreateGUIControls(); -} - - -BreakPointDlg::~BreakPointDlg() -{ -} - - -void BreakPointDlg::CreateGUIControls() -{ - SetIcon(wxNullIcon); - SetSize(8,8,279,121); - Center(); - - - wxStaticText* WxStaticText1 = new wxStaticText(this, ID_WXSTATICTEXT1, wxT("Address"), wxPoint(8,24), wxDefaultSize, 0, wxT("WxStaticText1")); - WxStaticText1->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); - - m_pButtonOK = new wxButton(this, ID_OK, wxT("OK"), wxPoint(192,64), wxSize(73,25), 0, wxDefaultValidator, wxT("OK")); - m_pButtonOK->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); - - m_pButtonCancel = new wxButton(this, ID_CANCEL, wxT("Cancel"), wxPoint(112,64), wxSize(73,25), 0, wxDefaultValidator, wxT("Cancel")); - m_pButtonCancel->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); - - m_pEditAddress = new wxTextCtrl(this, ID_ADDRESS, wxT("80000000"), wxPoint(56,24), wxSize(197,20), 0, wxDefaultValidator, wxT("WxEdit1")); - m_pEditAddress->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); - - wxStaticBox* WxStaticBox1 = new wxStaticBox(this, ID_WXSTATICBOX1, wxT("Address"), wxPoint(0,0), wxSize(265,57)); - WxStaticBox1->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); -} - - -void BreakPointDlg::OnClose(wxCloseEvent& /*event*/) -{ - Destroy(); -} - -void BreakPointDlg::OnOK(wxCommandEvent& /*event*/) -{ - wxString AddressString = m_pEditAddress->GetLineText(0); - u32 Address = 0; - if (AsciiToHex(AddressString.mb_str(), Address)) - { - CBreakPoints::AddBreakPoint(Address); - CBreakPoints::UpdateBreakPointView(); - Close(); - } -} - -void BreakPointDlg::OnCancel(wxCommandEvent& /*event*/) -{ - Close(); -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "BreakPointDlg.h" +#include "Common.h" +#include "Debugger.h" +#include "StringUtil.h" +#include "Debugger/Debugger_BreakPoints.h" + + +BEGIN_EVENT_TABLE(BreakPointDlg,wxDialog) + EVT_CLOSE(BreakPointDlg::OnClose) + EVT_BUTTON(ID_OK, BreakPointDlg::OnOK) + EVT_BUTTON(ID_CANCEL, BreakPointDlg::OnCancel) +END_EVENT_TABLE() + + +BreakPointDlg::BreakPointDlg(wxWindow *parent, wxWindowID id, const wxString &title, const wxPoint &position, const wxSize& size, long style) +: wxDialog(parent, id, title, position, size, style) +{ + CreateGUIControls(); +} + + +BreakPointDlg::~BreakPointDlg() +{ +} + + +void BreakPointDlg::CreateGUIControls() +{ + SetIcon(wxNullIcon); + SetSize(8,8,279,121); + Center(); + + + wxStaticText* WxStaticText1 = new wxStaticText(this, ID_WXSTATICTEXT1, wxT("Address"), wxPoint(8,24), wxDefaultSize, 0, wxT("WxStaticText1")); + WxStaticText1->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); + + m_pButtonOK = new wxButton(this, ID_OK, wxT("OK"), wxPoint(192,64), wxSize(73,25), 0, wxDefaultValidator, wxT("OK")); + m_pButtonOK->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); + + m_pButtonCancel = new wxButton(this, ID_CANCEL, wxT("Cancel"), wxPoint(112,64), wxSize(73,25), 0, wxDefaultValidator, wxT("Cancel")); + m_pButtonCancel->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); + + m_pEditAddress = new wxTextCtrl(this, ID_ADDRESS, wxT("80000000"), wxPoint(56,24), wxSize(197,20), 0, wxDefaultValidator, wxT("WxEdit1")); + m_pEditAddress->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); + + wxStaticBox* WxStaticBox1 = new wxStaticBox(this, ID_WXSTATICBOX1, wxT("Address"), wxPoint(0,0), wxSize(265,57)); + WxStaticBox1->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); +} + + +void BreakPointDlg::OnClose(wxCloseEvent& /*event*/) +{ + Destroy(); +} + +void BreakPointDlg::OnOK(wxCommandEvent& /*event*/) +{ + wxString AddressString = m_pEditAddress->GetLineText(0); + u32 Address = 0; + if (AsciiToHex(AddressString.mb_str(), Address)) + { + CBreakPoints::AddBreakPoint(Address); + CBreakPoints::UpdateBreakPointView(); + Close(); + } +} + +void BreakPointDlg::OnCancel(wxCommandEvent& /*event*/) +{ + Close(); +} diff --git a/Source/Core/DebuggerWX/Src/BreakpointView.cpp b/Source/Core/DebuggerWX/Src/BreakpointView.cpp index 2f863af897..c3da612491 100644 --- a/Source/Core/DebuggerWX/Src/BreakpointView.cpp +++ b/Source/Core/DebuggerWX/Src/BreakpointView.cpp @@ -1,121 +1,121 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Debugger.h" -#include "Common.h" - -#include "BreakpointView.h" -#include "Debugger/Debugger_BreakPoints.h" -#include "Debugger/Debugger_SymbolMap.h" -#include "PowerPC/SymbolDB.h" - -BEGIN_EVENT_TABLE(CBreakPointView, wxListCtrl) - -END_EVENT_TABLE() - -CBreakPointView::CBreakPointView(wxWindow* parent, const wxWindowID id, const wxPoint& pos, const wxSize& size, long style) - : wxListCtrl(parent, id, pos, size, style) -{ - SetFont(wxFont(9, wxSWISS, wxNORMAL, wxNORMAL, false, wxT("Segoe UI"))); - - Refresh(); -} - - -void CBreakPointView::Update() -{ - ClearAll(); - - InsertColumn(0, wxT("Active"), wxLIST_FORMAT_LEFT, 50); - InsertColumn(1, wxT("Type"), wxLIST_FORMAT_LEFT, 50); - InsertColumn(2, wxT("Function"), wxLIST_FORMAT_CENTER, 200); - InsertColumn(3, wxT("Address"), wxLIST_FORMAT_LEFT, 100); - InsertColumn(4, wxT("Flags"), wxLIST_FORMAT_CENTER, 100); - - char szBuffer[64]; - const CBreakPoints::TBreakPoints& rBreakPoints = CBreakPoints::GetBreakPoints(); - for (size_t i = 0; i < rBreakPoints.size(); i++) - { - const TBreakPoint& rBP = rBreakPoints[i]; - if (!rBP.bTemporary) - { - wxString temp; - temp = wxString::FromAscii(rBP.bOn ? "on" : " "); - int Item = InsertItem(0, temp); - temp = wxString::FromAscii("BP"); - SetItem(Item, 1, temp); - - Symbol *symbol = g_symbolDB.GetSymbolFromAddr(rBP.iAddress); - if (symbol) - { - temp = wxString::FromAscii(g_symbolDB.GetDescription(rBP.iAddress)); - SetItem(Item, 2, temp); - } - - sprintf(szBuffer, "0x%08x", rBP.iAddress); - temp = wxString::FromAscii(szBuffer); - SetItem(Item, 3, temp); - - SetItemData(Item, rBP.iAddress); - } - } - - const CBreakPoints::TMemChecks& rMemChecks = CBreakPoints::GetMemChecks(); - for (size_t i = 0; i < rMemChecks.size(); i++) - { - const TMemCheck& rMemCheck = rMemChecks[i]; - - wxString temp; - temp = wxString::FromAscii(rMemCheck.Break ? "on" : " "); - int Item = InsertItem(0, temp); - temp = wxString::FromAscii("MC"); - SetItem(Item, 1, temp); - - Symbol *symbol = g_symbolDB.GetSymbolFromAddr(rMemCheck.StartAddress); - if (symbol) - { - temp = wxString::FromAscii(g_symbolDB.GetDescription(rMemCheck.StartAddress)); - SetItem(Item, 2, temp); - } - - sprintf(szBuffer, "0x%08x to 0%08x", rMemCheck.StartAddress, rMemCheck.EndAddress); - temp = wxString::FromAscii(szBuffer); - SetItem(Item, 3, temp); - - size_t c = 0; - if (rMemCheck.OnRead) szBuffer[c++] = 'r'; - if (rMemCheck.OnWrite) szBuffer[c++] = 'w'; - szBuffer[c] = 0x00; - temp = wxString::FromAscii(szBuffer); - SetItem(Item, 4, temp); - - SetItemData(Item, rMemCheck.StartAddress); - } - - - Refresh(); -} - -void CBreakPointView::DeleteCurrentSelection() -{ - int Item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); - if (Item >= 0) - { - u32 Address = (u32)GetItemData(Item); - CBreakPoints::DeleteElementByAddress(Address); - } -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Debugger.h" +#include "Common.h" + +#include "BreakpointView.h" +#include "Debugger/Debugger_BreakPoints.h" +#include "Debugger/Debugger_SymbolMap.h" +#include "PowerPC/SymbolDB.h" + +BEGIN_EVENT_TABLE(CBreakPointView, wxListCtrl) + +END_EVENT_TABLE() + +CBreakPointView::CBreakPointView(wxWindow* parent, const wxWindowID id, const wxPoint& pos, const wxSize& size, long style) + : wxListCtrl(parent, id, pos, size, style) +{ + SetFont(wxFont(9, wxSWISS, wxNORMAL, wxNORMAL, false, wxT("Segoe UI"))); + + Refresh(); +} + + +void CBreakPointView::Update() +{ + ClearAll(); + + InsertColumn(0, wxT("Active"), wxLIST_FORMAT_LEFT, 50); + InsertColumn(1, wxT("Type"), wxLIST_FORMAT_LEFT, 50); + InsertColumn(2, wxT("Function"), wxLIST_FORMAT_CENTER, 200); + InsertColumn(3, wxT("Address"), wxLIST_FORMAT_LEFT, 100); + InsertColumn(4, wxT("Flags"), wxLIST_FORMAT_CENTER, 100); + + char szBuffer[64]; + const CBreakPoints::TBreakPoints& rBreakPoints = CBreakPoints::GetBreakPoints(); + for (size_t i = 0; i < rBreakPoints.size(); i++) + { + const TBreakPoint& rBP = rBreakPoints[i]; + if (!rBP.bTemporary) + { + wxString temp; + temp = wxString::FromAscii(rBP.bOn ? "on" : " "); + int Item = InsertItem(0, temp); + temp = wxString::FromAscii("BP"); + SetItem(Item, 1, temp); + + Symbol *symbol = g_symbolDB.GetSymbolFromAddr(rBP.iAddress); + if (symbol) + { + temp = wxString::FromAscii(g_symbolDB.GetDescription(rBP.iAddress)); + SetItem(Item, 2, temp); + } + + sprintf(szBuffer, "0x%08x", rBP.iAddress); + temp = wxString::FromAscii(szBuffer); + SetItem(Item, 3, temp); + + SetItemData(Item, rBP.iAddress); + } + } + + const CBreakPoints::TMemChecks& rMemChecks = CBreakPoints::GetMemChecks(); + for (size_t i = 0; i < rMemChecks.size(); i++) + { + const TMemCheck& rMemCheck = rMemChecks[i]; + + wxString temp; + temp = wxString::FromAscii(rMemCheck.Break ? "on" : " "); + int Item = InsertItem(0, temp); + temp = wxString::FromAscii("MC"); + SetItem(Item, 1, temp); + + Symbol *symbol = g_symbolDB.GetSymbolFromAddr(rMemCheck.StartAddress); + if (symbol) + { + temp = wxString::FromAscii(g_symbolDB.GetDescription(rMemCheck.StartAddress)); + SetItem(Item, 2, temp); + } + + sprintf(szBuffer, "0x%08x to 0%08x", rMemCheck.StartAddress, rMemCheck.EndAddress); + temp = wxString::FromAscii(szBuffer); + SetItem(Item, 3, temp); + + size_t c = 0; + if (rMemCheck.OnRead) szBuffer[c++] = 'r'; + if (rMemCheck.OnWrite) szBuffer[c++] = 'w'; + szBuffer[c] = 0x00; + temp = wxString::FromAscii(szBuffer); + SetItem(Item, 4, temp); + + SetItemData(Item, rMemCheck.StartAddress); + } + + + Refresh(); +} + +void CBreakPointView::DeleteCurrentSelection() +{ + int Item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (Item >= 0) + { + u32 Address = (u32)GetItemData(Item); + CBreakPoints::DeleteElementByAddress(Address); + } +} diff --git a/Source/Core/DebuggerWX/Src/BreakpointWindow.cpp b/Source/Core/DebuggerWX/Src/BreakpointWindow.cpp index 8dc6913513..bcd812ebfc 100644 --- a/Source/Core/DebuggerWX/Src/BreakpointWindow.cpp +++ b/Source/Core/DebuggerWX/Src/BreakpointWindow.cpp @@ -1,376 +1,376 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Debugger.h" -#include "BreakpointWindow.h" -#include "BreakpointView.h" -#include "CodeWindow.h" -#include "HW/Memmap.h" -#include "BreakPointDlg.h" -#include "MemoryCheckDlg.h" -#include "IniFile.h" -#include "Debugger/Debugger_BreakPoints.h" // for TMemCheck - -#include - -extern "C" { -#include "../resources/toolbar_add_breakpoint.c" -#include "../resources/toolbar_add_memorycheck.c" -#include "../resources/toolbar_delete.c" -} - -static const long TOOLBAR_STYLE = wxTB_FLAT | wxTB_DOCKABLE | wxTB_TEXT; - -BEGIN_EVENT_TABLE(CBreakPointWindow, wxFrame) - EVT_CLOSE(CBreakPointWindow::OnClose) - EVT_MENU(IDM_DELETE, CBreakPointWindow::OnDelete) - EVT_MENU(IDM_CLEAR, CBreakPointWindow::OnClear) - EVT_MENU(IDM_ADD_BREAKPOINT, CBreakPointWindow::OnAddBreakPoint) - EVT_MENU(IDM_ADD_BREAKPOINTMANY, CBreakPointWindow::OnAddBreakPointMany) - EVT_MENU(IDM_ADD_MEMORYCHECK, CBreakPointWindow::OnAddMemoryCheck) - EVT_MENU(IDM_ADD_MEMORYCHECKMANY, CBreakPointWindow::OnAddMemoryCheckMany) - EVT_LIST_ITEM_ACTIVATED(ID_BPS, CBreakPointWindow::OnActivated) -END_EVENT_TABLE() - - -#define wxGetBitmapFromMemory(name) _wxGetBitmapFromMemory(name, sizeof(name)) -inline wxBitmap _wxGetBitmapFromMemory(const unsigned char* data, int length) -{ - wxMemoryInputStream is(data, length); - return(wxBitmap(wxImage(is, wxBITMAP_TYPE_ANY, -1), -1)); -} - - -CBreakPointWindow::CBreakPointWindow(CCodeWindow* _pCodeWindow, wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& position, const wxSize& size, long style) - : wxFrame(parent, id, title, position, size, style) - , m_BreakPointListView(NULL) - , m_pCodeWindow(_pCodeWindow) -{ - InitBitmaps(); - - CreateGUIControls(); - - // Create the toolbar - RecreateToolbar(); - -} - - -CBreakPointWindow::~CBreakPointWindow() -{} - - -void -CBreakPointWindow::Save(IniFile& _IniFile) const -{ - _IniFile.Set("BreakPoint", "x", GetPosition().x); - _IniFile.Set("BreakPoint", "y", GetPosition().y); - _IniFile.Set("BreakPoint", "w", GetSize().GetWidth()); - _IniFile.Set("BreakPoint", "h", GetSize().GetHeight()); -} - - -void -CBreakPointWindow::Load(IniFile& _IniFile) -{ - int x,y,w,h; - _IniFile.Get("BreakPoint", "x", &x, GetPosition().x); - _IniFile.Get("BreakPoint", "y", &y, GetPosition().y); - _IniFile.Get("BreakPoint", "w", &w, GetSize().GetWidth()); - _IniFile.Get("BreakPoint", "h", &h, GetSize().GetHeight()); - SetSize(x, y, w, h); -} - - -void -CBreakPointWindow::CreateGUIControls() -{ - SetTitle(wxT("Breakpoints")); - SetIcon(wxNullIcon); - SetSize(8, 8, 400, 370); - Center(); - - m_BreakPointListView = new CBreakPointView(this, ID_BPS, wxDefaultPosition, GetSize(), - wxLC_REPORT | wxSUNKEN_BORDER | wxLC_ALIGN_LEFT | wxLC_SINGLE_SEL | wxLC_SORT_ASCENDING); - - NotifyUpdate(); -} - - -void -CBreakPointWindow::PopulateToolbar(wxToolBar* toolBar) -{ - int w = m_Bitmaps[Toolbar_Delete].GetWidth(), - h = m_Bitmaps[Toolbar_Delete].GetHeight(); - - toolBar->SetToolBitmapSize(wxSize(w, h)); - toolBar->AddTool(IDM_DELETE, _T("Delete"), m_Bitmaps[Toolbar_Delete], _T("Delete the selected BreakPoint or MemoryCheck")); - toolBar->AddTool(IDM_CLEAR, _T("Clear all"), m_Bitmaps[Toolbar_Delete], _T("Clear all BreakPoints and MemoryChecks")); - - toolBar->AddSeparator(); - - toolBar->AddTool(IDM_ADD_BREAKPOINT, _T("BP"), m_Bitmaps[Toolbar_Add_BreakPoint], _T("Add BreakPoint...")); - toolBar->AddTool(IDM_ADD_BREAKPOINTMANY, _T("BPs"), m_Bitmaps[Toolbar_Add_BreakPoint], _T("Add BreakPoints...")); - - // just add memory breakpoints if you can use them - if (Memory::AreMemoryBreakpointsActivated()) - { - toolBar->AddTool(IDM_ADD_MEMORYCHECK, _T("MC"), m_Bitmaps[Toolbar_Add_Memcheck], _T("Add MemoryCheck...")); - toolBar->AddTool(IDM_ADD_MEMORYCHECKMANY, _T("MCs"), m_Bitmaps[Toolbar_Add_Memcheck], _T("Add MemoryChecks...")); - } - - // after adding the buttons to the toolbar, must call Realize() to reflect - // the changes - toolBar->Realize(); -} - - -void -CBreakPointWindow::RecreateToolbar() -{ - // delete and recreate the toolbar - wxToolBarBase* toolBar = GetToolBar(); - long style = toolBar ? toolBar->GetWindowStyle() : TOOLBAR_STYLE; - - delete toolBar; - SetToolBar(NULL); - - style &= ~(wxTB_HORIZONTAL | wxTB_VERTICAL | wxTB_BOTTOM | wxTB_RIGHT | wxTB_HORZ_LAYOUT | wxTB_TOP); - wxToolBar* theToolBar = CreateToolBar(style, ID_TOOLBAR); - - PopulateToolbar(theToolBar); - SetToolBar(theToolBar); -} - - -void -CBreakPointWindow::InitBitmaps() -{ - // load orignal size 48x48 - m_Bitmaps[Toolbar_Delete] = wxGetBitmapFromMemory(toolbar_delete_png); - m_Bitmaps[Toolbar_Add_BreakPoint] = wxGetBitmapFromMemory(toolbar_add_breakpoint_png); - m_Bitmaps[Toolbar_Add_Memcheck] = wxGetBitmapFromMemory(toolbar_add_memcheck_png); - - // scale to 24x24 for toolbar - for (size_t n = Toolbar_Delete; n < WXSIZEOF(m_Bitmaps); n++) - { - m_Bitmaps[n] = wxBitmap(m_Bitmaps[n].ConvertToImage().Scale(16, 16)); - } -} - - -void -CBreakPointWindow::OnClose(wxCloseEvent& /*event*/) -{ - Hide(); -} - - -void CBreakPointWindow::NotifyUpdate() -{ - if (m_BreakPointListView != NULL) - { - m_BreakPointListView->Update(); - } -} - - -void -CBreakPointWindow::OnDelete(wxCommandEvent& event) -{ - if (m_BreakPointListView) - { - m_BreakPointListView->DeleteCurrentSelection(); - } -} - - -// ========================================================================================== -// Clear all breakpoints -// ------------ -void -CBreakPointWindow::OnClear(wxCommandEvent& event) -{ - CBreakPoints::ClearAllBreakPoints(); -} -// ============ - - -void -CBreakPointWindow::OnAddBreakPoint(wxCommandEvent& event) -{ - BreakPointDlg bpDlg(this); - bpDlg.ShowModal(); -} - - -// ========================================================================================== -// Load breakpoints from file -// -------------- -void -CBreakPointWindow::OnAddBreakPointMany(wxCommandEvent& event) -{ - // load ini - IniFile ini; - std::string filename = std::string(FULL_GAMECONFIG_DIR "BreakPoints.ini"); - - if (ini.Load(filename.c_str())) // check if there is any file there - { - // get lines from a certain section - std::vector lines; - if (!ini.GetLines("BreakPoints", lines)) - { - wxMessageBox(_T("You have no [BreakPoints] line in your file")); - return; - } - - for (std::vector::const_iterator iter = lines.begin(); iter != lines.end(); ++iter) - { - std::string line = StripSpaces(*iter); - u32 Address = 0; - if (AsciiToHex(line.c_str(), Address)) - { - CBreakPoints::AddBreakPoint(Address); - } - } - // only update after we are done with the loop - CBreakPoints::UpdateBreakPointView(); - } - else - { - wxMessageBox(_T("You have no GameIni/BreakPoints.ini file")); - } - -} -// ================= - - -void -CBreakPointWindow::OnAddMemoryCheck(wxCommandEvent& event) -{ - MemoryCheckDlg memDlg(this); - memDlg.ShowModal(); -} - - -// ========================================================================================== -// Load memory checks from file -// -------------- -void -CBreakPointWindow::OnAddMemoryCheckMany(wxCommandEvent& event) -{ - // load ini - IniFile ini; - std::string filename = std::string(FULL_GAMECONFIG_DIR "MemoryChecks.ini"); - - if (ini.Load(filename.c_str())) - { - // get lines from a certain section - std::vector lines; - if (!ini.GetLines("MemoryChecks", lines)) - { - wxMessageBox(_T("You have no [MemoryChecks] line in your file")); - return; - } - - for (std::vector::const_iterator iter = lines.begin(); iter != lines.end(); ++iter) - { - std::string line = StripSpaces(*iter); - std::vector pieces; - SplitString(line, " ", pieces); // split string - - TMemCheck MemCheck; - u32 sAddress = 0; - u32 eAddress = 0; - bool doCommon = false; - - // ------------------------------------------------------------------------------------------ - // Decide if we have a range or just one address, and if we should break or not - // -------------- - if ( - pieces.size() == 1 - && AsciiToHex(pieces[0].c_str(), sAddress) - && pieces[0].size() == 8 - ) - { - // address range - MemCheck.StartAddress = sAddress; - MemCheck.EndAddress = sAddress; - doCommon = true; - MemCheck.Break = false; - } - else if( - pieces.size() == 2 - && AsciiToHex(pieces[0].c_str(), sAddress) && AsciiToHex(pieces[1].c_str(), eAddress) - && pieces[0].size() == 8 && pieces[1].size() == 8 - ) - { - // address range - MemCheck.StartAddress = sAddress; - MemCheck.EndAddress = eAddress; - doCommon = true; - MemCheck.Break = false; - } - else if( - pieces.size() == 3 - && AsciiToHex(pieces[0].c_str(), sAddress) && AsciiToHex(pieces[1].c_str(), eAddress) - && pieces[0].size() == 8 && pieces[1].size() == 8 && pieces[2].size() == 1 - ) - { - // address range - MemCheck.StartAddress = sAddress; - MemCheck.EndAddress = eAddress; - doCommon = true; - MemCheck.Break = true; - } - - if(doCommon) - { - // settings for the memory check - MemCheck.OnRead = true; - MemCheck.OnWrite = true; - MemCheck.Log = true; - //MemCheck.Break = false; // this is also what sets Active "on" in the breakpoint window - // so don't think it's off because we are only writing this to the log - CBreakPoints::AddMemoryCheck(MemCheck); - } - } - // update after we are done with the loop - CBreakPoints::UpdateBreakPointView(); - } - else - { - wxMessageBox(_T("You have no " FULL_GAMECONFIG_DIR "MemoryChecks.ini file")); - } -} -// ================= - - -void -CBreakPointWindow::OnActivated(wxListEvent& event) -{ - long Index = event.GetIndex(); - if (Index >= 0) - { - u32 Address = (u32)m_BreakPointListView->GetItemData(Index); - if (m_pCodeWindow != NULL) - { - m_pCodeWindow->JumpToAddress(Address); - } - } -} - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Debugger.h" +#include "BreakpointWindow.h" +#include "BreakpointView.h" +#include "CodeWindow.h" +#include "HW/Memmap.h" +#include "BreakPointDlg.h" +#include "MemoryCheckDlg.h" +#include "IniFile.h" +#include "Debugger/Debugger_BreakPoints.h" // for TMemCheck + +#include + +extern "C" { +#include "../resources/toolbar_add_breakpoint.c" +#include "../resources/toolbar_add_memorycheck.c" +#include "../resources/toolbar_delete.c" +} + +static const long TOOLBAR_STYLE = wxTB_FLAT | wxTB_DOCKABLE | wxTB_TEXT; + +BEGIN_EVENT_TABLE(CBreakPointWindow, wxFrame) + EVT_CLOSE(CBreakPointWindow::OnClose) + EVT_MENU(IDM_DELETE, CBreakPointWindow::OnDelete) + EVT_MENU(IDM_CLEAR, CBreakPointWindow::OnClear) + EVT_MENU(IDM_ADD_BREAKPOINT, CBreakPointWindow::OnAddBreakPoint) + EVT_MENU(IDM_ADD_BREAKPOINTMANY, CBreakPointWindow::OnAddBreakPointMany) + EVT_MENU(IDM_ADD_MEMORYCHECK, CBreakPointWindow::OnAddMemoryCheck) + EVT_MENU(IDM_ADD_MEMORYCHECKMANY, CBreakPointWindow::OnAddMemoryCheckMany) + EVT_LIST_ITEM_ACTIVATED(ID_BPS, CBreakPointWindow::OnActivated) +END_EVENT_TABLE() + + +#define wxGetBitmapFromMemory(name) _wxGetBitmapFromMemory(name, sizeof(name)) +inline wxBitmap _wxGetBitmapFromMemory(const unsigned char* data, int length) +{ + wxMemoryInputStream is(data, length); + return(wxBitmap(wxImage(is, wxBITMAP_TYPE_ANY, -1), -1)); +} + + +CBreakPointWindow::CBreakPointWindow(CCodeWindow* _pCodeWindow, wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& position, const wxSize& size, long style) + : wxFrame(parent, id, title, position, size, style) + , m_BreakPointListView(NULL) + , m_pCodeWindow(_pCodeWindow) +{ + InitBitmaps(); + + CreateGUIControls(); + + // Create the toolbar + RecreateToolbar(); + +} + + +CBreakPointWindow::~CBreakPointWindow() +{} + + +void +CBreakPointWindow::Save(IniFile& _IniFile) const +{ + _IniFile.Set("BreakPoint", "x", GetPosition().x); + _IniFile.Set("BreakPoint", "y", GetPosition().y); + _IniFile.Set("BreakPoint", "w", GetSize().GetWidth()); + _IniFile.Set("BreakPoint", "h", GetSize().GetHeight()); +} + + +void +CBreakPointWindow::Load(IniFile& _IniFile) +{ + int x,y,w,h; + _IniFile.Get("BreakPoint", "x", &x, GetPosition().x); + _IniFile.Get("BreakPoint", "y", &y, GetPosition().y); + _IniFile.Get("BreakPoint", "w", &w, GetSize().GetWidth()); + _IniFile.Get("BreakPoint", "h", &h, GetSize().GetHeight()); + SetSize(x, y, w, h); +} + + +void +CBreakPointWindow::CreateGUIControls() +{ + SetTitle(wxT("Breakpoints")); + SetIcon(wxNullIcon); + SetSize(8, 8, 400, 370); + Center(); + + m_BreakPointListView = new CBreakPointView(this, ID_BPS, wxDefaultPosition, GetSize(), + wxLC_REPORT | wxSUNKEN_BORDER | wxLC_ALIGN_LEFT | wxLC_SINGLE_SEL | wxLC_SORT_ASCENDING); + + NotifyUpdate(); +} + + +void +CBreakPointWindow::PopulateToolbar(wxToolBar* toolBar) +{ + int w = m_Bitmaps[Toolbar_Delete].GetWidth(), + h = m_Bitmaps[Toolbar_Delete].GetHeight(); + + toolBar->SetToolBitmapSize(wxSize(w, h)); + toolBar->AddTool(IDM_DELETE, _T("Delete"), m_Bitmaps[Toolbar_Delete], _T("Delete the selected BreakPoint or MemoryCheck")); + toolBar->AddTool(IDM_CLEAR, _T("Clear all"), m_Bitmaps[Toolbar_Delete], _T("Clear all BreakPoints and MemoryChecks")); + + toolBar->AddSeparator(); + + toolBar->AddTool(IDM_ADD_BREAKPOINT, _T("BP"), m_Bitmaps[Toolbar_Add_BreakPoint], _T("Add BreakPoint...")); + toolBar->AddTool(IDM_ADD_BREAKPOINTMANY, _T("BPs"), m_Bitmaps[Toolbar_Add_BreakPoint], _T("Add BreakPoints...")); + + // just add memory breakpoints if you can use them + if (Memory::AreMemoryBreakpointsActivated()) + { + toolBar->AddTool(IDM_ADD_MEMORYCHECK, _T("MC"), m_Bitmaps[Toolbar_Add_Memcheck], _T("Add MemoryCheck...")); + toolBar->AddTool(IDM_ADD_MEMORYCHECKMANY, _T("MCs"), m_Bitmaps[Toolbar_Add_Memcheck], _T("Add MemoryChecks...")); + } + + // after adding the buttons to the toolbar, must call Realize() to reflect + // the changes + toolBar->Realize(); +} + + +void +CBreakPointWindow::RecreateToolbar() +{ + // delete and recreate the toolbar + wxToolBarBase* toolBar = GetToolBar(); + long style = toolBar ? toolBar->GetWindowStyle() : TOOLBAR_STYLE; + + delete toolBar; + SetToolBar(NULL); + + style &= ~(wxTB_HORIZONTAL | wxTB_VERTICAL | wxTB_BOTTOM | wxTB_RIGHT | wxTB_HORZ_LAYOUT | wxTB_TOP); + wxToolBar* theToolBar = CreateToolBar(style, ID_TOOLBAR); + + PopulateToolbar(theToolBar); + SetToolBar(theToolBar); +} + + +void +CBreakPointWindow::InitBitmaps() +{ + // load orignal size 48x48 + m_Bitmaps[Toolbar_Delete] = wxGetBitmapFromMemory(toolbar_delete_png); + m_Bitmaps[Toolbar_Add_BreakPoint] = wxGetBitmapFromMemory(toolbar_add_breakpoint_png); + m_Bitmaps[Toolbar_Add_Memcheck] = wxGetBitmapFromMemory(toolbar_add_memcheck_png); + + // scale to 24x24 for toolbar + for (size_t n = Toolbar_Delete; n < WXSIZEOF(m_Bitmaps); n++) + { + m_Bitmaps[n] = wxBitmap(m_Bitmaps[n].ConvertToImage().Scale(16, 16)); + } +} + + +void +CBreakPointWindow::OnClose(wxCloseEvent& /*event*/) +{ + Hide(); +} + + +void CBreakPointWindow::NotifyUpdate() +{ + if (m_BreakPointListView != NULL) + { + m_BreakPointListView->Update(); + } +} + + +void +CBreakPointWindow::OnDelete(wxCommandEvent& event) +{ + if (m_BreakPointListView) + { + m_BreakPointListView->DeleteCurrentSelection(); + } +} + + +// ========================================================================================== +// Clear all breakpoints +// ------------ +void +CBreakPointWindow::OnClear(wxCommandEvent& event) +{ + CBreakPoints::ClearAllBreakPoints(); +} +// ============ + + +void +CBreakPointWindow::OnAddBreakPoint(wxCommandEvent& event) +{ + BreakPointDlg bpDlg(this); + bpDlg.ShowModal(); +} + + +// ========================================================================================== +// Load breakpoints from file +// -------------- +void +CBreakPointWindow::OnAddBreakPointMany(wxCommandEvent& event) +{ + // load ini + IniFile ini; + std::string filename = std::string(FULL_GAMECONFIG_DIR "BreakPoints.ini"); + + if (ini.Load(filename.c_str())) // check if there is any file there + { + // get lines from a certain section + std::vector lines; + if (!ini.GetLines("BreakPoints", lines)) + { + wxMessageBox(_T("You have no [BreakPoints] line in your file")); + return; + } + + for (std::vector::const_iterator iter = lines.begin(); iter != lines.end(); ++iter) + { + std::string line = StripSpaces(*iter); + u32 Address = 0; + if (AsciiToHex(line.c_str(), Address)) + { + CBreakPoints::AddBreakPoint(Address); + } + } + // only update after we are done with the loop + CBreakPoints::UpdateBreakPointView(); + } + else + { + wxMessageBox(_T("You have no GameIni/BreakPoints.ini file")); + } + +} +// ================= + + +void +CBreakPointWindow::OnAddMemoryCheck(wxCommandEvent& event) +{ + MemoryCheckDlg memDlg(this); + memDlg.ShowModal(); +} + + +// ========================================================================================== +// Load memory checks from file +// -------------- +void +CBreakPointWindow::OnAddMemoryCheckMany(wxCommandEvent& event) +{ + // load ini + IniFile ini; + std::string filename = std::string(FULL_GAMECONFIG_DIR "MemoryChecks.ini"); + + if (ini.Load(filename.c_str())) + { + // get lines from a certain section + std::vector lines; + if (!ini.GetLines("MemoryChecks", lines)) + { + wxMessageBox(_T("You have no [MemoryChecks] line in your file")); + return; + } + + for (std::vector::const_iterator iter = lines.begin(); iter != lines.end(); ++iter) + { + std::string line = StripSpaces(*iter); + std::vector pieces; + SplitString(line, " ", pieces); // split string + + TMemCheck MemCheck; + u32 sAddress = 0; + u32 eAddress = 0; + bool doCommon = false; + + // ------------------------------------------------------------------------------------------ + // Decide if we have a range or just one address, and if we should break or not + // -------------- + if ( + pieces.size() == 1 + && AsciiToHex(pieces[0].c_str(), sAddress) + && pieces[0].size() == 8 + ) + { + // address range + MemCheck.StartAddress = sAddress; + MemCheck.EndAddress = sAddress; + doCommon = true; + MemCheck.Break = false; + } + else if( + pieces.size() == 2 + && AsciiToHex(pieces[0].c_str(), sAddress) && AsciiToHex(pieces[1].c_str(), eAddress) + && pieces[0].size() == 8 && pieces[1].size() == 8 + ) + { + // address range + MemCheck.StartAddress = sAddress; + MemCheck.EndAddress = eAddress; + doCommon = true; + MemCheck.Break = false; + } + else if( + pieces.size() == 3 + && AsciiToHex(pieces[0].c_str(), sAddress) && AsciiToHex(pieces[1].c_str(), eAddress) + && pieces[0].size() == 8 && pieces[1].size() == 8 && pieces[2].size() == 1 + ) + { + // address range + MemCheck.StartAddress = sAddress; + MemCheck.EndAddress = eAddress; + doCommon = true; + MemCheck.Break = true; + } + + if(doCommon) + { + // settings for the memory check + MemCheck.OnRead = true; + MemCheck.OnWrite = true; + MemCheck.Log = true; + //MemCheck.Break = false; // this is also what sets Active "on" in the breakpoint window + // so don't think it's off because we are only writing this to the log + CBreakPoints::AddMemoryCheck(MemCheck); + } + } + // update after we are done with the loop + CBreakPoints::UpdateBreakPointView(); + } + else + { + wxMessageBox(_T("You have no " FULL_GAMECONFIG_DIR "MemoryChecks.ini file")); + } +} +// ================= + + +void +CBreakPointWindow::OnActivated(wxListEvent& event) +{ + long Index = event.GetIndex(); + if (Index >= 0) + { + u32 Address = (u32)m_BreakPointListView->GetItemData(Index); + if (m_pCodeWindow != NULL) + { + m_pCodeWindow->JumpToAddress(Address); + } + } +} + diff --git a/Source/Core/DebuggerWX/Src/CodeView.cpp b/Source/Core/DebuggerWX/Src/CodeView.cpp index ea7d782a5d..1d75cb8283 100644 --- a/Source/Core/DebuggerWX/Src/CodeView.cpp +++ b/Source/Core/DebuggerWX/Src/CodeView.cpp @@ -1,561 +1,561 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Debugger.h" -#include "Debugger/PPCDebugInterface.h" -#include "PowerPC/SymbolDB.h" -#include "HW/Memmap.h" // for Write_U32 -#include "Common.h" -#include "StringUtil.h" - -#include "Host.h" -#include "CodeView.h" -#include "JitWindow.h" - -#include -#include -#include - -DEFINE_EVENT_TYPE(wxEVT_CODEVIEW_CHANGE); - -enum -{ - IDM_GOTOINMEMVIEW = 12000, - IDM_COPYADDRESS, - IDM_COPYHEX, - IDM_COPYCODE, - IDM_INSERTBLR, - IDM_RUNTOHERE, - IDM_JITRESULTS, - IDM_FOLLOWBRANCH, - IDM_RENAMESYMBOL, - IDM_PATCHALERT, - IDM_COPYFUNCTION, -}; - - -BEGIN_EVENT_TABLE(CCodeView, wxControl) - EVT_ERASE_BACKGROUND(CCodeView::OnErase) - EVT_PAINT(CCodeView::OnPaint) - EVT_LEFT_DOWN(CCodeView::OnMouseDown) - EVT_LEFT_UP(CCodeView::OnMouseUpL) - EVT_MOTION(CCodeView::OnMouseMove) - EVT_RIGHT_DOWN(CCodeView::OnMouseDown) - EVT_RIGHT_UP(CCodeView::OnMouseUpR) - EVT_MENU(-1, CCodeView::OnPopupMenu) -END_EVENT_TABLE() - -CCodeView::CCodeView(DebugInterface* debuginterface, wxWindow* parent, wxWindowID Id, const wxSize& Size) - : wxControl(parent, Id, wxDefaultPosition, Size), - debugger(debuginterface), - rowHeight(13), - selection(0), - oldSelection(0), - selectionChanged(false), - selecting(false), - hasFocus(false), - showHex(false), - lx(-1), - ly(-1) -{ - rowHeight = 13; - align = debuginterface->getInstructionSize(0); - curAddress = debuginterface->getPC(); - selection = 0; -} - - -wxSize CCodeView::DoGetBestSize() const -{ - wxSize bestSize; - bestSize.x = 400; - bestSize.y = 800; - return(bestSize); -} - - -int CCodeView::YToAddress(int y) -{ - wxRect rc = GetClientRect(); - int ydiff = y - rc.height / 2 - rowHeight / 2; - ydiff = (int)(floorf((float)ydiff / (float)rowHeight)) + 1; - return(curAddress + ydiff * align); -} - - -void CCodeView::OnMouseDown(wxMouseEvent& event) -{ - int x = event.m_x; - int y = event.m_y; - - if (x > 16) - { - oldSelection = selection; - selection = YToAddress(y); - // SetCapture(wnd); - bool oldselecting = selecting; - selecting = true; - - if (!oldselecting || (selection != oldSelection)) - { - redraw(); - } - } - else - { - debugger->toggleBreakpoint(YToAddress(y)); - redraw(); - } - - event.Skip(true); -} - - -void CCodeView::OnMouseMove(wxMouseEvent& event) -{ - wxRect rc = GetClientRect(); - - if (event.m_leftDown) - { - if (event.m_x > 16) - { - if (event.m_y < 0) - { - curAddress -= align; - redraw(); - } - else if (event.m_y > rc.height) - { - curAddress += align; - redraw(); - } - else - { - OnMouseDown(event); - } - } - } - - event.Skip(true); -} - -void CCodeView::RaiseEvent() -{ - wxCommandEvent ev(wxEVT_CODEVIEW_CHANGE, GetId()); - ev.SetEventObject(this); - ev.SetInt(selection); - GetEventHandler()->ProcessEvent(ev); -} - -void CCodeView::OnMouseUpL(wxMouseEvent& event) -{ - if (event.m_x > 16) - { - curAddress = YToAddress(event.m_y); - selecting = false; - //ReleaseCapture(); - redraw(); - } - RaiseEvent(); - event.Skip(true); -} - -u32 CCodeView::AddrToBranch(u32 addr) -{ - const char *temp = debugger->disasm(addr); - const char *mojs = strstr(temp, "->0x"); - if (mojs) - { - u32 dest; - sscanf(mojs+4,"%08x", &dest); - return dest; - } - return 0; -} - -void CCodeView::OnPopupMenu(wxCommandEvent& event) -{ -#if wxUSE_CLIPBOARD - wxTheClipboard->Open(); -#endif - - switch (event.GetId()) - { - case IDM_GOTOINMEMVIEW: - // CMemoryDlg::Goto(selection); - break; - -#if wxUSE_CLIPBOARD - case IDM_COPYADDRESS: - wxTheClipboard->SetData(new wxTextDataObject(wxString::Format(_T("%08x"), selection))); - break; - - case IDM_COPYCODE: - wxTheClipboard->SetData(new wxTextDataObject(wxString::FromAscii(debugger->disasm(selection)))); //Have to manually convert from char* to wxString, don't have to in Windows? - break; - - case IDM_COPYHEX: - { - char temp[24]; - sprintf(temp, "%08x", debugger->readInstruction(selection)); - wxTheClipboard->SetData(new wxTextDataObject(wxString::FromAscii(temp))); - } - break; - case IDM_COPYFUNCTION: - { - Symbol *symbol = g_symbolDB.GetSymbolFromAddr(selection); - if (symbol) { - std::string text; - text = text + symbol->name + "\r\n"; - // we got a function - u32 start = symbol->address; - u32 end = start + symbol->size; - for (u32 addr = start; addr != end; addr += 4) { - text = text + StringFromFormat("%08x: ", addr) + debugger->disasm(addr) + "\r\n"; - } - wxTheClipboard->SetData(new wxTextDataObject(wxString::FromAscii(text.c_str()))); - } - } - break; -#endif - - case IDM_RUNTOHERE: - debugger->setBreakpoint(selection); - debugger->runToBreakpoint(); - redraw(); - break; - - // Insert blr or restore old value - case IDM_INSERTBLR: - { - // Check if this address has been modified - int find = -1; - for(u32 i = 0; i < BlrList.size(); i++) - { - if(BlrList.at(i).Address == selection) - { find = i; break; } - } - - // Save the old value - if(find >= 0) - { - Memory::Write_U32(BlrList.at(find).OldValue, selection); - BlrList.erase(BlrList.begin() + find); - } - else - { - BlrStruct Temp; - Temp.Address = selection; - Temp.OldValue = debugger->readMemory(selection); - BlrList.push_back(Temp); - debugger->insertBLR(selection); - } - redraw(); - } - break; - - case IDM_JITRESULTS: - CJitWindow::ViewAddr(selection); - break; - - case IDM_FOLLOWBRANCH: - { - u32 dest = AddrToBranch(selection); - if (dest) - Center(dest); - RaiseEvent(); - } - break; - - case IDM_RENAMESYMBOL: - { - Symbol *symbol = g_symbolDB.GetSymbolFromAddr(selection); - if (symbol) { - wxTextEntryDialog input_symbol(this, wxString::FromAscii("Rename symbol:"), wxGetTextFromUserPromptStr, - wxString::FromAscii(symbol->name.c_str())); - if (input_symbol.ShowModal() == wxID_OK) { - symbol->name = input_symbol.GetValue().mb_str(); - } -// redraw(); - Host_NotifyMapLoaded(); - } - } - break; - - case IDM_PATCHALERT: - { - - } - break; - } - -#if wxUSE_CLIPBOARD - wxTheClipboard->Close(); -#endif - event.Skip(true); -} - - -void CCodeView::OnMouseUpR(wxMouseEvent& event) -{ - bool isSymbol = g_symbolDB.GetSymbolFromAddr(selection) != 0; - // popup menu - wxMenu menu; - //menu.Append(IDM_GOTOINMEMVIEW, "&Goto in mem view"); - menu.Append(IDM_FOLLOWBRANCH, wxString::FromAscii("&Follow branch"))->Enable(AddrToBranch(selection) ? true : false); - menu.AppendSeparator(); -#if wxUSE_CLIPBOARD - menu.Append(IDM_COPYADDRESS, wxString::FromAscii("Copy &address")); - menu.Append(IDM_COPYFUNCTION, wxString::FromAscii("Copy &function"))->Enable(isSymbol); - menu.Append(IDM_COPYCODE, wxString::FromAscii("Copy &code line")); - menu.Append(IDM_COPYHEX, wxString::FromAscii("Copy &hex")); - menu.AppendSeparator(); -#endif - menu.Append(IDM_RENAMESYMBOL, wxString::FromAscii("Rename &symbol"))->Enable(isSymbol); - menu.AppendSeparator(); - menu.Append(IDM_RUNTOHERE, _T("&Run To Here")); - menu.Append(IDM_JITRESULTS, wxString::FromAscii("PPC vs X86")); - menu.Append(IDM_INSERTBLR, wxString::FromAscii("Insert &blr")); - menu.Append(IDM_PATCHALERT, wxString::FromAscii("Patch alert")); - PopupMenu(&menu); - event.Skip(true); -} - - -void CCodeView::OnErase(wxEraseEvent& event) -{} - - -void CCodeView::OnPaint(wxPaintEvent& event) -{ - // -------------------------------------------------------------------- - // General settings - // ------------------------- - wxPaintDC dc(this); - wxRect rc = GetClientRect(); - wxFont font(7, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_LIGHT); - dc.SetFont(font); - struct branch - { - int src, dst, srcAddr; - }; - - branch branches[256]; - int numBranches = 0; - // TODO: Add any drawing code here... - int width = rc.width; - int numRows = (rc.height / rowHeight) / 2 + 2; - //numRows=(numRows&(~1)) + 1; - // ------------ - - - // -------------------------------------------------------------------- - // Colors and brushes - // ------------------------- - dc.SetBackgroundMode(wxTRANSPARENT); // the text background - const wxChar* bgColor = _T("#ffffff"); - wxPen nullPen(bgColor); - wxPen currentPen(_T("#000000")); - wxPen selPen(_T("#808080")); // gray - nullPen.SetStyle(wxTRANSPARENT); - currentPen.SetStyle(wxSOLID); - wxBrush currentBrush(_T("#FFEfE8")); // the ... ? ... is light gray - wxBrush pcBrush(_T("#70FF70")); // the selected code line is green - wxBrush bpBrush(_T("#FF3311")); // red - - wxBrush bgBrush(bgColor); - wxBrush nullBrush(bgColor); - nullBrush.SetStyle(wxTRANSPARENT); - - dc.SetPen(nullPen); - dc.SetBrush(bgBrush); - dc.DrawRectangle(0, 0, 16, rc.height); - dc.DrawRectangle(0, 0, rc.width, 5); - // ------------ - - - // -------------------------------------------------------------------- - // Walk through all visible rows - // ------------------------- - for (int i = -numRows; i <= numRows; i++) - { - unsigned int address = curAddress + i * align; - - int rowY1 = rc.height / 2 + rowHeight * i - rowHeight / 2; - int rowY2 = rc.height / 2 + rowHeight * i + rowHeight / 2; - - wxString temp = wxString::Format(_T("%08x"), address); - u32 col = debugger->getColor(address); - wxBrush rowBrush(wxColor(col >> 16, col >> 8, col)); - dc.SetBrush(nullBrush); - dc.SetPen(nullPen); - dc.DrawRectangle(0, rowY1, 16, rowY2 - rowY1 + 2); - - if (selecting && (address == selection)) - dc.SetPen(selPen); - else - dc.SetPen(i == 0 ? currentPen : nullPen); - - if (address == debugger->getPC()) - dc.SetBrush(pcBrush); - else - dc.SetBrush(rowBrush); - - dc.DrawRectangle(16, rowY1, width, rowY2 - rowY1 + 1); - dc.SetBrush(currentBrush); - dc.SetTextForeground(_T("#600000")); // the address text is dark red - dc.DrawText(temp, 17, rowY1); - dc.SetTextForeground(_T("#000000")); - - if (debugger->isAlive()) - { - char dis[256] = {0}; - strcpy(dis, debugger->disasm(address)); - char* dis2 = strchr(dis, '\t'); - char desc[256] = ""; - - // If we have a code - if (dis2) - { - *dis2 = 0; - dis2++; - const char* mojs = strstr(dis2, "0x8"); - - // -------------------------------------------------------------------- - // Colors and brushes - // ------------------------- - if (mojs) - { - for (int k = 0; k < 8; k++) - { - bool found = false; - - for (int j = 0; j < 22; j++) - { - if (mojs[k + 2] == "0123456789ABCDEFabcdef"[j]) - { - found = true; - } - } - if (!found) - { - mojs = 0; - break; - } - } - } - // ------------ - - - // -------------------------------------------------------------------- - // Colors and brushes - // ------------------------- - if (mojs) - { - int offs; - sscanf(mojs + 2, "%08x", &offs); - branches[numBranches].src = rowY1 + rowHeight / 2; - branches[numBranches].srcAddr = address / align; - branches[numBranches++].dst = (int)(rowY1 + ((s64)(u32)offs - (s64)(u32)address) * rowHeight / align + rowHeight / 2); - sprintf(desc, "-->%s", debugger->getDescription(offs).c_str()); - dc.SetTextForeground(_T("#600060")); // the -> arrow illustrations are purple - } - else - { - dc.SetTextForeground(_T("#000000")); - } - - dc.DrawText(wxString::FromAscii(dis2), 126, rowY1); - // ------------ - } - - // Show blr as its' own color - if (strcmp(dis, "blr")) - dc.SetTextForeground(_T("#007000")); // dark green - else - dc.SetTextForeground(_T("#8000FF")); // purple - - dc.DrawText(wxString::FromAscii(dis), 70, rowY1); - - if (desc[0] == 0) - { - strcpy(desc, debugger->getDescription(address).c_str()); - } - - dc.SetTextForeground(_T("#0000FF")); // blue - - //char temp[256]; - //UnDecorateSymbolName(desc,temp,255,UNDNAME_COMPLETE); - if (strlen(desc)) - { - dc.DrawText(wxString::FromAscii(desc), 235, rowY1); - } - - // Show red breakpoint dot - if (debugger->isBreakpoint(address)) - { - dc.SetBrush(bpBrush); - dc.DrawRectangle(2, rowY1, 7, 7); - //DrawIconEx(hdc, 2, rowY1, breakPoint, 32, 32, 0, 0, DI_NORMAL); - } - } - } // end of for - // ------------ - - - // -------------------------------------------------------------------- - // Colors and brushes - // ------------------------- - dc.SetPen(currentPen); - - for (int i = 0; i < numBranches; i++) - { - int x = 300 + (branches[i].srcAddr % 9) * 8; - _MoveTo(x-2, branches[i].src); - - if (branches[i].dst < rc.height + 400 && branches[i].dst > -400) - { - _LineTo(dc, x+2, branches[i].src); - _LineTo(dc, x+2, branches[i].dst); - _LineTo(dc, x-4, branches[i].dst); - - _MoveTo(x, branches[i].dst - 4); - _LineTo(dc, x-4, branches[i].dst); - _LineTo(dc, x+1, branches[i].dst+5); - } - else - { - _LineTo(dc, x+4, branches[i].src); - //MoveToEx(hdc,x+2,branches[i].dst-4,0); - //LineTo(hdc,x+6,branches[i].dst); - //LineTo(hdc,x+1,branches[i].dst+5); - } - - //LineTo(hdc,x,branches[i].dst+4); - //LineTo(hdc,x-2,branches[i].dst); - } - // ------------ -} - - -void CCodeView::_LineTo(wxPaintDC &dc, int x, int y) -{ - dc.DrawLine(lx, ly, x, y); - lx = x; - ly = y; -} - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Debugger.h" +#include "Debugger/PPCDebugInterface.h" +#include "PowerPC/SymbolDB.h" +#include "HW/Memmap.h" // for Write_U32 +#include "Common.h" +#include "StringUtil.h" + +#include "Host.h" +#include "CodeView.h" +#include "JitWindow.h" + +#include +#include +#include + +DEFINE_EVENT_TYPE(wxEVT_CODEVIEW_CHANGE); + +enum +{ + IDM_GOTOINMEMVIEW = 12000, + IDM_COPYADDRESS, + IDM_COPYHEX, + IDM_COPYCODE, + IDM_INSERTBLR, + IDM_RUNTOHERE, + IDM_JITRESULTS, + IDM_FOLLOWBRANCH, + IDM_RENAMESYMBOL, + IDM_PATCHALERT, + IDM_COPYFUNCTION, +}; + + +BEGIN_EVENT_TABLE(CCodeView, wxControl) + EVT_ERASE_BACKGROUND(CCodeView::OnErase) + EVT_PAINT(CCodeView::OnPaint) + EVT_LEFT_DOWN(CCodeView::OnMouseDown) + EVT_LEFT_UP(CCodeView::OnMouseUpL) + EVT_MOTION(CCodeView::OnMouseMove) + EVT_RIGHT_DOWN(CCodeView::OnMouseDown) + EVT_RIGHT_UP(CCodeView::OnMouseUpR) + EVT_MENU(-1, CCodeView::OnPopupMenu) +END_EVENT_TABLE() + +CCodeView::CCodeView(DebugInterface* debuginterface, wxWindow* parent, wxWindowID Id, const wxSize& Size) + : wxControl(parent, Id, wxDefaultPosition, Size), + debugger(debuginterface), + rowHeight(13), + selection(0), + oldSelection(0), + selectionChanged(false), + selecting(false), + hasFocus(false), + showHex(false), + lx(-1), + ly(-1) +{ + rowHeight = 13; + align = debuginterface->getInstructionSize(0); + curAddress = debuginterface->getPC(); + selection = 0; +} + + +wxSize CCodeView::DoGetBestSize() const +{ + wxSize bestSize; + bestSize.x = 400; + bestSize.y = 800; + return(bestSize); +} + + +int CCodeView::YToAddress(int y) +{ + wxRect rc = GetClientRect(); + int ydiff = y - rc.height / 2 - rowHeight / 2; + ydiff = (int)(floorf((float)ydiff / (float)rowHeight)) + 1; + return(curAddress + ydiff * align); +} + + +void CCodeView::OnMouseDown(wxMouseEvent& event) +{ + int x = event.m_x; + int y = event.m_y; + + if (x > 16) + { + oldSelection = selection; + selection = YToAddress(y); + // SetCapture(wnd); + bool oldselecting = selecting; + selecting = true; + + if (!oldselecting || (selection != oldSelection)) + { + redraw(); + } + } + else + { + debugger->toggleBreakpoint(YToAddress(y)); + redraw(); + } + + event.Skip(true); +} + + +void CCodeView::OnMouseMove(wxMouseEvent& event) +{ + wxRect rc = GetClientRect(); + + if (event.m_leftDown) + { + if (event.m_x > 16) + { + if (event.m_y < 0) + { + curAddress -= align; + redraw(); + } + else if (event.m_y > rc.height) + { + curAddress += align; + redraw(); + } + else + { + OnMouseDown(event); + } + } + } + + event.Skip(true); +} + +void CCodeView::RaiseEvent() +{ + wxCommandEvent ev(wxEVT_CODEVIEW_CHANGE, GetId()); + ev.SetEventObject(this); + ev.SetInt(selection); + GetEventHandler()->ProcessEvent(ev); +} + +void CCodeView::OnMouseUpL(wxMouseEvent& event) +{ + if (event.m_x > 16) + { + curAddress = YToAddress(event.m_y); + selecting = false; + //ReleaseCapture(); + redraw(); + } + RaiseEvent(); + event.Skip(true); +} + +u32 CCodeView::AddrToBranch(u32 addr) +{ + const char *temp = debugger->disasm(addr); + const char *mojs = strstr(temp, "->0x"); + if (mojs) + { + u32 dest; + sscanf(mojs+4,"%08x", &dest); + return dest; + } + return 0; +} + +void CCodeView::OnPopupMenu(wxCommandEvent& event) +{ +#if wxUSE_CLIPBOARD + wxTheClipboard->Open(); +#endif + + switch (event.GetId()) + { + case IDM_GOTOINMEMVIEW: + // CMemoryDlg::Goto(selection); + break; + +#if wxUSE_CLIPBOARD + case IDM_COPYADDRESS: + wxTheClipboard->SetData(new wxTextDataObject(wxString::Format(_T("%08x"), selection))); + break; + + case IDM_COPYCODE: + wxTheClipboard->SetData(new wxTextDataObject(wxString::FromAscii(debugger->disasm(selection)))); //Have to manually convert from char* to wxString, don't have to in Windows? + break; + + case IDM_COPYHEX: + { + char temp[24]; + sprintf(temp, "%08x", debugger->readInstruction(selection)); + wxTheClipboard->SetData(new wxTextDataObject(wxString::FromAscii(temp))); + } + break; + case IDM_COPYFUNCTION: + { + Symbol *symbol = g_symbolDB.GetSymbolFromAddr(selection); + if (symbol) { + std::string text; + text = text + symbol->name + "\r\n"; + // we got a function + u32 start = symbol->address; + u32 end = start + symbol->size; + for (u32 addr = start; addr != end; addr += 4) { + text = text + StringFromFormat("%08x: ", addr) + debugger->disasm(addr) + "\r\n"; + } + wxTheClipboard->SetData(new wxTextDataObject(wxString::FromAscii(text.c_str()))); + } + } + break; +#endif + + case IDM_RUNTOHERE: + debugger->setBreakpoint(selection); + debugger->runToBreakpoint(); + redraw(); + break; + + // Insert blr or restore old value + case IDM_INSERTBLR: + { + // Check if this address has been modified + int find = -1; + for(u32 i = 0; i < BlrList.size(); i++) + { + if(BlrList.at(i).Address == selection) + { find = i; break; } + } + + // Save the old value + if(find >= 0) + { + Memory::Write_U32(BlrList.at(find).OldValue, selection); + BlrList.erase(BlrList.begin() + find); + } + else + { + BlrStruct Temp; + Temp.Address = selection; + Temp.OldValue = debugger->readMemory(selection); + BlrList.push_back(Temp); + debugger->insertBLR(selection); + } + redraw(); + } + break; + + case IDM_JITRESULTS: + CJitWindow::ViewAddr(selection); + break; + + case IDM_FOLLOWBRANCH: + { + u32 dest = AddrToBranch(selection); + if (dest) + Center(dest); + RaiseEvent(); + } + break; + + case IDM_RENAMESYMBOL: + { + Symbol *symbol = g_symbolDB.GetSymbolFromAddr(selection); + if (symbol) { + wxTextEntryDialog input_symbol(this, wxString::FromAscii("Rename symbol:"), wxGetTextFromUserPromptStr, + wxString::FromAscii(symbol->name.c_str())); + if (input_symbol.ShowModal() == wxID_OK) { + symbol->name = input_symbol.GetValue().mb_str(); + } +// redraw(); + Host_NotifyMapLoaded(); + } + } + break; + + case IDM_PATCHALERT: + { + + } + break; + } + +#if wxUSE_CLIPBOARD + wxTheClipboard->Close(); +#endif + event.Skip(true); +} + + +void CCodeView::OnMouseUpR(wxMouseEvent& event) +{ + bool isSymbol = g_symbolDB.GetSymbolFromAddr(selection) != 0; + // popup menu + wxMenu menu; + //menu.Append(IDM_GOTOINMEMVIEW, "&Goto in mem view"); + menu.Append(IDM_FOLLOWBRANCH, wxString::FromAscii("&Follow branch"))->Enable(AddrToBranch(selection) ? true : false); + menu.AppendSeparator(); +#if wxUSE_CLIPBOARD + menu.Append(IDM_COPYADDRESS, wxString::FromAscii("Copy &address")); + menu.Append(IDM_COPYFUNCTION, wxString::FromAscii("Copy &function"))->Enable(isSymbol); + menu.Append(IDM_COPYCODE, wxString::FromAscii("Copy &code line")); + menu.Append(IDM_COPYHEX, wxString::FromAscii("Copy &hex")); + menu.AppendSeparator(); +#endif + menu.Append(IDM_RENAMESYMBOL, wxString::FromAscii("Rename &symbol"))->Enable(isSymbol); + menu.AppendSeparator(); + menu.Append(IDM_RUNTOHERE, _T("&Run To Here")); + menu.Append(IDM_JITRESULTS, wxString::FromAscii("PPC vs X86")); + menu.Append(IDM_INSERTBLR, wxString::FromAscii("Insert &blr")); + menu.Append(IDM_PATCHALERT, wxString::FromAscii("Patch alert")); + PopupMenu(&menu); + event.Skip(true); +} + + +void CCodeView::OnErase(wxEraseEvent& event) +{} + + +void CCodeView::OnPaint(wxPaintEvent& event) +{ + // -------------------------------------------------------------------- + // General settings + // ------------------------- + wxPaintDC dc(this); + wxRect rc = GetClientRect(); + wxFont font(7, wxFONTFAMILY_SWISS, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_LIGHT); + dc.SetFont(font); + struct branch + { + int src, dst, srcAddr; + }; + + branch branches[256]; + int numBranches = 0; + // TODO: Add any drawing code here... + int width = rc.width; + int numRows = (rc.height / rowHeight) / 2 + 2; + //numRows=(numRows&(~1)) + 1; + // ------------ + + + // -------------------------------------------------------------------- + // Colors and brushes + // ------------------------- + dc.SetBackgroundMode(wxTRANSPARENT); // the text background + const wxChar* bgColor = _T("#ffffff"); + wxPen nullPen(bgColor); + wxPen currentPen(_T("#000000")); + wxPen selPen(_T("#808080")); // gray + nullPen.SetStyle(wxTRANSPARENT); + currentPen.SetStyle(wxSOLID); + wxBrush currentBrush(_T("#FFEfE8")); // the ... ? ... is light gray + wxBrush pcBrush(_T("#70FF70")); // the selected code line is green + wxBrush bpBrush(_T("#FF3311")); // red + + wxBrush bgBrush(bgColor); + wxBrush nullBrush(bgColor); + nullBrush.SetStyle(wxTRANSPARENT); + + dc.SetPen(nullPen); + dc.SetBrush(bgBrush); + dc.DrawRectangle(0, 0, 16, rc.height); + dc.DrawRectangle(0, 0, rc.width, 5); + // ------------ + + + // -------------------------------------------------------------------- + // Walk through all visible rows + // ------------------------- + for (int i = -numRows; i <= numRows; i++) + { + unsigned int address = curAddress + i * align; + + int rowY1 = rc.height / 2 + rowHeight * i - rowHeight / 2; + int rowY2 = rc.height / 2 + rowHeight * i + rowHeight / 2; + + wxString temp = wxString::Format(_T("%08x"), address); + u32 col = debugger->getColor(address); + wxBrush rowBrush(wxColor(col >> 16, col >> 8, col)); + dc.SetBrush(nullBrush); + dc.SetPen(nullPen); + dc.DrawRectangle(0, rowY1, 16, rowY2 - rowY1 + 2); + + if (selecting && (address == selection)) + dc.SetPen(selPen); + else + dc.SetPen(i == 0 ? currentPen : nullPen); + + if (address == debugger->getPC()) + dc.SetBrush(pcBrush); + else + dc.SetBrush(rowBrush); + + dc.DrawRectangle(16, rowY1, width, rowY2 - rowY1 + 1); + dc.SetBrush(currentBrush); + dc.SetTextForeground(_T("#600000")); // the address text is dark red + dc.DrawText(temp, 17, rowY1); + dc.SetTextForeground(_T("#000000")); + + if (debugger->isAlive()) + { + char dis[256] = {0}; + strcpy(dis, debugger->disasm(address)); + char* dis2 = strchr(dis, '\t'); + char desc[256] = ""; + + // If we have a code + if (dis2) + { + *dis2 = 0; + dis2++; + const char* mojs = strstr(dis2, "0x8"); + + // -------------------------------------------------------------------- + // Colors and brushes + // ------------------------- + if (mojs) + { + for (int k = 0; k < 8; k++) + { + bool found = false; + + for (int j = 0; j < 22; j++) + { + if (mojs[k + 2] == "0123456789ABCDEFabcdef"[j]) + { + found = true; + } + } + if (!found) + { + mojs = 0; + break; + } + } + } + // ------------ + + + // -------------------------------------------------------------------- + // Colors and brushes + // ------------------------- + if (mojs) + { + int offs; + sscanf(mojs + 2, "%08x", &offs); + branches[numBranches].src = rowY1 + rowHeight / 2; + branches[numBranches].srcAddr = address / align; + branches[numBranches++].dst = (int)(rowY1 + ((s64)(u32)offs - (s64)(u32)address) * rowHeight / align + rowHeight / 2); + sprintf(desc, "-->%s", debugger->getDescription(offs).c_str()); + dc.SetTextForeground(_T("#600060")); // the -> arrow illustrations are purple + } + else + { + dc.SetTextForeground(_T("#000000")); + } + + dc.DrawText(wxString::FromAscii(dis2), 126, rowY1); + // ------------ + } + + // Show blr as its' own color + if (strcmp(dis, "blr")) + dc.SetTextForeground(_T("#007000")); // dark green + else + dc.SetTextForeground(_T("#8000FF")); // purple + + dc.DrawText(wxString::FromAscii(dis), 70, rowY1); + + if (desc[0] == 0) + { + strcpy(desc, debugger->getDescription(address).c_str()); + } + + dc.SetTextForeground(_T("#0000FF")); // blue + + //char temp[256]; + //UnDecorateSymbolName(desc,temp,255,UNDNAME_COMPLETE); + if (strlen(desc)) + { + dc.DrawText(wxString::FromAscii(desc), 235, rowY1); + } + + // Show red breakpoint dot + if (debugger->isBreakpoint(address)) + { + dc.SetBrush(bpBrush); + dc.DrawRectangle(2, rowY1, 7, 7); + //DrawIconEx(hdc, 2, rowY1, breakPoint, 32, 32, 0, 0, DI_NORMAL); + } + } + } // end of for + // ------------ + + + // -------------------------------------------------------------------- + // Colors and brushes + // ------------------------- + dc.SetPen(currentPen); + + for (int i = 0; i < numBranches; i++) + { + int x = 300 + (branches[i].srcAddr % 9) * 8; + _MoveTo(x-2, branches[i].src); + + if (branches[i].dst < rc.height + 400 && branches[i].dst > -400) + { + _LineTo(dc, x+2, branches[i].src); + _LineTo(dc, x+2, branches[i].dst); + _LineTo(dc, x-4, branches[i].dst); + + _MoveTo(x, branches[i].dst - 4); + _LineTo(dc, x-4, branches[i].dst); + _LineTo(dc, x+1, branches[i].dst+5); + } + else + { + _LineTo(dc, x+4, branches[i].src); + //MoveToEx(hdc,x+2,branches[i].dst-4,0); + //LineTo(hdc,x+6,branches[i].dst); + //LineTo(hdc,x+1,branches[i].dst+5); + } + + //LineTo(hdc,x,branches[i].dst+4); + //LineTo(hdc,x-2,branches[i].dst); + } + // ------------ +} + + +void CCodeView::_LineTo(wxPaintDC &dc, int x, int y) +{ + dc.DrawLine(lx, ly, x, y); + lx = x; + ly = y; +} + diff --git a/Source/Core/DebuggerWX/Src/CodeWindow.cpp b/Source/Core/DebuggerWX/Src/CodeWindow.cpp index 42a6ad9376..dfe1489559 100644 --- a/Source/Core/DebuggerWX/Src/CodeWindow.cpp +++ b/Source/Core/DebuggerWX/Src/CodeWindow.cpp @@ -1,1234 +1,1234 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include -#include -#include -#include -#include -#include - -// ugly that this lib included code from the main -#include "../../DolphinWX/Src/Globals.h" - -#include "IniFile.h" -#include "Host.h" - -#include "Debugger.h" - -#include "RegisterWindow.h" -#include "LogWindow.h" -#include "BreakpointWindow.h" -#include "MemoryWindow.h" -#include "JitWindow.h" - -#include "CodeWindow.h" -#include "CodeView.h" - -#include "FileUtil.h" -#include "Core.h" -#include "HLE/HLE.h" -#include "Boot/Boot.h" -#include "LogManager.h" -#include "HW/CPU.h" -#include "PowerPC/PowerPC.h" -#include "Debugger/PPCDebugInterface.h" -#include "Debugger/Debugger_SymbolMap.h" -#include "PowerPC/PPCAnalyst.h" -#include "PowerPC/Profiler.h" -#include "PowerPC/SymbolDB.h" -#include "PowerPC/SignatureDB.h" -#include "PowerPC/PPCTables.h" -#include "PowerPC/Jit64/Jit.h" -#include "PowerPC/Jit64/JitCache.h" // for ClearCache() - -#include "Plugins/Plugin_DSP.h" // new stuff, to let us open the DLLDebugger -#include "Plugins/Plugin_Video.h" // new stuff, to let us open the DLLDebugger -#include "../../DolphinWX/Src/PluginManager.h" -#include "../../DolphinWX/Src/Config.h" - -// and here are the classes -class CPluginInfo; -class CPluginManager; -//extern DynamicLibrary Common::CPlugin; -//extern CPluginManager CPluginManager::m_Instance; - -extern "C" { - #include "../resources/toolbar_play.c" - #include "../resources/toolbar_pause.c" - #include "../resources/toolbar_add_memorycheck.c" - #include "../resources/toolbar_delete.c" - #include "../resources/toolbar_add_breakpoint.c" -} - -static const long TOOLBAR_STYLE = wxTB_FLAT | wxTB_DOCKABLE | wxTB_TEXT; - -BEGIN_EVENT_TABLE(CCodeWindow, wxFrame) - EVT_LISTBOX(IDM_SYMBOLLIST, CCodeWindow::OnSymbolListChange) - EVT_LISTBOX(IDM_CALLSTACKLIST, CCodeWindow::OnCallstackListChange) - EVT_LISTBOX(IDM_CALLERSLIST, CCodeWindow::OnCallersListChange) - EVT_LISTBOX(IDM_CALLSLIST, CCodeWindow::OnCallsListChange) - EVT_HOST_COMMAND(wxID_ANY, CCodeWindow::OnHostMessage) - EVT_MENU(IDM_LOGWINDOW, CCodeWindow::OnToggleLogWindow) - EVT_MENU(IDM_REGISTERWINDOW, CCodeWindow::OnToggleRegisterWindow) - EVT_MENU(IDM_BREAKPOINTWINDOW, CCodeWindow::OnToggleBreakPointWindow) - EVT_MENU(IDM_MEMORYWINDOW, CCodeWindow::OnToggleMemoryWindow) - EVT_MENU(IDM_JITWINDOW, CCodeWindow::OnToggleJitWindow) - EVT_MENU(IDM_SOUNDWINDOW, CCodeWindow::OnToggleSoundWindow) - EVT_MENU(IDM_VIDEOWINDOW, CCodeWindow::OnToggleVideoWindow) - - EVT_MENU(IDM_INTERPRETER, CCodeWindow::OnInterpreter) // CPU Mode - EVT_MENU(IDM_AUTOMATICSTART, CCodeWindow::OnAutomaticStart) // CPU Mode - EVT_MENU(IDM_JITUNLIMITED, CCodeWindow::OnJITOff) - EVT_MENU(IDM_JITOFF, CCodeWindow::OnJITOff) - EVT_MENU(IDM_JITLSOFF, CCodeWindow::OnJITOff) - EVT_MENU(IDM_JITLSLXZOFF, CCodeWindow::OnJITOff) - EVT_MENU(IDM_JITLSLWZOFF, CCodeWindow::OnJITOff) - EVT_MENU(IDM_JITLSLBZXOFF, CCodeWindow::OnJITOff) - EVT_MENU(IDM_JITLSFOFF, CCodeWindow::OnJITOff) - EVT_MENU(IDM_JITLSPOFF, CCodeWindow::OnJITOff) - EVT_MENU(IDM_JITFPOFF, CCodeWindow::OnJITOff) - EVT_MENU(IDM_JITIOFF, CCodeWindow::OnJITOff) - EVT_MENU(IDM_JITPOFF, CCodeWindow::OnJITOff) - EVT_MENU(IDM_JITSROFF, CCodeWindow::OnJITOff) - - EVT_MENU(IDM_CLEARSYMBOLS, CCodeWindow::OnSymbolsMenu) - EVT_MENU(IDM_LOADMAPFILE, CCodeWindow::OnSymbolsMenu) - EVT_MENU(IDM_SCANFUNCTIONS, CCodeWindow::OnSymbolsMenu) - EVT_MENU(IDM_SAVEMAPFILE, CCodeWindow::OnSymbolsMenu) - EVT_MENU(IDM_SAVEMAPFILEWITHCODES, CCodeWindow::OnSymbolsMenu) - EVT_MENU(IDM_CREATESIGNATUREFILE, CCodeWindow::OnSymbolsMenu) - EVT_MENU(IDM_USESIGNATUREFILE, CCodeWindow::OnSymbolsMenu) - EVT_MENU(IDM_PATCHHLEFUNCTIONS, CCodeWindow::OnSymbolsMenu) - - EVT_MENU(IDM_CLEARCODECACHE, CCodeWindow::OnJitMenu) - EVT_MENU(IDM_LOGINSTRUCTIONS, CCodeWindow::OnJitMenu) - - EVT_MENU(IDM_PROFILEBLOCKS, CCodeWindow::OnProfilerMenu) - EVT_MENU(IDM_WRITEPROFILE, CCodeWindow::OnProfilerMenu) - - // toolbar - EVT_MENU(IDM_DEBUG_GO, CCodeWindow::OnCodeStep) - EVT_MENU(IDM_STEP, CCodeWindow::OnCodeStep) - EVT_MENU(IDM_STEPOVER, CCodeWindow::OnCodeStep) - EVT_MENU(IDM_SKIP, CCodeWindow::OnCodeStep) - EVT_MENU(IDM_SETPC, CCodeWindow::OnCodeStep) - EVT_MENU(IDM_GOTOPC, CCodeWindow::OnCodeStep) - EVT_TEXT(IDM_ADDRBOX, CCodeWindow::OnAddrBoxChange) - - EVT_COMMAND(IDM_CODEVIEW, wxEVT_CODEVIEW_CHANGE, CCodeWindow::OnCodeViewChange) -END_EVENT_TABLE() - -#define wxGetBitmapFromMemory(name) _wxGetBitmapFromMemory(name, sizeof(name)) -inline wxBitmap _wxGetBitmapFromMemory(const unsigned char* data, int length) -{ - wxMemoryInputStream is(data, length); - return(wxBitmap(wxImage(is, wxBITMAP_TYPE_ANY, -1), -1)); -} - -// ======================================================================================= -// WARNING: If you create a new dialog window you must add m_dialog(NULL) below otherwise -// m_dialog = true and things will crash. -// ---------------- -CCodeWindow::CCodeWindow(const SCoreStartupParameter& _LocalCoreStartupParameter, wxWindow* parent, wxWindowID id, - const wxString& title, const wxPoint& pos, const wxSize& size, long style) - : wxFrame(parent, id, title, pos, size, style) - , m_LogWindow(NULL) - , m_RegisterWindow(NULL) - , m_BreakpointWindow(NULL) - , m_MemoryWindow(NULL) - , m_JitWindow(NULL) -{ - // Load ini settings - IniFile file; - file.Load(DEBUGGER_CONFIG_FILE); - this->Load_(file); - - InitBitmaps(); - - CreateGUIControls(_LocalCoreStartupParameter); - - // Create the toolbar - RecreateToolbar(); - - UpdateButtonStates(); - - wxTheApp->Connect(wxID_ANY, wxEVT_KEY_DOWN, - wxKeyEventHandler(CCodeWindow::OnKeyDown), - (wxObject*)0, this); - - // Load settings for selectable windowses, but only if they have been created - this->Load(file); - if (m_BreakpointWindow) m_BreakpointWindow->Load(file); - if (m_RegisterWindow) m_RegisterWindow->Load(file); - if (m_MemoryWindow) m_MemoryWindow->Load(file); - if (m_JitWindow) m_JitWindow->Load(file); -} -// =============== - - -CCodeWindow::~CCodeWindow() -{ - IniFile file; - file.Load(DEBUGGER_CONFIG_FILE); - - this->Save(file); - if (m_BreakpointWindow) m_BreakpointWindow->Save(file); - if (m_LogWindow) m_LogWindow->Save(file); - if (m_RegisterWindow) m_RegisterWindow->Save(file); - if (m_MemoryWindow) m_MemoryWindow->Save(file); - if (m_JitWindow) m_JitWindow->Save(file); - - file.Save(DEBUGGER_CONFIG_FILE); -} - - -// ======================================================================================= -// Load before CreateGUIControls() -// -------------- -void CCodeWindow::Load_( IniFile &ini ) -{ - - // Decide what windows to use - ini.Get("ShowOnStart", "LogWindow", &bLogWindow, true); - ini.Get("ShowOnStart", "RegisterWindow", &bRegisterWindow, true); - ini.Get("ShowOnStart", "BreakpointWindow", &bBreakpointWindow, true); - ini.Get("ShowOnStart", "MemoryWindow", &bMemoryWindow, true); - ini.Get("ShowOnStart", "JitWindow", &bJitWindow, true); - ini.Get("ShowOnStart", "SoundWindow", &bSoundWindow, false); - ini.Get("ShowOnStart", "VideoWindow", &bVideoWindow, false); - // =============== - - // Boot to pause or not - ini.Get("ShowOnStart", "AutomaticStart", &bAutomaticStart, false); -} - - -void CCodeWindow::Load( IniFile &ini ) -{ - int x,y,w,h; - ini.Get("CodeWindow", "x", &x, GetPosition().x); - ini.Get("CodeWindow", "y", &y, GetPosition().y); - ini.Get("CodeWindow", "w", &w, GetSize().GetWidth()); - ini.Get("CodeWindow", "h", &h, GetSize().GetHeight()); - this->SetSize(x, y, w, h); -} - - -void CCodeWindow::Save(IniFile &ini) const -{ - ini.Set("CodeWindow", "x", GetPosition().x); - ini.Set("CodeWindow", "y", GetPosition().y); - ini.Set("CodeWindow", "w", GetSize().GetWidth()); - ini.Set("CodeWindow", "h", GetSize().GetHeight()); - - // Boot to pause or not - ini.Set("ShowOnStart", "AutomaticStart", GetMenuBar()->IsChecked(IDM_AUTOMATICSTART)); - - // Save windows settings - ini.Set("ShowOnStart", "LogWindow", GetMenuBar()->IsChecked(IDM_LOGWINDOW)); - ini.Set("ShowOnStart", "RegisterWindow", GetMenuBar()->IsChecked(IDM_REGISTERWINDOW)); - ini.Set("ShowOnStart", "BreakpointWindow", GetMenuBar()->IsChecked(IDM_BREAKPOINTWINDOW)); - ini.Set("ShowOnStart", "MemoryWindow", GetMenuBar()->IsChecked(IDM_MEMORYWINDOW)); - ini.Set("ShowOnStart", "JitWindow", GetMenuBar()->IsChecked(IDM_JITWINDOW)); - ini.Set("ShowOnStart", "SoundWindow", GetMenuBar()->IsChecked(IDM_SOUNDWINDOW)); - ini.Set("ShowOnStart", "VideoWindow", GetMenuBar()->IsChecked(IDM_VIDEOWINDOW)); -} - - -void CCodeWindow::CreateGUIControls(const SCoreStartupParameter& _LocalCoreStartupParameter) -{ - CreateMenu(_LocalCoreStartupParameter); - - // ======================================================================================= - // Configure the code window - wxBoxSizer* sizerBig = new wxBoxSizer(wxHORIZONTAL); - wxBoxSizer* sizerLeft = new wxBoxSizer(wxVERTICAL); - - DebugInterface* di = new PPCDebugInterface(); - - codeview = new CCodeView(di, this, IDM_CODEVIEW); - sizerBig->Add(sizerLeft, 2, wxEXPAND); - sizerBig->Add(codeview, 5, wxEXPAND); - - sizerLeft->Add(callstack = new wxListBox(this, IDM_CALLSTACKLIST, wxDefaultPosition, wxSize(90, 100)), 0, wxEXPAND); - sizerLeft->Add(symbols = new wxListBox(this, IDM_SYMBOLLIST, wxDefaultPosition, wxSize(90, 100), 0, NULL, wxLB_SORT), 1, wxEXPAND); - sizerLeft->Add(calls = new wxListBox(this, IDM_CALLSLIST, wxDefaultPosition, wxSize(90, 100), 0, NULL, wxLB_SORT), 0, wxEXPAND); - sizerLeft->Add(callers = new wxListBox(this, IDM_CALLERSLIST, wxDefaultPosition, wxSize(90, 100), 0, NULL, wxLB_SORT), 0, wxEXPAND); - - SetSizer(sizerBig); - - sizerLeft->SetSizeHints(this); - sizerLeft->Fit(this); - sizerBig->SetSizeHints(this); - sizerBig->Fit(this); - - sync_event.Init(); - // ================= - - - // additional dialogs - if (IsLoggingActivated() && bLogWindow) - { - m_LogWindow = new CLogWindow(this); - m_LogWindow->Show(true); - } - - if (bRegisterWindow) - { - m_RegisterWindow = new CRegisterWindow(this); - m_RegisterWindow->Show(true); - } - - if(bBreakpointWindow) - { - m_BreakpointWindow = new CBreakPointWindow(this, this); - m_BreakpointWindow->Show(true); - } - - if(bMemoryWindow) - { - m_MemoryWindow = new CMemoryWindow(this); - m_MemoryWindow->Show(true); - } - - if(bJitWindow) - { - m_JitWindow = new CJitWindow(this); - m_JitWindow->Show(true); - } - - if(bSoundWindow) - { - // possible todo: add some kind of if here to? can it fail? - CPluginManager::GetInstance().OpenDebug( - GetHandle(), - SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDSPPlugin.c_str(), - false, true - ); - } // don't have any else, just ignore it - - if(bVideoWindow) - { - // possible todo: add some kind of if here to? can it fail? - CPluginManager::GetInstance().OpenDebug( - GetHandle(), - _LocalCoreStartupParameter.m_strVideoPlugin.c_str(), - true, true - ); - } // don't have any else, just ignore it -} - - -void CCodeWindow::CreateMenu(const SCoreStartupParameter& _LocalCoreStartupParameter) -{ - - // ======================================================================================= - // Windowses - // --------------- - wxMenuBar* pMenuBar = new wxMenuBar(wxMB_DOCKABLE); - - { - wxMenu* pCoreMenu = new wxMenu; - - wxMenuItem* interpreter = pCoreMenu->Append(IDM_INTERPRETER, _T("&Interpreter core"), wxEmptyString, wxITEM_CHECK); - interpreter->Check(!_LocalCoreStartupParameter.bUseJIT); - pCoreMenu->AppendSeparator(); - wxMenuItem* automaticstart = pCoreMenu->Append(IDM_AUTOMATICSTART, _T("&Automatic start"), wxEmptyString, wxITEM_CHECK); - automaticstart->Check(bAutomaticStart); - pCoreMenu->AppendSeparator(); - -#ifdef JIT_OFF_OPTIONS - jitunlimited = pCoreMenu->Append(IDM_JITUNLIMITED, _T("&Unlimited JIT Cache"), wxEmptyString, wxITEM_CHECK); - pCoreMenu->AppendSeparator(); - jitoff = pCoreMenu->Append(IDM_JITOFF, _T("&JIT off (JIT core)"), wxEmptyString, wxITEM_CHECK); - jitlsoff = pCoreMenu->Append(IDM_JITLSOFF, _T("&JIT LoadStore off"), wxEmptyString, wxITEM_CHECK); - jitlslbzxoff = pCoreMenu->Append(IDM_JITLSLBZXOFF, _T(" &JIT LoadStore lbzx off"), wxEmptyString, wxITEM_CHECK); - jitlslxzoff = pCoreMenu->Append(IDM_JITLSLXZOFF, _T(" &JIT LoadStore lXz off"), wxEmptyString, wxITEM_CHECK); - jitlslwzoff = pCoreMenu->Append(IDM_JITLSLWZOFF, _T(" &JIT LoadStore lwz off"), wxEmptyString, wxITEM_CHECK); - jitlspoff = pCoreMenu->Append(IDM_JITLSFOFF, _T("&JIT LoadStore Floating off"), wxEmptyString, wxITEM_CHECK); - jitlsfoff = pCoreMenu->Append(IDM_JITLSPOFF, _T("&JIT LoadStore Paired off"), wxEmptyString, wxITEM_CHECK); - jitfpoff = pCoreMenu->Append(IDM_JITFPOFF, _T("&JIT FloatingPoint off"), wxEmptyString, wxITEM_CHECK); - jitioff = pCoreMenu->Append(IDM_JITIOFF, _T("&JIT Integer off"), wxEmptyString, wxITEM_CHECK); - jitpoff = pCoreMenu->Append(IDM_JITPOFF, _T("&JIT Paired off"), wxEmptyString, wxITEM_CHECK); - jitsroff = pCoreMenu->Append(IDM_JITSROFF, _T("&JIT SystemRegisters off"), wxEmptyString, wxITEM_CHECK); -#endif - -// wxMenuItem* dualcore = pDebugMenu->Append(IDM_DUALCORE, _T("&DualCore"), wxEmptyString, wxITEM_CHECK); -// dualcore->Check(_LocalCoreStartupParameter.bUseDualCore); - - pMenuBar->Append(pCoreMenu, _T("&CPU Mode")); - - } - - { - wxMenu* pDebugDialogs = new wxMenu; - - if (IsLoggingActivated()) - { - wxMenuItem* pLogWindow = pDebugDialogs->Append(IDM_LOGWINDOW, _T("&LogManager"), wxEmptyString, wxITEM_CHECK); - pLogWindow->Check(bLogWindow); - } - - wxMenuItem* pRegister = pDebugDialogs->Append(IDM_REGISTERWINDOW, _T("&Registers"), wxEmptyString, wxITEM_CHECK); - pRegister->Check(bRegisterWindow); - - wxMenuItem* pBreakPoints = pDebugDialogs->Append(IDM_BREAKPOINTWINDOW, _T("&BreakPoints"), wxEmptyString, wxITEM_CHECK); - pBreakPoints->Check(bBreakpointWindow); - - wxMenuItem* pMemory = pDebugDialogs->Append(IDM_MEMORYWINDOW, _T("&Memory"), wxEmptyString, wxITEM_CHECK); - pMemory->Check(bMemoryWindow); - - wxMenuItem* pJit = pDebugDialogs->Append(IDM_JITWINDOW, _T("&Jit"), wxEmptyString, wxITEM_CHECK); - pJit->Check(bJitWindow); - - wxMenuItem* pSound = pDebugDialogs->Append(IDM_SOUNDWINDOW, _T("&Sound"), wxEmptyString, wxITEM_CHECK); - pSound->Check(bSoundWindow); - - wxMenuItem* pVideo = pDebugDialogs->Append(IDM_VIDEOWINDOW, _T("&Video"), wxEmptyString, wxITEM_CHECK); - pVideo->Check(bVideoWindow); - - pMenuBar->Append(pDebugDialogs, _T("&Views")); - } - // =============== - - - { - wxMenu *pSymbolsMenu = new wxMenu; - pSymbolsMenu->Append(IDM_CLEARSYMBOLS, _T("&Clear symbols")); - // pSymbolsMenu->Append(IDM_CLEANSYMBOLS, _T("&Clean symbols (zz)")); - pSymbolsMenu->Append(IDM_SCANFUNCTIONS, _T("&Generate symbol map")); - pSymbolsMenu->AppendSeparator(); - pSymbolsMenu->Append(IDM_LOADMAPFILE, _T("&Load symbol map")); - pSymbolsMenu->Append(IDM_SAVEMAPFILE, _T("&Save symbol map")); - pSymbolsMenu->AppendSeparator(); - pSymbolsMenu->Append(IDM_SAVEMAPFILEWITHCODES, _T("Save code")); - pSymbolsMenu->AppendSeparator(); - pSymbolsMenu->Append(IDM_CREATESIGNATUREFILE, _T("&Create signature file...")); - pSymbolsMenu->Append(IDM_USESIGNATUREFILE, _T("&Use signature file...")); - pSymbolsMenu->AppendSeparator(); - pSymbolsMenu->Append(IDM_PATCHHLEFUNCTIONS, _T("&Patch HLE functions")); - pMenuBar->Append(pSymbolsMenu, _T("&Symbols")); - } - - { - wxMenu *pJitMenu = new wxMenu; - pJitMenu->Append(IDM_CLEARCODECACHE, _T("&Clear code cache")); - pJitMenu->Append(IDM_LOGINSTRUCTIONS, _T("&Log JIT instruction coverage")); - pMenuBar->Append(pJitMenu, _T("&JIT")); - } - - { - wxMenu *pProfilerMenu = new wxMenu; - pProfilerMenu->Append(IDM_PROFILEBLOCKS, _T("&Profile blocks"), wxEmptyString, wxITEM_CHECK); - pProfilerMenu->AppendSeparator(); - pProfilerMenu->Append(IDM_WRITEPROFILE, _T("&Write to profile.txt, show")); - pMenuBar->Append(pProfilerMenu, _T("&Profiler")); - } - - - SetMenuBar(pMenuBar); -} - - -bool CCodeWindow::UseInterpreter() -{ - return GetMenuBar()->IsChecked(IDM_INTERPRETER); -} - -bool CCodeWindow::AutomaticStart() -{ - return GetMenuBar()->IsChecked(IDM_AUTOMATICSTART); -} - -bool CCodeWindow::UseDualCore() -{ - return GetMenuBar()->IsChecked(IDM_DUALCORE); -} - - -// ======================================================================================= -// CPU Mode -// -------------- -void CCodeWindow::OnInterpreter(wxCommandEvent& event) -{ - if (Core::GetState() != Core::CORE_RUN) { - PowerPC::SetMode(UseInterpreter() ? PowerPC::MODE_INTERPRETER : PowerPC::MODE_JIT); - } else { - event.Skip(); - wxMessageBox(_T("Please pause the emulator before changing mode.")); - } -} - - -void CCodeWindow::OnAutomaticStart(wxCommandEvent& event) -{ - bAutomaticStart = !bAutomaticStart; -} - - -void CCodeWindow::OnJITOff(wxCommandEvent& event) -{ - if (Core::GetState() == Core::CORE_UNINITIALIZED) - { - // we disallow changing the status here because it will be reset to the defult when BootCore() - // creates the SCoreStartupParameter as a game is loaded - GetMenuBar()->Check(event.GetId(),!event.IsChecked()); - wxMessageBox(_T("Please start a game before changing mode.")); - } else { - if (Core::GetState() != Core::CORE_RUN) - { - switch (event.GetId()) - { - case IDM_JITUNLIMITED: - Core::g_CoreStartupParameter.bJITUnlimitedCache = event.IsChecked(); - Jit64::ClearCache(); // allow InitCache() even after the game has started - Jit64::InitCache(); - GetMenuBar()->Enable(event.GetId(),!event.IsChecked()); - break; - case IDM_JITOFF: - Core::g_CoreStartupParameter.bJITOff = event.IsChecked(); break; - case IDM_JITLSOFF: - Core::g_CoreStartupParameter.bJITLoadStoreOff = event.IsChecked(); break; - case IDM_JITLSLXZOFF: - Core::g_CoreStartupParameter.bJITLoadStorelXzOff = event.IsChecked(); break; - case IDM_JITLSLWZOFF: - Core::g_CoreStartupParameter.bJITLoadStorelwzOff = event.IsChecked(); break; - case IDM_JITLSLBZXOFF: - Core::g_CoreStartupParameter.bJITLoadStorelbzxOff = event.IsChecked(); break; - case IDM_JITLSFOFF: - Core::g_CoreStartupParameter.bJITLoadStoreFloatingOff = event.IsChecked(); break; - case IDM_JITLSPOFF: - Core::g_CoreStartupParameter.bJITLoadStorePairedOff = event.IsChecked(); break; - case IDM_JITFPOFF: - Core::g_CoreStartupParameter.bJITFloatingPointOff = event.IsChecked(); break; - case IDM_JITIOFF: - Core::g_CoreStartupParameter.bJITIntegerOff = event.IsChecked(); break; - case IDM_JITPOFF: - Core::g_CoreStartupParameter.bJITPairedOff = event.IsChecked(); break; - case IDM_JITSROFF: - Core::g_CoreStartupParameter.bJITSystemRegistersOff = event.IsChecked(); break; - } - Jit64::ClearCache(); - } else { - //event.Skip(); // this doesn't work - GetMenuBar()->Check(event.GetId(),!event.IsChecked()); - wxMessageBox(_T("Please pause the emulator before changing mode.")); - } - } -} -// ============== - - -void CCodeWindow::OnJitMenu(wxCommandEvent& event) -{ - switch (event.GetId()) - { - case IDM_CLEARCODECACHE: - Jit64::ClearCache(); - break; - case IDM_LOGINSTRUCTIONS: - PPCTables::LogCompiledInstructions(); - break; - } -} - -void CCodeWindow::OnProfilerMenu(wxCommandEvent& event) -{ - if (Core::GetState() == Core::CORE_RUN) { - event.Skip(); - return; - } - switch (event.GetId()) - { - case IDM_PROFILEBLOCKS: - Jit64::ClearCache(); - Profiler::g_ProfileBlocks = GetMenuBar()->IsChecked(IDM_PROFILEBLOCKS); - break; - case IDM_WRITEPROFILE: - Profiler::WriteProfileResults("profiler.txt"); - File::Launch("profiler.txt"); - break; - } -} - -void CCodeWindow::OnSymbolsMenu(wxCommandEvent& event) -{ - if (Core::GetState() == Core::CORE_UNINITIALIZED) - { - // TODO: disable menu items instead :P - return; - } - std::string mapfile = CBoot::GenerateMapFilename(); - switch (event.GetId()) - { - case IDM_CLEARSYMBOLS: - g_symbolDB.Clear(); - Host_NotifyMapLoaded(); - break; - case IDM_CLEANSYMBOLS: - g_symbolDB.Clear("zz"); - Host_NotifyMapLoaded(); - break; - case IDM_SCANFUNCTIONS: - { - PPCAnalyst::FindFunctions(0x80000000, 0x80400000, &g_symbolDB); - SignatureDB db; - if (db.Load(TOTALDB_FILE)) - db.Apply(&g_symbolDB); - - // HLE::PatchFunctions(); - NotifyMapLoaded(); - break; - } - case IDM_LOADMAPFILE: - if (!File::Exists(mapfile.c_str())) - { - g_symbolDB.Clear(); - PPCAnalyst::FindFunctions(0x80000000, 0x80400000, &g_symbolDB); - SignatureDB db; - if (db.Load(TOTALDB_FILE)) - db.Apply(&g_symbolDB); - } else { - g_symbolDB.LoadMap(mapfile.c_str()); - } - NotifyMapLoaded(); - break; - case IDM_SAVEMAPFILE: - g_symbolDB.SaveMap(mapfile.c_str()); - break; - case IDM_SAVEMAPFILEWITHCODES: - g_symbolDB.SaveMap(mapfile.c_str(), true); - break; - case IDM_CREATESIGNATUREFILE: - { - wxTextEntryDialog input_prefix(this, wxString::FromAscii("Only export symbols with prefix:"), wxGetTextFromUserPromptStr, _T(".")); - if (input_prefix.ShowModal() == wxID_OK) { - std::string prefix(input_prefix.GetValue().mb_str()); - - wxString path = wxFileSelector( - _T("Save signature as"), wxEmptyString, wxEmptyString, wxEmptyString, - _T("Dolphin Signature File (*.dsy)|*.dsy;"), wxFD_SAVE, - this); - if (path) { - SignatureDB db; - db.Initialize(&g_symbolDB, prefix.c_str()); - std::string filename(path.ToAscii()); // PPCAnalyst::SaveSignatureDB( - db.Save(path.ToAscii()); - } - } - } - break; - case IDM_USESIGNATUREFILE: - { - wxString path = wxFileSelector( - _T("Apply signature file"), wxEmptyString, wxEmptyString, wxEmptyString, - _T("Dolphin Signature File (*.dsy)|*.dsy;"), wxFD_OPEN | wxFD_FILE_MUST_EXIST, - this); - if (path) { - SignatureDB db; - db.Load(path.ToAscii()); - db.Apply(&g_symbolDB); - } - } - NotifyMapLoaded(); - break; - case IDM_PATCHHLEFUNCTIONS: - HLE::PatchFunctions(); - Update(); - break; - } -} - -// ======================================================================================= -// The Play, Stop, Step, Skip, Go to PC and Show PC buttons all go here -// -------------- -void CCodeWindow::OnCodeStep(wxCommandEvent& event) -{ - switch (event.GetId()) - { - case IDM_DEBUG_GO: - { - // [F|RES] prolly we should disable the other buttons in go mode too ... - JumpToAddress(PC); - - if (CCPU::IsStepping()) - { - CCPU::EnableStepping(false); - } - else - { - CCPU::EnableStepping(true); - Host_UpdateLogDisplay(); - } - - Update(); - } - break; - - case IDM_STEP: - SingleCPUStep(); - - break; - - case IDM_STEPOVER: - CCPU::EnableStepping(true); - break; - - case IDM_SKIP: - PC += 4; - Update(); - break; - - case IDM_SETPC: - PC = codeview->GetSelection(); - Update(); - break; - - case IDM_GOTOPC: - JumpToAddress(PC); - break; - } - - UpdateButtonStates(); -} - - -void CCodeWindow::JumpToAddress(u32 _Address) -{ - codeview->Center(_Address); - UpdateLists(); -} - - -void CCodeWindow::UpdateLists() -{ - callers->Clear(); - u32 addr = codeview->GetSelection(); - Symbol *symbol = g_symbolDB.GetSymbolFromAddr(addr); - if (!symbol) - return; - for (int i = 0; i < (int)symbol->callers.size(); i++) - { - u32 caller_addr = symbol->callers[i].callAddress; - Symbol *caller_symbol = g_symbolDB.GetSymbolFromAddr(caller_addr); - if (caller_symbol) { - int idx = callers->Append(wxString::Format( wxT("< %s (%08x)"), caller_symbol->name.c_str(), caller_addr)); - callers->SetClientData(idx, (void*)caller_addr); - } - } - - calls->Clear(); - for (int i = 0; i < (int)symbol->calls.size(); i++) - { - u32 call_addr = symbol->calls[i].function; - Symbol *call_symbol = g_symbolDB.GetSymbolFromAddr(call_addr); - if (call_symbol) { - int idx = calls->Append(wxString::Format(_T("> %s (%08x)"), call_symbol->name.c_str(), call_addr)); - calls->SetClientData(idx, (void*)call_addr); - } - } -} - - -void CCodeWindow::OnCodeViewChange(wxCommandEvent &event) -{ - //PanicAlert("boo"); - UpdateLists(); -} - -void CCodeWindow::OnAddrBoxChange(wxCommandEvent& event) -{ - wxTextCtrl* pAddrCtrl = (wxTextCtrl*)GetToolBar()->FindControl(IDM_ADDRBOX); - wxString txt = pAddrCtrl->GetValue(); - - std::string text(txt.mb_str()); - text = StripSpaces(text); - if (text.size() == 8) - { - u32 addr; - sscanf(text.c_str(), "%08x", &addr); - JumpToAddress(addr); - } - - event.Skip(1); -} - -void CCodeWindow::OnCallstackListChange(wxCommandEvent& event) -{ - int index = callstack->GetSelection(); - if (index >= 0) { - u32 address = (u32)(u64)(callstack->GetClientData(index)); - if (address) - JumpToAddress(address); - } -} - -void CCodeWindow::OnCallersListChange(wxCommandEvent& event) -{ - int index = callers->GetSelection(); - if (index >= 0) { - u32 address = (u32)(u64)(callers->GetClientData(index)); - if (address) - JumpToAddress(address); - } -} - -void CCodeWindow::OnCallsListChange(wxCommandEvent& event) -{ - int index = calls->GetSelection(); - if (index >= 0) { - u32 address = (u32)(u64)(calls->GetClientData(index)); - if (address) - JumpToAddress(address); - } -} - -void CCodeWindow::Update() -{ - codeview->Refresh(); - callstack->Clear(); - - std::vector stack; - - if (Debugger::GetCallstack(stack)) - { - for (size_t i = 0; i < stack.size(); i++) - { - int idx = callstack->Append(wxString::FromAscii(stack[i].Name.c_str())); - callstack->SetClientData(idx, (void*)(u64)stack[i].vAddress); - } - } - else - { - callstack->Append(wxString::FromAscii("invalid callstack")); - } - - UpdateButtonStates(); - - /* Automatically show the current PC position when a breakpoint is hit or - when we pause */ - codeview->Center(PC); -} - - -void CCodeWindow::NotifyMapLoaded() -{ - g_symbolDB.FillInCallers(); - symbols->Show(false); // hide it for faster filling - symbols->Clear(); - for (SymbolDB::XFuncMap::iterator iter = g_symbolDB.GetIterator(); iter != g_symbolDB.End(); iter++) - { - int idx = symbols->Append(wxString::FromAscii(iter->second.name.c_str())); - symbols->SetClientData(idx, (void*)&iter->second); - } - symbols->Show(true); - Update(); -} - - -void CCodeWindow::UpdateButtonStates() -{ - wxToolBar* toolBar = GetToolBar(); - if (Core::GetState() == Core::CORE_UNINITIALIZED) - { - toolBar->EnableTool(IDM_DEBUG_GO, false); - toolBar->EnableTool(IDM_STEP, false); - toolBar->EnableTool(IDM_STEPOVER, false); - toolBar->EnableTool(IDM_SKIP, false); - } - else - { - if (!CCPU::IsStepping()) - { - toolBar->SetToolShortHelp(IDM_DEBUG_GO, _T("&Pause")); - toolBar->SetToolNormalBitmap(IDM_DEBUG_GO, m_Bitmaps[Toolbar_Pause]); - toolBar->EnableTool(IDM_DEBUG_GO, true); - toolBar->EnableTool(IDM_STEP, false); - toolBar->EnableTool(IDM_STEPOVER, false); - toolBar->EnableTool(IDM_SKIP, false); - } - else - { - toolBar->SetToolShortHelp(IDM_DEBUG_GO, _T("&Play")); - toolBar->SetToolNormalBitmap(IDM_DEBUG_GO, m_Bitmaps[Toolbar_DebugGo]); - toolBar->EnableTool(IDM_DEBUG_GO, true); - toolBar->EnableTool(IDM_STEP, true); - toolBar->EnableTool(IDM_STEPOVER, true); - toolBar->EnableTool(IDM_SKIP, true); - } - } -} - - -void CCodeWindow::OnSymbolListChange(wxCommandEvent& event) -{ - int index = symbols->GetSelection(); - if (index >= 0) { - Symbol* pSymbol = static_cast(symbols->GetClientData(index)); - if (pSymbol != NULL) - { - if(pSymbol->type == Symbol::SYMBOL_DATA) - { - if(m_MemoryWindow && m_MemoryWindow->IsVisible()) - m_MemoryWindow->JumpToAddress(pSymbol->address); - } - else - { - JumpToAddress(pSymbol->address); - } - } - } -} - -void CCodeWindow::OnSymbolListContextMenu(wxContextMenuEvent& event) -{ -} - - -void CCodeWindow::OnToggleLogWindow(wxCommandEvent& event) -{ - if (IsLoggingActivated()) - { - bool show = GetMenuBar()->IsChecked(event.GetId()); - - if (show) - { - if (!m_LogWindow) - { - m_LogWindow = new CLogWindow(this); - } - - m_LogWindow->Show(true); - } - else // hide - { - // If m_dialog is NULL, then possibly the system - // didn't report the checked menu item status correctly. - // It should be true just after the menu item was selected, - // if there was no modeless dialog yet. - wxASSERT(m_LogWindow != NULL); - - if (m_LogWindow) - { - m_LogWindow->Hide(); - } - } - } -} - - -void CCodeWindow::OnToggleRegisterWindow(wxCommandEvent& event) -{ - bool show = GetMenuBar()->IsChecked(event.GetId()); - - if (show) - { - if (!m_RegisterWindow) - { - m_RegisterWindow = new CRegisterWindow(this); - } - - m_RegisterWindow->Show(true); - } - else // hide - { - // If m_dialog is NULL, then possibly the system - // didn't report the checked menu item status correctly. - // It should be true just after the menu item was selected, - // if there was no modeless dialog yet. - wxASSERT(m_RegisterWindow != NULL); - - if (m_RegisterWindow) - { - m_RegisterWindow->Hide(); - } - } -} - - -// ======================================================================================= -// Toggle Sound Debugging Window -// ------------ -void CCodeWindow::OnToggleSoundWindow(wxCommandEvent& event) -{ - bool show = GetMenuBar()->IsChecked(event.GetId()); - - if (show) - { - // TODO: add some kind of if() check here to? - CPluginManager::GetInstance().OpenDebug( - GetHandle(), - SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDSPPlugin.c_str(), - false, true // DSP, show - ); - } - else // hide - { - // Close the sound dll that has an open debugger - CPluginManager::GetInstance().OpenDebug( - GetHandle(), - SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDSPPlugin.c_str(), - false, false // DSP, hide - ); - } -} -// =========== - - -// ======================================================================================= -// Toggle Video Debugging Window -// ------------ -void CCodeWindow::OnToggleVideoWindow(wxCommandEvent& event) -{ - bool show = GetMenuBar()->IsChecked(event.GetId()); - //GetMenuBar()->Check(event.GetId(), false); // Turn off - - if (show) - { - // It works now, but I'll keep this message in case the problem reappears - /*if(Core::GetState() == Core::CORE_UNINITIALIZED) - { - wxMessageBox(_T("Warning, opening this window before a game is started \n\ -may cause a crash when a game is later started. Todo: figure out why and fix it."), wxT("OpenGL Debugging Window")); - }*/ - - // TODO: add some kind of if() check here to? - CPluginManager::GetInstance().OpenDebug( - GetHandle(), - SConfig::GetInstance().m_LocalCoreStartupParameter.m_strVideoPlugin.c_str(), - true, true // Video, show - ); - } - else // hide - { - // Close the video dll that has an open debugger - CPluginManager::GetInstance().OpenDebug( - GetHandle(), - SConfig::GetInstance().m_LocalCoreStartupParameter.m_strVideoPlugin.c_str(), - true, false // Video, hide - ); - } -} -// =========== - - -void CCodeWindow::OnToggleJitWindow(wxCommandEvent& event) -{ - bool show = GetMenuBar()->IsChecked(event.GetId()); - - if (show) - { - if (!m_JitWindow) - { - m_JitWindow = new CJitWindow(this); - } - - m_JitWindow->Show(true); - } - else // hide - { - // If m_dialog is NULL, then possibly the system - // didn't report the checked menu item status correctly. - // It should be true just after the menu item was selected, - // if there was no modeless dialog yet. - wxASSERT(m_JitWindow != NULL); - - if (m_JitWindow) - { - m_JitWindow->Hide(); - } - } -} - - -void CCodeWindow::OnToggleBreakPointWindow(wxCommandEvent& event) -{ - bool show = GetMenuBar()->IsChecked(event.GetId()); - - if (show) - { - if (!m_BreakpointWindow) - { - m_BreakpointWindow = new CBreakPointWindow(this, this); - } - - m_BreakpointWindow->Show(true); - } - else // hide - { - // If m_dialog is NULL, then possibly the system - // didn't report the checked menu item status correctly. - // It should be true just after the menu item was selected, - // if there was no modeless dialog yet. - wxASSERT(m_BreakpointWindow != NULL); - - if (m_BreakpointWindow) - { - m_BreakpointWindow->Hide(); - } - } -} - -void CCodeWindow::OnToggleMemoryWindow(wxCommandEvent& event) -{ - bool show = GetMenuBar()->IsChecked(event.GetId()); - - if (show) - { - if (!m_MemoryWindow) - { - m_MemoryWindow = new CMemoryWindow(this); - } - - m_MemoryWindow->Show(true); - } - else // hide - { - // If m_dialog is NULL, then possibly the system - // didn't report the checked menu item status correctly. - // It should be true just after the menu item was selected, - // if there was no modeless dialog yet. - wxASSERT(m_MemoryWindow != NULL); - - if (m_MemoryWindow) - { - m_MemoryWindow->Hide(); - } - } -} - -void CCodeWindow::OnHostMessage(wxCommandEvent& event) -{ - switch (event.GetId()) - { - case IDM_NOTIFYMAPLOADED: - NotifyMapLoaded(); - break; - - case IDM_UPDATELOGDISPLAY: - - if (m_LogWindow) - { - m_LogWindow->NotifyUpdate(); - } - - break; - - case IDM_UPDATEDISASMDIALOG: - Update(); - - if (m_RegisterWindow) - { - m_RegisterWindow->NotifyUpdate(); - } - break; - - case IDM_UPDATEBREAKPOINTS: - Update(); - - if (m_BreakpointWindow) - { - m_BreakpointWindow->NotifyUpdate(); - } - break; - - } -} - -void CCodeWindow::PopulateToolbar(wxToolBar* toolBar) -{ - int w = m_Bitmaps[Toolbar_DebugGo].GetWidth(), - h = m_Bitmaps[Toolbar_DebugGo].GetHeight(); - - toolBar->SetToolBitmapSize(wxSize(w, h)); - toolBar->AddTool(IDM_DEBUG_GO, _T("Play"), m_Bitmaps[Toolbar_DebugGo]); - toolBar->AddTool(IDM_STEP, _T("Step"), m_Bitmaps[Toolbar_Step]); - toolBar->AddTool(IDM_STEPOVER, _T("Step Over"), m_Bitmaps[Toolbar_StepOver]); - toolBar->AddTool(IDM_SKIP, _T("Skip"), m_Bitmaps[Toolbar_Skip]); - toolBar->AddSeparator(); - toolBar->AddTool(IDM_GOTOPC, _T("Show PC"), m_Bitmaps[Toolbar_GotoPC]); - toolBar->AddTool(IDM_SETPC, _T("Set PC"), m_Bitmaps[Toolbar_SetPC]); - toolBar->AddSeparator(); - toolBar->AddControl(new wxTextCtrl(toolBar, IDM_ADDRBOX, _T(""))); - - // after adding the buttons to the toolbar, must call Realize() to reflect - // the changes - toolBar->Realize(); -} - - -void CCodeWindow::RecreateToolbar() -{ - // delete and recreate the toolbar - wxToolBarBase* toolBar = GetToolBar(); - delete toolBar; - SetToolBar(NULL); - - long style = TOOLBAR_STYLE; - style &= ~(wxTB_HORIZONTAL | wxTB_VERTICAL | wxTB_BOTTOM | wxTB_RIGHT | wxTB_HORZ_LAYOUT | wxTB_TOP); - wxToolBar* theToolBar = CreateToolBar(style, ID_TOOLBAR); - - PopulateToolbar(theToolBar); - SetToolBar(theToolBar); -} - - -void CCodeWindow::InitBitmaps() -{ - // load original size 48x48 - m_Bitmaps[Toolbar_DebugGo] = wxGetBitmapFromMemory(toolbar_play_png); - m_Bitmaps[Toolbar_Step] = wxGetBitmapFromMemory(toolbar_add_breakpoint_png); - m_Bitmaps[Toolbar_StepOver] = wxGetBitmapFromMemory(toolbar_add_memcheck_png); - m_Bitmaps[Toolbar_Skip] = wxGetBitmapFromMemory(toolbar_add_memcheck_png); - m_Bitmaps[Toolbar_GotoPC] = wxGetBitmapFromMemory(toolbar_add_memcheck_png); - m_Bitmaps[Toolbar_SetPC] = wxGetBitmapFromMemory(toolbar_add_memcheck_png); - m_Bitmaps[Toolbar_Pause] = wxGetBitmapFromMemory(toolbar_pause_png); - - - // scale to 16x16 for toolbar - for (size_t n = Toolbar_DebugGo; n < Bitmaps_max; n++) - { - m_Bitmaps[n] = wxBitmap(m_Bitmaps[n].ConvertToImage().Scale(16, 16)); - } -} - - -void CCodeWindow::OnKeyDown(wxKeyEvent& event) -{ - if ((event.GetKeyCode() == WXK_SPACE) && IsActive()) - { - SingleCPUStep(); - } - else - { - event.Skip(); - } -} - - -void CCodeWindow::SingleCPUStep() -{ - CCPU::StepOpcode(&sync_event); - // if (CCPU::IsStepping()) - // sync_event.Wait(); - wxThread::Sleep(20); - // need a short wait here - JumpToAddress(PC); - Update(); - Host_UpdateLogDisplay(); -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include +#include +#include +#include +#include + +// ugly that this lib included code from the main +#include "../../DolphinWX/Src/Globals.h" + +#include "IniFile.h" +#include "Host.h" + +#include "Debugger.h" + +#include "RegisterWindow.h" +#include "LogWindow.h" +#include "BreakpointWindow.h" +#include "MemoryWindow.h" +#include "JitWindow.h" + +#include "CodeWindow.h" +#include "CodeView.h" + +#include "FileUtil.h" +#include "Core.h" +#include "HLE/HLE.h" +#include "Boot/Boot.h" +#include "LogManager.h" +#include "HW/CPU.h" +#include "PowerPC/PowerPC.h" +#include "Debugger/PPCDebugInterface.h" +#include "Debugger/Debugger_SymbolMap.h" +#include "PowerPC/PPCAnalyst.h" +#include "PowerPC/Profiler.h" +#include "PowerPC/SymbolDB.h" +#include "PowerPC/SignatureDB.h" +#include "PowerPC/PPCTables.h" +#include "PowerPC/Jit64/Jit.h" +#include "PowerPC/Jit64/JitCache.h" // for ClearCache() + +#include "Plugins/Plugin_DSP.h" // new stuff, to let us open the DLLDebugger +#include "Plugins/Plugin_Video.h" // new stuff, to let us open the DLLDebugger +#include "../../DolphinWX/Src/PluginManager.h" +#include "../../DolphinWX/Src/Config.h" + +// and here are the classes +class CPluginInfo; +class CPluginManager; +//extern DynamicLibrary Common::CPlugin; +//extern CPluginManager CPluginManager::m_Instance; + +extern "C" { + #include "../resources/toolbar_play.c" + #include "../resources/toolbar_pause.c" + #include "../resources/toolbar_add_memorycheck.c" + #include "../resources/toolbar_delete.c" + #include "../resources/toolbar_add_breakpoint.c" +} + +static const long TOOLBAR_STYLE = wxTB_FLAT | wxTB_DOCKABLE | wxTB_TEXT; + +BEGIN_EVENT_TABLE(CCodeWindow, wxFrame) + EVT_LISTBOX(IDM_SYMBOLLIST, CCodeWindow::OnSymbolListChange) + EVT_LISTBOX(IDM_CALLSTACKLIST, CCodeWindow::OnCallstackListChange) + EVT_LISTBOX(IDM_CALLERSLIST, CCodeWindow::OnCallersListChange) + EVT_LISTBOX(IDM_CALLSLIST, CCodeWindow::OnCallsListChange) + EVT_HOST_COMMAND(wxID_ANY, CCodeWindow::OnHostMessage) + EVT_MENU(IDM_LOGWINDOW, CCodeWindow::OnToggleLogWindow) + EVT_MENU(IDM_REGISTERWINDOW, CCodeWindow::OnToggleRegisterWindow) + EVT_MENU(IDM_BREAKPOINTWINDOW, CCodeWindow::OnToggleBreakPointWindow) + EVT_MENU(IDM_MEMORYWINDOW, CCodeWindow::OnToggleMemoryWindow) + EVT_MENU(IDM_JITWINDOW, CCodeWindow::OnToggleJitWindow) + EVT_MENU(IDM_SOUNDWINDOW, CCodeWindow::OnToggleSoundWindow) + EVT_MENU(IDM_VIDEOWINDOW, CCodeWindow::OnToggleVideoWindow) + + EVT_MENU(IDM_INTERPRETER, CCodeWindow::OnInterpreter) // CPU Mode + EVT_MENU(IDM_AUTOMATICSTART, CCodeWindow::OnAutomaticStart) // CPU Mode + EVT_MENU(IDM_JITUNLIMITED, CCodeWindow::OnJITOff) + EVT_MENU(IDM_JITOFF, CCodeWindow::OnJITOff) + EVT_MENU(IDM_JITLSOFF, CCodeWindow::OnJITOff) + EVT_MENU(IDM_JITLSLXZOFF, CCodeWindow::OnJITOff) + EVT_MENU(IDM_JITLSLWZOFF, CCodeWindow::OnJITOff) + EVT_MENU(IDM_JITLSLBZXOFF, CCodeWindow::OnJITOff) + EVT_MENU(IDM_JITLSFOFF, CCodeWindow::OnJITOff) + EVT_MENU(IDM_JITLSPOFF, CCodeWindow::OnJITOff) + EVT_MENU(IDM_JITFPOFF, CCodeWindow::OnJITOff) + EVT_MENU(IDM_JITIOFF, CCodeWindow::OnJITOff) + EVT_MENU(IDM_JITPOFF, CCodeWindow::OnJITOff) + EVT_MENU(IDM_JITSROFF, CCodeWindow::OnJITOff) + + EVT_MENU(IDM_CLEARSYMBOLS, CCodeWindow::OnSymbolsMenu) + EVT_MENU(IDM_LOADMAPFILE, CCodeWindow::OnSymbolsMenu) + EVT_MENU(IDM_SCANFUNCTIONS, CCodeWindow::OnSymbolsMenu) + EVT_MENU(IDM_SAVEMAPFILE, CCodeWindow::OnSymbolsMenu) + EVT_MENU(IDM_SAVEMAPFILEWITHCODES, CCodeWindow::OnSymbolsMenu) + EVT_MENU(IDM_CREATESIGNATUREFILE, CCodeWindow::OnSymbolsMenu) + EVT_MENU(IDM_USESIGNATUREFILE, CCodeWindow::OnSymbolsMenu) + EVT_MENU(IDM_PATCHHLEFUNCTIONS, CCodeWindow::OnSymbolsMenu) + + EVT_MENU(IDM_CLEARCODECACHE, CCodeWindow::OnJitMenu) + EVT_MENU(IDM_LOGINSTRUCTIONS, CCodeWindow::OnJitMenu) + + EVT_MENU(IDM_PROFILEBLOCKS, CCodeWindow::OnProfilerMenu) + EVT_MENU(IDM_WRITEPROFILE, CCodeWindow::OnProfilerMenu) + + // toolbar + EVT_MENU(IDM_DEBUG_GO, CCodeWindow::OnCodeStep) + EVT_MENU(IDM_STEP, CCodeWindow::OnCodeStep) + EVT_MENU(IDM_STEPOVER, CCodeWindow::OnCodeStep) + EVT_MENU(IDM_SKIP, CCodeWindow::OnCodeStep) + EVT_MENU(IDM_SETPC, CCodeWindow::OnCodeStep) + EVT_MENU(IDM_GOTOPC, CCodeWindow::OnCodeStep) + EVT_TEXT(IDM_ADDRBOX, CCodeWindow::OnAddrBoxChange) + + EVT_COMMAND(IDM_CODEVIEW, wxEVT_CODEVIEW_CHANGE, CCodeWindow::OnCodeViewChange) +END_EVENT_TABLE() + +#define wxGetBitmapFromMemory(name) _wxGetBitmapFromMemory(name, sizeof(name)) +inline wxBitmap _wxGetBitmapFromMemory(const unsigned char* data, int length) +{ + wxMemoryInputStream is(data, length); + return(wxBitmap(wxImage(is, wxBITMAP_TYPE_ANY, -1), -1)); +} + +// ======================================================================================= +// WARNING: If you create a new dialog window you must add m_dialog(NULL) below otherwise +// m_dialog = true and things will crash. +// ---------------- +CCodeWindow::CCodeWindow(const SCoreStartupParameter& _LocalCoreStartupParameter, wxWindow* parent, wxWindowID id, + const wxString& title, const wxPoint& pos, const wxSize& size, long style) + : wxFrame(parent, id, title, pos, size, style) + , m_LogWindow(NULL) + , m_RegisterWindow(NULL) + , m_BreakpointWindow(NULL) + , m_MemoryWindow(NULL) + , m_JitWindow(NULL) +{ + // Load ini settings + IniFile file; + file.Load(DEBUGGER_CONFIG_FILE); + this->Load_(file); + + InitBitmaps(); + + CreateGUIControls(_LocalCoreStartupParameter); + + // Create the toolbar + RecreateToolbar(); + + UpdateButtonStates(); + + wxTheApp->Connect(wxID_ANY, wxEVT_KEY_DOWN, + wxKeyEventHandler(CCodeWindow::OnKeyDown), + (wxObject*)0, this); + + // Load settings for selectable windowses, but only if they have been created + this->Load(file); + if (m_BreakpointWindow) m_BreakpointWindow->Load(file); + if (m_RegisterWindow) m_RegisterWindow->Load(file); + if (m_MemoryWindow) m_MemoryWindow->Load(file); + if (m_JitWindow) m_JitWindow->Load(file); +} +// =============== + + +CCodeWindow::~CCodeWindow() +{ + IniFile file; + file.Load(DEBUGGER_CONFIG_FILE); + + this->Save(file); + if (m_BreakpointWindow) m_BreakpointWindow->Save(file); + if (m_LogWindow) m_LogWindow->Save(file); + if (m_RegisterWindow) m_RegisterWindow->Save(file); + if (m_MemoryWindow) m_MemoryWindow->Save(file); + if (m_JitWindow) m_JitWindow->Save(file); + + file.Save(DEBUGGER_CONFIG_FILE); +} + + +// ======================================================================================= +// Load before CreateGUIControls() +// -------------- +void CCodeWindow::Load_( IniFile &ini ) +{ + + // Decide what windows to use + ini.Get("ShowOnStart", "LogWindow", &bLogWindow, true); + ini.Get("ShowOnStart", "RegisterWindow", &bRegisterWindow, true); + ini.Get("ShowOnStart", "BreakpointWindow", &bBreakpointWindow, true); + ini.Get("ShowOnStart", "MemoryWindow", &bMemoryWindow, true); + ini.Get("ShowOnStart", "JitWindow", &bJitWindow, true); + ini.Get("ShowOnStart", "SoundWindow", &bSoundWindow, false); + ini.Get("ShowOnStart", "VideoWindow", &bVideoWindow, false); + // =============== + + // Boot to pause or not + ini.Get("ShowOnStart", "AutomaticStart", &bAutomaticStart, false); +} + + +void CCodeWindow::Load( IniFile &ini ) +{ + int x,y,w,h; + ini.Get("CodeWindow", "x", &x, GetPosition().x); + ini.Get("CodeWindow", "y", &y, GetPosition().y); + ini.Get("CodeWindow", "w", &w, GetSize().GetWidth()); + ini.Get("CodeWindow", "h", &h, GetSize().GetHeight()); + this->SetSize(x, y, w, h); +} + + +void CCodeWindow::Save(IniFile &ini) const +{ + ini.Set("CodeWindow", "x", GetPosition().x); + ini.Set("CodeWindow", "y", GetPosition().y); + ini.Set("CodeWindow", "w", GetSize().GetWidth()); + ini.Set("CodeWindow", "h", GetSize().GetHeight()); + + // Boot to pause or not + ini.Set("ShowOnStart", "AutomaticStart", GetMenuBar()->IsChecked(IDM_AUTOMATICSTART)); + + // Save windows settings + ini.Set("ShowOnStart", "LogWindow", GetMenuBar()->IsChecked(IDM_LOGWINDOW)); + ini.Set("ShowOnStart", "RegisterWindow", GetMenuBar()->IsChecked(IDM_REGISTERWINDOW)); + ini.Set("ShowOnStart", "BreakpointWindow", GetMenuBar()->IsChecked(IDM_BREAKPOINTWINDOW)); + ini.Set("ShowOnStart", "MemoryWindow", GetMenuBar()->IsChecked(IDM_MEMORYWINDOW)); + ini.Set("ShowOnStart", "JitWindow", GetMenuBar()->IsChecked(IDM_JITWINDOW)); + ini.Set("ShowOnStart", "SoundWindow", GetMenuBar()->IsChecked(IDM_SOUNDWINDOW)); + ini.Set("ShowOnStart", "VideoWindow", GetMenuBar()->IsChecked(IDM_VIDEOWINDOW)); +} + + +void CCodeWindow::CreateGUIControls(const SCoreStartupParameter& _LocalCoreStartupParameter) +{ + CreateMenu(_LocalCoreStartupParameter); + + // ======================================================================================= + // Configure the code window + wxBoxSizer* sizerBig = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer* sizerLeft = new wxBoxSizer(wxVERTICAL); + + DebugInterface* di = new PPCDebugInterface(); + + codeview = new CCodeView(di, this, IDM_CODEVIEW); + sizerBig->Add(sizerLeft, 2, wxEXPAND); + sizerBig->Add(codeview, 5, wxEXPAND); + + sizerLeft->Add(callstack = new wxListBox(this, IDM_CALLSTACKLIST, wxDefaultPosition, wxSize(90, 100)), 0, wxEXPAND); + sizerLeft->Add(symbols = new wxListBox(this, IDM_SYMBOLLIST, wxDefaultPosition, wxSize(90, 100), 0, NULL, wxLB_SORT), 1, wxEXPAND); + sizerLeft->Add(calls = new wxListBox(this, IDM_CALLSLIST, wxDefaultPosition, wxSize(90, 100), 0, NULL, wxLB_SORT), 0, wxEXPAND); + sizerLeft->Add(callers = new wxListBox(this, IDM_CALLERSLIST, wxDefaultPosition, wxSize(90, 100), 0, NULL, wxLB_SORT), 0, wxEXPAND); + + SetSizer(sizerBig); + + sizerLeft->SetSizeHints(this); + sizerLeft->Fit(this); + sizerBig->SetSizeHints(this); + sizerBig->Fit(this); + + sync_event.Init(); + // ================= + + + // additional dialogs + if (IsLoggingActivated() && bLogWindow) + { + m_LogWindow = new CLogWindow(this); + m_LogWindow->Show(true); + } + + if (bRegisterWindow) + { + m_RegisterWindow = new CRegisterWindow(this); + m_RegisterWindow->Show(true); + } + + if(bBreakpointWindow) + { + m_BreakpointWindow = new CBreakPointWindow(this, this); + m_BreakpointWindow->Show(true); + } + + if(bMemoryWindow) + { + m_MemoryWindow = new CMemoryWindow(this); + m_MemoryWindow->Show(true); + } + + if(bJitWindow) + { + m_JitWindow = new CJitWindow(this); + m_JitWindow->Show(true); + } + + if(bSoundWindow) + { + // possible todo: add some kind of if here to? can it fail? + CPluginManager::GetInstance().OpenDebug( + GetHandle(), + SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDSPPlugin.c_str(), + false, true + ); + } // don't have any else, just ignore it + + if(bVideoWindow) + { + // possible todo: add some kind of if here to? can it fail? + CPluginManager::GetInstance().OpenDebug( + GetHandle(), + _LocalCoreStartupParameter.m_strVideoPlugin.c_str(), + true, true + ); + } // don't have any else, just ignore it +} + + +void CCodeWindow::CreateMenu(const SCoreStartupParameter& _LocalCoreStartupParameter) +{ + + // ======================================================================================= + // Windowses + // --------------- + wxMenuBar* pMenuBar = new wxMenuBar(wxMB_DOCKABLE); + + { + wxMenu* pCoreMenu = new wxMenu; + + wxMenuItem* interpreter = pCoreMenu->Append(IDM_INTERPRETER, _T("&Interpreter core"), wxEmptyString, wxITEM_CHECK); + interpreter->Check(!_LocalCoreStartupParameter.bUseJIT); + pCoreMenu->AppendSeparator(); + wxMenuItem* automaticstart = pCoreMenu->Append(IDM_AUTOMATICSTART, _T("&Automatic start"), wxEmptyString, wxITEM_CHECK); + automaticstart->Check(bAutomaticStart); + pCoreMenu->AppendSeparator(); + +#ifdef JIT_OFF_OPTIONS + jitunlimited = pCoreMenu->Append(IDM_JITUNLIMITED, _T("&Unlimited JIT Cache"), wxEmptyString, wxITEM_CHECK); + pCoreMenu->AppendSeparator(); + jitoff = pCoreMenu->Append(IDM_JITOFF, _T("&JIT off (JIT core)"), wxEmptyString, wxITEM_CHECK); + jitlsoff = pCoreMenu->Append(IDM_JITLSOFF, _T("&JIT LoadStore off"), wxEmptyString, wxITEM_CHECK); + jitlslbzxoff = pCoreMenu->Append(IDM_JITLSLBZXOFF, _T(" &JIT LoadStore lbzx off"), wxEmptyString, wxITEM_CHECK); + jitlslxzoff = pCoreMenu->Append(IDM_JITLSLXZOFF, _T(" &JIT LoadStore lXz off"), wxEmptyString, wxITEM_CHECK); + jitlslwzoff = pCoreMenu->Append(IDM_JITLSLWZOFF, _T(" &JIT LoadStore lwz off"), wxEmptyString, wxITEM_CHECK); + jitlspoff = pCoreMenu->Append(IDM_JITLSFOFF, _T("&JIT LoadStore Floating off"), wxEmptyString, wxITEM_CHECK); + jitlsfoff = pCoreMenu->Append(IDM_JITLSPOFF, _T("&JIT LoadStore Paired off"), wxEmptyString, wxITEM_CHECK); + jitfpoff = pCoreMenu->Append(IDM_JITFPOFF, _T("&JIT FloatingPoint off"), wxEmptyString, wxITEM_CHECK); + jitioff = pCoreMenu->Append(IDM_JITIOFF, _T("&JIT Integer off"), wxEmptyString, wxITEM_CHECK); + jitpoff = pCoreMenu->Append(IDM_JITPOFF, _T("&JIT Paired off"), wxEmptyString, wxITEM_CHECK); + jitsroff = pCoreMenu->Append(IDM_JITSROFF, _T("&JIT SystemRegisters off"), wxEmptyString, wxITEM_CHECK); +#endif + +// wxMenuItem* dualcore = pDebugMenu->Append(IDM_DUALCORE, _T("&DualCore"), wxEmptyString, wxITEM_CHECK); +// dualcore->Check(_LocalCoreStartupParameter.bUseDualCore); + + pMenuBar->Append(pCoreMenu, _T("&CPU Mode")); + + } + + { + wxMenu* pDebugDialogs = new wxMenu; + + if (IsLoggingActivated()) + { + wxMenuItem* pLogWindow = pDebugDialogs->Append(IDM_LOGWINDOW, _T("&LogManager"), wxEmptyString, wxITEM_CHECK); + pLogWindow->Check(bLogWindow); + } + + wxMenuItem* pRegister = pDebugDialogs->Append(IDM_REGISTERWINDOW, _T("&Registers"), wxEmptyString, wxITEM_CHECK); + pRegister->Check(bRegisterWindow); + + wxMenuItem* pBreakPoints = pDebugDialogs->Append(IDM_BREAKPOINTWINDOW, _T("&BreakPoints"), wxEmptyString, wxITEM_CHECK); + pBreakPoints->Check(bBreakpointWindow); + + wxMenuItem* pMemory = pDebugDialogs->Append(IDM_MEMORYWINDOW, _T("&Memory"), wxEmptyString, wxITEM_CHECK); + pMemory->Check(bMemoryWindow); + + wxMenuItem* pJit = pDebugDialogs->Append(IDM_JITWINDOW, _T("&Jit"), wxEmptyString, wxITEM_CHECK); + pJit->Check(bJitWindow); + + wxMenuItem* pSound = pDebugDialogs->Append(IDM_SOUNDWINDOW, _T("&Sound"), wxEmptyString, wxITEM_CHECK); + pSound->Check(bSoundWindow); + + wxMenuItem* pVideo = pDebugDialogs->Append(IDM_VIDEOWINDOW, _T("&Video"), wxEmptyString, wxITEM_CHECK); + pVideo->Check(bVideoWindow); + + pMenuBar->Append(pDebugDialogs, _T("&Views")); + } + // =============== + + + { + wxMenu *pSymbolsMenu = new wxMenu; + pSymbolsMenu->Append(IDM_CLEARSYMBOLS, _T("&Clear symbols")); + // pSymbolsMenu->Append(IDM_CLEANSYMBOLS, _T("&Clean symbols (zz)")); + pSymbolsMenu->Append(IDM_SCANFUNCTIONS, _T("&Generate symbol map")); + pSymbolsMenu->AppendSeparator(); + pSymbolsMenu->Append(IDM_LOADMAPFILE, _T("&Load symbol map")); + pSymbolsMenu->Append(IDM_SAVEMAPFILE, _T("&Save symbol map")); + pSymbolsMenu->AppendSeparator(); + pSymbolsMenu->Append(IDM_SAVEMAPFILEWITHCODES, _T("Save code")); + pSymbolsMenu->AppendSeparator(); + pSymbolsMenu->Append(IDM_CREATESIGNATUREFILE, _T("&Create signature file...")); + pSymbolsMenu->Append(IDM_USESIGNATUREFILE, _T("&Use signature file...")); + pSymbolsMenu->AppendSeparator(); + pSymbolsMenu->Append(IDM_PATCHHLEFUNCTIONS, _T("&Patch HLE functions")); + pMenuBar->Append(pSymbolsMenu, _T("&Symbols")); + } + + { + wxMenu *pJitMenu = new wxMenu; + pJitMenu->Append(IDM_CLEARCODECACHE, _T("&Clear code cache")); + pJitMenu->Append(IDM_LOGINSTRUCTIONS, _T("&Log JIT instruction coverage")); + pMenuBar->Append(pJitMenu, _T("&JIT")); + } + + { + wxMenu *pProfilerMenu = new wxMenu; + pProfilerMenu->Append(IDM_PROFILEBLOCKS, _T("&Profile blocks"), wxEmptyString, wxITEM_CHECK); + pProfilerMenu->AppendSeparator(); + pProfilerMenu->Append(IDM_WRITEPROFILE, _T("&Write to profile.txt, show")); + pMenuBar->Append(pProfilerMenu, _T("&Profiler")); + } + + + SetMenuBar(pMenuBar); +} + + +bool CCodeWindow::UseInterpreter() +{ + return GetMenuBar()->IsChecked(IDM_INTERPRETER); +} + +bool CCodeWindow::AutomaticStart() +{ + return GetMenuBar()->IsChecked(IDM_AUTOMATICSTART); +} + +bool CCodeWindow::UseDualCore() +{ + return GetMenuBar()->IsChecked(IDM_DUALCORE); +} + + +// ======================================================================================= +// CPU Mode +// -------------- +void CCodeWindow::OnInterpreter(wxCommandEvent& event) +{ + if (Core::GetState() != Core::CORE_RUN) { + PowerPC::SetMode(UseInterpreter() ? PowerPC::MODE_INTERPRETER : PowerPC::MODE_JIT); + } else { + event.Skip(); + wxMessageBox(_T("Please pause the emulator before changing mode.")); + } +} + + +void CCodeWindow::OnAutomaticStart(wxCommandEvent& event) +{ + bAutomaticStart = !bAutomaticStart; +} + + +void CCodeWindow::OnJITOff(wxCommandEvent& event) +{ + if (Core::GetState() == Core::CORE_UNINITIALIZED) + { + // we disallow changing the status here because it will be reset to the defult when BootCore() + // creates the SCoreStartupParameter as a game is loaded + GetMenuBar()->Check(event.GetId(),!event.IsChecked()); + wxMessageBox(_T("Please start a game before changing mode.")); + } else { + if (Core::GetState() != Core::CORE_RUN) + { + switch (event.GetId()) + { + case IDM_JITUNLIMITED: + Core::g_CoreStartupParameter.bJITUnlimitedCache = event.IsChecked(); + Jit64::ClearCache(); // allow InitCache() even after the game has started + Jit64::InitCache(); + GetMenuBar()->Enable(event.GetId(),!event.IsChecked()); + break; + case IDM_JITOFF: + Core::g_CoreStartupParameter.bJITOff = event.IsChecked(); break; + case IDM_JITLSOFF: + Core::g_CoreStartupParameter.bJITLoadStoreOff = event.IsChecked(); break; + case IDM_JITLSLXZOFF: + Core::g_CoreStartupParameter.bJITLoadStorelXzOff = event.IsChecked(); break; + case IDM_JITLSLWZOFF: + Core::g_CoreStartupParameter.bJITLoadStorelwzOff = event.IsChecked(); break; + case IDM_JITLSLBZXOFF: + Core::g_CoreStartupParameter.bJITLoadStorelbzxOff = event.IsChecked(); break; + case IDM_JITLSFOFF: + Core::g_CoreStartupParameter.bJITLoadStoreFloatingOff = event.IsChecked(); break; + case IDM_JITLSPOFF: + Core::g_CoreStartupParameter.bJITLoadStorePairedOff = event.IsChecked(); break; + case IDM_JITFPOFF: + Core::g_CoreStartupParameter.bJITFloatingPointOff = event.IsChecked(); break; + case IDM_JITIOFF: + Core::g_CoreStartupParameter.bJITIntegerOff = event.IsChecked(); break; + case IDM_JITPOFF: + Core::g_CoreStartupParameter.bJITPairedOff = event.IsChecked(); break; + case IDM_JITSROFF: + Core::g_CoreStartupParameter.bJITSystemRegistersOff = event.IsChecked(); break; + } + Jit64::ClearCache(); + } else { + //event.Skip(); // this doesn't work + GetMenuBar()->Check(event.GetId(),!event.IsChecked()); + wxMessageBox(_T("Please pause the emulator before changing mode.")); + } + } +} +// ============== + + +void CCodeWindow::OnJitMenu(wxCommandEvent& event) +{ + switch (event.GetId()) + { + case IDM_CLEARCODECACHE: + Jit64::ClearCache(); + break; + case IDM_LOGINSTRUCTIONS: + PPCTables::LogCompiledInstructions(); + break; + } +} + +void CCodeWindow::OnProfilerMenu(wxCommandEvent& event) +{ + if (Core::GetState() == Core::CORE_RUN) { + event.Skip(); + return; + } + switch (event.GetId()) + { + case IDM_PROFILEBLOCKS: + Jit64::ClearCache(); + Profiler::g_ProfileBlocks = GetMenuBar()->IsChecked(IDM_PROFILEBLOCKS); + break; + case IDM_WRITEPROFILE: + Profiler::WriteProfileResults("profiler.txt"); + File::Launch("profiler.txt"); + break; + } +} + +void CCodeWindow::OnSymbolsMenu(wxCommandEvent& event) +{ + if (Core::GetState() == Core::CORE_UNINITIALIZED) + { + // TODO: disable menu items instead :P + return; + } + std::string mapfile = CBoot::GenerateMapFilename(); + switch (event.GetId()) + { + case IDM_CLEARSYMBOLS: + g_symbolDB.Clear(); + Host_NotifyMapLoaded(); + break; + case IDM_CLEANSYMBOLS: + g_symbolDB.Clear("zz"); + Host_NotifyMapLoaded(); + break; + case IDM_SCANFUNCTIONS: + { + PPCAnalyst::FindFunctions(0x80000000, 0x80400000, &g_symbolDB); + SignatureDB db; + if (db.Load(TOTALDB_FILE)) + db.Apply(&g_symbolDB); + + // HLE::PatchFunctions(); + NotifyMapLoaded(); + break; + } + case IDM_LOADMAPFILE: + if (!File::Exists(mapfile.c_str())) + { + g_symbolDB.Clear(); + PPCAnalyst::FindFunctions(0x80000000, 0x80400000, &g_symbolDB); + SignatureDB db; + if (db.Load(TOTALDB_FILE)) + db.Apply(&g_symbolDB); + } else { + g_symbolDB.LoadMap(mapfile.c_str()); + } + NotifyMapLoaded(); + break; + case IDM_SAVEMAPFILE: + g_symbolDB.SaveMap(mapfile.c_str()); + break; + case IDM_SAVEMAPFILEWITHCODES: + g_symbolDB.SaveMap(mapfile.c_str(), true); + break; + case IDM_CREATESIGNATUREFILE: + { + wxTextEntryDialog input_prefix(this, wxString::FromAscii("Only export symbols with prefix:"), wxGetTextFromUserPromptStr, _T(".")); + if (input_prefix.ShowModal() == wxID_OK) { + std::string prefix(input_prefix.GetValue().mb_str()); + + wxString path = wxFileSelector( + _T("Save signature as"), wxEmptyString, wxEmptyString, wxEmptyString, + _T("Dolphin Signature File (*.dsy)|*.dsy;"), wxFD_SAVE, + this); + if (path) { + SignatureDB db; + db.Initialize(&g_symbolDB, prefix.c_str()); + std::string filename(path.ToAscii()); // PPCAnalyst::SaveSignatureDB( + db.Save(path.ToAscii()); + } + } + } + break; + case IDM_USESIGNATUREFILE: + { + wxString path = wxFileSelector( + _T("Apply signature file"), wxEmptyString, wxEmptyString, wxEmptyString, + _T("Dolphin Signature File (*.dsy)|*.dsy;"), wxFD_OPEN | wxFD_FILE_MUST_EXIST, + this); + if (path) { + SignatureDB db; + db.Load(path.ToAscii()); + db.Apply(&g_symbolDB); + } + } + NotifyMapLoaded(); + break; + case IDM_PATCHHLEFUNCTIONS: + HLE::PatchFunctions(); + Update(); + break; + } +} + +// ======================================================================================= +// The Play, Stop, Step, Skip, Go to PC and Show PC buttons all go here +// -------------- +void CCodeWindow::OnCodeStep(wxCommandEvent& event) +{ + switch (event.GetId()) + { + case IDM_DEBUG_GO: + { + // [F|RES] prolly we should disable the other buttons in go mode too ... + JumpToAddress(PC); + + if (CCPU::IsStepping()) + { + CCPU::EnableStepping(false); + } + else + { + CCPU::EnableStepping(true); + Host_UpdateLogDisplay(); + } + + Update(); + } + break; + + case IDM_STEP: + SingleCPUStep(); + + break; + + case IDM_STEPOVER: + CCPU::EnableStepping(true); + break; + + case IDM_SKIP: + PC += 4; + Update(); + break; + + case IDM_SETPC: + PC = codeview->GetSelection(); + Update(); + break; + + case IDM_GOTOPC: + JumpToAddress(PC); + break; + } + + UpdateButtonStates(); +} + + +void CCodeWindow::JumpToAddress(u32 _Address) +{ + codeview->Center(_Address); + UpdateLists(); +} + + +void CCodeWindow::UpdateLists() +{ + callers->Clear(); + u32 addr = codeview->GetSelection(); + Symbol *symbol = g_symbolDB.GetSymbolFromAddr(addr); + if (!symbol) + return; + for (int i = 0; i < (int)symbol->callers.size(); i++) + { + u32 caller_addr = symbol->callers[i].callAddress; + Symbol *caller_symbol = g_symbolDB.GetSymbolFromAddr(caller_addr); + if (caller_symbol) { + int idx = callers->Append(wxString::Format( wxT("< %s (%08x)"), caller_symbol->name.c_str(), caller_addr)); + callers->SetClientData(idx, (void*)caller_addr); + } + } + + calls->Clear(); + for (int i = 0; i < (int)symbol->calls.size(); i++) + { + u32 call_addr = symbol->calls[i].function; + Symbol *call_symbol = g_symbolDB.GetSymbolFromAddr(call_addr); + if (call_symbol) { + int idx = calls->Append(wxString::Format(_T("> %s (%08x)"), call_symbol->name.c_str(), call_addr)); + calls->SetClientData(idx, (void*)call_addr); + } + } +} + + +void CCodeWindow::OnCodeViewChange(wxCommandEvent &event) +{ + //PanicAlert("boo"); + UpdateLists(); +} + +void CCodeWindow::OnAddrBoxChange(wxCommandEvent& event) +{ + wxTextCtrl* pAddrCtrl = (wxTextCtrl*)GetToolBar()->FindControl(IDM_ADDRBOX); + wxString txt = pAddrCtrl->GetValue(); + + std::string text(txt.mb_str()); + text = StripSpaces(text); + if (text.size() == 8) + { + u32 addr; + sscanf(text.c_str(), "%08x", &addr); + JumpToAddress(addr); + } + + event.Skip(1); +} + +void CCodeWindow::OnCallstackListChange(wxCommandEvent& event) +{ + int index = callstack->GetSelection(); + if (index >= 0) { + u32 address = (u32)(u64)(callstack->GetClientData(index)); + if (address) + JumpToAddress(address); + } +} + +void CCodeWindow::OnCallersListChange(wxCommandEvent& event) +{ + int index = callers->GetSelection(); + if (index >= 0) { + u32 address = (u32)(u64)(callers->GetClientData(index)); + if (address) + JumpToAddress(address); + } +} + +void CCodeWindow::OnCallsListChange(wxCommandEvent& event) +{ + int index = calls->GetSelection(); + if (index >= 0) { + u32 address = (u32)(u64)(calls->GetClientData(index)); + if (address) + JumpToAddress(address); + } +} + +void CCodeWindow::Update() +{ + codeview->Refresh(); + callstack->Clear(); + + std::vector stack; + + if (Debugger::GetCallstack(stack)) + { + for (size_t i = 0; i < stack.size(); i++) + { + int idx = callstack->Append(wxString::FromAscii(stack[i].Name.c_str())); + callstack->SetClientData(idx, (void*)(u64)stack[i].vAddress); + } + } + else + { + callstack->Append(wxString::FromAscii("invalid callstack")); + } + + UpdateButtonStates(); + + /* Automatically show the current PC position when a breakpoint is hit or + when we pause */ + codeview->Center(PC); +} + + +void CCodeWindow::NotifyMapLoaded() +{ + g_symbolDB.FillInCallers(); + symbols->Show(false); // hide it for faster filling + symbols->Clear(); + for (SymbolDB::XFuncMap::iterator iter = g_symbolDB.GetIterator(); iter != g_symbolDB.End(); iter++) + { + int idx = symbols->Append(wxString::FromAscii(iter->second.name.c_str())); + symbols->SetClientData(idx, (void*)&iter->second); + } + symbols->Show(true); + Update(); +} + + +void CCodeWindow::UpdateButtonStates() +{ + wxToolBar* toolBar = GetToolBar(); + if (Core::GetState() == Core::CORE_UNINITIALIZED) + { + toolBar->EnableTool(IDM_DEBUG_GO, false); + toolBar->EnableTool(IDM_STEP, false); + toolBar->EnableTool(IDM_STEPOVER, false); + toolBar->EnableTool(IDM_SKIP, false); + } + else + { + if (!CCPU::IsStepping()) + { + toolBar->SetToolShortHelp(IDM_DEBUG_GO, _T("&Pause")); + toolBar->SetToolNormalBitmap(IDM_DEBUG_GO, m_Bitmaps[Toolbar_Pause]); + toolBar->EnableTool(IDM_DEBUG_GO, true); + toolBar->EnableTool(IDM_STEP, false); + toolBar->EnableTool(IDM_STEPOVER, false); + toolBar->EnableTool(IDM_SKIP, false); + } + else + { + toolBar->SetToolShortHelp(IDM_DEBUG_GO, _T("&Play")); + toolBar->SetToolNormalBitmap(IDM_DEBUG_GO, m_Bitmaps[Toolbar_DebugGo]); + toolBar->EnableTool(IDM_DEBUG_GO, true); + toolBar->EnableTool(IDM_STEP, true); + toolBar->EnableTool(IDM_STEPOVER, true); + toolBar->EnableTool(IDM_SKIP, true); + } + } +} + + +void CCodeWindow::OnSymbolListChange(wxCommandEvent& event) +{ + int index = symbols->GetSelection(); + if (index >= 0) { + Symbol* pSymbol = static_cast(symbols->GetClientData(index)); + if (pSymbol != NULL) + { + if(pSymbol->type == Symbol::SYMBOL_DATA) + { + if(m_MemoryWindow && m_MemoryWindow->IsVisible()) + m_MemoryWindow->JumpToAddress(pSymbol->address); + } + else + { + JumpToAddress(pSymbol->address); + } + } + } +} + +void CCodeWindow::OnSymbolListContextMenu(wxContextMenuEvent& event) +{ +} + + +void CCodeWindow::OnToggleLogWindow(wxCommandEvent& event) +{ + if (IsLoggingActivated()) + { + bool show = GetMenuBar()->IsChecked(event.GetId()); + + if (show) + { + if (!m_LogWindow) + { + m_LogWindow = new CLogWindow(this); + } + + m_LogWindow->Show(true); + } + else // hide + { + // If m_dialog is NULL, then possibly the system + // didn't report the checked menu item status correctly. + // It should be true just after the menu item was selected, + // if there was no modeless dialog yet. + wxASSERT(m_LogWindow != NULL); + + if (m_LogWindow) + { + m_LogWindow->Hide(); + } + } + } +} + + +void CCodeWindow::OnToggleRegisterWindow(wxCommandEvent& event) +{ + bool show = GetMenuBar()->IsChecked(event.GetId()); + + if (show) + { + if (!m_RegisterWindow) + { + m_RegisterWindow = new CRegisterWindow(this); + } + + m_RegisterWindow->Show(true); + } + else // hide + { + // If m_dialog is NULL, then possibly the system + // didn't report the checked menu item status correctly. + // It should be true just after the menu item was selected, + // if there was no modeless dialog yet. + wxASSERT(m_RegisterWindow != NULL); + + if (m_RegisterWindow) + { + m_RegisterWindow->Hide(); + } + } +} + + +// ======================================================================================= +// Toggle Sound Debugging Window +// ------------ +void CCodeWindow::OnToggleSoundWindow(wxCommandEvent& event) +{ + bool show = GetMenuBar()->IsChecked(event.GetId()); + + if (show) + { + // TODO: add some kind of if() check here to? + CPluginManager::GetInstance().OpenDebug( + GetHandle(), + SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDSPPlugin.c_str(), + false, true // DSP, show + ); + } + else // hide + { + // Close the sound dll that has an open debugger + CPluginManager::GetInstance().OpenDebug( + GetHandle(), + SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDSPPlugin.c_str(), + false, false // DSP, hide + ); + } +} +// =========== + + +// ======================================================================================= +// Toggle Video Debugging Window +// ------------ +void CCodeWindow::OnToggleVideoWindow(wxCommandEvent& event) +{ + bool show = GetMenuBar()->IsChecked(event.GetId()); + //GetMenuBar()->Check(event.GetId(), false); // Turn off + + if (show) + { + // It works now, but I'll keep this message in case the problem reappears + /*if(Core::GetState() == Core::CORE_UNINITIALIZED) + { + wxMessageBox(_T("Warning, opening this window before a game is started \n\ +may cause a crash when a game is later started. Todo: figure out why and fix it."), wxT("OpenGL Debugging Window")); + }*/ + + // TODO: add some kind of if() check here to? + CPluginManager::GetInstance().OpenDebug( + GetHandle(), + SConfig::GetInstance().m_LocalCoreStartupParameter.m_strVideoPlugin.c_str(), + true, true // Video, show + ); + } + else // hide + { + // Close the video dll that has an open debugger + CPluginManager::GetInstance().OpenDebug( + GetHandle(), + SConfig::GetInstance().m_LocalCoreStartupParameter.m_strVideoPlugin.c_str(), + true, false // Video, hide + ); + } +} +// =========== + + +void CCodeWindow::OnToggleJitWindow(wxCommandEvent& event) +{ + bool show = GetMenuBar()->IsChecked(event.GetId()); + + if (show) + { + if (!m_JitWindow) + { + m_JitWindow = new CJitWindow(this); + } + + m_JitWindow->Show(true); + } + else // hide + { + // If m_dialog is NULL, then possibly the system + // didn't report the checked menu item status correctly. + // It should be true just after the menu item was selected, + // if there was no modeless dialog yet. + wxASSERT(m_JitWindow != NULL); + + if (m_JitWindow) + { + m_JitWindow->Hide(); + } + } +} + + +void CCodeWindow::OnToggleBreakPointWindow(wxCommandEvent& event) +{ + bool show = GetMenuBar()->IsChecked(event.GetId()); + + if (show) + { + if (!m_BreakpointWindow) + { + m_BreakpointWindow = new CBreakPointWindow(this, this); + } + + m_BreakpointWindow->Show(true); + } + else // hide + { + // If m_dialog is NULL, then possibly the system + // didn't report the checked menu item status correctly. + // It should be true just after the menu item was selected, + // if there was no modeless dialog yet. + wxASSERT(m_BreakpointWindow != NULL); + + if (m_BreakpointWindow) + { + m_BreakpointWindow->Hide(); + } + } +} + +void CCodeWindow::OnToggleMemoryWindow(wxCommandEvent& event) +{ + bool show = GetMenuBar()->IsChecked(event.GetId()); + + if (show) + { + if (!m_MemoryWindow) + { + m_MemoryWindow = new CMemoryWindow(this); + } + + m_MemoryWindow->Show(true); + } + else // hide + { + // If m_dialog is NULL, then possibly the system + // didn't report the checked menu item status correctly. + // It should be true just after the menu item was selected, + // if there was no modeless dialog yet. + wxASSERT(m_MemoryWindow != NULL); + + if (m_MemoryWindow) + { + m_MemoryWindow->Hide(); + } + } +} + +void CCodeWindow::OnHostMessage(wxCommandEvent& event) +{ + switch (event.GetId()) + { + case IDM_NOTIFYMAPLOADED: + NotifyMapLoaded(); + break; + + case IDM_UPDATELOGDISPLAY: + + if (m_LogWindow) + { + m_LogWindow->NotifyUpdate(); + } + + break; + + case IDM_UPDATEDISASMDIALOG: + Update(); + + if (m_RegisterWindow) + { + m_RegisterWindow->NotifyUpdate(); + } + break; + + case IDM_UPDATEBREAKPOINTS: + Update(); + + if (m_BreakpointWindow) + { + m_BreakpointWindow->NotifyUpdate(); + } + break; + + } +} + +void CCodeWindow::PopulateToolbar(wxToolBar* toolBar) +{ + int w = m_Bitmaps[Toolbar_DebugGo].GetWidth(), + h = m_Bitmaps[Toolbar_DebugGo].GetHeight(); + + toolBar->SetToolBitmapSize(wxSize(w, h)); + toolBar->AddTool(IDM_DEBUG_GO, _T("Play"), m_Bitmaps[Toolbar_DebugGo]); + toolBar->AddTool(IDM_STEP, _T("Step"), m_Bitmaps[Toolbar_Step]); + toolBar->AddTool(IDM_STEPOVER, _T("Step Over"), m_Bitmaps[Toolbar_StepOver]); + toolBar->AddTool(IDM_SKIP, _T("Skip"), m_Bitmaps[Toolbar_Skip]); + toolBar->AddSeparator(); + toolBar->AddTool(IDM_GOTOPC, _T("Show PC"), m_Bitmaps[Toolbar_GotoPC]); + toolBar->AddTool(IDM_SETPC, _T("Set PC"), m_Bitmaps[Toolbar_SetPC]); + toolBar->AddSeparator(); + toolBar->AddControl(new wxTextCtrl(toolBar, IDM_ADDRBOX, _T(""))); + + // after adding the buttons to the toolbar, must call Realize() to reflect + // the changes + toolBar->Realize(); +} + + +void CCodeWindow::RecreateToolbar() +{ + // delete and recreate the toolbar + wxToolBarBase* toolBar = GetToolBar(); + delete toolBar; + SetToolBar(NULL); + + long style = TOOLBAR_STYLE; + style &= ~(wxTB_HORIZONTAL | wxTB_VERTICAL | wxTB_BOTTOM | wxTB_RIGHT | wxTB_HORZ_LAYOUT | wxTB_TOP); + wxToolBar* theToolBar = CreateToolBar(style, ID_TOOLBAR); + + PopulateToolbar(theToolBar); + SetToolBar(theToolBar); +} + + +void CCodeWindow::InitBitmaps() +{ + // load original size 48x48 + m_Bitmaps[Toolbar_DebugGo] = wxGetBitmapFromMemory(toolbar_play_png); + m_Bitmaps[Toolbar_Step] = wxGetBitmapFromMemory(toolbar_add_breakpoint_png); + m_Bitmaps[Toolbar_StepOver] = wxGetBitmapFromMemory(toolbar_add_memcheck_png); + m_Bitmaps[Toolbar_Skip] = wxGetBitmapFromMemory(toolbar_add_memcheck_png); + m_Bitmaps[Toolbar_GotoPC] = wxGetBitmapFromMemory(toolbar_add_memcheck_png); + m_Bitmaps[Toolbar_SetPC] = wxGetBitmapFromMemory(toolbar_add_memcheck_png); + m_Bitmaps[Toolbar_Pause] = wxGetBitmapFromMemory(toolbar_pause_png); + + + // scale to 16x16 for toolbar + for (size_t n = Toolbar_DebugGo; n < Bitmaps_max; n++) + { + m_Bitmaps[n] = wxBitmap(m_Bitmaps[n].ConvertToImage().Scale(16, 16)); + } +} + + +void CCodeWindow::OnKeyDown(wxKeyEvent& event) +{ + if ((event.GetKeyCode() == WXK_SPACE) && IsActive()) + { + SingleCPUStep(); + } + else + { + event.Skip(); + } +} + + +void CCodeWindow::SingleCPUStep() +{ + CCPU::StepOpcode(&sync_event); + // if (CCPU::IsStepping()) + // sync_event.Wait(); + wxThread::Sleep(20); + // need a short wait here + JumpToAddress(PC); + Update(); + Host_UpdateLogDisplay(); +} diff --git a/Source/Core/DebuggerWX/Src/Debugger.cpp b/Source/Core/DebuggerWX/Src/Debugger.cpp index 8f263d774d..2bf8df55ff 100644 --- a/Source/Core/DebuggerWX/Src/Debugger.cpp +++ b/Source/Core/DebuggerWX/Src/Debugger.cpp @@ -1,19 +1,19 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Debugger.h" - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Debugger.h" + diff --git a/Source/Core/DebuggerWX/Src/JitWindow.cpp b/Source/Core/DebuggerWX/Src/JitWindow.cpp index aa4d4b5300..7914a3b68a 100644 --- a/Source/Core/DebuggerWX/Src/JitWindow.cpp +++ b/Source/Core/DebuggerWX/Src/JitWindow.cpp @@ -1,211 +1,211 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Debugger.h" - -#include "IniFile.h" - -#include -#include -#include -#include -#include -#include "JitWindow.h" -#include "HW/CPU.h" -#include "PowerPC/PowerPC.h" -#include "PowerPC/Jit64/Jit.h" -#include "PowerPC/Jit64/JitCache.h" -#include "Host.h" -#include "disasm.h" - -#include "Debugger/PPCDebugInterface.h" -#include "Debugger/Debugger_SymbolMap.h" - -#include "Core.h" -#include "StringUtil.h" -#include "LogManager.h" - -// ugly that this lib included code from the main -#include "../../DolphinWX/Src/Globals.h" - -// UGLY -namespace { -CJitWindow *the_jit_window; -} - -enum -{ - IDM_REFRESH_LIST = 23350, - IDM_PPC_BOX, - IDM_X86_BOX, - IDM_NEXT, - IDM_PREV, - IDM_BLOCKLIST, -}; - -BEGIN_EVENT_TABLE(CJitWindow, wxFrame) -// EVT_TEXT(IDM_ADDRBOX, CJitWindow::OnAddrBoxChange) - // EVT_LISTBOX(IDM_SYMBOLLIST, CJitWindow::OnSymbolListChange) - //EVT_HOST_COMMAND(wxID_ANY, CJitWindow::OnHostMessage) - EVT_BUTTON(IDM_REFRESH_LIST, CJitWindow::OnRefresh) -END_EVENT_TABLE() - - -CJitWindow::CJitWindow(wxWindow* parent, wxWindowID id, - const wxString& title, const wxPoint& pos, const wxSize& size, long style) - : wxFrame(parent, id, title, pos, size, style) -{ - the_jit_window = this; - wxBoxSizer* sizerBig = new wxBoxSizer(wxVERTICAL); - wxBoxSizer* sizerSplit = new wxBoxSizer(wxHORIZONTAL); - sizerSplit->Add(ppc_box = new wxTextCtrl(this, IDM_PPC_BOX, _T("(ppc)"), wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE), 1, wxEXPAND); - sizerSplit->Add(x86_box = new wxTextCtrl(this, IDM_X86_BOX, _T("(x86)"), wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE), 1, wxEXPAND); - sizerBig->Add(block_list = new JitBlockList(this, IDM_BLOCKLIST, - wxDefaultPosition, wxSize(100, 140), - wxLC_REPORT | wxSUNKEN_BORDER | wxLC_ALIGN_LEFT | wxLC_SINGLE_SEL | wxLC_SORT_ASCENDING), 0, wxEXPAND); - sizerBig->Add(sizerSplit, 2, wxEXPAND); -// sizerBig->Add(memview, 5, wxEXPAND); -// sizerBig->Add(sizerRight, 0, wxEXPAND | wxALL, 3); - sizerBig->Add(button_refresh = new wxButton(this, IDM_REFRESH_LIST, _T("&Refresh"))); -// sizerRight->Add(addrbox = new wxTextCtrl(this, IDM_ADDRBOX, _T(""))); -// sizerRight->Add(new wxButton(this, IDM_SETPC, _T("S&et PC"))); - - SetSizer(sizerBig); - - sizerSplit->SetSizeHints(this); - sizerSplit->Fit(this); - sizerBig->SetSizeHints(this); - sizerBig->Fit(this); -} - - -CJitWindow::~CJitWindow() -{ -} - - -void CJitWindow::Save(IniFile& _IniFile) const -{ - _IniFile.Set("JitWindow", "x", GetPosition().x); - _IniFile.Set("JitWindow", "y", GetPosition().y); - _IniFile.Set("JitWindow", "w", GetSize().GetWidth()); - _IniFile.Set("JitWindow", "h", GetSize().GetHeight()); -} - - -void CJitWindow::Load(IniFile& _IniFile) -{ - int x,y,w,h; - _IniFile.Get("JitWindow", "x", &x, GetPosition().x); - _IniFile.Get("JitWindow", "y", &y, GetPosition().y); - _IniFile.Get("JitWindow", "w", &w, GetSize().GetWidth()); - _IniFile.Get("JitWindow", "h", &h, GetSize().GetHeight()); - SetSize(x, y, w, h); -} - -void CJitWindow::OnRefresh(wxCommandEvent& /*event*/) { - block_list->Update(); -} - -void CJitWindow::ViewAddr(u32 em_address) -{ - the_jit_window->Compare(em_address); -} - -void CJitWindow::Compare(u32 em_address) -{ - u8 *xDis = new u8[65536]; - memset(xDis, 0, 65536); - - disassembler x64disasm; - x64disasm.set_syntax_intel(); - - int block_num = Jit64::GetBlockNumberFromAddress(em_address); - if (block_num < 0) - { - ppc_box->SetValue(wxString::FromAscii(StringFromFormat("(non-code address: %08x)", em_address).c_str())); - x86_box->SetValue(wxString::FromAscii(StringFromFormat("(no translation)").c_str())); - return; - } - Jit64::JitBlock *block = Jit64::GetBlock(block_num); - const u8 *code = (const u8 *)Jit64::GetCompiledCodeFromBlock(block_num); - u64 disasmPtr = (u64)code; - int size = block->codeSize; - const u8 *end = code + size; - char *sptr = (char*)xDis; - - while ((u8*)disasmPtr < end) - { - disasmPtr += x64disasm.disasm64(disasmPtr, disasmPtr, (u8*)disasmPtr, sptr); - sptr += strlen(sptr); - *sptr++ = 13; - *sptr++ = 10; - } - x86_box->SetValue(wxString::FromAscii((char*)xDis)); - delete [] xDis; -} - -void CJitWindow::Update() -{ - -} - -void CJitWindow::OnHostMessage(wxCommandEvent& event) -{ - switch (event.GetId()) - { - case IDM_NOTIFYMAPLOADED: - //NotifyMapLoaded(); - break; - } -} - - - -// JitBlockList -//================ - -enum { - COLUMN_ADDRESS, - COLUMN_PPCSIZE, - COLUMN_X86SIZE, - COLUMN_NAME, - COLUMN_FLAGS, - COLUMN_NUMEXEC, - COLUMN_COST, // (estimated as x86size * numexec) -}; - -JitBlockList::JitBlockList(wxWindow* parent, const wxWindowID id, const wxPoint& pos, const wxSize& size, long style) - : wxListCtrl(parent, id, pos, size, style) // | wxLC_VIRTUAL) -{ - Init(); -} - -void JitBlockList::Init() -{ - InsertColumn(COLUMN_ADDRESS, _T("Address")); - InsertColumn(COLUMN_PPCSIZE, _T("PPC Size")); - InsertColumn(COLUMN_X86SIZE, _T("x86 Size")); - InsertColumn(COLUMN_NAME, _T("Symbol")); - InsertColumn(COLUMN_FLAGS, _T("Flags")); - InsertColumn(COLUMN_NUMEXEC, _T("NumExec")); - InsertColumn(COLUMN_COST, _T("Cost")); -} - -void JitBlockList::Update() -{ -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Debugger.h" + +#include "IniFile.h" + +#include +#include +#include +#include +#include +#include "JitWindow.h" +#include "HW/CPU.h" +#include "PowerPC/PowerPC.h" +#include "PowerPC/Jit64/Jit.h" +#include "PowerPC/Jit64/JitCache.h" +#include "Host.h" +#include "disasm.h" + +#include "Debugger/PPCDebugInterface.h" +#include "Debugger/Debugger_SymbolMap.h" + +#include "Core.h" +#include "StringUtil.h" +#include "LogManager.h" + +// ugly that this lib included code from the main +#include "../../DolphinWX/Src/Globals.h" + +// UGLY +namespace { +CJitWindow *the_jit_window; +} + +enum +{ + IDM_REFRESH_LIST = 23350, + IDM_PPC_BOX, + IDM_X86_BOX, + IDM_NEXT, + IDM_PREV, + IDM_BLOCKLIST, +}; + +BEGIN_EVENT_TABLE(CJitWindow, wxFrame) +// EVT_TEXT(IDM_ADDRBOX, CJitWindow::OnAddrBoxChange) + // EVT_LISTBOX(IDM_SYMBOLLIST, CJitWindow::OnSymbolListChange) + //EVT_HOST_COMMAND(wxID_ANY, CJitWindow::OnHostMessage) + EVT_BUTTON(IDM_REFRESH_LIST, CJitWindow::OnRefresh) +END_EVENT_TABLE() + + +CJitWindow::CJitWindow(wxWindow* parent, wxWindowID id, + const wxString& title, const wxPoint& pos, const wxSize& size, long style) + : wxFrame(parent, id, title, pos, size, style) +{ + the_jit_window = this; + wxBoxSizer* sizerBig = new wxBoxSizer(wxVERTICAL); + wxBoxSizer* sizerSplit = new wxBoxSizer(wxHORIZONTAL); + sizerSplit->Add(ppc_box = new wxTextCtrl(this, IDM_PPC_BOX, _T("(ppc)"), wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE), 1, wxEXPAND); + sizerSplit->Add(x86_box = new wxTextCtrl(this, IDM_X86_BOX, _T("(x86)"), wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE), 1, wxEXPAND); + sizerBig->Add(block_list = new JitBlockList(this, IDM_BLOCKLIST, + wxDefaultPosition, wxSize(100, 140), + wxLC_REPORT | wxSUNKEN_BORDER | wxLC_ALIGN_LEFT | wxLC_SINGLE_SEL | wxLC_SORT_ASCENDING), 0, wxEXPAND); + sizerBig->Add(sizerSplit, 2, wxEXPAND); +// sizerBig->Add(memview, 5, wxEXPAND); +// sizerBig->Add(sizerRight, 0, wxEXPAND | wxALL, 3); + sizerBig->Add(button_refresh = new wxButton(this, IDM_REFRESH_LIST, _T("&Refresh"))); +// sizerRight->Add(addrbox = new wxTextCtrl(this, IDM_ADDRBOX, _T(""))); +// sizerRight->Add(new wxButton(this, IDM_SETPC, _T("S&et PC"))); + + SetSizer(sizerBig); + + sizerSplit->SetSizeHints(this); + sizerSplit->Fit(this); + sizerBig->SetSizeHints(this); + sizerBig->Fit(this); +} + + +CJitWindow::~CJitWindow() +{ +} + + +void CJitWindow::Save(IniFile& _IniFile) const +{ + _IniFile.Set("JitWindow", "x", GetPosition().x); + _IniFile.Set("JitWindow", "y", GetPosition().y); + _IniFile.Set("JitWindow", "w", GetSize().GetWidth()); + _IniFile.Set("JitWindow", "h", GetSize().GetHeight()); +} + + +void CJitWindow::Load(IniFile& _IniFile) +{ + int x,y,w,h; + _IniFile.Get("JitWindow", "x", &x, GetPosition().x); + _IniFile.Get("JitWindow", "y", &y, GetPosition().y); + _IniFile.Get("JitWindow", "w", &w, GetSize().GetWidth()); + _IniFile.Get("JitWindow", "h", &h, GetSize().GetHeight()); + SetSize(x, y, w, h); +} + +void CJitWindow::OnRefresh(wxCommandEvent& /*event*/) { + block_list->Update(); +} + +void CJitWindow::ViewAddr(u32 em_address) +{ + the_jit_window->Compare(em_address); +} + +void CJitWindow::Compare(u32 em_address) +{ + u8 *xDis = new u8[65536]; + memset(xDis, 0, 65536); + + disassembler x64disasm; + x64disasm.set_syntax_intel(); + + int block_num = Jit64::GetBlockNumberFromAddress(em_address); + if (block_num < 0) + { + ppc_box->SetValue(wxString::FromAscii(StringFromFormat("(non-code address: %08x)", em_address).c_str())); + x86_box->SetValue(wxString::FromAscii(StringFromFormat("(no translation)").c_str())); + return; + } + Jit64::JitBlock *block = Jit64::GetBlock(block_num); + const u8 *code = (const u8 *)Jit64::GetCompiledCodeFromBlock(block_num); + u64 disasmPtr = (u64)code; + int size = block->codeSize; + const u8 *end = code + size; + char *sptr = (char*)xDis; + + while ((u8*)disasmPtr < end) + { + disasmPtr += x64disasm.disasm64(disasmPtr, disasmPtr, (u8*)disasmPtr, sptr); + sptr += strlen(sptr); + *sptr++ = 13; + *sptr++ = 10; + } + x86_box->SetValue(wxString::FromAscii((char*)xDis)); + delete [] xDis; +} + +void CJitWindow::Update() +{ + +} + +void CJitWindow::OnHostMessage(wxCommandEvent& event) +{ + switch (event.GetId()) + { + case IDM_NOTIFYMAPLOADED: + //NotifyMapLoaded(); + break; + } +} + + + +// JitBlockList +//================ + +enum { + COLUMN_ADDRESS, + COLUMN_PPCSIZE, + COLUMN_X86SIZE, + COLUMN_NAME, + COLUMN_FLAGS, + COLUMN_NUMEXEC, + COLUMN_COST, // (estimated as x86size * numexec) +}; + +JitBlockList::JitBlockList(wxWindow* parent, const wxWindowID id, const wxPoint& pos, const wxSize& size, long style) + : wxListCtrl(parent, id, pos, size, style) // | wxLC_VIRTUAL) +{ + Init(); +} + +void JitBlockList::Init() +{ + InsertColumn(COLUMN_ADDRESS, _T("Address")); + InsertColumn(COLUMN_PPCSIZE, _T("PPC Size")); + InsertColumn(COLUMN_X86SIZE, _T("x86 Size")); + InsertColumn(COLUMN_NAME, _T("Symbol")); + InsertColumn(COLUMN_FLAGS, _T("Flags")); + InsertColumn(COLUMN_NUMEXEC, _T("NumExec")); + InsertColumn(COLUMN_COST, _T("Cost")); +} + +void JitBlockList::Update() +{ +} diff --git a/Source/Core/DebuggerWX/Src/LogWindow.cpp b/Source/Core/DebuggerWX/Src/LogWindow.cpp index c6f57cf0e2..7c14c96368 100644 --- a/Source/Core/DebuggerWX/Src/LogWindow.cpp +++ b/Source/Core/DebuggerWX/Src/LogWindow.cpp @@ -1,519 +1,519 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Debugger.h" -#include "LogManager.h" - -#include -#include -#include -#include - -#include "Core.h" // for Core::GetState() -#include "LogWindow.h" -#include "Console.h" -#include "IniFile.h" - -// declare this now to be able to use it in Load() -CDebugger_LogSettings* LogManager::m_LogSettings; - -BEGIN_EVENT_TABLE(CLogWindow, wxDialog) - EVT_BUTTON(IDM_SUBMITCMD, CLogWindow::OnSubmit) - EVT_BUTTON(IDM_UPDATELOG, CLogWindow::OnUpdateLog) - EVT_BUTTON(IDM_CLEARLOG, CLogWindow::OnClear) - EVT_BUTTON(IDM_ENABLEALL, CLogWindow::OnEnableAll) - EVT_CHECKLISTBOX(IDM_OPTIONS, CLogWindow::OnOptionsCheck) - EVT_CHECKLISTBOX(IDM_LOGCHECKS, CLogWindow::OnLogCheck) - EVT_RADIOBOX(IDM_RADIO0, CLogWindow::OnRadioChange) -END_EVENT_TABLE() - - -CLogWindow::CLogWindow(wxWindow* parent) - : wxDialog(parent, wxID_ANY, _T("Log/Console"), wxPoint(100, 700), wxSize(800, 270), - wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) -{ - wxBoxSizer* sizerTop = new wxBoxSizer(wxHORIZONTAL), // buttons - * sizerUber = new wxBoxSizer(wxHORIZONTAL), // whole plane - * sizerBig = new wxBoxSizer(wxVERTICAL), // RIGHT sizer - * sizerBottom = new wxBoxSizer(wxHORIZONTAL), // submit row - * sizerLeft = new wxBoxSizer(wxVERTICAL); // LEFT sizer - - // left checkboxes and radio boxes ----------------------------------- - int m_radioBoxNChoices[1]; - wxString m_radioBoxChoices0[] = { wxT("0"), wxT("1"), wxT("2"), wxT("3") }; - m_radioBoxNChoices[0] = sizeof( m_radioBoxChoices0 ) / sizeof( wxString ); - m_RadioBox[0] = new wxRadioBox( this, IDM_RADIO0, wxT("Verbosity"), - wxDefaultPosition, wxDefaultSize, m_radioBoxNChoices[0], m_radioBoxChoices0, 1, wxRA_SPECIFY_ROWS); - - wxStaticBoxSizer * m_optionsSizer = new wxStaticBoxSizer(wxVERTICAL, this, wxT("Settings")); - m_options = new wxCheckListBox(this, IDM_OPTIONS, wxDefaultPosition, wxDefaultSize, - 0, NULL, wxNO_BORDER); - m_options->Append(wxT("Unify")); - m_options->Append(wxT("Resolve symbols")); - m_options->Append(wxT("Write master")); - m_options->Append(wxT("Show unique")); - m_optionsSizer->Add(m_options, 0, 0, 0); - - // I could not find any transparency setting and it would not automatically space correctly - m_options->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); - m_options->SetMinSize(wxSize(m_options->GetSize().GetWidth() - 40, - m_options->GetCount() * 15)); - #ifdef _WIN32 - for (int i = 0; i < m_options->GetCount(); ++i) - m_options->GetItem(i)->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); - #endif - - m_checks = new wxCheckListBox(this, IDM_LOGCHECKS, wxDefaultPosition, wxSize(120, 280)); - - // finally add it to the sizer - sizerLeft->Add(m_RadioBox[0], 0, wxGROW); - sizerLeft->Add(m_optionsSizer, 0, wxGROW); - sizerLeft->Add(m_checks, 1, wxGROW); - - - // right windows ----------------------------------------------------- - m_log = new wxTextCtrl(this, IDM_LOG, _T(""), wxDefaultPosition, wxSize(600, 120), - wxTE_MULTILINE | wxTE_READONLY | wxTE_DONTWRAP); - m_cmdline = new wxTextCtrl(this, wxID_ANY, _T(""), wxDefaultPosition); - wxButton* btn = new wxButton(this, IDM_SUBMITCMD, _T("Submit")); - - sizerTop->Add(new wxButton(this, IDM_UPDATELOG, _T("Update"))); - sizerTop->Add(new wxButton(this, IDM_CLEARLOG, _T("Clear"))); - sizerTop->Add(new wxButton(this, IDM_ENABLEALL, _T("Enable all"))); - - sizerBottom->Add(m_cmdline, 8, wxGROW | wxRIGHT, 5); - sizerBottom->Add(btn, 1, wxGROW, 0); - - sizerBig->Add(sizerTop, 0, wxGROW); - sizerBig->Add(m_log, 1, wxGROW | wxSHRINK); - sizerBig->Add(sizerBottom, 0, wxGROW); - - sizerUber->Add(sizerLeft, 0, wxGROW); - sizerUber->Add(sizerBig, 1, wxGROW); - - SetSizer(sizerUber); - SetAffirmativeId(IDM_SUBMITCMD); - - // declare this now to be able to use it in Load() - LogManager::m_LogSettings = new CDebugger_LogSettings; - - //sizerTop->SetSizeHints(this); - //sizerTop->Fit(this); - UpdateChecks(); - m_cmdline->SetFocus(); - m_bCheckDirty = false; - - /* Load ini from here instead of from CodeWindow.cpp to make sure that - settings are loaded if the window is showing */ - IniFile file; - file.Load(DEBUGGER_CONFIG_FILE); - Load(file); -} - - -// ======================================================= -// This is called from the CodeWindow deconstruction function. -// ------------- -void CLogWindow::Save(IniFile& _IniFile) const -{ - _IniFile.Set("LogWindow", "x", GetPosition().x); - _IniFile.Set("LogWindow", "y", GetPosition().y); - _IniFile.Set("LogWindow", "w", GetSize().GetWidth()); - _IniFile.Set("LogWindow", "h", GetSize().GetHeight()); -} - - -// ======================================================= -// This is called from the class construction function. -// ------------- -void CLogWindow::Load(IniFile& _IniFile) -{ - int x,y,w,h; - _IniFile.Get("LogWindow", "x", &x, GetPosition().x); - _IniFile.Get("LogWindow", "y", &y, GetPosition().y); - _IniFile.Get("LogWindow", "w", &w, GetSize().GetWidth()); - _IniFile.Get("LogWindow", "h", &h, GetSize().GetHeight()); - SetSize(x, y, w, h); - - // Load verbosity setting - int v; - _IniFile.Get("LogWindow", "Verbosity", &v, m_RadioBox[0]->GetSelection()); - m_RadioBox[0]->SetSelection(v); - LogManager::m_LogSettings->m_iVerbosity = v; - - // Load options - _IniFile.Get("LogWindow", "Unify", &LogManager::m_LogSettings->bUnify, false); - _IniFile.Get("LogWindow", "ResolveSymbols", &LogManager::m_LogSettings->bResolve, false); - _IniFile.Get("LogWindow", "WriteMaster", &LogManager::m_LogSettings->bWriteMaster, false); - _IniFile.Get("LogWindow", "OnlyUnique", &bOnlyUnique, false); - m_options->Check(0, LogManager::m_LogSettings->bUnify); - m_options->Check(1, LogManager::m_LogSettings->bResolve); - m_options->Check(2, LogManager::m_LogSettings->bWriteMaster); - m_options->Check(3, bOnlyUnique); - - // If we use the Unify option - if(LogManager::m_LogSettings->bUnify) - { - m_RadioBox[0]->SetSelection(LogManager::VERBOSITY_LEVELS); - LogManager::m_LogSettings->m_iVerbosity = LogManager::VERBOSITY_LEVELS; - m_RadioBox[0]->Disable(); - } -} - -void CLogWindow::OnSubmit(wxCommandEvent& event) -{ - Console_Submit(m_cmdline->GetValue().To8BitData()); - m_cmdline->SetValue(_T("")); - NotifyUpdate(); -} - - -void CLogWindow::OnClear(wxCommandEvent& event) -{ - if (Core::GetState() != Core::CORE_UNINITIALIZED) // avoid crash - { - LogManager::Clear(); - LOG(MASTER_LOG, "(log cleared)."); - NotifyUpdate(); - } -} - - -// ---------------------------------------------------------------------------------------- -// Enable or disable all boxes for the current verbosity level and save the changes. -// ------------- -void CLogWindow::OnEnableAll(wxCommandEvent& event) -{ - if (!LogManager::m_Log[0]) - return; - static bool enable = true; - int v = LogManager::m_LogSettings->m_iVerbosity; - IniFile ini; - ini.Load(DEBUGGER_CONFIG_FILE); - - // Unified case. Write the same to all levels. - if(m_options->IsChecked(0)) - { - for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) - { - m_checks->Check(i, enable); // get all from the current selection - for (int j = 0; j <= LogManager::VERBOSITY_LEVELS; j++) - { - LogManager::m_Log[i + j*100]->m_bEnable = enable; - LogManager::m_Log[i + j*100]->m_bShowInLog = enable; - ini.Set("LogManager", LogManager::m_Log[i + j*100]->m_szShortName, enable); - } - } - } - else // otherwise only update the current shown level - { - for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) - { - m_checks->Check(i, enable); - LogManager::m_Log[i + v*100]->m_bEnable = enable; - LogManager::m_Log[i + v*100]->m_bShowInLog = enable; - ini.Set("LogManager", LogManager::m_Log[i + v*100]->m_szShortName, enable); - } - } - - - ini.Save(DEBUGGER_CONFIG_FILE); - enable = !enable; -} - - -// ---------------------------------------------------------------------------------------- -// Append checkboxes and update checked groups. -// ------------- -void CLogWindow::UpdateChecks() -{ - if (!LogManager::m_bInitialized) - { - return; - } - - // This is only run once to append checkboxes to the wxCheckListBox. - if (m_checks->GetCount() == 0) - { - // [F|RES] hide the window while we fill it... wxwidgets gets trouble if you don't do it - // (at least the win version) - m_checks->Show(false); - - for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) - { - m_checks->Append(wxString::FromAscii(LogManager::m_Log[i]->m_szName)); - } - - m_checks->Show(true); - } - - // ---------------------------------------------------------------------------------------- - // Load the correct values and enable/disable the right groups - // ------------- - int v = LogManager::m_LogSettings->m_iVerbosity; - IniFile ini; - ini.Load(DEBUGGER_CONFIG_FILE); - - for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) - { - for (int j = 0; j <= LogManager::VERBOSITY_LEVELS; j++) - { - bool Enabled = false; - ini.Get("LogManager", LogManager::m_Log[i + j*100]->m_szShortName, &Enabled, false); - LogManager::m_Log[i + j*100]->m_bEnable = Enabled; - LogManager::m_Log[i + j*100]->m_bShowInLog = Enabled; - if(j == v) m_checks->Check(i, Enabled); - } - } - - m_bCheckDirty = true; -} - - -// ---------------------------------------------------------------------------------------- -// When an option is changed, save the change -// --------------- -void CLogWindow::OnOptionsCheck(wxCommandEvent& event) -{ - IniFile ini; - ini.Load(DEBUGGER_CONFIG_FILE); - - //PanicAlert("%i", (int)Core::GetState()); - - // Unified case. If the core is uninitialized we only disable the radio boxes - if(m_options->IsChecked(0) && Core::GetState() == Core::CORE_UNINITIALIZED) - { - m_RadioBox[0]->SetSelection(LogManager::VERBOSITY_LEVELS); - LogManager::m_LogSettings->m_iVerbosity = LogManager::VERBOSITY_LEVELS; - m_RadioBox[0]->Disable(); - } - // otherwise we both disable them and update all blocks - else if(m_options->IsChecked(0) && Core::GetState() != Core::CORE_UNINITIALIZED) - { - m_RadioBox[0]->SetSelection(LogManager::VERBOSITY_LEVELS); - LogManager::m_LogSettings->m_iVerbosity = LogManager::VERBOSITY_LEVELS; - m_RadioBox[0]->Disable(); - - for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) - { - bool Enabled = m_checks->IsChecked(i); // get all from the current i - for (int j = 0; j <= LogManager::VERBOSITY_LEVELS; j++) - { - // update groups to enabled or disabled - LogManager::m_Log[i + 100*j]->m_bEnable = Enabled; - LogManager::m_Log[i + 100*j]->m_bShowInLog = Enabled; - - // update all verbosity levels to this level's Enabled - ini.Set("LogManager", LogManager::m_Log[i + 100*j]->m_szShortName, Enabled); - } - } - } - else - { - m_RadioBox[0]->Enable(true); - } - - LogManager::m_LogSettings->bUnify = m_options->IsChecked(0); - LogManager::m_LogSettings->bResolve = m_options->IsChecked(1); - LogManager::m_LogSettings->bWriteMaster = m_options->IsChecked(2); - bOnlyUnique = m_options->IsChecked(3); - - ini.Set("LogWindow", "Unify", m_options->IsChecked(0)); - ini.Set("LogWindow", "ResolveSymbols", m_options->IsChecked(1)); - ini.Set("LogWindow", "WriteMaster", m_options->IsChecked(2)); - ini.Set("LogWindow", "OnlyUnique", m_options->IsChecked(3)); - ini.Save(DEBUGGER_CONFIG_FILE); - if (Core::GetState() != Core::CORE_UNINITIALIZED) UpdateLog(); -} - - -// ---------------------------------------------------------------------------------------- -// When a checkbox is changed -// --------------- -void CLogWindow::OnLogCheck(wxCommandEvent& event) -{ - if (!LogManager::m_bInitialized) return; - - IniFile ini; - ini.Load(DEBUGGER_CONFIG_FILE); - int v = LogManager::m_LogSettings->m_iVerbosity; // current radio button - int uni = LogManager::m_LogSettings->bUnify; - - // Unified case - if(uni) - { - for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) - { - for (int j = 0; j <= LogManager::VERBOSITY_LEVELS; j++) - { - // update groups to enabled or disabled - bool Enabled = m_checks->IsChecked(i); // get all from the current i - LogManager::m_Log[i + 100*j]->m_bEnable = Enabled; - LogManager::m_Log[i + 100*j]->m_bShowInLog = Enabled; - - ini.Set("LogManager", LogManager::m_Log[i + 100*j]->m_szShortName, Enabled); - } - } - } - else - { - for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) - { - // update groups to enabled or disabled - bool Enabled = m_checks->IsChecked(i); - LogManager::m_Log[i + 100*v]->m_bEnable = Enabled; - LogManager::m_Log[i + 100*v]->m_bShowInLog = Enabled; - - ini.Set("LogManager", LogManager::m_Log[i + 100*v]->m_szShortName, Enabled); - } - } - - ini.Save(DEBUGGER_CONFIG_FILE); - - m_bCheckDirty = true; - if (Core::GetState() != Core::CORE_UNINITIALIZED) UpdateLog(); -} - - -// ---------------------------------------------------------------------------------------- -// When the verbosity level is changed -// ------------- -void CLogWindow::OnRadioChange(wxCommandEvent& event) -{ - // get selection - int v = m_RadioBox[0]->GetSelection(); - - // save it - LogManager::m_LogSettings->m_iVerbosity = v; - IniFile ini; - ini.Load(DEBUGGER_CONFIG_FILE); - ini.Set("LogWindow", "Verbosity", v); - ini.Save(DEBUGGER_CONFIG_FILE); - - // This check is because we allow this to be changed before a game has been loaded so - // that the boxes do not exist yet - if (Core::GetState() != Core::CORE_UNINITIALIZED) - { - for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) - { - // update groups to enabled or disabled - bool Enabled; - ini.Get("LogManager", LogManager::m_Log[i + 100*v]->m_szShortName, &Enabled, false); - LogManager::m_Log[i + 100*v]->m_bEnable = Enabled; - LogManager::m_Log[i + 100*v]->m_bShowInLog = Enabled; - m_checks->Check(i, Enabled); - } - - m_bCheckDirty = true; - UpdateLog(); - } -} - - -void CLogWindow::OnUpdateLog(wxCommandEvent& event) -{ - if (Core::GetState() != Core::CORE_UNINITIALIZED) UpdateLog(); -} - - -void CLogWindow::NotifyUpdate() -{ - UpdateChecks(); - UpdateLog(); -} - - -void CLogWindow::UpdateLog() -{ - static int last = -1; - int v = LogManager::m_LogSettings->m_iVerbosity; - int i = LogManager::m_nextMessages[v]; - - // check if the the log has been updated (ie if it's dirty) - if ((last == i) && !m_bCheckDirty) - { - return; - } - m_bCheckDirty = false; - last = i; - - // ---------------------------------------------------------------------------------------- - // Prepare a selection of the memory log to show to screen - // --------------- - int count = 0; - char* p = m_logBuffer; - - // go through all rows - while (count < MAX_MESSAGES) - { - count++; - const LogManager::SMessage& message = LogManager::m_Messages[v][i]; - - if (message.m_bInUse) // check if the line has a value - { - int len = message.m_dwMsgLen; - - // this is what we use, I'm not sure why we have this option - if (LogManager::m_activeLog == LogTypes::MASTER_LOG) - { - // only show checkboxed logs - if (LogManager::m_Log[message.m_type]->m_bShowInLog) - { - if(bOnlyUnique) /* don't show lower level messages that have fallen through - to this higher level */ - { - if(message.m_verbosity == v) - { - // memcpy is faster than strcpy - memcpy(p, message.m_szMessage, len); - p += len; - } - } - else - { - // memcpy is faster than strcpy - memcpy(p, message.m_szMessage, len); - p += len; - } - } - } - else - { - if (message.m_type == LogManager::m_activeLog) - { - memcpy(p, message.m_szMessage, len); - p += len; - } - } - } - - i++; - - if (i >= MAX_MESSAGES) - { - i = 0; - } - } - // --------------- - - *p = 0; //end the string - m_log->SetValue(wxString::FromAscii(m_logBuffer)); - m_log->SetInsertionPoint(p - m_logBuffer - 1); - m_log->ShowPosition( m_log->GetLastPosition()); // show last line -} - - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Debugger.h" +#include "LogManager.h" + +#include +#include +#include +#include + +#include "Core.h" // for Core::GetState() +#include "LogWindow.h" +#include "Console.h" +#include "IniFile.h" + +// declare this now to be able to use it in Load() +CDebugger_LogSettings* LogManager::m_LogSettings; + +BEGIN_EVENT_TABLE(CLogWindow, wxDialog) + EVT_BUTTON(IDM_SUBMITCMD, CLogWindow::OnSubmit) + EVT_BUTTON(IDM_UPDATELOG, CLogWindow::OnUpdateLog) + EVT_BUTTON(IDM_CLEARLOG, CLogWindow::OnClear) + EVT_BUTTON(IDM_ENABLEALL, CLogWindow::OnEnableAll) + EVT_CHECKLISTBOX(IDM_OPTIONS, CLogWindow::OnOptionsCheck) + EVT_CHECKLISTBOX(IDM_LOGCHECKS, CLogWindow::OnLogCheck) + EVT_RADIOBOX(IDM_RADIO0, CLogWindow::OnRadioChange) +END_EVENT_TABLE() + + +CLogWindow::CLogWindow(wxWindow* parent) + : wxDialog(parent, wxID_ANY, _T("Log/Console"), wxPoint(100, 700), wxSize(800, 270), + wxDEFAULT_DIALOG_STYLE | wxRESIZE_BORDER) +{ + wxBoxSizer* sizerTop = new wxBoxSizer(wxHORIZONTAL), // buttons + * sizerUber = new wxBoxSizer(wxHORIZONTAL), // whole plane + * sizerBig = new wxBoxSizer(wxVERTICAL), // RIGHT sizer + * sizerBottom = new wxBoxSizer(wxHORIZONTAL), // submit row + * sizerLeft = new wxBoxSizer(wxVERTICAL); // LEFT sizer + + // left checkboxes and radio boxes ----------------------------------- + int m_radioBoxNChoices[1]; + wxString m_radioBoxChoices0[] = { wxT("0"), wxT("1"), wxT("2"), wxT("3") }; + m_radioBoxNChoices[0] = sizeof( m_radioBoxChoices0 ) / sizeof( wxString ); + m_RadioBox[0] = new wxRadioBox( this, IDM_RADIO0, wxT("Verbosity"), + wxDefaultPosition, wxDefaultSize, m_radioBoxNChoices[0], m_radioBoxChoices0, 1, wxRA_SPECIFY_ROWS); + + wxStaticBoxSizer * m_optionsSizer = new wxStaticBoxSizer(wxVERTICAL, this, wxT("Settings")); + m_options = new wxCheckListBox(this, IDM_OPTIONS, wxDefaultPosition, wxDefaultSize, + 0, NULL, wxNO_BORDER); + m_options->Append(wxT("Unify")); + m_options->Append(wxT("Resolve symbols")); + m_options->Append(wxT("Write master")); + m_options->Append(wxT("Show unique")); + m_optionsSizer->Add(m_options, 0, 0, 0); + + // I could not find any transparency setting and it would not automatically space correctly + m_options->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); + m_options->SetMinSize(wxSize(m_options->GetSize().GetWidth() - 40, + m_options->GetCount() * 15)); + #ifdef _WIN32 + for (int i = 0; i < m_options->GetCount(); ++i) + m_options->GetItem(i)->SetBackgroundColour(wxSystemSettings::GetColour(wxSYS_COLOUR_BTNFACE)); + #endif + + m_checks = new wxCheckListBox(this, IDM_LOGCHECKS, wxDefaultPosition, wxSize(120, 280)); + + // finally add it to the sizer + sizerLeft->Add(m_RadioBox[0], 0, wxGROW); + sizerLeft->Add(m_optionsSizer, 0, wxGROW); + sizerLeft->Add(m_checks, 1, wxGROW); + + + // right windows ----------------------------------------------------- + m_log = new wxTextCtrl(this, IDM_LOG, _T(""), wxDefaultPosition, wxSize(600, 120), + wxTE_MULTILINE | wxTE_READONLY | wxTE_DONTWRAP); + m_cmdline = new wxTextCtrl(this, wxID_ANY, _T(""), wxDefaultPosition); + wxButton* btn = new wxButton(this, IDM_SUBMITCMD, _T("Submit")); + + sizerTop->Add(new wxButton(this, IDM_UPDATELOG, _T("Update"))); + sizerTop->Add(new wxButton(this, IDM_CLEARLOG, _T("Clear"))); + sizerTop->Add(new wxButton(this, IDM_ENABLEALL, _T("Enable all"))); + + sizerBottom->Add(m_cmdline, 8, wxGROW | wxRIGHT, 5); + sizerBottom->Add(btn, 1, wxGROW, 0); + + sizerBig->Add(sizerTop, 0, wxGROW); + sizerBig->Add(m_log, 1, wxGROW | wxSHRINK); + sizerBig->Add(sizerBottom, 0, wxGROW); + + sizerUber->Add(sizerLeft, 0, wxGROW); + sizerUber->Add(sizerBig, 1, wxGROW); + + SetSizer(sizerUber); + SetAffirmativeId(IDM_SUBMITCMD); + + // declare this now to be able to use it in Load() + LogManager::m_LogSettings = new CDebugger_LogSettings; + + //sizerTop->SetSizeHints(this); + //sizerTop->Fit(this); + UpdateChecks(); + m_cmdline->SetFocus(); + m_bCheckDirty = false; + + /* Load ini from here instead of from CodeWindow.cpp to make sure that + settings are loaded if the window is showing */ + IniFile file; + file.Load(DEBUGGER_CONFIG_FILE); + Load(file); +} + + +// ======================================================= +// This is called from the CodeWindow deconstruction function. +// ------------- +void CLogWindow::Save(IniFile& _IniFile) const +{ + _IniFile.Set("LogWindow", "x", GetPosition().x); + _IniFile.Set("LogWindow", "y", GetPosition().y); + _IniFile.Set("LogWindow", "w", GetSize().GetWidth()); + _IniFile.Set("LogWindow", "h", GetSize().GetHeight()); +} + + +// ======================================================= +// This is called from the class construction function. +// ------------- +void CLogWindow::Load(IniFile& _IniFile) +{ + int x,y,w,h; + _IniFile.Get("LogWindow", "x", &x, GetPosition().x); + _IniFile.Get("LogWindow", "y", &y, GetPosition().y); + _IniFile.Get("LogWindow", "w", &w, GetSize().GetWidth()); + _IniFile.Get("LogWindow", "h", &h, GetSize().GetHeight()); + SetSize(x, y, w, h); + + // Load verbosity setting + int v; + _IniFile.Get("LogWindow", "Verbosity", &v, m_RadioBox[0]->GetSelection()); + m_RadioBox[0]->SetSelection(v); + LogManager::m_LogSettings->m_iVerbosity = v; + + // Load options + _IniFile.Get("LogWindow", "Unify", &LogManager::m_LogSettings->bUnify, false); + _IniFile.Get("LogWindow", "ResolveSymbols", &LogManager::m_LogSettings->bResolve, false); + _IniFile.Get("LogWindow", "WriteMaster", &LogManager::m_LogSettings->bWriteMaster, false); + _IniFile.Get("LogWindow", "OnlyUnique", &bOnlyUnique, false); + m_options->Check(0, LogManager::m_LogSettings->bUnify); + m_options->Check(1, LogManager::m_LogSettings->bResolve); + m_options->Check(2, LogManager::m_LogSettings->bWriteMaster); + m_options->Check(3, bOnlyUnique); + + // If we use the Unify option + if(LogManager::m_LogSettings->bUnify) + { + m_RadioBox[0]->SetSelection(LogManager::VERBOSITY_LEVELS); + LogManager::m_LogSettings->m_iVerbosity = LogManager::VERBOSITY_LEVELS; + m_RadioBox[0]->Disable(); + } +} + +void CLogWindow::OnSubmit(wxCommandEvent& event) +{ + Console_Submit(m_cmdline->GetValue().To8BitData()); + m_cmdline->SetValue(_T("")); + NotifyUpdate(); +} + + +void CLogWindow::OnClear(wxCommandEvent& event) +{ + if (Core::GetState() != Core::CORE_UNINITIALIZED) // avoid crash + { + LogManager::Clear(); + LOG(MASTER_LOG, "(log cleared)."); + NotifyUpdate(); + } +} + + +// ---------------------------------------------------------------------------------------- +// Enable or disable all boxes for the current verbosity level and save the changes. +// ------------- +void CLogWindow::OnEnableAll(wxCommandEvent& event) +{ + if (!LogManager::m_Log[0]) + return; + static bool enable = true; + int v = LogManager::m_LogSettings->m_iVerbosity; + IniFile ini; + ini.Load(DEBUGGER_CONFIG_FILE); + + // Unified case. Write the same to all levels. + if(m_options->IsChecked(0)) + { + for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) + { + m_checks->Check(i, enable); // get all from the current selection + for (int j = 0; j <= LogManager::VERBOSITY_LEVELS; j++) + { + LogManager::m_Log[i + j*100]->m_bEnable = enable; + LogManager::m_Log[i + j*100]->m_bShowInLog = enable; + ini.Set("LogManager", LogManager::m_Log[i + j*100]->m_szShortName, enable); + } + } + } + else // otherwise only update the current shown level + { + for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) + { + m_checks->Check(i, enable); + LogManager::m_Log[i + v*100]->m_bEnable = enable; + LogManager::m_Log[i + v*100]->m_bShowInLog = enable; + ini.Set("LogManager", LogManager::m_Log[i + v*100]->m_szShortName, enable); + } + } + + + ini.Save(DEBUGGER_CONFIG_FILE); + enable = !enable; +} + + +// ---------------------------------------------------------------------------------------- +// Append checkboxes and update checked groups. +// ------------- +void CLogWindow::UpdateChecks() +{ + if (!LogManager::m_bInitialized) + { + return; + } + + // This is only run once to append checkboxes to the wxCheckListBox. + if (m_checks->GetCount() == 0) + { + // [F|RES] hide the window while we fill it... wxwidgets gets trouble if you don't do it + // (at least the win version) + m_checks->Show(false); + + for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) + { + m_checks->Append(wxString::FromAscii(LogManager::m_Log[i]->m_szName)); + } + + m_checks->Show(true); + } + + // ---------------------------------------------------------------------------------------- + // Load the correct values and enable/disable the right groups + // ------------- + int v = LogManager::m_LogSettings->m_iVerbosity; + IniFile ini; + ini.Load(DEBUGGER_CONFIG_FILE); + + for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) + { + for (int j = 0; j <= LogManager::VERBOSITY_LEVELS; j++) + { + bool Enabled = false; + ini.Get("LogManager", LogManager::m_Log[i + j*100]->m_szShortName, &Enabled, false); + LogManager::m_Log[i + j*100]->m_bEnable = Enabled; + LogManager::m_Log[i + j*100]->m_bShowInLog = Enabled; + if(j == v) m_checks->Check(i, Enabled); + } + } + + m_bCheckDirty = true; +} + + +// ---------------------------------------------------------------------------------------- +// When an option is changed, save the change +// --------------- +void CLogWindow::OnOptionsCheck(wxCommandEvent& event) +{ + IniFile ini; + ini.Load(DEBUGGER_CONFIG_FILE); + + //PanicAlert("%i", (int)Core::GetState()); + + // Unified case. If the core is uninitialized we only disable the radio boxes + if(m_options->IsChecked(0) && Core::GetState() == Core::CORE_UNINITIALIZED) + { + m_RadioBox[0]->SetSelection(LogManager::VERBOSITY_LEVELS); + LogManager::m_LogSettings->m_iVerbosity = LogManager::VERBOSITY_LEVELS; + m_RadioBox[0]->Disable(); + } + // otherwise we both disable them and update all blocks + else if(m_options->IsChecked(0) && Core::GetState() != Core::CORE_UNINITIALIZED) + { + m_RadioBox[0]->SetSelection(LogManager::VERBOSITY_LEVELS); + LogManager::m_LogSettings->m_iVerbosity = LogManager::VERBOSITY_LEVELS; + m_RadioBox[0]->Disable(); + + for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) + { + bool Enabled = m_checks->IsChecked(i); // get all from the current i + for (int j = 0; j <= LogManager::VERBOSITY_LEVELS; j++) + { + // update groups to enabled or disabled + LogManager::m_Log[i + 100*j]->m_bEnable = Enabled; + LogManager::m_Log[i + 100*j]->m_bShowInLog = Enabled; + + // update all verbosity levels to this level's Enabled + ini.Set("LogManager", LogManager::m_Log[i + 100*j]->m_szShortName, Enabled); + } + } + } + else + { + m_RadioBox[0]->Enable(true); + } + + LogManager::m_LogSettings->bUnify = m_options->IsChecked(0); + LogManager::m_LogSettings->bResolve = m_options->IsChecked(1); + LogManager::m_LogSettings->bWriteMaster = m_options->IsChecked(2); + bOnlyUnique = m_options->IsChecked(3); + + ini.Set("LogWindow", "Unify", m_options->IsChecked(0)); + ini.Set("LogWindow", "ResolveSymbols", m_options->IsChecked(1)); + ini.Set("LogWindow", "WriteMaster", m_options->IsChecked(2)); + ini.Set("LogWindow", "OnlyUnique", m_options->IsChecked(3)); + ini.Save(DEBUGGER_CONFIG_FILE); + if (Core::GetState() != Core::CORE_UNINITIALIZED) UpdateLog(); +} + + +// ---------------------------------------------------------------------------------------- +// When a checkbox is changed +// --------------- +void CLogWindow::OnLogCheck(wxCommandEvent& event) +{ + if (!LogManager::m_bInitialized) return; + + IniFile ini; + ini.Load(DEBUGGER_CONFIG_FILE); + int v = LogManager::m_LogSettings->m_iVerbosity; // current radio button + int uni = LogManager::m_LogSettings->bUnify; + + // Unified case + if(uni) + { + for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) + { + for (int j = 0; j <= LogManager::VERBOSITY_LEVELS; j++) + { + // update groups to enabled or disabled + bool Enabled = m_checks->IsChecked(i); // get all from the current i + LogManager::m_Log[i + 100*j]->m_bEnable = Enabled; + LogManager::m_Log[i + 100*j]->m_bShowInLog = Enabled; + + ini.Set("LogManager", LogManager::m_Log[i + 100*j]->m_szShortName, Enabled); + } + } + } + else + { + for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) + { + // update groups to enabled or disabled + bool Enabled = m_checks->IsChecked(i); + LogManager::m_Log[i + 100*v]->m_bEnable = Enabled; + LogManager::m_Log[i + 100*v]->m_bShowInLog = Enabled; + + ini.Set("LogManager", LogManager::m_Log[i + 100*v]->m_szShortName, Enabled); + } + } + + ini.Save(DEBUGGER_CONFIG_FILE); + + m_bCheckDirty = true; + if (Core::GetState() != Core::CORE_UNINITIALIZED) UpdateLog(); +} + + +// ---------------------------------------------------------------------------------------- +// When the verbosity level is changed +// ------------- +void CLogWindow::OnRadioChange(wxCommandEvent& event) +{ + // get selection + int v = m_RadioBox[0]->GetSelection(); + + // save it + LogManager::m_LogSettings->m_iVerbosity = v; + IniFile ini; + ini.Load(DEBUGGER_CONFIG_FILE); + ini.Set("LogWindow", "Verbosity", v); + ini.Save(DEBUGGER_CONFIG_FILE); + + // This check is because we allow this to be changed before a game has been loaded so + // that the boxes do not exist yet + if (Core::GetState() != Core::CORE_UNINITIALIZED) + { + for (int i = 0; i < LogTypes::NUMBER_OF_LOGS; i++) + { + // update groups to enabled or disabled + bool Enabled; + ini.Get("LogManager", LogManager::m_Log[i + 100*v]->m_szShortName, &Enabled, false); + LogManager::m_Log[i + 100*v]->m_bEnable = Enabled; + LogManager::m_Log[i + 100*v]->m_bShowInLog = Enabled; + m_checks->Check(i, Enabled); + } + + m_bCheckDirty = true; + UpdateLog(); + } +} + + +void CLogWindow::OnUpdateLog(wxCommandEvent& event) +{ + if (Core::GetState() != Core::CORE_UNINITIALIZED) UpdateLog(); +} + + +void CLogWindow::NotifyUpdate() +{ + UpdateChecks(); + UpdateLog(); +} + + +void CLogWindow::UpdateLog() +{ + static int last = -1; + int v = LogManager::m_LogSettings->m_iVerbosity; + int i = LogManager::m_nextMessages[v]; + + // check if the the log has been updated (ie if it's dirty) + if ((last == i) && !m_bCheckDirty) + { + return; + } + m_bCheckDirty = false; + last = i; + + // ---------------------------------------------------------------------------------------- + // Prepare a selection of the memory log to show to screen + // --------------- + int count = 0; + char* p = m_logBuffer; + + // go through all rows + while (count < MAX_MESSAGES) + { + count++; + const LogManager::SMessage& message = LogManager::m_Messages[v][i]; + + if (message.m_bInUse) // check if the line has a value + { + int len = message.m_dwMsgLen; + + // this is what we use, I'm not sure why we have this option + if (LogManager::m_activeLog == LogTypes::MASTER_LOG) + { + // only show checkboxed logs + if (LogManager::m_Log[message.m_type]->m_bShowInLog) + { + if(bOnlyUnique) /* don't show lower level messages that have fallen through + to this higher level */ + { + if(message.m_verbosity == v) + { + // memcpy is faster than strcpy + memcpy(p, message.m_szMessage, len); + p += len; + } + } + else + { + // memcpy is faster than strcpy + memcpy(p, message.m_szMessage, len); + p += len; + } + } + } + else + { + if (message.m_type == LogManager::m_activeLog) + { + memcpy(p, message.m_szMessage, len); + p += len; + } + } + } + + i++; + + if (i >= MAX_MESSAGES) + { + i = 0; + } + } + // --------------- + + *p = 0; //end the string + m_log->SetValue(wxString::FromAscii(m_logBuffer)); + m_log->SetInsertionPoint(p - m_logBuffer - 1); + m_log->ShowPosition( m_log->GetLastPosition()); // show last line +} + + diff --git a/Source/Core/DebuggerWX/Src/MemoryCheckDlg.cpp b/Source/Core/DebuggerWX/Src/MemoryCheckDlg.cpp index 41e09aec49..2adc1413c7 100644 --- a/Source/Core/DebuggerWX/Src/MemoryCheckDlg.cpp +++ b/Source/Core/DebuggerWX/Src/MemoryCheckDlg.cpp @@ -1,112 +1,112 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "MemoryCheckDlg.h" -#include "Common.h" -#include "Debugger.h" -#include "StringUtil.h" -#include "Debugger/Debugger_BreakPoints.h" - -BEGIN_EVENT_TABLE(MemoryCheckDlg,wxDialog) - EVT_CLOSE(MemoryCheckDlg::OnClose) - EVT_BUTTON(ID_OK, MemoryCheckDlg::OnOK) - EVT_BUTTON(ID_CANCEL, MemoryCheckDlg::OnCancel) -END_EVENT_TABLE() - - -MemoryCheckDlg::MemoryCheckDlg(wxWindow *parent, wxWindowID id, const wxString &title, const wxPoint &position, const wxSize& size, long style) -: wxDialog(parent, id, title, position, size, style) -{ - CreateGUIControls(); -} - -MemoryCheckDlg::~MemoryCheckDlg() -{ -} - -void MemoryCheckDlg::CreateGUIControls() -{ - SetIcon(wxNullIcon); - SetSize(8,8,415,122); - Center(); - - m_pButtonCancel = new wxButton(this, ID_CANCEL, wxT("Cancel"), wxPoint(248,64), wxSize(73,25), 0, wxDefaultValidator, wxT("Cancel")); - m_pButtonCancel->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); - - m_pButtonOK = new wxButton(this, ID_OK, wxT("OK"), wxPoint(328,64), wxSize(73,25), 0, wxDefaultValidator, wxT("OK")); - m_pButtonOK->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); - - m_pReadFlag = new wxCheckBox(this, ID_READ_FLAG, wxT("Read"), wxPoint(336,33), wxSize(57,15), 0, wxDefaultValidator, wxT("Read")); - m_pReadFlag->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); - - m_pWriteFlag = new wxCheckBox(this, ID_WRITE_FLAG, wxT("Write"), wxPoint(336,16), wxSize(57,17), 0, wxDefaultValidator, wxT("WxCheckBox1")); - m_pWriteFlag->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); - - wxStaticBox* WxStaticBox2 = new wxStaticBox(this, ID_WXSTATICBOX2, wxT("Break On"), wxPoint(328,0), wxSize(73,57)); - WxStaticBox2->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); - - wxStaticText* WxStaticText2 = new wxStaticText(this, ID_WXSTATICTEXT2, wxT("End"), wxPoint(168,24), wxDefaultSize, 0, wxT("WxStaticText2")); - WxStaticText2->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); - - wxStaticText* WxStaticText1 = new wxStaticText(this, ID_WXSTATICTEXT1, wxT("Start"), wxPoint(8,24), wxDefaultSize, 0, wxT("WxStaticText1")); - WxStaticText1->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); - - m_pEditStartAddress = new wxTextCtrl(this, ID_EDIT_START_ADDR, wxT("80000000"), wxPoint(40,24), wxSize(109,20), 0, wxDefaultValidator, wxT("WxEdit1")); - m_pEditStartAddress->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); - - m_pEditEndAddress = new wxTextCtrl(this, ID_EDIT_END_ADDRESS, wxT("80000000"), wxPoint(200,24), wxSize(109,20), 0, wxDefaultValidator, wxT("WxEdit2")); - m_pEditEndAddress->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); - - wxStaticBox* WxStaticBox1 = new wxStaticBox(this, ID_WXSTATICBOX1, wxT("Address Range"), wxPoint(0,0), wxSize(321,57)); - WxStaticBox1->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); -} - -void MemoryCheckDlg::OnClose(wxCloseEvent& /*event*/) -{ - Destroy(); -} - -void MemoryCheckDlg::OnOK(wxCommandEvent& /*event*/) -{ - wxString StartAddressString = m_pEditStartAddress->GetLineText(0); - wxString EndAddressString = m_pEditEndAddress->GetLineText(0); - bool OnRead = m_pReadFlag->GetValue(); - bool OnWrite = m_pWriteFlag->GetValue(); - - u32 StartAddress, EndAddress; - if (AsciiToHex(StartAddressString.mb_str(), StartAddress) && - AsciiToHex(EndAddressString.mb_str(), EndAddress)) - { - TMemCheck MemCheck; - MemCheck.StartAddress = StartAddress; - MemCheck.EndAddress = EndAddress; - MemCheck.OnRead = OnRead; - MemCheck.OnWrite = OnWrite; - - MemCheck.Log = true; - MemCheck.Break = true; - - CBreakPoints::AddMemoryCheck(MemCheck); - CBreakPoints::UpdateBreakPointView(); - Close(); - } -} - -void MemoryCheckDlg::OnCancel(wxCommandEvent& /*event*/) -{ - Close(); -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "MemoryCheckDlg.h" +#include "Common.h" +#include "Debugger.h" +#include "StringUtil.h" +#include "Debugger/Debugger_BreakPoints.h" + +BEGIN_EVENT_TABLE(MemoryCheckDlg,wxDialog) + EVT_CLOSE(MemoryCheckDlg::OnClose) + EVT_BUTTON(ID_OK, MemoryCheckDlg::OnOK) + EVT_BUTTON(ID_CANCEL, MemoryCheckDlg::OnCancel) +END_EVENT_TABLE() + + +MemoryCheckDlg::MemoryCheckDlg(wxWindow *parent, wxWindowID id, const wxString &title, const wxPoint &position, const wxSize& size, long style) +: wxDialog(parent, id, title, position, size, style) +{ + CreateGUIControls(); +} + +MemoryCheckDlg::~MemoryCheckDlg() +{ +} + +void MemoryCheckDlg::CreateGUIControls() +{ + SetIcon(wxNullIcon); + SetSize(8,8,415,122); + Center(); + + m_pButtonCancel = new wxButton(this, ID_CANCEL, wxT("Cancel"), wxPoint(248,64), wxSize(73,25), 0, wxDefaultValidator, wxT("Cancel")); + m_pButtonCancel->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); + + m_pButtonOK = new wxButton(this, ID_OK, wxT("OK"), wxPoint(328,64), wxSize(73,25), 0, wxDefaultValidator, wxT("OK")); + m_pButtonOK->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); + + m_pReadFlag = new wxCheckBox(this, ID_READ_FLAG, wxT("Read"), wxPoint(336,33), wxSize(57,15), 0, wxDefaultValidator, wxT("Read")); + m_pReadFlag->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); + + m_pWriteFlag = new wxCheckBox(this, ID_WRITE_FLAG, wxT("Write"), wxPoint(336,16), wxSize(57,17), 0, wxDefaultValidator, wxT("WxCheckBox1")); + m_pWriteFlag->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); + + wxStaticBox* WxStaticBox2 = new wxStaticBox(this, ID_WXSTATICBOX2, wxT("Break On"), wxPoint(328,0), wxSize(73,57)); + WxStaticBox2->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); + + wxStaticText* WxStaticText2 = new wxStaticText(this, ID_WXSTATICTEXT2, wxT("End"), wxPoint(168,24), wxDefaultSize, 0, wxT("WxStaticText2")); + WxStaticText2->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); + + wxStaticText* WxStaticText1 = new wxStaticText(this, ID_WXSTATICTEXT1, wxT("Start"), wxPoint(8,24), wxDefaultSize, 0, wxT("WxStaticText1")); + WxStaticText1->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); + + m_pEditStartAddress = new wxTextCtrl(this, ID_EDIT_START_ADDR, wxT("80000000"), wxPoint(40,24), wxSize(109,20), 0, wxDefaultValidator, wxT("WxEdit1")); + m_pEditStartAddress->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); + + m_pEditEndAddress = new wxTextCtrl(this, ID_EDIT_END_ADDRESS, wxT("80000000"), wxPoint(200,24), wxSize(109,20), 0, wxDefaultValidator, wxT("WxEdit2")); + m_pEditEndAddress->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); + + wxStaticBox* WxStaticBox1 = new wxStaticBox(this, ID_WXSTATICBOX1, wxT("Address Range"), wxPoint(0,0), wxSize(321,57)); + WxStaticBox1->SetFont(wxFont(8, wxSWISS, wxNORMAL,wxNORMAL, false, wxT("Tahoma"))); +} + +void MemoryCheckDlg::OnClose(wxCloseEvent& /*event*/) +{ + Destroy(); +} + +void MemoryCheckDlg::OnOK(wxCommandEvent& /*event*/) +{ + wxString StartAddressString = m_pEditStartAddress->GetLineText(0); + wxString EndAddressString = m_pEditEndAddress->GetLineText(0); + bool OnRead = m_pReadFlag->GetValue(); + bool OnWrite = m_pWriteFlag->GetValue(); + + u32 StartAddress, EndAddress; + if (AsciiToHex(StartAddressString.mb_str(), StartAddress) && + AsciiToHex(EndAddressString.mb_str(), EndAddress)) + { + TMemCheck MemCheck; + MemCheck.StartAddress = StartAddress; + MemCheck.EndAddress = EndAddress; + MemCheck.OnRead = OnRead; + MemCheck.OnWrite = OnWrite; + + MemCheck.Log = true; + MemCheck.Break = true; + + CBreakPoints::AddMemoryCheck(MemCheck); + CBreakPoints::UpdateBreakPointView(); + Close(); + } +} + +void MemoryCheckDlg::OnCancel(wxCommandEvent& /*event*/) +{ + Close(); +} diff --git a/Source/Core/DebuggerWX/Src/MemoryView.cpp b/Source/Core/DebuggerWX/Src/MemoryView.cpp index 1aef490c51..b449a0c62c 100644 --- a/Source/Core/DebuggerWX/Src/MemoryView.cpp +++ b/Source/Core/DebuggerWX/Src/MemoryView.cpp @@ -1,430 +1,430 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Debugger.h" -#include "Common.h" - -#include "MemoryView.h" -#include -#include - - -enum -{ - IDM_GOTOINMEMVIEW = 12000, - IDM_COPYADDRESS, - IDM_COPYHEX, - IDM_COPYCODE, - IDM_RUNTOHERE, - IDM_DYNARECRESULTS, -}; - - -BEGIN_EVENT_TABLE(CMemoryView, wxControl) -EVT_ERASE_BACKGROUND(CMemoryView::OnErase) -EVT_PAINT(CMemoryView::OnPaint) -EVT_LEFT_DOWN(CMemoryView::OnMouseDown) -EVT_LEFT_UP(CMemoryView::OnMouseUpL) -EVT_MOTION(CMemoryView::OnMouseMove) -EVT_RIGHT_DOWN(CMemoryView::OnMouseDown) -EVT_RIGHT_UP(CMemoryView::OnMouseUpR) -EVT_MENU(-1, CMemoryView::OnPopupMenu) -END_EVENT_TABLE() - -CMemoryView::CMemoryView(DebugInterface* debuginterface, wxWindow* parent, wxWindowID Id, const wxSize& Size) - : wxControl(parent, Id, wxDefaultPosition, Size), - debugger(debuginterface), - rowHeight(13), - selection(0), - oldSelection(0), - selectionChanged(false), - selecting(false), - hasFocus(false), - showHex(false) -{ - rowHeight = 13; - align = debuginterface->getInstructionSize(0); - curAddress = debuginterface->getPC(); -} - - -wxSize CMemoryView::DoGetBestSize() const -{ - wxSize bestSize; - bestSize.x = 300; - bestSize.y = 300; - return(bestSize); -} - - -int CMemoryView::YToAddress(int y) -{ - wxRect rc = GetClientRect(); - int ydiff = y - rc.height / 2 - rowHeight / 2; - ydiff = (int)(floorf((float)ydiff / (float)rowHeight)) + 1; - return(curAddress + ydiff * align); -} - - -void CMemoryView::OnMouseDown(wxMouseEvent& event) -{ - int x = event.m_x; - int y = event.m_y; - - if (x > 16) - { - oldSelection = selection; - selection = YToAddress(y); - // SetCapture(wnd); - bool oldselecting = selecting; - selecting = true; - - if (!oldselecting || (selection != oldSelection)) - { - redraw(); - } - } - else - { - debugger->toggleBreakpoint(YToAddress(y)); - redraw(); - } - - event.Skip(true); -} - - -void CMemoryView::OnMouseMove(wxMouseEvent& event) -{ - wxRect rc = GetClientRect(); - - if (event.m_leftDown) - { - if (event.m_x > 16) - { - if (event.m_y < 0) - { - curAddress -= align; - redraw(); - } - else if (event.m_y > rc.height) - { - curAddress += align; - redraw(); - } - else - { - OnMouseDown(event); - } - } - } - - event.Skip(true); -} - - -void CMemoryView::OnMouseUpL(wxMouseEvent& event) -{ - if (event.m_x > 16) - { - curAddress = YToAddress(event.m_y); - selecting = false; - //ReleaseCapture(); - redraw(); - } - - event.Skip(true); -} - - -void CMemoryView::OnPopupMenu(wxCommandEvent& event) -{ -#if wxUSE_CLIPBOARD - wxTheClipboard->Open(); -#endif - - switch (event.GetId()) - { - case IDM_GOTOINMEMVIEW: - // CMemoryDlg::Goto(selection); - break; - -#if wxUSE_CLIPBOARD - case IDM_COPYADDRESS: - { - wxTheClipboard->SetData(new wxTextDataObject(wxString::Format(_T("%08x"), selection))); - } - break; - - case IDM_COPYCODE: - wxTheClipboard->SetData(new wxTextDataObject(wxString::FromAscii(debugger->disasm(selection)))); //Have to manually convert from char* to wxString, don't have to in Windows? - break; - - case IDM_COPYHEX: - { - char temp[24]; - sprintf(temp, "%08x", debugger->readMemory(selection)); - wxTheClipboard->SetData(new wxTextDataObject(wxString::FromAscii(temp))); - } - break; -#endif - - case IDM_RUNTOHERE: - { - debugger->setBreakpoint(selection); - debugger->runToBreakpoint(); - redraw(); - } - break; - - case IDM_DYNARECRESULTS: - { -// CDynaViewDlg::ViewAddr(selection); -// CDynaViewDlg::Show(TRUE); -// MessageBox(NULL, "not impl", "CtrlDisAsmView", MB_OK); - } - break; - } - -#if wxUSE_CLIPBOARD - wxTheClipboard->Close(); -#endif - event.Skip(true); -} - - -void CMemoryView::OnMouseUpR(wxMouseEvent& event) -{ - // popup menu - wxMenu menu; - //menu.Append(IDM_GOTOINMEMVIEW, "&Goto in mem view"); -#if wxUSE_CLIPBOARD - menu.Append(IDM_COPYADDRESS, wxString::FromAscii("Copy &address")); - menu.Append(IDM_COPYCODE, wxString::FromAscii("Copy &code")); - menu.Append(IDM_COPYHEX, wxString::FromAscii("Copy &hex")); -#endif - menu.Append(IDM_RUNTOHERE, _T("&Run To Here")); - //menu.Append(IDM_DYNARECRESULTS, "Copy &address"); - PopupMenu(&menu); - event.Skip(true); -} - - -void CMemoryView::OnErase(wxEraseEvent& event) -{} - - -void CMemoryView::OnPaint(wxPaintEvent& event) -{ - wxPaintDC dc(this); - int fontSize = 8; - wxRect rc = GetClientRect(); - wxFont font(fontSize, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_LIGHT); - dc.SetFont(font); - struct branch - { - int src, dst, srcAddr; - }; - - branch branches[256]; - int numBranches = 0; - // TODO: Add any drawing code here... - int width = rc.width; - int numRows = (rc.height / rowHeight) / 2 + 2; - //numRows=(numRows&(~1)) + 1; - dc.SetBackgroundMode(wxTRANSPARENT); - const wxChar* bgColor = _T("#ffffff"); - wxPen nullPen(bgColor); - wxPen currentPen(_T("#000000")); - wxPen selPen(_T("#808080")); // gray - nullPen.SetStyle(wxTRANSPARENT); - - wxBrush currentBrush(_T("#FFEfE8")); // ligh gray - wxBrush pcBrush(_T("#70FF70")); // green - wxBrush bpBrush(_T("#FF3311")); // red - wxBrush bgBrush(bgColor); - wxBrush nullBrush(bgColor); - nullBrush.SetStyle(wxTRANSPARENT); - - dc.SetPen(nullPen); - dc.SetBrush(bgBrush); - dc.DrawRectangle(0, 0, 16, rc.height); - dc.DrawRectangle(0, 0, rc.width, 5+8); - // TODO - clean up this freaking mess!!!!! - int i; - - for (i = -numRows; i <= numRows; i++) - { - unsigned int address = curAddress + i * align; - - int rowY1 = rc.height / 2 + rowHeight * i - rowHeight / 2; - int rowY2 = rc.height / 2 + rowHeight * i + rowHeight / 2; - - wxString temp = wxString::Format(_T("%08x"), address); - u32 col = debugger->getColor(address); - wxBrush rowBrush(wxColor(col >> 16, col >> 8, col)); - dc.SetBrush(nullBrush); - dc.SetPen(nullPen); - dc.DrawRectangle(0, rowY1, 16, rowY2); - - if (selecting && (address == selection)) - { - dc.SetPen(selPen); - } - else - { - dc.SetPen(i == 0 ? currentPen : nullPen); - } - - if (address == debugger->getPC()) - { - dc.SetBrush(pcBrush); - } - else - { - dc.SetBrush(rowBrush); - } - - dc.DrawRectangle(16, rowY1, width, rowY2 - 1); - dc.SetBrush(currentBrush); - dc.SetTextForeground(_T("#600000")); - dc.DrawText(temp, 17, rowY1); - char mem[256] = {0}; - strcpy(mem, debugger->getRawMemoryString(address)); - dc.SetTextForeground(_T("#000080")); - dc.DrawText(wxString::FromAscii(mem), 17+fontSize*(8), rowY1); - dc.SetTextForeground(_T("#000000")); - - if (debugger->isAlive()) - { - char dis[256] = {0}; - strcpy(dis, debugger->disasm(address)); - char* dis2 = strchr(dis, '\t'); - char desc[256] = ""; - - if (dis2) - { - *dis2 = 0; - dis2++; - const char* mojs = strstr(dis2, "0x8"); - - if (mojs) - { - for (int k = 0; k < 8; k++) - { - bool found = false; - - for (int j = 0; j < 22; j++) - { - if (mojs[k + 2] == "0123456789ABCDEFabcdef"[j]) - { - found = true; - } - } - - - if (!found) - { - mojs = 0; - break; - } - } - } - - if (mojs) - { - int offs; - sscanf(mojs + 2, "%08x", &offs); - branches[numBranches].src = rowY1 + rowHeight / 2; - branches[numBranches].srcAddr = address / align; - branches[numBranches++].dst = (int)(rowY1 + ((s64)offs - (s64)address) * rowHeight / align + rowHeight / 2); - sprintf(desc, "-->%s", debugger->getDescription(offs).c_str()); - dc.SetTextForeground(_T("#600060")); - } - else - { - dc.SetTextForeground(_T("#000000")); - } - - dc.DrawText(wxString::FromAscii(dis2), 17+fontSize*(8+8+8), rowY1); - } - - if (strcmp(dis, "blr")) - { - dc.SetTextForeground(_T("#007000")); - } - else - { - dc.SetTextForeground(_T("#8000FF")); - } - - dc.DrawText(wxString::FromAscii(dis), 17+fontSize*(8+8), rowY1); - - if (desc[0] == 0) - { - strcpy(desc, debugger->getDescription(address).c_str()); - } - - dc.SetTextForeground(_T("#0000FF")); - - //char temp[256]; - //UnDecorateSymbolName(desc,temp,255,UNDNAME_COMPLETE); - if (strlen(desc)) - { - dc.DrawText(wxString::FromAscii(desc), 17+fontSize*(8+8+8+30), rowY1); - } - - if (debugger->isBreakpoint(address)) - { - dc.SetBrush(bpBrush); - dc.DrawRectangle(2, rowY1, 7, 7); -// DrawIconEx(hdc, 2, rowY1, breakPoint, 32, 32, 0, 0, DI_NORMAL); - } - } - } - - dc.SetPen(currentPen); - /* - for (i = 0; i < numBranches; i++) - { - int x = 250 + (branches[i].srcAddr % 9) * 8; - MoveToEx(hdc, x-2, branches[i].src, 0); - - if (branches[i].dst < rect.bottom + 200 && branches[i].dst > -200) - { - LineTo(hdc, x+2, branches[i].src); - LineTo(hdc, x+2, branches[i].dst); - LineTo(hdc, x-4, branches[i].dst); - - MoveToEx(hdc, x, branches[i].dst - 4,0); - LineTo(hdc, x-4, branches[i].dst); - LineTo(hdc, x+1, branches[i].dst+5); - } - else - { - LineTo(hdc, x+4, branches[i].src); - //MoveToEx(hdc,x+2,branches[i].dst-4,0); - //LineTo(hdc,x+6,branches[i].dst); - //LineTo(hdc,x+1,branches[i].dst+5); - } - //LineTo(hdc,x,branches[i].dst+4); - - //LineTo(hdc,x-2,branches[i].dst); - }*/ -} - - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Debugger.h" +#include "Common.h" + +#include "MemoryView.h" +#include +#include + + +enum +{ + IDM_GOTOINMEMVIEW = 12000, + IDM_COPYADDRESS, + IDM_COPYHEX, + IDM_COPYCODE, + IDM_RUNTOHERE, + IDM_DYNARECRESULTS, +}; + + +BEGIN_EVENT_TABLE(CMemoryView, wxControl) +EVT_ERASE_BACKGROUND(CMemoryView::OnErase) +EVT_PAINT(CMemoryView::OnPaint) +EVT_LEFT_DOWN(CMemoryView::OnMouseDown) +EVT_LEFT_UP(CMemoryView::OnMouseUpL) +EVT_MOTION(CMemoryView::OnMouseMove) +EVT_RIGHT_DOWN(CMemoryView::OnMouseDown) +EVT_RIGHT_UP(CMemoryView::OnMouseUpR) +EVT_MENU(-1, CMemoryView::OnPopupMenu) +END_EVENT_TABLE() + +CMemoryView::CMemoryView(DebugInterface* debuginterface, wxWindow* parent, wxWindowID Id, const wxSize& Size) + : wxControl(parent, Id, wxDefaultPosition, Size), + debugger(debuginterface), + rowHeight(13), + selection(0), + oldSelection(0), + selectionChanged(false), + selecting(false), + hasFocus(false), + showHex(false) +{ + rowHeight = 13; + align = debuginterface->getInstructionSize(0); + curAddress = debuginterface->getPC(); +} + + +wxSize CMemoryView::DoGetBestSize() const +{ + wxSize bestSize; + bestSize.x = 300; + bestSize.y = 300; + return(bestSize); +} + + +int CMemoryView::YToAddress(int y) +{ + wxRect rc = GetClientRect(); + int ydiff = y - rc.height / 2 - rowHeight / 2; + ydiff = (int)(floorf((float)ydiff / (float)rowHeight)) + 1; + return(curAddress + ydiff * align); +} + + +void CMemoryView::OnMouseDown(wxMouseEvent& event) +{ + int x = event.m_x; + int y = event.m_y; + + if (x > 16) + { + oldSelection = selection; + selection = YToAddress(y); + // SetCapture(wnd); + bool oldselecting = selecting; + selecting = true; + + if (!oldselecting || (selection != oldSelection)) + { + redraw(); + } + } + else + { + debugger->toggleBreakpoint(YToAddress(y)); + redraw(); + } + + event.Skip(true); +} + + +void CMemoryView::OnMouseMove(wxMouseEvent& event) +{ + wxRect rc = GetClientRect(); + + if (event.m_leftDown) + { + if (event.m_x > 16) + { + if (event.m_y < 0) + { + curAddress -= align; + redraw(); + } + else if (event.m_y > rc.height) + { + curAddress += align; + redraw(); + } + else + { + OnMouseDown(event); + } + } + } + + event.Skip(true); +} + + +void CMemoryView::OnMouseUpL(wxMouseEvent& event) +{ + if (event.m_x > 16) + { + curAddress = YToAddress(event.m_y); + selecting = false; + //ReleaseCapture(); + redraw(); + } + + event.Skip(true); +} + + +void CMemoryView::OnPopupMenu(wxCommandEvent& event) +{ +#if wxUSE_CLIPBOARD + wxTheClipboard->Open(); +#endif + + switch (event.GetId()) + { + case IDM_GOTOINMEMVIEW: + // CMemoryDlg::Goto(selection); + break; + +#if wxUSE_CLIPBOARD + case IDM_COPYADDRESS: + { + wxTheClipboard->SetData(new wxTextDataObject(wxString::Format(_T("%08x"), selection))); + } + break; + + case IDM_COPYCODE: + wxTheClipboard->SetData(new wxTextDataObject(wxString::FromAscii(debugger->disasm(selection)))); //Have to manually convert from char* to wxString, don't have to in Windows? + break; + + case IDM_COPYHEX: + { + char temp[24]; + sprintf(temp, "%08x", debugger->readMemory(selection)); + wxTheClipboard->SetData(new wxTextDataObject(wxString::FromAscii(temp))); + } + break; +#endif + + case IDM_RUNTOHERE: + { + debugger->setBreakpoint(selection); + debugger->runToBreakpoint(); + redraw(); + } + break; + + case IDM_DYNARECRESULTS: + { +// CDynaViewDlg::ViewAddr(selection); +// CDynaViewDlg::Show(TRUE); +// MessageBox(NULL, "not impl", "CtrlDisAsmView", MB_OK); + } + break; + } + +#if wxUSE_CLIPBOARD + wxTheClipboard->Close(); +#endif + event.Skip(true); +} + + +void CMemoryView::OnMouseUpR(wxMouseEvent& event) +{ + // popup menu + wxMenu menu; + //menu.Append(IDM_GOTOINMEMVIEW, "&Goto in mem view"); +#if wxUSE_CLIPBOARD + menu.Append(IDM_COPYADDRESS, wxString::FromAscii("Copy &address")); + menu.Append(IDM_COPYCODE, wxString::FromAscii("Copy &code")); + menu.Append(IDM_COPYHEX, wxString::FromAscii("Copy &hex")); +#endif + menu.Append(IDM_RUNTOHERE, _T("&Run To Here")); + //menu.Append(IDM_DYNARECRESULTS, "Copy &address"); + PopupMenu(&menu); + event.Skip(true); +} + + +void CMemoryView::OnErase(wxEraseEvent& event) +{} + + +void CMemoryView::OnPaint(wxPaintEvent& event) +{ + wxPaintDC dc(this); + int fontSize = 8; + wxRect rc = GetClientRect(); + wxFont font(fontSize, wxFONTFAMILY_MODERN, wxFONTSTYLE_NORMAL, wxFONTWEIGHT_LIGHT); + dc.SetFont(font); + struct branch + { + int src, dst, srcAddr; + }; + + branch branches[256]; + int numBranches = 0; + // TODO: Add any drawing code here... + int width = rc.width; + int numRows = (rc.height / rowHeight) / 2 + 2; + //numRows=(numRows&(~1)) + 1; + dc.SetBackgroundMode(wxTRANSPARENT); + const wxChar* bgColor = _T("#ffffff"); + wxPen nullPen(bgColor); + wxPen currentPen(_T("#000000")); + wxPen selPen(_T("#808080")); // gray + nullPen.SetStyle(wxTRANSPARENT); + + wxBrush currentBrush(_T("#FFEfE8")); // ligh gray + wxBrush pcBrush(_T("#70FF70")); // green + wxBrush bpBrush(_T("#FF3311")); // red + wxBrush bgBrush(bgColor); + wxBrush nullBrush(bgColor); + nullBrush.SetStyle(wxTRANSPARENT); + + dc.SetPen(nullPen); + dc.SetBrush(bgBrush); + dc.DrawRectangle(0, 0, 16, rc.height); + dc.DrawRectangle(0, 0, rc.width, 5+8); + // TODO - clean up this freaking mess!!!!! + int i; + + for (i = -numRows; i <= numRows; i++) + { + unsigned int address = curAddress + i * align; + + int rowY1 = rc.height / 2 + rowHeight * i - rowHeight / 2; + int rowY2 = rc.height / 2 + rowHeight * i + rowHeight / 2; + + wxString temp = wxString::Format(_T("%08x"), address); + u32 col = debugger->getColor(address); + wxBrush rowBrush(wxColor(col >> 16, col >> 8, col)); + dc.SetBrush(nullBrush); + dc.SetPen(nullPen); + dc.DrawRectangle(0, rowY1, 16, rowY2); + + if (selecting && (address == selection)) + { + dc.SetPen(selPen); + } + else + { + dc.SetPen(i == 0 ? currentPen : nullPen); + } + + if (address == debugger->getPC()) + { + dc.SetBrush(pcBrush); + } + else + { + dc.SetBrush(rowBrush); + } + + dc.DrawRectangle(16, rowY1, width, rowY2 - 1); + dc.SetBrush(currentBrush); + dc.SetTextForeground(_T("#600000")); + dc.DrawText(temp, 17, rowY1); + char mem[256] = {0}; + strcpy(mem, debugger->getRawMemoryString(address)); + dc.SetTextForeground(_T("#000080")); + dc.DrawText(wxString::FromAscii(mem), 17+fontSize*(8), rowY1); + dc.SetTextForeground(_T("#000000")); + + if (debugger->isAlive()) + { + char dis[256] = {0}; + strcpy(dis, debugger->disasm(address)); + char* dis2 = strchr(dis, '\t'); + char desc[256] = ""; + + if (dis2) + { + *dis2 = 0; + dis2++; + const char* mojs = strstr(dis2, "0x8"); + + if (mojs) + { + for (int k = 0; k < 8; k++) + { + bool found = false; + + for (int j = 0; j < 22; j++) + { + if (mojs[k + 2] == "0123456789ABCDEFabcdef"[j]) + { + found = true; + } + } + + + if (!found) + { + mojs = 0; + break; + } + } + } + + if (mojs) + { + int offs; + sscanf(mojs + 2, "%08x", &offs); + branches[numBranches].src = rowY1 + rowHeight / 2; + branches[numBranches].srcAddr = address / align; + branches[numBranches++].dst = (int)(rowY1 + ((s64)offs - (s64)address) * rowHeight / align + rowHeight / 2); + sprintf(desc, "-->%s", debugger->getDescription(offs).c_str()); + dc.SetTextForeground(_T("#600060")); + } + else + { + dc.SetTextForeground(_T("#000000")); + } + + dc.DrawText(wxString::FromAscii(dis2), 17+fontSize*(8+8+8), rowY1); + } + + if (strcmp(dis, "blr")) + { + dc.SetTextForeground(_T("#007000")); + } + else + { + dc.SetTextForeground(_T("#8000FF")); + } + + dc.DrawText(wxString::FromAscii(dis), 17+fontSize*(8+8), rowY1); + + if (desc[0] == 0) + { + strcpy(desc, debugger->getDescription(address).c_str()); + } + + dc.SetTextForeground(_T("#0000FF")); + + //char temp[256]; + //UnDecorateSymbolName(desc,temp,255,UNDNAME_COMPLETE); + if (strlen(desc)) + { + dc.DrawText(wxString::FromAscii(desc), 17+fontSize*(8+8+8+30), rowY1); + } + + if (debugger->isBreakpoint(address)) + { + dc.SetBrush(bpBrush); + dc.DrawRectangle(2, rowY1, 7, 7); +// DrawIconEx(hdc, 2, rowY1, breakPoint, 32, 32, 0, 0, DI_NORMAL); + } + } + } + + dc.SetPen(currentPen); + /* + for (i = 0; i < numBranches; i++) + { + int x = 250 + (branches[i].srcAddr % 9) * 8; + MoveToEx(hdc, x-2, branches[i].src, 0); + + if (branches[i].dst < rect.bottom + 200 && branches[i].dst > -200) + { + LineTo(hdc, x+2, branches[i].src); + LineTo(hdc, x+2, branches[i].dst); + LineTo(hdc, x-4, branches[i].dst); + + MoveToEx(hdc, x, branches[i].dst - 4,0); + LineTo(hdc, x-4, branches[i].dst); + LineTo(hdc, x+1, branches[i].dst+5); + } + else + { + LineTo(hdc, x+4, branches[i].src); + //MoveToEx(hdc,x+2,branches[i].dst-4,0); + //LineTo(hdc,x+6,branches[i].dst); + //LineTo(hdc,x+1,branches[i].dst+5); + } + //LineTo(hdc,x,branches[i].dst+4); + + //LineTo(hdc,x-2,branches[i].dst); + }*/ +} + + diff --git a/Source/Core/DebuggerWX/Src/MemoryWindow.cpp b/Source/Core/DebuggerWX/Src/MemoryWindow.cpp index ccd563ae8b..c406bc1a84 100644 --- a/Source/Core/DebuggerWX/Src/MemoryWindow.cpp +++ b/Source/Core/DebuggerWX/Src/MemoryWindow.cpp @@ -1,221 +1,221 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Debugger.h" - -#include "IniFile.h" - -#include -#include -#include -#include -#include -#include "MemoryWindow.h" -#include "HW/CPU.h" -#include "PowerPC/PowerPC.h" -#include "Host.h" - -#include "Debugger/PPCDebugInterface.h" -#include "PowerPC/SymbolDB.h" - -#include "Core.h" -#include "LogManager.h" - -#include "HW/Memmap.h" - -// ugly that this lib included code from the main -#include "../../DolphinWX/Src/Globals.h" - - -enum -{ - IDM_DEBUG_GO = 350, - IDM_STEP, - IDM_STEPOVER, - IDM_SKIP, - IDM_SETPC, - IDM_GOTOPC, - IDM_ADDRBOX, - IDM_CALLSTACKLIST, - IDM_SYMBOLLIST, - IDM_INTERPRETER, - IDM_DUALCORE, - IDM_LOGWINDOW, - IDM_REGISTERWINDOW, - IDM_BREAKPOINTWINDOW, - IDM_VALBOX, - IDM_SETVALBUTTON -}; - -BEGIN_EVENT_TABLE(CMemoryWindow, wxFrame) - EVT_TEXT(IDM_ADDRBOX, CMemoryWindow::OnAddrBoxChange) - EVT_LISTBOX(IDM_SYMBOLLIST, CMemoryWindow::OnSymbolListChange) - EVT_HOST_COMMAND(wxID_ANY, CMemoryWindow::OnHostMessage) - EVT_BUTTON(IDM_SETVALBUTTON, CMemoryWindow::SetMemoryValue) -END_EVENT_TABLE() - - -CMemoryWindow::CMemoryWindow(wxWindow* parent, wxWindowID id, - const wxString& title, const wxPoint& pos, const wxSize& size, long style) - : wxFrame(parent, id, title, pos, size, style) -{ - wxBoxSizer* sizerBig = new wxBoxSizer(wxHORIZONTAL); - wxBoxSizer* sizerRight = new wxBoxSizer(wxVERTICAL); - // didn't see anything usefull in the left part - //wxBoxSizer* sizerLeft = new wxBoxSizer(wxVERTICAL); - - DebugInterface* di = new PPCDebugInterface(); - - //sizerLeft->Add(symbols = new wxListBox(this, IDM_SYMBOLLIST, wxDefaultPosition, wxSize(20, 100), 0, NULL, wxLB_SORT), 1, wxEXPAND); - memview = new CMemoryView(di, this, wxID_ANY); - //sizerBig->Add(sizerLeft, 1, wxEXPAND); - sizerBig->Add(memview, 20, wxEXPAND); - sizerBig->Add(sizerRight, 0, wxEXPAND | wxALL, 3); - sizerRight->Add(buttonGo = new wxButton(this, IDM_DEBUG_GO, _T("&Go"))); - sizerRight->Add(addrbox = new wxTextCtrl(this, IDM_ADDRBOX, _T(""))); - sizerRight->Add(new wxButton(this, IDM_SETPC, _T("S&et PC"))); - sizerRight->Add(valbox = new wxTextCtrl(this, IDM_VALBOX, _T(""))); - sizerRight->Add(new wxButton(this, IDM_SETVALBUTTON, _T("Set &Value"))); - - SetSizer(sizerBig); - - //sizerLeft->SetSizeHints(this); - //sizerLeft->Fit(this); - sizerRight->SetSizeHints(this); - sizerRight->Fit(this); - sizerBig->SetSizeHints(this); - sizerBig->Fit(this); -} - - -CMemoryWindow::~CMemoryWindow() -{ -} - - -void CMemoryWindow::Save(IniFile& _IniFile) const -{ - // Prevent these bad values that can happen after a crash or hanging - if(GetPosition().x != -32000 && GetPosition().y != -32000) - { - _IniFile.Set("MemoryWindow", "x", GetPosition().x); - _IniFile.Set("MemoryWindow", "y", GetPosition().y); - _IniFile.Set("MemoryWindow", "w", GetSize().GetWidth()); - _IniFile.Set("MemoryWindow", "h", GetSize().GetHeight()); - } -} - - -void CMemoryWindow::Load(IniFile& _IniFile) -{ - int x,y,w,h; - _IniFile.Get("MemoryWindow", "x", &x, GetPosition().x); - _IniFile.Get("MemoryWindow", "y", &y, GetPosition().y); - _IniFile.Get("MemoryWindow", "w", &w, GetSize().GetWidth()); - _IniFile.Get("MemoryWindow", "h", &h, GetSize().GetHeight()); - SetSize(x, y, w, h); -} - - -void CMemoryWindow::JumpToAddress(u32 _Address) -{ - memview->Center(_Address); -} - - -void CMemoryWindow::SetMemoryValue(wxCommandEvent& event) -{ - std::string str_addr = std::string(addrbox->GetValue().mb_str()); - std::string str_val = std::string(valbox->GetValue().mb_str()); - u32 addr; - u32 val; - - if (!TryParseUInt(std::string("0x") + str_addr, &addr)) { - PanicAlert("Invalid Address: %s", str_addr.c_str()); - return; - } - - if (!TryParseUInt(std::string("0x") + str_val, &val)) { - PanicAlert("Invalid Value: %s", str_val.c_str()); - return; - } - - Memory::Write_U32(val, addr); - memview->Refresh(); -} -void CMemoryWindow::OnAddrBoxChange(wxCommandEvent& event) -{ - wxString txt = addrbox->GetValue(); - if (txt.size() == 8) - { - u32 addr; - sscanf(txt.mb_str(), "%08x", &addr); - memview->Center(addr); - } - - event.Skip(1); -} - -void CMemoryWindow::Update() -{ - memview->Refresh(); - memview->Center(PC); -} - -void CMemoryWindow::NotifyMapLoaded() -{ - symbols->Show(false); // hide it for faster filling - symbols->Clear(); - /* -#ifdef _WIN32 - const FunctionDB::XFuncMap &syms = g_symbolDB.Symbols(); - for (FuntionDB::XFuncMap::iterator iter = syms.begin(); iter != syms.end(); ++iter) - { - int idx = symbols->Append(iter->second.name.c_str()); - symbols->SetClientData(idx, (void*)&iter->second); - } - - // -#endif -*/ - symbols->Show(true); - Update(); -} - -void CMemoryWindow::OnSymbolListChange(wxCommandEvent& event) -{ - int index = symbols->GetSelection(); - if (index >= 0) { - Symbol* pSymbol = static_cast(symbols->GetClientData(index)); - if (pSymbol != NULL) - { - memview->Center(pSymbol->address); - } - } -} - -void CMemoryWindow::OnHostMessage(wxCommandEvent& event) -{ - switch (event.GetId()) - { - case IDM_NOTIFYMAPLOADED: - NotifyMapLoaded(); - break; - } -} - - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Debugger.h" + +#include "IniFile.h" + +#include +#include +#include +#include +#include +#include "MemoryWindow.h" +#include "HW/CPU.h" +#include "PowerPC/PowerPC.h" +#include "Host.h" + +#include "Debugger/PPCDebugInterface.h" +#include "PowerPC/SymbolDB.h" + +#include "Core.h" +#include "LogManager.h" + +#include "HW/Memmap.h" + +// ugly that this lib included code from the main +#include "../../DolphinWX/Src/Globals.h" + + +enum +{ + IDM_DEBUG_GO = 350, + IDM_STEP, + IDM_STEPOVER, + IDM_SKIP, + IDM_SETPC, + IDM_GOTOPC, + IDM_ADDRBOX, + IDM_CALLSTACKLIST, + IDM_SYMBOLLIST, + IDM_INTERPRETER, + IDM_DUALCORE, + IDM_LOGWINDOW, + IDM_REGISTERWINDOW, + IDM_BREAKPOINTWINDOW, + IDM_VALBOX, + IDM_SETVALBUTTON +}; + +BEGIN_EVENT_TABLE(CMemoryWindow, wxFrame) + EVT_TEXT(IDM_ADDRBOX, CMemoryWindow::OnAddrBoxChange) + EVT_LISTBOX(IDM_SYMBOLLIST, CMemoryWindow::OnSymbolListChange) + EVT_HOST_COMMAND(wxID_ANY, CMemoryWindow::OnHostMessage) + EVT_BUTTON(IDM_SETVALBUTTON, CMemoryWindow::SetMemoryValue) +END_EVENT_TABLE() + + +CMemoryWindow::CMemoryWindow(wxWindow* parent, wxWindowID id, + const wxString& title, const wxPoint& pos, const wxSize& size, long style) + : wxFrame(parent, id, title, pos, size, style) +{ + wxBoxSizer* sizerBig = new wxBoxSizer(wxHORIZONTAL); + wxBoxSizer* sizerRight = new wxBoxSizer(wxVERTICAL); + // didn't see anything usefull in the left part + //wxBoxSizer* sizerLeft = new wxBoxSizer(wxVERTICAL); + + DebugInterface* di = new PPCDebugInterface(); + + //sizerLeft->Add(symbols = new wxListBox(this, IDM_SYMBOLLIST, wxDefaultPosition, wxSize(20, 100), 0, NULL, wxLB_SORT), 1, wxEXPAND); + memview = new CMemoryView(di, this, wxID_ANY); + //sizerBig->Add(sizerLeft, 1, wxEXPAND); + sizerBig->Add(memview, 20, wxEXPAND); + sizerBig->Add(sizerRight, 0, wxEXPAND | wxALL, 3); + sizerRight->Add(buttonGo = new wxButton(this, IDM_DEBUG_GO, _T("&Go"))); + sizerRight->Add(addrbox = new wxTextCtrl(this, IDM_ADDRBOX, _T(""))); + sizerRight->Add(new wxButton(this, IDM_SETPC, _T("S&et PC"))); + sizerRight->Add(valbox = new wxTextCtrl(this, IDM_VALBOX, _T(""))); + sizerRight->Add(new wxButton(this, IDM_SETVALBUTTON, _T("Set &Value"))); + + SetSizer(sizerBig); + + //sizerLeft->SetSizeHints(this); + //sizerLeft->Fit(this); + sizerRight->SetSizeHints(this); + sizerRight->Fit(this); + sizerBig->SetSizeHints(this); + sizerBig->Fit(this); +} + + +CMemoryWindow::~CMemoryWindow() +{ +} + + +void CMemoryWindow::Save(IniFile& _IniFile) const +{ + // Prevent these bad values that can happen after a crash or hanging + if(GetPosition().x != -32000 && GetPosition().y != -32000) + { + _IniFile.Set("MemoryWindow", "x", GetPosition().x); + _IniFile.Set("MemoryWindow", "y", GetPosition().y); + _IniFile.Set("MemoryWindow", "w", GetSize().GetWidth()); + _IniFile.Set("MemoryWindow", "h", GetSize().GetHeight()); + } +} + + +void CMemoryWindow::Load(IniFile& _IniFile) +{ + int x,y,w,h; + _IniFile.Get("MemoryWindow", "x", &x, GetPosition().x); + _IniFile.Get("MemoryWindow", "y", &y, GetPosition().y); + _IniFile.Get("MemoryWindow", "w", &w, GetSize().GetWidth()); + _IniFile.Get("MemoryWindow", "h", &h, GetSize().GetHeight()); + SetSize(x, y, w, h); +} + + +void CMemoryWindow::JumpToAddress(u32 _Address) +{ + memview->Center(_Address); +} + + +void CMemoryWindow::SetMemoryValue(wxCommandEvent& event) +{ + std::string str_addr = std::string(addrbox->GetValue().mb_str()); + std::string str_val = std::string(valbox->GetValue().mb_str()); + u32 addr; + u32 val; + + if (!TryParseUInt(std::string("0x") + str_addr, &addr)) { + PanicAlert("Invalid Address: %s", str_addr.c_str()); + return; + } + + if (!TryParseUInt(std::string("0x") + str_val, &val)) { + PanicAlert("Invalid Value: %s", str_val.c_str()); + return; + } + + Memory::Write_U32(val, addr); + memview->Refresh(); +} +void CMemoryWindow::OnAddrBoxChange(wxCommandEvent& event) +{ + wxString txt = addrbox->GetValue(); + if (txt.size() == 8) + { + u32 addr; + sscanf(txt.mb_str(), "%08x", &addr); + memview->Center(addr); + } + + event.Skip(1); +} + +void CMemoryWindow::Update() +{ + memview->Refresh(); + memview->Center(PC); +} + +void CMemoryWindow::NotifyMapLoaded() +{ + symbols->Show(false); // hide it for faster filling + symbols->Clear(); + /* +#ifdef _WIN32 + const FunctionDB::XFuncMap &syms = g_symbolDB.Symbols(); + for (FuntionDB::XFuncMap::iterator iter = syms.begin(); iter != syms.end(); ++iter) + { + int idx = symbols->Append(iter->second.name.c_str()); + symbols->SetClientData(idx, (void*)&iter->second); + } + + // +#endif +*/ + symbols->Show(true); + Update(); +} + +void CMemoryWindow::OnSymbolListChange(wxCommandEvent& event) +{ + int index = symbols->GetSelection(); + if (index >= 0) { + Symbol* pSymbol = static_cast(symbols->GetClientData(index)); + if (pSymbol != NULL) + { + memview->Center(pSymbol->address); + } + } +} + +void CMemoryWindow::OnHostMessage(wxCommandEvent& event) +{ + switch (event.GetId()) + { + case IDM_NOTIFYMAPLOADED: + NotifyMapLoaded(); + break; + } +} + + diff --git a/Source/Core/DebuggerWX/Src/RegisterView.cpp b/Source/Core/DebuggerWX/Src/RegisterView.cpp index fb52b52eac..148c69228a 100644 --- a/Source/Core/DebuggerWX/Src/RegisterView.cpp +++ b/Source/Core/DebuggerWX/Src/RegisterView.cpp @@ -1,158 +1,158 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Debugger.h" -#include "RegisterView.h" -#include "PowerPC/PowerPC.h" - -extern const char* GetGRPName(unsigned int index); - - -BEGIN_EVENT_TABLE(CRegisterView, wxListCtrl) -END_EVENT_TABLE() - -CRegisterView::CRegisterView(wxWindow* parent, const wxWindowID id, const wxPoint& pos, const wxSize& size, long style) - : wxListCtrl(parent, id, pos, size, style) -{ - InsertColumn(1, wxT("Reg 16-31"), wxLIST_FORMAT_LEFT, 100); - InsertColumn(0, wxT("Value"), wxLIST_FORMAT_CENTER, 80); - InsertColumn(0, wxT("Reg 0-15"), wxLIST_FORMAT_LEFT, 100); - InsertColumn(0, wxT("Value"), wxLIST_FORMAT_CENTER, 80); - - SetFont(wxFont(9, wxSWISS, wxNORMAL, wxNORMAL, false, wxT("Segoe UI"))); - - for (int i = 0; i < 16; i++) - { - // 0-15 - int Item = InsertItem(0, wxString::FromAscii(GetGRPName(i))); - - // 16-31 - SetItem(Item, 2, wxString::FromAscii(GetGRPName(16 + i))); - - // just for easy sort - - wxListItem item; - item.SetId(Item); - item.SetBackgroundColour(0xFFFFFF); - item.SetData(i); - SetItem(item); - } - - Refresh(); -} - - -void -CRegisterView::Update() -{ - for (size_t i = 0; i < 16; i++) - { - // 0-15 - if (m_CachedRegs[i] != GPR(i)) - { - m_CachedRegHasChanged[i] = true; - } - else - { - m_CachedRegHasChanged[i] = false; - } - - m_CachedRegs[i] = GPR(i); - - // 16-31 - if (m_CachedRegs[16 + i] != GPR(16 + i)) - { - m_CachedRegHasChanged[16 + i] = true; - } - else - { - m_CachedRegHasChanged[16 + i] = false; - } - - m_CachedRegs[16 + i] = GPR(16 + i); - } - - Refresh(); -} - -void CRegisterView::Refresh() -{ - for (size_t i = 0; i < 16; i++) - { - wxListItem item; - item.SetId(i); - item.SetColumn(1); - char temp[16]; - sprintf(temp, "0x%08x",m_CachedRegs[i]); - item.SetText(wxString::FromAscii(temp)); - SetItem(item); - } - for (size_t i = 0; i < 16; i++) - { - wxListItem item; - item.SetId(i); - item.SetColumn(3); - char temp[16]; - sprintf(temp, "0x%08x",m_CachedRegs[16 + i]); - item.SetText(wxString::FromAscii(temp)); - SetItem(item); - } -} -#ifdef _WIN32 -bool -CRegisterView::MSWDrawSubItem(wxPaintDC& rPainDC, int item, int subitem) -{ - bool Result = false; - -#ifdef __WXMSW__ - switch (subitem) - { - case 1: - case 3: - { - int Register = (subitem == 1) ? item : item + 16; - - const wxChar* bgColor = _T("#ffffff"); - wxBrush bgBrush(bgColor); - wxPen bgPen(bgColor); - - wxRect SubItemRect; - this->GetSubItemRect(item, subitem, SubItemRect); - rPainDC.SetBrush(bgBrush); - rPainDC.SetPen(bgPen); - rPainDC.DrawRectangle(SubItemRect); - - if (m_CachedRegHasChanged[Register]) - { - rPainDC.SetTextForeground(_T("#FF0000")); - } - else - { - rPainDC.SetTextForeground(_T("#000000")); - } - - wxString text; - text.Printf(wxT("0x%08x"), m_CachedRegs[Register]); - rPainDC.DrawText(text, SubItemRect.GetLeft() + 10, SubItemRect.GetTop() + 4); - return(true); - } - break; - } -#endif - return(Result); -} -#endif +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Debugger.h" +#include "RegisterView.h" +#include "PowerPC/PowerPC.h" + +extern const char* GetGRPName(unsigned int index); + + +BEGIN_EVENT_TABLE(CRegisterView, wxListCtrl) +END_EVENT_TABLE() + +CRegisterView::CRegisterView(wxWindow* parent, const wxWindowID id, const wxPoint& pos, const wxSize& size, long style) + : wxListCtrl(parent, id, pos, size, style) +{ + InsertColumn(1, wxT("Reg 16-31"), wxLIST_FORMAT_LEFT, 100); + InsertColumn(0, wxT("Value"), wxLIST_FORMAT_CENTER, 80); + InsertColumn(0, wxT("Reg 0-15"), wxLIST_FORMAT_LEFT, 100); + InsertColumn(0, wxT("Value"), wxLIST_FORMAT_CENTER, 80); + + SetFont(wxFont(9, wxSWISS, wxNORMAL, wxNORMAL, false, wxT("Segoe UI"))); + + for (int i = 0; i < 16; i++) + { + // 0-15 + int Item = InsertItem(0, wxString::FromAscii(GetGRPName(i))); + + // 16-31 + SetItem(Item, 2, wxString::FromAscii(GetGRPName(16 + i))); + + // just for easy sort + + wxListItem item; + item.SetId(Item); + item.SetBackgroundColour(0xFFFFFF); + item.SetData(i); + SetItem(item); + } + + Refresh(); +} + + +void +CRegisterView::Update() +{ + for (size_t i = 0; i < 16; i++) + { + // 0-15 + if (m_CachedRegs[i] != GPR(i)) + { + m_CachedRegHasChanged[i] = true; + } + else + { + m_CachedRegHasChanged[i] = false; + } + + m_CachedRegs[i] = GPR(i); + + // 16-31 + if (m_CachedRegs[16 + i] != GPR(16 + i)) + { + m_CachedRegHasChanged[16 + i] = true; + } + else + { + m_CachedRegHasChanged[16 + i] = false; + } + + m_CachedRegs[16 + i] = GPR(16 + i); + } + + Refresh(); +} + +void CRegisterView::Refresh() +{ + for (size_t i = 0; i < 16; i++) + { + wxListItem item; + item.SetId(i); + item.SetColumn(1); + char temp[16]; + sprintf(temp, "0x%08x",m_CachedRegs[i]); + item.SetText(wxString::FromAscii(temp)); + SetItem(item); + } + for (size_t i = 0; i < 16; i++) + { + wxListItem item; + item.SetId(i); + item.SetColumn(3); + char temp[16]; + sprintf(temp, "0x%08x",m_CachedRegs[16 + i]); + item.SetText(wxString::FromAscii(temp)); + SetItem(item); + } +} +#ifdef _WIN32 +bool +CRegisterView::MSWDrawSubItem(wxPaintDC& rPainDC, int item, int subitem) +{ + bool Result = false; + +#ifdef __WXMSW__ + switch (subitem) + { + case 1: + case 3: + { + int Register = (subitem == 1) ? item : item + 16; + + const wxChar* bgColor = _T("#ffffff"); + wxBrush bgBrush(bgColor); + wxPen bgPen(bgColor); + + wxRect SubItemRect; + this->GetSubItemRect(item, subitem, SubItemRect); + rPainDC.SetBrush(bgBrush); + rPainDC.SetPen(bgPen); + rPainDC.DrawRectangle(SubItemRect); + + if (m_CachedRegHasChanged[Register]) + { + rPainDC.SetTextForeground(_T("#FF0000")); + } + else + { + rPainDC.SetTextForeground(_T("#000000")); + } + + wxString text; + text.Printf(wxT("0x%08x"), m_CachedRegs[Register]); + rPainDC.DrawText(text, SubItemRect.GetLeft() + 10, SubItemRect.GetTop() + 4); + return(true); + } + break; + } +#endif + return(Result); +} +#endif diff --git a/Source/Core/DebuggerWX/Src/RegisterWindow.cpp b/Source/Core/DebuggerWX/Src/RegisterWindow.cpp index 1c14a3de6c..32124a377a 100644 --- a/Source/Core/DebuggerWX/Src/RegisterWindow.cpp +++ b/Source/Core/DebuggerWX/Src/RegisterWindow.cpp @@ -1,97 +1,97 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Debugger.h" -#include "RegisterWindow.h" -#include "PowerPC/PowerPC.h" -#include "RegisterView.h" -#include "IniFile.h" - -extern const char* GetGRPName(unsigned int index); - -BEGIN_EVENT_TABLE(CRegisterWindow, wxDialog) - EVT_CLOSE(CRegisterWindow::OnClose) - EVT_MENU(wxID_REFRESH, CRegisterWindow::OnRefresh) -END_EVENT_TABLE() - -CRegisterWindow::CRegisterWindow(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& position, const wxSize& size, long style) - : wxDialog(parent, id, title, position, size, style) - , m_GPRListView(NULL) -{ - CreateGUIControls(); -} - - -CRegisterWindow::~CRegisterWindow() -{} - - -void CRegisterWindow::Save(IniFile& _IniFile) const -{ - _IniFile.Set("RegisterWindow", "x", GetPosition().x); - _IniFile.Set("RegisterWindow", "y", GetPosition().y); - _IniFile.Set("RegisterWindow", "w", GetSize().GetWidth()); - _IniFile.Set("RegisterWindow", "h", GetSize().GetHeight()); -} - - -void CRegisterWindow::Load(IniFile& _IniFile) -{ - int x,y,w,h; - _IniFile.Get("RegisterWindow", "x", &x, GetPosition().x); - _IniFile.Get("RegisterWindow", "y", &y, GetPosition().y); - _IniFile.Get("RegisterWindow", "w", &w, GetSize().GetWidth()); - _IniFile.Get("RegisterWindow", "h", &h, GetSize().GetHeight()); - SetSize(x, y, w, h); -} - - -void CRegisterWindow::CreateGUIControls() -{ - SetTitle(wxT("Registers")); - SetIcon(wxNullIcon); - SetSize(8, 8, 400, 370); - Center(); - - m_GPRListView = new CRegisterView(this, ID_GPR, wxDefaultPosition, GetSize(), - wxLC_REPORT | wxSUNKEN_BORDER | wxLC_ALIGN_LEFT | wxLC_SINGLE_SEL | wxLC_SORT_ASCENDING); - - NotifyUpdate(); -} - - -void CRegisterWindow::OnClose(wxCloseEvent& /*event*/) -{ - Hide(); -} - -void CRegisterWindow::OnRefresh(wxCommandEvent& WXUNUSED (event)) -{ - m_GPRListView->Refresh(); - m_GPRListView->Update(); -} - - -void CRegisterWindow::NotifyUpdate() -{ - if (m_GPRListView != NULL) - { - m_GPRListView->Update(); - } -} - - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Debugger.h" +#include "RegisterWindow.h" +#include "PowerPC/PowerPC.h" +#include "RegisterView.h" +#include "IniFile.h" + +extern const char* GetGRPName(unsigned int index); + +BEGIN_EVENT_TABLE(CRegisterWindow, wxDialog) + EVT_CLOSE(CRegisterWindow::OnClose) + EVT_MENU(wxID_REFRESH, CRegisterWindow::OnRefresh) +END_EVENT_TABLE() + +CRegisterWindow::CRegisterWindow(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& position, const wxSize& size, long style) + : wxDialog(parent, id, title, position, size, style) + , m_GPRListView(NULL) +{ + CreateGUIControls(); +} + + +CRegisterWindow::~CRegisterWindow() +{} + + +void CRegisterWindow::Save(IniFile& _IniFile) const +{ + _IniFile.Set("RegisterWindow", "x", GetPosition().x); + _IniFile.Set("RegisterWindow", "y", GetPosition().y); + _IniFile.Set("RegisterWindow", "w", GetSize().GetWidth()); + _IniFile.Set("RegisterWindow", "h", GetSize().GetHeight()); +} + + +void CRegisterWindow::Load(IniFile& _IniFile) +{ + int x,y,w,h; + _IniFile.Get("RegisterWindow", "x", &x, GetPosition().x); + _IniFile.Get("RegisterWindow", "y", &y, GetPosition().y); + _IniFile.Get("RegisterWindow", "w", &w, GetSize().GetWidth()); + _IniFile.Get("RegisterWindow", "h", &h, GetSize().GetHeight()); + SetSize(x, y, w, h); +} + + +void CRegisterWindow::CreateGUIControls() +{ + SetTitle(wxT("Registers")); + SetIcon(wxNullIcon); + SetSize(8, 8, 400, 370); + Center(); + + m_GPRListView = new CRegisterView(this, ID_GPR, wxDefaultPosition, GetSize(), + wxLC_REPORT | wxSUNKEN_BORDER | wxLC_ALIGN_LEFT | wxLC_SINGLE_SEL | wxLC_SORT_ASCENDING); + + NotifyUpdate(); +} + + +void CRegisterWindow::OnClose(wxCloseEvent& /*event*/) +{ + Hide(); +} + +void CRegisterWindow::OnRefresh(wxCommandEvent& WXUNUSED (event)) +{ + m_GPRListView->Refresh(); + m_GPRListView->Update(); +} + + +void CRegisterWindow::NotifyUpdate() +{ + if (m_GPRListView != NULL) + { + m_GPRListView->Update(); + } +} + + diff --git a/Source/Core/DiscIO/Src/BannerLoader.cpp b/Source/Core/DiscIO/Src/BannerLoader.cpp index a0ec090973..60cee599b0 100644 --- a/Source/Core/DiscIO/Src/BannerLoader.cpp +++ b/Source/Core/DiscIO/Src/BannerLoader.cpp @@ -1,103 +1,103 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ -#include "stdafx.h" - -#include "BannerLoader.h" -#include "BannerLoaderWii.h" -#include "BannerLoaderGC.h" - -#include "VolumeCreator.h" -#include "FileUtil.h" - -namespace DiscIO -{ -bool IBannerLoader::CopyToStringAndCheck(std::string& _rDestination, const char* _src) -{ - static bool bValidChars[256]; - static bool bInitialized = false; - - if (!bInitialized) - { - for (int i = 0; i < 256; i++) - { - bValidChars[i] = false; - } - - // generate valid chars - for (unsigned char c = 0x20; c <= 0x80; c++) - { - bValidChars[c] = true; - } - - bValidChars[0x0a] = true; - bValidChars[0xa9] = true; - bValidChars[0xe9] = true; - - bInitialized = true; - } - - bool bResult = true; - char destBuffer[2048]; - char* dest = destBuffer; - const char* src = _src; - - // copy the string and check for "unknown" characters - while (*src != 0x00) - { - u8 c = *src; - - if (c == 0x0a){c = 0x20;} - - if (bValidChars[c] == false) - { - bResult = false; - break; - } - - *dest = c; - dest++; - src++; - } - - // finalize the string - if (bResult) - { - *dest = 0x00; - } - else - { - dest[0] = ' '; - dest[1] = 0x00; - } - - _rDestination = destBuffer; - - return(bResult); -} - - -IBannerLoader* CreateBannerLoader(DiscIO::IFileSystem& _rFileSystem) -{ - if (IsVolumeWiiDisc(_rFileSystem.GetVolume())) - { - return(new CBannerLoaderWii(_rFileSystem)); - } - - return(new CBannerLoaderGC(_rFileSystem)); -} -} // namespace - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +#include "stdafx.h" + +#include "BannerLoader.h" +#include "BannerLoaderWii.h" +#include "BannerLoaderGC.h" + +#include "VolumeCreator.h" +#include "FileUtil.h" + +namespace DiscIO +{ +bool IBannerLoader::CopyToStringAndCheck(std::string& _rDestination, const char* _src) +{ + static bool bValidChars[256]; + static bool bInitialized = false; + + if (!bInitialized) + { + for (int i = 0; i < 256; i++) + { + bValidChars[i] = false; + } + + // generate valid chars + for (unsigned char c = 0x20; c <= 0x80; c++) + { + bValidChars[c] = true; + } + + bValidChars[0x0a] = true; + bValidChars[0xa9] = true; + bValidChars[0xe9] = true; + + bInitialized = true; + } + + bool bResult = true; + char destBuffer[2048]; + char* dest = destBuffer; + const char* src = _src; + + // copy the string and check for "unknown" characters + while (*src != 0x00) + { + u8 c = *src; + + if (c == 0x0a){c = 0x20;} + + if (bValidChars[c] == false) + { + bResult = false; + break; + } + + *dest = c; + dest++; + src++; + } + + // finalize the string + if (bResult) + { + *dest = 0x00; + } + else + { + dest[0] = ' '; + dest[1] = 0x00; + } + + _rDestination = destBuffer; + + return(bResult); +} + + +IBannerLoader* CreateBannerLoader(DiscIO::IFileSystem& _rFileSystem) +{ + if (IsVolumeWiiDisc(_rFileSystem.GetVolume())) + { + return(new CBannerLoaderWii(_rFileSystem)); + } + + return(new CBannerLoaderGC(_rFileSystem)); +} +} // namespace + diff --git a/Source/Core/DiscIO/Src/BannerLoaderGC.cpp b/Source/Core/DiscIO/Src/BannerLoaderGC.cpp index 9e897c22d8..2480739979 100644 --- a/Source/Core/DiscIO/Src/BannerLoaderGC.cpp +++ b/Source/Core/DiscIO/Src/BannerLoaderGC.cpp @@ -1,193 +1,193 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "stdafx.h" - -#include "BannerLoaderGC.h" - -namespace DiscIO -{ -CBannerLoaderGC::CBannerLoaderGC(DiscIO::IFileSystem& _rFileSystem) - : m_pBannerFile(NULL), - m_IsValid(false) -{ - // build LUT Table - for (int i = 0; i < 8; i++) - { - lut3to8[i] = (i * 255) / 7; - } - - for (int i = 0; i < 16; i++) - { - lut4to8[i] = (i * 255) / 15; - } - - for (int i = 0; i < 32; i++) - { - lut5to8[i] = (i * 255) / 31; - } - - // load the opening.bnr - size_t FileSize = _rFileSystem.GetFileSize("opening.bnr"); - - if (FileSize > 0) - { - m_pBannerFile = new u8[FileSize]; - _rFileSystem.ReadFile("opening.bnr", m_pBannerFile, FileSize); - m_IsValid = true; - } -} - - -CBannerLoaderGC::~CBannerLoaderGC() -{ - delete [] m_pBannerFile; - m_pBannerFile = NULL; -} - - -bool -CBannerLoaderGC::IsValid() -{ - return(m_IsValid); -} - - -bool -CBannerLoaderGC::GetBanner(u32* _pBannerImage) -{ - if (!IsValid()) - { - return(false); - } - - DVDBanner2* pBanner = (DVDBanner2*)m_pBannerFile; - decode5A3image(_pBannerImage, pBanner->image, DVD_BANNER_WIDTH, DVD_BANNER_HEIGHT); - - return(true); -} - - -bool -CBannerLoaderGC::GetName(std::string& _rName, int language) -{ - _rName = "invalid image"; - - if (!IsValid()) - { - return(false); - } - - DVDBanner2* pBanner = (DVDBanner2*)m_pBannerFile; - - if (!CopyToStringAndCheck(_rName, language != 0 ? pBanner->comment[0].shortTitle : pBanner->comment[0].longTitle)) - { - return(false); - } - - return(true); -} - - -bool -CBannerLoaderGC::GetCompany(std::string& _rCompany) -{ - _rCompany = "invalid images"; - - if (!IsValid()) - { - return(false); - } - - DVDBanner2* pBanner = (DVDBanner2*)m_pBannerFile; - - if (!CopyToStringAndCheck(_rCompany, pBanner->comment[0].shortMaker)) - { - return(false); - } - - return(true); -} - - -bool -CBannerLoaderGC::GetDescription(std::string& _rDescription) -{ - _rDescription = "invalid images"; - - if (!IsValid()) - { - return(false); - } - - DVDBanner2* pBanner = (DVDBanner2*)m_pBannerFile; - - if (!CopyToStringAndCheck(_rDescription, pBanner->comment[0].comment)) - { - _rDescription = ""; - } - - return(true); -} - - -u32 -CBannerLoaderGC::decode5A3(u16 val) -{ - u32 bannerBGColor = 0x00000000; - - int r, g, b, a; - - if ((val & 0x8000)) - { - r = lut5to8[(val >> 10) & 0x1f]; - g = lut5to8[(val >> 5) & 0x1f]; - b = lut5to8[(val) & 0x1f]; - a = 0xFF; - } - else - { - a = lut3to8[(val >> 12) & 0x7]; - r = (lut4to8[(val >> 8) & 0xf] * a + (bannerBGColor & 0xFF) * (255 - a)) / 255; - g = (lut4to8[(val >> 4) & 0xf] * a + ((bannerBGColor >> 8) & 0xFF) * (255 - a)) / 255; - b = (lut4to8[(val) & 0xf] * a + ((bannerBGColor >> 16) & 0xFF) * (255 - a)) / 255; - a = 0xFF; - } - - return((a << 24) | (r << 16) | (g << 8) | b); -} - - -void -CBannerLoaderGC::decode5A3image(u32* dst, u16* src, int width, int height) -{ - for (int y = 0; y < height; y += 4) - { - for (int x = 0; x < width; x += 4) - { - for (int iy = 0; iy < 4; iy++, src += 4) - { - for (int ix = 0; ix < 4; ix++) - { - u32 RGBA = decode5A3(Common::swap16(src[ix])); - dst[(y + iy) * width + (x + ix)] = RGBA; - } - } - } - } -} -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "stdafx.h" + +#include "BannerLoaderGC.h" + +namespace DiscIO +{ +CBannerLoaderGC::CBannerLoaderGC(DiscIO::IFileSystem& _rFileSystem) + : m_pBannerFile(NULL), + m_IsValid(false) +{ + // build LUT Table + for (int i = 0; i < 8; i++) + { + lut3to8[i] = (i * 255) / 7; + } + + for (int i = 0; i < 16; i++) + { + lut4to8[i] = (i * 255) / 15; + } + + for (int i = 0; i < 32; i++) + { + lut5to8[i] = (i * 255) / 31; + } + + // load the opening.bnr + size_t FileSize = _rFileSystem.GetFileSize("opening.bnr"); + + if (FileSize > 0) + { + m_pBannerFile = new u8[FileSize]; + _rFileSystem.ReadFile("opening.bnr", m_pBannerFile, FileSize); + m_IsValid = true; + } +} + + +CBannerLoaderGC::~CBannerLoaderGC() +{ + delete [] m_pBannerFile; + m_pBannerFile = NULL; +} + + +bool +CBannerLoaderGC::IsValid() +{ + return(m_IsValid); +} + + +bool +CBannerLoaderGC::GetBanner(u32* _pBannerImage) +{ + if (!IsValid()) + { + return(false); + } + + DVDBanner2* pBanner = (DVDBanner2*)m_pBannerFile; + decode5A3image(_pBannerImage, pBanner->image, DVD_BANNER_WIDTH, DVD_BANNER_HEIGHT); + + return(true); +} + + +bool +CBannerLoaderGC::GetName(std::string& _rName, int language) +{ + _rName = "invalid image"; + + if (!IsValid()) + { + return(false); + } + + DVDBanner2* pBanner = (DVDBanner2*)m_pBannerFile; + + if (!CopyToStringAndCheck(_rName, language != 0 ? pBanner->comment[0].shortTitle : pBanner->comment[0].longTitle)) + { + return(false); + } + + return(true); +} + + +bool +CBannerLoaderGC::GetCompany(std::string& _rCompany) +{ + _rCompany = "invalid images"; + + if (!IsValid()) + { + return(false); + } + + DVDBanner2* pBanner = (DVDBanner2*)m_pBannerFile; + + if (!CopyToStringAndCheck(_rCompany, pBanner->comment[0].shortMaker)) + { + return(false); + } + + return(true); +} + + +bool +CBannerLoaderGC::GetDescription(std::string& _rDescription) +{ + _rDescription = "invalid images"; + + if (!IsValid()) + { + return(false); + } + + DVDBanner2* pBanner = (DVDBanner2*)m_pBannerFile; + + if (!CopyToStringAndCheck(_rDescription, pBanner->comment[0].comment)) + { + _rDescription = ""; + } + + return(true); +} + + +u32 +CBannerLoaderGC::decode5A3(u16 val) +{ + u32 bannerBGColor = 0x00000000; + + int r, g, b, a; + + if ((val & 0x8000)) + { + r = lut5to8[(val >> 10) & 0x1f]; + g = lut5to8[(val >> 5) & 0x1f]; + b = lut5to8[(val) & 0x1f]; + a = 0xFF; + } + else + { + a = lut3to8[(val >> 12) & 0x7]; + r = (lut4to8[(val >> 8) & 0xf] * a + (bannerBGColor & 0xFF) * (255 - a)) / 255; + g = (lut4to8[(val >> 4) & 0xf] * a + ((bannerBGColor >> 8) & 0xFF) * (255 - a)) / 255; + b = (lut4to8[(val) & 0xf] * a + ((bannerBGColor >> 16) & 0xFF) * (255 - a)) / 255; + a = 0xFF; + } + + return((a << 24) | (r << 16) | (g << 8) | b); +} + + +void +CBannerLoaderGC::decode5A3image(u32* dst, u16* src, int width, int height) +{ + for (int y = 0; y < height; y += 4) + { + for (int x = 0; x < width; x += 4) + { + for (int iy = 0; iy < 4; iy++, src += 4) + { + for (int ix = 0; ix < 4; ix++) + { + u32 RGBA = decode5A3(Common::swap16(src[ix])); + dst[(y + iy) * width + (x + ix)] = RGBA; + } + } + } + } +} +} // namespace diff --git a/Source/Core/DiscIO/Src/BannerLoaderWii.cpp b/Source/Core/DiscIO/Src/BannerLoaderWii.cpp index 204c1fe771..e9048fc41d 100644 --- a/Source/Core/DiscIO/Src/BannerLoaderWii.cpp +++ b/Source/Core/DiscIO/Src/BannerLoaderWii.cpp @@ -1,224 +1,224 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "stdafx.h" - -#include - -#include "Common.h" -#include "BannerLoaderWii.h" -#include "FileUtil.h" - -namespace DiscIO -{ -CBannerLoaderWii::CBannerLoaderWii(DiscIO::IFileSystem& _rFileSystem) - : m_pBannerFile(NULL) - , m_IsValid(false) -{ - InitLUTTable(); - - char Filename[260]; - char TitleID[4]; - - _rFileSystem.GetVolume()->Read(0, 4, (u8*)TitleID); - sprintf(Filename, FULL_WII_USER_DIR "title/00010000/%02x%02x%02x%02x/data/banner.bin", (u8)TitleID[0], (u8)TitleID[1], (u8)TitleID[2], (u8)TitleID[3]); - - // load the opening.bnr - size_t FileSize = File::GetSize(Filename); - - if (FileSize > 0) - { - m_pBannerFile = new u8[FileSize]; - FILE* pFile = fopen(Filename, "rb"); - if (pFile != NULL) - { - fread(m_pBannerFile, FileSize, 1, pFile); - fclose(pFile); - m_IsValid = true; - } - } -} - -CBannerLoaderWii::~CBannerLoaderWii() -{ - if (m_pBannerFile) - { - delete [] m_pBannerFile; - m_pBannerFile = NULL; - } -} - - -bool -CBannerLoaderWii::IsValid() -{ - return (m_IsValid); -} - - -bool -CBannerLoaderWii::GetBanner(u32* _pBannerImage) -{ - if (!IsValid()) - { - return false; - } - - SWiiBanner* pBanner = (SWiiBanner*)m_pBannerFile; - - static u32 Buffer[192 * 64]; - decode5A3image(Buffer, (u16*)pBanner->m_BannerTexture, 192, 64); - - // ugly scaling :) - for (int y=0; y<32; y++) - { - for (int x=0; x<96; x++) - { - _pBannerImage[y*96+x] = Buffer[(y*192*2)+(x*2)]; - } - } - - - return true; -} - -std::string -CBannerLoaderWii::StupidWideCharToString(u16* _pSrc, size_t _max) -{ - std::string temp; - - u32 offset = 0; - while (_pSrc[offset] != 0x0000) - { - temp += (char)(_pSrc[offset] >> 8); - offset ++; - - if (offset >= _max) - break; - } - - return temp; -} - -bool -CBannerLoaderWii::GetName(std::string& _rName, int language) -{ - if (IsValid()) - { - SWiiBanner* pBanner = (SWiiBanner*)m_pBannerFile; - - // very stupid - _rName = StupidWideCharToString(pBanner->m_Comment[0], WII_BANNER_COMMENT_SIZE); - return true; - } - - return true; -} - - -bool -CBannerLoaderWii::GetCompany(std::string& _rCompany) -{ - _rCompany = "N/A"; - return true; -} - - -bool -CBannerLoaderWii::GetDescription(std::string& _rDescription) -{ - if (IsValid()) - { - SWiiBanner* pBanner = (SWiiBanner*)m_pBannerFile; - - // very stupid - _rDescription = StupidWideCharToString(pBanner->m_Comment[1], WII_BANNER_COMMENT_SIZE); - return true; - } - - return false; -} - - -void -CBannerLoaderWii::InitLUTTable() -{ - // build LUT Table - for (int i = 0; i < 8; i++) - { - lut3to8[i] = (i * 255) / 7; - } - - for (int i = 0; i < 16; i++) - { - lut4to8[i] = (i * 255) / 15; - } - - for (int i = 0; i < 32; i++) - { - lut5to8[i] = (i * 255) / 31; - } -} - -u32 -CBannerLoaderWii::decode5A3(u16 val) -{ - u32 bannerBGColor = 0x00000000; - - int r, g, b, a; - - if ((val & 0x8000)) - { - r = lut5to8[(val >> 10) & 0x1f]; - g = lut5to8[(val >> 5) & 0x1f]; - b = lut5to8[(val) & 0x1f]; - a = 0xFF; - } - else - { - a = lut3to8[(val >> 12) & 0x7]; - r = (lut4to8[(val >> 8) & 0xf] * a + (bannerBGColor & 0xFF) * (255 - a)) / 255; - g = (lut4to8[(val >> 4) & 0xf] * a + ((bannerBGColor >> 8) & 0xFF) * (255 - a)) / 255; - b = (lut4to8[(val) & 0xf] * a + ((bannerBGColor >> 16) & 0xFF) * (255 - a)) / 255; - a = 0xFF; - } - - return ((a << 24) | (r << 16) | (g << 8) | b); -} - - -void -CBannerLoaderWii::decode5A3image(u32* dst, u16* src, int width, int height) -{ - for (int y = 0; y < height; y += 4) - { - for (int x = 0; x < width; x += 4) - { - for (int iy = 0; iy < 4; iy++, src += 4) - { - for (int ix = 0; ix < 4; ix++) - { - u32 RGBA = decode5A3(Common::swap16(src[ix])); - dst[(y + iy) * width + (x + ix)] = RGBA; - } - } - } - } -} - -} // namespace - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "stdafx.h" + +#include + +#include "Common.h" +#include "BannerLoaderWii.h" +#include "FileUtil.h" + +namespace DiscIO +{ +CBannerLoaderWii::CBannerLoaderWii(DiscIO::IFileSystem& _rFileSystem) + : m_pBannerFile(NULL) + , m_IsValid(false) +{ + InitLUTTable(); + + char Filename[260]; + char TitleID[4]; + + _rFileSystem.GetVolume()->Read(0, 4, (u8*)TitleID); + sprintf(Filename, FULL_WII_USER_DIR "title/00010000/%02x%02x%02x%02x/data/banner.bin", (u8)TitleID[0], (u8)TitleID[1], (u8)TitleID[2], (u8)TitleID[3]); + + // load the opening.bnr + size_t FileSize = File::GetSize(Filename); + + if (FileSize > 0) + { + m_pBannerFile = new u8[FileSize]; + FILE* pFile = fopen(Filename, "rb"); + if (pFile != NULL) + { + fread(m_pBannerFile, FileSize, 1, pFile); + fclose(pFile); + m_IsValid = true; + } + } +} + +CBannerLoaderWii::~CBannerLoaderWii() +{ + if (m_pBannerFile) + { + delete [] m_pBannerFile; + m_pBannerFile = NULL; + } +} + + +bool +CBannerLoaderWii::IsValid() +{ + return (m_IsValid); +} + + +bool +CBannerLoaderWii::GetBanner(u32* _pBannerImage) +{ + if (!IsValid()) + { + return false; + } + + SWiiBanner* pBanner = (SWiiBanner*)m_pBannerFile; + + static u32 Buffer[192 * 64]; + decode5A3image(Buffer, (u16*)pBanner->m_BannerTexture, 192, 64); + + // ugly scaling :) + for (int y=0; y<32; y++) + { + for (int x=0; x<96; x++) + { + _pBannerImage[y*96+x] = Buffer[(y*192*2)+(x*2)]; + } + } + + + return true; +} + +std::string +CBannerLoaderWii::StupidWideCharToString(u16* _pSrc, size_t _max) +{ + std::string temp; + + u32 offset = 0; + while (_pSrc[offset] != 0x0000) + { + temp += (char)(_pSrc[offset] >> 8); + offset ++; + + if (offset >= _max) + break; + } + + return temp; +} + +bool +CBannerLoaderWii::GetName(std::string& _rName, int language) +{ + if (IsValid()) + { + SWiiBanner* pBanner = (SWiiBanner*)m_pBannerFile; + + // very stupid + _rName = StupidWideCharToString(pBanner->m_Comment[0], WII_BANNER_COMMENT_SIZE); + return true; + } + + return true; +} + + +bool +CBannerLoaderWii::GetCompany(std::string& _rCompany) +{ + _rCompany = "N/A"; + return true; +} + + +bool +CBannerLoaderWii::GetDescription(std::string& _rDescription) +{ + if (IsValid()) + { + SWiiBanner* pBanner = (SWiiBanner*)m_pBannerFile; + + // very stupid + _rDescription = StupidWideCharToString(pBanner->m_Comment[1], WII_BANNER_COMMENT_SIZE); + return true; + } + + return false; +} + + +void +CBannerLoaderWii::InitLUTTable() +{ + // build LUT Table + for (int i = 0; i < 8; i++) + { + lut3to8[i] = (i * 255) / 7; + } + + for (int i = 0; i < 16; i++) + { + lut4to8[i] = (i * 255) / 15; + } + + for (int i = 0; i < 32; i++) + { + lut5to8[i] = (i * 255) / 31; + } +} + +u32 +CBannerLoaderWii::decode5A3(u16 val) +{ + u32 bannerBGColor = 0x00000000; + + int r, g, b, a; + + if ((val & 0x8000)) + { + r = lut5to8[(val >> 10) & 0x1f]; + g = lut5to8[(val >> 5) & 0x1f]; + b = lut5to8[(val) & 0x1f]; + a = 0xFF; + } + else + { + a = lut3to8[(val >> 12) & 0x7]; + r = (lut4to8[(val >> 8) & 0xf] * a + (bannerBGColor & 0xFF) * (255 - a)) / 255; + g = (lut4to8[(val >> 4) & 0xf] * a + ((bannerBGColor >> 8) & 0xFF) * (255 - a)) / 255; + b = (lut4to8[(val) & 0xf] * a + ((bannerBGColor >> 16) & 0xFF) * (255 - a)) / 255; + a = 0xFF; + } + + return ((a << 24) | (r << 16) | (g << 8) | b); +} + + +void +CBannerLoaderWii::decode5A3image(u32* dst, u16* src, int width, int height) +{ + for (int y = 0; y < height; y += 4) + { + for (int x = 0; x < width; x += 4) + { + for (int iy = 0; iy < 4; iy++, src += 4) + { + for (int ix = 0; ix < 4; ix++) + { + u32 RGBA = decode5A3(Common::swap16(src[ix])); + dst[(y + iy) * width + (x + ix)] = RGBA; + } + } + } + } +} + +} // namespace + diff --git a/Source/Core/DiscIO/Src/Blob.cpp b/Source/Core/DiscIO/Src/Blob.cpp index f26c53ef90..c592c61c33 100644 --- a/Source/Core/DiscIO/Src/Blob.cpp +++ b/Source/Core/DiscIO/Src/Blob.cpp @@ -1,110 +1,110 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "FileUtil.h" -#include "Blob.h" -#include "CompressedBlob.h" -#include "FileBlob.h" -#include "DriveBlob.h" -#include "MappedFile.h" - -namespace DiscIO -{ - -// Provides caching and split-operation-to-block-operations facilities. -// Used for compressed blob reading and direct drive reading. - -void SectorReader::SetSectorSize(int blocksize) -{ - for (int i = 0; i < CACHE_SIZE; i++) - { - cache[i] = new u8[blocksize]; - cache_tags[i] = (u64)(s64) - 1; - } - m_blocksize = blocksize; -} - -SectorReader::~SectorReader() { - for (int i = 0; i < CACHE_SIZE; i++) - delete[] cache[i]; -} - -const u8 *SectorReader::GetBlockData(u64 block_num) -{ - if (cache_tags[0] == block_num) - { - return cache[0]; - } - else - { - GetBlock(block_num, cache[0]); - cache_tags[0] = block_num; - return cache[0]; - } -} - -bool SectorReader::Read(u64 offset, u64 size, u8* out_ptr) -{ - u64 startingBlock = offset / m_blocksize; - u64 remain = size; - - int positionInBlock = (int)(offset % m_blocksize); - u64 block = startingBlock; - - while (remain > 0) - { - const u8* data = GetBlockData(block); - if (!data) - return false; - - u32 toCopy = m_blocksize - positionInBlock; - if (toCopy >= remain) - { - // yay, we are done! - memcpy(out_ptr, data + positionInBlock, (size_t)remain); - return true; - } - else - { - memcpy(out_ptr, data + positionInBlock, toCopy); - out_ptr += toCopy; - remain -= toCopy; - positionInBlock = 0; - block++; - } - } - return true; -} - - -IBlobReader* CreateBlobReader(const char* filename) -{ - //if (strlen(filename) < 4 && filename[1] == ':') // Drive, for sure. - // return DriveReader::Create(filename); - - if (!File::Exists(filename)) - return 0; - - if (IsCompressedBlob(filename)) - return CompressedBlobReader::Create(filename); - - // Still here? Assume plain file. - return PlainFileReader::Create(filename); -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "FileUtil.h" +#include "Blob.h" +#include "CompressedBlob.h" +#include "FileBlob.h" +#include "DriveBlob.h" +#include "MappedFile.h" + +namespace DiscIO +{ + +// Provides caching and split-operation-to-block-operations facilities. +// Used for compressed blob reading and direct drive reading. + +void SectorReader::SetSectorSize(int blocksize) +{ + for (int i = 0; i < CACHE_SIZE; i++) + { + cache[i] = new u8[blocksize]; + cache_tags[i] = (u64)(s64) - 1; + } + m_blocksize = blocksize; +} + +SectorReader::~SectorReader() { + for (int i = 0; i < CACHE_SIZE; i++) + delete[] cache[i]; +} + +const u8 *SectorReader::GetBlockData(u64 block_num) +{ + if (cache_tags[0] == block_num) + { + return cache[0]; + } + else + { + GetBlock(block_num, cache[0]); + cache_tags[0] = block_num; + return cache[0]; + } +} + +bool SectorReader::Read(u64 offset, u64 size, u8* out_ptr) +{ + u64 startingBlock = offset / m_blocksize; + u64 remain = size; + + int positionInBlock = (int)(offset % m_blocksize); + u64 block = startingBlock; + + while (remain > 0) + { + const u8* data = GetBlockData(block); + if (!data) + return false; + + u32 toCopy = m_blocksize - positionInBlock; + if (toCopy >= remain) + { + // yay, we are done! + memcpy(out_ptr, data + positionInBlock, (size_t)remain); + return true; + } + else + { + memcpy(out_ptr, data + positionInBlock, toCopy); + out_ptr += toCopy; + remain -= toCopy; + positionInBlock = 0; + block++; + } + } + return true; +} + + +IBlobReader* CreateBlobReader(const char* filename) +{ + //if (strlen(filename) < 4 && filename[1] == ':') // Drive, for sure. + // return DriveReader::Create(filename); + + if (!File::Exists(filename)) + return 0; + + if (IsCompressedBlob(filename)) + return CompressedBlobReader::Create(filename); + + // Still here? Assume plain file. + return PlainFileReader::Create(filename); +} + +} // namespace diff --git a/Source/Core/DiscIO/Src/CompressedBlob.cpp b/Source/Core/DiscIO/Src/CompressedBlob.cpp index 370c8b8ede..f424b2c465 100644 --- a/Source/Core/DiscIO/Src/CompressedBlob.cpp +++ b/Source/Core/DiscIO/Src/CompressedBlob.cpp @@ -1,342 +1,342 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "stdafx.h" - -#ifdef _WIN32 -#include -#include -#else -#include -#endif - -#include "Common.h" -#include "CompressedBlob.h" -#include "FileUtil.h" -#include "Hash.h" - -#ifdef _WIN32 -#include "../../../../Externals/zlib/zlib.h" -#else -// TODO: Include generic zlib.h -#include "../../../../Externals/zlib/zlib.h" -#endif - -namespace DiscIO -{ - -CompressedBlobReader::CompressedBlobReader(const char *filename) -{ - file = fopen(filename, "rb"); - fseek(file, 0, SEEK_END); - file_size = ftell(file); - fseek(file, 0, SEEK_SET); - fread(&header, sizeof(CompressedBlobHeader), 1, file); - - SetSectorSize(header.block_size); - - // cache block pointers and hashes - block_pointers = new u64[header.num_blocks]; - fread(block_pointers, sizeof(u64), header.num_blocks, file); - hashes = new u32[header.num_blocks]; - fread(hashes, sizeof(u32), header.num_blocks, file); - - data_offset = (sizeof(CompressedBlobHeader)) - + (sizeof(u64)) * header.num_blocks // skip block pointers - + (sizeof(u32)) * header.num_blocks; // skip hashes - - // A compressed block is never ever longer than a decompressed block, so just header.block_size should be fine. - // I still add some safety margin. - zlib_buffer_size = header.block_size + 64; - zlib_buffer = new u8[zlib_buffer_size]; - memset(zlib_buffer, 0, zlib_buffer_size); -} - -CompressedBlobReader* CompressedBlobReader::Create(const char* filename) -{ - if (IsCompressedBlob(filename)) - return new CompressedBlobReader(filename); - else - return 0; -} - -CompressedBlobReader::~CompressedBlobReader() -{ - delete [] zlib_buffer; - delete [] block_pointers; - delete [] hashes; - fclose(file); - file = 0; -} - -// IMPORTANT: Calling this function invalidates all earlier pointers gotten from this function. -u64 CompressedBlobReader::GetBlockCompressedSize(u64 block_num) const -{ - u64 start = block_pointers[block_num]; - if (block_num < header.num_blocks - 1) - return block_pointers[block_num + 1] - start; - else if (block_num == header.num_blocks - 1) - return header.compressed_data_size - start; - else - PanicAlert("GetBlockCompressedSize - illegal block number %i", (int)block_num); - return 0; -} - -void CompressedBlobReader::GetBlock(u64 block_num, u8 *out_ptr) -{ - bool uncompressed = false; - u32 comp_block_size = (u32)GetBlockCompressedSize(block_num); - u64 offset = block_pointers[block_num] + data_offset; - - if (offset & (1ULL << 63)) - { - if (comp_block_size != header.block_size) - PanicAlert("Uncompressed block with wrong size"); - uncompressed = true; - offset &= ~(1ULL << 63); - } - - // clear unused part of zlib buffer. maybe this can be deleted when it works fully. - memset(zlib_buffer + comp_block_size, 0, zlib_buffer_size - comp_block_size); - - fseek(file, offset, SEEK_SET); - fread(zlib_buffer, 1, comp_block_size, file); - - u8* source = zlib_buffer; - u8* dest = out_ptr; - - // First, check hash. - u32 block_hash = HashAdler32(source, comp_block_size); - if (block_hash != hashes[block_num]) - PanicAlert("Hash of block %i is %08x instead of %08x. Your ISO is corrupt.", - block_num, block_hash, hashes[block_num]); - - if (uncompressed) - { - memcpy(dest, source, comp_block_size); - } - else - { - z_stream z; - memset(&z, 0, sizeof(z)); - z.next_in = source; - z.avail_in = comp_block_size; - if (z.avail_in > header.block_size) - { - PanicAlert("We have a problem"); - } - z.next_out = dest; - z.avail_out = header.block_size; - inflateInit(&z); - int status = inflate(&z, Z_FULL_FLUSH); - u32 uncomp_size = header.block_size - z.avail_out; - if (status != Z_STREAM_END) - { - // this seem to fire wrongly from time to time - // to be sure, don't use compressed isos :P - PanicAlert("Failure reading block %i - out of data and not at end.", block_num); - } - inflateEnd(&z); - if (uncomp_size != header.block_size) - PanicAlert("Wrong block size"); - } -} - -bool CompressFileToBlob(const char* infile, const char* outfile, u32 sub_type, - int block_size, CompressCB callback, void* arg) -{ - if (File::GetSize(infile) > 2000000000ULL) { - PanicAlert("Sorry - compressing Wii games not yet supported."); - return false; - } - - if (IsCompressedBlob(infile)) - { - PanicAlert("%s is already compressed! Cannot compress it further.", infile); - return false; - } - - FILE* inf = fopen(infile, "rb"); - if (!inf) - return false; - - FILE* f = fopen(outfile, "wb"); - if (!f) - return false; - - callback("Files opened, ready to compress.", 0, arg); - - fseek(inf, 0, SEEK_END); - s64 insize = ftell(inf); - fseek(inf, 0, SEEK_SET); - CompressedBlobHeader header; - header.magic_cookie = kBlobCookie; - header.sub_type = sub_type; - header.block_size = block_size; - header.data_size = insize; - - // round upwards! - header.num_blocks = (u32)((header.data_size + (block_size - 1)) / block_size); - - u64* offsets = new u64[header.num_blocks]; - u32* hashes = new u32[header.num_blocks]; - u8* out_buf = new u8[block_size]; - u8* in_buf = new u8[block_size]; - - // seek past the header (we will write it at the end) - fseek(f, sizeof(CompressedBlobHeader), SEEK_CUR); - // seek past the offset and hash tables (we will write them at the end) - fseek(f, (sizeof(u64) + sizeof(u32)) * header.num_blocks, SEEK_CUR); - - // Now we are ready to write compressed data! - u64 position = 0; - int num_compressed = 0; - int num_stored = 0; - for (u32 i = 0; i < header.num_blocks; i++) - { - if (i % (header.num_blocks / 1000) == 0) - { - u64 inpos = ftell(inf); - int ratio = 0; - if (inpos != 0) - ratio = (int)(100 * position / inpos); - char temp[512]; - sprintf(temp, "%i of %i blocks. compression ratio %i%%", i, header.num_blocks, ratio); - callback(temp, (float)i / (float)header.num_blocks, arg); - } - - offsets[i] = position; - // u64 start = i * header.block_size; - // u64 size = header.block_size; - memset(in_buf, 0, header.block_size); - fread(in_buf, header.block_size, 1, inf); - z_stream z; - memset(&z, 0, sizeof(z)); - z.zalloc = Z_NULL; - z.zfree = Z_NULL; - z.opaque = Z_NULL; - z.next_in = in_buf; - z.avail_in = header.block_size; - z.next_out = out_buf; - z.avail_out = block_size; - int retval = deflateInit(&z, 9); - - if (retval != Z_OK) - { - PanicAlert("Deflate failed"); - goto cleanup; - } - - int status = deflate(&z, Z_FINISH); - int comp_size = block_size - z.avail_out; - if ((status != Z_STREAM_END) || (z.avail_out < 10)) - { - //PanicAlert("%i %i Store %i", i*block_size, position, comp_size); - // let's store uncompressed - offsets[i] |= 0x8000000000000000ULL; - fwrite(in_buf, block_size, 1, f); - hashes[i] = HashAdler32(in_buf, block_size); - position += block_size; - num_stored++; - } - else - { - // let's store compressed - //PanicAlert("Comp %i to %i", block_size, comp_size); - fwrite(out_buf, comp_size, 1, f); - hashes[i] = HashAdler32(out_buf, comp_size); - position += comp_size; - num_compressed++; - } - - deflateEnd(&z); - } - - header.compressed_data_size = position; - - // Okay, go back and fill in headers - fseek(f, 0, SEEK_SET); - fwrite(&header, sizeof(header), 1, f); - fwrite(offsets, sizeof(u64), header.num_blocks, f); - fwrite(hashes, sizeof(u32), header.num_blocks, f); - -cleanup: - // Cleanup - delete[] in_buf; - delete[] out_buf; - delete[] offsets; - fclose(f); - fclose(inf); - callback("Done compressing disc image.", 1.0f, arg); - return true; -} - -bool DecompressBlobToFile(const char* infile, const char* outfile, CompressCB callback, void* arg) -{ - if (!IsCompressedBlob(infile)) - { - PanicAlert("File not compressed"); - return false; - } - - CompressedBlobReader* reader = CompressedBlobReader::Create(infile); - if (!reader) return false; - - FILE* f = fopen(outfile, "wb"); - const CompressedBlobHeader &header = reader->GetHeader(); - u8* buffer = new u8[header.block_size]; - - for (u64 i = 0; i < header.num_blocks; i++) - { - if (i % (header.num_blocks / 100) == 0) - { - callback("Unpacking", (float)i / (float)header.num_blocks, arg); - } - reader->Read(i * header.block_size, header.block_size, buffer); - fwrite(buffer, header.block_size, 1, f); - } - - delete[] buffer; - -#ifdef _WIN32 - // ector: _chsize sucks, not 64-bit safe - // F|RES: changed to _chsize_s. i think it is 64-bit safe - _chsize_s(_fileno(f), (long)header.data_size); -#else - ftruncate(fileno(f), header.data_size); -#endif - fclose(f); - - delete reader; - - return true; -} - -bool IsCompressedBlob(const char* filename) -{ - FILE* f = fopen(filename, "rb"); - - if (!f) - return false; - - CompressedBlobHeader header; - fread(&header, sizeof(header), 1, f); - fclose(f); - return header.magic_cookie == kBlobCookie; -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "stdafx.h" + +#ifdef _WIN32 +#include +#include +#else +#include +#endif + +#include "Common.h" +#include "CompressedBlob.h" +#include "FileUtil.h" +#include "Hash.h" + +#ifdef _WIN32 +#include "../../../../Externals/zlib/zlib.h" +#else +// TODO: Include generic zlib.h +#include "../../../../Externals/zlib/zlib.h" +#endif + +namespace DiscIO +{ + +CompressedBlobReader::CompressedBlobReader(const char *filename) +{ + file = fopen(filename, "rb"); + fseek(file, 0, SEEK_END); + file_size = ftell(file); + fseek(file, 0, SEEK_SET); + fread(&header, sizeof(CompressedBlobHeader), 1, file); + + SetSectorSize(header.block_size); + + // cache block pointers and hashes + block_pointers = new u64[header.num_blocks]; + fread(block_pointers, sizeof(u64), header.num_blocks, file); + hashes = new u32[header.num_blocks]; + fread(hashes, sizeof(u32), header.num_blocks, file); + + data_offset = (sizeof(CompressedBlobHeader)) + + (sizeof(u64)) * header.num_blocks // skip block pointers + + (sizeof(u32)) * header.num_blocks; // skip hashes + + // A compressed block is never ever longer than a decompressed block, so just header.block_size should be fine. + // I still add some safety margin. + zlib_buffer_size = header.block_size + 64; + zlib_buffer = new u8[zlib_buffer_size]; + memset(zlib_buffer, 0, zlib_buffer_size); +} + +CompressedBlobReader* CompressedBlobReader::Create(const char* filename) +{ + if (IsCompressedBlob(filename)) + return new CompressedBlobReader(filename); + else + return 0; +} + +CompressedBlobReader::~CompressedBlobReader() +{ + delete [] zlib_buffer; + delete [] block_pointers; + delete [] hashes; + fclose(file); + file = 0; +} + +// IMPORTANT: Calling this function invalidates all earlier pointers gotten from this function. +u64 CompressedBlobReader::GetBlockCompressedSize(u64 block_num) const +{ + u64 start = block_pointers[block_num]; + if (block_num < header.num_blocks - 1) + return block_pointers[block_num + 1] - start; + else if (block_num == header.num_blocks - 1) + return header.compressed_data_size - start; + else + PanicAlert("GetBlockCompressedSize - illegal block number %i", (int)block_num); + return 0; +} + +void CompressedBlobReader::GetBlock(u64 block_num, u8 *out_ptr) +{ + bool uncompressed = false; + u32 comp_block_size = (u32)GetBlockCompressedSize(block_num); + u64 offset = block_pointers[block_num] + data_offset; + + if (offset & (1ULL << 63)) + { + if (comp_block_size != header.block_size) + PanicAlert("Uncompressed block with wrong size"); + uncompressed = true; + offset &= ~(1ULL << 63); + } + + // clear unused part of zlib buffer. maybe this can be deleted when it works fully. + memset(zlib_buffer + comp_block_size, 0, zlib_buffer_size - comp_block_size); + + fseek(file, offset, SEEK_SET); + fread(zlib_buffer, 1, comp_block_size, file); + + u8* source = zlib_buffer; + u8* dest = out_ptr; + + // First, check hash. + u32 block_hash = HashAdler32(source, comp_block_size); + if (block_hash != hashes[block_num]) + PanicAlert("Hash of block %i is %08x instead of %08x. Your ISO is corrupt.", + block_num, block_hash, hashes[block_num]); + + if (uncompressed) + { + memcpy(dest, source, comp_block_size); + } + else + { + z_stream z; + memset(&z, 0, sizeof(z)); + z.next_in = source; + z.avail_in = comp_block_size; + if (z.avail_in > header.block_size) + { + PanicAlert("We have a problem"); + } + z.next_out = dest; + z.avail_out = header.block_size; + inflateInit(&z); + int status = inflate(&z, Z_FULL_FLUSH); + u32 uncomp_size = header.block_size - z.avail_out; + if (status != Z_STREAM_END) + { + // this seem to fire wrongly from time to time + // to be sure, don't use compressed isos :P + PanicAlert("Failure reading block %i - out of data and not at end.", block_num); + } + inflateEnd(&z); + if (uncomp_size != header.block_size) + PanicAlert("Wrong block size"); + } +} + +bool CompressFileToBlob(const char* infile, const char* outfile, u32 sub_type, + int block_size, CompressCB callback, void* arg) +{ + if (File::GetSize(infile) > 2000000000ULL) { + PanicAlert("Sorry - compressing Wii games not yet supported."); + return false; + } + + if (IsCompressedBlob(infile)) + { + PanicAlert("%s is already compressed! Cannot compress it further.", infile); + return false; + } + + FILE* inf = fopen(infile, "rb"); + if (!inf) + return false; + + FILE* f = fopen(outfile, "wb"); + if (!f) + return false; + + callback("Files opened, ready to compress.", 0, arg); + + fseek(inf, 0, SEEK_END); + s64 insize = ftell(inf); + fseek(inf, 0, SEEK_SET); + CompressedBlobHeader header; + header.magic_cookie = kBlobCookie; + header.sub_type = sub_type; + header.block_size = block_size; + header.data_size = insize; + + // round upwards! + header.num_blocks = (u32)((header.data_size + (block_size - 1)) / block_size); + + u64* offsets = new u64[header.num_blocks]; + u32* hashes = new u32[header.num_blocks]; + u8* out_buf = new u8[block_size]; + u8* in_buf = new u8[block_size]; + + // seek past the header (we will write it at the end) + fseek(f, sizeof(CompressedBlobHeader), SEEK_CUR); + // seek past the offset and hash tables (we will write them at the end) + fseek(f, (sizeof(u64) + sizeof(u32)) * header.num_blocks, SEEK_CUR); + + // Now we are ready to write compressed data! + u64 position = 0; + int num_compressed = 0; + int num_stored = 0; + for (u32 i = 0; i < header.num_blocks; i++) + { + if (i % (header.num_blocks / 1000) == 0) + { + u64 inpos = ftell(inf); + int ratio = 0; + if (inpos != 0) + ratio = (int)(100 * position / inpos); + char temp[512]; + sprintf(temp, "%i of %i blocks. compression ratio %i%%", i, header.num_blocks, ratio); + callback(temp, (float)i / (float)header.num_blocks, arg); + } + + offsets[i] = position; + // u64 start = i * header.block_size; + // u64 size = header.block_size; + memset(in_buf, 0, header.block_size); + fread(in_buf, header.block_size, 1, inf); + z_stream z; + memset(&z, 0, sizeof(z)); + z.zalloc = Z_NULL; + z.zfree = Z_NULL; + z.opaque = Z_NULL; + z.next_in = in_buf; + z.avail_in = header.block_size; + z.next_out = out_buf; + z.avail_out = block_size; + int retval = deflateInit(&z, 9); + + if (retval != Z_OK) + { + PanicAlert("Deflate failed"); + goto cleanup; + } + + int status = deflate(&z, Z_FINISH); + int comp_size = block_size - z.avail_out; + if ((status != Z_STREAM_END) || (z.avail_out < 10)) + { + //PanicAlert("%i %i Store %i", i*block_size, position, comp_size); + // let's store uncompressed + offsets[i] |= 0x8000000000000000ULL; + fwrite(in_buf, block_size, 1, f); + hashes[i] = HashAdler32(in_buf, block_size); + position += block_size; + num_stored++; + } + else + { + // let's store compressed + //PanicAlert("Comp %i to %i", block_size, comp_size); + fwrite(out_buf, comp_size, 1, f); + hashes[i] = HashAdler32(out_buf, comp_size); + position += comp_size; + num_compressed++; + } + + deflateEnd(&z); + } + + header.compressed_data_size = position; + + // Okay, go back and fill in headers + fseek(f, 0, SEEK_SET); + fwrite(&header, sizeof(header), 1, f); + fwrite(offsets, sizeof(u64), header.num_blocks, f); + fwrite(hashes, sizeof(u32), header.num_blocks, f); + +cleanup: + // Cleanup + delete[] in_buf; + delete[] out_buf; + delete[] offsets; + fclose(f); + fclose(inf); + callback("Done compressing disc image.", 1.0f, arg); + return true; +} + +bool DecompressBlobToFile(const char* infile, const char* outfile, CompressCB callback, void* arg) +{ + if (!IsCompressedBlob(infile)) + { + PanicAlert("File not compressed"); + return false; + } + + CompressedBlobReader* reader = CompressedBlobReader::Create(infile); + if (!reader) return false; + + FILE* f = fopen(outfile, "wb"); + const CompressedBlobHeader &header = reader->GetHeader(); + u8* buffer = new u8[header.block_size]; + + for (u64 i = 0; i < header.num_blocks; i++) + { + if (i % (header.num_blocks / 100) == 0) + { + callback("Unpacking", (float)i / (float)header.num_blocks, arg); + } + reader->Read(i * header.block_size, header.block_size, buffer); + fwrite(buffer, header.block_size, 1, f); + } + + delete[] buffer; + +#ifdef _WIN32 + // ector: _chsize sucks, not 64-bit safe + // F|RES: changed to _chsize_s. i think it is 64-bit safe + _chsize_s(_fileno(f), (long)header.data_size); +#else + ftruncate(fileno(f), header.data_size); +#endif + fclose(f); + + delete reader; + + return true; +} + +bool IsCompressedBlob(const char* filename) +{ + FILE* f = fopen(filename, "rb"); + + if (!f) + return false; + + CompressedBlobHeader header; + fread(&header, sizeof(header), 1, f); + fclose(f); + return header.magic_cookie == kBlobCookie; +} + +} // namespace diff --git a/Source/Core/DiscIO/Src/DriveBlob.cpp b/Source/Core/DiscIO/Src/DriveBlob.cpp index aec48d14d0..11f99faada 100644 --- a/Source/Core/DiscIO/Src/DriveBlob.cpp +++ b/Source/Core/DiscIO/Src/DriveBlob.cpp @@ -1,25 +1,25 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "stdafx.h" - -#include "DriveBlob.h" - -namespace DiscIO -{ - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "stdafx.h" + +#include "DriveBlob.h" + +namespace DiscIO +{ + +} // namespace diff --git a/Source/Core/DiscIO/Src/FileBlob.cpp b/Source/Core/DiscIO/Src/FileBlob.cpp index 87de28a8da..d3465140ae 100644 --- a/Source/Core/DiscIO/Src/FileBlob.cpp +++ b/Source/Core/DiscIO/Src/FileBlob.cpp @@ -1,110 +1,110 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "stdafx.h" - -#include "Blob.h" -#include "FileBlob.h" - -#ifdef _WIN32 -#include -#endif - -namespace DiscIO -{ - -#ifdef _WIN32 - -PlainFileReader::PlainFileReader(HANDLE hFile_) -{ - hFile = hFile_; - DWORD size_low, size_high; - size_low = GetFileSize(hFile, &size_high); - size = ((u64)size_low) | ((u64)size_high << 32); -} - -PlainFileReader* PlainFileReader::Create(const char* filename) -{ - HANDLE hFile = CreateFile( - filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, - FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL); - if (hFile != INVALID_HANDLE_VALUE) - return new PlainFileReader(hFile); - else - return 0; -} - -PlainFileReader::~PlainFileReader() -{ - CloseHandle(hFile); -} - -bool PlainFileReader::Read(u64 offset, u64 nbytes, u8* out_ptr) -{ - LONG offset_high = (LONG)(offset >> 32); - SetFilePointer(hFile, (DWORD)(offset & 0xFFFFFFFF), &offset_high, FILE_BEGIN); - - if (nbytes >= 0x100000000ULL) - return false; // WTF, does windows really have this limitation? - - DWORD unused = 0; - if (!ReadFile(hFile, out_ptr, DWORD(nbytes & 0xFFFFFFFF), &unused, NULL)) - return false; - else - return true; -} - -#else // POSIX - -PlainFileReader::PlainFileReader(FILE* file__) -{ - file_ = file__; - #if 0 - fseek64(file_, 0, SEEK_END); - #else - fseek(file_, 0, SEEK_END); // I don't have fseek64 with gcc 4.3 - #endif - size = ftell(file_); - fseek(file_, 0, SEEK_SET); -} - -PlainFileReader* PlainFileReader::Create(const char* filename) -{ - FILE* file_ = fopen(filename, "rb"); - if (file_) - return new PlainFileReader(file_); - else - return 0; -} - -PlainFileReader::~PlainFileReader() -{ - fclose(file_); -} - -bool PlainFileReader::Read(u64 offset, u64 nbytes, u8* out_ptr) -{ - int seekStatus = fseek(file_, offset, SEEK_SET); - if (seekStatus != 0) - return false; - size_t bytesRead = fread(out_ptr, 1, nbytes, file_); - return bytesRead == nbytes; -} - -#endif - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "stdafx.h" + +#include "Blob.h" +#include "FileBlob.h" + +#ifdef _WIN32 +#include +#endif + +namespace DiscIO +{ + +#ifdef _WIN32 + +PlainFileReader::PlainFileReader(HANDLE hFile_) +{ + hFile = hFile_; + DWORD size_low, size_high; + size_low = GetFileSize(hFile, &size_high); + size = ((u64)size_low) | ((u64)size_high << 32); +} + +PlainFileReader* PlainFileReader::Create(const char* filename) +{ + HANDLE hFile = CreateFile( + filename, GENERIC_READ, FILE_SHARE_READ, 0, OPEN_EXISTING, + FILE_ATTRIBUTE_NORMAL | FILE_FLAG_RANDOM_ACCESS, NULL); + if (hFile != INVALID_HANDLE_VALUE) + return new PlainFileReader(hFile); + else + return 0; +} + +PlainFileReader::~PlainFileReader() +{ + CloseHandle(hFile); +} + +bool PlainFileReader::Read(u64 offset, u64 nbytes, u8* out_ptr) +{ + LONG offset_high = (LONG)(offset >> 32); + SetFilePointer(hFile, (DWORD)(offset & 0xFFFFFFFF), &offset_high, FILE_BEGIN); + + if (nbytes >= 0x100000000ULL) + return false; // WTF, does windows really have this limitation? + + DWORD unused = 0; + if (!ReadFile(hFile, out_ptr, DWORD(nbytes & 0xFFFFFFFF), &unused, NULL)) + return false; + else + return true; +} + +#else // POSIX + +PlainFileReader::PlainFileReader(FILE* file__) +{ + file_ = file__; + #if 0 + fseek64(file_, 0, SEEK_END); + #else + fseek(file_, 0, SEEK_END); // I don't have fseek64 with gcc 4.3 + #endif + size = ftell(file_); + fseek(file_, 0, SEEK_SET); +} + +PlainFileReader* PlainFileReader::Create(const char* filename) +{ + FILE* file_ = fopen(filename, "rb"); + if (file_) + return new PlainFileReader(file_); + else + return 0; +} + +PlainFileReader::~PlainFileReader() +{ + fclose(file_); +} + +bool PlainFileReader::Read(u64 offset, u64 nbytes, u8* out_ptr) +{ + int seekStatus = fseek(file_, offset, SEEK_SET); + if (seekStatus != 0) + return false; + size_t bytesRead = fread(out_ptr, 1, nbytes, file_); + return bytesRead == nbytes; +} + +#endif + +} // namespace diff --git a/Source/Core/DiscIO/Src/FileHandlerARC.cpp b/Source/Core/DiscIO/Src/FileHandlerARC.cpp index 17c7270efb..e9a7b96044 100644 --- a/Source/Core/DiscIO/Src/FileHandlerARC.cpp +++ b/Source/Core/DiscIO/Src/FileHandlerARC.cpp @@ -1,237 +1,237 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "stdafx.h" -#include "Common.h" - -#include "FileHandlerARC.h" -#include "StringUtil.h" - - -#define ARC_ID 0x55aa382d - -namespace DiscIO -{ -CARCFile::CARCFile(const u8* _pBuffer, size_t _BufferSize) - : m_pBuffer(NULL), - m_Initialized(false) -{ - m_pBuffer = new u8[_BufferSize]; - - if (m_pBuffer) - { - memcpy(m_pBuffer, _pBuffer, _BufferSize); - m_Initialized = ParseBuffer(); - } -} - - -CARCFile::~CARCFile() -{ - delete [] m_pBuffer; -} - - -bool -CARCFile::IsInitialized() -{ - return(m_Initialized); -} - - -size_t -CARCFile::GetFileSize(const std::string& _rFullPath) -{ - if (!m_Initialized) - { - return(0); - } - - const SFileInfo* pFileInfo = FindFileInfo(_rFullPath); - - if (pFileInfo != NULL) - { - return(pFileInfo->m_FileSize); - } - - return(0); -} - - -size_t -CARCFile::ReadFile(const std::string& _rFullPath, u8* _pBuffer, size_t _MaxBufferSize) -{ - if (!m_Initialized) - { - return(0); - } - - const SFileInfo* pFileInfo = FindFileInfo(_rFullPath); - - if (pFileInfo == NULL) - { - return(0); - } - - if (pFileInfo->m_FileSize > _MaxBufferSize) - { - return(0); - } - - memcpy(_pBuffer, &m_pBuffer[pFileInfo->m_Offset], pFileInfo->m_FileSize); - return(pFileInfo->m_FileSize); -} - - -bool -CARCFile::ExportFile(const std::string& _rFullPath, const std::string& _rExportFilename) -{ - if (!m_Initialized) - { - return(false); - } - - const SFileInfo* pFileInfo = FindFileInfo(_rFullPath); - - if (pFileInfo == NULL) - { - return(false); - } - - FILE* pFile = fopen(_rExportFilename.c_str(), "wb"); - - if (pFile == NULL) - { - return(false); - } - - fwrite(&m_pBuffer[pFileInfo->m_Offset], pFileInfo->m_FileSize, 1, pFile); - fclose(pFile); - return(true); -} - - -bool -CARCFile::ExportAllFiles(const std::string& _rFullPath) -{ - return(false); -} - - -bool -CARCFile::ParseBuffer() -{ - // check ID - u32 ID = Common::swap32(*(u32*)(m_pBuffer)); - - if (ID != ARC_ID) - { - return(false); - } - - // read header - u32 FSTOffset = Common::swap32(*(u32*)(m_pBuffer + 0x4)); - //u32 FSTSize = Common::swap32(*(u32*)(m_pBuffer + 0x8)); - //u32 FileOffset = Common::swap32(*(u32*)(m_pBuffer + 0xC)); - - // read all file infos - SFileInfo Root; - Root.m_NameOffset = Common::swap32(*(u32*)(m_pBuffer + FSTOffset + 0x0)); - Root.m_Offset = Common::swap32(*(u32*)(m_pBuffer + FSTOffset + 0x4)); - Root.m_FileSize = Common::swap32(*(u32*)(m_pBuffer + FSTOffset + 0x8)); - - if (Root.IsDirectory()) - { - m_FileInfoVector.resize(Root.m_FileSize); - const char* szNameTable = (char*)(m_pBuffer + FSTOffset); - - for (size_t i = 0; i < m_FileInfoVector.size(); i++) - { - u8* Offset = m_pBuffer + FSTOffset + (i * 0xC); - m_FileInfoVector[i].m_NameOffset = Common::swap32(*(u32*)(Offset + 0x0)); - m_FileInfoVector[i].m_Offset = Common::swap32(*(u32*)(Offset + 0x4)); - m_FileInfoVector[i].m_FileSize = Common::swap32(*(u32*)(Offset + 0x8)); - - szNameTable += 0xC; - } - - BuildFilenames(1, m_FileInfoVector.size(), NULL, szNameTable); - } - - return(true); -} - - -size_t -CARCFile::BuildFilenames(const size_t _FirstIndex, const size_t _LastIndex, const char* _szDirectory, const char* _szNameTable) -{ - size_t CurrentIndex = _FirstIndex; - - while (CurrentIndex < _LastIndex) - { - SFileInfo& rFileInfo = m_FileInfoVector[CurrentIndex]; - int uOffset = rFileInfo.m_NameOffset & 0xFFFFFF; - - // check next index - if (rFileInfo.IsDirectory()) - { - // this is a directory, build up the new szDirectory - if (_szDirectory != NULL) - { - sprintf(rFileInfo.m_FullPath, "%s%s\\", _szDirectory, &_szNameTable[uOffset]); - } - else - { - sprintf(rFileInfo.m_FullPath, "%s\\", &_szNameTable[uOffset]); - } - - CurrentIndex = BuildFilenames(CurrentIndex + 1, rFileInfo.m_FileSize, rFileInfo.m_FullPath, _szNameTable); - } - else - { - // this is a filename - if (_szDirectory != NULL) - { - sprintf(rFileInfo.m_FullPath, "%s%s", _szDirectory, &_szNameTable[uOffset]); - } - else - { - sprintf(rFileInfo.m_FullPath, "%s", &_szNameTable[uOffset]); - } - - CurrentIndex++; - } - } - - return(CurrentIndex); -} - - -const SFileInfo* -CARCFile::FindFileInfo(std::string _rFullPath) const -{ - for (size_t i = 0; i < m_FileInfoVector.size(); i++) - { - if (!strcasecmp(m_FileInfoVector[i].m_FullPath, _rFullPath.c_str())) - { - return(&m_FileInfoVector[i]); - } - } - - return(NULL); -} -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "stdafx.h" +#include "Common.h" + +#include "FileHandlerARC.h" +#include "StringUtil.h" + + +#define ARC_ID 0x55aa382d + +namespace DiscIO +{ +CARCFile::CARCFile(const u8* _pBuffer, size_t _BufferSize) + : m_pBuffer(NULL), + m_Initialized(false) +{ + m_pBuffer = new u8[_BufferSize]; + + if (m_pBuffer) + { + memcpy(m_pBuffer, _pBuffer, _BufferSize); + m_Initialized = ParseBuffer(); + } +} + + +CARCFile::~CARCFile() +{ + delete [] m_pBuffer; +} + + +bool +CARCFile::IsInitialized() +{ + return(m_Initialized); +} + + +size_t +CARCFile::GetFileSize(const std::string& _rFullPath) +{ + if (!m_Initialized) + { + return(0); + } + + const SFileInfo* pFileInfo = FindFileInfo(_rFullPath); + + if (pFileInfo != NULL) + { + return(pFileInfo->m_FileSize); + } + + return(0); +} + + +size_t +CARCFile::ReadFile(const std::string& _rFullPath, u8* _pBuffer, size_t _MaxBufferSize) +{ + if (!m_Initialized) + { + return(0); + } + + const SFileInfo* pFileInfo = FindFileInfo(_rFullPath); + + if (pFileInfo == NULL) + { + return(0); + } + + if (pFileInfo->m_FileSize > _MaxBufferSize) + { + return(0); + } + + memcpy(_pBuffer, &m_pBuffer[pFileInfo->m_Offset], pFileInfo->m_FileSize); + return(pFileInfo->m_FileSize); +} + + +bool +CARCFile::ExportFile(const std::string& _rFullPath, const std::string& _rExportFilename) +{ + if (!m_Initialized) + { + return(false); + } + + const SFileInfo* pFileInfo = FindFileInfo(_rFullPath); + + if (pFileInfo == NULL) + { + return(false); + } + + FILE* pFile = fopen(_rExportFilename.c_str(), "wb"); + + if (pFile == NULL) + { + return(false); + } + + fwrite(&m_pBuffer[pFileInfo->m_Offset], pFileInfo->m_FileSize, 1, pFile); + fclose(pFile); + return(true); +} + + +bool +CARCFile::ExportAllFiles(const std::string& _rFullPath) +{ + return(false); +} + + +bool +CARCFile::ParseBuffer() +{ + // check ID + u32 ID = Common::swap32(*(u32*)(m_pBuffer)); + + if (ID != ARC_ID) + { + return(false); + } + + // read header + u32 FSTOffset = Common::swap32(*(u32*)(m_pBuffer + 0x4)); + //u32 FSTSize = Common::swap32(*(u32*)(m_pBuffer + 0x8)); + //u32 FileOffset = Common::swap32(*(u32*)(m_pBuffer + 0xC)); + + // read all file infos + SFileInfo Root; + Root.m_NameOffset = Common::swap32(*(u32*)(m_pBuffer + FSTOffset + 0x0)); + Root.m_Offset = Common::swap32(*(u32*)(m_pBuffer + FSTOffset + 0x4)); + Root.m_FileSize = Common::swap32(*(u32*)(m_pBuffer + FSTOffset + 0x8)); + + if (Root.IsDirectory()) + { + m_FileInfoVector.resize(Root.m_FileSize); + const char* szNameTable = (char*)(m_pBuffer + FSTOffset); + + for (size_t i = 0; i < m_FileInfoVector.size(); i++) + { + u8* Offset = m_pBuffer + FSTOffset + (i * 0xC); + m_FileInfoVector[i].m_NameOffset = Common::swap32(*(u32*)(Offset + 0x0)); + m_FileInfoVector[i].m_Offset = Common::swap32(*(u32*)(Offset + 0x4)); + m_FileInfoVector[i].m_FileSize = Common::swap32(*(u32*)(Offset + 0x8)); + + szNameTable += 0xC; + } + + BuildFilenames(1, m_FileInfoVector.size(), NULL, szNameTable); + } + + return(true); +} + + +size_t +CARCFile::BuildFilenames(const size_t _FirstIndex, const size_t _LastIndex, const char* _szDirectory, const char* _szNameTable) +{ + size_t CurrentIndex = _FirstIndex; + + while (CurrentIndex < _LastIndex) + { + SFileInfo& rFileInfo = m_FileInfoVector[CurrentIndex]; + int uOffset = rFileInfo.m_NameOffset & 0xFFFFFF; + + // check next index + if (rFileInfo.IsDirectory()) + { + // this is a directory, build up the new szDirectory + if (_szDirectory != NULL) + { + sprintf(rFileInfo.m_FullPath, "%s%s\\", _szDirectory, &_szNameTable[uOffset]); + } + else + { + sprintf(rFileInfo.m_FullPath, "%s\\", &_szNameTable[uOffset]); + } + + CurrentIndex = BuildFilenames(CurrentIndex + 1, rFileInfo.m_FileSize, rFileInfo.m_FullPath, _szNameTable); + } + else + { + // this is a filename + if (_szDirectory != NULL) + { + sprintf(rFileInfo.m_FullPath, "%s%s", _szDirectory, &_szNameTable[uOffset]); + } + else + { + sprintf(rFileInfo.m_FullPath, "%s", &_szNameTable[uOffset]); + } + + CurrentIndex++; + } + } + + return(CurrentIndex); +} + + +const SFileInfo* +CARCFile::FindFileInfo(std::string _rFullPath) const +{ + for (size_t i = 0; i < m_FileInfoVector.size(); i++) + { + if (!strcasecmp(m_FileInfoVector[i].m_FullPath, _rFullPath.c_str())) + { + return(&m_FileInfoVector[i]); + } + } + + return(NULL); +} +} // namespace diff --git a/Source/Core/DiscIO/Src/FileSystemGCWii.cpp b/Source/Core/DiscIO/Src/FileSystemGCWii.cpp index a06b55836e..13376e447c 100644 --- a/Source/Core/DiscIO/Src/FileSystemGCWii.cpp +++ b/Source/Core/DiscIO/Src/FileSystemGCWii.cpp @@ -1,249 +1,249 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "stdafx.h" -#include "Common.h" - -#include - -#include "FileSystemGCWii.h" -#include "StringUtil.h" - -namespace DiscIO -{ -CFileSystemGCWii::CFileSystemGCWii(const IVolume *_rVolume) - : IFileSystem(_rVolume), - m_Initialized(false), - m_OffsetShift(0) -{ - m_Initialized = InitFileSystem(); -} - - -CFileSystemGCWii::~CFileSystemGCWii() -{ -} - -bool CFileSystemGCWii::IsInitialized() const -{ - return m_Initialized; -} - -u64 CFileSystemGCWii::GetFileSize(const char* _rFullPath) const -{ - if (!m_Initialized) - return 0; - - const SFileInfo* pFileInfo = FindFileInfo(_rFullPath); - - if (pFileInfo != NULL) - return pFileInfo->m_FileSize; - - return 0; -} - -const char* CFileSystemGCWii::GetFileName(u64 _Address) const -{ - for (size_t i = 0; i < m_FileInfoVector.size(); i++) - { - if ((m_FileInfoVector[i].m_Offset <= _Address) && - ((m_FileInfoVector[i].m_Offset + m_FileInfoVector[i].m_FileSize) > _Address)) - { - return m_FileInfoVector[i].m_FullPath; - } - } - - return NULL; -} - -u64 CFileSystemGCWii::ReadFile(const char* _rFullPath, u8* _pBuffer, size_t _MaxBufferSize) const -{ - if (!m_Initialized) - return 0; - - const SFileInfo* pFileInfo = FindFileInfo(_rFullPath); - if (pFileInfo == NULL) - return 0; - - if (pFileInfo->m_FileSize > _MaxBufferSize) - return 0; - - m_rVolume->Read(pFileInfo->m_Offset, pFileInfo->m_FileSize, _pBuffer); - return pFileInfo->m_FileSize; -} - -bool CFileSystemGCWii::ExportFile(const char* _rFullPath, const char* _rExportFilename) const -{ - size_t filesize = GetFileSize(_rFullPath); - - if (filesize == 0) - return false; - - u8* buffer = new u8[filesize]; - - if (!ReadFile(_rFullPath, buffer, filesize)) - { - delete[] buffer; - return false; - } - - FILE* f = fopen(_rExportFilename, "wb"); - - if (f) - { - fwrite(buffer, filesize, 1, f); - fclose(f); - delete[] buffer; - return true; - } - - delete[] buffer; - return false; -} - -bool CFileSystemGCWii::ExportAllFiles(const char* _rFullPath) const -{ - return false; -} - -u32 CFileSystemGCWii::Read32(u64 _Offset) const -{ - u32 Temp = 0; - m_rVolume->Read(_Offset, 4, (u8*)&Temp); - return Common::swap32(Temp); -} - -void CFileSystemGCWii::GetStringFromOffset(u64 _Offset, char* Filename) const -{ - m_rVolume->Read(_Offset, 255, (u8*)Filename); -} - -size_t CFileSystemGCWii::GetFileList(std::vector &_rFilenames) const -{ - if (_rFilenames.size()) - PanicAlert("GetFileList : input list has contents?"); - _rFilenames.clear(); - _rFilenames.reserve(m_FileInfoVector.size()); - for (size_t i = 0; i < m_FileInfoVector.size(); i++) - _rFilenames.push_back(&m_FileInfoVector[i]); - return m_FileInfoVector.size(); -} - -const SFileInfo* CFileSystemGCWii::FindFileInfo(const char* _rFullPath) const -{ - for (size_t i = 0; i < m_FileInfoVector.size(); i++) - { - if (!strcasecmp(m_FileInfoVector[i].m_FullPath, _rFullPath)) - return &m_FileInfoVector[i]; - } - - return NULL; -} - -bool CFileSystemGCWii::InitFileSystem() -{ - if (Read32(0x18) == 0x5D1C9EA3) - { - m_OffsetShift = 2; // Wii file system - } - else if (Read32(0x1c) == 0xC2339F3D) - { - m_OffsetShift = 0; // GC file system - } - else - { - return false; - } - - // read the whole FST - u64 FSTOffset = (u64)Read32(0x424) << m_OffsetShift; - // u32 FSTSize = Read32(0x428); - // u32 FSTMaxSize = Read32(0x42C); - - - // read all fileinfos - SFileInfo Root; - Root.m_NameOffset = Read32(FSTOffset + 0x0); - Root.m_Offset = (u64)Read32(FSTOffset + 0x4) << m_OffsetShift; - Root.m_FileSize = Read32(FSTOffset + 0x8); - - if (Root.IsDirectory()) - { - if (m_FileInfoVector.size()) - PanicAlert("Wtf?"); - u64 NameTableOffset = FSTOffset; - - m_FileInfoVector.reserve(Root.m_FileSize); - for (u32 i = 0; i < Root.m_FileSize; i++) - { - SFileInfo sfi; - u64 Offset = FSTOffset + (i * 0xC); - sfi.m_NameOffset = Read32(Offset + 0x0); - sfi.m_Offset = (u64)Read32(Offset + 0x4) << m_OffsetShift; - sfi.m_FileSize = Read32(Offset + 0x8); - - m_FileInfoVector.push_back(sfi); - NameTableOffset += 0xC; - } - - BuildFilenames(1, m_FileInfoVector.size(), NULL, NameTableOffset); - } - - return true; -} - -// Changed this stuff from C++ string to C strings for speed in debug mode. Doesn't matter in release, but -// std::string is SLOW in debug mode. -size_t CFileSystemGCWii::BuildFilenames(const size_t _FirstIndex, const size_t _LastIndex, const char* _szDirectory, u64 _NameTableOffset) -{ - size_t CurrentIndex = _FirstIndex; - - while (CurrentIndex < _LastIndex) - { - SFileInfo *rFileInfo = &m_FileInfoVector[CurrentIndex]; - u64 uOffset = _NameTableOffset + (rFileInfo->m_NameOffset & 0xFFFFFF); - char filename[512]; - memset(filename, 0, sizeof(filename)); - GetStringFromOffset(uOffset, filename); - - // check next index - if (rFileInfo->IsDirectory()) - { - // this is a directory, build up the new szDirectory - if (_szDirectory != NULL) - CharArrayFromFormat(rFileInfo->m_FullPath, "%s%s\\", _szDirectory, filename); - else - CharArrayFromFormat(rFileInfo->m_FullPath, "%s\\", filename); - - CurrentIndex = BuildFilenames(CurrentIndex + 1, rFileInfo->m_FileSize, rFileInfo->m_FullPath, _NameTableOffset); - } - else - { - // this is a filename - if (_szDirectory != NULL) - CharArrayFromFormat(rFileInfo->m_FullPath, "%s%s", _szDirectory, filename); - else - CharArrayFromFormat(rFileInfo->m_FullPath, "%s", filename); - - CurrentIndex++; - } - } - - return CurrentIndex; -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "stdafx.h" +#include "Common.h" + +#include + +#include "FileSystemGCWii.h" +#include "StringUtil.h" + +namespace DiscIO +{ +CFileSystemGCWii::CFileSystemGCWii(const IVolume *_rVolume) + : IFileSystem(_rVolume), + m_Initialized(false), + m_OffsetShift(0) +{ + m_Initialized = InitFileSystem(); +} + + +CFileSystemGCWii::~CFileSystemGCWii() +{ +} + +bool CFileSystemGCWii::IsInitialized() const +{ + return m_Initialized; +} + +u64 CFileSystemGCWii::GetFileSize(const char* _rFullPath) const +{ + if (!m_Initialized) + return 0; + + const SFileInfo* pFileInfo = FindFileInfo(_rFullPath); + + if (pFileInfo != NULL) + return pFileInfo->m_FileSize; + + return 0; +} + +const char* CFileSystemGCWii::GetFileName(u64 _Address) const +{ + for (size_t i = 0; i < m_FileInfoVector.size(); i++) + { + if ((m_FileInfoVector[i].m_Offset <= _Address) && + ((m_FileInfoVector[i].m_Offset + m_FileInfoVector[i].m_FileSize) > _Address)) + { + return m_FileInfoVector[i].m_FullPath; + } + } + + return NULL; +} + +u64 CFileSystemGCWii::ReadFile(const char* _rFullPath, u8* _pBuffer, size_t _MaxBufferSize) const +{ + if (!m_Initialized) + return 0; + + const SFileInfo* pFileInfo = FindFileInfo(_rFullPath); + if (pFileInfo == NULL) + return 0; + + if (pFileInfo->m_FileSize > _MaxBufferSize) + return 0; + + m_rVolume->Read(pFileInfo->m_Offset, pFileInfo->m_FileSize, _pBuffer); + return pFileInfo->m_FileSize; +} + +bool CFileSystemGCWii::ExportFile(const char* _rFullPath, const char* _rExportFilename) const +{ + size_t filesize = GetFileSize(_rFullPath); + + if (filesize == 0) + return false; + + u8* buffer = new u8[filesize]; + + if (!ReadFile(_rFullPath, buffer, filesize)) + { + delete[] buffer; + return false; + } + + FILE* f = fopen(_rExportFilename, "wb"); + + if (f) + { + fwrite(buffer, filesize, 1, f); + fclose(f); + delete[] buffer; + return true; + } + + delete[] buffer; + return false; +} + +bool CFileSystemGCWii::ExportAllFiles(const char* _rFullPath) const +{ + return false; +} + +u32 CFileSystemGCWii::Read32(u64 _Offset) const +{ + u32 Temp = 0; + m_rVolume->Read(_Offset, 4, (u8*)&Temp); + return Common::swap32(Temp); +} + +void CFileSystemGCWii::GetStringFromOffset(u64 _Offset, char* Filename) const +{ + m_rVolume->Read(_Offset, 255, (u8*)Filename); +} + +size_t CFileSystemGCWii::GetFileList(std::vector &_rFilenames) const +{ + if (_rFilenames.size()) + PanicAlert("GetFileList : input list has contents?"); + _rFilenames.clear(); + _rFilenames.reserve(m_FileInfoVector.size()); + for (size_t i = 0; i < m_FileInfoVector.size(); i++) + _rFilenames.push_back(&m_FileInfoVector[i]); + return m_FileInfoVector.size(); +} + +const SFileInfo* CFileSystemGCWii::FindFileInfo(const char* _rFullPath) const +{ + for (size_t i = 0; i < m_FileInfoVector.size(); i++) + { + if (!strcasecmp(m_FileInfoVector[i].m_FullPath, _rFullPath)) + return &m_FileInfoVector[i]; + } + + return NULL; +} + +bool CFileSystemGCWii::InitFileSystem() +{ + if (Read32(0x18) == 0x5D1C9EA3) + { + m_OffsetShift = 2; // Wii file system + } + else if (Read32(0x1c) == 0xC2339F3D) + { + m_OffsetShift = 0; // GC file system + } + else + { + return false; + } + + // read the whole FST + u64 FSTOffset = (u64)Read32(0x424) << m_OffsetShift; + // u32 FSTSize = Read32(0x428); + // u32 FSTMaxSize = Read32(0x42C); + + + // read all fileinfos + SFileInfo Root; + Root.m_NameOffset = Read32(FSTOffset + 0x0); + Root.m_Offset = (u64)Read32(FSTOffset + 0x4) << m_OffsetShift; + Root.m_FileSize = Read32(FSTOffset + 0x8); + + if (Root.IsDirectory()) + { + if (m_FileInfoVector.size()) + PanicAlert("Wtf?"); + u64 NameTableOffset = FSTOffset; + + m_FileInfoVector.reserve(Root.m_FileSize); + for (u32 i = 0; i < Root.m_FileSize; i++) + { + SFileInfo sfi; + u64 Offset = FSTOffset + (i * 0xC); + sfi.m_NameOffset = Read32(Offset + 0x0); + sfi.m_Offset = (u64)Read32(Offset + 0x4) << m_OffsetShift; + sfi.m_FileSize = Read32(Offset + 0x8); + + m_FileInfoVector.push_back(sfi); + NameTableOffset += 0xC; + } + + BuildFilenames(1, m_FileInfoVector.size(), NULL, NameTableOffset); + } + + return true; +} + +// Changed this stuff from C++ string to C strings for speed in debug mode. Doesn't matter in release, but +// std::string is SLOW in debug mode. +size_t CFileSystemGCWii::BuildFilenames(const size_t _FirstIndex, const size_t _LastIndex, const char* _szDirectory, u64 _NameTableOffset) +{ + size_t CurrentIndex = _FirstIndex; + + while (CurrentIndex < _LastIndex) + { + SFileInfo *rFileInfo = &m_FileInfoVector[CurrentIndex]; + u64 uOffset = _NameTableOffset + (rFileInfo->m_NameOffset & 0xFFFFFF); + char filename[512]; + memset(filename, 0, sizeof(filename)); + GetStringFromOffset(uOffset, filename); + + // check next index + if (rFileInfo->IsDirectory()) + { + // this is a directory, build up the new szDirectory + if (_szDirectory != NULL) + CharArrayFromFormat(rFileInfo->m_FullPath, "%s%s\\", _szDirectory, filename); + else + CharArrayFromFormat(rFileInfo->m_FullPath, "%s\\", filename); + + CurrentIndex = BuildFilenames(CurrentIndex + 1, rFileInfo->m_FileSize, rFileInfo->m_FullPath, _NameTableOffset); + } + else + { + // this is a filename + if (_szDirectory != NULL) + CharArrayFromFormat(rFileInfo->m_FullPath, "%s%s", _szDirectory, filename); + else + CharArrayFromFormat(rFileInfo->m_FullPath, "%s", filename); + + CurrentIndex++; + } + } + + return CurrentIndex; +} + +} // namespace diff --git a/Source/Core/DiscIO/Src/Filesystem.cpp b/Source/Core/DiscIO/Src/Filesystem.cpp index 39bf82e822..5118c8e7b2 100644 --- a/Source/Core/DiscIO/Src/Filesystem.cpp +++ b/Source/Core/DiscIO/Src/Filesystem.cpp @@ -1,50 +1,50 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "stdafx.h" -#include "Filesystem.h" - -#include "VolumeCreator.h" -#include "FileSystemGCWii.h" - -namespace DiscIO -{ -IFileSystem::IFileSystem(const IVolume *_rVolume) - : m_rVolume(_rVolume) -{} - - -IFileSystem::~IFileSystem() -{} - - -IFileSystem* CreateFileSystem(const IVolume* _rVolume) -{ - IFileSystem* pFileSystem = new CFileSystemGCWii(_rVolume); - - if (!pFileSystem) - return 0; - - if (!pFileSystem->IsInitialized()) - { - delete pFileSystem; - pFileSystem = NULL; - } - - return pFileSystem; -} -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "stdafx.h" +#include "Filesystem.h" + +#include "VolumeCreator.h" +#include "FileSystemGCWii.h" + +namespace DiscIO +{ +IFileSystem::IFileSystem(const IVolume *_rVolume) + : m_rVolume(_rVolume) +{} + + +IFileSystem::~IFileSystem() +{} + + +IFileSystem* CreateFileSystem(const IVolume* _rVolume) +{ + IFileSystem* pFileSystem = new CFileSystemGCWii(_rVolume); + + if (!pFileSystem) + return 0; + + if (!pFileSystem->IsInitialized()) + { + delete pFileSystem; + pFileSystem = NULL; + } + + return pFileSystem; +} +} // namespace diff --git a/Source/Core/DiscIO/Src/VolumeCreator.cpp b/Source/Core/DiscIO/Src/VolumeCreator.cpp index 319af6b7d9..2ff5e8471b 100644 --- a/Source/Core/DiscIO/Src/VolumeCreator.cpp +++ b/Source/Core/DiscIO/Src/VolumeCreator.cpp @@ -1,262 +1,262 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include - -#include "AES/aes.h" - -#include "VolumeCreator.h" - -#include "Volume.h" -#include "VolumeDirectory.h" -#include "VolumeGC.h" -#include "VolumeWiiCrypted.h" - -#include "Hash.h" - -namespace DiscIO -{ -enum EDiscType -{ - DISC_TYPE_UNK, - DISC_TYPE_WII, - DISC_TYPE_WII_CONTAINER, - DISC_TYPE_GC -}; - -#ifndef _WIN32 -struct SPartition -{ - u64 Offset; - u32 Type; -}; //gcc 4.3 cries if it's local -#endif - -class CBlobBigEndianReader -{ -public: - CBlobBigEndianReader(IBlobReader& _rReader) : m_rReader(_rReader) {} - - u32 Read32(u64 _Offset) - { - u32 Temp; - m_rReader.Read(_Offset, 4, (u8*)&Temp); - return(Common::swap32(Temp)); - } - -private: - IBlobReader& m_rReader; -}; - -unsigned char g_MasterKey[16]; -bool g_MasterKeyInit = false; - -IVolume* CreateVolumeFromCryptedWiiImage(IBlobReader& _rReader, u32 _VolumeType); -EDiscType GetDiscType(IBlobReader& _rReader); - -IVolume* CreateVolumeFromFilename(const std::string& _rFilename) -{ - IBlobReader* pReader = CreateBlobReader(_rFilename.c_str()); - - if (pReader == NULL) - return NULL; - - switch (GetDiscType(*pReader)) - { - case DISC_TYPE_WII: - case DISC_TYPE_GC: - return new CVolumeGC(pReader); - - case DISC_TYPE_WII_CONTAINER: - { - IVolume* pVolume = CreateVolumeFromCryptedWiiImage(*pReader, 0); - - if (pVolume == NULL) - { - delete pReader; - } - - return(pVolume); - } - break; - - case DISC_TYPE_UNK: - default: - delete pReader; - return NULL; - } - - // unreachable code - return NULL; -} - -IVolume* CreateVolumeFromDirectory(const std::string& _rDirectory, bool _bIsWii) -{ - if (CVolumeDirectory::IsValidDirectory(_rDirectory)) - return new CVolumeDirectory(_rDirectory, _bIsWii); - - return NULL; -} - -bool IsVolumeWiiDisc(const IVolume *_rVolume) -{ - u32 MagicWord = 0; - _rVolume->Read(0x18, 4, (u8*)&MagicWord); - - return (Common::swap32(MagicWord) == 0x5D1C9EA3); -} - -IVolume* CreateVolumeFromCryptedWiiImage(IBlobReader& _rReader, u32 _VolumeType) -{ - if (!g_MasterKeyInit) - { - FILE* pT = fopen(WII_MASTERKEY_FILE, "rb"); - if (pT == NULL) - { - if(PanicYesNo("Can't open '" WII_MASTERKEY_FILE "'.\n If you know the key, now it's the time to paste it into '" - WII_MASTERKEY_FILE_HEX "' before pressing [YES].")) - { - pT = fopen(WII_MASTERKEY_FILE_HEX, "r"); - if(pT==NULL){ - PanicAlert("could not open " WII_MASTERKEY_FILE_HEX ); - return NULL; - } - - static char hexkey[32]; - if(fread(hexkey,1,32,pT)<32) - { - PanicAlert(WII_MASTERKEY_FILE_HEX " is not the right size" ); - fclose(pT); - return NULL; - } - fclose(pT); - - static char binkey[16]; - char *t=hexkey; - for(int i=0;i<16;i++) - { - char h[3]={*(t++),*(t++),0}; - binkey[i] = strtol(h,NULL,16); - } - - pT = fopen(WII_MASTERKEY_FILE, "wb"); - if(pT==NULL){ - PanicAlert("could not open/make '" WII_MASTERKEY_FILE "' for writing!"); - return NULL; - } - - fwrite(binkey,16,1,pT); - fclose(pT); - - pT = fopen(WII_MASTERKEY_FILE, "rb"); - if(pT==NULL){ - PanicAlert("could not open '" WII_MASTERKEY_FILE "' for reading!\n did the file get deleted or locked after converting?"); - return NULL; - } - } - else - return NULL; - } - - fread(g_MasterKey, 16, 1, pT); - fclose(pT); - const u32 keyhash = 0x4bc30936; - u32 hash = HashAdler32(g_MasterKey, 16); - if (hash != keyhash) - PanicAlert("Your Wii disc decryption key is bad.", keyhash, hash); - else - g_MasterKeyInit = true; - } - - CBlobBigEndianReader Reader(_rReader); - - u32 numPartitions = Reader.Read32(0x40000); - u64 PartitionsOffset = (u64)Reader.Read32(0x40004) << 2; - #ifdef _WIN32 - struct SPartition - { - u64 Offset; - u32 Type; - }; - #endif - std::vector PartitionsVec; - - // read all partitions - for (u32 i = 0; i < numPartitions; i++) - { - SPartition Partition; - Partition.Offset = ((u64)Reader.Read32(PartitionsOffset + (i * 8) + 0)) << 2; - Partition.Type = Reader.Read32(PartitionsOffset + (i * 8) + 4); - PartitionsVec.push_back(Partition); - } - - // find the partition with the game... type == 1 is prolly the firmware update partition - for (size_t i = 0; i < PartitionsVec.size(); i++) - { - const SPartition& rPartition = PartitionsVec[i]; - - if (rPartition.Type == _VolumeType) - { - u8 SubKey[16]; - _rReader.Read(rPartition.Offset + 0x1bf, 16, SubKey); - - u8 IV[16]; - memset(IV, 0, 16); - _rReader.Read(rPartition.Offset + 0x44c, 8, IV); - - AES_KEY AES_KEY; - AES_set_decrypt_key(g_MasterKey, 128, &AES_KEY); - - u8 VolumeKey[16]; - AES_cbc_encrypt(SubKey, VolumeKey, 16, &AES_KEY, IV, AES_DECRYPT); - - return new CVolumeWiiCrypted(&_rReader, rPartition.Offset + 0x20000, VolumeKey); - } - } - - return NULL; -} - -EDiscType GetDiscType(IBlobReader& _rReader) -{ - CBlobBigEndianReader Reader(_rReader); - - // check for wii - { - u32 MagicWord = Reader.Read32(0x18); - - if (MagicWord == 0x5D1C9EA3) - { - if (Reader.Read32(0x60) != 0) - return(DISC_TYPE_WII); - else - return(DISC_TYPE_WII_CONTAINER); - } - } - - // check for GC - { - u32 MagicWord = Reader.Read32(0x1C); - - if (MagicWord == 0xC2339F3D) - return(DISC_TYPE_GC); - } - - return DISC_TYPE_UNK; -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "AES/aes.h" + +#include "VolumeCreator.h" + +#include "Volume.h" +#include "VolumeDirectory.h" +#include "VolumeGC.h" +#include "VolumeWiiCrypted.h" + +#include "Hash.h" + +namespace DiscIO +{ +enum EDiscType +{ + DISC_TYPE_UNK, + DISC_TYPE_WII, + DISC_TYPE_WII_CONTAINER, + DISC_TYPE_GC +}; + +#ifndef _WIN32 +struct SPartition +{ + u64 Offset; + u32 Type; +}; //gcc 4.3 cries if it's local +#endif + +class CBlobBigEndianReader +{ +public: + CBlobBigEndianReader(IBlobReader& _rReader) : m_rReader(_rReader) {} + + u32 Read32(u64 _Offset) + { + u32 Temp; + m_rReader.Read(_Offset, 4, (u8*)&Temp); + return(Common::swap32(Temp)); + } + +private: + IBlobReader& m_rReader; +}; + +unsigned char g_MasterKey[16]; +bool g_MasterKeyInit = false; + +IVolume* CreateVolumeFromCryptedWiiImage(IBlobReader& _rReader, u32 _VolumeType); +EDiscType GetDiscType(IBlobReader& _rReader); + +IVolume* CreateVolumeFromFilename(const std::string& _rFilename) +{ + IBlobReader* pReader = CreateBlobReader(_rFilename.c_str()); + + if (pReader == NULL) + return NULL; + + switch (GetDiscType(*pReader)) + { + case DISC_TYPE_WII: + case DISC_TYPE_GC: + return new CVolumeGC(pReader); + + case DISC_TYPE_WII_CONTAINER: + { + IVolume* pVolume = CreateVolumeFromCryptedWiiImage(*pReader, 0); + + if (pVolume == NULL) + { + delete pReader; + } + + return(pVolume); + } + break; + + case DISC_TYPE_UNK: + default: + delete pReader; + return NULL; + } + + // unreachable code + return NULL; +} + +IVolume* CreateVolumeFromDirectory(const std::string& _rDirectory, bool _bIsWii) +{ + if (CVolumeDirectory::IsValidDirectory(_rDirectory)) + return new CVolumeDirectory(_rDirectory, _bIsWii); + + return NULL; +} + +bool IsVolumeWiiDisc(const IVolume *_rVolume) +{ + u32 MagicWord = 0; + _rVolume->Read(0x18, 4, (u8*)&MagicWord); + + return (Common::swap32(MagicWord) == 0x5D1C9EA3); +} + +IVolume* CreateVolumeFromCryptedWiiImage(IBlobReader& _rReader, u32 _VolumeType) +{ + if (!g_MasterKeyInit) + { + FILE* pT = fopen(WII_MASTERKEY_FILE, "rb"); + if (pT == NULL) + { + if(PanicYesNo("Can't open '" WII_MASTERKEY_FILE "'.\n If you know the key, now it's the time to paste it into '" + WII_MASTERKEY_FILE_HEX "' before pressing [YES].")) + { + pT = fopen(WII_MASTERKEY_FILE_HEX, "r"); + if(pT==NULL){ + PanicAlert("could not open " WII_MASTERKEY_FILE_HEX ); + return NULL; + } + + static char hexkey[32]; + if(fread(hexkey,1,32,pT)<32) + { + PanicAlert(WII_MASTERKEY_FILE_HEX " is not the right size" ); + fclose(pT); + return NULL; + } + fclose(pT); + + static char binkey[16]; + char *t=hexkey; + for(int i=0;i<16;i++) + { + char h[3]={*(t++),*(t++),0}; + binkey[i] = strtol(h,NULL,16); + } + + pT = fopen(WII_MASTERKEY_FILE, "wb"); + if(pT==NULL){ + PanicAlert("could not open/make '" WII_MASTERKEY_FILE "' for writing!"); + return NULL; + } + + fwrite(binkey,16,1,pT); + fclose(pT); + + pT = fopen(WII_MASTERKEY_FILE, "rb"); + if(pT==NULL){ + PanicAlert("could not open '" WII_MASTERKEY_FILE "' for reading!\n did the file get deleted or locked after converting?"); + return NULL; + } + } + else + return NULL; + } + + fread(g_MasterKey, 16, 1, pT); + fclose(pT); + const u32 keyhash = 0x4bc30936; + u32 hash = HashAdler32(g_MasterKey, 16); + if (hash != keyhash) + PanicAlert("Your Wii disc decryption key is bad.", keyhash, hash); + else + g_MasterKeyInit = true; + } + + CBlobBigEndianReader Reader(_rReader); + + u32 numPartitions = Reader.Read32(0x40000); + u64 PartitionsOffset = (u64)Reader.Read32(0x40004) << 2; + #ifdef _WIN32 + struct SPartition + { + u64 Offset; + u32 Type; + }; + #endif + std::vector PartitionsVec; + + // read all partitions + for (u32 i = 0; i < numPartitions; i++) + { + SPartition Partition; + Partition.Offset = ((u64)Reader.Read32(PartitionsOffset + (i * 8) + 0)) << 2; + Partition.Type = Reader.Read32(PartitionsOffset + (i * 8) + 4); + PartitionsVec.push_back(Partition); + } + + // find the partition with the game... type == 1 is prolly the firmware update partition + for (size_t i = 0; i < PartitionsVec.size(); i++) + { + const SPartition& rPartition = PartitionsVec[i]; + + if (rPartition.Type == _VolumeType) + { + u8 SubKey[16]; + _rReader.Read(rPartition.Offset + 0x1bf, 16, SubKey); + + u8 IV[16]; + memset(IV, 0, 16); + _rReader.Read(rPartition.Offset + 0x44c, 8, IV); + + AES_KEY AES_KEY; + AES_set_decrypt_key(g_MasterKey, 128, &AES_KEY); + + u8 VolumeKey[16]; + AES_cbc_encrypt(SubKey, VolumeKey, 16, &AES_KEY, IV, AES_DECRYPT); + + return new CVolumeWiiCrypted(&_rReader, rPartition.Offset + 0x20000, VolumeKey); + } + } + + return NULL; +} + +EDiscType GetDiscType(IBlobReader& _rReader) +{ + CBlobBigEndianReader Reader(_rReader); + + // check for wii + { + u32 MagicWord = Reader.Read32(0x18); + + if (MagicWord == 0x5D1C9EA3) + { + if (Reader.Read32(0x60) != 0) + return(DISC_TYPE_WII); + else + return(DISC_TYPE_WII_CONTAINER); + } + } + + // check for GC + { + u32 MagicWord = Reader.Read32(0x1C); + + if (MagicWord == 0xC2339F3D) + return(DISC_TYPE_GC); + } + + return DISC_TYPE_UNK; +} + +} // namespace diff --git a/Source/Core/DiscIO/Src/VolumeDirectory.cpp b/Source/Core/DiscIO/Src/VolumeDirectory.cpp index 0b9396fa61..ff7f883636 100644 --- a/Source/Core/DiscIO/Src/VolumeDirectory.cpp +++ b/Source/Core/DiscIO/Src/VolumeDirectory.cpp @@ -1,466 +1,466 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ -#include "stdafx.h" - -#include "VolumeDirectory.h" -#include "FileBlob.h" - -namespace DiscIO -{ - -static const u8 ENTRY_SIZE = 0x0c; -static const u8 FILE_ENTRY = 0; -static const u8 DIRECTORY_ENTRY = 1; -static const u64 FST_ADDRESS = 0x440; -static const u32 MAX_NAME_LENGTH = 0x3df; - -CVolumeDirectory::CVolumeDirectory(const std::string& _rDirectory, bool _bIsWii) - : m_totalNameSize(0) - , m_dataStartAddress(-1) - , m_fstSize(0) - , m_FSTData(NULL) -{ - m_rootDirectory = ExtractDirectoryName(_rDirectory); - - // create the default disk header - m_diskHeader = new u8[FST_ADDRESS]; - memset(m_diskHeader, 0, (size_t)FST_ADDRESS); - SetUniqueID("RZDE01"); - SetName("Default name"); - - if(_bIsWii) - { - SetDiskTypeWii(); - } - else - { - SetDiskTypeGC(); - } - - BuildFST(); -} - -CVolumeDirectory::~CVolumeDirectory() -{ - delete m_FSTData; - m_FSTData = NULL; - - delete m_diskHeader; - m_diskHeader = NULL; -} - -bool CVolumeDirectory::IsValidDirectory(const std::string& _rDirectory) -{ - std::string directoryName = ExtractDirectoryName(_rDirectory); - return File::IsDirectory(directoryName.c_str()); -} - -bool CVolumeDirectory::Read(u64 _Offset, u64 _Length, u8* _pBuffer) const -{ - if(_Offset < FST_ADDRESS) - { - WriteToBuffer(0, FST_ADDRESS, m_diskHeader, _Offset, _Length, _pBuffer); - } - - if(_Offset < m_dataStartAddress) - { - WriteToBuffer(FST_ADDRESS, m_fstSize, m_FSTData, _Offset, _Length, _pBuffer); - } - - if(m_virtualDisk.size() == 0) - return true; - - // Determine which file the offset refers to - std::map::const_iterator fileIter = m_virtualDisk.lower_bound(_Offset); - if(fileIter->first > _Offset && fileIter != m_virtualDisk.begin()) - --fileIter; - - // zero fill to start of file data - PadToAddress(fileIter->first, _Offset, _Length, _pBuffer); - - while(fileIter != m_virtualDisk.end() && _Length > 0) - { - _dbg_assert_(DVDINTERFACE, fileIter->first <= _Offset); - u64 fileOffset = _Offset - fileIter->first; - - PlainFileReader* reader = PlainFileReader::Create(fileIter->second.c_str()); - if(reader == NULL) - return false; - - u64 fileSize = reader->GetDataSize(); - - if(fileOffset < fileSize) - { - u64 fileBytes = fileSize - fileOffset; - if(_Length < fileBytes) - fileBytes = _Length; - - if(!reader->Read(fileOffset, fileBytes, _pBuffer)) - return false; - - _Length -= fileBytes; - _pBuffer += fileBytes; - _Offset += fileBytes; - } - - ++fileIter; - - if(fileIter != m_virtualDisk.end()) - { - _dbg_assert_(DVDINTERFACE, fileIter->first >= _Offset); - PadToAddress(fileIter->first, _Offset, _Length, _pBuffer); - } - } - - return true; -} - -std::string CVolumeDirectory::GetUniqueID() const -{ - _dbg_assert_(DVDINTERFACE, m_diskHeader); - - char buffer[7]; - memcpy(buffer, m_diskHeader, 6); - buffer[6] = 0; - - std::string id = buffer; - return id; -} - -void CVolumeDirectory::SetUniqueID(std::string _ID) -{ - _dbg_assert_(DVDINTERFACE, m_diskHeader); - - u32 length = _ID.length(); - if(length > 6) - length = 6; - - memcpy(m_diskHeader, _ID.c_str(), length); -} - -IVolume::ECountry CVolumeDirectory::GetCountry() const -{ - _dbg_assert_(DVDINTERFACE, m_diskHeader); - - u8 CountryCode = m_diskHeader[3]; - - ECountry country = COUNTRY_UNKNOWN; - - switch (CountryCode) - { - case 'S': - country = COUNTRY_EUROPE; - break; // PAL <- that is shitty :) zelda demo disc - - case 'P': - country = COUNTRY_EUROPE; - break; // PAL - - case 'D': - country = COUNTRY_EUROPE; - break; // PAL - - case 'F': - country = COUNTRY_FRANCE; - break; // PAL - - case 'X': - country = COUNTRY_EUROPE; - break; // XIII <- uses X but is PAL rip - - case 'E': - country = COUNTRY_USA; - break; // USA - - case 'J': - country = COUNTRY_JAP; - break; // JAP - - case 'O': - country = COUNTRY_UNKNOWN; - break; // SDK - - default: - country = COUNTRY_UNKNOWN; - break; - } - - return(country); -} - -std::string CVolumeDirectory::GetMakerID() const -{ - return "VOID"; -} - -std::string CVolumeDirectory::GetName() const -{ - _dbg_assert_(DVDINTERFACE, m_diskHeader); - std::string name = (char*)(m_diskHeader + 0x20); - return name; -} - -void CVolumeDirectory::SetName(std::string _Name) -{ - _dbg_assert_(DVDINTERFACE, m_diskHeader); - - u32 length = _Name.length(); - if(length > MAX_NAME_LENGTH) - length = MAX_NAME_LENGTH; - - memcpy(m_diskHeader + 0x20, _Name.c_str(), length); - m_diskHeader[length + 0x20] = 0; -} - -u32 CVolumeDirectory::GetFSTSize() const -{ - return 0; -} - -std::string CVolumeDirectory::GetApploaderDate() const -{ - return "VOID"; -} - -u64 CVolumeDirectory::GetSize() const -{ - return 0; -} - -static const char DIR_SEPARATOR = -#ifdef _WIN32 - '\\'; -#else - '/'; -#endif - -std::string CVolumeDirectory::ExtractDirectoryName(const std::string& _rDirectory) -{ - std::string directoryName = _rDirectory; - - size_t lastSep = directoryName.find_last_of(DIR_SEPARATOR); - - if(lastSep != directoryName.size() - 1) - { - // TODO: This assumes that file names will always have a dot in them - // and directory names never will; both assumptions are often - // right but in general wrong. - size_t extensionStart = directoryName.find_last_of('.'); - if(extensionStart != std::string::npos && extensionStart > lastSep) - { - directoryName.resize(lastSep); - } - } - else - { - directoryName.resize(lastSep); - } - - return directoryName; -} - -void CVolumeDirectory::SetDiskTypeWii() -{ - _dbg_assert_(DVDINTERFACE, m_diskHeader); - - m_diskHeader[0x18] = 0x5d; - m_diskHeader[0x19] = 0x1c; - m_diskHeader[0x1a] = 0x9e; - m_diskHeader[0x1b] = 0xa3; - memset(m_diskHeader + 0x1c, 0, 4); - - m_addressShift = 2; -} - -void CVolumeDirectory::SetDiskTypeGC() -{ - _dbg_assert_(DVDINTERFACE, m_diskHeader); - - memset(m_diskHeader + 0x18, 0, 4); - m_diskHeader[0x1c] = 0xc2; - m_diskHeader[0x1d] = 0x33; - m_diskHeader[0x1e] = 0x9f; - m_diskHeader[0x1f] = 0x3d; - - m_addressShift = 0; -} - -void CVolumeDirectory::BuildFST() -{ - if(m_FSTData) - { - delete m_FSTData; - } - - File::FSTEntry rootEntry; - - // read data from physical disk to rootEntry - u32 totalEntries = AddDirectoryEntries(m_rootDirectory, rootEntry) + 1; - - m_fstNameOffset = totalEntries * ENTRY_SIZE; // offset in FST nameTable - m_fstSize = m_fstNameOffset + m_totalNameSize; - m_FSTData = new u8[(u32)m_fstSize]; - - // 4 byte aligned start of data on disk - m_dataStartAddress = (FST_ADDRESS + m_fstSize + 3) & ~3; - u64 curDataAddress = m_dataStartAddress; - - u32 fstOffset = 0; // offset within FST data - u32 nameOffset = 0; // offset within name table - u32 rootOffset = 0; // offset of root of FST - - // write root entry - WriteEntryData(fstOffset, DIRECTORY_ENTRY, 0, 0, totalEntries); - - for(std::vector::iterator iter = rootEntry.children.begin(); iter != rootEntry.children.end(); ++iter) - { - WriteEntry(*iter, fstOffset, nameOffset, curDataAddress, rootOffset); - } - - // overflow check - _dbg_assert_(DVDINTERFACE, nameOffset == m_fstSize); - - // write FST size and location - _dbg_assert_(DVDINTERFACE, m_diskHeader); - Write32((u32)(FST_ADDRESS >> m_addressShift), 0x0424, m_diskHeader); - Write32((u32)m_fstSize, 0x0428, m_diskHeader); - Write32((u32)m_fstSize, 0x042c, m_diskHeader); -} - -void CVolumeDirectory::WriteToBuffer(u64 _SrcStartAddress, u64 _SrcLength, u8* _Src, - u64& _Address, u64& _Length, u8*& _pBuffer) const -{ - _dbg_assert_(DVDINTERFACE, _Address >= _SrcStartAddress); - - u64 srcOffset = _Address - _SrcStartAddress; - - if(srcOffset < _SrcLength) - { - u64 srcBytes = _SrcLength - srcOffset; - if(_Length < srcBytes) - srcBytes = _Length; - - memcpy(_pBuffer, _Src + srcOffset, (size_t)srcBytes); - - _Length -= srcBytes; - _pBuffer += srcBytes; - _Address += srcBytes; - } -} - -void CVolumeDirectory::PadToAddress(u64 _StartAddress, u64& _Address, u64& _Length, u8*& _pBuffer) const -{ - if(_StartAddress <= _Address) - return; - - u64 padBytes = _StartAddress - _Address; - if(padBytes > _Length) - padBytes = _Length; - - if(_Length > 0) - { - memset(_pBuffer, 0, (size_t)padBytes); - _Length -= padBytes; - _pBuffer += padBytes; - _Address += padBytes; - } -} - -void CVolumeDirectory::Write32(u32 data, u32 offset, u8* buffer) -{ - buffer[offset++] = (data >> 24); - buffer[offset++] = (data >> 16) & 0xff; - buffer[offset++] = (data >> 8) & 0xff; - buffer[offset] = (data) & 0xff; -} - -void CVolumeDirectory::WriteEntryData(u32& entryOffset, u8 type, u32 nameOffset, u64 dataOffset, u32 length) -{ - m_FSTData[entryOffset++] = type; - - m_FSTData[entryOffset++] = (nameOffset >> 16) & 0xff; - m_FSTData[entryOffset++] = (nameOffset >> 8) & 0xff; - m_FSTData[entryOffset++] = (nameOffset) & 0xff; - - Write32((u32)(dataOffset >> m_addressShift), entryOffset, m_FSTData); - entryOffset += 4; - - Write32((u32)length, entryOffset, m_FSTData); - entryOffset += 4; -} - -void CVolumeDirectory::WriteEntryName(u32& nameOffset, const std::string& name) -{ - strncpy((char*)(m_FSTData + nameOffset + m_fstNameOffset), name.c_str(), name.length() + 1); - - nameOffset += (name.length() + 1); -} - -void CVolumeDirectory::WriteEntry(const File::FSTEntry& entry, u32& fstOffset, u32& nameOffset, u64& dataOffset, u32 parentEntryNum) -{ - if(entry.isDirectory) - { - u32 myOffset = fstOffset; - u32 myEntryNum = myOffset / ENTRY_SIZE; - WriteEntryData(fstOffset, DIRECTORY_ENTRY, nameOffset, parentEntryNum, myEntryNum + entry.size + 1); - WriteEntryName(nameOffset, entry.virtualName); - - for(std::vector::const_iterator iter = entry.children.begin(); iter != entry.children.end(); ++iter) - { - WriteEntry(*iter, fstOffset, nameOffset, dataOffset, myEntryNum); - } - } - else - { - // put entry in FST - WriteEntryData(fstOffset, FILE_ENTRY, nameOffset, dataOffset, entry.size); - WriteEntryName(nameOffset, entry.virtualName); - - // write entry to virtual disk - _dbg_assert_(DVDINTERFACE, m_virtualDisk.find(dataOffset) == m_virtualDisk.end()); - m_virtualDisk.insert(make_pair(dataOffset, entry.physicalName)); - - // 4 byte aligned - dataOffset = (dataOffset + entry.size + 3) & ~3ULL; - } -} - -static u32 ComputeNameSize(const File::FSTEntry& parentEntry) -{ - u32 nameSize = 0; - const std::vector& children = parentEntry.children; - for (std::vector::const_iterator it = children.begin(); - it != children.end(); ++it) - { - const File::FSTEntry& entry = *it; - if (entry.isDirectory) - { - nameSize += ComputeNameSize(entry); - } - nameSize += entry.virtualName.length() + 1; - } - return nameSize; -} - -u32 CVolumeDirectory::AddDirectoryEntries(const std::string& _Directory, File::FSTEntry& parentEntry) -{ - u32 foundEntries = ScanDirectoryTree(_Directory, parentEntry); - m_totalNameSize += ComputeNameSize(parentEntry); - return foundEntries; -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +#include "stdafx.h" + +#include "VolumeDirectory.h" +#include "FileBlob.h" + +namespace DiscIO +{ + +static const u8 ENTRY_SIZE = 0x0c; +static const u8 FILE_ENTRY = 0; +static const u8 DIRECTORY_ENTRY = 1; +static const u64 FST_ADDRESS = 0x440; +static const u32 MAX_NAME_LENGTH = 0x3df; + +CVolumeDirectory::CVolumeDirectory(const std::string& _rDirectory, bool _bIsWii) + : m_totalNameSize(0) + , m_dataStartAddress(-1) + , m_fstSize(0) + , m_FSTData(NULL) +{ + m_rootDirectory = ExtractDirectoryName(_rDirectory); + + // create the default disk header + m_diskHeader = new u8[FST_ADDRESS]; + memset(m_diskHeader, 0, (size_t)FST_ADDRESS); + SetUniqueID("RZDE01"); + SetName("Default name"); + + if(_bIsWii) + { + SetDiskTypeWii(); + } + else + { + SetDiskTypeGC(); + } + + BuildFST(); +} + +CVolumeDirectory::~CVolumeDirectory() +{ + delete m_FSTData; + m_FSTData = NULL; + + delete m_diskHeader; + m_diskHeader = NULL; +} + +bool CVolumeDirectory::IsValidDirectory(const std::string& _rDirectory) +{ + std::string directoryName = ExtractDirectoryName(_rDirectory); + return File::IsDirectory(directoryName.c_str()); +} + +bool CVolumeDirectory::Read(u64 _Offset, u64 _Length, u8* _pBuffer) const +{ + if(_Offset < FST_ADDRESS) + { + WriteToBuffer(0, FST_ADDRESS, m_diskHeader, _Offset, _Length, _pBuffer); + } + + if(_Offset < m_dataStartAddress) + { + WriteToBuffer(FST_ADDRESS, m_fstSize, m_FSTData, _Offset, _Length, _pBuffer); + } + + if(m_virtualDisk.size() == 0) + return true; + + // Determine which file the offset refers to + std::map::const_iterator fileIter = m_virtualDisk.lower_bound(_Offset); + if(fileIter->first > _Offset && fileIter != m_virtualDisk.begin()) + --fileIter; + + // zero fill to start of file data + PadToAddress(fileIter->first, _Offset, _Length, _pBuffer); + + while(fileIter != m_virtualDisk.end() && _Length > 0) + { + _dbg_assert_(DVDINTERFACE, fileIter->first <= _Offset); + u64 fileOffset = _Offset - fileIter->first; + + PlainFileReader* reader = PlainFileReader::Create(fileIter->second.c_str()); + if(reader == NULL) + return false; + + u64 fileSize = reader->GetDataSize(); + + if(fileOffset < fileSize) + { + u64 fileBytes = fileSize - fileOffset; + if(_Length < fileBytes) + fileBytes = _Length; + + if(!reader->Read(fileOffset, fileBytes, _pBuffer)) + return false; + + _Length -= fileBytes; + _pBuffer += fileBytes; + _Offset += fileBytes; + } + + ++fileIter; + + if(fileIter != m_virtualDisk.end()) + { + _dbg_assert_(DVDINTERFACE, fileIter->first >= _Offset); + PadToAddress(fileIter->first, _Offset, _Length, _pBuffer); + } + } + + return true; +} + +std::string CVolumeDirectory::GetUniqueID() const +{ + _dbg_assert_(DVDINTERFACE, m_diskHeader); + + char buffer[7]; + memcpy(buffer, m_diskHeader, 6); + buffer[6] = 0; + + std::string id = buffer; + return id; +} + +void CVolumeDirectory::SetUniqueID(std::string _ID) +{ + _dbg_assert_(DVDINTERFACE, m_diskHeader); + + u32 length = _ID.length(); + if(length > 6) + length = 6; + + memcpy(m_diskHeader, _ID.c_str(), length); +} + +IVolume::ECountry CVolumeDirectory::GetCountry() const +{ + _dbg_assert_(DVDINTERFACE, m_diskHeader); + + u8 CountryCode = m_diskHeader[3]; + + ECountry country = COUNTRY_UNKNOWN; + + switch (CountryCode) + { + case 'S': + country = COUNTRY_EUROPE; + break; // PAL <- that is shitty :) zelda demo disc + + case 'P': + country = COUNTRY_EUROPE; + break; // PAL + + case 'D': + country = COUNTRY_EUROPE; + break; // PAL + + case 'F': + country = COUNTRY_FRANCE; + break; // PAL + + case 'X': + country = COUNTRY_EUROPE; + break; // XIII <- uses X but is PAL rip + + case 'E': + country = COUNTRY_USA; + break; // USA + + case 'J': + country = COUNTRY_JAP; + break; // JAP + + case 'O': + country = COUNTRY_UNKNOWN; + break; // SDK + + default: + country = COUNTRY_UNKNOWN; + break; + } + + return(country); +} + +std::string CVolumeDirectory::GetMakerID() const +{ + return "VOID"; +} + +std::string CVolumeDirectory::GetName() const +{ + _dbg_assert_(DVDINTERFACE, m_diskHeader); + std::string name = (char*)(m_diskHeader + 0x20); + return name; +} + +void CVolumeDirectory::SetName(std::string _Name) +{ + _dbg_assert_(DVDINTERFACE, m_diskHeader); + + u32 length = _Name.length(); + if(length > MAX_NAME_LENGTH) + length = MAX_NAME_LENGTH; + + memcpy(m_diskHeader + 0x20, _Name.c_str(), length); + m_diskHeader[length + 0x20] = 0; +} + +u32 CVolumeDirectory::GetFSTSize() const +{ + return 0; +} + +std::string CVolumeDirectory::GetApploaderDate() const +{ + return "VOID"; +} + +u64 CVolumeDirectory::GetSize() const +{ + return 0; +} + +static const char DIR_SEPARATOR = +#ifdef _WIN32 + '\\'; +#else + '/'; +#endif + +std::string CVolumeDirectory::ExtractDirectoryName(const std::string& _rDirectory) +{ + std::string directoryName = _rDirectory; + + size_t lastSep = directoryName.find_last_of(DIR_SEPARATOR); + + if(lastSep != directoryName.size() - 1) + { + // TODO: This assumes that file names will always have a dot in them + // and directory names never will; both assumptions are often + // right but in general wrong. + size_t extensionStart = directoryName.find_last_of('.'); + if(extensionStart != std::string::npos && extensionStart > lastSep) + { + directoryName.resize(lastSep); + } + } + else + { + directoryName.resize(lastSep); + } + + return directoryName; +} + +void CVolumeDirectory::SetDiskTypeWii() +{ + _dbg_assert_(DVDINTERFACE, m_diskHeader); + + m_diskHeader[0x18] = 0x5d; + m_diskHeader[0x19] = 0x1c; + m_diskHeader[0x1a] = 0x9e; + m_diskHeader[0x1b] = 0xa3; + memset(m_diskHeader + 0x1c, 0, 4); + + m_addressShift = 2; +} + +void CVolumeDirectory::SetDiskTypeGC() +{ + _dbg_assert_(DVDINTERFACE, m_diskHeader); + + memset(m_diskHeader + 0x18, 0, 4); + m_diskHeader[0x1c] = 0xc2; + m_diskHeader[0x1d] = 0x33; + m_diskHeader[0x1e] = 0x9f; + m_diskHeader[0x1f] = 0x3d; + + m_addressShift = 0; +} + +void CVolumeDirectory::BuildFST() +{ + if(m_FSTData) + { + delete m_FSTData; + } + + File::FSTEntry rootEntry; + + // read data from physical disk to rootEntry + u32 totalEntries = AddDirectoryEntries(m_rootDirectory, rootEntry) + 1; + + m_fstNameOffset = totalEntries * ENTRY_SIZE; // offset in FST nameTable + m_fstSize = m_fstNameOffset + m_totalNameSize; + m_FSTData = new u8[(u32)m_fstSize]; + + // 4 byte aligned start of data on disk + m_dataStartAddress = (FST_ADDRESS + m_fstSize + 3) & ~3; + u64 curDataAddress = m_dataStartAddress; + + u32 fstOffset = 0; // offset within FST data + u32 nameOffset = 0; // offset within name table + u32 rootOffset = 0; // offset of root of FST + + // write root entry + WriteEntryData(fstOffset, DIRECTORY_ENTRY, 0, 0, totalEntries); + + for(std::vector::iterator iter = rootEntry.children.begin(); iter != rootEntry.children.end(); ++iter) + { + WriteEntry(*iter, fstOffset, nameOffset, curDataAddress, rootOffset); + } + + // overflow check + _dbg_assert_(DVDINTERFACE, nameOffset == m_fstSize); + + // write FST size and location + _dbg_assert_(DVDINTERFACE, m_diskHeader); + Write32((u32)(FST_ADDRESS >> m_addressShift), 0x0424, m_diskHeader); + Write32((u32)m_fstSize, 0x0428, m_diskHeader); + Write32((u32)m_fstSize, 0x042c, m_diskHeader); +} + +void CVolumeDirectory::WriteToBuffer(u64 _SrcStartAddress, u64 _SrcLength, u8* _Src, + u64& _Address, u64& _Length, u8*& _pBuffer) const +{ + _dbg_assert_(DVDINTERFACE, _Address >= _SrcStartAddress); + + u64 srcOffset = _Address - _SrcStartAddress; + + if(srcOffset < _SrcLength) + { + u64 srcBytes = _SrcLength - srcOffset; + if(_Length < srcBytes) + srcBytes = _Length; + + memcpy(_pBuffer, _Src + srcOffset, (size_t)srcBytes); + + _Length -= srcBytes; + _pBuffer += srcBytes; + _Address += srcBytes; + } +} + +void CVolumeDirectory::PadToAddress(u64 _StartAddress, u64& _Address, u64& _Length, u8*& _pBuffer) const +{ + if(_StartAddress <= _Address) + return; + + u64 padBytes = _StartAddress - _Address; + if(padBytes > _Length) + padBytes = _Length; + + if(_Length > 0) + { + memset(_pBuffer, 0, (size_t)padBytes); + _Length -= padBytes; + _pBuffer += padBytes; + _Address += padBytes; + } +} + +void CVolumeDirectory::Write32(u32 data, u32 offset, u8* buffer) +{ + buffer[offset++] = (data >> 24); + buffer[offset++] = (data >> 16) & 0xff; + buffer[offset++] = (data >> 8) & 0xff; + buffer[offset] = (data) & 0xff; +} + +void CVolumeDirectory::WriteEntryData(u32& entryOffset, u8 type, u32 nameOffset, u64 dataOffset, u32 length) +{ + m_FSTData[entryOffset++] = type; + + m_FSTData[entryOffset++] = (nameOffset >> 16) & 0xff; + m_FSTData[entryOffset++] = (nameOffset >> 8) & 0xff; + m_FSTData[entryOffset++] = (nameOffset) & 0xff; + + Write32((u32)(dataOffset >> m_addressShift), entryOffset, m_FSTData); + entryOffset += 4; + + Write32((u32)length, entryOffset, m_FSTData); + entryOffset += 4; +} + +void CVolumeDirectory::WriteEntryName(u32& nameOffset, const std::string& name) +{ + strncpy((char*)(m_FSTData + nameOffset + m_fstNameOffset), name.c_str(), name.length() + 1); + + nameOffset += (name.length() + 1); +} + +void CVolumeDirectory::WriteEntry(const File::FSTEntry& entry, u32& fstOffset, u32& nameOffset, u64& dataOffset, u32 parentEntryNum) +{ + if(entry.isDirectory) + { + u32 myOffset = fstOffset; + u32 myEntryNum = myOffset / ENTRY_SIZE; + WriteEntryData(fstOffset, DIRECTORY_ENTRY, nameOffset, parentEntryNum, myEntryNum + entry.size + 1); + WriteEntryName(nameOffset, entry.virtualName); + + for(std::vector::const_iterator iter = entry.children.begin(); iter != entry.children.end(); ++iter) + { + WriteEntry(*iter, fstOffset, nameOffset, dataOffset, myEntryNum); + } + } + else + { + // put entry in FST + WriteEntryData(fstOffset, FILE_ENTRY, nameOffset, dataOffset, entry.size); + WriteEntryName(nameOffset, entry.virtualName); + + // write entry to virtual disk + _dbg_assert_(DVDINTERFACE, m_virtualDisk.find(dataOffset) == m_virtualDisk.end()); + m_virtualDisk.insert(make_pair(dataOffset, entry.physicalName)); + + // 4 byte aligned + dataOffset = (dataOffset + entry.size + 3) & ~3ULL; + } +} + +static u32 ComputeNameSize(const File::FSTEntry& parentEntry) +{ + u32 nameSize = 0; + const std::vector& children = parentEntry.children; + for (std::vector::const_iterator it = children.begin(); + it != children.end(); ++it) + { + const File::FSTEntry& entry = *it; + if (entry.isDirectory) + { + nameSize += ComputeNameSize(entry); + } + nameSize += entry.virtualName.length() + 1; + } + return nameSize; +} + +u32 CVolumeDirectory::AddDirectoryEntries(const std::string& _Directory, File::FSTEntry& parentEntry) +{ + u32 foundEntries = ScanDirectoryTree(_Directory, parentEntry); + m_totalNameSize += ComputeNameSize(parentEntry); + return foundEntries; +} + +} // namespace diff --git a/Source/Core/DiscIO/Src/VolumeGC.cpp b/Source/Core/DiscIO/Src/VolumeGC.cpp index b10319c7cb..638355a943 100644 --- a/Source/Core/DiscIO/Src/VolumeGC.cpp +++ b/Source/Core/DiscIO/Src/VolumeGC.cpp @@ -1,169 +1,169 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "stdafx.h" - -#include "VolumeGC.h" -#include "StringUtil.h" - -namespace DiscIO -{ -CVolumeGC::CVolumeGC(IBlobReader* _pReader) - : m_pReader(_pReader) -{} - -CVolumeGC::~CVolumeGC() -{ - delete m_pReader; -} - -bool CVolumeGC::Read(u64 _Offset, u64 _Length, u8* _pBuffer) const -{ - if (m_pReader == NULL) - return false; - return m_pReader->Read(_Offset, _Length, _pBuffer); -} - -std::string CVolumeGC::GetUniqueID() const -{ - static const std::string NO_UID("NO_UID"); - if (m_pReader == NULL) - return NO_UID; - - char id[6]; - if (!Read(0, sizeof(id), reinterpret_cast(id))) - { - PanicAlert("Failed to read unique ID from disc image"); - return NO_UID; - } - - return std::string(id, sizeof(id)); -} - -IVolume::ECountry CVolumeGC::GetCountry() const -{ - if (!m_pReader) - return COUNTRY_UNKNOWN; - - u8 CountryCode; - m_pReader->Read(3, 1, &CountryCode); - - ECountry country = COUNTRY_UNKNOWN; - - switch (CountryCode) - { - case 'S': - country = COUNTRY_EUROPE; - break; // PAL // <- that is shitty :) zelda demo disc - - case 'P': - country = COUNTRY_EUROPE; - break; // PAL - - case 'D': - country = COUNTRY_EUROPE; - break; // PAL - - case 'F': - country = COUNTRY_FRANCE; - break; // PAL - - case 'X': - country = COUNTRY_EUROPE; - break; // XIII <- uses X but is PAL - - case 'E': - country = COUNTRY_USA; - break; // USA - - case 'J': - country = COUNTRY_JAP; - break; // JAP - - case 'O': - country = COUNTRY_UNKNOWN; - break; // SDK - - default: - // PanicAlert(StringFromFormat("Unknown Country Code!").c_str()); - country = COUNTRY_UNKNOWN; - break; - } - - return(country); -} - -std::string CVolumeGC::GetMakerID() const -{ - if (m_pReader == NULL) - return false; - - char makerID[3]; - if (!Read(0x4, 0x2, (u8*)&makerID)) - return false; - makerID[2] = 0; - - return makerID; -} - -std::string CVolumeGC::GetName() const -{ - if (m_pReader == NULL) - return false; - - char name[128]; - if (!Read(0x20, 0x60, (u8*)&name)) - return false; - - return name; -} - -u32 CVolumeGC::GetFSTSize() const -{ - if (m_pReader == NULL) - return false; - - u32 size; - if (!Read(0x428, 0x4, (u8*)&size)) - return false; - - return Common::swap32(size); -} - -std::string CVolumeGC::GetApploaderDate() const -{ - if (m_pReader == NULL) - return false; - - char date[16]; - if (!Read(0x2440, 0x10, (u8*)&date)) - return false; - // Should be 0 already, but just in case - date[10] = 0; - - return date; -} - -u64 CVolumeGC::GetSize() const -{ - if (m_pReader) - return (size_t)m_pReader->GetDataSize(); - else - return 0; -} - -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "stdafx.h" + +#include "VolumeGC.h" +#include "StringUtil.h" + +namespace DiscIO +{ +CVolumeGC::CVolumeGC(IBlobReader* _pReader) + : m_pReader(_pReader) +{} + +CVolumeGC::~CVolumeGC() +{ + delete m_pReader; +} + +bool CVolumeGC::Read(u64 _Offset, u64 _Length, u8* _pBuffer) const +{ + if (m_pReader == NULL) + return false; + return m_pReader->Read(_Offset, _Length, _pBuffer); +} + +std::string CVolumeGC::GetUniqueID() const +{ + static const std::string NO_UID("NO_UID"); + if (m_pReader == NULL) + return NO_UID; + + char id[6]; + if (!Read(0, sizeof(id), reinterpret_cast(id))) + { + PanicAlert("Failed to read unique ID from disc image"); + return NO_UID; + } + + return std::string(id, sizeof(id)); +} + +IVolume::ECountry CVolumeGC::GetCountry() const +{ + if (!m_pReader) + return COUNTRY_UNKNOWN; + + u8 CountryCode; + m_pReader->Read(3, 1, &CountryCode); + + ECountry country = COUNTRY_UNKNOWN; + + switch (CountryCode) + { + case 'S': + country = COUNTRY_EUROPE; + break; // PAL // <- that is shitty :) zelda demo disc + + case 'P': + country = COUNTRY_EUROPE; + break; // PAL + + case 'D': + country = COUNTRY_EUROPE; + break; // PAL + + case 'F': + country = COUNTRY_FRANCE; + break; // PAL + + case 'X': + country = COUNTRY_EUROPE; + break; // XIII <- uses X but is PAL + + case 'E': + country = COUNTRY_USA; + break; // USA + + case 'J': + country = COUNTRY_JAP; + break; // JAP + + case 'O': + country = COUNTRY_UNKNOWN; + break; // SDK + + default: + // PanicAlert(StringFromFormat("Unknown Country Code!").c_str()); + country = COUNTRY_UNKNOWN; + break; + } + + return(country); +} + +std::string CVolumeGC::GetMakerID() const +{ + if (m_pReader == NULL) + return false; + + char makerID[3]; + if (!Read(0x4, 0x2, (u8*)&makerID)) + return false; + makerID[2] = 0; + + return makerID; +} + +std::string CVolumeGC::GetName() const +{ + if (m_pReader == NULL) + return false; + + char name[128]; + if (!Read(0x20, 0x60, (u8*)&name)) + return false; + + return name; +} + +u32 CVolumeGC::GetFSTSize() const +{ + if (m_pReader == NULL) + return false; + + u32 size; + if (!Read(0x428, 0x4, (u8*)&size)) + return false; + + return Common::swap32(size); +} + +std::string CVolumeGC::GetApploaderDate() const +{ + if (m_pReader == NULL) + return false; + + char date[16]; + if (!Read(0x2440, 0x10, (u8*)&date)) + return false; + // Should be 0 already, but just in case + date[10] = 0; + + return date; +} + +u64 CVolumeGC::GetSize() const +{ + if (m_pReader) + return (size_t)m_pReader->GetDataSize(); + else + return 0; +} + +} // namespace diff --git a/Source/Core/DiscIO/Src/VolumeWiiCrypted.cpp b/Source/Core/DiscIO/Src/VolumeWiiCrypted.cpp index 549664f9b0..d5e744bed2 100644 --- a/Source/Core/DiscIO/Src/VolumeWiiCrypted.cpp +++ b/Source/Core/DiscIO/Src/VolumeWiiCrypted.cpp @@ -1,254 +1,254 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "stdafx.h" - -#include "VolumeWiiCrypted.h" -#include "StringUtil.h" - -namespace DiscIO -{ -CVolumeWiiCrypted::CVolumeWiiCrypted(IBlobReader* _pReader, u64 _VolumeOffset, const unsigned char* _pVolumeKey) - : m_pReader(_pReader), - m_pBuffer(0), - m_VolumeOffset(_VolumeOffset), - m_LastDecryptedBlockOffset(-1) -{ - AES_set_decrypt_key(_pVolumeKey, 128, &m_AES_KEY); - m_pBuffer = new u8[0x8000]; -} - - -CVolumeWiiCrypted::~CVolumeWiiCrypted() -{ - delete m_pReader; // is this really our responsibility? - m_pReader = NULL; - delete[] m_pBuffer; - m_pBuffer = NULL; -} - - -bool -CVolumeWiiCrypted::Read(u64 _ReadOffset, u64 _Length, u8* _pBuffer) const -{ - if (m_pReader == NULL) - { - return(false); - } - - while (_Length > 0) - { - static unsigned char IV[16]; - - // math block offset - u64 Block = _ReadOffset / 0x7C00; - u64 Offset = _ReadOffset % 0x7C00; - - // read current block - if (!m_pReader->Read(m_VolumeOffset + Block * 0x8000, 0x8000, m_pBuffer)) - { - return(false); - } - - if (m_LastDecryptedBlockOffset != Block) - { - memcpy(IV, m_pBuffer + 0x3d0, 16); - AES_cbc_encrypt(m_pBuffer + 0x400, m_LastDecryptedBlock, 0x7C00, &m_AES_KEY, IV, AES_DECRYPT); - - m_LastDecryptedBlockOffset = Block; - } - - // copy the encrypted data - u64 MaxSizeToCopy = 0x7C00 - Offset; - u64 CopySize = (_Length > MaxSizeToCopy) ? MaxSizeToCopy : _Length; - memcpy(_pBuffer, &m_LastDecryptedBlock[Offset], (size_t)CopySize); - - // increase buffers - _Length -= CopySize; - _pBuffer += CopySize; - _ReadOffset += CopySize; - } - - return(true); -} - -std::string -CVolumeWiiCrypted::GetUniqueID() const -{ - if (m_pReader == NULL) - { - return(false); - } - - char ID[7]; - - if (!Read(0, 6, (u8*)ID)) - { - return(false); - } - - ID[6] = 0; - - return(ID); -} - - -IVolume::ECountry -CVolumeWiiCrypted::GetCountry() const -{ - if (!m_pReader) - { - return(COUNTRY_UNKNOWN); - } - - u8 CountryCode; - m_pReader->Read(3, 1, &CountryCode); - - ECountry country = COUNTRY_UNKNOWN; - - switch (CountryCode) - { - case 'S': - country = COUNTRY_EUROPE; - break; // PAL // <- that is shitty :) zelda demo disc - - case 'P': - country = COUNTRY_EUROPE; - break; // PAL - - case 'D': - country = COUNTRY_EUROPE; - break; // PAL - - case 'F': - country = COUNTRY_FRANCE; - break; // PAL - - case 'X': - country = COUNTRY_EUROPE; - break; // XIII <- uses X but is PAL rip - - case 'E': - country = COUNTRY_USA; - break; // USA - - case 'J': - country = COUNTRY_JAP; - break; // JAP - - case 'O': - country = COUNTRY_UNKNOWN; - break; // SDK - - default: - PanicAlert(StringFromFormat("Unknown Country Code!").c_str()); - break; - } - - return(country); -} - -std::string -CVolumeWiiCrypted::GetMakerID() const -{ - if (m_pReader == NULL) - { - return(false); - } - - char makerID[3]; - - if (!Read(0x4, 0x2, (u8*)&makerID)) - { - return(false); - } - - makerID[2] = 0; - - return(makerID); -} - -std::string -CVolumeWiiCrypted::GetName() const -{ - if (m_pReader == NULL) - { - return(false); - } - - char name[0xFF]; - - if (!Read(0x20, 0x60, (u8*)&name)) - { - return(false); - } - - return(name); -} - -u32 -CVolumeWiiCrypted::GetFSTSize() const -{ - if (m_pReader == NULL) - { - return(false); - } - - u32 size; - - if (!Read(0x428, 0x4, (u8*)&size)) - { - return(false); - } - - return(size); -} - -std::string -CVolumeWiiCrypted::GetApploaderDate() const -{ - if (m_pReader == NULL) - { - return(false); - } - - char date[16]; - - if (!Read(0x2440, 0x10, (u8*)&date)) - { - return(false); - } - - date[10] = 0; - - return(date); -} - - -u64 -CVolumeWiiCrypted::GetSize() const -{ - if (m_pReader) - { - return(m_pReader->GetDataSize()); - } - else - { - return(0); - } -} -} // namespace +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "stdafx.h" + +#include "VolumeWiiCrypted.h" +#include "StringUtil.h" + +namespace DiscIO +{ +CVolumeWiiCrypted::CVolumeWiiCrypted(IBlobReader* _pReader, u64 _VolumeOffset, const unsigned char* _pVolumeKey) + : m_pReader(_pReader), + m_pBuffer(0), + m_VolumeOffset(_VolumeOffset), + m_LastDecryptedBlockOffset(-1) +{ + AES_set_decrypt_key(_pVolumeKey, 128, &m_AES_KEY); + m_pBuffer = new u8[0x8000]; +} + + +CVolumeWiiCrypted::~CVolumeWiiCrypted() +{ + delete m_pReader; // is this really our responsibility? + m_pReader = NULL; + delete[] m_pBuffer; + m_pBuffer = NULL; +} + + +bool +CVolumeWiiCrypted::Read(u64 _ReadOffset, u64 _Length, u8* _pBuffer) const +{ + if (m_pReader == NULL) + { + return(false); + } + + while (_Length > 0) + { + static unsigned char IV[16]; + + // math block offset + u64 Block = _ReadOffset / 0x7C00; + u64 Offset = _ReadOffset % 0x7C00; + + // read current block + if (!m_pReader->Read(m_VolumeOffset + Block * 0x8000, 0x8000, m_pBuffer)) + { + return(false); + } + + if (m_LastDecryptedBlockOffset != Block) + { + memcpy(IV, m_pBuffer + 0x3d0, 16); + AES_cbc_encrypt(m_pBuffer + 0x400, m_LastDecryptedBlock, 0x7C00, &m_AES_KEY, IV, AES_DECRYPT); + + m_LastDecryptedBlockOffset = Block; + } + + // copy the encrypted data + u64 MaxSizeToCopy = 0x7C00 - Offset; + u64 CopySize = (_Length > MaxSizeToCopy) ? MaxSizeToCopy : _Length; + memcpy(_pBuffer, &m_LastDecryptedBlock[Offset], (size_t)CopySize); + + // increase buffers + _Length -= CopySize; + _pBuffer += CopySize; + _ReadOffset += CopySize; + } + + return(true); +} + +std::string +CVolumeWiiCrypted::GetUniqueID() const +{ + if (m_pReader == NULL) + { + return(false); + } + + char ID[7]; + + if (!Read(0, 6, (u8*)ID)) + { + return(false); + } + + ID[6] = 0; + + return(ID); +} + + +IVolume::ECountry +CVolumeWiiCrypted::GetCountry() const +{ + if (!m_pReader) + { + return(COUNTRY_UNKNOWN); + } + + u8 CountryCode; + m_pReader->Read(3, 1, &CountryCode); + + ECountry country = COUNTRY_UNKNOWN; + + switch (CountryCode) + { + case 'S': + country = COUNTRY_EUROPE; + break; // PAL // <- that is shitty :) zelda demo disc + + case 'P': + country = COUNTRY_EUROPE; + break; // PAL + + case 'D': + country = COUNTRY_EUROPE; + break; // PAL + + case 'F': + country = COUNTRY_FRANCE; + break; // PAL + + case 'X': + country = COUNTRY_EUROPE; + break; // XIII <- uses X but is PAL rip + + case 'E': + country = COUNTRY_USA; + break; // USA + + case 'J': + country = COUNTRY_JAP; + break; // JAP + + case 'O': + country = COUNTRY_UNKNOWN; + break; // SDK + + default: + PanicAlert(StringFromFormat("Unknown Country Code!").c_str()); + break; + } + + return(country); +} + +std::string +CVolumeWiiCrypted::GetMakerID() const +{ + if (m_pReader == NULL) + { + return(false); + } + + char makerID[3]; + + if (!Read(0x4, 0x2, (u8*)&makerID)) + { + return(false); + } + + makerID[2] = 0; + + return(makerID); +} + +std::string +CVolumeWiiCrypted::GetName() const +{ + if (m_pReader == NULL) + { + return(false); + } + + char name[0xFF]; + + if (!Read(0x20, 0x60, (u8*)&name)) + { + return(false); + } + + return(name); +} + +u32 +CVolumeWiiCrypted::GetFSTSize() const +{ + if (m_pReader == NULL) + { + return(false); + } + + u32 size; + + if (!Read(0x428, 0x4, (u8*)&size)) + { + return(false); + } + + return(size); +} + +std::string +CVolumeWiiCrypted::GetApploaderDate() const +{ + if (m_pReader == NULL) + { + return(false); + } + + char date[16]; + + if (!Read(0x2440, 0x10, (u8*)&date)) + { + return(false); + } + + date[10] = 0; + + return(date); +} + + +u64 +CVolumeWiiCrypted::GetSize() const +{ + if (m_pReader) + { + return(m_pReader->GetDataSize()); + } + else + { + return(0); + } +} +} // namespace diff --git a/Source/Core/DiscIO/Src/stdafx.cpp b/Source/Core/DiscIO/Src/stdafx.cpp index 80a57737ae..903e31a1fc 100644 --- a/Source/Core/DiscIO/Src/stdafx.cpp +++ b/Source/Core/DiscIO/Src/stdafx.cpp @@ -1,21 +1,21 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "stdafx.h" - -// TODO: reference any additional headers you need in STDAFX.H -// and not in this file +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "stdafx.h" + +// TODO: reference any additional headers you need in STDAFX.H +// and not in this file diff --git a/Source/Core/DolphinWX/Src/AboutDolphin.cpp b/Source/Core/DolphinWX/Src/AboutDolphin.cpp index 60d7c8e811..503e9f18f4 100644 --- a/Source/Core/DolphinWX/Src/AboutDolphin.cpp +++ b/Source/Core/DolphinWX/Src/AboutDolphin.cpp @@ -1,93 +1,93 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ -#include "AboutDolphin.h" -#include "svnrev.h" -#include "CPUDetect.h" -#include "../resources/dolphin_logo.cpp" - - -BEGIN_EVENT_TABLE(AboutDolphin, wxDialog) - EVT_CLOSE(AboutDolphin::OnClose) - EVT_BUTTON(ID_CLOSE, AboutDolphin::CloseClick) -END_EVENT_TABLE() - -AboutDolphin::AboutDolphin(wxWindow *parent, wxWindowID id, const wxString &title, const wxPoint &position, const wxSize& size, long style) -: wxDialog(parent, id, title, position, size, style) -{ - CreateGUIControls(); -} - -AboutDolphin::~AboutDolphin() -{ -} - -void AboutDolphin::CreateGUIControls() -{ - m_Close = new wxButton(this, ID_CLOSE, wxT("Close"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - //miloszwl@miloszwl.com (miloszwl.deviantart.com) - - wxMemoryInputStream istream(dolphin_logo_png, sizeof dolphin_logo_png); - wxImage iDolphinLogo(istream, wxBITMAP_TYPE_PNG); - DolphinLogo = new wxBitmap(iDolphinLogo); - sbDolphinLogo = new wxStaticBitmap(this, ID_LOGO, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0); - sbDolphinLogo->SetBitmap(*DolphinLogo); - std::string Text = std::string("Dolphin SVN revision ") + SVN_REV_STR + "\nCPU: " + cpu_info.Summarize() + "\n\n" // Maybe add OS/arch info too? - "Copyright (c) by F|RES & ector 2003-2008\n" - "Additional code by zerofrog, yaz0r, Schibo, Costis, JPeterson, etc etc...\n\n" - "Greets to Azimer, Caustik, Costis, Desktopman, EFX, Epsilon, Falcon4Ever, Hotquik, Jazzmin, mamedevs, Masken, Martin64, or9, " - " tmbinc, vEX, Zezu, Zilmar, and everyone we forget.\n\n" - "Special thanks to Costis, CrowTRobo, Titanik, or9 and Hotquik for their reverse engineering and docs/demos.\n\n" - "Big thanks to Gilles Mouchard whose Microlib PPC emulator gave our development a kickstart.\n\n" - "Thanks to Frank Wille for his PowerPC disassembler, which or9 and we modified to include Gekko specifics.\n\n" - "Thanks to Shinji Chiba for his GC ADPCM decoder.\n\n" - "We are not affiliated with Nintendo in any way. Gamecube and Wii are trademarks of Nintendo.\n" - "The emulator is for educational purposes only and should not be used to play games you do not legally own.\n\n"; - Message = new wxStaticText(this, ID_MESSAGE, - wxString::FromAscii(Text.c_str()), - wxDefaultPosition, wxDefaultSize, 0); - Message->Wrap(this->GetSize().GetWidth()); - - sMain = new wxBoxSizer(wxVERTICAL); - sMainHor = new wxBoxSizer(wxHORIZONTAL); - sMainHor->Add(sbDolphinLogo); - - sInfo = new wxBoxSizer(wxVERTICAL); - sInfo->Add(Message, 1, wxEXPAND|wxALL, 5); - sMainHor->Add(sInfo); - sMain->Add(sMainHor, 1, wxEXPAND); - - sButtons = new wxBoxSizer(wxHORIZONTAL); - sButtons->Add(0, 0, 1, wxEXPAND, 5); - sButtons->Add(m_Close, 0, wxALL, 5); - sMain->Add(sButtons, 0, wxEXPAND); - - this->SetSizer(sMain); - sMain->Layout(); - - CenterOnParent(); - Fit(); -} - -void AboutDolphin::OnClose(wxCloseEvent& WXUNUSED (event)) -{ - Destroy(); -} - -void AboutDolphin::CloseClick(wxCommandEvent& WXUNUSED (event)) -{ - Close(); -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ +#include "AboutDolphin.h" +#include "svnrev.h" +#include "CPUDetect.h" +#include "../resources/dolphin_logo.cpp" + + +BEGIN_EVENT_TABLE(AboutDolphin, wxDialog) + EVT_CLOSE(AboutDolphin::OnClose) + EVT_BUTTON(ID_CLOSE, AboutDolphin::CloseClick) +END_EVENT_TABLE() + +AboutDolphin::AboutDolphin(wxWindow *parent, wxWindowID id, const wxString &title, const wxPoint &position, const wxSize& size, long style) +: wxDialog(parent, id, title, position, size, style) +{ + CreateGUIControls(); +} + +AboutDolphin::~AboutDolphin() +{ +} + +void AboutDolphin::CreateGUIControls() +{ + m_Close = new wxButton(this, ID_CLOSE, wxT("Close"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + //miloszwl@miloszwl.com (miloszwl.deviantart.com) + + wxMemoryInputStream istream(dolphin_logo_png, sizeof dolphin_logo_png); + wxImage iDolphinLogo(istream, wxBITMAP_TYPE_PNG); + DolphinLogo = new wxBitmap(iDolphinLogo); + sbDolphinLogo = new wxStaticBitmap(this, ID_LOGO, wxNullBitmap, wxDefaultPosition, wxDefaultSize, 0); + sbDolphinLogo->SetBitmap(*DolphinLogo); + std::string Text = std::string("Dolphin SVN revision ") + SVN_REV_STR + "\nCPU: " + cpu_info.Summarize() + "\n\n" // Maybe add OS/arch info too? + "Copyright (c) by F|RES & ector 2003-2008\n" + "Additional code by zerofrog, yaz0r, Schibo, Costis, JPeterson, etc etc...\n\n" + "Greets to Azimer, Caustik, Costis, Desktopman, EFX, Epsilon, Falcon4Ever, Hotquik, Jazzmin, mamedevs, Masken, Martin64, or9, " + " tmbinc, vEX, Zezu, Zilmar, and everyone we forget.\n\n" + "Special thanks to Costis, CrowTRobo, Titanik, or9 and Hotquik for their reverse engineering and docs/demos.\n\n" + "Big thanks to Gilles Mouchard whose Microlib PPC emulator gave our development a kickstart.\n\n" + "Thanks to Frank Wille for his PowerPC disassembler, which or9 and we modified to include Gekko specifics.\n\n" + "Thanks to Shinji Chiba for his GC ADPCM decoder.\n\n" + "We are not affiliated with Nintendo in any way. Gamecube and Wii are trademarks of Nintendo.\n" + "The emulator is for educational purposes only and should not be used to play games you do not legally own.\n\n"; + Message = new wxStaticText(this, ID_MESSAGE, + wxString::FromAscii(Text.c_str()), + wxDefaultPosition, wxDefaultSize, 0); + Message->Wrap(this->GetSize().GetWidth()); + + sMain = new wxBoxSizer(wxVERTICAL); + sMainHor = new wxBoxSizer(wxHORIZONTAL); + sMainHor->Add(sbDolphinLogo); + + sInfo = new wxBoxSizer(wxVERTICAL); + sInfo->Add(Message, 1, wxEXPAND|wxALL, 5); + sMainHor->Add(sInfo); + sMain->Add(sMainHor, 1, wxEXPAND); + + sButtons = new wxBoxSizer(wxHORIZONTAL); + sButtons->Add(0, 0, 1, wxEXPAND, 5); + sButtons->Add(m_Close, 0, wxALL, 5); + sMain->Add(sButtons, 0, wxEXPAND); + + this->SetSizer(sMain); + sMain->Layout(); + + CenterOnParent(); + Fit(); +} + +void AboutDolphin::OnClose(wxCloseEvent& WXUNUSED (event)) +{ + Destroy(); +} + +void AboutDolphin::CloseClick(wxCommandEvent& WXUNUSED (event)) +{ + Close(); +} diff --git a/Source/Core/DolphinWX/Src/BootManager.cpp b/Source/Core/DolphinWX/Src/BootManager.cpp index 51c6b437bc..55c7d50436 100644 --- a/Source/Core/DolphinWX/Src/BootManager.cpp +++ b/Source/Core/DolphinWX/Src/BootManager.cpp @@ -1,181 +1,181 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include -#include - -#include "Globals.h" -#include "Common.h" -#include "IniFile.h" -#include "BootManager.h" -#include "ISOFile.h" -#include "Volume.h" -#include "VolumeCreator.h" -#include "Config.h" -#include "Core.h" -#if defined(HAVE_WX) && HAVE_WX -#include "ConfigMain.h" -#include "Frame.h" -#include "CodeWindow.h" -#endif - -static std::string s_DataBasePath_EUR = "Data_EUR"; -static std::string s_DataBasePath_USA = "Data_USA"; -static std::string s_DataBasePath_JAP = "Data_JAP"; - -#if defined(HAVE_WX) && HAVE_WX -extern CFrame* main_frame; -extern CCodeWindow* g_pCodeWindow; -#endif - -namespace BootManager -{ -#ifdef _WIN32 -extern "C" HINSTANCE wxGetInstance(); -#endif - -bool BootCore(const std::string& _rFilename) -{ - SCoreStartupParameter StartUp = SConfig::GetInstance().m_LocalCoreStartupParameter; - -#if defined(HAVE_WX) && HAVE_WX - if (g_pCodeWindow) - { -// StartUp.bUseDualCore = code_frame->UseDualCore(); - StartUp.bUseJIT = !g_pCodeWindow->UseInterpreter(); - StartUp.bAutomaticStart = g_pCodeWindow->AutomaticStart(); - } - else - { -// StartUp.bUseDualCore = false; -// StartUp.bUseJIT = true; - } -#endif - StartUp.m_BootType = SCoreStartupParameter::BOOT_ISO; - StartUp.m_strFilename = _rFilename; - SConfig::GetInstance().m_LastFilename = StartUp.m_strFilename; - StartUp.bRunCompareClient = false; - StartUp.bRunCompareServer = false; -#if defined(HAVE_WX) && HAVE_WX - StartUp.bEnableDebugging = g_pCodeWindow ? true : false; // RUNNING_DEBUG -#endif - std::string BaseDataPath; -#ifdef _WIN32 - StartUp.hInstance = wxGetInstance(); -#ifdef _M_X64 - StartUp.bUseFastMem = true; -#endif -#endif - - if ( !StartUp.AutoSetup(SCoreStartupParameter::BOOT_DEFAULT) ) - { - return false; - } - - // ------------------------------------------------ - // Load game specific settings - // ---------------- - IniFile ini; - std::string unique_id = StartUp.GetUniqueID(); - if (unique_id.size() == 6 && ini.Load((FULL_GAMECONFIG_DIR + unique_id + ".ini").c_str())) - { - // ------------------------------------------------ - // General settings - // ---------------- - ini.Get("Core", "UseDualCore", &StartUp.bUseDualCore, StartUp.bUseDualCore); - ini.Get("Core", "SkipIdle", &StartUp.bSkipIdle, StartUp.bSkipIdle); - ini.Get("Core", "OptimizeQuantizers", &StartUp.bOptimizeQuantizers, StartUp.bOptimizeQuantizers); - - - // ------------------------------------------------ - // Update SYSCONF with game specific settings - // ---------------- - bool bEnableProgressiveScan, bEnableWideScreen; - //bRefreshList = false; - FILE* pStream; // file handle - u8 m_SYSCONF[0x4000]; // SYSCONF file - u16 IPL_PGS = 0x17CC; // pregressive scan - u16 IPL_AR = 0x04D9; // widescreen - std::string FullSYSCONFPath = FULL_WII_USER_DIR "shared2/sys/SYSCONF"; - - // Load Wii SYSCONF - pStream = NULL; - pStream = fopen(FullSYSCONFPath.c_str(), "rb"); - if (pStream != NULL) - { - fread(m_SYSCONF, 1, 0x4000, pStream); - fclose(pStream); - - //wxMessageBox(wxString::Format(": %02x", m_SYSCONF[IPL_AR])); - - ini.Get("Core", "EnableProgressiveScan", &bEnableProgressiveScan, (int)m_SYSCONF[IPL_PGS]); - ini.Get("Core", "EnableWideScreen", &bEnableWideScreen, (int)m_SYSCONF[IPL_AR]); - - m_SYSCONF[IPL_PGS] = bEnableProgressiveScan; - m_SYSCONF[IPL_AR] = bEnableWideScreen; - - //wxMessageBox(wxString::Format(": %02x", m_SYSCONF[IPL_AR])); - - // Enable custom Wii SYSCONF settings by saving the file to shared2 - pStream = NULL; - pStream = fopen(FullSYSCONFPath.c_str(), "wb"); - if (pStream != NULL) - { - fwrite(m_SYSCONF, 1, 0x4000, pStream); - fclose(pStream); - } - else - { - PanicAlert("Could not write to %s", FullSYSCONFPath.c_str()); - } - - } - else - { - PanicAlert("Could not read %s", FullSYSCONFPath.c_str()); - } - // --------- - } - // --------- -#if defined(HAVE_WX) && HAVE_WX - if(main_frame) - StartUp.hMainWindow = main_frame->GetRenderHandle(); -#endif - // init the core - if (!Core::Init(StartUp)) - { - PanicAlert("Couldn't init the core.\nCheck your configuration."); - return(false); - } - -#if defined(HAVE_WX) && HAVE_WX - // Boot to pause or not - Core::SetState((g_pCodeWindow && !StartUp.bAutomaticStart) - ? Core::CORE_PAUSE : Core::CORE_RUN); -#else - Core::SetState(Core::CORE_RUN); -#endif - return(true); -} - - -void Stop() -{ - Core::Stop(); -} -} // namespace - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include + +#include "Globals.h" +#include "Common.h" +#include "IniFile.h" +#include "BootManager.h" +#include "ISOFile.h" +#include "Volume.h" +#include "VolumeCreator.h" +#include "Config.h" +#include "Core.h" +#if defined(HAVE_WX) && HAVE_WX +#include "ConfigMain.h" +#include "Frame.h" +#include "CodeWindow.h" +#endif + +static std::string s_DataBasePath_EUR = "Data_EUR"; +static std::string s_DataBasePath_USA = "Data_USA"; +static std::string s_DataBasePath_JAP = "Data_JAP"; + +#if defined(HAVE_WX) && HAVE_WX +extern CFrame* main_frame; +extern CCodeWindow* g_pCodeWindow; +#endif + +namespace BootManager +{ +#ifdef _WIN32 +extern "C" HINSTANCE wxGetInstance(); +#endif + +bool BootCore(const std::string& _rFilename) +{ + SCoreStartupParameter StartUp = SConfig::GetInstance().m_LocalCoreStartupParameter; + +#if defined(HAVE_WX) && HAVE_WX + if (g_pCodeWindow) + { +// StartUp.bUseDualCore = code_frame->UseDualCore(); + StartUp.bUseJIT = !g_pCodeWindow->UseInterpreter(); + StartUp.bAutomaticStart = g_pCodeWindow->AutomaticStart(); + } + else + { +// StartUp.bUseDualCore = false; +// StartUp.bUseJIT = true; + } +#endif + StartUp.m_BootType = SCoreStartupParameter::BOOT_ISO; + StartUp.m_strFilename = _rFilename; + SConfig::GetInstance().m_LastFilename = StartUp.m_strFilename; + StartUp.bRunCompareClient = false; + StartUp.bRunCompareServer = false; +#if defined(HAVE_WX) && HAVE_WX + StartUp.bEnableDebugging = g_pCodeWindow ? true : false; // RUNNING_DEBUG +#endif + std::string BaseDataPath; +#ifdef _WIN32 + StartUp.hInstance = wxGetInstance(); +#ifdef _M_X64 + StartUp.bUseFastMem = true; +#endif +#endif + + if ( !StartUp.AutoSetup(SCoreStartupParameter::BOOT_DEFAULT) ) + { + return false; + } + + // ------------------------------------------------ + // Load game specific settings + // ---------------- + IniFile ini; + std::string unique_id = StartUp.GetUniqueID(); + if (unique_id.size() == 6 && ini.Load((FULL_GAMECONFIG_DIR + unique_id + ".ini").c_str())) + { + // ------------------------------------------------ + // General settings + // ---------------- + ini.Get("Core", "UseDualCore", &StartUp.bUseDualCore, StartUp.bUseDualCore); + ini.Get("Core", "SkipIdle", &StartUp.bSkipIdle, StartUp.bSkipIdle); + ini.Get("Core", "OptimizeQuantizers", &StartUp.bOptimizeQuantizers, StartUp.bOptimizeQuantizers); + + + // ------------------------------------------------ + // Update SYSCONF with game specific settings + // ---------------- + bool bEnableProgressiveScan, bEnableWideScreen; + //bRefreshList = false; + FILE* pStream; // file handle + u8 m_SYSCONF[0x4000]; // SYSCONF file + u16 IPL_PGS = 0x17CC; // pregressive scan + u16 IPL_AR = 0x04D9; // widescreen + std::string FullSYSCONFPath = FULL_WII_USER_DIR "shared2/sys/SYSCONF"; + + // Load Wii SYSCONF + pStream = NULL; + pStream = fopen(FullSYSCONFPath.c_str(), "rb"); + if (pStream != NULL) + { + fread(m_SYSCONF, 1, 0x4000, pStream); + fclose(pStream); + + //wxMessageBox(wxString::Format(": %02x", m_SYSCONF[IPL_AR])); + + ini.Get("Core", "EnableProgressiveScan", &bEnableProgressiveScan, (int)m_SYSCONF[IPL_PGS]); + ini.Get("Core", "EnableWideScreen", &bEnableWideScreen, (int)m_SYSCONF[IPL_AR]); + + m_SYSCONF[IPL_PGS] = bEnableProgressiveScan; + m_SYSCONF[IPL_AR] = bEnableWideScreen; + + //wxMessageBox(wxString::Format(": %02x", m_SYSCONF[IPL_AR])); + + // Enable custom Wii SYSCONF settings by saving the file to shared2 + pStream = NULL; + pStream = fopen(FullSYSCONFPath.c_str(), "wb"); + if (pStream != NULL) + { + fwrite(m_SYSCONF, 1, 0x4000, pStream); + fclose(pStream); + } + else + { + PanicAlert("Could not write to %s", FullSYSCONFPath.c_str()); + } + + } + else + { + PanicAlert("Could not read %s", FullSYSCONFPath.c_str()); + } + // --------- + } + // --------- +#if defined(HAVE_WX) && HAVE_WX + if(main_frame) + StartUp.hMainWindow = main_frame->GetRenderHandle(); +#endif + // init the core + if (!Core::Init(StartUp)) + { + PanicAlert("Couldn't init the core.\nCheck your configuration."); + return(false); + } + +#if defined(HAVE_WX) && HAVE_WX + // Boot to pause or not + Core::SetState((g_pCodeWindow && !StartUp.bAutomaticStart) + ? Core::CORE_PAUSE : Core::CORE_RUN); +#else + Core::SetState(Core::CORE_RUN); +#endif + return(true); +} + + +void Stop() +{ + Core::Stop(); +} +} // namespace + diff --git a/Source/Core/DolphinWX/Src/CheatsWindow.cpp b/Source/Core/DolphinWX/Src/CheatsWindow.cpp index 102f8446fc..4085f9f642 100644 --- a/Source/Core/DolphinWX/Src/CheatsWindow.cpp +++ b/Source/Core/DolphinWX/Src/CheatsWindow.cpp @@ -1,178 +1,178 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Globals.h" -#include "CheatsWindow.h" -#include "ActionReplay.h" -#include "Core.h" - -BEGIN_EVENT_TABLE(wxCheatsWindow, wxWindow) - EVT_SIZE( wxCheatsWindow::OnEvent_Window_Resize) - EVT_CLOSE( wxCheatsWindow::OnEvent_Window_Close) - EVT_BUTTON(ID_BUTTON_CLOSE, wxCheatsWindow::OnEvent_ButtonClose_Press) - EVT_LISTBOX(ID_CHECKLISTBOX_CHEATSLIST, wxCheatsWindow::OnEvent_CheatsList_ItemSelected) - EVT_CHECKLISTBOX(ID_CHECKLISTBOX_CHEATSLIST, wxCheatsWindow::OnEvent_CheatsList_ItemToggled) - EVT_BUTTON(ID_BUTTON_APPLYCODES, wxCheatsWindow::OnEvent_ButtonUpdateCodes_Press) -END_EVENT_TABLE() - -wxCheatsWindow::wxCheatsWindow(wxFrame* parent, const wxPoint& pos, const wxSize& size) : wxFrame(parent, wxID_ANY, _T("Action Replay"), pos, size, wxCHEATSWINDOW_STYLE) -{ - // Create the GUI controls - Init_ChildControls(); - - // Setup Window - SetBackgroundColour(m_Notebook_Main->GetBackgroundColour()); - SetSize(size); - SetPosition(pos); - - // Load Data - Load_ARCodes(); - - Layout(); - Show(); -} - -wxCheatsWindow::~wxCheatsWindow() -{ - // On Disposal -} - -void wxCheatsWindow::Init_ChildControls() -{ - // Main Notebook - m_Notebook_Main = new wxNotebook(this, ID_NOTEBOOK_MAIN, wxDefaultPosition, wxDefaultSize); - // --- Tabs --- - // $ Cheats List Tab - m_Tab_Cheats = new wxPanel(m_Notebook_Main, ID_TAB_CHEATS, wxDefaultPosition, wxDefaultSize); - m_CheckListBox_CheatsList = new wxCheckListBox(m_Tab_Cheats, ID_CHECKLISTBOX_CHEATSLIST, wxDefaultPosition, wxSize(300, 0), m_CheatStringList, wxLB_HSCROLL, wxDefaultValidator); - m_Label_Codename = new wxStaticText(m_Tab_Cheats, ID_LABEL_CODENAME, _T("Name: "), wxDefaultPosition, wxDefaultSize); - m_GroupBox_Info = new wxStaticBox(m_Tab_Cheats, ID_GROUPBOX_INFO, _T("Code Info"), wxDefaultPosition, wxDefaultSize); - m_Button_ApplyCodes = new wxButton(m_Tab_Cheats, ID_BUTTON_APPLYCODES, _T("Apply Changes"), wxDefaultPosition, wxDefaultSize); - m_Label_NumCodes = new wxStaticText(m_Tab_Cheats, ID_LABEL_NUMCODES, _T("Number Of Codes: "), wxDefaultPosition, wxDefaultSize); - m_ListBox_CodesList = new wxListBox(m_Tab_Cheats, ID_LISTBOX_CODESLIST, wxDefaultPosition, wxSize(120, 150), 0, 0, wxLB_HSCROLL); - - wxBoxSizer* sCheatsListH = new wxBoxSizer(wxHORIZONTAL); - sCheatsListH->Add(m_CheckListBox_CheatsList, 1, wxALL|wxEXPAND, 5); - - wxStaticBoxSizer* sGroupBoxInfo = new wxStaticBoxSizer(m_GroupBox_Info, wxVERTICAL); - sGroupBoxInfo->Add(m_Label_Codename, 0, wxALL, 5); - sGroupBoxInfo->Add(m_Label_NumCodes, 0, wxALL, 5); - sGroupBoxInfo->Add(m_ListBox_CodesList, 0, wxALL, 5); - - wxBoxSizer* sB1 = new wxBoxSizer(wxVERTICAL); - sB1->Add(m_Button_ApplyCodes, 0, wxALL, 5); - sB1->Add(sGroupBoxInfo, 0, wxALL, 5); - - m_Sizer_TabCheats = new wxGridBagSizer(); - m_Sizer_TabCheats->AddGrowableRow(0); - m_Sizer_TabCheats->Add(sCheatsListH, wxGBPosition(0, 0), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); - m_Sizer_TabCheats->Add(sB1, wxGBPosition(0, 1), wxGBSpan(1, 1), wxALIGN_LEFT|wxALL, 5); - - m_Tab_Cheats->SetSizer(m_Sizer_TabCheats); - m_Tab_Cheats->Layout(); - - - // $ Log Tab - m_Tab_Log = new wxPanel(m_Notebook_Main, ID_TAB_LOG, wxDefaultPosition, wxDefaultSize); - - // Add Tabs to Notebook - m_Notebook_Main->AddPage(m_Tab_Cheats, _T("Codes List")); - m_Notebook_Main->AddPage(m_Tab_Log, _T("Logging")); - - // Button Strip - m_Button_Close = new wxButton(this, ID_BUTTON_CLOSE, _T("Close"), wxDefaultPosition, wxDefaultSize); - wxBoxSizer* sButtons = new wxBoxSizer(wxHORIZONTAL); - sButtons->Add(m_Button_Close, 0, wxALL, 5); - - wxBoxSizer* sMain = new wxBoxSizer(wxVERTICAL); - sMain->Add(m_Notebook_Main, 1, wxEXPAND|wxALL, 5); - sMain->Add(sButtons, 0, wxALL, 5); - SetSizer(sMain); - Layout(); - - Fit(); -} -void wxCheatsWindow::OnEvent_Window_Resize(wxSizeEvent& WXUNUSED (event)) -{ - Layout(); -} -void wxCheatsWindow::OnEvent_ButtonClose_Press(wxCommandEvent& WXUNUSED (event)) -{ - Destroy(); -} -void wxCheatsWindow::OnEvent_Window_Close(wxCloseEvent& WXUNUSED (event)) -{ - Destroy(); -} -void wxCheatsWindow::Load_ARCodes() -{ - indexList.clear(); - size_t size = ActionReplay_GetCodeListSize(); - for (size_t i = 0; i < size; i++) - { - ARCode code = ActionReplay_GetARCode(i); - ARCodeIndex ind; - u32 index = m_CheckListBox_CheatsList->Append(wxString::FromAscii(code.name.c_str())); - m_CheckListBox_CheatsList->Check(index, code.active); - ind.index = i; - ind.uiIndex = index; - indexList.push_back(ind); - } -} -void wxCheatsWindow::OnEvent_CheatsList_ItemSelected(wxCommandEvent& WXUNUSED (event)) -{ - int index = m_CheckListBox_CheatsList->GetSelection(); - for (size_t i = 0; i < indexList.size(); i++) - { - if ((int)indexList[i].uiIndex == index) - { - ARCode code = ActionReplay_GetARCode(i); - m_Label_Codename->SetLabel(wxT("Name: ") + wxString::FromAscii(code.name.c_str())); - char text[CHAR_MAX]; - char* numcodes = text; - sprintf(numcodes, "Number of Codes: %i", code.ops.size()); - m_Label_NumCodes->SetLabel(wxString::FromAscii(numcodes)); - m_ListBox_CodesList->Clear(); - for (size_t j = 0; j < code.ops.size(); j++) - { - char text2[CHAR_MAX]; - char* ops = text2; - sprintf(ops, "%08x %08x", code.ops[j].cmd_addr, code.ops[j].value); - m_ListBox_CodesList->Append(wxString::FromAscii(ops)); - } - } - } - m_Sizer_TabCheats->Layout(); -} -void wxCheatsWindow::OnEvent_CheatsList_ItemToggled(wxCommandEvent& WXUNUSED (event)) -{ - int index = m_CheckListBox_CheatsList->GetSelection(); - for (size_t i = 0; i < indexList.size(); i++) - { - if ((int)indexList[i].uiIndex == index) - { - ActionReplay_SetARCode_IsActive(m_CheckListBox_CheatsList->IsChecked(index), indexList[i].index); - } - } -} -void wxCheatsWindow::OnEvent_ButtonUpdateCodes_Press(wxCommandEvent& WXUNUSED (event)) -{ - for (size_t i = 0; i < indexList.size(); i++) - { - ActionReplay_SetARCode_IsActive(m_CheckListBox_CheatsList->IsChecked(indexList[i].uiIndex), indexList[i].index); - } -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Globals.h" +#include "CheatsWindow.h" +#include "ActionReplay.h" +#include "Core.h" + +BEGIN_EVENT_TABLE(wxCheatsWindow, wxWindow) + EVT_SIZE( wxCheatsWindow::OnEvent_Window_Resize) + EVT_CLOSE( wxCheatsWindow::OnEvent_Window_Close) + EVT_BUTTON(ID_BUTTON_CLOSE, wxCheatsWindow::OnEvent_ButtonClose_Press) + EVT_LISTBOX(ID_CHECKLISTBOX_CHEATSLIST, wxCheatsWindow::OnEvent_CheatsList_ItemSelected) + EVT_CHECKLISTBOX(ID_CHECKLISTBOX_CHEATSLIST, wxCheatsWindow::OnEvent_CheatsList_ItemToggled) + EVT_BUTTON(ID_BUTTON_APPLYCODES, wxCheatsWindow::OnEvent_ButtonUpdateCodes_Press) +END_EVENT_TABLE() + +wxCheatsWindow::wxCheatsWindow(wxFrame* parent, const wxPoint& pos, const wxSize& size) : wxFrame(parent, wxID_ANY, _T("Action Replay"), pos, size, wxCHEATSWINDOW_STYLE) +{ + // Create the GUI controls + Init_ChildControls(); + + // Setup Window + SetBackgroundColour(m_Notebook_Main->GetBackgroundColour()); + SetSize(size); + SetPosition(pos); + + // Load Data + Load_ARCodes(); + + Layout(); + Show(); +} + +wxCheatsWindow::~wxCheatsWindow() +{ + // On Disposal +} + +void wxCheatsWindow::Init_ChildControls() +{ + // Main Notebook + m_Notebook_Main = new wxNotebook(this, ID_NOTEBOOK_MAIN, wxDefaultPosition, wxDefaultSize); + // --- Tabs --- + // $ Cheats List Tab + m_Tab_Cheats = new wxPanel(m_Notebook_Main, ID_TAB_CHEATS, wxDefaultPosition, wxDefaultSize); + m_CheckListBox_CheatsList = new wxCheckListBox(m_Tab_Cheats, ID_CHECKLISTBOX_CHEATSLIST, wxDefaultPosition, wxSize(300, 0), m_CheatStringList, wxLB_HSCROLL, wxDefaultValidator); + m_Label_Codename = new wxStaticText(m_Tab_Cheats, ID_LABEL_CODENAME, _T("Name: "), wxDefaultPosition, wxDefaultSize); + m_GroupBox_Info = new wxStaticBox(m_Tab_Cheats, ID_GROUPBOX_INFO, _T("Code Info"), wxDefaultPosition, wxDefaultSize); + m_Button_ApplyCodes = new wxButton(m_Tab_Cheats, ID_BUTTON_APPLYCODES, _T("Apply Changes"), wxDefaultPosition, wxDefaultSize); + m_Label_NumCodes = new wxStaticText(m_Tab_Cheats, ID_LABEL_NUMCODES, _T("Number Of Codes: "), wxDefaultPosition, wxDefaultSize); + m_ListBox_CodesList = new wxListBox(m_Tab_Cheats, ID_LISTBOX_CODESLIST, wxDefaultPosition, wxSize(120, 150), 0, 0, wxLB_HSCROLL); + + wxBoxSizer* sCheatsListH = new wxBoxSizer(wxHORIZONTAL); + sCheatsListH->Add(m_CheckListBox_CheatsList, 1, wxALL|wxEXPAND, 5); + + wxStaticBoxSizer* sGroupBoxInfo = new wxStaticBoxSizer(m_GroupBox_Info, wxVERTICAL); + sGroupBoxInfo->Add(m_Label_Codename, 0, wxALL, 5); + sGroupBoxInfo->Add(m_Label_NumCodes, 0, wxALL, 5); + sGroupBoxInfo->Add(m_ListBox_CodesList, 0, wxALL, 5); + + wxBoxSizer* sB1 = new wxBoxSizer(wxVERTICAL); + sB1->Add(m_Button_ApplyCodes, 0, wxALL, 5); + sB1->Add(sGroupBoxInfo, 0, wxALL, 5); + + m_Sizer_TabCheats = new wxGridBagSizer(); + m_Sizer_TabCheats->AddGrowableRow(0); + m_Sizer_TabCheats->Add(sCheatsListH, wxGBPosition(0, 0), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); + m_Sizer_TabCheats->Add(sB1, wxGBPosition(0, 1), wxGBSpan(1, 1), wxALIGN_LEFT|wxALL, 5); + + m_Tab_Cheats->SetSizer(m_Sizer_TabCheats); + m_Tab_Cheats->Layout(); + + + // $ Log Tab + m_Tab_Log = new wxPanel(m_Notebook_Main, ID_TAB_LOG, wxDefaultPosition, wxDefaultSize); + + // Add Tabs to Notebook + m_Notebook_Main->AddPage(m_Tab_Cheats, _T("Codes List")); + m_Notebook_Main->AddPage(m_Tab_Log, _T("Logging")); + + // Button Strip + m_Button_Close = new wxButton(this, ID_BUTTON_CLOSE, _T("Close"), wxDefaultPosition, wxDefaultSize); + wxBoxSizer* sButtons = new wxBoxSizer(wxHORIZONTAL); + sButtons->Add(m_Button_Close, 0, wxALL, 5); + + wxBoxSizer* sMain = new wxBoxSizer(wxVERTICAL); + sMain->Add(m_Notebook_Main, 1, wxEXPAND|wxALL, 5); + sMain->Add(sButtons, 0, wxALL, 5); + SetSizer(sMain); + Layout(); + + Fit(); +} +void wxCheatsWindow::OnEvent_Window_Resize(wxSizeEvent& WXUNUSED (event)) +{ + Layout(); +} +void wxCheatsWindow::OnEvent_ButtonClose_Press(wxCommandEvent& WXUNUSED (event)) +{ + Destroy(); +} +void wxCheatsWindow::OnEvent_Window_Close(wxCloseEvent& WXUNUSED (event)) +{ + Destroy(); +} +void wxCheatsWindow::Load_ARCodes() +{ + indexList.clear(); + size_t size = ActionReplay_GetCodeListSize(); + for (size_t i = 0; i < size; i++) + { + ARCode code = ActionReplay_GetARCode(i); + ARCodeIndex ind; + u32 index = m_CheckListBox_CheatsList->Append(wxString::FromAscii(code.name.c_str())); + m_CheckListBox_CheatsList->Check(index, code.active); + ind.index = i; + ind.uiIndex = index; + indexList.push_back(ind); + } +} +void wxCheatsWindow::OnEvent_CheatsList_ItemSelected(wxCommandEvent& WXUNUSED (event)) +{ + int index = m_CheckListBox_CheatsList->GetSelection(); + for (size_t i = 0; i < indexList.size(); i++) + { + if ((int)indexList[i].uiIndex == index) + { + ARCode code = ActionReplay_GetARCode(i); + m_Label_Codename->SetLabel(wxT("Name: ") + wxString::FromAscii(code.name.c_str())); + char text[CHAR_MAX]; + char* numcodes = text; + sprintf(numcodes, "Number of Codes: %i", code.ops.size()); + m_Label_NumCodes->SetLabel(wxString::FromAscii(numcodes)); + m_ListBox_CodesList->Clear(); + for (size_t j = 0; j < code.ops.size(); j++) + { + char text2[CHAR_MAX]; + char* ops = text2; + sprintf(ops, "%08x %08x", code.ops[j].cmd_addr, code.ops[j].value); + m_ListBox_CodesList->Append(wxString::FromAscii(ops)); + } + } + } + m_Sizer_TabCheats->Layout(); +} +void wxCheatsWindow::OnEvent_CheatsList_ItemToggled(wxCommandEvent& WXUNUSED (event)) +{ + int index = m_CheckListBox_CheatsList->GetSelection(); + for (size_t i = 0; i < indexList.size(); i++) + { + if ((int)indexList[i].uiIndex == index) + { + ActionReplay_SetARCode_IsActive(m_CheckListBox_CheatsList->IsChecked(index), indexList[i].index); + } + } +} +void wxCheatsWindow::OnEvent_ButtonUpdateCodes_Press(wxCommandEvent& WXUNUSED (event)) +{ + for (size_t i = 0; i < indexList.size(); i++) + { + ActionReplay_SetARCode_IsActive(m_CheckListBox_CheatsList->IsChecked(indexList[i].uiIndex), indexList[i].index); + } +} diff --git a/Source/Core/DolphinWX/Src/Config.cpp b/Source/Core/DolphinWX/Src/Config.cpp index 306f13ed10..f54b1c9b78 100644 --- a/Source/Core/DolphinWX/Src/Config.cpp +++ b/Source/Core/DolphinWX/Src/Config.cpp @@ -1,137 +1,137 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include - -#include "Globals.h" -#include "Common.h" -#include "IniFile.h" -#include "Config.h" - -SConfig SConfig::m_Instance; - - -SConfig::SConfig() -{ - LoadSettings(); -} - - -SConfig::~SConfig() -{ - SaveSettings(); -} - - -void SConfig::SaveSettings() -{ - IniFile ini; - // ini.Load(CONFIG_FILE); // yes we must load first to not kill unknown stuff - - // misc - { - ini.Set("General", "LastFilename", m_LastFilename); - - // ISO folders - ini.Set("General", "GCMPathes", (int)m_ISOFolder.size()); - - for (size_t i = 0; i < m_ISOFolder.size(); i++) - { - TCHAR tmp[16]; - sprintf(tmp, "GCMPath%i", (int)i); - ini.Set("General", tmp, m_ISOFolder[i]); - } - } - - // core - { - ini.Set("Core", "GFXPlugin", m_LocalCoreStartupParameter.m_strVideoPlugin); - ini.Set("Core", "DSPPlugin", m_LocalCoreStartupParameter.m_strDSPPlugin); - ini.Set("Core", "PadPlugin", m_LocalCoreStartupParameter.m_strPadPlugin); - ini.Set("Core", "WiiMotePlugin", m_LocalCoreStartupParameter.m_strWiimotePlugin); - - ini.Set("Core", "HLEBios", m_LocalCoreStartupParameter.bHLEBios); - ini.Set("Core", "UseDynarec", m_LocalCoreStartupParameter.bUseJIT); - ini.Set("Core", "UseDualCore", m_LocalCoreStartupParameter.bUseDualCore); - ini.Set("Core", "SkipIdle", m_LocalCoreStartupParameter.bSkipIdle); - ini.Set("Core", "LockThreads", m_LocalCoreStartupParameter.bLockThreads); - ini.Set("Core", "DefaultGCM", m_LocalCoreStartupParameter.m_strDefaultGCM); - ini.Set("Core", "DVDRoot", m_LocalCoreStartupParameter.m_strDVDRoot); - ini.Set("Core", "OptimizeQuantizers", m_LocalCoreStartupParameter.bOptimizeQuantizers); - ini.Set("Core", "EnableCheats", m_LocalCoreStartupParameter.bEnableCheats); - ini.Set("Core", "SelectedLanguage", m_LocalCoreStartupParameter.SelectedLanguage); - ini.Set("Core", "RunCompareServer", m_LocalCoreStartupParameter.bRunCompareServer); - ini.Set("Core", "RunCompareClient", m_LocalCoreStartupParameter.bRunCompareClient); - } - - ini.Save(CONFIG_FILE); -} - - -void SConfig::LoadSettings() -{ - IniFile ini; - ini.Load(CONFIG_FILE); - - // hard coded default plugin - { - m_DefaultGFXPlugin = PLUGINS_DIR DIR_SEP DEFAULT_GFX_PLUGIN; - m_DefaultDSPPlugin = PLUGINS_DIR DIR_SEP DEFAULT_DSP_PLUGIN; - m_DefaultPADPlugin = PLUGINS_DIR DIR_SEP DEFAULT_PAD_PLUGIN; - m_DefaultWiiMotePlugin = PLUGINS_DIR DIR_SEP DEFAULT_WIIMOTE_PLUGIN; - } - - // misc - { - ini.Get("General", "LastFilename", &m_LastFilename); - - m_ISOFolder.clear(); - int numGCMPaths; - - if (ini.Get("General", "GCMPathes", &numGCMPaths, 0)) - { - for (int i = 0; i < numGCMPaths; i++) - { - TCHAR tmp[16]; - sprintf(tmp, "GCMPath%i", i); - std::string tmpPath; - ini.Get("General", tmp, &tmpPath, ""); - m_ISOFolder.push_back(tmpPath); - } - } - } - - // core - { - ini.Get("Core", "GFXPlugin", &m_LocalCoreStartupParameter.m_strVideoPlugin, m_DefaultGFXPlugin.c_str()); - ini.Get("Core", "DSPPlugin", &m_LocalCoreStartupParameter.m_strDSPPlugin, m_DefaultDSPPlugin.c_str()); - ini.Get("Core", "PadPlugin", &m_LocalCoreStartupParameter.m_strPadPlugin, m_DefaultPADPlugin.c_str()); - ini.Get("Core", "WiiMotePlugin", &m_LocalCoreStartupParameter.m_strWiimotePlugin, m_DefaultWiiMotePlugin.c_str()); - ini.Get("Core", "HLEBios", &m_LocalCoreStartupParameter.bHLEBios, true); - ini.Get("Core", "UseDynarec", &m_LocalCoreStartupParameter.bUseJIT, true); - ini.Get("Core", "UseDualCore", &m_LocalCoreStartupParameter.bUseDualCore, false); - ini.Get("Core", "SkipIdle", &m_LocalCoreStartupParameter.bSkipIdle, true); - ini.Get("Core", "LockThreads", &m_LocalCoreStartupParameter.bLockThreads, true); - ini.Get("Core", "DefaultGCM", &m_LocalCoreStartupParameter.m_strDefaultGCM); - ini.Get("Core", "DVDRoot", &m_LocalCoreStartupParameter.m_strDVDRoot); - ini.Get("Core", "OptimizeQuantizers", &m_LocalCoreStartupParameter.bOptimizeQuantizers, true); - ini.Get("Core", "EnableCheats", &m_LocalCoreStartupParameter.bEnableCheats, false); - ini.Get("Core", "SelectedLanguage", &m_LocalCoreStartupParameter.SelectedLanguage, 0); - ini.Get("Core", "RunCompareServer", &m_LocalCoreStartupParameter.bRunCompareServer, false); - ini.Get("Core", "RunCompareClient", &m_LocalCoreStartupParameter.bRunCompareClient, false); - } -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "Globals.h" +#include "Common.h" +#include "IniFile.h" +#include "Config.h" + +SConfig SConfig::m_Instance; + + +SConfig::SConfig() +{ + LoadSettings(); +} + + +SConfig::~SConfig() +{ + SaveSettings(); +} + + +void SConfig::SaveSettings() +{ + IniFile ini; + // ini.Load(CONFIG_FILE); // yes we must load first to not kill unknown stuff + + // misc + { + ini.Set("General", "LastFilename", m_LastFilename); + + // ISO folders + ini.Set("General", "GCMPathes", (int)m_ISOFolder.size()); + + for (size_t i = 0; i < m_ISOFolder.size(); i++) + { + TCHAR tmp[16]; + sprintf(tmp, "GCMPath%i", (int)i); + ini.Set("General", tmp, m_ISOFolder[i]); + } + } + + // core + { + ini.Set("Core", "GFXPlugin", m_LocalCoreStartupParameter.m_strVideoPlugin); + ini.Set("Core", "DSPPlugin", m_LocalCoreStartupParameter.m_strDSPPlugin); + ini.Set("Core", "PadPlugin", m_LocalCoreStartupParameter.m_strPadPlugin); + ini.Set("Core", "WiiMotePlugin", m_LocalCoreStartupParameter.m_strWiimotePlugin); + + ini.Set("Core", "HLEBios", m_LocalCoreStartupParameter.bHLEBios); + ini.Set("Core", "UseDynarec", m_LocalCoreStartupParameter.bUseJIT); + ini.Set("Core", "UseDualCore", m_LocalCoreStartupParameter.bUseDualCore); + ini.Set("Core", "SkipIdle", m_LocalCoreStartupParameter.bSkipIdle); + ini.Set("Core", "LockThreads", m_LocalCoreStartupParameter.bLockThreads); + ini.Set("Core", "DefaultGCM", m_LocalCoreStartupParameter.m_strDefaultGCM); + ini.Set("Core", "DVDRoot", m_LocalCoreStartupParameter.m_strDVDRoot); + ini.Set("Core", "OptimizeQuantizers", m_LocalCoreStartupParameter.bOptimizeQuantizers); + ini.Set("Core", "EnableCheats", m_LocalCoreStartupParameter.bEnableCheats); + ini.Set("Core", "SelectedLanguage", m_LocalCoreStartupParameter.SelectedLanguage); + ini.Set("Core", "RunCompareServer", m_LocalCoreStartupParameter.bRunCompareServer); + ini.Set("Core", "RunCompareClient", m_LocalCoreStartupParameter.bRunCompareClient); + } + + ini.Save(CONFIG_FILE); +} + + +void SConfig::LoadSettings() +{ + IniFile ini; + ini.Load(CONFIG_FILE); + + // hard coded default plugin + { + m_DefaultGFXPlugin = PLUGINS_DIR DIR_SEP DEFAULT_GFX_PLUGIN; + m_DefaultDSPPlugin = PLUGINS_DIR DIR_SEP DEFAULT_DSP_PLUGIN; + m_DefaultPADPlugin = PLUGINS_DIR DIR_SEP DEFAULT_PAD_PLUGIN; + m_DefaultWiiMotePlugin = PLUGINS_DIR DIR_SEP DEFAULT_WIIMOTE_PLUGIN; + } + + // misc + { + ini.Get("General", "LastFilename", &m_LastFilename); + + m_ISOFolder.clear(); + int numGCMPaths; + + if (ini.Get("General", "GCMPathes", &numGCMPaths, 0)) + { + for (int i = 0; i < numGCMPaths; i++) + { + TCHAR tmp[16]; + sprintf(tmp, "GCMPath%i", i); + std::string tmpPath; + ini.Get("General", tmp, &tmpPath, ""); + m_ISOFolder.push_back(tmpPath); + } + } + } + + // core + { + ini.Get("Core", "GFXPlugin", &m_LocalCoreStartupParameter.m_strVideoPlugin, m_DefaultGFXPlugin.c_str()); + ini.Get("Core", "DSPPlugin", &m_LocalCoreStartupParameter.m_strDSPPlugin, m_DefaultDSPPlugin.c_str()); + ini.Get("Core", "PadPlugin", &m_LocalCoreStartupParameter.m_strPadPlugin, m_DefaultPADPlugin.c_str()); + ini.Get("Core", "WiiMotePlugin", &m_LocalCoreStartupParameter.m_strWiimotePlugin, m_DefaultWiiMotePlugin.c_str()); + ini.Get("Core", "HLEBios", &m_LocalCoreStartupParameter.bHLEBios, true); + ini.Get("Core", "UseDynarec", &m_LocalCoreStartupParameter.bUseJIT, true); + ini.Get("Core", "UseDualCore", &m_LocalCoreStartupParameter.bUseDualCore, false); + ini.Get("Core", "SkipIdle", &m_LocalCoreStartupParameter.bSkipIdle, true); + ini.Get("Core", "LockThreads", &m_LocalCoreStartupParameter.bLockThreads, true); + ini.Get("Core", "DefaultGCM", &m_LocalCoreStartupParameter.m_strDefaultGCM); + ini.Get("Core", "DVDRoot", &m_LocalCoreStartupParameter.m_strDVDRoot); + ini.Get("Core", "OptimizeQuantizers", &m_LocalCoreStartupParameter.bOptimizeQuantizers, true); + ini.Get("Core", "EnableCheats", &m_LocalCoreStartupParameter.bEnableCheats, false); + ini.Get("Core", "SelectedLanguage", &m_LocalCoreStartupParameter.SelectedLanguage, 0); + ini.Get("Core", "RunCompareServer", &m_LocalCoreStartupParameter.bRunCompareServer, false); + ini.Get("Core", "RunCompareClient", &m_LocalCoreStartupParameter.bRunCompareClient, false); + } +} diff --git a/Source/Core/DolphinWX/Src/ConfigMain.cpp b/Source/Core/DolphinWX/Src/ConfigMain.cpp index e58d025ddd..23f6604b35 100644 --- a/Source/Core/DolphinWX/Src/ConfigMain.cpp +++ b/Source/Core/DolphinWX/Src/ConfigMain.cpp @@ -1,539 +1,539 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include -#include - -#include "Globals.h" - -#include "ConfigMain.h" -#include "PluginManager.h" - -BEGIN_EVENT_TABLE(CConfigMain, wxDialog) - -EVT_CLOSE(CConfigMain::OnClose) -EVT_BUTTON(ID_CLOSE, CConfigMain::CloseClick) -EVT_CHECKBOX(ID_ALLWAYS_HLEBIOS, CConfigMain::CoreSettingsChanged) -EVT_CHECKBOX(ID_USEDYNAREC, CConfigMain::CoreSettingsChanged) -EVT_CHECKBOX(ID_USEDUALCORE, CConfigMain::CoreSettingsChanged) -EVT_CHECKBOX(ID_LOCKTHREADS, CConfigMain::CoreSettingsChanged) -EVT_CHECKBOX(ID_OPTIMIZEQUANTIZERS, CConfigMain::CoreSettingsChanged) -EVT_CHECKBOX(ID_IDLESKIP, CConfigMain::CoreSettingsChanged) -EVT_CHECKBOX(ID_ENABLECHEATS, CConfigMain::CoreSettingsChanged) -EVT_CHOICE(ID_GC_SRAM_LNG, CConfigMain::GCSettingsChanged) -EVT_CHOICE(ID_WII_BT_BAR, CConfigMain::WiiSettingsChanged) -EVT_CHECKBOX(ID_WII_IPL_SSV, CConfigMain::WiiSettingsChanged) -EVT_CHECKBOX(ID_WII_IPL_PGS, CConfigMain::WiiSettingsChanged) -EVT_CHECKBOX(ID_WII_IPL_E60, CConfigMain::WiiSettingsChanged) -EVT_CHOICE(ID_WII_IPL_AR, CConfigMain::WiiSettingsChanged) -EVT_CHOICE(ID_WII_IPL_LNG, CConfigMain::WiiSettingsChanged) -EVT_LISTBOX(ID_ISOPATHS, CConfigMain::ISOPathsSelectionChanged) -EVT_BUTTON(ID_ADDISOPATH, CConfigMain::AddRemoveISOPaths) -EVT_BUTTON(ID_REMOVEISOPATH, CConfigMain::AddRemoveISOPaths) -EVT_FILEPICKER_CHANGED(ID_DEFAULTISO, CConfigMain::DefaultISOChanged) -EVT_DIRPICKER_CHANGED(ID_DVDROOT, CConfigMain::DVDRootChanged) -EVT_CHOICE(ID_GRAPHIC_CB, CConfigMain::OnSelectionChanged) -EVT_BUTTON(ID_GRAPHIC_CONFIG, CConfigMain::OnConfig) -EVT_CHOICE(ID_DSP_CB, CConfigMain::OnSelectionChanged) -EVT_BUTTON(ID_DSP_CONFIG, CConfigMain::OnConfig) -EVT_CHOICE(ID_PAD_CB, CConfigMain::OnSelectionChanged) -EVT_BUTTON(ID_PAD_CONFIG, CConfigMain::OnConfig) -EVT_CHOICE(ID_WIIMOTE_CB, CConfigMain::OnSelectionChanged) -EVT_BUTTON(ID_WIIMOTE_CONFIG, CConfigMain::OnConfig) - -END_EVENT_TABLE() - -CConfigMain::CConfigMain(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& position, const wxSize& size, long style) - : wxDialog(parent, id, title, position, size, style) -{ - bRefreshList = false; - - // Load Wii SYSCONF - FullSYSCONFPath = FULL_WII_USER_DIR "shared2/sys/SYSCONF"; - pStream = NULL; - pStream = fopen(FullSYSCONFPath.c_str(), "rb"); - if (pStream != NULL) - { - fread(m_SYSCONF, 1, 0x4000, pStream); - fclose(pStream); - m_bSysconfOK = true; - } - else - { - PanicAlert("Could not read %s. Please recover the SYSCONF file to that location.", FullSYSCONFPath.c_str()); - m_bSysconfOK = false; - } - - CreateGUIControls(); - for(u32 i = 0; i < SConfig::GetInstance().m_ISOFolder.size(); i++) - { - ISOPaths->Append(wxString(SConfig::GetInstance().m_ISOFolder[i].c_str(), wxConvUTF8)); - } -} - -CConfigMain::~CConfigMain() -{ -} - -void CConfigMain::CreateGUIControls() -{ - Notebook = new wxNotebook(this, ID_NOTEBOOK, wxDefaultPosition, wxDefaultSize); - - GeneralPage = new wxPanel(Notebook, ID_GENERALPAGE, wxDefaultPosition, wxDefaultSize); - Notebook->AddPage(GeneralPage, wxT("Core")); - GamecubePage = new wxPanel(Notebook, ID_GAMECUBEPAGE, wxDefaultPosition, wxDefaultSize); - Notebook->AddPage(GamecubePage, wxT("Gamecube")); - WiiPage = new wxPanel(Notebook, ID_WIIPAGE, wxDefaultPosition, wxDefaultSize); - Notebook->AddPage(WiiPage, wxT("Wii")); - PathsPage = new wxPanel(Notebook, ID_PATHSPAGE, wxDefaultPosition, wxDefaultSize); - Notebook->AddPage(PathsPage, wxT("Paths")); - PluginPage = new wxPanel(Notebook, ID_PLUGINPAGE, wxDefaultPosition, wxDefaultSize); - Notebook->AddPage(PluginPage, wxT("Plugins")); - - m_Close = new wxButton(this, ID_CLOSE, wxT("Close"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - - wxBoxSizer* sButtons; - sButtons = new wxBoxSizer(wxHORIZONTAL); - sButtons->Add(0, 0, 1, wxEXPAND, 5); - sButtons->Add(m_Close, 0, wxALL, 5); - - wxBoxSizer* sMain; - sMain = new wxBoxSizer(wxVERTICAL); - sMain->Add(Notebook, 1, wxEXPAND|wxALL, 5); - sMain->Add(sButtons, 0, wxEXPAND, 5); - - this->SetSizer(sMain); - this->Layout(); - - // Core page - sbBasic = new wxStaticBoxSizer(wxVERTICAL, GeneralPage, wxT("Basic Settings")); - UseDualCore = new wxCheckBox(GeneralPage, ID_USEDUALCORE, wxT("Enable Dual Core"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - UseDualCore->SetValue(SConfig::GetInstance().m_LocalCoreStartupParameter.bUseDualCore); - SkipIdle = new wxCheckBox(GeneralPage, ID_IDLESKIP, wxT("Enable Idle Skipping"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - SkipIdle->SetValue(SConfig::GetInstance().m_LocalCoreStartupParameter.bSkipIdle); - EnableCheats = new wxCheckBox(GeneralPage, ID_ENABLECHEATS, wxT("Enable Cheats"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - EnableCheats->SetValue(SConfig::GetInstance().m_LocalCoreStartupParameter.bEnableCheats); - - sbAdvanced = new wxStaticBoxSizer(wxVERTICAL, GeneralPage, wxT("Advanced Settings")); - AllwaysHLEBIOS = new wxCheckBox(GeneralPage, ID_ALLWAYS_HLEBIOS, wxT("HLE the BIOS all the time"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - AllwaysHLEBIOS->SetValue(SConfig::GetInstance().m_LocalCoreStartupParameter.bHLEBios); - UseDynaRec = new wxCheckBox(GeneralPage, ID_USEDYNAREC, wxT("Enable Dynamic Recompilation"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - UseDynaRec->SetValue(SConfig::GetInstance().m_LocalCoreStartupParameter.bUseJIT); - LockThreads = new wxCheckBox(GeneralPage, ID_LOCKTHREADS, wxT("Lock threads to cores"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - LockThreads->SetValue(SConfig::GetInstance().m_LocalCoreStartupParameter.bLockThreads); - OptimizeQuantizers = new wxCheckBox(GeneralPage, ID_OPTIMIZEQUANTIZERS, wxT("Optimize Quantizers"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - OptimizeQuantizers->SetValue(SConfig::GetInstance().m_LocalCoreStartupParameter.bOptimizeQuantizers); - - sGeneral = new wxBoxSizer(wxVERTICAL); - sbBasic->Add(UseDualCore, 0, wxALL, 5); - sbBasic->Add(SkipIdle, 0, wxALL, 5); - sbBasic->Add(EnableCheats, 0, wxALL, 5); - sGeneral->Add(sbBasic, 0, wxEXPAND|wxALL, 5); - sGeneral->AddStretchSpacer(); - - sbAdvanced->Add(AllwaysHLEBIOS, 0, wxALL, 5); - sbAdvanced->Add(UseDynaRec, 0, wxALL, 5); - sbAdvanced->Add(LockThreads, 0, wxALL, 5); - sbAdvanced->Add(OptimizeQuantizers, 0, wxALL, 5); - sGeneral->Add(sbAdvanced, 0, wxEXPAND|wxALL, 5); - GeneralPage->SetSizer(sGeneral); - sGeneral->Layout(); - - // Gamecube page - sbGamecubeIPLSettings = new wxStaticBoxSizer(wxVERTICAL, GamecubePage, wxT("IPL Settings")); - arrayStringFor_GCSystemLang.Add(wxT("English")); - arrayStringFor_GCSystemLang.Add(wxT("German")); - arrayStringFor_GCSystemLang.Add(wxT("French")); - arrayStringFor_GCSystemLang.Add(wxT("Spanish")); - arrayStringFor_GCSystemLang.Add(wxT("Italian")); - arrayStringFor_GCSystemLang.Add(wxT("Dutch")); - GCSystemLangText = new wxStaticText(GamecubePage, ID_GC_SRAM_LNG_TEXT, wxT("System Language:"), wxDefaultPosition, wxDefaultSize); - GCSystemLang = new wxChoice(GamecubePage, ID_GC_SRAM_LNG, wxDefaultPosition, wxDefaultSize, arrayStringFor_GCSystemLang, 0, wxDefaultValidator); - GCSystemLang->SetSelection(SConfig::GetInstance().m_LocalCoreStartupParameter.SelectedLanguage); - - sGamecube = new wxBoxSizer(wxVERTICAL); - sGamecubeIPLSettings = new wxGridBagSizer(0, 0); - sGamecubeIPLSettings->Add(GCSystemLangText, wxGBPosition(0, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); - sGamecubeIPLSettings->Add(GCSystemLang, wxGBPosition(0, 1), wxGBSpan(1, 1), wxALL, 5); - sbGamecubeIPLSettings->Add(sGamecubeIPLSettings); - sGamecube->Add(sbGamecubeIPLSettings, 0, wxEXPAND|wxALL, 5); - GamecubePage->SetSizer(sGamecube); - sGamecube->Layout(); - - // Wii SYSCONF page - sbWiimoteSettings = new wxStaticBoxSizer(wxVERTICAL, WiiPage, wxT("Wiimote Settings")); - arrayStringFor_WiiSensBarPos.Add(wxT("Bottom")); arrayStringFor_WiiSensBarPos.Add(wxT("Top")); - WiiSensBarPosText = new wxStaticText(WiiPage, ID_WII_BT_BAR_TEXT, wxT("Sensor Bar Position:"), wxDefaultPosition, wxDefaultSize); - WiiSensBarPos = new wxChoice(WiiPage, ID_WII_BT_BAR, wxDefaultPosition, wxDefaultSize, arrayStringFor_WiiSensBarPos, 0, wxDefaultValidator); - WiiSensBarPos->SetSelection(m_SYSCONF[BT_BAR]); - - sbWiiIPLSettings = new wxStaticBoxSizer(wxVERTICAL, WiiPage, wxT("IPL Settings")); - WiiScreenSaver = new wxCheckBox(WiiPage, ID_WII_IPL_SSV, wxT("Enable Screen Saver (burn-in reduction)"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - WiiScreenSaver->SetValue(m_SYSCONF[IPL_SSV]!=0); - WiiProgressiveScan = new wxCheckBox(WiiPage, ID_WII_IPL_PGS, wxT("Enable Progressive Scan"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - WiiProgressiveScan->SetValue(m_SYSCONF[IPL_PGS]!=0); - WiiEuRGB60 = new wxCheckBox(WiiPage, ID_WII_IPL_E60, wxT("Use EuRGB60 Mode (PAL6)"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - WiiEuRGB60->SetValue(m_SYSCONF[IPL_E60]!=0); - arrayStringFor_WiiAspectRatio.Add(wxT("4:3")); arrayStringFor_WiiAspectRatio.Add(wxT("16:9")); - WiiAspectRatioText = new wxStaticText(WiiPage, ID_WII_IPL_AR_TEXT, wxT("Aspect Ratio:"), wxDefaultPosition, wxDefaultSize); - WiiAspectRatio = new wxChoice(WiiPage, ID_WII_IPL_AR, wxDefaultPosition, wxDefaultSize, arrayStringFor_WiiAspectRatio, 0, wxDefaultValidator); - WiiAspectRatio->SetSelection(m_SYSCONF[IPL_AR]); - arrayStringFor_WiiSystemLang = arrayStringFor_GCSystemLang; - arrayStringFor_WiiSystemLang.Insert(wxT("Japanese"), 0); - WiiSystemLangText = new wxStaticText(WiiPage, ID_WII_IPL_LNG_TEXT, wxT("System Language:"), wxDefaultPosition, wxDefaultSize); - WiiSystemLang = new wxChoice(WiiPage, ID_WII_IPL_LNG, wxDefaultPosition, wxDefaultSize, arrayStringFor_WiiSystemLang, 0, wxDefaultValidator); - WiiSystemLang->SetSelection(m_SYSCONF[IPL_LNG]); - - sWii = new wxBoxSizer(wxVERTICAL); - sWiimoteSettings = new wxGridBagSizer(0, 0); - sWiimoteSettings->Add(WiiSensBarPosText, wxGBPosition(0, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); - sWiimoteSettings->Add(WiiSensBarPos, wxGBPosition(0, 1), wxGBSpan(1, 1), wxALL, 5); - sbWiimoteSettings->Add(sWiimoteSettings); - sWii->Add(sbWiimoteSettings, 0, wxEXPAND|wxALL, 5); - - sWiiIPLSettings = new wxGridBagSizer(0, 0); - sWiiIPLSettings->Add(WiiScreenSaver, wxGBPosition(0, 0), wxGBSpan(1, 2), wxALL, 5); - sWiiIPLSettings->Add(WiiProgressiveScan, wxGBPosition(1, 0), wxGBSpan(1, 2), wxALL, 5); - sWiiIPLSettings->Add(WiiEuRGB60, wxGBPosition(2, 0), wxGBSpan(1, 2), wxALL, 5); - sWiiIPLSettings->Add(WiiAspectRatioText, wxGBPosition(3, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); - sWiiIPLSettings->Add(WiiAspectRatio, wxGBPosition(3, 1), wxGBSpan(1, 1), wxALL, 5); - sWiiIPLSettings->Add(WiiSystemLangText, wxGBPosition(4, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); - sWiiIPLSettings->Add(WiiSystemLang, wxGBPosition(4, 1), wxGBSpan(1, 1), wxALL, 5); - sbWiiIPLSettings->Add(sWiiIPLSettings); - sWii->Add(sbWiiIPLSettings, 0, wxEXPAND|wxALL, 5); - WiiPage->SetSizer(sWii); - sWii->Layout(); - - // Paths page - sbISOPaths = new wxStaticBoxSizer(wxVERTICAL, PathsPage, wxT("ISO Directories")); - ISOPaths = new wxListBox(PathsPage, ID_ISOPATHS, wxDefaultPosition, wxDefaultSize, arrayStringFor_ISOPaths, wxLB_SINGLE, wxDefaultValidator); - AddISOPath = new wxButton(PathsPage, ID_ADDISOPATH, wxT("Add..."), wxDefaultPosition, wxDefaultSize, 0); - RemoveISOPath = new wxButton(PathsPage, ID_REMOVEISOPATH, wxT("Remove"), wxDefaultPosition, wxDefaultSize, 0); - RemoveISOPath->Enable(false); - - DefaultISOText = new wxStaticText(PathsPage, ID_DEFAULTISO_TEXT, wxT("Default ISO:"), wxDefaultPosition, wxDefaultSize); - DefaultISO = new wxFilePickerCtrl(PathsPage, ID_DEFAULTISO, wxEmptyString, wxT("Choose a default ISO:"), - wxString::Format(wxT("All GC/Wii images (gcm, iso, gcz)|*.gcm;*.iso;*.gcz|All files (%s)|%s"), wxFileSelectorDefaultWildcardStr, wxFileSelectorDefaultWildcardStr), - wxDefaultPosition, wxDefaultSize, wxFLP_USE_TEXTCTRL|wxFLP_FILE_MUST_EXIST|wxFLP_OPEN); - DefaultISO->SetPath(wxString::FromAscii(SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDefaultGCM.c_str())); - - DVDRootText = new wxStaticText(PathsPage, ID_DVDROOT_TEXT, wxT("DVD Root:"), wxDefaultPosition, wxDefaultSize); - DVDRoot = new wxDirPickerCtrl(PathsPage, ID_DVDROOT, wxEmptyString, wxT("Choose a DVD root directory:"), wxDefaultPosition, wxDefaultSize, wxDIRP_USE_TEXTCTRL); - DVDRoot->SetPath(wxString::FromAscii(SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDVDRoot.c_str())); - - sPaths = new wxBoxSizer(wxVERTICAL); - - sbISOPaths->Add(ISOPaths, 1, wxEXPAND|wxALL, 0); - - sISOButtons = new wxBoxSizer(wxHORIZONTAL); - sISOButtons->AddStretchSpacer(1); - sISOButtons->Add(AddISOPath, 0, wxALL, 0); - sISOButtons->Add(RemoveISOPath, 0, wxALL, 0); - sbISOPaths->Add(sISOButtons, 0, wxEXPAND|wxALL, 5); - sPaths->Add(sbISOPaths, 1, wxEXPAND|wxALL, 5); - - sOtherPaths = new wxGridBagSizer(0, 0); - sOtherPaths->AddGrowableCol(1); - sOtherPaths->Add(DefaultISOText, wxGBPosition(0, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); - sOtherPaths->Add(DefaultISO, wxGBPosition(0, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); - sOtherPaths->Add(DVDRootText, wxGBPosition(1, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); - sOtherPaths->Add(DVDRoot, wxGBPosition(1, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); - sPaths->Add(sOtherPaths, 0, wxEXPAND|wxALL, 5); - PathsPage->SetSizer(sPaths); - sPaths->Layout(); - - // Plugin page - sbGraphicsPlugin = new wxStaticBoxSizer(wxHORIZONTAL, PluginPage, wxT("Graphics")); - GraphicSelection = new wxChoice(PluginPage, ID_GRAPHIC_CB, wxDefaultPosition, wxDefaultSize, NULL, 0, wxDefaultValidator); - GraphicConfig = new wxButton(PluginPage, ID_GRAPHIC_CONFIG, wxT("Config..."), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - - sbDSPPlugin = new wxStaticBoxSizer(wxHORIZONTAL, PluginPage, wxT("DSP")); - DSPSelection = new wxChoice(PluginPage, ID_DSP_CB, wxDefaultPosition, wxDefaultSize, NULL, 0, wxDefaultValidator); - DSPConfig = new wxButton(PluginPage, ID_DSP_CONFIG, wxT("Config..."), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - - sbPadPlugin = new wxStaticBoxSizer(wxHORIZONTAL, PluginPage, wxT("Pad")); - PADSelection = new wxChoice(PluginPage, ID_PAD_CB, wxDefaultPosition, wxDefaultSize, NULL, 0, wxDefaultValidator); - PADConfig = new wxButton(PluginPage, ID_PAD_CONFIG, wxT("Config..."), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - - sbWiimotePlugin = new wxStaticBoxSizer(wxHORIZONTAL, PluginPage, wxT("Wiimote")); - WiimoteSelection = new wxChoice(PluginPage, ID_WIIMOTE_CB, wxDefaultPosition, wxDefaultSize, NULL, 0, wxDefaultValidator); - WiimoteConfig = new wxButton(PluginPage, ID_WIIMOTE_CONFIG, wxT("Config..."), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - - FillChoiceBox(GraphicSelection, PLUGIN_TYPE_VIDEO, SConfig::GetInstance().m_LocalCoreStartupParameter.m_strVideoPlugin); - FillChoiceBox(DSPSelection, PLUGIN_TYPE_DSP, SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDSPPlugin); - FillChoiceBox(PADSelection, PLUGIN_TYPE_PAD, SConfig::GetInstance().m_LocalCoreStartupParameter.m_strPadPlugin); - FillChoiceBox(WiimoteSelection, PLUGIN_TYPE_WIIMOTE, SConfig::GetInstance().m_LocalCoreStartupParameter.m_strWiimotePlugin); - - sPlugins = new wxBoxSizer(wxVERTICAL); - sbGraphicsPlugin->Add(GraphicSelection, 1, wxEXPAND|wxALL, 5); - sbGraphicsPlugin->Add(GraphicConfig, 0, wxALL, 5); - sPlugins->Add(sbGraphicsPlugin, 0, wxEXPAND|wxALL, 5); - - sbDSPPlugin->Add(DSPSelection, 1, wxEXPAND|wxALL, 5); - sbDSPPlugin->Add(DSPConfig, 0, wxALL, 5); - sPlugins->Add(sbDSPPlugin, 0, wxEXPAND|wxALL, 5); - - sbPadPlugin->Add(PADSelection, 1, wxEXPAND|wxALL, 5); - sbPadPlugin->Add(PADConfig, 0, wxALL, 5); - sPlugins->Add(sbPadPlugin, 0, wxEXPAND|wxALL, 5); - - sbWiimotePlugin->Add(WiimoteSelection, 1, wxEXPAND|wxALL, 5); - sbWiimotePlugin->Add(WiimoteConfig, 0, wxALL, 5); - sPlugins->Add(sbWiimotePlugin, 0, wxEXPAND|wxALL, 5); - PluginPage->SetSizer(sPlugins); - sPlugins->Layout(); - Fit(); - Center(); -} - -void CConfigMain::OnClose(wxCloseEvent& WXUNUSED (event)) -{ - Destroy(); - - /* First check that we did successfully populate m_SYSCONF earlier, otherwise don't - save anything, it will be a corrupted file */ - if(m_bSysconfOK) - { - // Save SYSCONF with the new settings - pStream = fopen(FullSYSCONFPath.c_str(), "wb"); - if (pStream != NULL) - { - fwrite(m_SYSCONF, 1, 0x4000, pStream); - fclose(pStream); - } - else - { - PanicAlert("Could not write to %s", FullSYSCONFPath.c_str()); - } - } - - // save the config... dolphin crashes by far to often to save the settings on closing only - SConfig::GetInstance().SaveSettings(); -} - -void CConfigMain::CloseClick(wxCommandEvent& WXUNUSED (event)) -{ - Close(); -} - -void CConfigMain::CoreSettingsChanged(wxCommandEvent& event) -{ - switch (event.GetId()) - { - case ID_ALLWAYS_HLEBIOS: - SConfig::GetInstance().m_LocalCoreStartupParameter.bHLEBios = AllwaysHLEBIOS->IsChecked(); - break; - case ID_USEDYNAREC: - SConfig::GetInstance().m_LocalCoreStartupParameter.bUseJIT = UseDynaRec->IsChecked(); - break; - case ID_USEDUALCORE: - SConfig::GetInstance().m_LocalCoreStartupParameter.bUseDualCore = UseDualCore->IsChecked(); - break; - case ID_LOCKTHREADS: - SConfig::GetInstance().m_LocalCoreStartupParameter.bLockThreads = LockThreads->IsChecked(); - break; - case ID_OPTIMIZEQUANTIZERS: - SConfig::GetInstance().m_LocalCoreStartupParameter.bOptimizeQuantizers = OptimizeQuantizers->IsChecked(); - break; - case ID_IDLESKIP: - SConfig::GetInstance().m_LocalCoreStartupParameter.bSkipIdle = SkipIdle->IsChecked(); - break; - case ID_ENABLECHEATS: - SConfig::GetInstance().m_LocalCoreStartupParameter.bEnableCheats = EnableCheats->IsChecked(); - break; - } -} - -void CConfigMain::GCSettingsChanged(wxCommandEvent& event) -{ - switch (event.GetId()) - { - case ID_GC_SRAM_LNG: - SConfig::GetInstance().m_LocalCoreStartupParameter.SelectedLanguage = GCSystemLang->GetSelection(); - break; - } -} - -void CConfigMain::WiiSettingsChanged(wxCommandEvent& event) -{ - switch (event.GetId()) - { - case ID_WII_BT_BAR: - m_SYSCONF[BT_BAR] = WiiSensBarPos->GetSelection(); - break; - case ID_WII_IPL_AR: - m_SYSCONF[IPL_AR] = WiiAspectRatio->GetSelection(); - break; - case ID_WII_IPL_SSV: - m_SYSCONF[IPL_SSV] = WiiScreenSaver->IsChecked(); - break; - case ID_WII_IPL_LNG: - m_SYSCONF[IPL_LNG] = WiiSystemLang->GetSelection(); - break; - case ID_WII_IPL_PGS: - m_SYSCONF[IPL_PGS] = WiiProgressiveScan->IsChecked(); - break; - case ID_WII_IPL_E60: - m_SYSCONF[IPL_E60] = WiiEuRGB60->IsChecked(); - break; - } -} - -void CConfigMain::ISOPathsSelectionChanged(wxCommandEvent& WXUNUSED (event)) -{ - if (!ISOPaths->GetStringSelection().empty()) - { - RemoveISOPath->Enable(true); - } - else - { - RemoveISOPath->Enable(false); - } -} - -void CConfigMain::AddRemoveISOPaths(wxCommandEvent& event) -{ - if (event.GetId() == ID_ADDISOPATH) - { - wxString dirHome; - wxGetHomeDir(&dirHome); - - wxDirDialog dialog(this, _T("Choose a directory to add"), dirHome, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST); - - if (dialog.ShowModal() == wxID_OK) - { - if (ISOPaths->FindString(dialog.GetPath()) != -1) - { - wxMessageBox(_("The chosen directory is already in the list"), _("Error"), wxOK); - } - else - { - bRefreshList = true; - ISOPaths->Append(dialog.GetPath()); - } - } - } - else - { - bRefreshList = true; - ISOPaths->Delete(ISOPaths->GetSelection()); - } - - // Save changes right away - SConfig::GetInstance().m_ISOFolder.clear(); - - for (unsigned int i = 0; i < ISOPaths->GetCount(); i++) - SConfig::GetInstance().m_ISOFolder.push_back(std::string(ISOPaths->GetStrings()[i].ToAscii())); -} - -void CConfigMain::DefaultISOChanged(wxFileDirPickerEvent& WXUNUSED (event)) -{ - SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDefaultGCM = DefaultISO->GetPath().ToAscii(); -} - -void CConfigMain::DVDRootChanged(wxFileDirPickerEvent& WXUNUSED (event)) -{ - SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDVDRoot = DVDRoot->GetPath().ToAscii(); -} - -void CConfigMain::OnSelectionChanged(wxCommandEvent& WXUNUSED (event)) -{ - GetFilename(GraphicSelection, SConfig::GetInstance().m_LocalCoreStartupParameter.m_strVideoPlugin); - GetFilename(DSPSelection, SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDSPPlugin); - GetFilename(PADSelection, SConfig::GetInstance().m_LocalCoreStartupParameter.m_strPadPlugin); - GetFilename(WiimoteSelection, SConfig::GetInstance().m_LocalCoreStartupParameter.m_strWiimotePlugin); -} - -void CConfigMain::OnConfig(wxCommandEvent& event) -{ - switch (event.GetId()) - { - case ID_GRAPHIC_CONFIG: - CallConfig(GraphicSelection); - break; - - case ID_DSP_CONFIG: - CallConfig(DSPSelection); - break; - - case ID_PAD_CONFIG: - CallConfig(PADSelection); - break; - - case ID_WIIMOTE_CONFIG: - CallConfig(WiimoteSelection); - break; - } -} - -void CConfigMain::FillChoiceBox(wxChoice* _pChoice, int _PluginType, const std::string& _SelectFilename) -{ - _pChoice->Clear(); - - int Index = -1; - const CPluginInfos& rInfos = CPluginManager::GetInstance().GetPluginInfos(); - - for (size_t i = 0; i < rInfos.size(); i++) - { - const PLUGIN_INFO& rPluginInfo = rInfos[i].GetPluginInfo(); - - if (rPluginInfo.Type == _PluginType) - { - wxString temp; - temp = wxString::FromAscii(rInfos[i].GetPluginInfo().Name); - int NewIndex = _pChoice->Append(temp, (void*)&rInfos[i]); - - if (rInfos[i].GetFileName() == _SelectFilename) - { - Index = NewIndex; - } - } - } - - _pChoice->Select(Index); -} - -void CConfigMain::CallConfig(wxChoice* _pChoice) -{ - int Index = _pChoice->GetSelection(); - - if (Index >= 0) - { - const CPluginInfo* pInfo = static_cast(_pChoice->GetClientData(Index)); - - if (pInfo != NULL) - CPluginManager::GetInstance().OpenConfig((HWND) this->GetHandle(), pInfo->GetFileName().c_str()); - } -} - -bool CConfigMain::GetFilename(wxChoice* _pChoice, std::string& _rFilename) -{ - _rFilename.clear(); - - int Index = _pChoice->GetSelection(); - printf("%i\n", Index); - - if (Index >= 0) - { - const CPluginInfo* pInfo = static_cast(_pChoice->GetClientData(Index)); - _rFilename = pInfo->GetFileName(); - printf("%s\n", _rFilename.c_str()); - return(true); - } - - return(false); -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include + +#include "Globals.h" + +#include "ConfigMain.h" +#include "PluginManager.h" + +BEGIN_EVENT_TABLE(CConfigMain, wxDialog) + +EVT_CLOSE(CConfigMain::OnClose) +EVT_BUTTON(ID_CLOSE, CConfigMain::CloseClick) +EVT_CHECKBOX(ID_ALLWAYS_HLEBIOS, CConfigMain::CoreSettingsChanged) +EVT_CHECKBOX(ID_USEDYNAREC, CConfigMain::CoreSettingsChanged) +EVT_CHECKBOX(ID_USEDUALCORE, CConfigMain::CoreSettingsChanged) +EVT_CHECKBOX(ID_LOCKTHREADS, CConfigMain::CoreSettingsChanged) +EVT_CHECKBOX(ID_OPTIMIZEQUANTIZERS, CConfigMain::CoreSettingsChanged) +EVT_CHECKBOX(ID_IDLESKIP, CConfigMain::CoreSettingsChanged) +EVT_CHECKBOX(ID_ENABLECHEATS, CConfigMain::CoreSettingsChanged) +EVT_CHOICE(ID_GC_SRAM_LNG, CConfigMain::GCSettingsChanged) +EVT_CHOICE(ID_WII_BT_BAR, CConfigMain::WiiSettingsChanged) +EVT_CHECKBOX(ID_WII_IPL_SSV, CConfigMain::WiiSettingsChanged) +EVT_CHECKBOX(ID_WII_IPL_PGS, CConfigMain::WiiSettingsChanged) +EVT_CHECKBOX(ID_WII_IPL_E60, CConfigMain::WiiSettingsChanged) +EVT_CHOICE(ID_WII_IPL_AR, CConfigMain::WiiSettingsChanged) +EVT_CHOICE(ID_WII_IPL_LNG, CConfigMain::WiiSettingsChanged) +EVT_LISTBOX(ID_ISOPATHS, CConfigMain::ISOPathsSelectionChanged) +EVT_BUTTON(ID_ADDISOPATH, CConfigMain::AddRemoveISOPaths) +EVT_BUTTON(ID_REMOVEISOPATH, CConfigMain::AddRemoveISOPaths) +EVT_FILEPICKER_CHANGED(ID_DEFAULTISO, CConfigMain::DefaultISOChanged) +EVT_DIRPICKER_CHANGED(ID_DVDROOT, CConfigMain::DVDRootChanged) +EVT_CHOICE(ID_GRAPHIC_CB, CConfigMain::OnSelectionChanged) +EVT_BUTTON(ID_GRAPHIC_CONFIG, CConfigMain::OnConfig) +EVT_CHOICE(ID_DSP_CB, CConfigMain::OnSelectionChanged) +EVT_BUTTON(ID_DSP_CONFIG, CConfigMain::OnConfig) +EVT_CHOICE(ID_PAD_CB, CConfigMain::OnSelectionChanged) +EVT_BUTTON(ID_PAD_CONFIG, CConfigMain::OnConfig) +EVT_CHOICE(ID_WIIMOTE_CB, CConfigMain::OnSelectionChanged) +EVT_BUTTON(ID_WIIMOTE_CONFIG, CConfigMain::OnConfig) + +END_EVENT_TABLE() + +CConfigMain::CConfigMain(wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& position, const wxSize& size, long style) + : wxDialog(parent, id, title, position, size, style) +{ + bRefreshList = false; + + // Load Wii SYSCONF + FullSYSCONFPath = FULL_WII_USER_DIR "shared2/sys/SYSCONF"; + pStream = NULL; + pStream = fopen(FullSYSCONFPath.c_str(), "rb"); + if (pStream != NULL) + { + fread(m_SYSCONF, 1, 0x4000, pStream); + fclose(pStream); + m_bSysconfOK = true; + } + else + { + PanicAlert("Could not read %s. Please recover the SYSCONF file to that location.", FullSYSCONFPath.c_str()); + m_bSysconfOK = false; + } + + CreateGUIControls(); + for(u32 i = 0; i < SConfig::GetInstance().m_ISOFolder.size(); i++) + { + ISOPaths->Append(wxString(SConfig::GetInstance().m_ISOFolder[i].c_str(), wxConvUTF8)); + } +} + +CConfigMain::~CConfigMain() +{ +} + +void CConfigMain::CreateGUIControls() +{ + Notebook = new wxNotebook(this, ID_NOTEBOOK, wxDefaultPosition, wxDefaultSize); + + GeneralPage = new wxPanel(Notebook, ID_GENERALPAGE, wxDefaultPosition, wxDefaultSize); + Notebook->AddPage(GeneralPage, wxT("Core")); + GamecubePage = new wxPanel(Notebook, ID_GAMECUBEPAGE, wxDefaultPosition, wxDefaultSize); + Notebook->AddPage(GamecubePage, wxT("Gamecube")); + WiiPage = new wxPanel(Notebook, ID_WIIPAGE, wxDefaultPosition, wxDefaultSize); + Notebook->AddPage(WiiPage, wxT("Wii")); + PathsPage = new wxPanel(Notebook, ID_PATHSPAGE, wxDefaultPosition, wxDefaultSize); + Notebook->AddPage(PathsPage, wxT("Paths")); + PluginPage = new wxPanel(Notebook, ID_PLUGINPAGE, wxDefaultPosition, wxDefaultSize); + Notebook->AddPage(PluginPage, wxT("Plugins")); + + m_Close = new wxButton(this, ID_CLOSE, wxT("Close"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + + wxBoxSizer* sButtons; + sButtons = new wxBoxSizer(wxHORIZONTAL); + sButtons->Add(0, 0, 1, wxEXPAND, 5); + sButtons->Add(m_Close, 0, wxALL, 5); + + wxBoxSizer* sMain; + sMain = new wxBoxSizer(wxVERTICAL); + sMain->Add(Notebook, 1, wxEXPAND|wxALL, 5); + sMain->Add(sButtons, 0, wxEXPAND, 5); + + this->SetSizer(sMain); + this->Layout(); + + // Core page + sbBasic = new wxStaticBoxSizer(wxVERTICAL, GeneralPage, wxT("Basic Settings")); + UseDualCore = new wxCheckBox(GeneralPage, ID_USEDUALCORE, wxT("Enable Dual Core"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + UseDualCore->SetValue(SConfig::GetInstance().m_LocalCoreStartupParameter.bUseDualCore); + SkipIdle = new wxCheckBox(GeneralPage, ID_IDLESKIP, wxT("Enable Idle Skipping"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + SkipIdle->SetValue(SConfig::GetInstance().m_LocalCoreStartupParameter.bSkipIdle); + EnableCheats = new wxCheckBox(GeneralPage, ID_ENABLECHEATS, wxT("Enable Cheats"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + EnableCheats->SetValue(SConfig::GetInstance().m_LocalCoreStartupParameter.bEnableCheats); + + sbAdvanced = new wxStaticBoxSizer(wxVERTICAL, GeneralPage, wxT("Advanced Settings")); + AllwaysHLEBIOS = new wxCheckBox(GeneralPage, ID_ALLWAYS_HLEBIOS, wxT("HLE the BIOS all the time"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + AllwaysHLEBIOS->SetValue(SConfig::GetInstance().m_LocalCoreStartupParameter.bHLEBios); + UseDynaRec = new wxCheckBox(GeneralPage, ID_USEDYNAREC, wxT("Enable Dynamic Recompilation"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + UseDynaRec->SetValue(SConfig::GetInstance().m_LocalCoreStartupParameter.bUseJIT); + LockThreads = new wxCheckBox(GeneralPage, ID_LOCKTHREADS, wxT("Lock threads to cores"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + LockThreads->SetValue(SConfig::GetInstance().m_LocalCoreStartupParameter.bLockThreads); + OptimizeQuantizers = new wxCheckBox(GeneralPage, ID_OPTIMIZEQUANTIZERS, wxT("Optimize Quantizers"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + OptimizeQuantizers->SetValue(SConfig::GetInstance().m_LocalCoreStartupParameter.bOptimizeQuantizers); + + sGeneral = new wxBoxSizer(wxVERTICAL); + sbBasic->Add(UseDualCore, 0, wxALL, 5); + sbBasic->Add(SkipIdle, 0, wxALL, 5); + sbBasic->Add(EnableCheats, 0, wxALL, 5); + sGeneral->Add(sbBasic, 0, wxEXPAND|wxALL, 5); + sGeneral->AddStretchSpacer(); + + sbAdvanced->Add(AllwaysHLEBIOS, 0, wxALL, 5); + sbAdvanced->Add(UseDynaRec, 0, wxALL, 5); + sbAdvanced->Add(LockThreads, 0, wxALL, 5); + sbAdvanced->Add(OptimizeQuantizers, 0, wxALL, 5); + sGeneral->Add(sbAdvanced, 0, wxEXPAND|wxALL, 5); + GeneralPage->SetSizer(sGeneral); + sGeneral->Layout(); + + // Gamecube page + sbGamecubeIPLSettings = new wxStaticBoxSizer(wxVERTICAL, GamecubePage, wxT("IPL Settings")); + arrayStringFor_GCSystemLang.Add(wxT("English")); + arrayStringFor_GCSystemLang.Add(wxT("German")); + arrayStringFor_GCSystemLang.Add(wxT("French")); + arrayStringFor_GCSystemLang.Add(wxT("Spanish")); + arrayStringFor_GCSystemLang.Add(wxT("Italian")); + arrayStringFor_GCSystemLang.Add(wxT("Dutch")); + GCSystemLangText = new wxStaticText(GamecubePage, ID_GC_SRAM_LNG_TEXT, wxT("System Language:"), wxDefaultPosition, wxDefaultSize); + GCSystemLang = new wxChoice(GamecubePage, ID_GC_SRAM_LNG, wxDefaultPosition, wxDefaultSize, arrayStringFor_GCSystemLang, 0, wxDefaultValidator); + GCSystemLang->SetSelection(SConfig::GetInstance().m_LocalCoreStartupParameter.SelectedLanguage); + + sGamecube = new wxBoxSizer(wxVERTICAL); + sGamecubeIPLSettings = new wxGridBagSizer(0, 0); + sGamecubeIPLSettings->Add(GCSystemLangText, wxGBPosition(0, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); + sGamecubeIPLSettings->Add(GCSystemLang, wxGBPosition(0, 1), wxGBSpan(1, 1), wxALL, 5); + sbGamecubeIPLSettings->Add(sGamecubeIPLSettings); + sGamecube->Add(sbGamecubeIPLSettings, 0, wxEXPAND|wxALL, 5); + GamecubePage->SetSizer(sGamecube); + sGamecube->Layout(); + + // Wii SYSCONF page + sbWiimoteSettings = new wxStaticBoxSizer(wxVERTICAL, WiiPage, wxT("Wiimote Settings")); + arrayStringFor_WiiSensBarPos.Add(wxT("Bottom")); arrayStringFor_WiiSensBarPos.Add(wxT("Top")); + WiiSensBarPosText = new wxStaticText(WiiPage, ID_WII_BT_BAR_TEXT, wxT("Sensor Bar Position:"), wxDefaultPosition, wxDefaultSize); + WiiSensBarPos = new wxChoice(WiiPage, ID_WII_BT_BAR, wxDefaultPosition, wxDefaultSize, arrayStringFor_WiiSensBarPos, 0, wxDefaultValidator); + WiiSensBarPos->SetSelection(m_SYSCONF[BT_BAR]); + + sbWiiIPLSettings = new wxStaticBoxSizer(wxVERTICAL, WiiPage, wxT("IPL Settings")); + WiiScreenSaver = new wxCheckBox(WiiPage, ID_WII_IPL_SSV, wxT("Enable Screen Saver (burn-in reduction)"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + WiiScreenSaver->SetValue(m_SYSCONF[IPL_SSV]!=0); + WiiProgressiveScan = new wxCheckBox(WiiPage, ID_WII_IPL_PGS, wxT("Enable Progressive Scan"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + WiiProgressiveScan->SetValue(m_SYSCONF[IPL_PGS]!=0); + WiiEuRGB60 = new wxCheckBox(WiiPage, ID_WII_IPL_E60, wxT("Use EuRGB60 Mode (PAL6)"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + WiiEuRGB60->SetValue(m_SYSCONF[IPL_E60]!=0); + arrayStringFor_WiiAspectRatio.Add(wxT("4:3")); arrayStringFor_WiiAspectRatio.Add(wxT("16:9")); + WiiAspectRatioText = new wxStaticText(WiiPage, ID_WII_IPL_AR_TEXT, wxT("Aspect Ratio:"), wxDefaultPosition, wxDefaultSize); + WiiAspectRatio = new wxChoice(WiiPage, ID_WII_IPL_AR, wxDefaultPosition, wxDefaultSize, arrayStringFor_WiiAspectRatio, 0, wxDefaultValidator); + WiiAspectRatio->SetSelection(m_SYSCONF[IPL_AR]); + arrayStringFor_WiiSystemLang = arrayStringFor_GCSystemLang; + arrayStringFor_WiiSystemLang.Insert(wxT("Japanese"), 0); + WiiSystemLangText = new wxStaticText(WiiPage, ID_WII_IPL_LNG_TEXT, wxT("System Language:"), wxDefaultPosition, wxDefaultSize); + WiiSystemLang = new wxChoice(WiiPage, ID_WII_IPL_LNG, wxDefaultPosition, wxDefaultSize, arrayStringFor_WiiSystemLang, 0, wxDefaultValidator); + WiiSystemLang->SetSelection(m_SYSCONF[IPL_LNG]); + + sWii = new wxBoxSizer(wxVERTICAL); + sWiimoteSettings = new wxGridBagSizer(0, 0); + sWiimoteSettings->Add(WiiSensBarPosText, wxGBPosition(0, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); + sWiimoteSettings->Add(WiiSensBarPos, wxGBPosition(0, 1), wxGBSpan(1, 1), wxALL, 5); + sbWiimoteSettings->Add(sWiimoteSettings); + sWii->Add(sbWiimoteSettings, 0, wxEXPAND|wxALL, 5); + + sWiiIPLSettings = new wxGridBagSizer(0, 0); + sWiiIPLSettings->Add(WiiScreenSaver, wxGBPosition(0, 0), wxGBSpan(1, 2), wxALL, 5); + sWiiIPLSettings->Add(WiiProgressiveScan, wxGBPosition(1, 0), wxGBSpan(1, 2), wxALL, 5); + sWiiIPLSettings->Add(WiiEuRGB60, wxGBPosition(2, 0), wxGBSpan(1, 2), wxALL, 5); + sWiiIPLSettings->Add(WiiAspectRatioText, wxGBPosition(3, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); + sWiiIPLSettings->Add(WiiAspectRatio, wxGBPosition(3, 1), wxGBSpan(1, 1), wxALL, 5); + sWiiIPLSettings->Add(WiiSystemLangText, wxGBPosition(4, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); + sWiiIPLSettings->Add(WiiSystemLang, wxGBPosition(4, 1), wxGBSpan(1, 1), wxALL, 5); + sbWiiIPLSettings->Add(sWiiIPLSettings); + sWii->Add(sbWiiIPLSettings, 0, wxEXPAND|wxALL, 5); + WiiPage->SetSizer(sWii); + sWii->Layout(); + + // Paths page + sbISOPaths = new wxStaticBoxSizer(wxVERTICAL, PathsPage, wxT("ISO Directories")); + ISOPaths = new wxListBox(PathsPage, ID_ISOPATHS, wxDefaultPosition, wxDefaultSize, arrayStringFor_ISOPaths, wxLB_SINGLE, wxDefaultValidator); + AddISOPath = new wxButton(PathsPage, ID_ADDISOPATH, wxT("Add..."), wxDefaultPosition, wxDefaultSize, 0); + RemoveISOPath = new wxButton(PathsPage, ID_REMOVEISOPATH, wxT("Remove"), wxDefaultPosition, wxDefaultSize, 0); + RemoveISOPath->Enable(false); + + DefaultISOText = new wxStaticText(PathsPage, ID_DEFAULTISO_TEXT, wxT("Default ISO:"), wxDefaultPosition, wxDefaultSize); + DefaultISO = new wxFilePickerCtrl(PathsPage, ID_DEFAULTISO, wxEmptyString, wxT("Choose a default ISO:"), + wxString::Format(wxT("All GC/Wii images (gcm, iso, gcz)|*.gcm;*.iso;*.gcz|All files (%s)|%s"), wxFileSelectorDefaultWildcardStr, wxFileSelectorDefaultWildcardStr), + wxDefaultPosition, wxDefaultSize, wxFLP_USE_TEXTCTRL|wxFLP_FILE_MUST_EXIST|wxFLP_OPEN); + DefaultISO->SetPath(wxString::FromAscii(SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDefaultGCM.c_str())); + + DVDRootText = new wxStaticText(PathsPage, ID_DVDROOT_TEXT, wxT("DVD Root:"), wxDefaultPosition, wxDefaultSize); + DVDRoot = new wxDirPickerCtrl(PathsPage, ID_DVDROOT, wxEmptyString, wxT("Choose a DVD root directory:"), wxDefaultPosition, wxDefaultSize, wxDIRP_USE_TEXTCTRL); + DVDRoot->SetPath(wxString::FromAscii(SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDVDRoot.c_str())); + + sPaths = new wxBoxSizer(wxVERTICAL); + + sbISOPaths->Add(ISOPaths, 1, wxEXPAND|wxALL, 0); + + sISOButtons = new wxBoxSizer(wxHORIZONTAL); + sISOButtons->AddStretchSpacer(1); + sISOButtons->Add(AddISOPath, 0, wxALL, 0); + sISOButtons->Add(RemoveISOPath, 0, wxALL, 0); + sbISOPaths->Add(sISOButtons, 0, wxEXPAND|wxALL, 5); + sPaths->Add(sbISOPaths, 1, wxEXPAND|wxALL, 5); + + sOtherPaths = new wxGridBagSizer(0, 0); + sOtherPaths->AddGrowableCol(1); + sOtherPaths->Add(DefaultISOText, wxGBPosition(0, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); + sOtherPaths->Add(DefaultISO, wxGBPosition(0, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); + sOtherPaths->Add(DVDRootText, wxGBPosition(1, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); + sOtherPaths->Add(DVDRoot, wxGBPosition(1, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); + sPaths->Add(sOtherPaths, 0, wxEXPAND|wxALL, 5); + PathsPage->SetSizer(sPaths); + sPaths->Layout(); + + // Plugin page + sbGraphicsPlugin = new wxStaticBoxSizer(wxHORIZONTAL, PluginPage, wxT("Graphics")); + GraphicSelection = new wxChoice(PluginPage, ID_GRAPHIC_CB, wxDefaultPosition, wxDefaultSize, NULL, 0, wxDefaultValidator); + GraphicConfig = new wxButton(PluginPage, ID_GRAPHIC_CONFIG, wxT("Config..."), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + + sbDSPPlugin = new wxStaticBoxSizer(wxHORIZONTAL, PluginPage, wxT("DSP")); + DSPSelection = new wxChoice(PluginPage, ID_DSP_CB, wxDefaultPosition, wxDefaultSize, NULL, 0, wxDefaultValidator); + DSPConfig = new wxButton(PluginPage, ID_DSP_CONFIG, wxT("Config..."), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + + sbPadPlugin = new wxStaticBoxSizer(wxHORIZONTAL, PluginPage, wxT("Pad")); + PADSelection = new wxChoice(PluginPage, ID_PAD_CB, wxDefaultPosition, wxDefaultSize, NULL, 0, wxDefaultValidator); + PADConfig = new wxButton(PluginPage, ID_PAD_CONFIG, wxT("Config..."), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + + sbWiimotePlugin = new wxStaticBoxSizer(wxHORIZONTAL, PluginPage, wxT("Wiimote")); + WiimoteSelection = new wxChoice(PluginPage, ID_WIIMOTE_CB, wxDefaultPosition, wxDefaultSize, NULL, 0, wxDefaultValidator); + WiimoteConfig = new wxButton(PluginPage, ID_WIIMOTE_CONFIG, wxT("Config..."), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + + FillChoiceBox(GraphicSelection, PLUGIN_TYPE_VIDEO, SConfig::GetInstance().m_LocalCoreStartupParameter.m_strVideoPlugin); + FillChoiceBox(DSPSelection, PLUGIN_TYPE_DSP, SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDSPPlugin); + FillChoiceBox(PADSelection, PLUGIN_TYPE_PAD, SConfig::GetInstance().m_LocalCoreStartupParameter.m_strPadPlugin); + FillChoiceBox(WiimoteSelection, PLUGIN_TYPE_WIIMOTE, SConfig::GetInstance().m_LocalCoreStartupParameter.m_strWiimotePlugin); + + sPlugins = new wxBoxSizer(wxVERTICAL); + sbGraphicsPlugin->Add(GraphicSelection, 1, wxEXPAND|wxALL, 5); + sbGraphicsPlugin->Add(GraphicConfig, 0, wxALL, 5); + sPlugins->Add(sbGraphicsPlugin, 0, wxEXPAND|wxALL, 5); + + sbDSPPlugin->Add(DSPSelection, 1, wxEXPAND|wxALL, 5); + sbDSPPlugin->Add(DSPConfig, 0, wxALL, 5); + sPlugins->Add(sbDSPPlugin, 0, wxEXPAND|wxALL, 5); + + sbPadPlugin->Add(PADSelection, 1, wxEXPAND|wxALL, 5); + sbPadPlugin->Add(PADConfig, 0, wxALL, 5); + sPlugins->Add(sbPadPlugin, 0, wxEXPAND|wxALL, 5); + + sbWiimotePlugin->Add(WiimoteSelection, 1, wxEXPAND|wxALL, 5); + sbWiimotePlugin->Add(WiimoteConfig, 0, wxALL, 5); + sPlugins->Add(sbWiimotePlugin, 0, wxEXPAND|wxALL, 5); + PluginPage->SetSizer(sPlugins); + sPlugins->Layout(); + Fit(); + Center(); +} + +void CConfigMain::OnClose(wxCloseEvent& WXUNUSED (event)) +{ + Destroy(); + + /* First check that we did successfully populate m_SYSCONF earlier, otherwise don't + save anything, it will be a corrupted file */ + if(m_bSysconfOK) + { + // Save SYSCONF with the new settings + pStream = fopen(FullSYSCONFPath.c_str(), "wb"); + if (pStream != NULL) + { + fwrite(m_SYSCONF, 1, 0x4000, pStream); + fclose(pStream); + } + else + { + PanicAlert("Could not write to %s", FullSYSCONFPath.c_str()); + } + } + + // save the config... dolphin crashes by far to often to save the settings on closing only + SConfig::GetInstance().SaveSettings(); +} + +void CConfigMain::CloseClick(wxCommandEvent& WXUNUSED (event)) +{ + Close(); +} + +void CConfigMain::CoreSettingsChanged(wxCommandEvent& event) +{ + switch (event.GetId()) + { + case ID_ALLWAYS_HLEBIOS: + SConfig::GetInstance().m_LocalCoreStartupParameter.bHLEBios = AllwaysHLEBIOS->IsChecked(); + break; + case ID_USEDYNAREC: + SConfig::GetInstance().m_LocalCoreStartupParameter.bUseJIT = UseDynaRec->IsChecked(); + break; + case ID_USEDUALCORE: + SConfig::GetInstance().m_LocalCoreStartupParameter.bUseDualCore = UseDualCore->IsChecked(); + break; + case ID_LOCKTHREADS: + SConfig::GetInstance().m_LocalCoreStartupParameter.bLockThreads = LockThreads->IsChecked(); + break; + case ID_OPTIMIZEQUANTIZERS: + SConfig::GetInstance().m_LocalCoreStartupParameter.bOptimizeQuantizers = OptimizeQuantizers->IsChecked(); + break; + case ID_IDLESKIP: + SConfig::GetInstance().m_LocalCoreStartupParameter.bSkipIdle = SkipIdle->IsChecked(); + break; + case ID_ENABLECHEATS: + SConfig::GetInstance().m_LocalCoreStartupParameter.bEnableCheats = EnableCheats->IsChecked(); + break; + } +} + +void CConfigMain::GCSettingsChanged(wxCommandEvent& event) +{ + switch (event.GetId()) + { + case ID_GC_SRAM_LNG: + SConfig::GetInstance().m_LocalCoreStartupParameter.SelectedLanguage = GCSystemLang->GetSelection(); + break; + } +} + +void CConfigMain::WiiSettingsChanged(wxCommandEvent& event) +{ + switch (event.GetId()) + { + case ID_WII_BT_BAR: + m_SYSCONF[BT_BAR] = WiiSensBarPos->GetSelection(); + break; + case ID_WII_IPL_AR: + m_SYSCONF[IPL_AR] = WiiAspectRatio->GetSelection(); + break; + case ID_WII_IPL_SSV: + m_SYSCONF[IPL_SSV] = WiiScreenSaver->IsChecked(); + break; + case ID_WII_IPL_LNG: + m_SYSCONF[IPL_LNG] = WiiSystemLang->GetSelection(); + break; + case ID_WII_IPL_PGS: + m_SYSCONF[IPL_PGS] = WiiProgressiveScan->IsChecked(); + break; + case ID_WII_IPL_E60: + m_SYSCONF[IPL_E60] = WiiEuRGB60->IsChecked(); + break; + } +} + +void CConfigMain::ISOPathsSelectionChanged(wxCommandEvent& WXUNUSED (event)) +{ + if (!ISOPaths->GetStringSelection().empty()) + { + RemoveISOPath->Enable(true); + } + else + { + RemoveISOPath->Enable(false); + } +} + +void CConfigMain::AddRemoveISOPaths(wxCommandEvent& event) +{ + if (event.GetId() == ID_ADDISOPATH) + { + wxString dirHome; + wxGetHomeDir(&dirHome); + + wxDirDialog dialog(this, _T("Choose a directory to add"), dirHome, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST); + + if (dialog.ShowModal() == wxID_OK) + { + if (ISOPaths->FindString(dialog.GetPath()) != -1) + { + wxMessageBox(_("The chosen directory is already in the list"), _("Error"), wxOK); + } + else + { + bRefreshList = true; + ISOPaths->Append(dialog.GetPath()); + } + } + } + else + { + bRefreshList = true; + ISOPaths->Delete(ISOPaths->GetSelection()); + } + + // Save changes right away + SConfig::GetInstance().m_ISOFolder.clear(); + + for (unsigned int i = 0; i < ISOPaths->GetCount(); i++) + SConfig::GetInstance().m_ISOFolder.push_back(std::string(ISOPaths->GetStrings()[i].ToAscii())); +} + +void CConfigMain::DefaultISOChanged(wxFileDirPickerEvent& WXUNUSED (event)) +{ + SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDefaultGCM = DefaultISO->GetPath().ToAscii(); +} + +void CConfigMain::DVDRootChanged(wxFileDirPickerEvent& WXUNUSED (event)) +{ + SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDVDRoot = DVDRoot->GetPath().ToAscii(); +} + +void CConfigMain::OnSelectionChanged(wxCommandEvent& WXUNUSED (event)) +{ + GetFilename(GraphicSelection, SConfig::GetInstance().m_LocalCoreStartupParameter.m_strVideoPlugin); + GetFilename(DSPSelection, SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDSPPlugin); + GetFilename(PADSelection, SConfig::GetInstance().m_LocalCoreStartupParameter.m_strPadPlugin); + GetFilename(WiimoteSelection, SConfig::GetInstance().m_LocalCoreStartupParameter.m_strWiimotePlugin); +} + +void CConfigMain::OnConfig(wxCommandEvent& event) +{ + switch (event.GetId()) + { + case ID_GRAPHIC_CONFIG: + CallConfig(GraphicSelection); + break; + + case ID_DSP_CONFIG: + CallConfig(DSPSelection); + break; + + case ID_PAD_CONFIG: + CallConfig(PADSelection); + break; + + case ID_WIIMOTE_CONFIG: + CallConfig(WiimoteSelection); + break; + } +} + +void CConfigMain::FillChoiceBox(wxChoice* _pChoice, int _PluginType, const std::string& _SelectFilename) +{ + _pChoice->Clear(); + + int Index = -1; + const CPluginInfos& rInfos = CPluginManager::GetInstance().GetPluginInfos(); + + for (size_t i = 0; i < rInfos.size(); i++) + { + const PLUGIN_INFO& rPluginInfo = rInfos[i].GetPluginInfo(); + + if (rPluginInfo.Type == _PluginType) + { + wxString temp; + temp = wxString::FromAscii(rInfos[i].GetPluginInfo().Name); + int NewIndex = _pChoice->Append(temp, (void*)&rInfos[i]); + + if (rInfos[i].GetFileName() == _SelectFilename) + { + Index = NewIndex; + } + } + } + + _pChoice->Select(Index); +} + +void CConfigMain::CallConfig(wxChoice* _pChoice) +{ + int Index = _pChoice->GetSelection(); + + if (Index >= 0) + { + const CPluginInfo* pInfo = static_cast(_pChoice->GetClientData(Index)); + + if (pInfo != NULL) + CPluginManager::GetInstance().OpenConfig((HWND) this->GetHandle(), pInfo->GetFileName().c_str()); + } +} + +bool CConfigMain::GetFilename(wxChoice* _pChoice, std::string& _rFilename) +{ + _rFilename.clear(); + + int Index = _pChoice->GetSelection(); + printf("%i\n", Index); + + if (Index >= 0) + { + const CPluginInfo* pInfo = static_cast(_pChoice->GetClientData(Index)); + _rFilename = pInfo->GetFileName(); + printf("%s\n", _rFilename.c_str()); + return(true); + } + + return(false); +} diff --git a/Source/Core/DolphinWX/Src/Frame.cpp b/Source/Core/DolphinWX/Src/Frame.cpp index 10cbc22ee8..e38df2420c 100644 --- a/Source/Core/DolphinWX/Src/Frame.cpp +++ b/Source/Core/DolphinWX/Src/Frame.cpp @@ -1,726 +1,726 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Globals.h" -#include "Frame.h" -#include "FileUtil.h" - -#include "GameListCtrl.h" -#include "BootManager.h" - -#include "Common.h" -#include "Config.h" -#include "Core.h" -#include "State.h" -#include "ConfigMain.h" -#include "PluginManager.h" -#include "MemcardManager.h" -#include "CheatsWindow.h" -#include "AboutDolphin.h" - -#include - -// ---------------------------------------------------------------------------- -// resources -// ---------------------------------------------------------------------------- - -extern "C" { -#include "../resources/Dolphin.c" -#include "../resources/toolbar_browse.c" -#include "../resources/toolbar_file_open.c" -#include "../resources/toolbar_fullscreen.c" -#include "../resources/toolbar_help.c" -#include "../resources/toolbar_pause.c" -#include "../resources/toolbar_play.c" -#include "../resources/toolbar_plugin_dsp.c" -#include "../resources/toolbar_plugin_gfx.c" -#include "../resources/toolbar_plugin_options.c" -#include "../resources/toolbar_plugin_pad.c" -#include "../resources/toolbar_refresh.c" -#include "../resources/toolbar_stop.c" -}; - -#define wxGetBitmapFromMemory(name) _wxGetBitmapFromMemory(name, sizeof(name)) -inline wxBitmap _wxGetBitmapFromMemory(const unsigned char* data, int length) -{ - wxMemoryInputStream is(data, length); - return(wxBitmap(wxImage(is, wxBITMAP_TYPE_ANY, -1), -1)); -} - - -// ---------------------------------------------------------------------------- -// constants -// ---------------------------------------------------------------------------- - -static const long TOOLBAR_STYLE = wxTB_FLAT | wxTB_DOCKABLE | wxTB_TEXT; - -// ---------------------------------------------------------------------------- -// event tables -// ---------------------------------------------------------------------------- - -// Notice that wxID_HELP will be processed for the 'About' menu and the toolbar -// help button. - -const wxEventType wxEVT_HOST_COMMAND = wxNewEventType(); - -BEGIN_EVENT_TABLE(CFrame, wxFrame) -EVT_CLOSE(CFrame::OnClose) -EVT_MENU(wxID_OPEN, CFrame::OnOpen) -EVT_MENU(wxID_EXIT, CFrame::OnQuit) -EVT_MENU(IDM_HELPWEBSITE, CFrame::OnHelp) -EVT_MENU(IDM_HELPGOOGLECODE, CFrame::OnHelp) -EVT_MENU(IDM_HELPABOUT, CFrame::OnHelp) -EVT_MENU(wxID_REFRESH, CFrame::OnRefresh) -EVT_MENU(IDM_PLAY, CFrame::OnPlay) -EVT_MENU(IDM_STOP, CFrame::OnStop) -EVT_MENU(IDM_CONFIG_MAIN, CFrame::OnConfigMain) -EVT_MENU(IDM_CONFIG_GFX_PLUGIN, CFrame::OnPluginGFX) -EVT_MENU(IDM_CONFIG_DSP_PLUGIN, CFrame::OnPluginDSP) -EVT_MENU(IDM_CONFIG_PAD_PLUGIN, CFrame::OnPluginPAD) -EVT_MENU(IDM_CONFIG_WIIMOTE_PLUGIN, CFrame::OnPluginWiimote) -EVT_MENU(IDM_BROWSE, CFrame::OnBrowse) -EVT_MENU(IDM_MEMCARD, CFrame::OnMemcard) -EVT_MENU(IDM_CHEATS, CFrame::OnShow_CheatsWindow) -EVT_MENU(IDM_TOGGLE_FULLSCREEN, CFrame::OnToggleFullscreen) -EVT_MENU(IDM_TOGGLE_DUALCORE, CFrame::OnToggleDualCore) -EVT_MENU(IDM_TOGGLE_SKIPIDLE, CFrame::OnToggleSkipIdle) -EVT_MENU(IDM_TOGGLE_TOOLBAR, CFrame::OnToggleToolbar) -EVT_MENU(IDM_TOGGLE_STATUSBAR, CFrame::OnToggleStatusbar) -EVT_MENU(IDM_LOADSLOT1, CFrame::OnLoadState) -EVT_MENU(IDM_LOADSLOT2, CFrame::OnLoadState) -EVT_MENU(IDM_LOADSLOT3, CFrame::OnLoadState) -EVT_MENU(IDM_LOADSLOT4, CFrame::OnLoadState) -EVT_MENU(IDM_LOADSLOT5, CFrame::OnLoadState) -EVT_MENU(IDM_LOADSLOT6, CFrame::OnLoadState) -EVT_MENU(IDM_LOADSLOT7, CFrame::OnLoadState) -EVT_MENU(IDM_LOADSLOT8, CFrame::OnLoadState) -EVT_MENU(IDM_LOADSLOT9, CFrame::OnLoadState) -EVT_MENU(IDM_LOADSLOT10, CFrame::OnLoadState) -EVT_MENU(IDM_SAVESLOT1, CFrame::OnSaveState) -EVT_MENU(IDM_SAVESLOT2, CFrame::OnSaveState) -EVT_MENU(IDM_SAVESLOT3, CFrame::OnSaveState) -EVT_MENU(IDM_SAVESLOT4, CFrame::OnSaveState) -EVT_MENU(IDM_SAVESLOT5, CFrame::OnSaveState) -EVT_MENU(IDM_SAVESLOT6, CFrame::OnSaveState) -EVT_MENU(IDM_SAVESLOT7, CFrame::OnSaveState) -EVT_MENU(IDM_SAVESLOT8, CFrame::OnSaveState) -EVT_MENU(IDM_SAVESLOT9, CFrame::OnSaveState) -EVT_MENU(IDM_SAVESLOT10, CFrame::OnSaveState) -EVT_HOST_COMMAND(wxID_ANY, CFrame::OnHostMessage) -END_EVENT_TABLE() - -// ---------------------------------------------------------------------------- -// Other Windows -// ---------------------------------------------------------------------------- - -wxCheatsWindow* CheatsWindow; - -// ---------------------------------------------------------------------------- -// implementation -// ---------------------------------------------------------------------------- - -CFrame::CFrame(wxFrame* parent, - wxWindowID id, - const wxString& title, - const wxPoint& pos, - const wxSize& size, - long style) - : wxFrame(parent, id, title, pos, size, style) - , m_Panel(NULL) - , m_pStatusBar(NULL) - , m_pMenuBar(NULL) - , m_pBootProcessDialog(NULL) -{ - InitBitmaps(); - - // Give it an icon - wxIcon IconTemp; - IconTemp.CopyFromBitmap(wxGetBitmapFromMemory(dolphin_png)); - SetIcon(IconTemp); - - // Give it a status line - m_pStatusBar = CreateStatusBar(2); - int StylesField[] = {wxSB_FLAT, wxSB_FLAT}; - m_pStatusBar->SetStatusStyles(2, StylesField); - CreateMenu(); - - // This panel is the parent for rendering and it holds the gamelistctrl - m_Panel = new wxPanel(this); - - m_GameListCtrl = new CGameListCtrl(m_Panel, LIST_CTRL, - wxDefaultPosition, wxDefaultSize, - wxLC_REPORT | wxSUNKEN_BORDER | wxLC_ALIGN_LEFT); - - sizerPanel = new wxBoxSizer(wxHORIZONTAL); - sizerPanel->Add(m_GameListCtrl, 2, wxEXPAND); - m_Panel->SetSizer(sizerPanel); - - // Create the toolbar - RecreateToolbar(); - - Show(); - - CPluginManager::GetInstance().ScanForPlugins(this); - - //if we are ever going back to optional iso caching: - //m_GameListCtrl->Update(SConfig::GetInstance().m_LocalCoreStartupParameter.bEnableIsoCache); - m_GameListCtrl->Update(); - //sizerPanel->SetSizeHints(m_Panel); - - wxTheApp->Connect(wxID_ANY, wxEVT_KEY_DOWN, - wxKeyEventHandler(CFrame::OnKeyDown), - (wxObject*)0, this); - - UpdateGUI(); -} - -#ifdef _WIN32 - -WXLRESULT CFrame::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) -{ - switch (nMsg) - { - case WM_SYSCOMMAND: - switch (wParam) - { - case SC_SCREENSAVE: - case SC_MONITORPOWER: - return 0; - } - default: - return wxFrame::MSWWindowProc(nMsg, wParam, lParam); - } -} - -#endif - -void CFrame::CreateMenu() -{ - delete m_pMenuBar; - m_pMenuBar = new wxMenuBar(wxMB_DOCKABLE); - - // file menu - wxMenu* fileMenu = new wxMenu; - fileMenu->Append(wxID_OPEN, _T("&Open...\tCtrl+O")); - fileMenu->Append(wxID_REFRESH, _T("&Refresh")); - fileMenu->Append(IDM_BROWSE, _T("&Browse for ISOs...")); - - fileMenu->AppendSeparator(); - fileMenu->Append(wxID_EXIT, _T("E&xit"), _T("Alt+F4")); - m_pMenuBar->Append(fileMenu, _T("&File")); - - // emulation menu - wxMenu* emulationMenu = new wxMenu; - m_pMenuItemPlay = emulationMenu->Append(IDM_PLAY, _T("&Play")); - m_pMenuItemStop = emulationMenu->Append(IDM_STOP, _T("&Stop")); - emulationMenu->AppendSeparator(); - wxMenu *saveMenu = new wxMenu; - wxMenu *loadMenu = new wxMenu; - m_pMenuItemLoad = emulationMenu->AppendSubMenu(saveMenu, _T("&Load State")); - m_pMenuItemSave = emulationMenu->AppendSubMenu(loadMenu, _T("Sa&ve State")); - for (int i = 1; i < 10; i++) { - saveMenu->Append(IDM_LOADSLOT1 + i - 1, wxString::Format(_T("Slot %i\tF%i"), i, i)); - loadMenu->Append(IDM_SAVESLOT1 + i - 1, wxString::Format(_T("Slot %i\tShift+F%i"), i, i)); - } - m_pMenuBar->Append(emulationMenu, _T("&Emulation")); - - // options menu - wxMenu* pOptionsMenu = new wxMenu; - m_pPluginOptions = pOptionsMenu->Append(IDM_CONFIG_MAIN, _T("Co&nfigure...")); - pOptionsMenu->AppendSeparator(); - pOptionsMenu->Append(IDM_CONFIG_GFX_PLUGIN, _T("&GFX settings")); - pOptionsMenu->Append(IDM_CONFIG_DSP_PLUGIN, _T("&DSP settings")); - pOptionsMenu->Append(IDM_CONFIG_PAD_PLUGIN, _T("&PAD settings")); - pOptionsMenu->AppendSeparator(); - pOptionsMenu->Append(IDM_TOGGLE_FULLSCREEN, _T("&Fullscreen\tAlt+Enter")); - m_pMenuBar->Append(pOptionsMenu, _T("&Options")); - - // misc menu - wxMenu* miscMenu = new wxMenu; - miscMenu->AppendCheckItem(IDM_TOGGLE_TOOLBAR, _T("View &toolbar")); - miscMenu->Check(IDM_TOGGLE_TOOLBAR, true); - miscMenu->AppendCheckItem(IDM_TOGGLE_STATUSBAR, _T("View &statusbar")); - miscMenu->Check(IDM_TOGGLE_STATUSBAR, true); - miscMenu->AppendSeparator(); - miscMenu->Append(IDM_MEMCARD, _T("&Memcard manager")); - miscMenu->Append(IDM_CHEATS, _T("Action &Replay Manager")); - m_pMenuBar->Append(miscMenu, _T("&Misc")); - - // help menu - wxMenu* helpMenu = new wxMenu; - /*helpMenu->Append(wxID_HELP, _T("&Help")); - re-enable when there's something useful to display*/ - helpMenu->Append(IDM_HELPWEBSITE, _T("Dolphin &web site")); - helpMenu->Append(IDM_HELPGOOGLECODE, _T("Dolphin at &Google Code")); - helpMenu->AppendSeparator(); - helpMenu->Append(IDM_HELPABOUT, _T("&About...")); - m_pMenuBar->Append(helpMenu, _T("&Help")); - - // Associate the menu bar with the frame - SetMenuBar(m_pMenuBar); -} - - -void CFrame::PopulateToolbar(wxToolBar* toolBar) -{ - int w = m_Bitmaps[Toolbar_FileOpen].GetWidth(), - h = m_Bitmaps[Toolbar_FileOpen].GetHeight(); - - toolBar->SetToolBitmapSize(wxSize(w, h)); - toolBar->AddTool(wxID_OPEN, _T("Open"), m_Bitmaps[Toolbar_FileOpen], _T("Open file...")); - toolBar->AddTool(wxID_REFRESH, _T("Refresh"), m_Bitmaps[Toolbar_Refresh], _T("Refresh")); - toolBar->AddTool(IDM_BROWSE, _T("Browse"), m_Bitmaps[Toolbar_Browse], _T("Browse for an ISO directory...")); - toolBar->AddSeparator(); - m_pToolPlay = toolBar->AddTool(IDM_PLAY, _T("Play"), m_Bitmaps[Toolbar_Play], _T("Play")); - - toolBar->AddTool(IDM_STOP, _T("Stop"), m_Bitmaps[Toolbar_Stop], _T("Stop")); - toolBar->AddTool(IDM_TOGGLE_FULLSCREEN, _T("Fullscr."), m_Bitmaps[Toolbar_FullScreen], _T("Toggle Fullscreen")); - toolBar->AddSeparator(); - toolBar->AddTool(IDM_CONFIG_MAIN, _T("Config"), m_Bitmaps[Toolbar_PluginOptions], _T("Configure...")); - toolBar->AddTool(IDM_CONFIG_GFX_PLUGIN, _T("GFX"), m_Bitmaps[Toolbar_PluginGFX], _T("GFX settings")); - toolBar->AddTool(IDM_CONFIG_DSP_PLUGIN, _T("DSP"), m_Bitmaps[Toolbar_PluginDSP], _T("DSP settings")); - toolBar->AddTool(IDM_CONFIG_PAD_PLUGIN, _T("PAD"), m_Bitmaps[Toolbar_PluginPAD], _T("PAD settings")); - toolBar->AddTool(IDM_CONFIG_WIIMOTE_PLUGIN, _T("Wiimote"), m_Bitmaps[Toolbar_PluginPAD], _T("Wiimote settings")); - toolBar->AddSeparator(); - toolBar->AddTool(IDM_HELPABOUT, _T("About"), m_Bitmaps[Toolbar_Help], _T("About Dolphin")); - - // after adding the buttons to the toolbar, must call Realize() to reflect - // the changes - toolBar->Realize(); -} - - -void CFrame::RecreateToolbar() -{ - // delete and recreate the toolbar - wxToolBarBase* toolBar = GetToolBar(); - long style = toolBar ? toolBar->GetWindowStyle() : TOOLBAR_STYLE; - - delete toolBar; - SetToolBar(NULL); - - style &= ~(wxTB_HORIZONTAL | wxTB_VERTICAL | wxTB_BOTTOM | wxTB_RIGHT | wxTB_HORZ_LAYOUT | wxTB_TOP); - wxToolBar* theToolBar = CreateToolBar(style, ID_TOOLBAR); - - PopulateToolbar(theToolBar); - SetToolBar(theToolBar); - UpdateGUI(); -} - - -void CFrame::InitBitmaps() -{ - // load orignal size 48x48 - m_Bitmaps[Toolbar_FileOpen] = wxGetBitmapFromMemory(toolbar_file_open_png); - m_Bitmaps[Toolbar_Refresh] = wxGetBitmapFromMemory(toolbar_refresh_png); - m_Bitmaps[Toolbar_Browse] = wxGetBitmapFromMemory(toolbar_browse_png); - m_Bitmaps[Toolbar_Play] = wxGetBitmapFromMemory(toolbar_play_png); - m_Bitmaps[Toolbar_Stop] = wxGetBitmapFromMemory(toolbar_stop_png); - m_Bitmaps[Toolbar_Pause] = wxGetBitmapFromMemory(toolbar_pause_png); - m_Bitmaps[Toolbar_PluginOptions] = wxGetBitmapFromMemory(toolbar_plugin_options_png); - m_Bitmaps[Toolbar_PluginGFX] = wxGetBitmapFromMemory(toolbar_plugin_gfx_png); - m_Bitmaps[Toolbar_PluginDSP] = wxGetBitmapFromMemory(toolbar_plugin_dsp_png); - m_Bitmaps[Toolbar_PluginPAD] = wxGetBitmapFromMemory(toolbar_plugin_pad_png); - m_Bitmaps[Toolbar_FullScreen] = wxGetBitmapFromMemory(toolbar_fullscreen_png); - m_Bitmaps[Toolbar_Help] = wxGetBitmapFromMemory(toolbar_help_png); - - // scale to 24x24 for toolbar - for (size_t n = Toolbar_FileOpen; n < WXSIZEOF(m_Bitmaps); n++) - { - m_Bitmaps[n] = wxBitmap(m_Bitmaps[n].ConvertToImage().Scale(24, 24)); - } -} - - -void CFrame::OnOpen(wxCommandEvent& WXUNUSED (event)) -{ - if (Core::GetState() != Core::CORE_UNINITIALIZED) - return; - wxString path = wxFileSelector( - _T("Select the file to load"), - wxEmptyString, wxEmptyString, wxEmptyString, - wxString::Format - ( - _T("All GC/Wii files (elf, dol, gcm, iso)|*.elf;*.dol;*.gcm;*.iso;*.gcz|All files (%s)|%s"), - wxFileSelectorDefaultWildcardStr, - wxFileSelectorDefaultWildcardStr - ), - wxFD_OPEN | wxFD_PREVIEW | wxFD_FILE_MUST_EXIST, - this); - if (!path) - { - return; - } - BootManager::BootCore(std::string(path.ToAscii())); -} - - -void CFrame::OnQuit(wxCommandEvent& WXUNUSED (event)) -{ - Close(true); -} - - -void CFrame::OnClose(wxCloseEvent& event) -{ - // Don't forget the skip of the window won't be destroyed - event.Skip(); - - if (Core::GetState() != Core::CORE_UNINITIALIZED) - { - Core::Stop(); - UpdateGUI(); - } -} - -void CFrame::OnHelp(wxCommandEvent& event) -{ - switch (event.GetId()) - { - case IDM_HELPABOUT: - { - AboutDolphin frame(this); - frame.ShowModal(); - break; - } - case IDM_HELPWEBSITE: - File::Launch("http://www.dolphin-emu.com/"); - break; - case IDM_HELPGOOGLECODE: - File::Launch("http://code.google.com/p/dolphin-emu/"); - break; - } -} - - -// ======================================================= -// Play button -// ------------- -void CFrame::OnPlay(wxCommandEvent& WXUNUSED (event)) -{ - if (Core::GetState() != Core::CORE_UNINITIALIZED) - { - if (Core::GetState() == Core::CORE_RUN) - { - Core::SetState(Core::CORE_PAUSE); - } - else - { - Core::SetState(Core::CORE_RUN); - } - UpdateGUI(); - } - // Start the selected ISO - else if (m_GameListCtrl->GetSelectedISO() != 0) - { - BootManager::BootCore(m_GameListCtrl->GetSelectedISO()->GetFileName()); - } - /* Start the default ISO, or if we don't have a default ISO, start the last - started ISO */ - else if (!SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDefaultGCM.empty() && - wxFileExists(wxString(SConfig::GetInstance().m_LocalCoreStartupParameter. - m_strDefaultGCM.c_str(), wxConvUTF8))) - { - BootManager::BootCore(SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDefaultGCM); - } - else if (!SConfig::GetInstance().m_LastFilename.empty() && - wxFileExists(wxString(SConfig::GetInstance().m_LastFilename.c_str(), wxConvUTF8))) - { - BootManager::BootCore(SConfig::GetInstance().m_LastFilename); - } -} -// ============= - - -void CFrame::OnStop(wxCommandEvent& WXUNUSED (event)) -{ - // Ask for confirmation in case the user accidently clicked Stop - int answer = wxMessageBox(wxT("Are you sure you want to stop the current emulation?"), - wxT("Confirm"), wxYES_NO); - - if (answer == wxYES && Core::GetState() != Core::CORE_UNINITIALIZED) - { - Core::Stop(); - UpdateGUI(); - } -} - - -void CFrame::OnRefresh(wxCommandEvent& WXUNUSED (event)) -{ - if (m_GameListCtrl) - { - m_GameListCtrl->Update(); - } -} - - -void CFrame::OnConfigMain(wxCommandEvent& WXUNUSED (event)) -{ - CConfigMain ConfigMain(this); - ConfigMain.ShowModal(); - if (ConfigMain.bRefreshList) - m_GameListCtrl->Update(); -} - - -void CFrame::OnPluginGFX(wxCommandEvent& WXUNUSED (event)) -{ - CPluginManager::GetInstance().OpenConfig( - GetHandle(), - SConfig::GetInstance().m_LocalCoreStartupParameter.m_strVideoPlugin.c_str() - ); -} - - -void CFrame::OnPluginDSP(wxCommandEvent& WXUNUSED (event)) -{ - CPluginManager::GetInstance().OpenConfig( - GetHandle(), - SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDSPPlugin.c_str() - ); -} - -void CFrame::OnPluginPAD(wxCommandEvent& WXUNUSED (event)) -{ - CPluginManager::GetInstance().OpenConfig( - GetHandle(), - SConfig::GetInstance().m_LocalCoreStartupParameter.m_strPadPlugin.c_str() - ); -} -void CFrame::OnPluginWiimote(wxCommandEvent& WXUNUSED (event)) -{ - CPluginManager::GetInstance().OpenConfig( - GetHandle(), - SConfig::GetInstance().m_LocalCoreStartupParameter.m_strWiimotePlugin.c_str() - ); -} - -void CFrame::OnBrowse(wxCommandEvent& WXUNUSED (event)) -{ - m_GameListCtrl->BrowseForDirectory(); -} - -void CFrame::OnMemcard(wxCommandEvent& WXUNUSED (event)) -{ - CMemcardManager MemcardManager(this); - MemcardManager.ShowModal(); -} - -void CFrame::OnShow_CheatsWindow(wxCommandEvent& WXUNUSED (event)) -{ - CheatsWindow = new wxCheatsWindow(this, wxDefaultPosition, wxSize(600, 390)); -} - -void CFrame::OnHostMessage(wxCommandEvent& event) -{ - switch (event.GetId()) - { - case IDM_UPDATEGUI: - UpdateGUI(); - break; - - case IDM_BOOTING_STARTED: - if (m_pBootProcessDialog == NULL) - { - /* m_pBootProcessDialog = new wxProgressDialog - (_T("Booting the core"), - _T("Booting..."), - 1, // range - this, - wxPD_APP_MODAL | - // wxPD_AUTO_HIDE | -- try this as well - wxPD_ELAPSED_TIME | - wxPD_SMOOTH // - makes indeterminate mode bar on WinXP very small - );*/ - - m_pBootProcessDialog = new wxBusyInfo(wxString::FromAscii("Booting..."), this); - } - break; - - case IDM_BOOTING_ENDED: - if (m_pBootProcessDialog != NULL) - { - // m_pBootProcessDialog->Destroy(); - delete m_pBootProcessDialog; - m_pBootProcessDialog = NULL; - } - break; - - case IDM_UPDATESTATUSBAR: - if (m_pStatusBar != NULL) - { - m_pStatusBar->SetStatusText(event.GetString(), event.GetInt()); - } - break; - } -} - -void CFrame::OnToggleFullscreen(wxCommandEvent& WXUNUSED (event)) -{ - ShowFullScreen(true); - UpdateGUI(); -} - -void CFrame::OnToggleDualCore(wxCommandEvent& WXUNUSED (event)) -{ - SConfig::GetInstance().m_LocalCoreStartupParameter.bUseDualCore = !SConfig::GetInstance().m_LocalCoreStartupParameter.bUseDualCore; - SConfig::GetInstance().SaveSettings(); -} -void CFrame::OnToggleSkipIdle(wxCommandEvent& WXUNUSED (event)) -{ - SConfig::GetInstance().m_LocalCoreStartupParameter.bSkipIdle = !SConfig::GetInstance().m_LocalCoreStartupParameter.bSkipIdle; - SConfig::GetInstance().SaveSettings(); -} - -void CFrame::OnLoadState(wxCommandEvent& event) -{ - int id = event.GetId(); - int slot = id - IDM_LOADSLOT1 + 1; - State_Load(slot); -} - -void CFrame::OnSaveState(wxCommandEvent& event) -{ - int id = event.GetId(); - int slot = id - IDM_SAVESLOT1 + 1; - State_Save(slot); -} - -void CFrame::OnToggleToolbar(wxCommandEvent& event) -{ - wxToolBarBase* toolBar = GetToolBar(); - - if (event.IsChecked()) - { - CFrame::RecreateToolbar(); - } - else - { - delete toolBar; - SetToolBar(NULL); - } -} - -void CFrame::OnToggleStatusbar(wxCommandEvent& event) -{ - if (event.IsChecked()) - { - m_pStatusBar = CreateStatusBar(); - } - else - { - delete m_pStatusBar; - m_pStatusBar = NULL; - } -} - -void CFrame::OnKeyDown(wxKeyEvent& event) -{ - // Toggle fullscreen from Alt + Enter or Esc - if (((event.GetKeyCode() == WXK_RETURN) && (event.GetModifiers() == wxMOD_ALT)) || - (event.GetKeyCode() == WXK_ESCAPE)) - { - ShowFullScreen(!IsFullScreen()); - UpdateGUI(); - } -#ifdef _WIN32 - else if(event.GetKeyCode() == 'E') // Send this to the video plugin WndProc - { - PostMessage((HWND)Core::GetWindowHandle(), WM_KEYDOWN, event.GetKeyCode(), 0); - event.Skip(); - } -#endif - else - { - event.Skip(); - } -} - -void CFrame::UpdateGUI() -{ - // buttons - { - if (Core::GetState() == Core::CORE_UNINITIALIZED) - { - GetToolBar()->EnableTool(IDM_CONFIG_MAIN, true); - m_pPluginOptions->Enable(true); - - GetToolBar()->EnableTool(IDM_STOP, false); - - m_pToolPlay->SetNormalBitmap(m_Bitmaps[Toolbar_Play]); - m_pToolPlay->SetShortHelp(_T("Play")); - m_pToolPlay->SetLabel(_T("Play")); - - m_pMenuItemPlay->SetText(_T("&Play")); - - m_pMenuItemStop->Enable(false); - m_pMenuItemLoad->Enable(false); - m_pMenuItemSave->Enable(false); - } - else - { - GetToolBar()->EnableTool(IDM_CONFIG_MAIN, false); - m_pPluginOptions->Enable(false); - - GetToolBar()->EnableTool(IDM_STOP, true); - - m_pMenuItemStop->Enable(true); - m_pMenuItemLoad->Enable(true); - m_pMenuItemSave->Enable(true); - - if (Core::GetState() == Core::CORE_RUN) - { - m_pToolPlay->SetNormalBitmap(m_Bitmaps[Toolbar_Pause]); - m_pToolPlay->SetShortHelp(_T("Pause")); - m_pToolPlay->SetLabel(_T("Pause")); - - m_pMenuItemPlay->SetText(_T("&Pause")); - } - else - { - m_pToolPlay->SetNormalBitmap(m_Bitmaps[Toolbar_Play]); - m_pToolPlay->SetShortHelp(_T("Play")); - m_pToolPlay->SetLabel(_T("Play")); - - m_pMenuItemPlay->SetText(_T("&Play")); - } - } - GetToolBar()->Realize(); - } - - // gamelistctrl - { - if (Core::GetState() == Core::CORE_UNINITIALIZED) - { - if (m_GameListCtrl && !m_GameListCtrl->IsShown()) - { - m_GameListCtrl->Enable(); - m_GameListCtrl->Show(); - sizerPanel->FitInside(m_Panel); - } - } - else - { - if (m_GameListCtrl && m_GameListCtrl->IsShown()) - { - m_GameListCtrl->Disable(); - m_GameListCtrl->Hide(); - } - } - } -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Globals.h" +#include "Frame.h" +#include "FileUtil.h" + +#include "GameListCtrl.h" +#include "BootManager.h" + +#include "Common.h" +#include "Config.h" +#include "Core.h" +#include "State.h" +#include "ConfigMain.h" +#include "PluginManager.h" +#include "MemcardManager.h" +#include "CheatsWindow.h" +#include "AboutDolphin.h" + +#include + +// ---------------------------------------------------------------------------- +// resources +// ---------------------------------------------------------------------------- + +extern "C" { +#include "../resources/Dolphin.c" +#include "../resources/toolbar_browse.c" +#include "../resources/toolbar_file_open.c" +#include "../resources/toolbar_fullscreen.c" +#include "../resources/toolbar_help.c" +#include "../resources/toolbar_pause.c" +#include "../resources/toolbar_play.c" +#include "../resources/toolbar_plugin_dsp.c" +#include "../resources/toolbar_plugin_gfx.c" +#include "../resources/toolbar_plugin_options.c" +#include "../resources/toolbar_plugin_pad.c" +#include "../resources/toolbar_refresh.c" +#include "../resources/toolbar_stop.c" +}; + +#define wxGetBitmapFromMemory(name) _wxGetBitmapFromMemory(name, sizeof(name)) +inline wxBitmap _wxGetBitmapFromMemory(const unsigned char* data, int length) +{ + wxMemoryInputStream is(data, length); + return(wxBitmap(wxImage(is, wxBITMAP_TYPE_ANY, -1), -1)); +} + + +// ---------------------------------------------------------------------------- +// constants +// ---------------------------------------------------------------------------- + +static const long TOOLBAR_STYLE = wxTB_FLAT | wxTB_DOCKABLE | wxTB_TEXT; + +// ---------------------------------------------------------------------------- +// event tables +// ---------------------------------------------------------------------------- + +// Notice that wxID_HELP will be processed for the 'About' menu and the toolbar +// help button. + +const wxEventType wxEVT_HOST_COMMAND = wxNewEventType(); + +BEGIN_EVENT_TABLE(CFrame, wxFrame) +EVT_CLOSE(CFrame::OnClose) +EVT_MENU(wxID_OPEN, CFrame::OnOpen) +EVT_MENU(wxID_EXIT, CFrame::OnQuit) +EVT_MENU(IDM_HELPWEBSITE, CFrame::OnHelp) +EVT_MENU(IDM_HELPGOOGLECODE, CFrame::OnHelp) +EVT_MENU(IDM_HELPABOUT, CFrame::OnHelp) +EVT_MENU(wxID_REFRESH, CFrame::OnRefresh) +EVT_MENU(IDM_PLAY, CFrame::OnPlay) +EVT_MENU(IDM_STOP, CFrame::OnStop) +EVT_MENU(IDM_CONFIG_MAIN, CFrame::OnConfigMain) +EVT_MENU(IDM_CONFIG_GFX_PLUGIN, CFrame::OnPluginGFX) +EVT_MENU(IDM_CONFIG_DSP_PLUGIN, CFrame::OnPluginDSP) +EVT_MENU(IDM_CONFIG_PAD_PLUGIN, CFrame::OnPluginPAD) +EVT_MENU(IDM_CONFIG_WIIMOTE_PLUGIN, CFrame::OnPluginWiimote) +EVT_MENU(IDM_BROWSE, CFrame::OnBrowse) +EVT_MENU(IDM_MEMCARD, CFrame::OnMemcard) +EVT_MENU(IDM_CHEATS, CFrame::OnShow_CheatsWindow) +EVT_MENU(IDM_TOGGLE_FULLSCREEN, CFrame::OnToggleFullscreen) +EVT_MENU(IDM_TOGGLE_DUALCORE, CFrame::OnToggleDualCore) +EVT_MENU(IDM_TOGGLE_SKIPIDLE, CFrame::OnToggleSkipIdle) +EVT_MENU(IDM_TOGGLE_TOOLBAR, CFrame::OnToggleToolbar) +EVT_MENU(IDM_TOGGLE_STATUSBAR, CFrame::OnToggleStatusbar) +EVT_MENU(IDM_LOADSLOT1, CFrame::OnLoadState) +EVT_MENU(IDM_LOADSLOT2, CFrame::OnLoadState) +EVT_MENU(IDM_LOADSLOT3, CFrame::OnLoadState) +EVT_MENU(IDM_LOADSLOT4, CFrame::OnLoadState) +EVT_MENU(IDM_LOADSLOT5, CFrame::OnLoadState) +EVT_MENU(IDM_LOADSLOT6, CFrame::OnLoadState) +EVT_MENU(IDM_LOADSLOT7, CFrame::OnLoadState) +EVT_MENU(IDM_LOADSLOT8, CFrame::OnLoadState) +EVT_MENU(IDM_LOADSLOT9, CFrame::OnLoadState) +EVT_MENU(IDM_LOADSLOT10, CFrame::OnLoadState) +EVT_MENU(IDM_SAVESLOT1, CFrame::OnSaveState) +EVT_MENU(IDM_SAVESLOT2, CFrame::OnSaveState) +EVT_MENU(IDM_SAVESLOT3, CFrame::OnSaveState) +EVT_MENU(IDM_SAVESLOT4, CFrame::OnSaveState) +EVT_MENU(IDM_SAVESLOT5, CFrame::OnSaveState) +EVT_MENU(IDM_SAVESLOT6, CFrame::OnSaveState) +EVT_MENU(IDM_SAVESLOT7, CFrame::OnSaveState) +EVT_MENU(IDM_SAVESLOT8, CFrame::OnSaveState) +EVT_MENU(IDM_SAVESLOT9, CFrame::OnSaveState) +EVT_MENU(IDM_SAVESLOT10, CFrame::OnSaveState) +EVT_HOST_COMMAND(wxID_ANY, CFrame::OnHostMessage) +END_EVENT_TABLE() + +// ---------------------------------------------------------------------------- +// Other Windows +// ---------------------------------------------------------------------------- + +wxCheatsWindow* CheatsWindow; + +// ---------------------------------------------------------------------------- +// implementation +// ---------------------------------------------------------------------------- + +CFrame::CFrame(wxFrame* parent, + wxWindowID id, + const wxString& title, + const wxPoint& pos, + const wxSize& size, + long style) + : wxFrame(parent, id, title, pos, size, style) + , m_Panel(NULL) + , m_pStatusBar(NULL) + , m_pMenuBar(NULL) + , m_pBootProcessDialog(NULL) +{ + InitBitmaps(); + + // Give it an icon + wxIcon IconTemp; + IconTemp.CopyFromBitmap(wxGetBitmapFromMemory(dolphin_png)); + SetIcon(IconTemp); + + // Give it a status line + m_pStatusBar = CreateStatusBar(2); + int StylesField[] = {wxSB_FLAT, wxSB_FLAT}; + m_pStatusBar->SetStatusStyles(2, StylesField); + CreateMenu(); + + // This panel is the parent for rendering and it holds the gamelistctrl + m_Panel = new wxPanel(this); + + m_GameListCtrl = new CGameListCtrl(m_Panel, LIST_CTRL, + wxDefaultPosition, wxDefaultSize, + wxLC_REPORT | wxSUNKEN_BORDER | wxLC_ALIGN_LEFT); + + sizerPanel = new wxBoxSizer(wxHORIZONTAL); + sizerPanel->Add(m_GameListCtrl, 2, wxEXPAND); + m_Panel->SetSizer(sizerPanel); + + // Create the toolbar + RecreateToolbar(); + + Show(); + + CPluginManager::GetInstance().ScanForPlugins(this); + + //if we are ever going back to optional iso caching: + //m_GameListCtrl->Update(SConfig::GetInstance().m_LocalCoreStartupParameter.bEnableIsoCache); + m_GameListCtrl->Update(); + //sizerPanel->SetSizeHints(m_Panel); + + wxTheApp->Connect(wxID_ANY, wxEVT_KEY_DOWN, + wxKeyEventHandler(CFrame::OnKeyDown), + (wxObject*)0, this); + + UpdateGUI(); +} + +#ifdef _WIN32 + +WXLRESULT CFrame::MSWWindowProc(WXUINT nMsg, WXWPARAM wParam, WXLPARAM lParam) +{ + switch (nMsg) + { + case WM_SYSCOMMAND: + switch (wParam) + { + case SC_SCREENSAVE: + case SC_MONITORPOWER: + return 0; + } + default: + return wxFrame::MSWWindowProc(nMsg, wParam, lParam); + } +} + +#endif + +void CFrame::CreateMenu() +{ + delete m_pMenuBar; + m_pMenuBar = new wxMenuBar(wxMB_DOCKABLE); + + // file menu + wxMenu* fileMenu = new wxMenu; + fileMenu->Append(wxID_OPEN, _T("&Open...\tCtrl+O")); + fileMenu->Append(wxID_REFRESH, _T("&Refresh")); + fileMenu->Append(IDM_BROWSE, _T("&Browse for ISOs...")); + + fileMenu->AppendSeparator(); + fileMenu->Append(wxID_EXIT, _T("E&xit"), _T("Alt+F4")); + m_pMenuBar->Append(fileMenu, _T("&File")); + + // emulation menu + wxMenu* emulationMenu = new wxMenu; + m_pMenuItemPlay = emulationMenu->Append(IDM_PLAY, _T("&Play")); + m_pMenuItemStop = emulationMenu->Append(IDM_STOP, _T("&Stop")); + emulationMenu->AppendSeparator(); + wxMenu *saveMenu = new wxMenu; + wxMenu *loadMenu = new wxMenu; + m_pMenuItemLoad = emulationMenu->AppendSubMenu(saveMenu, _T("&Load State")); + m_pMenuItemSave = emulationMenu->AppendSubMenu(loadMenu, _T("Sa&ve State")); + for (int i = 1; i < 10; i++) { + saveMenu->Append(IDM_LOADSLOT1 + i - 1, wxString::Format(_T("Slot %i\tF%i"), i, i)); + loadMenu->Append(IDM_SAVESLOT1 + i - 1, wxString::Format(_T("Slot %i\tShift+F%i"), i, i)); + } + m_pMenuBar->Append(emulationMenu, _T("&Emulation")); + + // options menu + wxMenu* pOptionsMenu = new wxMenu; + m_pPluginOptions = pOptionsMenu->Append(IDM_CONFIG_MAIN, _T("Co&nfigure...")); + pOptionsMenu->AppendSeparator(); + pOptionsMenu->Append(IDM_CONFIG_GFX_PLUGIN, _T("&GFX settings")); + pOptionsMenu->Append(IDM_CONFIG_DSP_PLUGIN, _T("&DSP settings")); + pOptionsMenu->Append(IDM_CONFIG_PAD_PLUGIN, _T("&PAD settings")); + pOptionsMenu->AppendSeparator(); + pOptionsMenu->Append(IDM_TOGGLE_FULLSCREEN, _T("&Fullscreen\tAlt+Enter")); + m_pMenuBar->Append(pOptionsMenu, _T("&Options")); + + // misc menu + wxMenu* miscMenu = new wxMenu; + miscMenu->AppendCheckItem(IDM_TOGGLE_TOOLBAR, _T("View &toolbar")); + miscMenu->Check(IDM_TOGGLE_TOOLBAR, true); + miscMenu->AppendCheckItem(IDM_TOGGLE_STATUSBAR, _T("View &statusbar")); + miscMenu->Check(IDM_TOGGLE_STATUSBAR, true); + miscMenu->AppendSeparator(); + miscMenu->Append(IDM_MEMCARD, _T("&Memcard manager")); + miscMenu->Append(IDM_CHEATS, _T("Action &Replay Manager")); + m_pMenuBar->Append(miscMenu, _T("&Misc")); + + // help menu + wxMenu* helpMenu = new wxMenu; + /*helpMenu->Append(wxID_HELP, _T("&Help")); + re-enable when there's something useful to display*/ + helpMenu->Append(IDM_HELPWEBSITE, _T("Dolphin &web site")); + helpMenu->Append(IDM_HELPGOOGLECODE, _T("Dolphin at &Google Code")); + helpMenu->AppendSeparator(); + helpMenu->Append(IDM_HELPABOUT, _T("&About...")); + m_pMenuBar->Append(helpMenu, _T("&Help")); + + // Associate the menu bar with the frame + SetMenuBar(m_pMenuBar); +} + + +void CFrame::PopulateToolbar(wxToolBar* toolBar) +{ + int w = m_Bitmaps[Toolbar_FileOpen].GetWidth(), + h = m_Bitmaps[Toolbar_FileOpen].GetHeight(); + + toolBar->SetToolBitmapSize(wxSize(w, h)); + toolBar->AddTool(wxID_OPEN, _T("Open"), m_Bitmaps[Toolbar_FileOpen], _T("Open file...")); + toolBar->AddTool(wxID_REFRESH, _T("Refresh"), m_Bitmaps[Toolbar_Refresh], _T("Refresh")); + toolBar->AddTool(IDM_BROWSE, _T("Browse"), m_Bitmaps[Toolbar_Browse], _T("Browse for an ISO directory...")); + toolBar->AddSeparator(); + m_pToolPlay = toolBar->AddTool(IDM_PLAY, _T("Play"), m_Bitmaps[Toolbar_Play], _T("Play")); + + toolBar->AddTool(IDM_STOP, _T("Stop"), m_Bitmaps[Toolbar_Stop], _T("Stop")); + toolBar->AddTool(IDM_TOGGLE_FULLSCREEN, _T("Fullscr."), m_Bitmaps[Toolbar_FullScreen], _T("Toggle Fullscreen")); + toolBar->AddSeparator(); + toolBar->AddTool(IDM_CONFIG_MAIN, _T("Config"), m_Bitmaps[Toolbar_PluginOptions], _T("Configure...")); + toolBar->AddTool(IDM_CONFIG_GFX_PLUGIN, _T("GFX"), m_Bitmaps[Toolbar_PluginGFX], _T("GFX settings")); + toolBar->AddTool(IDM_CONFIG_DSP_PLUGIN, _T("DSP"), m_Bitmaps[Toolbar_PluginDSP], _T("DSP settings")); + toolBar->AddTool(IDM_CONFIG_PAD_PLUGIN, _T("PAD"), m_Bitmaps[Toolbar_PluginPAD], _T("PAD settings")); + toolBar->AddTool(IDM_CONFIG_WIIMOTE_PLUGIN, _T("Wiimote"), m_Bitmaps[Toolbar_PluginPAD], _T("Wiimote settings")); + toolBar->AddSeparator(); + toolBar->AddTool(IDM_HELPABOUT, _T("About"), m_Bitmaps[Toolbar_Help], _T("About Dolphin")); + + // after adding the buttons to the toolbar, must call Realize() to reflect + // the changes + toolBar->Realize(); +} + + +void CFrame::RecreateToolbar() +{ + // delete and recreate the toolbar + wxToolBarBase* toolBar = GetToolBar(); + long style = toolBar ? toolBar->GetWindowStyle() : TOOLBAR_STYLE; + + delete toolBar; + SetToolBar(NULL); + + style &= ~(wxTB_HORIZONTAL | wxTB_VERTICAL | wxTB_BOTTOM | wxTB_RIGHT | wxTB_HORZ_LAYOUT | wxTB_TOP); + wxToolBar* theToolBar = CreateToolBar(style, ID_TOOLBAR); + + PopulateToolbar(theToolBar); + SetToolBar(theToolBar); + UpdateGUI(); +} + + +void CFrame::InitBitmaps() +{ + // load orignal size 48x48 + m_Bitmaps[Toolbar_FileOpen] = wxGetBitmapFromMemory(toolbar_file_open_png); + m_Bitmaps[Toolbar_Refresh] = wxGetBitmapFromMemory(toolbar_refresh_png); + m_Bitmaps[Toolbar_Browse] = wxGetBitmapFromMemory(toolbar_browse_png); + m_Bitmaps[Toolbar_Play] = wxGetBitmapFromMemory(toolbar_play_png); + m_Bitmaps[Toolbar_Stop] = wxGetBitmapFromMemory(toolbar_stop_png); + m_Bitmaps[Toolbar_Pause] = wxGetBitmapFromMemory(toolbar_pause_png); + m_Bitmaps[Toolbar_PluginOptions] = wxGetBitmapFromMemory(toolbar_plugin_options_png); + m_Bitmaps[Toolbar_PluginGFX] = wxGetBitmapFromMemory(toolbar_plugin_gfx_png); + m_Bitmaps[Toolbar_PluginDSP] = wxGetBitmapFromMemory(toolbar_plugin_dsp_png); + m_Bitmaps[Toolbar_PluginPAD] = wxGetBitmapFromMemory(toolbar_plugin_pad_png); + m_Bitmaps[Toolbar_FullScreen] = wxGetBitmapFromMemory(toolbar_fullscreen_png); + m_Bitmaps[Toolbar_Help] = wxGetBitmapFromMemory(toolbar_help_png); + + // scale to 24x24 for toolbar + for (size_t n = Toolbar_FileOpen; n < WXSIZEOF(m_Bitmaps); n++) + { + m_Bitmaps[n] = wxBitmap(m_Bitmaps[n].ConvertToImage().Scale(24, 24)); + } +} + + +void CFrame::OnOpen(wxCommandEvent& WXUNUSED (event)) +{ + if (Core::GetState() != Core::CORE_UNINITIALIZED) + return; + wxString path = wxFileSelector( + _T("Select the file to load"), + wxEmptyString, wxEmptyString, wxEmptyString, + wxString::Format + ( + _T("All GC/Wii files (elf, dol, gcm, iso)|*.elf;*.dol;*.gcm;*.iso;*.gcz|All files (%s)|%s"), + wxFileSelectorDefaultWildcardStr, + wxFileSelectorDefaultWildcardStr + ), + wxFD_OPEN | wxFD_PREVIEW | wxFD_FILE_MUST_EXIST, + this); + if (!path) + { + return; + } + BootManager::BootCore(std::string(path.ToAscii())); +} + + +void CFrame::OnQuit(wxCommandEvent& WXUNUSED (event)) +{ + Close(true); +} + + +void CFrame::OnClose(wxCloseEvent& event) +{ + // Don't forget the skip of the window won't be destroyed + event.Skip(); + + if (Core::GetState() != Core::CORE_UNINITIALIZED) + { + Core::Stop(); + UpdateGUI(); + } +} + +void CFrame::OnHelp(wxCommandEvent& event) +{ + switch (event.GetId()) + { + case IDM_HELPABOUT: + { + AboutDolphin frame(this); + frame.ShowModal(); + break; + } + case IDM_HELPWEBSITE: + File::Launch("http://www.dolphin-emu.com/"); + break; + case IDM_HELPGOOGLECODE: + File::Launch("http://code.google.com/p/dolphin-emu/"); + break; + } +} + + +// ======================================================= +// Play button +// ------------- +void CFrame::OnPlay(wxCommandEvent& WXUNUSED (event)) +{ + if (Core::GetState() != Core::CORE_UNINITIALIZED) + { + if (Core::GetState() == Core::CORE_RUN) + { + Core::SetState(Core::CORE_PAUSE); + } + else + { + Core::SetState(Core::CORE_RUN); + } + UpdateGUI(); + } + // Start the selected ISO + else if (m_GameListCtrl->GetSelectedISO() != 0) + { + BootManager::BootCore(m_GameListCtrl->GetSelectedISO()->GetFileName()); + } + /* Start the default ISO, or if we don't have a default ISO, start the last + started ISO */ + else if (!SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDefaultGCM.empty() && + wxFileExists(wxString(SConfig::GetInstance().m_LocalCoreStartupParameter. + m_strDefaultGCM.c_str(), wxConvUTF8))) + { + BootManager::BootCore(SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDefaultGCM); + } + else if (!SConfig::GetInstance().m_LastFilename.empty() && + wxFileExists(wxString(SConfig::GetInstance().m_LastFilename.c_str(), wxConvUTF8))) + { + BootManager::BootCore(SConfig::GetInstance().m_LastFilename); + } +} +// ============= + + +void CFrame::OnStop(wxCommandEvent& WXUNUSED (event)) +{ + // Ask for confirmation in case the user accidently clicked Stop + int answer = wxMessageBox(wxT("Are you sure you want to stop the current emulation?"), + wxT("Confirm"), wxYES_NO); + + if (answer == wxYES && Core::GetState() != Core::CORE_UNINITIALIZED) + { + Core::Stop(); + UpdateGUI(); + } +} + + +void CFrame::OnRefresh(wxCommandEvent& WXUNUSED (event)) +{ + if (m_GameListCtrl) + { + m_GameListCtrl->Update(); + } +} + + +void CFrame::OnConfigMain(wxCommandEvent& WXUNUSED (event)) +{ + CConfigMain ConfigMain(this); + ConfigMain.ShowModal(); + if (ConfigMain.bRefreshList) + m_GameListCtrl->Update(); +} + + +void CFrame::OnPluginGFX(wxCommandEvent& WXUNUSED (event)) +{ + CPluginManager::GetInstance().OpenConfig( + GetHandle(), + SConfig::GetInstance().m_LocalCoreStartupParameter.m_strVideoPlugin.c_str() + ); +} + + +void CFrame::OnPluginDSP(wxCommandEvent& WXUNUSED (event)) +{ + CPluginManager::GetInstance().OpenConfig( + GetHandle(), + SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDSPPlugin.c_str() + ); +} + +void CFrame::OnPluginPAD(wxCommandEvent& WXUNUSED (event)) +{ + CPluginManager::GetInstance().OpenConfig( + GetHandle(), + SConfig::GetInstance().m_LocalCoreStartupParameter.m_strPadPlugin.c_str() + ); +} +void CFrame::OnPluginWiimote(wxCommandEvent& WXUNUSED (event)) +{ + CPluginManager::GetInstance().OpenConfig( + GetHandle(), + SConfig::GetInstance().m_LocalCoreStartupParameter.m_strWiimotePlugin.c_str() + ); +} + +void CFrame::OnBrowse(wxCommandEvent& WXUNUSED (event)) +{ + m_GameListCtrl->BrowseForDirectory(); +} + +void CFrame::OnMemcard(wxCommandEvent& WXUNUSED (event)) +{ + CMemcardManager MemcardManager(this); + MemcardManager.ShowModal(); +} + +void CFrame::OnShow_CheatsWindow(wxCommandEvent& WXUNUSED (event)) +{ + CheatsWindow = new wxCheatsWindow(this, wxDefaultPosition, wxSize(600, 390)); +} + +void CFrame::OnHostMessage(wxCommandEvent& event) +{ + switch (event.GetId()) + { + case IDM_UPDATEGUI: + UpdateGUI(); + break; + + case IDM_BOOTING_STARTED: + if (m_pBootProcessDialog == NULL) + { + /* m_pBootProcessDialog = new wxProgressDialog + (_T("Booting the core"), + _T("Booting..."), + 1, // range + this, + wxPD_APP_MODAL | + // wxPD_AUTO_HIDE | -- try this as well + wxPD_ELAPSED_TIME | + wxPD_SMOOTH // - makes indeterminate mode bar on WinXP very small + );*/ + + m_pBootProcessDialog = new wxBusyInfo(wxString::FromAscii("Booting..."), this); + } + break; + + case IDM_BOOTING_ENDED: + if (m_pBootProcessDialog != NULL) + { + // m_pBootProcessDialog->Destroy(); + delete m_pBootProcessDialog; + m_pBootProcessDialog = NULL; + } + break; + + case IDM_UPDATESTATUSBAR: + if (m_pStatusBar != NULL) + { + m_pStatusBar->SetStatusText(event.GetString(), event.GetInt()); + } + break; + } +} + +void CFrame::OnToggleFullscreen(wxCommandEvent& WXUNUSED (event)) +{ + ShowFullScreen(true); + UpdateGUI(); +} + +void CFrame::OnToggleDualCore(wxCommandEvent& WXUNUSED (event)) +{ + SConfig::GetInstance().m_LocalCoreStartupParameter.bUseDualCore = !SConfig::GetInstance().m_LocalCoreStartupParameter.bUseDualCore; + SConfig::GetInstance().SaveSettings(); +} +void CFrame::OnToggleSkipIdle(wxCommandEvent& WXUNUSED (event)) +{ + SConfig::GetInstance().m_LocalCoreStartupParameter.bSkipIdle = !SConfig::GetInstance().m_LocalCoreStartupParameter.bSkipIdle; + SConfig::GetInstance().SaveSettings(); +} + +void CFrame::OnLoadState(wxCommandEvent& event) +{ + int id = event.GetId(); + int slot = id - IDM_LOADSLOT1 + 1; + State_Load(slot); +} + +void CFrame::OnSaveState(wxCommandEvent& event) +{ + int id = event.GetId(); + int slot = id - IDM_SAVESLOT1 + 1; + State_Save(slot); +} + +void CFrame::OnToggleToolbar(wxCommandEvent& event) +{ + wxToolBarBase* toolBar = GetToolBar(); + + if (event.IsChecked()) + { + CFrame::RecreateToolbar(); + } + else + { + delete toolBar; + SetToolBar(NULL); + } +} + +void CFrame::OnToggleStatusbar(wxCommandEvent& event) +{ + if (event.IsChecked()) + { + m_pStatusBar = CreateStatusBar(); + } + else + { + delete m_pStatusBar; + m_pStatusBar = NULL; + } +} + +void CFrame::OnKeyDown(wxKeyEvent& event) +{ + // Toggle fullscreen from Alt + Enter or Esc + if (((event.GetKeyCode() == WXK_RETURN) && (event.GetModifiers() == wxMOD_ALT)) || + (event.GetKeyCode() == WXK_ESCAPE)) + { + ShowFullScreen(!IsFullScreen()); + UpdateGUI(); + } +#ifdef _WIN32 + else if(event.GetKeyCode() == 'E') // Send this to the video plugin WndProc + { + PostMessage((HWND)Core::GetWindowHandle(), WM_KEYDOWN, event.GetKeyCode(), 0); + event.Skip(); + } +#endif + else + { + event.Skip(); + } +} + +void CFrame::UpdateGUI() +{ + // buttons + { + if (Core::GetState() == Core::CORE_UNINITIALIZED) + { + GetToolBar()->EnableTool(IDM_CONFIG_MAIN, true); + m_pPluginOptions->Enable(true); + + GetToolBar()->EnableTool(IDM_STOP, false); + + m_pToolPlay->SetNormalBitmap(m_Bitmaps[Toolbar_Play]); + m_pToolPlay->SetShortHelp(_T("Play")); + m_pToolPlay->SetLabel(_T("Play")); + + m_pMenuItemPlay->SetText(_T("&Play")); + + m_pMenuItemStop->Enable(false); + m_pMenuItemLoad->Enable(false); + m_pMenuItemSave->Enable(false); + } + else + { + GetToolBar()->EnableTool(IDM_CONFIG_MAIN, false); + m_pPluginOptions->Enable(false); + + GetToolBar()->EnableTool(IDM_STOP, true); + + m_pMenuItemStop->Enable(true); + m_pMenuItemLoad->Enable(true); + m_pMenuItemSave->Enable(true); + + if (Core::GetState() == Core::CORE_RUN) + { + m_pToolPlay->SetNormalBitmap(m_Bitmaps[Toolbar_Pause]); + m_pToolPlay->SetShortHelp(_T("Pause")); + m_pToolPlay->SetLabel(_T("Pause")); + + m_pMenuItemPlay->SetText(_T("&Pause")); + } + else + { + m_pToolPlay->SetNormalBitmap(m_Bitmaps[Toolbar_Play]); + m_pToolPlay->SetShortHelp(_T("Play")); + m_pToolPlay->SetLabel(_T("Play")); + + m_pMenuItemPlay->SetText(_T("&Play")); + } + } + GetToolBar()->Realize(); + } + + // gamelistctrl + { + if (Core::GetState() == Core::CORE_UNINITIALIZED) + { + if (m_GameListCtrl && !m_GameListCtrl->IsShown()) + { + m_GameListCtrl->Enable(); + m_GameListCtrl->Show(); + sizerPanel->FitInside(m_Panel); + } + } + else + { + if (m_GameListCtrl && m_GameListCtrl->IsShown()) + { + m_GameListCtrl->Disable(); + m_GameListCtrl->Hide(); + } + } + } +} diff --git a/Source/Core/DolphinWX/Src/GameListCtrl.cpp b/Source/Core/DolphinWX/Src/GameListCtrl.cpp index 20b19a87f2..4ef5495b37 100644 --- a/Source/Core/DolphinWX/Src/GameListCtrl.cpp +++ b/Source/Core/DolphinWX/Src/GameListCtrl.cpp @@ -1,806 +1,806 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Globals.h" - -#include - -#include - -#include "FileSearch.h" -#include "FileUtil.h" -#include "StringUtil.h" -#include "BootManager.h" -#include "Config.h" -#include "GameListCtrl.h" -#include "Blob.h" -#include "ISOProperties.h" -#include "IniFile.h" - -#if USE_XPM_BITMAPS - #include "../resources/Flag_Europe.xpm" - #include "../resources/Flag_France.xpm" - #include "../resources/Flag_Japan.xpm" - #include "../resources/Flag_USA.xpm" -#endif // USE_XPM_BITMAPS - -size_t CGameListCtrl::m_currentItem = 0; -size_t CGameListCtrl::m_numberItem = 0; -std::string CGameListCtrl::m_currentFilename; - - -static int currentColumn = 0; -bool operator < (const GameListItem &one, const GameListItem &other) -{ - switch(currentColumn) - { - case CGameListCtrl::COLUMN_TITLE: return strcasecmp(one.GetName().c_str(), other.GetName().c_str()) < 0; - case CGameListCtrl::COLUMN_COMPANY: return strcasecmp(one.GetCompany().c_str(), other.GetCompany().c_str()) < 0; - case CGameListCtrl::COLUMN_NOTES: return strcasecmp(one.GetDescription().c_str(), other.GetDescription().c_str()) < 0; - case CGameListCtrl::COLUMN_COUNTRY: return (one.GetCountry() < other.GetCountry()); - case CGameListCtrl::COLUMN_SIZE: return (one.GetFileSize() < other.GetFileSize()); - default: return strcasecmp(one.GetName().c_str(), other.GetName().c_str()) < 0; - } -} - -BEGIN_EVENT_TABLE(CGameListCtrl, wxListCtrl) - -EVT_SIZE(CGameListCtrl::OnSize) -EVT_RIGHT_DOWN(CGameListCtrl::OnRightClick) -EVT_LIST_COL_BEGIN_DRAG(LIST_CTRL, CGameListCtrl::OnColBeginDrag) -EVT_LIST_ITEM_ACTIVATED(LIST_CTRL, CGameListCtrl::OnActivated) -EVT_LIST_COL_CLICK(LIST_CTRL, CGameListCtrl::OnColumnClick) -EVT_MENU(IDM_PROPERTIES, CGameListCtrl::OnProperties) -EVT_MENU(IDM_OPENCONTAININGFOLDER, CGameListCtrl::OnOpenContainingFolder) -EVT_MENU(IDM_SETDEFAULTGCM, CGameListCtrl::OnSetDefaultGCM) -EVT_MENU(IDM_COMPRESSGCM, CGameListCtrl::OnCompressGCM) -EVT_MENU(IDM_MULTICOMPRESSGCM, CGameListCtrl::OnMultiCompressGCM) -EVT_MENU(IDM_MULTIDECOMPRESSGCM, CGameListCtrl::OnMultiDecompressGCM) -EVT_MENU(IDM_DELETEGCM, CGameListCtrl::OnDeleteGCM) -END_EVENT_TABLE() - -CGameListCtrl::CGameListCtrl(wxWindow* parent, const wxWindowID id, const wxPoint& pos, const wxSize& size, long style) - : wxListCtrl(parent, id, pos, size, style) // | wxLC_VIRTUAL) -{ -} - -CGameListCtrl::~CGameListCtrl() -{ - if (m_imageListSmall) delete m_imageListSmall; -} - -void CGameListCtrl::InitBitmaps() -{ - m_imageListSmall = new wxImageList(96, 32); - SetImageList(m_imageListSmall, wxIMAGE_LIST_SMALL); - m_FlagImageIndex.resize(DiscIO::IVolume::NUMBER_OF_COUNTRIES); - wxIcon iconTemp; - iconTemp.CopyFromBitmap(wxBitmap(Flag_Europe_xpm)); - m_FlagImageIndex[DiscIO::IVolume::COUNTRY_EUROPE] = m_imageListSmall->Add(iconTemp); - iconTemp.CopyFromBitmap(wxBitmap(Flag_France_xpm)); - m_FlagImageIndex[DiscIO::IVolume::COUNTRY_FRANCE] = m_imageListSmall->Add(iconTemp); - iconTemp.CopyFromBitmap(wxBitmap(Flag_USA_xpm)); - m_FlagImageIndex[DiscIO::IVolume::COUNTRY_USA] = m_imageListSmall->Add(iconTemp); - iconTemp.CopyFromBitmap(wxBitmap(Flag_Japan_xpm)); - m_FlagImageIndex[DiscIO::IVolume::COUNTRY_JAP] = m_imageListSmall->Add(iconTemp); - iconTemp.CopyFromBitmap(wxBitmap(Flag_Europe_xpm)); - m_FlagImageIndex[DiscIO::IVolume::COUNTRY_UNKNOWN] = m_imageListSmall->Add(iconTemp); -} - -void CGameListCtrl::BrowseForDirectory() -{ - wxString dirHome; - wxGetHomeDir(&dirHome); - - // browse - wxDirDialog dialog(this, _("Browse directory"), dirHome, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST); - - if (dialog.ShowModal() == wxID_OK) - { - SConfig::GetInstance().m_ISOFolder.push_back( - std::string(dialog.GetPath().ToAscii()) - ); - SConfig::GetInstance().SaveSettings(); - Update(); - } -} - -void CGameListCtrl::Update() -{ - if (m_imageListSmall) - { - delete m_imageListSmall; - m_imageListSmall = NULL; - } - - Hide(); - - ScanForISOs(); - - ClearAll(); - - if (m_ISOFiles.size() != 0) - { - // Don't load bitmaps unless there are games to list - InitBitmaps(); - - // add columns - InsertColumn(COLUMN_BANNER, _("Banner")); - InsertColumn(COLUMN_TITLE, _("Title")); - InsertColumn(COLUMN_COMPANY, _("Company")); - InsertColumn(COLUMN_NOTES, _("Notes")); - InsertColumn(COLUMN_COUNTRY, _("")); - InsertColumn(COLUMN_SIZE, _("Size")); - InsertColumn(COLUMN_EMULATION_STATE, _("Emulation")); - - // set initial sizes for columns - SetColumnWidth(COLUMN_BANNER, 106); - SetColumnWidth(COLUMN_TITLE, 150); - SetColumnWidth(COLUMN_COMPANY, 100); - SetColumnWidth(COLUMN_NOTES, 200); - SetColumnWidth(COLUMN_COUNTRY, 32); - SetColumnWidth(COLUMN_EMULATION_STATE, 75); - - // add all items - for (int i = 0; i < (int)m_ISOFiles.size(); i++) - { - InsertItemInReportView(i); - if (m_ISOFiles[i].IsCompressed()) - SetItemTextColour(i, wxColour(0xFF0000)); - } - - SetColumnWidth(COLUMN_SIZE, wxLIST_AUTOSIZE); - } - else - { - InsertColumn(COLUMN_BANNER, _("No ISOs found")); - - long index = InsertItem(0, wxString::FromAscii("msgRow")); - SetItem(index, COLUMN_BANNER, _("Dolphin could not find any GC/Wii ISOs. Doubleclick here to browse for files...")); - SetItemFont(index, *wxITALIC_FONT); - SetColumnWidth(COLUMN_BANNER, wxLIST_AUTOSIZE); - } - - AutomaticColumnWidth(); - - Show(); -} - -wxString NiceSizeFormat(s64 _size) -{ - const char* sizes[] = {"b", "KB", "MB", "GB", "TB", "PB", "EB"}; - int s = 0; - int frac = 0; - - while (_size > (s64)1024) - { - s++; - frac = (int)_size & 1023; - _size /= (s64)1024; - } - - float f = (float)_size + ((float)frac / 1024.0f); - - wxString NiceString; - char tempstr[32]; - sprintf(tempstr,"%3.1f %s", f, sizes[s]); - NiceString = wxString::FromAscii(tempstr); - return(NiceString); -} - -void CGameListCtrl::InsertItemInReportView(long _Index) -{ - GameListItem& rISOFile = m_ISOFiles[_Index]; - - int ImageIndex = -1; - - if (rISOFile.GetImage().IsOk()) - { - ImageIndex = m_imageListSmall->Add(rISOFile.GetImage()); - } - - // Insert a row with the banner image - long ItemIndex = InsertItem(_Index, wxEmptyString, ImageIndex); - - // Background color - SetBackgroundColor(); - - // When using wxListCtrl, there is no hope of per-column text colors. - // But for reference, here are the old colors that were used: (BGR) - // title: 0xFF0000 - // company: 0x007030 - - SetItem(_Index, COLUMN_TITLE, wxString::FromAscii(rISOFile.GetName().c_str()), -1); - SetItem(_Index, COLUMN_COMPANY, wxString::FromAscii(rISOFile.GetCompany().c_str()), -1); - SetItem(_Index, COLUMN_NOTES, wxString::FromAscii(rISOFile.GetDescription().c_str()), -1); - SetItem(_Index, COLUMN_SIZE, NiceSizeFormat(rISOFile.GetFileSize()), -1); - - // Emulation status = COLUMN_EMULATION_STATE - { - wxListItem item; - item.SetId(_Index); - IniFile ini; - std::string EmuState; - std::string GameIni; - item.SetColumn(COLUMN_EMULATION_STATE); - //NOTE (Daco): i dont like the fact of having so much ini's just to have - //the game emulation state of every game you have. but 1 huge ini is no option - GameIni = FULL_GAMECONFIG_DIR + (rISOFile.GetUniqueID()) + ".ini"; - ini.Load(GameIni.c_str()); - ini.Get("EmuState","EmulationStateId",&EmuState); - if (!EmuState.empty()) - { - if(EmuState == "5") - item.SetText(_("Perfect")); - else if(EmuState == "4") - item.SetText(_("In Game")); - else if(EmuState == "3") - item.SetText(_("Intro")); - else if(EmuState == "2") - { - item.SetText(_("Problems: Other")); - //NOTE (Daco): IMO under 2 i see problems like music and games that only work - //with GL or only with DX9 - //TODO (Daco): maybe 2 should get a function to present more info... o.o - } - else if(EmuState == "1") - item.SetText(_("Broken")); - else if(EmuState == "0") - item.SetText(_("Not Set")); - else - { - //if the EmuState isn't a number between 0 & 5 we dont know the state - //hence why it should say unknown - item.SetText(_("unknown emu ID")); - } - } - SetItem(item); - } - -#ifndef __WXMSW__ - // Country - { - // Can't do this in Windows - we use DrawSubItem instead, see below - wxListItem item; - item.m_itemId = ItemIndex; - item.SetColumn(COLUMN_COUNTRY); - //item.SetBackgroundColour(color); - DiscIO::IVolume::ECountry Country = rISOFile.GetCountry(); - - if (size_t(Country) < m_FlagImageIndex.size()) - { - item.SetImage(m_FlagImageIndex[rISOFile.GetCountry()]); - } - - SetItem(item); - } -#endif // __WXMSW__ - - // Item data - SetItemData(_Index, ItemIndex); -} -#ifdef _WIN32 -bool CGameListCtrl::MSWDrawSubItem(wxPaintDC& rPaintDC, int item, int subitem) -{ - bool Result = false; -#ifdef __WXMSW__ - switch (subitem) - { - case COLUMN_COUNTRY: - size_t Index = GetItemData(item); - - if (Index < m_ISOFiles.size()) - { - const GameListItem& rISO = m_ISOFiles[Index]; - wxRect SubItemRect; - this->GetSubItemRect(item, subitem, SubItemRect); - m_imageListSmall->Draw(m_FlagImageIndex[rISO.GetCountry()], rPaintDC, SubItemRect.GetLeft(), SubItemRect.GetTop()); - } - } -#endif - - return(Result); -} -#endif - -wxColour blend50(const wxColour& c1, const wxColour& c2) -{ - unsigned char r,g,b,a; - r = c1.Red()/2 + c2.Red()/2; - g = c1.Green()/2 + c2.Green()/2; - b = c1.Blue()/2 + c2.Blue()/2; - a = c1.Alpha()/2 + c2.Alpha()/2; - return a << 24 | b << 16 | g << 8 | r; -} - -void CGameListCtrl::SetBackgroundColor() -{ - for(long i = 0; i < GetItemCount(); i++) - { - wxColour color = (i & 1) ? blend50(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT), wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)) : wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); - CGameListCtrl::SetItemBackgroundColour(i, color); - } -} - -void CGameListCtrl::ScanForISOs() -{ - m_ISOFiles.clear(); - CFileSearch::XStringVector Directories(SConfig::GetInstance().m_ISOFolder); - - CFileSearch::XStringVector Extensions; - Extensions.push_back("*.iso"); - Extensions.push_back("*.gcm"); - Extensions.push_back("*.gcz"); - - CFileSearch FileSearch(Extensions, Directories); - const CFileSearch::XStringVector& rFilenames = FileSearch.GetFileNames(); - - if (rFilenames.size() > 0) - { - wxProgressDialog dialog(_T("Scanning for ISOs"), - _T("Scanning..."), - rFilenames.size(), // range - this, // parent - wxPD_APP_MODAL | - // wxPD_AUTO_HIDE | -- try this as well - wxPD_ELAPSED_TIME | - wxPD_ESTIMATED_TIME | - wxPD_REMAINING_TIME | - wxPD_SMOOTH // - makes indeterminate mode bar on WinXP very small - ); - dialog.CenterOnParent(); - - for (u32 i = 0; i < rFilenames.size(); i++) - { - std::string FileName; - SplitPath(rFilenames[i], NULL, &FileName, NULL); - - wxString msg; - char tempstring[128]; - sprintf(tempstring,"Scanning %s", FileName.c_str()); - msg = wxString::FromAscii(tempstring); - - bool Cont = dialog.Update(i, msg); - - if (!Cont) - { - break; - } - GameListItem ISOFile(rFilenames[i]); - if (ISOFile.IsValid()) - { - m_ISOFiles.push_back(ISOFile); - } - else - PanicAlert("Invalid ISO file %s", rFilenames[i].c_str()); - } - } - std::sort(m_ISOFiles.begin(), m_ISOFiles.end()); -} - -void CGameListCtrl::OnColBeginDrag(wxListEvent& event) -{ - if (event.GetColumn() != COLUMN_TITLE && event.GetColumn() != COLUMN_COMPANY - && event.GetColumn() != COLUMN_NOTES) - event.Veto(); -} - -const GameListItem *CGameListCtrl::GetISO(int index) const -{ - return &m_ISOFiles[index]; -} - -CGameListCtrl *caller; -int wxCALLBACK wxListCompare(long item1, long item2, long sortData) -{ - //return 1 if item1 > item2 - //return -1 if item1 < item2 - //0 for identity - const GameListItem *iso1 = caller->GetISO(item1); - const GameListItem *iso2 = caller->GetISO(item2); - - int t = 1; - - if (sortData < 0) - { - t = -1; - sortData = -sortData; - } - - switch(sortData) - { - case CGameListCtrl::COLUMN_TITLE: - return strcasecmp(iso1->GetName().c_str(),iso2->GetName().c_str()) *t; - case CGameListCtrl::COLUMN_COMPANY: - return strcasecmp(iso1->GetCompany().c_str(),iso2->GetCompany().c_str()) *t; - case CGameListCtrl::COLUMN_NOTES: - return strcasecmp(iso1->GetDescription().c_str(),iso2->GetDescription().c_str()) *t; - case CGameListCtrl::COLUMN_COUNTRY: - if(iso1->GetCountry() > iso2->GetCountry()) return 1 *t; - if(iso1->GetCountry() < iso2->GetCountry()) return -1 *t; - return 0; - case CGameListCtrl::COLUMN_SIZE: - if (iso1->GetFileSize() > iso2->GetFileSize()) return 1 *t; - if (iso1->GetFileSize() < iso2->GetFileSize()) return -1 *t; - return 0; - } - - return 0; -} - -void CGameListCtrl::OnColumnClick(wxListEvent& event) -{ - if(event.GetColumn() != COLUMN_BANNER && event.GetColumn() != COLUMN_EMULATION_STATE) - { - int current_column = event.GetColumn(); - - if(last_column == current_column) - { - last_sort = -last_sort; - } - else - { - last_column = current_column; - last_sort = current_column; - } - - caller = this; - SortItems(wxListCompare, last_sort); - } - - SetBackgroundColor(); - - event.Skip(); -} - -void CGameListCtrl::OnRightClick(wxMouseEvent& event) -{ - // Focus the clicked item. - int flags; - long item = HitTest(event.GetPosition(), flags); - if (item != wxNOT_FOUND) - { - if (GetItemState(item, wxLIST_STATE_SELECTED) != wxLIST_STATE_SELECTED) - { - UnselectAll(); - SetItemState(item, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED ); - } - SetItemState(item, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED); - } - - if (GetSelectedItemCount() == 1) - { - const GameListItem *selected_iso = GetSelectedISO(); - if (selected_iso) - { - wxMenu popupMenu; - popupMenu.Append(IDM_PROPERTIES, _("&Properties")); - popupMenu.AppendSeparator(); - popupMenu.Append(IDM_OPENCONTAININGFOLDER, _("Open &containing folder")); - popupMenu.AppendCheckItem(IDM_SETDEFAULTGCM, _("Set as &default ISO")); - if(selected_iso->GetFileName() == SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDefaultGCM) - popupMenu.FindItemByPosition(3)->Check(); - - popupMenu.AppendSeparator(); - popupMenu.Append(IDM_DELETEGCM, _("&Delete ISO...")); - - if (selected_iso->IsCompressed()) - popupMenu.Append(IDM_COMPRESSGCM, _("Decompress ISO...")); - else - popupMenu.Append(IDM_COMPRESSGCM, _("Compress ISO...")); - - PopupMenu(&popupMenu); - } - } - else if (GetSelectedItemCount() > 1) - { - wxMenu popupMenu; - popupMenu.Append(IDM_DELETEGCM, _("&Delete selected ISOs...")); - popupMenu.AppendSeparator(); - popupMenu.Append(IDM_MULTICOMPRESSGCM, _("Compress selected ISOs...")); - popupMenu.Append(IDM_MULTIDECOMPRESSGCM, _("Decompress selected ISOs...")); - PopupMenu(&popupMenu); - } -} - -void CGameListCtrl::OnActivated(wxListEvent& event) -{ - if (m_ISOFiles.size() == 0) - { - BrowseForDirectory(); - } - else - { - size_t Index = event.GetData(); - if (Index < m_ISOFiles.size()) - { - const GameListItem& rISOFile = m_ISOFiles[Index]; - BootManager::BootCore(rISOFile.GetFileName()); - } - } -} - -const GameListItem * CGameListCtrl::GetSelectedISO() -{ - long item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); - if (item == -1) - return 0; - else - { - if (GetSelectedItemCount() > 1) - SetItemState(item, 0, wxLIST_STATE_SELECTED); - - return &m_ISOFiles[GetItemData(item)]; - } -} - -void CGameListCtrl::OnOpenContainingFolder(wxCommandEvent& WXUNUSED (event)) -{ - const GameListItem *iso = GetSelectedISO(); - if (!iso) - return; - std::string path; - SplitPath(iso->GetFileName(), &path, 0, 0); - File::Explore(path.c_str()); -} - - -// ======================================================= -// Save this file as the default file -// ------------- -void CGameListCtrl::OnSetDefaultGCM(wxCommandEvent& WXUNUSED (event)) -{ - const GameListItem *iso = GetSelectedISO(); - if (!iso) return; - SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDefaultGCM = iso->GetFileName(); - SConfig::GetInstance().SaveSettings(); -} -// ============= - - -void CGameListCtrl::OnDeleteGCM(wxCommandEvent& WXUNUSED (event)) -{ - if (GetSelectedItemCount() == 1) - { - const GameListItem *iso = GetSelectedISO(); - if (!iso) - return; - if (wxMessageBox(_("Are you sure you want to delete this file?\nIt will be gone forever!"), - wxMessageBoxCaptionStr, wxYES_NO|wxICON_EXCLAMATION) == wxYES) - { - File::Delete(iso->GetFileName().c_str()); - Update(); - } - } - else - { - if (wxMessageBox(_("Are you sure you want to delete these files?\nThey will be gone forever!"), - wxMessageBoxCaptionStr, wxYES_NO|wxICON_EXCLAMATION) == wxYES) - { - int selected = GetSelectedItemCount(); - - for (int i = 0; i < selected; i++) - { - const GameListItem *iso = GetSelectedISO(); - File::Delete(iso->GetFileName().c_str()); - } - Update(); - } - } -} - -void CGameListCtrl::OnProperties(wxCommandEvent& WXUNUSED (event)) -{ - const GameListItem *iso = GetSelectedISO(); - if (!iso) - return; - CISOProperties ISOProperties(iso->GetFileName(), this); - ISOProperties.ShowModal(); - if (ISOProperties.bRefreshList) - Update(); -} - -void CGameListCtrl::MultiCompressCB(const char* text, float percent, void* arg) -{ - wxString textString(wxString::Format(wxT("%s (%i/%i) - %s"), m_currentFilename.c_str(), m_currentItem+1, m_numberItem, text)); - - percent = (((float)m_currentItem) + percent) / (float)m_numberItem; - wxProgressDialog* pDialog = (wxProgressDialog*)arg; - pDialog->Update((int)(percent*1000), textString); -} - -void CGameListCtrl::OnMultiCompressGCM(wxCommandEvent& /*event*/) -{ - CompressSelection(true); -} - -void CGameListCtrl::OnMultiDecompressGCM(wxCommandEvent& /*event*/) -{ - CompressSelection(false); -} - -void CGameListCtrl::CompressSelection(bool _compress) -{ - wxString dirHome; - wxGetHomeDir(&dirHome); - - wxDirDialog browseDialog(this, _("Browse for output directory"), dirHome, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST); - if (browseDialog.ShowModal() != wxID_OK) - return; - - wxProgressDialog progressDialog(_compress ? _("Compressing ISO") : _("Decompressing ISO"), - _("Working..."), - 1000, // range - this, // parent - wxPD_APP_MODAL | - // wxPD_AUTO_HIDE | -- try this as well - wxPD_ELAPSED_TIME | - wxPD_ESTIMATED_TIME | - wxPD_REMAINING_TIME | - wxPD_SMOOTH // - makes indeterminate mode bar on WinXP very small - ); - - progressDialog.SetSize(wxSize(600, 180)); - progressDialog.CenterOnParent(); - - m_currentItem = 0; - m_numberItem = GetSelectedItemCount(); - for (u32 i=0; iIsCompressed() && _compress) - { - std::string FileName; - SplitPath(iso->GetFileName(), NULL, &FileName, NULL); - m_currentFilename = FileName; - FileName.append(".gcz"); - - std::string OutputFileName; - BuildCompleteFilename(OutputFileName, (const char *)browseDialog.GetPath().mb_str(wxConvUTF8), FileName); - - DiscIO::CompressFileToBlob(iso->GetFileName().c_str(), OutputFileName.c_str(), 0, 16384, &MultiCompressCB, &progressDialog); - } - else if (iso->IsCompressed() && !_compress) - { - std::string FileName; - SplitPath(iso->GetFileName(), NULL, &FileName, NULL); - m_currentFilename = FileName; - FileName.append(".gcm"); - - std::string OutputFileName; - BuildCompleteFilename(OutputFileName, (const char *)browseDialog.GetPath().mb_str(wxConvUTF8), FileName); - - DiscIO::DecompressBlobToFile(iso->GetFileName().c_str(), OutputFileName.c_str(), &MultiCompressCB, &progressDialog); - } - m_currentItem++; - } - Update(); -} - -void CGameListCtrl::CompressCB(const char* text, float percent, void* arg) -{ - wxProgressDialog* pDialog = (wxProgressDialog*)arg; - pDialog->Update((int)(percent*1000), wxString::FromAscii(text)); -} - -void CGameListCtrl::OnCompressGCM(wxCommandEvent& WXUNUSED (event)) -{ - const GameListItem *iso = GetSelectedISO(); - if (!iso) - return; - - wxString path; - - std::string FileName; - SplitPath(iso->GetFileName(), NULL, &FileName, NULL); - - if (iso->IsCompressed()) - { - path = wxFileSelector( - _T("Save decompressed ISO"), - wxEmptyString, wxString::FromAscii(FileName.c_str()), wxEmptyString, - wxString::Format - ( - _T("All GC/Wii ISO files (gcm)|*.gcm|All files (%s)|%s"), - wxFileSelectorDefaultWildcardStr, - wxFileSelectorDefaultWildcardStr - ), - wxFD_SAVE, - this); - - if (!path) - { - return; - } - } - else - { - path = wxFileSelector( - _T("Save compressed ISO"), - wxEmptyString, wxString::FromAscii(FileName.c_str()), wxEmptyString, - wxString::Format - ( - _T("All compressed GC/Wii ISO files (gcz)|*.gcz|All files (%s)|%s"), - wxFileSelectorDefaultWildcardStr, - wxFileSelectorDefaultWildcardStr - ), - wxFD_SAVE, - this); - - if (!path) - { - return; - } - } - - wxProgressDialog dialog(iso->IsCompressed() ? _T("Decompressing ISO") : _T("Compressing ISO"), - _T("Working..."), - 1000, // range - this, // parent - wxPD_APP_MODAL | - // wxPD_AUTO_HIDE | -- try this as well - wxPD_ELAPSED_TIME | - wxPD_ESTIMATED_TIME | - wxPD_REMAINING_TIME | - wxPD_SMOOTH // - makes indeterminate mode bar on WinXP very small - ); - - dialog.SetSize(wxSize(280, 180)); - dialog.CenterOnParent(); - - if (iso->IsCompressed()) - DiscIO::DecompressBlobToFile(iso->GetFileName().c_str(), path.char_str(), &CompressCB, &dialog); - else - DiscIO::CompressFileToBlob(iso->GetFileName().c_str(), path.char_str(), 0, 16384, &CompressCB, &dialog); - - Update(); -} - -void CGameListCtrl::OnSize(wxSizeEvent& event) -{ - AutomaticColumnWidth(); - - event.Skip(); -} - -void CGameListCtrl::AutomaticColumnWidth() -{ - wxRect rc(GetClientRect()); - - if (GetColumnCount() == 1) - { - SetColumnWidth(0, rc.GetWidth()); - } - else if (GetColumnCount() > 4) - { - int resizable = rc.GetWidth() - (213 + GetColumnWidth(COLUMN_SIZE)); - - SetColumnWidth(COLUMN_TITLE, wxMax(0.3*resizable, 100)); - SetColumnWidth(COLUMN_COMPANY, wxMax(0.2*resizable, 100)); - SetColumnWidth(COLUMN_NOTES, wxMax(0.5*resizable, 100)); - } -} - -void CGameListCtrl::UnselectAll() -{ - for (int i=0; i + +#include + +#include "FileSearch.h" +#include "FileUtil.h" +#include "StringUtil.h" +#include "BootManager.h" +#include "Config.h" +#include "GameListCtrl.h" +#include "Blob.h" +#include "ISOProperties.h" +#include "IniFile.h" + +#if USE_XPM_BITMAPS + #include "../resources/Flag_Europe.xpm" + #include "../resources/Flag_France.xpm" + #include "../resources/Flag_Japan.xpm" + #include "../resources/Flag_USA.xpm" +#endif // USE_XPM_BITMAPS + +size_t CGameListCtrl::m_currentItem = 0; +size_t CGameListCtrl::m_numberItem = 0; +std::string CGameListCtrl::m_currentFilename; + + +static int currentColumn = 0; +bool operator < (const GameListItem &one, const GameListItem &other) +{ + switch(currentColumn) + { + case CGameListCtrl::COLUMN_TITLE: return strcasecmp(one.GetName().c_str(), other.GetName().c_str()) < 0; + case CGameListCtrl::COLUMN_COMPANY: return strcasecmp(one.GetCompany().c_str(), other.GetCompany().c_str()) < 0; + case CGameListCtrl::COLUMN_NOTES: return strcasecmp(one.GetDescription().c_str(), other.GetDescription().c_str()) < 0; + case CGameListCtrl::COLUMN_COUNTRY: return (one.GetCountry() < other.GetCountry()); + case CGameListCtrl::COLUMN_SIZE: return (one.GetFileSize() < other.GetFileSize()); + default: return strcasecmp(one.GetName().c_str(), other.GetName().c_str()) < 0; + } +} + +BEGIN_EVENT_TABLE(CGameListCtrl, wxListCtrl) + +EVT_SIZE(CGameListCtrl::OnSize) +EVT_RIGHT_DOWN(CGameListCtrl::OnRightClick) +EVT_LIST_COL_BEGIN_DRAG(LIST_CTRL, CGameListCtrl::OnColBeginDrag) +EVT_LIST_ITEM_ACTIVATED(LIST_CTRL, CGameListCtrl::OnActivated) +EVT_LIST_COL_CLICK(LIST_CTRL, CGameListCtrl::OnColumnClick) +EVT_MENU(IDM_PROPERTIES, CGameListCtrl::OnProperties) +EVT_MENU(IDM_OPENCONTAININGFOLDER, CGameListCtrl::OnOpenContainingFolder) +EVT_MENU(IDM_SETDEFAULTGCM, CGameListCtrl::OnSetDefaultGCM) +EVT_MENU(IDM_COMPRESSGCM, CGameListCtrl::OnCompressGCM) +EVT_MENU(IDM_MULTICOMPRESSGCM, CGameListCtrl::OnMultiCompressGCM) +EVT_MENU(IDM_MULTIDECOMPRESSGCM, CGameListCtrl::OnMultiDecompressGCM) +EVT_MENU(IDM_DELETEGCM, CGameListCtrl::OnDeleteGCM) +END_EVENT_TABLE() + +CGameListCtrl::CGameListCtrl(wxWindow* parent, const wxWindowID id, const wxPoint& pos, const wxSize& size, long style) + : wxListCtrl(parent, id, pos, size, style) // | wxLC_VIRTUAL) +{ +} + +CGameListCtrl::~CGameListCtrl() +{ + if (m_imageListSmall) delete m_imageListSmall; +} + +void CGameListCtrl::InitBitmaps() +{ + m_imageListSmall = new wxImageList(96, 32); + SetImageList(m_imageListSmall, wxIMAGE_LIST_SMALL); + m_FlagImageIndex.resize(DiscIO::IVolume::NUMBER_OF_COUNTRIES); + wxIcon iconTemp; + iconTemp.CopyFromBitmap(wxBitmap(Flag_Europe_xpm)); + m_FlagImageIndex[DiscIO::IVolume::COUNTRY_EUROPE] = m_imageListSmall->Add(iconTemp); + iconTemp.CopyFromBitmap(wxBitmap(Flag_France_xpm)); + m_FlagImageIndex[DiscIO::IVolume::COUNTRY_FRANCE] = m_imageListSmall->Add(iconTemp); + iconTemp.CopyFromBitmap(wxBitmap(Flag_USA_xpm)); + m_FlagImageIndex[DiscIO::IVolume::COUNTRY_USA] = m_imageListSmall->Add(iconTemp); + iconTemp.CopyFromBitmap(wxBitmap(Flag_Japan_xpm)); + m_FlagImageIndex[DiscIO::IVolume::COUNTRY_JAP] = m_imageListSmall->Add(iconTemp); + iconTemp.CopyFromBitmap(wxBitmap(Flag_Europe_xpm)); + m_FlagImageIndex[DiscIO::IVolume::COUNTRY_UNKNOWN] = m_imageListSmall->Add(iconTemp); +} + +void CGameListCtrl::BrowseForDirectory() +{ + wxString dirHome; + wxGetHomeDir(&dirHome); + + // browse + wxDirDialog dialog(this, _("Browse directory"), dirHome, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST); + + if (dialog.ShowModal() == wxID_OK) + { + SConfig::GetInstance().m_ISOFolder.push_back( + std::string(dialog.GetPath().ToAscii()) + ); + SConfig::GetInstance().SaveSettings(); + Update(); + } +} + +void CGameListCtrl::Update() +{ + if (m_imageListSmall) + { + delete m_imageListSmall; + m_imageListSmall = NULL; + } + + Hide(); + + ScanForISOs(); + + ClearAll(); + + if (m_ISOFiles.size() != 0) + { + // Don't load bitmaps unless there are games to list + InitBitmaps(); + + // add columns + InsertColumn(COLUMN_BANNER, _("Banner")); + InsertColumn(COLUMN_TITLE, _("Title")); + InsertColumn(COLUMN_COMPANY, _("Company")); + InsertColumn(COLUMN_NOTES, _("Notes")); + InsertColumn(COLUMN_COUNTRY, _("")); + InsertColumn(COLUMN_SIZE, _("Size")); + InsertColumn(COLUMN_EMULATION_STATE, _("Emulation")); + + // set initial sizes for columns + SetColumnWidth(COLUMN_BANNER, 106); + SetColumnWidth(COLUMN_TITLE, 150); + SetColumnWidth(COLUMN_COMPANY, 100); + SetColumnWidth(COLUMN_NOTES, 200); + SetColumnWidth(COLUMN_COUNTRY, 32); + SetColumnWidth(COLUMN_EMULATION_STATE, 75); + + // add all items + for (int i = 0; i < (int)m_ISOFiles.size(); i++) + { + InsertItemInReportView(i); + if (m_ISOFiles[i].IsCompressed()) + SetItemTextColour(i, wxColour(0xFF0000)); + } + + SetColumnWidth(COLUMN_SIZE, wxLIST_AUTOSIZE); + } + else + { + InsertColumn(COLUMN_BANNER, _("No ISOs found")); + + long index = InsertItem(0, wxString::FromAscii("msgRow")); + SetItem(index, COLUMN_BANNER, _("Dolphin could not find any GC/Wii ISOs. Doubleclick here to browse for files...")); + SetItemFont(index, *wxITALIC_FONT); + SetColumnWidth(COLUMN_BANNER, wxLIST_AUTOSIZE); + } + + AutomaticColumnWidth(); + + Show(); +} + +wxString NiceSizeFormat(s64 _size) +{ + const char* sizes[] = {"b", "KB", "MB", "GB", "TB", "PB", "EB"}; + int s = 0; + int frac = 0; + + while (_size > (s64)1024) + { + s++; + frac = (int)_size & 1023; + _size /= (s64)1024; + } + + float f = (float)_size + ((float)frac / 1024.0f); + + wxString NiceString; + char tempstr[32]; + sprintf(tempstr,"%3.1f %s", f, sizes[s]); + NiceString = wxString::FromAscii(tempstr); + return(NiceString); +} + +void CGameListCtrl::InsertItemInReportView(long _Index) +{ + GameListItem& rISOFile = m_ISOFiles[_Index]; + + int ImageIndex = -1; + + if (rISOFile.GetImage().IsOk()) + { + ImageIndex = m_imageListSmall->Add(rISOFile.GetImage()); + } + + // Insert a row with the banner image + long ItemIndex = InsertItem(_Index, wxEmptyString, ImageIndex); + + // Background color + SetBackgroundColor(); + + // When using wxListCtrl, there is no hope of per-column text colors. + // But for reference, here are the old colors that were used: (BGR) + // title: 0xFF0000 + // company: 0x007030 + + SetItem(_Index, COLUMN_TITLE, wxString::FromAscii(rISOFile.GetName().c_str()), -1); + SetItem(_Index, COLUMN_COMPANY, wxString::FromAscii(rISOFile.GetCompany().c_str()), -1); + SetItem(_Index, COLUMN_NOTES, wxString::FromAscii(rISOFile.GetDescription().c_str()), -1); + SetItem(_Index, COLUMN_SIZE, NiceSizeFormat(rISOFile.GetFileSize()), -1); + + // Emulation status = COLUMN_EMULATION_STATE + { + wxListItem item; + item.SetId(_Index); + IniFile ini; + std::string EmuState; + std::string GameIni; + item.SetColumn(COLUMN_EMULATION_STATE); + //NOTE (Daco): i dont like the fact of having so much ini's just to have + //the game emulation state of every game you have. but 1 huge ini is no option + GameIni = FULL_GAMECONFIG_DIR + (rISOFile.GetUniqueID()) + ".ini"; + ini.Load(GameIni.c_str()); + ini.Get("EmuState","EmulationStateId",&EmuState); + if (!EmuState.empty()) + { + if(EmuState == "5") + item.SetText(_("Perfect")); + else if(EmuState == "4") + item.SetText(_("In Game")); + else if(EmuState == "3") + item.SetText(_("Intro")); + else if(EmuState == "2") + { + item.SetText(_("Problems: Other")); + //NOTE (Daco): IMO under 2 i see problems like music and games that only work + //with GL or only with DX9 + //TODO (Daco): maybe 2 should get a function to present more info... o.o + } + else if(EmuState == "1") + item.SetText(_("Broken")); + else if(EmuState == "0") + item.SetText(_("Not Set")); + else + { + //if the EmuState isn't a number between 0 & 5 we dont know the state + //hence why it should say unknown + item.SetText(_("unknown emu ID")); + } + } + SetItem(item); + } + +#ifndef __WXMSW__ + // Country + { + // Can't do this in Windows - we use DrawSubItem instead, see below + wxListItem item; + item.m_itemId = ItemIndex; + item.SetColumn(COLUMN_COUNTRY); + //item.SetBackgroundColour(color); + DiscIO::IVolume::ECountry Country = rISOFile.GetCountry(); + + if (size_t(Country) < m_FlagImageIndex.size()) + { + item.SetImage(m_FlagImageIndex[rISOFile.GetCountry()]); + } + + SetItem(item); + } +#endif // __WXMSW__ + + // Item data + SetItemData(_Index, ItemIndex); +} +#ifdef _WIN32 +bool CGameListCtrl::MSWDrawSubItem(wxPaintDC& rPaintDC, int item, int subitem) +{ + bool Result = false; +#ifdef __WXMSW__ + switch (subitem) + { + case COLUMN_COUNTRY: + size_t Index = GetItemData(item); + + if (Index < m_ISOFiles.size()) + { + const GameListItem& rISO = m_ISOFiles[Index]; + wxRect SubItemRect; + this->GetSubItemRect(item, subitem, SubItemRect); + m_imageListSmall->Draw(m_FlagImageIndex[rISO.GetCountry()], rPaintDC, SubItemRect.GetLeft(), SubItemRect.GetTop()); + } + } +#endif + + return(Result); +} +#endif + +wxColour blend50(const wxColour& c1, const wxColour& c2) +{ + unsigned char r,g,b,a; + r = c1.Red()/2 + c2.Red()/2; + g = c1.Green()/2 + c2.Green()/2; + b = c1.Blue()/2 + c2.Blue()/2; + a = c1.Alpha()/2 + c2.Alpha()/2; + return a << 24 | b << 16 | g << 8 | r; +} + +void CGameListCtrl::SetBackgroundColor() +{ + for(long i = 0; i < GetItemCount(); i++) + { + wxColour color = (i & 1) ? blend50(wxSystemSettings::GetColour(wxSYS_COLOUR_3DLIGHT), wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW)) : wxSystemSettings::GetColour(wxSYS_COLOUR_WINDOW); + CGameListCtrl::SetItemBackgroundColour(i, color); + } +} + +void CGameListCtrl::ScanForISOs() +{ + m_ISOFiles.clear(); + CFileSearch::XStringVector Directories(SConfig::GetInstance().m_ISOFolder); + + CFileSearch::XStringVector Extensions; + Extensions.push_back("*.iso"); + Extensions.push_back("*.gcm"); + Extensions.push_back("*.gcz"); + + CFileSearch FileSearch(Extensions, Directories); + const CFileSearch::XStringVector& rFilenames = FileSearch.GetFileNames(); + + if (rFilenames.size() > 0) + { + wxProgressDialog dialog(_T("Scanning for ISOs"), + _T("Scanning..."), + rFilenames.size(), // range + this, // parent + wxPD_APP_MODAL | + // wxPD_AUTO_HIDE | -- try this as well + wxPD_ELAPSED_TIME | + wxPD_ESTIMATED_TIME | + wxPD_REMAINING_TIME | + wxPD_SMOOTH // - makes indeterminate mode bar on WinXP very small + ); + dialog.CenterOnParent(); + + for (u32 i = 0; i < rFilenames.size(); i++) + { + std::string FileName; + SplitPath(rFilenames[i], NULL, &FileName, NULL); + + wxString msg; + char tempstring[128]; + sprintf(tempstring,"Scanning %s", FileName.c_str()); + msg = wxString::FromAscii(tempstring); + + bool Cont = dialog.Update(i, msg); + + if (!Cont) + { + break; + } + GameListItem ISOFile(rFilenames[i]); + if (ISOFile.IsValid()) + { + m_ISOFiles.push_back(ISOFile); + } + else + PanicAlert("Invalid ISO file %s", rFilenames[i].c_str()); + } + } + std::sort(m_ISOFiles.begin(), m_ISOFiles.end()); +} + +void CGameListCtrl::OnColBeginDrag(wxListEvent& event) +{ + if (event.GetColumn() != COLUMN_TITLE && event.GetColumn() != COLUMN_COMPANY + && event.GetColumn() != COLUMN_NOTES) + event.Veto(); +} + +const GameListItem *CGameListCtrl::GetISO(int index) const +{ + return &m_ISOFiles[index]; +} + +CGameListCtrl *caller; +int wxCALLBACK wxListCompare(long item1, long item2, long sortData) +{ + //return 1 if item1 > item2 + //return -1 if item1 < item2 + //0 for identity + const GameListItem *iso1 = caller->GetISO(item1); + const GameListItem *iso2 = caller->GetISO(item2); + + int t = 1; + + if (sortData < 0) + { + t = -1; + sortData = -sortData; + } + + switch(sortData) + { + case CGameListCtrl::COLUMN_TITLE: + return strcasecmp(iso1->GetName().c_str(),iso2->GetName().c_str()) *t; + case CGameListCtrl::COLUMN_COMPANY: + return strcasecmp(iso1->GetCompany().c_str(),iso2->GetCompany().c_str()) *t; + case CGameListCtrl::COLUMN_NOTES: + return strcasecmp(iso1->GetDescription().c_str(),iso2->GetDescription().c_str()) *t; + case CGameListCtrl::COLUMN_COUNTRY: + if(iso1->GetCountry() > iso2->GetCountry()) return 1 *t; + if(iso1->GetCountry() < iso2->GetCountry()) return -1 *t; + return 0; + case CGameListCtrl::COLUMN_SIZE: + if (iso1->GetFileSize() > iso2->GetFileSize()) return 1 *t; + if (iso1->GetFileSize() < iso2->GetFileSize()) return -1 *t; + return 0; + } + + return 0; +} + +void CGameListCtrl::OnColumnClick(wxListEvent& event) +{ + if(event.GetColumn() != COLUMN_BANNER && event.GetColumn() != COLUMN_EMULATION_STATE) + { + int current_column = event.GetColumn(); + + if(last_column == current_column) + { + last_sort = -last_sort; + } + else + { + last_column = current_column; + last_sort = current_column; + } + + caller = this; + SortItems(wxListCompare, last_sort); + } + + SetBackgroundColor(); + + event.Skip(); +} + +void CGameListCtrl::OnRightClick(wxMouseEvent& event) +{ + // Focus the clicked item. + int flags; + long item = HitTest(event.GetPosition(), flags); + if (item != wxNOT_FOUND) + { + if (GetItemState(item, wxLIST_STATE_SELECTED) != wxLIST_STATE_SELECTED) + { + UnselectAll(); + SetItemState(item, wxLIST_STATE_SELECTED, wxLIST_STATE_SELECTED ); + } + SetItemState(item, wxLIST_STATE_FOCUSED, wxLIST_STATE_FOCUSED); + } + + if (GetSelectedItemCount() == 1) + { + const GameListItem *selected_iso = GetSelectedISO(); + if (selected_iso) + { + wxMenu popupMenu; + popupMenu.Append(IDM_PROPERTIES, _("&Properties")); + popupMenu.AppendSeparator(); + popupMenu.Append(IDM_OPENCONTAININGFOLDER, _("Open &containing folder")); + popupMenu.AppendCheckItem(IDM_SETDEFAULTGCM, _("Set as &default ISO")); + if(selected_iso->GetFileName() == SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDefaultGCM) + popupMenu.FindItemByPosition(3)->Check(); + + popupMenu.AppendSeparator(); + popupMenu.Append(IDM_DELETEGCM, _("&Delete ISO...")); + + if (selected_iso->IsCompressed()) + popupMenu.Append(IDM_COMPRESSGCM, _("Decompress ISO...")); + else + popupMenu.Append(IDM_COMPRESSGCM, _("Compress ISO...")); + + PopupMenu(&popupMenu); + } + } + else if (GetSelectedItemCount() > 1) + { + wxMenu popupMenu; + popupMenu.Append(IDM_DELETEGCM, _("&Delete selected ISOs...")); + popupMenu.AppendSeparator(); + popupMenu.Append(IDM_MULTICOMPRESSGCM, _("Compress selected ISOs...")); + popupMenu.Append(IDM_MULTIDECOMPRESSGCM, _("Decompress selected ISOs...")); + PopupMenu(&popupMenu); + } +} + +void CGameListCtrl::OnActivated(wxListEvent& event) +{ + if (m_ISOFiles.size() == 0) + { + BrowseForDirectory(); + } + else + { + size_t Index = event.GetData(); + if (Index < m_ISOFiles.size()) + { + const GameListItem& rISOFile = m_ISOFiles[Index]; + BootManager::BootCore(rISOFile.GetFileName()); + } + } +} + +const GameListItem * CGameListCtrl::GetSelectedISO() +{ + long item = GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + if (item == -1) + return 0; + else + { + if (GetSelectedItemCount() > 1) + SetItemState(item, 0, wxLIST_STATE_SELECTED); + + return &m_ISOFiles[GetItemData(item)]; + } +} + +void CGameListCtrl::OnOpenContainingFolder(wxCommandEvent& WXUNUSED (event)) +{ + const GameListItem *iso = GetSelectedISO(); + if (!iso) + return; + std::string path; + SplitPath(iso->GetFileName(), &path, 0, 0); + File::Explore(path.c_str()); +} + + +// ======================================================= +// Save this file as the default file +// ------------- +void CGameListCtrl::OnSetDefaultGCM(wxCommandEvent& WXUNUSED (event)) +{ + const GameListItem *iso = GetSelectedISO(); + if (!iso) return; + SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDefaultGCM = iso->GetFileName(); + SConfig::GetInstance().SaveSettings(); +} +// ============= + + +void CGameListCtrl::OnDeleteGCM(wxCommandEvent& WXUNUSED (event)) +{ + if (GetSelectedItemCount() == 1) + { + const GameListItem *iso = GetSelectedISO(); + if (!iso) + return; + if (wxMessageBox(_("Are you sure you want to delete this file?\nIt will be gone forever!"), + wxMessageBoxCaptionStr, wxYES_NO|wxICON_EXCLAMATION) == wxYES) + { + File::Delete(iso->GetFileName().c_str()); + Update(); + } + } + else + { + if (wxMessageBox(_("Are you sure you want to delete these files?\nThey will be gone forever!"), + wxMessageBoxCaptionStr, wxYES_NO|wxICON_EXCLAMATION) == wxYES) + { + int selected = GetSelectedItemCount(); + + for (int i = 0; i < selected; i++) + { + const GameListItem *iso = GetSelectedISO(); + File::Delete(iso->GetFileName().c_str()); + } + Update(); + } + } +} + +void CGameListCtrl::OnProperties(wxCommandEvent& WXUNUSED (event)) +{ + const GameListItem *iso = GetSelectedISO(); + if (!iso) + return; + CISOProperties ISOProperties(iso->GetFileName(), this); + ISOProperties.ShowModal(); + if (ISOProperties.bRefreshList) + Update(); +} + +void CGameListCtrl::MultiCompressCB(const char* text, float percent, void* arg) +{ + wxString textString(wxString::Format(wxT("%s (%i/%i) - %s"), m_currentFilename.c_str(), m_currentItem+1, m_numberItem, text)); + + percent = (((float)m_currentItem) + percent) / (float)m_numberItem; + wxProgressDialog* pDialog = (wxProgressDialog*)arg; + pDialog->Update((int)(percent*1000), textString); +} + +void CGameListCtrl::OnMultiCompressGCM(wxCommandEvent& /*event*/) +{ + CompressSelection(true); +} + +void CGameListCtrl::OnMultiDecompressGCM(wxCommandEvent& /*event*/) +{ + CompressSelection(false); +} + +void CGameListCtrl::CompressSelection(bool _compress) +{ + wxString dirHome; + wxGetHomeDir(&dirHome); + + wxDirDialog browseDialog(this, _("Browse for output directory"), dirHome, wxDD_DEFAULT_STYLE | wxDD_DIR_MUST_EXIST); + if (browseDialog.ShowModal() != wxID_OK) + return; + + wxProgressDialog progressDialog(_compress ? _("Compressing ISO") : _("Decompressing ISO"), + _("Working..."), + 1000, // range + this, // parent + wxPD_APP_MODAL | + // wxPD_AUTO_HIDE | -- try this as well + wxPD_ELAPSED_TIME | + wxPD_ESTIMATED_TIME | + wxPD_REMAINING_TIME | + wxPD_SMOOTH // - makes indeterminate mode bar on WinXP very small + ); + + progressDialog.SetSize(wxSize(600, 180)); + progressDialog.CenterOnParent(); + + m_currentItem = 0; + m_numberItem = GetSelectedItemCount(); + for (u32 i=0; iIsCompressed() && _compress) + { + std::string FileName; + SplitPath(iso->GetFileName(), NULL, &FileName, NULL); + m_currentFilename = FileName; + FileName.append(".gcz"); + + std::string OutputFileName; + BuildCompleteFilename(OutputFileName, (const char *)browseDialog.GetPath().mb_str(wxConvUTF8), FileName); + + DiscIO::CompressFileToBlob(iso->GetFileName().c_str(), OutputFileName.c_str(), 0, 16384, &MultiCompressCB, &progressDialog); + } + else if (iso->IsCompressed() && !_compress) + { + std::string FileName; + SplitPath(iso->GetFileName(), NULL, &FileName, NULL); + m_currentFilename = FileName; + FileName.append(".gcm"); + + std::string OutputFileName; + BuildCompleteFilename(OutputFileName, (const char *)browseDialog.GetPath().mb_str(wxConvUTF8), FileName); + + DiscIO::DecompressBlobToFile(iso->GetFileName().c_str(), OutputFileName.c_str(), &MultiCompressCB, &progressDialog); + } + m_currentItem++; + } + Update(); +} + +void CGameListCtrl::CompressCB(const char* text, float percent, void* arg) +{ + wxProgressDialog* pDialog = (wxProgressDialog*)arg; + pDialog->Update((int)(percent*1000), wxString::FromAscii(text)); +} + +void CGameListCtrl::OnCompressGCM(wxCommandEvent& WXUNUSED (event)) +{ + const GameListItem *iso = GetSelectedISO(); + if (!iso) + return; + + wxString path; + + std::string FileName; + SplitPath(iso->GetFileName(), NULL, &FileName, NULL); + + if (iso->IsCompressed()) + { + path = wxFileSelector( + _T("Save decompressed ISO"), + wxEmptyString, wxString::FromAscii(FileName.c_str()), wxEmptyString, + wxString::Format + ( + _T("All GC/Wii ISO files (gcm)|*.gcm|All files (%s)|%s"), + wxFileSelectorDefaultWildcardStr, + wxFileSelectorDefaultWildcardStr + ), + wxFD_SAVE, + this); + + if (!path) + { + return; + } + } + else + { + path = wxFileSelector( + _T("Save compressed ISO"), + wxEmptyString, wxString::FromAscii(FileName.c_str()), wxEmptyString, + wxString::Format + ( + _T("All compressed GC/Wii ISO files (gcz)|*.gcz|All files (%s)|%s"), + wxFileSelectorDefaultWildcardStr, + wxFileSelectorDefaultWildcardStr + ), + wxFD_SAVE, + this); + + if (!path) + { + return; + } + } + + wxProgressDialog dialog(iso->IsCompressed() ? _T("Decompressing ISO") : _T("Compressing ISO"), + _T("Working..."), + 1000, // range + this, // parent + wxPD_APP_MODAL | + // wxPD_AUTO_HIDE | -- try this as well + wxPD_ELAPSED_TIME | + wxPD_ESTIMATED_TIME | + wxPD_REMAINING_TIME | + wxPD_SMOOTH // - makes indeterminate mode bar on WinXP very small + ); + + dialog.SetSize(wxSize(280, 180)); + dialog.CenterOnParent(); + + if (iso->IsCompressed()) + DiscIO::DecompressBlobToFile(iso->GetFileName().c_str(), path.char_str(), &CompressCB, &dialog); + else + DiscIO::CompressFileToBlob(iso->GetFileName().c_str(), path.char_str(), 0, 16384, &CompressCB, &dialog); + + Update(); +} + +void CGameListCtrl::OnSize(wxSizeEvent& event) +{ + AutomaticColumnWidth(); + + event.Skip(); +} + +void CGameListCtrl::AutomaticColumnWidth() +{ + wxRect rc(GetClientRect()); + + if (GetColumnCount() == 1) + { + SetColumnWidth(0, rc.GetWidth()); + } + else if (GetColumnCount() > 4) + { + int resizable = rc.GetWidth() - (213 + GetColumnWidth(COLUMN_SIZE)); + + SetColumnWidth(COLUMN_TITLE, wxMax(0.3*resizable, 100)); + SetColumnWidth(COLUMN_COMPANY, wxMax(0.2*resizable, 100)); + SetColumnWidth(COLUMN_NOTES, wxMax(0.5*resizable, 100)); + } +} + +void CGameListCtrl::UnselectAll() +{ + for (int i=0; i -#include -#include - -#include "Globals.h" -#include "FileUtil.h" -#include "ISOFile.h" -#include "StringUtil.h" - -#include "VolumeCreator.h" -#include "Filesystem.h" -#include "BannerLoader.h" -#include "FileSearch.h" -#include "CompressedBlob.h" -#include "ChunkFile.h" -#include "../resources/no_banner.cpp" - -#define CACHE_REVISION 0x105 - -#define DVD_BANNER_WIDTH 96 -#define DVD_BANNER_HEIGHT 32 - -static u32 g_ImageTemp[DVD_BANNER_WIDTH * DVD_BANNER_HEIGHT]; - -GameListItem::GameListItem(const std::string& _rFileName) - : m_FileName(_rFileName) - , m_FileSize(0) - , m_Valid(false) - , m_BlobCompressed(false) - , m_pImage(NULL) - , m_ImageSize(0) -{ - - if (LoadFromCache()) - { - m_Valid = true; - } - else - { - DiscIO::IVolume* pVolume = DiscIO::CreateVolumeFromFilename(_rFileName); - - if (pVolume != NULL) - { - m_Name = _rFileName; - m_Country = pVolume->GetCountry(); - m_FileSize = File::GetSize(_rFileName.c_str()); - m_VolumeSize = pVolume->GetSize(); - m_Name = pVolume->GetName(); - m_UniqueID = pVolume->GetUniqueID(); - m_BlobCompressed = DiscIO::IsCompressedBlob(_rFileName.c_str()); - - // check if we can get some infos from the banner file too - DiscIO::IFileSystem* pFileSystem = DiscIO::CreateFileSystem(pVolume); - - if (pFileSystem != NULL) - { - DiscIO::IBannerLoader* pBannerLoader = DiscIO::CreateBannerLoader(*pFileSystem); - - if (pBannerLoader != NULL) - { - if (pBannerLoader->IsValid()) - { - pBannerLoader->GetName(m_Name, 0); //m_Country == DiscIO::IVolume::COUNTRY_JAP ? 1 : 0); - pBannerLoader->GetCompany(m_Company); - pBannerLoader->GetDescription(m_Description); - if (pBannerLoader->GetBanner(g_ImageTemp)) - { - m_ImageSize = DVD_BANNER_WIDTH * DVD_BANNER_HEIGHT * 3; - m_pImage = new u8[m_ImageSize]; //(u8*)malloc(m_ImageSize); - - for (size_t i = 0; i < DVD_BANNER_WIDTH * DVD_BANNER_HEIGHT; i++) - { - m_pImage[i * 3 + 0] = (g_ImageTemp[i] & 0xFF0000) >> 16; - m_pImage[i * 3 + 1] = (g_ImageTemp[i] & 0x00FF00) >> 8; - m_pImage[i * 3 + 2] = (g_ImageTemp[i] & 0x0000FF) >> 0; - } - } - } - - delete pBannerLoader; - } - - delete pFileSystem; - } - - delete pVolume; - - m_Valid = true; - - // just if we have an image create a cache file - // Wii isos create their images after you have generated the first savegame - if (m_pImage) - SaveToCache(); - } - } - - // i am not sure if this is a leak or if wxImage will release the code - if (m_pImage) - { -#if defined(HAVE_WX) && HAVE_WX - m_Image.Create(DVD_BANNER_WIDTH, DVD_BANNER_HEIGHT, m_pImage); -#endif - } - else - { - // default banner - wxMemoryInputStream istream(no_banner_png, sizeof no_banner_png); - wxImage iNoBanner(istream, wxBITMAP_TYPE_PNG); - m_Image = iNoBanner; - } -} - - -GameListItem::~GameListItem() -{ -} - -bool GameListItem::LoadFromCache() -{ - return CChunkFileReader::Load(CreateCacheFilename(), CACHE_REVISION, *this); -} - -void GameListItem::SaveToCache() -{ - if (!File::IsDirectory(FULL_CACHE_DIR)) - { - File::CreateDir(FULL_CACHE_DIR); - } - - CChunkFileReader::Save(CreateCacheFilename(), CACHE_REVISION, *this); -} - -void GameListItem::DoState(PointerWrap &p) -{ - p.Do(m_Name); - p.Do(m_Company); - p.Do(m_Description); - p.Do(m_UniqueID); - p.Do(m_FileSize); - p.Do(m_VolumeSize); - p.Do(m_Country); - p.Do(m_BlobCompressed); - p.DoBuffer(&m_pImage, m_ImageSize); -} - -std::string GameListItem::CreateCacheFilename() -{ - std::string Filename; - SplitPath(m_FileName, NULL, &Filename, NULL); - Filename.append(".cache"); - - std::string fullname(FULL_CACHE_DIR); - fullname += Filename; - return fullname; -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include +#include + +#include "Globals.h" +#include "FileUtil.h" +#include "ISOFile.h" +#include "StringUtil.h" + +#include "VolumeCreator.h" +#include "Filesystem.h" +#include "BannerLoader.h" +#include "FileSearch.h" +#include "CompressedBlob.h" +#include "ChunkFile.h" +#include "../resources/no_banner.cpp" + +#define CACHE_REVISION 0x105 + +#define DVD_BANNER_WIDTH 96 +#define DVD_BANNER_HEIGHT 32 + +static u32 g_ImageTemp[DVD_BANNER_WIDTH * DVD_BANNER_HEIGHT]; + +GameListItem::GameListItem(const std::string& _rFileName) + : m_FileName(_rFileName) + , m_FileSize(0) + , m_Valid(false) + , m_BlobCompressed(false) + , m_pImage(NULL) + , m_ImageSize(0) +{ + + if (LoadFromCache()) + { + m_Valid = true; + } + else + { + DiscIO::IVolume* pVolume = DiscIO::CreateVolumeFromFilename(_rFileName); + + if (pVolume != NULL) + { + m_Name = _rFileName; + m_Country = pVolume->GetCountry(); + m_FileSize = File::GetSize(_rFileName.c_str()); + m_VolumeSize = pVolume->GetSize(); + m_Name = pVolume->GetName(); + m_UniqueID = pVolume->GetUniqueID(); + m_BlobCompressed = DiscIO::IsCompressedBlob(_rFileName.c_str()); + + // check if we can get some infos from the banner file too + DiscIO::IFileSystem* pFileSystem = DiscIO::CreateFileSystem(pVolume); + + if (pFileSystem != NULL) + { + DiscIO::IBannerLoader* pBannerLoader = DiscIO::CreateBannerLoader(*pFileSystem); + + if (pBannerLoader != NULL) + { + if (pBannerLoader->IsValid()) + { + pBannerLoader->GetName(m_Name, 0); //m_Country == DiscIO::IVolume::COUNTRY_JAP ? 1 : 0); + pBannerLoader->GetCompany(m_Company); + pBannerLoader->GetDescription(m_Description); + if (pBannerLoader->GetBanner(g_ImageTemp)) + { + m_ImageSize = DVD_BANNER_WIDTH * DVD_BANNER_HEIGHT * 3; + m_pImage = new u8[m_ImageSize]; //(u8*)malloc(m_ImageSize); + + for (size_t i = 0; i < DVD_BANNER_WIDTH * DVD_BANNER_HEIGHT; i++) + { + m_pImage[i * 3 + 0] = (g_ImageTemp[i] & 0xFF0000) >> 16; + m_pImage[i * 3 + 1] = (g_ImageTemp[i] & 0x00FF00) >> 8; + m_pImage[i * 3 + 2] = (g_ImageTemp[i] & 0x0000FF) >> 0; + } + } + } + + delete pBannerLoader; + } + + delete pFileSystem; + } + + delete pVolume; + + m_Valid = true; + + // just if we have an image create a cache file + // Wii isos create their images after you have generated the first savegame + if (m_pImage) + SaveToCache(); + } + } + + // i am not sure if this is a leak or if wxImage will release the code + if (m_pImage) + { +#if defined(HAVE_WX) && HAVE_WX + m_Image.Create(DVD_BANNER_WIDTH, DVD_BANNER_HEIGHT, m_pImage); +#endif + } + else + { + // default banner + wxMemoryInputStream istream(no_banner_png, sizeof no_banner_png); + wxImage iNoBanner(istream, wxBITMAP_TYPE_PNG); + m_Image = iNoBanner; + } +} + + +GameListItem::~GameListItem() +{ +} + +bool GameListItem::LoadFromCache() +{ + return CChunkFileReader::Load(CreateCacheFilename(), CACHE_REVISION, *this); +} + +void GameListItem::SaveToCache() +{ + if (!File::IsDirectory(FULL_CACHE_DIR)) + { + File::CreateDir(FULL_CACHE_DIR); + } + + CChunkFileReader::Save(CreateCacheFilename(), CACHE_REVISION, *this); +} + +void GameListItem::DoState(PointerWrap &p) +{ + p.Do(m_Name); + p.Do(m_Company); + p.Do(m_Description); + p.Do(m_UniqueID); + p.Do(m_FileSize); + p.Do(m_VolumeSize); + p.Do(m_Country); + p.Do(m_BlobCompressed); + p.DoBuffer(&m_pImage, m_ImageSize); +} + +std::string GameListItem::CreateCacheFilename() +{ + std::string Filename; + SplitPath(m_FileName, NULL, &Filename, NULL); + Filename.append(".cache"); + + std::string fullname(FULL_CACHE_DIR); + fullname += Filename; + return fullname; +} diff --git a/Source/Core/DolphinWX/Src/ISOProperties.cpp b/Source/Core/DolphinWX/Src/ISOProperties.cpp index 4b29648857..96f1982d37 100644 --- a/Source/Core/DolphinWX/Src/ISOProperties.cpp +++ b/Source/Core/DolphinWX/Src/ISOProperties.cpp @@ -1,775 +1,775 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Globals.h" - -#include "ISOFile.h" -#include "VolumeCreator.h" -#include "Filesystem.h" -#include "ISOProperties.h" -#include "PatchAddEdit.h" - - -DiscIO::IVolume *OpenISO = NULL; -DiscIO::IFileSystem *pFileSystem = NULL; - -std::vector onFrame; - -struct ARListCode { - std::string name; - bool enabled; - std::vector ops; - u32 uiIndex; -}; -std::vector ARCodes; - - -BEGIN_EVENT_TABLE(CISOProperties, wxDialog) - EVT_CLOSE(CISOProperties::OnClose) - EVT_BUTTON(ID_CLOSE, CISOProperties::OnCloseClick) - EVT_BUTTON(ID_EDITCONFIG, CISOProperties::OnEditConfig) - EVT_CHOICE(ID_EMUSTATE, CISOProperties::SetRefresh) - EVT_LISTBOX(ID_PATCHES_LIST, CISOProperties::ListSelectionChanged) - EVT_BUTTON(ID_EDITPATCH, CISOProperties::PatchButtonClicked) - EVT_BUTTON(ID_ADDPATCH, CISOProperties::PatchButtonClicked) - EVT_BUTTON(ID_REMOVEPATCH, CISOProperties::PatchButtonClicked) - EVT_LISTBOX(ID_CHEATS_LIST, CISOProperties::ListSelectionChanged) - EVT_BUTTON(ID_EDITCHEAT, CISOProperties::ActionReplayButtonClicked) - EVT_BUTTON(ID_ADDCHEAT, CISOProperties::ActionReplayButtonClicked) - EVT_BUTTON(ID_REMOVECHEAT, CISOProperties::ActionReplayButtonClicked) - EVT_MENU(IDM_BNRSAVEAS, CISOProperties::OnBannerImageSave) - EVT_TREE_ITEM_RIGHT_CLICK(ID_TREECTRL, CISOProperties::OnRightClickOnTree) - EVT_MENU(IDM_EXTRACTFILE, CISOProperties::OnExtractFile) - EVT_MENU(IDM_EXTRACTDIR, CISOProperties::OnExtractDir) -END_EVENT_TABLE() - -CISOProperties::CISOProperties(const std::string fileName, wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& position, const wxSize& size, long style) - : wxDialog(parent, id, title, position, size, style) -{ - OpenISO = DiscIO::CreateVolumeFromFilename(fileName); - pFileSystem = DiscIO::CreateFileSystem(OpenISO); - - pFileSystem->GetFileList(Our_Files); - - GameListItem OpenISO_(fileName); - - bRefreshList = false; - CreateGUIControls(); - - GameIniFile = FULL_GAMECONFIG_DIR + (OpenISO->GetUniqueID()) + ".ini"; - if (GameIni.Load(GameIniFile.c_str())) - LoadGameConfig(); - else - { - FILE *f = fopen(GameIniFile.c_str(), "w"); - fprintf(f, "# %s - %s\n", OpenISO->GetUniqueID().c_str(), OpenISO->GetName().c_str()); - fprintf(f, "[Core]\n#Values set here will override the main dolphin settings.\n"); - fprintf(f, "[EmuState]\n#The Emulation State. 1 is worst, 5 is best, 0 is not set.\n"); - fprintf(f, "[OnFrame]\n#Add memory patches to be applied every frame here.\n"); - fprintf(f, "[ActionReplay]\n#Add action replay cheats here.\n"); - fclose(f); - if (GameIni.Load(GameIniFile.c_str())) - LoadGameConfig(); - else - wxMessageBox(wxString::Format(_("Could not create %s"), GameIniFile.c_str()), _("Error"), wxOK|wxICON_ERROR, this); - } - - // Disk header and apploader - m_Name->SetValue(wxString(OpenISO->GetName().c_str(), wxConvUTF8)); - m_GameID->SetValue(wxString(OpenISO->GetUniqueID().c_str(), wxConvUTF8)); - switch (OpenISO->GetCountry()) - { - case DiscIO::IVolume::COUNTRY_EUROPE: - case DiscIO::IVolume::COUNTRY_FRANCE: - m_Country->SetValue(wxString::FromAscii("EUR")); - break; - case DiscIO::IVolume::COUNTRY_USA: - m_Country->SetValue(wxString::FromAscii("USA")); - break; - case DiscIO::IVolume::COUNTRY_JAP: - m_Country->SetValue(wxString::FromAscii("JAP")); - break; - default: - m_Country->SetValue(wxString::FromAscii("UNKNOWN")); - break; - } - wxString temp; - temp = _T("0x") + wxString::FromAscii(OpenISO->GetMakerID().c_str()); - m_MakerID->SetValue(temp); - m_Date->SetValue(wxString(OpenISO->GetApploaderDate().c_str(), wxConvUTF8)); - m_FST->SetValue(wxString::Format(_T("%u"), OpenISO->GetFSTSize())); - - // Banner - // ...all the BannerLoader functions are bool...gross - //m_Version; - //if (OpenISO_.GetBNRVersion() == "BNR1") - m_Lang->Enable(false); - m_ShortName->SetValue(wxString(OpenISO_.GetName().c_str(), wxConvUTF8)); - //m_LongName->SetValue(wxString(OpenISO_.GetLongName().c_str(), wxConvUTF8)); - m_Maker->SetValue(wxString(OpenISO_.GetCompany().c_str(), wxConvUTF8));//dev too - m_Comment->SetValue(wxString(OpenISO_.GetDescription().c_str(), wxConvUTF8)); - m_Banner->SetBitmap(OpenISO_.GetImage()); - m_Banner->Connect(wxID_ANY, wxEVT_RIGHT_DOWN, - wxMouseEventHandler(CISOProperties::RightClickOnBanner), (wxObject*)NULL, this); - - // Filesystem browser/dumper - fileIter beginning = Our_Files.begin(), end = Our_Files.end(), pos = Our_Files.begin(); - CreateDirectoryTree(RootId, beginning, end, pos, (char *)"\\"); - m_Treectrl->Expand(RootId); - - std::string filename, extension; - SplitPath(fileName, 0, &filename, &extension); - SetTitle(wxString::Format(_("%s%s: %s - %s"), filename.c_str(), extension.c_str(), OpenISO_.GetUniqueID().c_str(), OpenISO_.GetName().c_str())); -} - -CISOProperties::~CISOProperties() -{ - if (pFileSystem) - delete pFileSystem; - if (OpenISO) - delete OpenISO; -} - -void CISOProperties::CreateDirectoryTree(wxTreeItemId& parent, - fileIter& begin, - fileIter& end, - fileIter& iterPos, - char *directory) -{ - bool bRoot = true; - - if(iterPos == begin) - ++iterPos; - else - bRoot = false; - - char *name = (char *)((*iterPos)->m_FullPath); - - if(iterPos == end) - return; - - do - { - if((*iterPos)->IsDirectory()) { - char *dirName; - name[strlen(name) - 1] = '\0'; - dirName = strrchr(name, '\\'); - if(!dirName) - dirName = name; - else - dirName++; - - wxTreeItemId item = m_Treectrl->AppendItem(parent, wxString::FromAscii(dirName)); - CreateDirectoryTree(item, begin, end, ++iterPos, name); - } else { - char *fileName = strrchr(name, '\\'); - if(!fileName) - fileName = name; - else - fileName++; - - m_Treectrl->AppendItem(parent, wxString::FromAscii(fileName)); - ++iterPos; - } - - if(iterPos == end) - break; - - name = (char *)((*iterPos)->m_FullPath); - - } while(bRoot || strstr(name, directory)); -} - -void CISOProperties::CreateGUIControls() -{ - m_Close = new wxButton(this, ID_CLOSE, _("Close"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - - // Notebook - m_Notebook = new wxNotebook(this, ID_NOTEBOOK, wxDefaultPosition, wxDefaultSize); - m_GameConfig = new wxPanel(m_Notebook, ID_GAMECONFIG, wxDefaultPosition, wxDefaultSize); - m_Notebook->AddPage(m_GameConfig, _("GameConfig")); - m_GameConfig_Notebook = new wxNotebook(m_GameConfig, ID_GAMECONFIG_NOTEBOOK, wxDefaultPosition, wxDefaultSize); - m_PatchPage = new wxPanel(m_GameConfig_Notebook, ID_PATCH_PAGE, wxDefaultPosition, wxDefaultSize); - m_GameConfig_Notebook->AddPage(m_PatchPage, _("Patches")); - m_CheatPage = new wxPanel(m_GameConfig_Notebook, ID_ARCODE_PAGE, wxDefaultPosition, wxDefaultSize); - m_GameConfig_Notebook->AddPage(m_CheatPage, _("AR Codes")); - m_Information = new wxPanel(m_Notebook, ID_INFORMATION, wxDefaultPosition, wxDefaultSize); - m_Notebook->AddPage(m_Information, _("Info")); - m_Filesystem = new wxPanel(m_Notebook, ID_FILESYSTEM, wxDefaultPosition, wxDefaultSize); - m_Notebook->AddPage(m_Filesystem, _("Filesystem")); - - wxBoxSizer* sButtons; - sButtons = new wxBoxSizer(wxHORIZONTAL); - sButtons->Add(0, 0, 1, wxEXPAND, 5); - sButtons->Add(m_Close, 0, wxALL, 5); - - wxBoxSizer* sMain; - sMain = new wxBoxSizer(wxVERTICAL); - sMain->Add(m_Notebook, 1, wxEXPAND|wxALL, 5); - sMain->Add(sButtons, 0, wxEXPAND, 5); - - this->SetSizer(sMain); - this->Layout(); - - // GameConfig editing - Core overrides and emulation state - sbCoreOverrides = new wxStaticBoxSizer(wxVERTICAL, m_GameConfig, _("Game-Specific Settings")); - sCoreOverrides = new wxBoxSizer(wxVERTICAL); - EditConfig = new wxButton(m_GameConfig, ID_EDITCONFIG, _("Edit Config"), wxDefaultPosition, wxDefaultSize); - OverrideText = new wxStaticText(m_GameConfig, ID_OVERRIDE_TEXT, _("These settings override core Dolphin settings.\nThe 3rd state means the game uses Dolphin's setting."), wxDefaultPosition, wxDefaultSize); - UseDualCore = 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); - OptimizeQuantizers = new wxCheckBox(m_GameConfig, ID_OPTIMIZEQUANTIZERS, _("Optimize Quantizers"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator); - EnableProgressiveScan = new wxCheckBox(m_GameConfig, ID_ENABLEPROGRESSIVESCAN, _("[Wii] Enable Progressive Scan"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator); - EnableWideScreen = new wxCheckBox(m_GameConfig, ID_ENABLEWIDESCREEN, _("[Wii] Enable WideScreen"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator); - - sEmuState = new wxBoxSizer(wxHORIZONTAL); - arrayStringFor_EmuState.Add(_("Not Set")); - arrayStringFor_EmuState.Add(_("Broken")); - arrayStringFor_EmuState.Add(_("Problems: Other")); - arrayStringFor_EmuState.Add(_("Intro")); - arrayStringFor_EmuState.Add(_("In Game")); - arrayStringFor_EmuState.Add(_("Perfect")); - EmuStateText = new wxStaticText(m_GameConfig, ID_EMUSTATE_TEXT, _("Emulation State: "), wxDefaultPosition, wxDefaultSize); - EmuState = new wxChoice(m_GameConfig, ID_EMUSTATE, wxDefaultPosition, wxDefaultSize, arrayStringFor_EmuState, 0, wxDefaultValidator); - - // Patches - sbPatches = new wxStaticBoxSizer(wxVERTICAL, m_PatchPage, _("Patches")); - sPatches = new wxBoxSizer(wxVERTICAL); - Patches = new wxCheckListBox(m_PatchPage, ID_PATCHES_LIST, wxDefaultPosition, wxDefaultSize, arrayStringFor_Patches, wxLB_HSCROLL, wxDefaultValidator); - sPatchButtons = new wxBoxSizer(wxHORIZONTAL); - EditPatch = new wxButton(m_PatchPage, ID_EDITPATCH, _("Edit..."), wxDefaultPosition, wxDefaultSize, 0); - AddPatch = new wxButton(m_PatchPage, ID_ADDPATCH, _("Add..."), wxDefaultPosition, wxDefaultSize, 0); - RemovePatch = new wxButton(m_PatchPage, ID_REMOVEPATCH, _("Remove"), wxDefaultPosition, wxDefaultSize, 0); - EditPatch->Enable(false); - RemovePatch->Enable(false); - - // Action Replay Cheats - sbCheats = new wxStaticBoxSizer(wxVERTICAL, m_CheatPage, _("Action Replay Codes")); - sCheats = new wxBoxSizer(wxVERTICAL); - Cheats = new wxCheckListBox(m_CheatPage, ID_CHEATS_LIST, wxDefaultPosition, wxDefaultSize, arrayStringFor_Cheats, wxLB_HSCROLL, wxDefaultValidator); - sCheatButtons = new wxBoxSizer(wxHORIZONTAL); - EditCheat = new wxButton(m_CheatPage, ID_EDITCHEAT, _("Edit..."), wxDefaultPosition, wxDefaultSize, 0); - AddCheat = new wxButton(m_CheatPage, ID_ADDCHEAT, _("Add..."), wxDefaultPosition, wxDefaultSize, 0); - RemoveCheat = new wxButton(m_CheatPage, ID_REMOVECHEAT, _("Remove"), wxDefaultPosition, wxDefaultSize, 0); - EditCheat->Enable(false); - RemoveCheat->Enable(false); - - wxBoxSizer* sConfigPage; - sConfigPage = new wxBoxSizer(wxVERTICAL); - sCoreOverrides->Add(OverrideText, 0, wxEXPAND|wxALL, 5); - sCoreOverrides->Add(UseDualCore, 0, wxEXPAND|wxLEFT, 5); - sCoreOverrides->Add(SkipIdle, 0, wxEXPAND|wxLEFT, 5); - sCoreOverrides->Add(OptimizeQuantizers, 0, wxEXPAND|wxLEFT, 5); - sCoreOverrides->Add(EnableProgressiveScan, 0, wxEXPAND|wxLEFT, 5); - sCoreOverrides->Add(EnableWideScreen, 0, wxEXPAND|wxLEFT, 5); - sEmuState->Add(EditConfig, 0, wxALL, 0); - sEmuState->AddStretchSpacer(); - sEmuState->Add(EmuStateText, 0, wxALIGN_CENTER_VERTICAL|wxALL, 0); - sEmuState->Add(EmuState, 0, wxEXPAND|wxALL, 0); - sCoreOverrides->Add(sEmuState, 0, wxEXPAND|wxALL, 5); - sbCoreOverrides->Add(sCoreOverrides, 0, wxEXPAND|wxALL, 0); - sConfigPage->Add(sbCoreOverrides, 0, wxEXPAND|wxALL, 5); - - wxBoxSizer* sPatchPage; - sPatchPage = new wxBoxSizer(wxVERTICAL); - sPatches->Add(Patches, 1, wxEXPAND|wxALL, 0); - sPatchButtons->Add(EditPatch, 0, wxEXPAND|wxALL, 0); - sPatchButtons->AddStretchSpacer(); - sPatchButtons->Add(AddPatch, 0, wxEXPAND|wxALL, 0); - sPatchButtons->Add(RemovePatch, 0, wxEXPAND|wxALL, 0); - sPatches->Add(sPatchButtons, 0, wxEXPAND|wxALL, 0); - sbPatches->Add(sPatches, 1, wxEXPAND|wxALL, 0); - sPatchPage->Add(sbPatches, 1, wxEXPAND|wxALL, 5); - m_PatchPage->SetSizer(sPatchPage); - sPatchPage->Layout(); - - wxBoxSizer* sCheatPage; - sCheatPage = new wxBoxSizer(wxVERTICAL); - sCheats->Add(Cheats, 1, wxEXPAND|wxALL, 0); - sCheatButtons->Add(EditCheat, 0, wxEXPAND|wxALL, 0); - sCheatButtons->AddStretchSpacer(); - sCheatButtons->Add(AddCheat, 0, wxEXPAND|wxALL, 0); - sCheatButtons->Add(RemoveCheat, 0, wxEXPAND|wxALL, 0); - sCheats->Add(sCheatButtons, 0, wxEXPAND|wxALL, 0); - sbCheats->Add(sCheats, 1, wxEXPAND|wxALL, 0); - sCheatPage->Add(sbCheats, 1, wxEXPAND|wxALL, 5); - m_CheatPage->SetSizer(sCheatPage); - sCheatPage->Layout(); - - sConfigPage->Add(m_GameConfig_Notebook, 1, wxEXPAND|wxALL, 5); - - m_GameConfig->SetSizer(sConfigPage); - sConfigPage->Layout(); - - // ISO Details - sbISODetails = new wxStaticBoxSizer(wxVERTICAL, m_Information, _("ISO Details")); - sISODetails = new wxGridBagSizer(0, 0); - sISODetails->AddGrowableCol(1); - m_NameText = new wxStaticText(m_Information, ID_NAME_TEXT, _("Name:"), wxDefaultPosition, wxDefaultSize); - m_Name = new wxTextCtrl(m_Information, ID_NAME, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); - m_GameIDText = new wxStaticText(m_Information, ID_GAMEID_TEXT, _("Game ID:"), wxDefaultPosition, wxDefaultSize); - m_GameID = new wxTextCtrl(m_Information, ID_GAMEID, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); - m_CountryText = new wxStaticText(m_Information, ID_COUNTRY_TEXT, _("Country:"), wxDefaultPosition, wxDefaultSize); - m_Country = new wxTextCtrl(m_Information, ID_COUNTRY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); - m_MakerIDText = new wxStaticText(m_Information, ID_MAKERID_TEXT, _("Maker ID:"), wxDefaultPosition, wxDefaultSize); - m_MakerID = new wxTextCtrl(m_Information, ID_MAKERID, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); - m_DateText = new wxStaticText(m_Information, ID_DATE_TEXT, _("Date:"), wxDefaultPosition, wxDefaultSize); - m_Date = new wxTextCtrl(m_Information, ID_DATE, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); - m_FSTText = new wxStaticText(m_Information, ID_FST_TEXT, _("FST Size:"), wxDefaultPosition, wxDefaultSize); - m_FST = new wxTextCtrl(m_Information, ID_FST, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); - - // Banner Details - sbBannerDetails = new wxStaticBoxSizer(wxVERTICAL, m_Information, _("Banner Details")); - sBannerDetails = new wxGridBagSizer(0, 0); - sBannerDetails->AddGrowableCol(1); sBannerDetails->AddGrowableCol(2); sBannerDetails->AddGrowableCol(3); - m_VersionText = new wxStaticText(m_Information, ID_VERSION_TEXT, _("Version:"), wxDefaultPosition, wxDefaultSize); - m_Version = new wxTextCtrl(m_Information, ID_VERSION, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); - m_LangText = new wxStaticText(m_Information, ID_LANG_TEXT, _("Show Language:"), wxDefaultPosition, wxDefaultSize); - arrayStringFor_Lang.Add(_("English")); - arrayStringFor_Lang.Add(_("German")); - arrayStringFor_Lang.Add(_("French")); - arrayStringFor_Lang.Add(_("Spanish")); - arrayStringFor_Lang.Add(_("Italian")); - arrayStringFor_Lang.Add(_("Dutch")); - m_Lang = new wxChoice(m_Information, ID_LANG, wxDefaultPosition, wxDefaultSize, arrayStringFor_Lang, 0, wxDefaultValidator); - m_Lang->SetSelection(0); - m_ShortText = new wxStaticText(m_Information, ID_SHORTNAME_TEXT, _("Short Name:"), wxDefaultPosition, wxDefaultSize); - m_ShortName = new wxTextCtrl(m_Information, ID_SHORTNAME, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); - m_LongText = new wxStaticText(m_Information, ID_LONGNAME_TEXT, _("Long Name:"), wxDefaultPosition, wxDefaultSize); - m_LongName = new wxTextCtrl(m_Information, ID_LONGNAME, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); - m_MakerText = new wxStaticText(m_Information, ID_MAKER_TEXT, _("Maker:"), wxDefaultPosition, wxDefaultSize); - m_Maker = new wxTextCtrl(m_Information, ID_MAKER, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); - m_CommentText = new wxStaticText(m_Information, ID_COMMENT_TEXT, _("Comment:"), wxDefaultPosition, wxDefaultSize); - m_Comment = new wxTextCtrl(m_Information, ID_COMMENT, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY); - m_BannerText = new wxStaticText(m_Information, ID_BANNER_TEXT, _("Banner:"), wxDefaultPosition, wxDefaultSize); - m_Banner = new wxStaticBitmap(m_Information, ID_BANNER, wxNullBitmap, wxDefaultPosition, wxSize(96, 32), 0); - - wxBoxSizer* sInfoPage; - sInfoPage = new wxBoxSizer(wxVERTICAL); - sISODetails->Add(m_NameText, wxGBPosition(0, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); - sISODetails->Add(m_Name, wxGBPosition(0, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); - sISODetails->Add(m_GameIDText, wxGBPosition(1, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); - sISODetails->Add(m_GameID, wxGBPosition(1, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); - sISODetails->Add(m_CountryText, wxGBPosition(2, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); - sISODetails->Add(m_Country, wxGBPosition(2, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); - sISODetails->Add(m_MakerIDText, wxGBPosition(3, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); - sISODetails->Add(m_MakerID, wxGBPosition(3, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); - sISODetails->Add(m_DateText, wxGBPosition(4, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); - sISODetails->Add(m_Date, wxGBPosition(4, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); - sISODetails->Add(m_FSTText, wxGBPosition(5, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); - sISODetails->Add(m_FST, wxGBPosition(5, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); - sbISODetails->Add(sISODetails, 0, wxEXPAND, 5); - - sBannerDetails->Add(m_VersionText, wxGBPosition(0, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); - sBannerDetails->Add(m_Version, wxGBPosition(0, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); - sBannerDetails->Add(m_LangText, wxGBPosition(0, 2), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); - sBannerDetails->Add(m_Lang, wxGBPosition(0, 3), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); - sBannerDetails->Add(m_ShortText, wxGBPosition(1, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); - sBannerDetails->Add(m_ShortName, wxGBPosition(1, 1), wxGBSpan(1, 3), wxEXPAND|wxALL, 5); - sBannerDetails->Add(m_LongText, wxGBPosition(2, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); - sBannerDetails->Add(m_LongName, wxGBPosition(2, 1), wxGBSpan(1, 3), wxEXPAND|wxALL, 5); - sBannerDetails->Add(m_MakerText, wxGBPosition(3, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); - sBannerDetails->Add(m_Maker, wxGBPosition(3, 1), wxGBSpan(1, 3), wxEXPAND|wxALL, 5); - sBannerDetails->Add(m_CommentText, wxGBPosition(4, 0), wxGBSpan(1, 1), wxALL, 5); - sBannerDetails->Add(m_Comment, wxGBPosition(4, 1), wxGBSpan(1, 3), wxEXPAND|wxALL, 5); - sBannerDetails->Add(m_BannerText, wxGBPosition(5, 0), wxGBSpan(1, 1), wxALL, 5); - sBannerDetails->Add(m_Banner, wxGBPosition(5, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); - sbBannerDetails->Add(sBannerDetails, 0, wxEXPAND, 0); - sInfoPage->Add(sbISODetails, 0, wxEXPAND|wxALL, 5); - sInfoPage->Add(sbBannerDetails, 0, wxEXPAND|wxALL, 5); - m_Information->SetSizer(sInfoPage); - sInfoPage->Layout(); - - // Filesystem tree - sbTreectrl = new wxStaticBoxSizer(wxVERTICAL, m_Filesystem, _("Filesystem")); - m_Treectrl = new wxTreeCtrl(m_Filesystem, ID_TREECTRL, wxDefaultPosition, wxDefaultSize, wxTR_DEFAULT_STYLE, wxDefaultValidator); - - RootId = m_Treectrl->AddRoot(_("Root"), -1, -1, 0); - - wxBoxSizer* sTreePage; - sTreePage = new wxBoxSizer(wxVERTICAL); - sbTreectrl->Add(m_Treectrl, 1, wxEXPAND); - sTreePage->Add(sbTreectrl, 1, wxEXPAND|wxALL, 5); - m_Filesystem->SetSizer(sTreePage); - sTreePage->Layout(); - - Fit(); -} - -void CISOProperties::OnClose(wxCloseEvent& WXUNUSED (event)) -{ - if (!SaveGameConfig()) - wxMessageBox(wxString::Format(_("Could not save %s"), GameIniFile.c_str()), _("Error"), wxOK|wxICON_ERROR, this); - - Destroy(); -} - -void CISOProperties::OnCloseClick(wxCommandEvent& WXUNUSED (event)) -{ - Close(); -} - -void CISOProperties::RightClickOnBanner(wxMouseEvent& event) -{ - wxMenu popupMenu; - popupMenu.Append(IDM_BNRSAVEAS, _("Save as...")); - PopupMenu(&popupMenu); - - event.Skip(); -} - -void CISOProperties::OnBannerImageSave(wxCommandEvent& WXUNUSED (event)) -{ - wxString dirHome; - - wxFileDialog dialog(this, _("Save as..."), wxGetHomeDir(&dirHome), wxString::Format(_("%s.png"), m_GameID->GetLabel().c_str()), - _("*.*"), wxFD_SAVE|wxFD_OVERWRITE_PROMPT, wxDefaultPosition, wxDefaultSize); - if (dialog.ShowModal() == wxID_OK) - { - m_Banner->GetBitmap().ConvertToImage().SaveFile(dialog.GetPath()); - } -} - -void CISOProperties::OnRightClickOnTree(wxTreeEvent& event) -{ - m_Treectrl->SelectItem(event.GetItem()); - - wxMenu popupMenu; - if (m_Treectrl->ItemHasChildren(m_Treectrl->GetSelection())) - ;//popupMenu.Append(IDM_EXTRACTDIR, _("Extract Directory...")); - else - popupMenu.Append(IDM_EXTRACTFILE, _("Extract File...")); - PopupMenu(&popupMenu); - - event.Skip(); -} - -void CISOProperties::OnExtractFile(wxCommandEvent& WXUNUSED (event)) -{ - wxString Path; - wxString File; - - File = m_Treectrl->GetItemText(m_Treectrl->GetSelection()); - - Path = wxFileSelector( - _T("Export File"), - wxEmptyString, File, wxEmptyString, - wxString::Format - ( - _T("All files (%s)|%s"), - wxFileSelectorDefaultWildcardStr, - wxFileSelectorDefaultWildcardStr - ), - wxFD_SAVE, - this); - - if (!Path || !File) - return; - - while (m_Treectrl->GetItemParent(m_Treectrl->GetSelection()) != m_Treectrl->GetRootItem()) - { - wxString temp; - temp = m_Treectrl->GetItemText(m_Treectrl->GetItemParent(m_Treectrl->GetSelection())); - File = temp + _T("\\") + File; - - m_Treectrl->SelectItem(m_Treectrl->GetItemParent(m_Treectrl->GetSelection())); - } - - pFileSystem->ExportFile(File.mb_str(), Path.mb_str()); -} - -void CISOProperties::OnExtractDir(wxCommandEvent& WXUNUSED (event)) -{ -} - -void CISOProperties::SetRefresh(wxCommandEvent& WXUNUSED (event)) -{ - bRefreshList = true; -} - -void CISOProperties::LoadGameConfig() -{ - bool bTemp; - int iTemp; - - if (GameIni.Get("Core", "UseDualCore", &bTemp)) - UseDualCore->Set3StateValue((wxCheckBoxState)bTemp); - else - UseDualCore->Set3StateValue(wxCHK_UNDETERMINED); - - if (GameIni.Get("Core", "SkipIdle", &bTemp)) - SkipIdle->Set3StateValue((wxCheckBoxState)bTemp); - else - SkipIdle->Set3StateValue(wxCHK_UNDETERMINED); - - if (GameIni.Get("Core", "OptimizeQuantizers", &bTemp)) - OptimizeQuantizers->Set3StateValue((wxCheckBoxState)bTemp); - else - OptimizeQuantizers->Set3StateValue(wxCHK_UNDETERMINED); - - if (GameIni.Get("Core", "EnableProgressiveScan", &bTemp)) - EnableProgressiveScan->Set3StateValue((wxCheckBoxState)bTemp); - else - EnableProgressiveScan->Set3StateValue(wxCHK_UNDETERMINED); - - if (GameIni.Get("Core", "EnableWideScreen", &bTemp)) - EnableWideScreen->Set3StateValue((wxCheckBoxState)bTemp); - else - EnableWideScreen->Set3StateValue(wxCHK_UNDETERMINED); - - GameIni.Get("EmuState", "EmulationStateId", &iTemp, -1); - if (iTemp == -1) - { - iTemp = 0; - bRefreshList = true; - } - EmuState->SetSelection(iTemp); - - PatchList_Load(); - ActionReplayList_Load(); -} - -bool CISOProperties::SaveGameConfig() -{ - if (UseDualCore->Get3StateValue() == wxCHK_UNDETERMINED) - GameIni.DeleteKey("Core", "UseDualCore"); - else - GameIni.Set("Core", "UseDualCore", UseDualCore->Get3StateValue()); - - if (SkipIdle->Get3StateValue() == wxCHK_UNDETERMINED) - GameIni.DeleteKey("Core", "SkipIdle"); - else - GameIni.Set("Core", "SkipIdle", SkipIdle->Get3StateValue()); - - if (OptimizeQuantizers->Get3StateValue() == wxCHK_UNDETERMINED) - GameIni.DeleteKey("Core", "OptimizeQuantizers"); - else - GameIni.Set("Core", "OptimizeQuantizers", OptimizeQuantizers->Get3StateValue()); - - if (EnableProgressiveScan->Get3StateValue() == wxCHK_UNDETERMINED) - GameIni.DeleteKey("Core", "EnableProgressiveScan"); - else - GameIni.Set("Core", "EnableProgressiveScan", EnableProgressiveScan->Get3StateValue()); - - if (EnableWideScreen->Get3StateValue() == wxCHK_UNDETERMINED) - GameIni.DeleteKey("Core", "EnableWideScreen"); - else - GameIni.Set("Core", "EnableWideScreen", EnableWideScreen->Get3StateValue()); - - GameIni.Set("EmuState", "EmulationStateId", EmuState->GetSelection()); - - PatchList_Save(); - ActionReplayList_Save(); - - return GameIni.Save(GameIniFile.c_str()); -} - -void CISOProperties::OnEditConfig(wxCommandEvent& WXUNUSED (event)) -{ - if (wxFileExists(wxString::FromAscii(GameIniFile.c_str()))) - { - SaveGameConfig(); - - wxFileType* filetype = wxTheMimeTypesManager->GetFileTypeFromExtension(_("ini")); - wxExecute(filetype->GetOpenCommand(wxString::FromAscii(GameIniFile.c_str())), wxEXEC_SYNC); - - GameIni.Load(GameIniFile.c_str()); - LoadGameConfig(); - - bRefreshList = true; // Just in case - } -} - -void CISOProperties::ListSelectionChanged(wxCommandEvent& event) -{ - switch (event.GetId()) - { - case ID_PATCHES_LIST: - if (Patches->GetSelection() != wxNOT_FOUND) - { - EditPatch->Enable(); - RemovePatch->Enable(); - } - break; - case ID_CHEATS_LIST: - if (Cheats->GetSelection() != wxNOT_FOUND) - { - EditCheat->Enable(); - RemoveCheat->Enable(); - } - break; - } -} - -void CISOProperties::PatchList_Load() -{ - onFrame.clear(); - Patches->Clear(); - PatchEngine::LoadPatchSection("OnFrame", onFrame, GameIni); - - u32 index = 0; - for (std::vector::const_iterator it = onFrame.begin(); it != onFrame.end(); ++it) - { - PatchEngine::Patch p = *it; - Patches->Append(wxString::FromAscii(p.name.c_str())); - Patches->Check(index, p.active); - ++index; - } -} - -void CISOProperties::PatchList_Save() -{ - std::vector lines; - u32 index = 0; - for (std::vector::const_iterator onFrame_it = onFrame.begin(); onFrame_it != onFrame.end(); ++onFrame_it) - { - lines.push_back(Patches->IsChecked(index) ? "+$" + onFrame_it->name : "$" + onFrame_it->name); - - for (std::vector::const_iterator iter2 = onFrame_it->entries.begin(); iter2 != onFrame_it->entries.end(); ++iter2) - { - std::string temp; - ToStringFromFormat(&temp, "0x%08X:%s:0x%08X", iter2->address, PatchEngine::PatchTypeStrings[iter2->type], iter2->value); - lines.push_back(temp); - } - ++index; - } - GameIni.SetLines("OnFrame", lines); - lines.clear(); -} - -void CISOProperties::PatchButtonClicked(wxCommandEvent& event) -{ - int selection = Patches->GetSelection(); - - switch (event.GetId()) - { - case ID_EDITPATCH: - { - CPatchAddEdit dlg(selection, this); - dlg.ShowModal(); - } - break; - case ID_ADDPATCH: - { - // dialog; - } - break; - case ID_REMOVEPATCH: - onFrame.erase(onFrame.begin() + Patches->GetSelection()); - break; - } - - PatchList_Save(); - Patches->Clear(); - PatchList_Load(); - - EditPatch->Enable(false); - RemovePatch->Enable(false); -} - -void CISOProperties::ActionReplayList_Load() -{ - ARCodes.clear(); - Cheats->Clear(); - std::vector lines; - - if (!GameIni.GetLines("ActionReplay", lines)) - return; - - ARListCode code; - - for (std::vector::const_iterator it = lines.begin(); it != lines.end(); ++it) - { - std::string line = *it; - - if (line[0] == '+' || line[0] == '$') - { - // Take care of the previous code - if (code.ops.size()) - { - code.uiIndex = Cheats->Append(wxString::FromAscii(code.name.c_str())); - ARCodes.push_back(code); - Cheats->Check(code.uiIndex, code.enabled); - code.ops.clear(); - } - - // Give name and enabled to current code - if(line.size() > 1) - { - if (line[0] == '+') - { - code.enabled = true; - code.name = line.substr(2, line.size() - 2); - } - else - { - code.enabled = false; - code.name = line.substr(1, line.size() - 1); - } - } - continue; - } - code.ops.push_back(line); - } - - if (code.ops.size()) - { - code.uiIndex = Cheats->Append(wxString::FromAscii(code.name.c_str())); - ARCodes.push_back(code); - Cheats->Check(code.uiIndex, code.enabled); - } -} - -void CISOProperties::ActionReplayList_Save() -{ - std::vector lines; - for (std::vector::const_iterator iter = ARCodes.begin(); iter != ARCodes.end(); ++iter) - { - ARListCode code = *iter; - - lines.push_back(Cheats->IsChecked(code.uiIndex) ? "+$" + code.name : "$" + code.name); - - for (std::vector::const_iterator iter2 = code.ops.begin(); iter2 != code.ops.end(); ++iter2) - { - lines.push_back(*iter2); - } - } - GameIni.SetLines("ActionReplay", lines); -} - -void CISOProperties::ActionReplayButtonClicked(wxCommandEvent& event) -{ - switch (event.GetId()) - { - case ID_EDITCHEAT: - // dialog - break; - case ID_ADDCHEAT: - // dialog - break; - case ID_REMOVECHEAT: - ARCodes.erase(ARCodes.begin() + Cheats->GetSelection()); - break; - } - - ActionReplayList_Save(); - Cheats->Clear(); - ActionReplayList_Load(); - - EditCheat->Enable(false); - RemoveCheat->Enable(false); -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Globals.h" + +#include "ISOFile.h" +#include "VolumeCreator.h" +#include "Filesystem.h" +#include "ISOProperties.h" +#include "PatchAddEdit.h" + + +DiscIO::IVolume *OpenISO = NULL; +DiscIO::IFileSystem *pFileSystem = NULL; + +std::vector onFrame; + +struct ARListCode { + std::string name; + bool enabled; + std::vector ops; + u32 uiIndex; +}; +std::vector ARCodes; + + +BEGIN_EVENT_TABLE(CISOProperties, wxDialog) + EVT_CLOSE(CISOProperties::OnClose) + EVT_BUTTON(ID_CLOSE, CISOProperties::OnCloseClick) + EVT_BUTTON(ID_EDITCONFIG, CISOProperties::OnEditConfig) + EVT_CHOICE(ID_EMUSTATE, CISOProperties::SetRefresh) + EVT_LISTBOX(ID_PATCHES_LIST, CISOProperties::ListSelectionChanged) + EVT_BUTTON(ID_EDITPATCH, CISOProperties::PatchButtonClicked) + EVT_BUTTON(ID_ADDPATCH, CISOProperties::PatchButtonClicked) + EVT_BUTTON(ID_REMOVEPATCH, CISOProperties::PatchButtonClicked) + EVT_LISTBOX(ID_CHEATS_LIST, CISOProperties::ListSelectionChanged) + EVT_BUTTON(ID_EDITCHEAT, CISOProperties::ActionReplayButtonClicked) + EVT_BUTTON(ID_ADDCHEAT, CISOProperties::ActionReplayButtonClicked) + EVT_BUTTON(ID_REMOVECHEAT, CISOProperties::ActionReplayButtonClicked) + EVT_MENU(IDM_BNRSAVEAS, CISOProperties::OnBannerImageSave) + EVT_TREE_ITEM_RIGHT_CLICK(ID_TREECTRL, CISOProperties::OnRightClickOnTree) + EVT_MENU(IDM_EXTRACTFILE, CISOProperties::OnExtractFile) + EVT_MENU(IDM_EXTRACTDIR, CISOProperties::OnExtractDir) +END_EVENT_TABLE() + +CISOProperties::CISOProperties(const std::string fileName, wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& position, const wxSize& size, long style) + : wxDialog(parent, id, title, position, size, style) +{ + OpenISO = DiscIO::CreateVolumeFromFilename(fileName); + pFileSystem = DiscIO::CreateFileSystem(OpenISO); + + pFileSystem->GetFileList(Our_Files); + + GameListItem OpenISO_(fileName); + + bRefreshList = false; + CreateGUIControls(); + + GameIniFile = FULL_GAMECONFIG_DIR + (OpenISO->GetUniqueID()) + ".ini"; + if (GameIni.Load(GameIniFile.c_str())) + LoadGameConfig(); + else + { + FILE *f = fopen(GameIniFile.c_str(), "w"); + fprintf(f, "# %s - %s\n", OpenISO->GetUniqueID().c_str(), OpenISO->GetName().c_str()); + fprintf(f, "[Core]\n#Values set here will override the main dolphin settings.\n"); + fprintf(f, "[EmuState]\n#The Emulation State. 1 is worst, 5 is best, 0 is not set.\n"); + fprintf(f, "[OnFrame]\n#Add memory patches to be applied every frame here.\n"); + fprintf(f, "[ActionReplay]\n#Add action replay cheats here.\n"); + fclose(f); + if (GameIni.Load(GameIniFile.c_str())) + LoadGameConfig(); + else + wxMessageBox(wxString::Format(_("Could not create %s"), GameIniFile.c_str()), _("Error"), wxOK|wxICON_ERROR, this); + } + + // Disk header and apploader + m_Name->SetValue(wxString(OpenISO->GetName().c_str(), wxConvUTF8)); + m_GameID->SetValue(wxString(OpenISO->GetUniqueID().c_str(), wxConvUTF8)); + switch (OpenISO->GetCountry()) + { + case DiscIO::IVolume::COUNTRY_EUROPE: + case DiscIO::IVolume::COUNTRY_FRANCE: + m_Country->SetValue(wxString::FromAscii("EUR")); + break; + case DiscIO::IVolume::COUNTRY_USA: + m_Country->SetValue(wxString::FromAscii("USA")); + break; + case DiscIO::IVolume::COUNTRY_JAP: + m_Country->SetValue(wxString::FromAscii("JAP")); + break; + default: + m_Country->SetValue(wxString::FromAscii("UNKNOWN")); + break; + } + wxString temp; + temp = _T("0x") + wxString::FromAscii(OpenISO->GetMakerID().c_str()); + m_MakerID->SetValue(temp); + m_Date->SetValue(wxString(OpenISO->GetApploaderDate().c_str(), wxConvUTF8)); + m_FST->SetValue(wxString::Format(_T("%u"), OpenISO->GetFSTSize())); + + // Banner + // ...all the BannerLoader functions are bool...gross + //m_Version; + //if (OpenISO_.GetBNRVersion() == "BNR1") + m_Lang->Enable(false); + m_ShortName->SetValue(wxString(OpenISO_.GetName().c_str(), wxConvUTF8)); + //m_LongName->SetValue(wxString(OpenISO_.GetLongName().c_str(), wxConvUTF8)); + m_Maker->SetValue(wxString(OpenISO_.GetCompany().c_str(), wxConvUTF8));//dev too + m_Comment->SetValue(wxString(OpenISO_.GetDescription().c_str(), wxConvUTF8)); + m_Banner->SetBitmap(OpenISO_.GetImage()); + m_Banner->Connect(wxID_ANY, wxEVT_RIGHT_DOWN, + wxMouseEventHandler(CISOProperties::RightClickOnBanner), (wxObject*)NULL, this); + + // Filesystem browser/dumper + fileIter beginning = Our_Files.begin(), end = Our_Files.end(), pos = Our_Files.begin(); + CreateDirectoryTree(RootId, beginning, end, pos, (char *)"\\"); + m_Treectrl->Expand(RootId); + + std::string filename, extension; + SplitPath(fileName, 0, &filename, &extension); + SetTitle(wxString::Format(_("%s%s: %s - %s"), filename.c_str(), extension.c_str(), OpenISO_.GetUniqueID().c_str(), OpenISO_.GetName().c_str())); +} + +CISOProperties::~CISOProperties() +{ + if (pFileSystem) + delete pFileSystem; + if (OpenISO) + delete OpenISO; +} + +void CISOProperties::CreateDirectoryTree(wxTreeItemId& parent, + fileIter& begin, + fileIter& end, + fileIter& iterPos, + char *directory) +{ + bool bRoot = true; + + if(iterPos == begin) + ++iterPos; + else + bRoot = false; + + char *name = (char *)((*iterPos)->m_FullPath); + + if(iterPos == end) + return; + + do + { + if((*iterPos)->IsDirectory()) { + char *dirName; + name[strlen(name) - 1] = '\0'; + dirName = strrchr(name, '\\'); + if(!dirName) + dirName = name; + else + dirName++; + + wxTreeItemId item = m_Treectrl->AppendItem(parent, wxString::FromAscii(dirName)); + CreateDirectoryTree(item, begin, end, ++iterPos, name); + } else { + char *fileName = strrchr(name, '\\'); + if(!fileName) + fileName = name; + else + fileName++; + + m_Treectrl->AppendItem(parent, wxString::FromAscii(fileName)); + ++iterPos; + } + + if(iterPos == end) + break; + + name = (char *)((*iterPos)->m_FullPath); + + } while(bRoot || strstr(name, directory)); +} + +void CISOProperties::CreateGUIControls() +{ + m_Close = new wxButton(this, ID_CLOSE, _("Close"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + + // Notebook + m_Notebook = new wxNotebook(this, ID_NOTEBOOK, wxDefaultPosition, wxDefaultSize); + m_GameConfig = new wxPanel(m_Notebook, ID_GAMECONFIG, wxDefaultPosition, wxDefaultSize); + m_Notebook->AddPage(m_GameConfig, _("GameConfig")); + m_GameConfig_Notebook = new wxNotebook(m_GameConfig, ID_GAMECONFIG_NOTEBOOK, wxDefaultPosition, wxDefaultSize); + m_PatchPage = new wxPanel(m_GameConfig_Notebook, ID_PATCH_PAGE, wxDefaultPosition, wxDefaultSize); + m_GameConfig_Notebook->AddPage(m_PatchPage, _("Patches")); + m_CheatPage = new wxPanel(m_GameConfig_Notebook, ID_ARCODE_PAGE, wxDefaultPosition, wxDefaultSize); + m_GameConfig_Notebook->AddPage(m_CheatPage, _("AR Codes")); + m_Information = new wxPanel(m_Notebook, ID_INFORMATION, wxDefaultPosition, wxDefaultSize); + m_Notebook->AddPage(m_Information, _("Info")); + m_Filesystem = new wxPanel(m_Notebook, ID_FILESYSTEM, wxDefaultPosition, wxDefaultSize); + m_Notebook->AddPage(m_Filesystem, _("Filesystem")); + + wxBoxSizer* sButtons; + sButtons = new wxBoxSizer(wxHORIZONTAL); + sButtons->Add(0, 0, 1, wxEXPAND, 5); + sButtons->Add(m_Close, 0, wxALL, 5); + + wxBoxSizer* sMain; + sMain = new wxBoxSizer(wxVERTICAL); + sMain->Add(m_Notebook, 1, wxEXPAND|wxALL, 5); + sMain->Add(sButtons, 0, wxEXPAND, 5); + + this->SetSizer(sMain); + this->Layout(); + + // GameConfig editing - Core overrides and emulation state + sbCoreOverrides = new wxStaticBoxSizer(wxVERTICAL, m_GameConfig, _("Game-Specific Settings")); + sCoreOverrides = new wxBoxSizer(wxVERTICAL); + EditConfig = new wxButton(m_GameConfig, ID_EDITCONFIG, _("Edit Config"), wxDefaultPosition, wxDefaultSize); + OverrideText = new wxStaticText(m_GameConfig, ID_OVERRIDE_TEXT, _("These settings override core Dolphin settings.\nThe 3rd state means the game uses Dolphin's setting."), wxDefaultPosition, wxDefaultSize); + UseDualCore = 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); + OptimizeQuantizers = new wxCheckBox(m_GameConfig, ID_OPTIMIZEQUANTIZERS, _("Optimize Quantizers"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator); + EnableProgressiveScan = new wxCheckBox(m_GameConfig, ID_ENABLEPROGRESSIVESCAN, _("[Wii] Enable Progressive Scan"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator); + EnableWideScreen = new wxCheckBox(m_GameConfig, ID_ENABLEWIDESCREEN, _("[Wii] Enable WideScreen"), wxDefaultPosition, wxDefaultSize, wxCHK_3STATE|wxCHK_ALLOW_3RD_STATE_FOR_USER, wxDefaultValidator); + + sEmuState = new wxBoxSizer(wxHORIZONTAL); + arrayStringFor_EmuState.Add(_("Not Set")); + arrayStringFor_EmuState.Add(_("Broken")); + arrayStringFor_EmuState.Add(_("Problems: Other")); + arrayStringFor_EmuState.Add(_("Intro")); + arrayStringFor_EmuState.Add(_("In Game")); + arrayStringFor_EmuState.Add(_("Perfect")); + EmuStateText = new wxStaticText(m_GameConfig, ID_EMUSTATE_TEXT, _("Emulation State: "), wxDefaultPosition, wxDefaultSize); + EmuState = new wxChoice(m_GameConfig, ID_EMUSTATE, wxDefaultPosition, wxDefaultSize, arrayStringFor_EmuState, 0, wxDefaultValidator); + + // Patches + sbPatches = new wxStaticBoxSizer(wxVERTICAL, m_PatchPage, _("Patches")); + sPatches = new wxBoxSizer(wxVERTICAL); + Patches = new wxCheckListBox(m_PatchPage, ID_PATCHES_LIST, wxDefaultPosition, wxDefaultSize, arrayStringFor_Patches, wxLB_HSCROLL, wxDefaultValidator); + sPatchButtons = new wxBoxSizer(wxHORIZONTAL); + EditPatch = new wxButton(m_PatchPage, ID_EDITPATCH, _("Edit..."), wxDefaultPosition, wxDefaultSize, 0); + AddPatch = new wxButton(m_PatchPage, ID_ADDPATCH, _("Add..."), wxDefaultPosition, wxDefaultSize, 0); + RemovePatch = new wxButton(m_PatchPage, ID_REMOVEPATCH, _("Remove"), wxDefaultPosition, wxDefaultSize, 0); + EditPatch->Enable(false); + RemovePatch->Enable(false); + + // Action Replay Cheats + sbCheats = new wxStaticBoxSizer(wxVERTICAL, m_CheatPage, _("Action Replay Codes")); + sCheats = new wxBoxSizer(wxVERTICAL); + Cheats = new wxCheckListBox(m_CheatPage, ID_CHEATS_LIST, wxDefaultPosition, wxDefaultSize, arrayStringFor_Cheats, wxLB_HSCROLL, wxDefaultValidator); + sCheatButtons = new wxBoxSizer(wxHORIZONTAL); + EditCheat = new wxButton(m_CheatPage, ID_EDITCHEAT, _("Edit..."), wxDefaultPosition, wxDefaultSize, 0); + AddCheat = new wxButton(m_CheatPage, ID_ADDCHEAT, _("Add..."), wxDefaultPosition, wxDefaultSize, 0); + RemoveCheat = new wxButton(m_CheatPage, ID_REMOVECHEAT, _("Remove"), wxDefaultPosition, wxDefaultSize, 0); + EditCheat->Enable(false); + RemoveCheat->Enable(false); + + wxBoxSizer* sConfigPage; + sConfigPage = new wxBoxSizer(wxVERTICAL); + sCoreOverrides->Add(OverrideText, 0, wxEXPAND|wxALL, 5); + sCoreOverrides->Add(UseDualCore, 0, wxEXPAND|wxLEFT, 5); + sCoreOverrides->Add(SkipIdle, 0, wxEXPAND|wxLEFT, 5); + sCoreOverrides->Add(OptimizeQuantizers, 0, wxEXPAND|wxLEFT, 5); + sCoreOverrides->Add(EnableProgressiveScan, 0, wxEXPAND|wxLEFT, 5); + sCoreOverrides->Add(EnableWideScreen, 0, wxEXPAND|wxLEFT, 5); + sEmuState->Add(EditConfig, 0, wxALL, 0); + sEmuState->AddStretchSpacer(); + sEmuState->Add(EmuStateText, 0, wxALIGN_CENTER_VERTICAL|wxALL, 0); + sEmuState->Add(EmuState, 0, wxEXPAND|wxALL, 0); + sCoreOverrides->Add(sEmuState, 0, wxEXPAND|wxALL, 5); + sbCoreOverrides->Add(sCoreOverrides, 0, wxEXPAND|wxALL, 0); + sConfigPage->Add(sbCoreOverrides, 0, wxEXPAND|wxALL, 5); + + wxBoxSizer* sPatchPage; + sPatchPage = new wxBoxSizer(wxVERTICAL); + sPatches->Add(Patches, 1, wxEXPAND|wxALL, 0); + sPatchButtons->Add(EditPatch, 0, wxEXPAND|wxALL, 0); + sPatchButtons->AddStretchSpacer(); + sPatchButtons->Add(AddPatch, 0, wxEXPAND|wxALL, 0); + sPatchButtons->Add(RemovePatch, 0, wxEXPAND|wxALL, 0); + sPatches->Add(sPatchButtons, 0, wxEXPAND|wxALL, 0); + sbPatches->Add(sPatches, 1, wxEXPAND|wxALL, 0); + sPatchPage->Add(sbPatches, 1, wxEXPAND|wxALL, 5); + m_PatchPage->SetSizer(sPatchPage); + sPatchPage->Layout(); + + wxBoxSizer* sCheatPage; + sCheatPage = new wxBoxSizer(wxVERTICAL); + sCheats->Add(Cheats, 1, wxEXPAND|wxALL, 0); + sCheatButtons->Add(EditCheat, 0, wxEXPAND|wxALL, 0); + sCheatButtons->AddStretchSpacer(); + sCheatButtons->Add(AddCheat, 0, wxEXPAND|wxALL, 0); + sCheatButtons->Add(RemoveCheat, 0, wxEXPAND|wxALL, 0); + sCheats->Add(sCheatButtons, 0, wxEXPAND|wxALL, 0); + sbCheats->Add(sCheats, 1, wxEXPAND|wxALL, 0); + sCheatPage->Add(sbCheats, 1, wxEXPAND|wxALL, 5); + m_CheatPage->SetSizer(sCheatPage); + sCheatPage->Layout(); + + sConfigPage->Add(m_GameConfig_Notebook, 1, wxEXPAND|wxALL, 5); + + m_GameConfig->SetSizer(sConfigPage); + sConfigPage->Layout(); + + // ISO Details + sbISODetails = new wxStaticBoxSizer(wxVERTICAL, m_Information, _("ISO Details")); + sISODetails = new wxGridBagSizer(0, 0); + sISODetails->AddGrowableCol(1); + m_NameText = new wxStaticText(m_Information, ID_NAME_TEXT, _("Name:"), wxDefaultPosition, wxDefaultSize); + m_Name = new wxTextCtrl(m_Information, ID_NAME, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); + m_GameIDText = new wxStaticText(m_Information, ID_GAMEID_TEXT, _("Game ID:"), wxDefaultPosition, wxDefaultSize); + m_GameID = new wxTextCtrl(m_Information, ID_GAMEID, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); + m_CountryText = new wxStaticText(m_Information, ID_COUNTRY_TEXT, _("Country:"), wxDefaultPosition, wxDefaultSize); + m_Country = new wxTextCtrl(m_Information, ID_COUNTRY, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); + m_MakerIDText = new wxStaticText(m_Information, ID_MAKERID_TEXT, _("Maker ID:"), wxDefaultPosition, wxDefaultSize); + m_MakerID = new wxTextCtrl(m_Information, ID_MAKERID, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); + m_DateText = new wxStaticText(m_Information, ID_DATE_TEXT, _("Date:"), wxDefaultPosition, wxDefaultSize); + m_Date = new wxTextCtrl(m_Information, ID_DATE, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); + m_FSTText = new wxStaticText(m_Information, ID_FST_TEXT, _("FST Size:"), wxDefaultPosition, wxDefaultSize); + m_FST = new wxTextCtrl(m_Information, ID_FST, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); + + // Banner Details + sbBannerDetails = new wxStaticBoxSizer(wxVERTICAL, m_Information, _("Banner Details")); + sBannerDetails = new wxGridBagSizer(0, 0); + sBannerDetails->AddGrowableCol(1); sBannerDetails->AddGrowableCol(2); sBannerDetails->AddGrowableCol(3); + m_VersionText = new wxStaticText(m_Information, ID_VERSION_TEXT, _("Version:"), wxDefaultPosition, wxDefaultSize); + m_Version = new wxTextCtrl(m_Information, ID_VERSION, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); + m_LangText = new wxStaticText(m_Information, ID_LANG_TEXT, _("Show Language:"), wxDefaultPosition, wxDefaultSize); + arrayStringFor_Lang.Add(_("English")); + arrayStringFor_Lang.Add(_("German")); + arrayStringFor_Lang.Add(_("French")); + arrayStringFor_Lang.Add(_("Spanish")); + arrayStringFor_Lang.Add(_("Italian")); + arrayStringFor_Lang.Add(_("Dutch")); + m_Lang = new wxChoice(m_Information, ID_LANG, wxDefaultPosition, wxDefaultSize, arrayStringFor_Lang, 0, wxDefaultValidator); + m_Lang->SetSelection(0); + m_ShortText = new wxStaticText(m_Information, ID_SHORTNAME_TEXT, _("Short Name:"), wxDefaultPosition, wxDefaultSize); + m_ShortName = new wxTextCtrl(m_Information, ID_SHORTNAME, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); + m_LongText = new wxStaticText(m_Information, ID_LONGNAME_TEXT, _("Long Name:"), wxDefaultPosition, wxDefaultSize); + m_LongName = new wxTextCtrl(m_Information, ID_LONGNAME, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); + m_MakerText = new wxStaticText(m_Information, ID_MAKER_TEXT, _("Maker:"), wxDefaultPosition, wxDefaultSize); + m_Maker = new wxTextCtrl(m_Information, ID_MAKER, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_READONLY); + m_CommentText = new wxStaticText(m_Information, ID_COMMENT_TEXT, _("Comment:"), wxDefaultPosition, wxDefaultSize); + m_Comment = new wxTextCtrl(m_Information, ID_COMMENT, wxEmptyString, wxDefaultPosition, wxDefaultSize, wxTE_MULTILINE|wxTE_READONLY); + m_BannerText = new wxStaticText(m_Information, ID_BANNER_TEXT, _("Banner:"), wxDefaultPosition, wxDefaultSize); + m_Banner = new wxStaticBitmap(m_Information, ID_BANNER, wxNullBitmap, wxDefaultPosition, wxSize(96, 32), 0); + + wxBoxSizer* sInfoPage; + sInfoPage = new wxBoxSizer(wxVERTICAL); + sISODetails->Add(m_NameText, wxGBPosition(0, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); + sISODetails->Add(m_Name, wxGBPosition(0, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); + sISODetails->Add(m_GameIDText, wxGBPosition(1, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); + sISODetails->Add(m_GameID, wxGBPosition(1, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); + sISODetails->Add(m_CountryText, wxGBPosition(2, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); + sISODetails->Add(m_Country, wxGBPosition(2, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); + sISODetails->Add(m_MakerIDText, wxGBPosition(3, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); + sISODetails->Add(m_MakerID, wxGBPosition(3, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); + sISODetails->Add(m_DateText, wxGBPosition(4, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); + sISODetails->Add(m_Date, wxGBPosition(4, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); + sISODetails->Add(m_FSTText, wxGBPosition(5, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); + sISODetails->Add(m_FST, wxGBPosition(5, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); + sbISODetails->Add(sISODetails, 0, wxEXPAND, 5); + + sBannerDetails->Add(m_VersionText, wxGBPosition(0, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); + sBannerDetails->Add(m_Version, wxGBPosition(0, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); + sBannerDetails->Add(m_LangText, wxGBPosition(0, 2), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); + sBannerDetails->Add(m_Lang, wxGBPosition(0, 3), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); + sBannerDetails->Add(m_ShortText, wxGBPosition(1, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); + sBannerDetails->Add(m_ShortName, wxGBPosition(1, 1), wxGBSpan(1, 3), wxEXPAND|wxALL, 5); + sBannerDetails->Add(m_LongText, wxGBPosition(2, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); + sBannerDetails->Add(m_LongName, wxGBPosition(2, 1), wxGBSpan(1, 3), wxEXPAND|wxALL, 5); + sBannerDetails->Add(m_MakerText, wxGBPosition(3, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); + sBannerDetails->Add(m_Maker, wxGBPosition(3, 1), wxGBSpan(1, 3), wxEXPAND|wxALL, 5); + sBannerDetails->Add(m_CommentText, wxGBPosition(4, 0), wxGBSpan(1, 1), wxALL, 5); + sBannerDetails->Add(m_Comment, wxGBPosition(4, 1), wxGBSpan(1, 3), wxEXPAND|wxALL, 5); + sBannerDetails->Add(m_BannerText, wxGBPosition(5, 0), wxGBSpan(1, 1), wxALL, 5); + sBannerDetails->Add(m_Banner, wxGBPosition(5, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); + sbBannerDetails->Add(sBannerDetails, 0, wxEXPAND, 0); + sInfoPage->Add(sbISODetails, 0, wxEXPAND|wxALL, 5); + sInfoPage->Add(sbBannerDetails, 0, wxEXPAND|wxALL, 5); + m_Information->SetSizer(sInfoPage); + sInfoPage->Layout(); + + // Filesystem tree + sbTreectrl = new wxStaticBoxSizer(wxVERTICAL, m_Filesystem, _("Filesystem")); + m_Treectrl = new wxTreeCtrl(m_Filesystem, ID_TREECTRL, wxDefaultPosition, wxDefaultSize, wxTR_DEFAULT_STYLE, wxDefaultValidator); + + RootId = m_Treectrl->AddRoot(_("Root"), -1, -1, 0); + + wxBoxSizer* sTreePage; + sTreePage = new wxBoxSizer(wxVERTICAL); + sbTreectrl->Add(m_Treectrl, 1, wxEXPAND); + sTreePage->Add(sbTreectrl, 1, wxEXPAND|wxALL, 5); + m_Filesystem->SetSizer(sTreePage); + sTreePage->Layout(); + + Fit(); +} + +void CISOProperties::OnClose(wxCloseEvent& WXUNUSED (event)) +{ + if (!SaveGameConfig()) + wxMessageBox(wxString::Format(_("Could not save %s"), GameIniFile.c_str()), _("Error"), wxOK|wxICON_ERROR, this); + + Destroy(); +} + +void CISOProperties::OnCloseClick(wxCommandEvent& WXUNUSED (event)) +{ + Close(); +} + +void CISOProperties::RightClickOnBanner(wxMouseEvent& event) +{ + wxMenu popupMenu; + popupMenu.Append(IDM_BNRSAVEAS, _("Save as...")); + PopupMenu(&popupMenu); + + event.Skip(); +} + +void CISOProperties::OnBannerImageSave(wxCommandEvent& WXUNUSED (event)) +{ + wxString dirHome; + + wxFileDialog dialog(this, _("Save as..."), wxGetHomeDir(&dirHome), wxString::Format(_("%s.png"), m_GameID->GetLabel().c_str()), + _("*.*"), wxFD_SAVE|wxFD_OVERWRITE_PROMPT, wxDefaultPosition, wxDefaultSize); + if (dialog.ShowModal() == wxID_OK) + { + m_Banner->GetBitmap().ConvertToImage().SaveFile(dialog.GetPath()); + } +} + +void CISOProperties::OnRightClickOnTree(wxTreeEvent& event) +{ + m_Treectrl->SelectItem(event.GetItem()); + + wxMenu popupMenu; + if (m_Treectrl->ItemHasChildren(m_Treectrl->GetSelection())) + ;//popupMenu.Append(IDM_EXTRACTDIR, _("Extract Directory...")); + else + popupMenu.Append(IDM_EXTRACTFILE, _("Extract File...")); + PopupMenu(&popupMenu); + + event.Skip(); +} + +void CISOProperties::OnExtractFile(wxCommandEvent& WXUNUSED (event)) +{ + wxString Path; + wxString File; + + File = m_Treectrl->GetItemText(m_Treectrl->GetSelection()); + + Path = wxFileSelector( + _T("Export File"), + wxEmptyString, File, wxEmptyString, + wxString::Format + ( + _T("All files (%s)|%s"), + wxFileSelectorDefaultWildcardStr, + wxFileSelectorDefaultWildcardStr + ), + wxFD_SAVE, + this); + + if (!Path || !File) + return; + + while (m_Treectrl->GetItemParent(m_Treectrl->GetSelection()) != m_Treectrl->GetRootItem()) + { + wxString temp; + temp = m_Treectrl->GetItemText(m_Treectrl->GetItemParent(m_Treectrl->GetSelection())); + File = temp + _T("\\") + File; + + m_Treectrl->SelectItem(m_Treectrl->GetItemParent(m_Treectrl->GetSelection())); + } + + pFileSystem->ExportFile(File.mb_str(), Path.mb_str()); +} + +void CISOProperties::OnExtractDir(wxCommandEvent& WXUNUSED (event)) +{ +} + +void CISOProperties::SetRefresh(wxCommandEvent& WXUNUSED (event)) +{ + bRefreshList = true; +} + +void CISOProperties::LoadGameConfig() +{ + bool bTemp; + int iTemp; + + if (GameIni.Get("Core", "UseDualCore", &bTemp)) + UseDualCore->Set3StateValue((wxCheckBoxState)bTemp); + else + UseDualCore->Set3StateValue(wxCHK_UNDETERMINED); + + if (GameIni.Get("Core", "SkipIdle", &bTemp)) + SkipIdle->Set3StateValue((wxCheckBoxState)bTemp); + else + SkipIdle->Set3StateValue(wxCHK_UNDETERMINED); + + if (GameIni.Get("Core", "OptimizeQuantizers", &bTemp)) + OptimizeQuantizers->Set3StateValue((wxCheckBoxState)bTemp); + else + OptimizeQuantizers->Set3StateValue(wxCHK_UNDETERMINED); + + if (GameIni.Get("Core", "EnableProgressiveScan", &bTemp)) + EnableProgressiveScan->Set3StateValue((wxCheckBoxState)bTemp); + else + EnableProgressiveScan->Set3StateValue(wxCHK_UNDETERMINED); + + if (GameIni.Get("Core", "EnableWideScreen", &bTemp)) + EnableWideScreen->Set3StateValue((wxCheckBoxState)bTemp); + else + EnableWideScreen->Set3StateValue(wxCHK_UNDETERMINED); + + GameIni.Get("EmuState", "EmulationStateId", &iTemp, -1); + if (iTemp == -1) + { + iTemp = 0; + bRefreshList = true; + } + EmuState->SetSelection(iTemp); + + PatchList_Load(); + ActionReplayList_Load(); +} + +bool CISOProperties::SaveGameConfig() +{ + if (UseDualCore->Get3StateValue() == wxCHK_UNDETERMINED) + GameIni.DeleteKey("Core", "UseDualCore"); + else + GameIni.Set("Core", "UseDualCore", UseDualCore->Get3StateValue()); + + if (SkipIdle->Get3StateValue() == wxCHK_UNDETERMINED) + GameIni.DeleteKey("Core", "SkipIdle"); + else + GameIni.Set("Core", "SkipIdle", SkipIdle->Get3StateValue()); + + if (OptimizeQuantizers->Get3StateValue() == wxCHK_UNDETERMINED) + GameIni.DeleteKey("Core", "OptimizeQuantizers"); + else + GameIni.Set("Core", "OptimizeQuantizers", OptimizeQuantizers->Get3StateValue()); + + if (EnableProgressiveScan->Get3StateValue() == wxCHK_UNDETERMINED) + GameIni.DeleteKey("Core", "EnableProgressiveScan"); + else + GameIni.Set("Core", "EnableProgressiveScan", EnableProgressiveScan->Get3StateValue()); + + if (EnableWideScreen->Get3StateValue() == wxCHK_UNDETERMINED) + GameIni.DeleteKey("Core", "EnableWideScreen"); + else + GameIni.Set("Core", "EnableWideScreen", EnableWideScreen->Get3StateValue()); + + GameIni.Set("EmuState", "EmulationStateId", EmuState->GetSelection()); + + PatchList_Save(); + ActionReplayList_Save(); + + return GameIni.Save(GameIniFile.c_str()); +} + +void CISOProperties::OnEditConfig(wxCommandEvent& WXUNUSED (event)) +{ + if (wxFileExists(wxString::FromAscii(GameIniFile.c_str()))) + { + SaveGameConfig(); + + wxFileType* filetype = wxTheMimeTypesManager->GetFileTypeFromExtension(_("ini")); + wxExecute(filetype->GetOpenCommand(wxString::FromAscii(GameIniFile.c_str())), wxEXEC_SYNC); + + GameIni.Load(GameIniFile.c_str()); + LoadGameConfig(); + + bRefreshList = true; // Just in case + } +} + +void CISOProperties::ListSelectionChanged(wxCommandEvent& event) +{ + switch (event.GetId()) + { + case ID_PATCHES_LIST: + if (Patches->GetSelection() != wxNOT_FOUND) + { + EditPatch->Enable(); + RemovePatch->Enable(); + } + break; + case ID_CHEATS_LIST: + if (Cheats->GetSelection() != wxNOT_FOUND) + { + EditCheat->Enable(); + RemoveCheat->Enable(); + } + break; + } +} + +void CISOProperties::PatchList_Load() +{ + onFrame.clear(); + Patches->Clear(); + PatchEngine::LoadPatchSection("OnFrame", onFrame, GameIni); + + u32 index = 0; + for (std::vector::const_iterator it = onFrame.begin(); it != onFrame.end(); ++it) + { + PatchEngine::Patch p = *it; + Patches->Append(wxString::FromAscii(p.name.c_str())); + Patches->Check(index, p.active); + ++index; + } +} + +void CISOProperties::PatchList_Save() +{ + std::vector lines; + u32 index = 0; + for (std::vector::const_iterator onFrame_it = onFrame.begin(); onFrame_it != onFrame.end(); ++onFrame_it) + { + lines.push_back(Patches->IsChecked(index) ? "+$" + onFrame_it->name : "$" + onFrame_it->name); + + for (std::vector::const_iterator iter2 = onFrame_it->entries.begin(); iter2 != onFrame_it->entries.end(); ++iter2) + { + std::string temp; + ToStringFromFormat(&temp, "0x%08X:%s:0x%08X", iter2->address, PatchEngine::PatchTypeStrings[iter2->type], iter2->value); + lines.push_back(temp); + } + ++index; + } + GameIni.SetLines("OnFrame", lines); + lines.clear(); +} + +void CISOProperties::PatchButtonClicked(wxCommandEvent& event) +{ + int selection = Patches->GetSelection(); + + switch (event.GetId()) + { + case ID_EDITPATCH: + { + CPatchAddEdit dlg(selection, this); + dlg.ShowModal(); + } + break; + case ID_ADDPATCH: + { + // dialog; + } + break; + case ID_REMOVEPATCH: + onFrame.erase(onFrame.begin() + Patches->GetSelection()); + break; + } + + PatchList_Save(); + Patches->Clear(); + PatchList_Load(); + + EditPatch->Enable(false); + RemovePatch->Enable(false); +} + +void CISOProperties::ActionReplayList_Load() +{ + ARCodes.clear(); + Cheats->Clear(); + std::vector lines; + + if (!GameIni.GetLines("ActionReplay", lines)) + return; + + ARListCode code; + + for (std::vector::const_iterator it = lines.begin(); it != lines.end(); ++it) + { + std::string line = *it; + + if (line[0] == '+' || line[0] == '$') + { + // Take care of the previous code + if (code.ops.size()) + { + code.uiIndex = Cheats->Append(wxString::FromAscii(code.name.c_str())); + ARCodes.push_back(code); + Cheats->Check(code.uiIndex, code.enabled); + code.ops.clear(); + } + + // Give name and enabled to current code + if(line.size() > 1) + { + if (line[0] == '+') + { + code.enabled = true; + code.name = line.substr(2, line.size() - 2); + } + else + { + code.enabled = false; + code.name = line.substr(1, line.size() - 1); + } + } + continue; + } + code.ops.push_back(line); + } + + if (code.ops.size()) + { + code.uiIndex = Cheats->Append(wxString::FromAscii(code.name.c_str())); + ARCodes.push_back(code); + Cheats->Check(code.uiIndex, code.enabled); + } +} + +void CISOProperties::ActionReplayList_Save() +{ + std::vector lines; + for (std::vector::const_iterator iter = ARCodes.begin(); iter != ARCodes.end(); ++iter) + { + ARListCode code = *iter; + + lines.push_back(Cheats->IsChecked(code.uiIndex) ? "+$" + code.name : "$" + code.name); + + for (std::vector::const_iterator iter2 = code.ops.begin(); iter2 != code.ops.end(); ++iter2) + { + lines.push_back(*iter2); + } + } + GameIni.SetLines("ActionReplay", lines); +} + +void CISOProperties::ActionReplayButtonClicked(wxCommandEvent& event) +{ + switch (event.GetId()) + { + case ID_EDITCHEAT: + // dialog + break; + case ID_ADDCHEAT: + // dialog + break; + case ID_REMOVECHEAT: + ARCodes.erase(ARCodes.begin() + Cheats->GetSelection()); + break; + } + + ActionReplayList_Save(); + Cheats->Clear(); + ActionReplayList_Load(); + + EditCheat->Enable(false); + RemoveCheat->Enable(false); +} diff --git a/Source/Core/DolphinWX/Src/Main.cpp b/Source/Core/DolphinWX/Src/Main.cpp index 87386c0d5d..0de4547f48 100644 --- a/Source/Core/DolphinWX/Src/Main.cpp +++ b/Source/Core/DolphinWX/Src/Main.cpp @@ -1,395 +1,395 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include -#include -#include "svnrev.h" - -#ifdef WIN32 -#include -#endif - -#include "Globals.h" // Core -#include "Host.h" - -#include "Common.h" // Common -#include "CPUDetect.h" -#include "IniFile.h" -#include "FileUtil.h" - -#include "Main.h" // Local -#include "Frame.h" -#include "Config.h" -#include "CodeWindow.h" -#include "ExtendedTrace.h" -#include "BootManager.h" - -IMPLEMENT_APP(DolphinApp) - -CFrame* main_frame = NULL; -CCodeWindow* g_pCodeWindow = NULL; - -#ifdef WIN32 -//Has no error handling. -//I think that if an error occurs here there's no way to handle it anyway. -LONG WINAPI MyUnhandledExceptionFilter(LPEXCEPTION_POINTERS e) { - //EnterCriticalSection(&g_uefcs); - - FILE* file=NULL; - fopen_s(&file, "exceptioninfo.txt", "a"); - fseek(file, 0, SEEK_END); - etfprint(file, "\n"); - //etfprint(file, g_buildtime); - //etfprint(file, "\n"); - //dumpCurrentDate(file); - etfprintf(file, "Unhandled Exception\n Code: 0x%08X\n", - e->ExceptionRecord->ExceptionCode); -#ifndef _M_X64 - STACKTRACE2(file, e->ContextRecord->Eip, e->ContextRecord->Esp, e->ContextRecord->Ebp); -#else - STACKTRACE2(file, e->ContextRecord->Rip, e->ContextRecord->Rsp, e->ContextRecord->Rbp); -#endif - fclose(file); - _flushall(); - - //LeaveCriticalSection(&g_uefcs); - return EXCEPTION_CONTINUE_SEARCH; -} -#endif - -// The `main program' equivalent, creating the windows and returning the -// main frame -bool DolphinApp::OnInit() -{ - DetectCPU(); - -#ifdef _DEBUG - int tmpflag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); - tmpflag |= _CRTDBG_DELAY_FREE_MEM_DF; - _CrtSetDbgFlag(tmpflag); -#endif - -#ifndef _WIN32 -// RegisterPanicAlertHandler(&wxPanicAlert); -#endif - -#ifdef _WIN32 - EXTENDEDTRACEINITIALIZE("."); - SetUnhandledExceptionFilter(&MyUnhandledExceptionFilter); - - // TODO: if First Boot - if (!cpu_info.bSSE2) - { - MessageBox(0, _T("Hi,\n\nDolphin requires that your CPU has support for SSE2 extensions.\n" - "Unfortunately your CPU does not support them, so Dolphin will not run.\n\n" - "Sayonara!\n"), "Dolphin", MB_ICONINFORMATION); - return false; - } -#else - if (!cpu_info.bSSE2) - { - printf("%s", "Hi,\n\nDolphin requires that your CPU has support for SSE2 extensions.\n" - "Unfortunately your CPU does not support them, so Dolphin will not run.\n\n" - "Sayonara!\n"); - exit(0); - } -#endif - - // ============ - // Check for debugger - bool UseDebugger = false; - bool LoadElf = false; wxString ElfFile; - -#if wxUSE_CMDLINE_PARSER - wxCmdLineEntryDesc cmdLineDesc[] = - { - { - wxCMD_LINE_SWITCH, _T("h"), _T("help"), _T("Show this help message"), - wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP - }, - { - wxCMD_LINE_SWITCH, _T("d"), _T("debugger"), _T("Opens the debugger") - }, - { - wxCMD_LINE_OPTION, _T("e"), _T("elf"), _T("Loads an elf file"), - wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL - }, - { - wxCMD_LINE_NONE - } - }; - -#if defined(__APPLE__) - // HACK: Get rid of bogous osx param - if (argc > 1 && wxString(argv[argc - 1]).StartsWith(_("-psn_"))) { - delete argv[argc-1]; - argv[argc-1] = NULL; - argc--; - } -#endif - - // Gets the passed media files from command line - wxCmdLineParser parser(cmdLineDesc, argc, argv); - - // Get filenames from the command line - if (parser.Parse() != 0) - { - return false; - } - - UseDebugger = parser.Found(_T("debugger")); - LoadElf = parser.Found(_T("elf"), &ElfFile); - - if( LoadElf && ElfFile == wxEmptyString ) - PanicAlert("You did not specify a file name"); - - // ============ -#endif - - SConfig::GetInstance().LoadSettings(); - wxInitAllImageHandlers(); - // Create the main frame window - -#ifdef _DEBUG - const char *title = "Dolphin Debug SVN R " SVN_REV_STR; -#else - const char *title = "Dolphin SVN R " SVN_REV_STR; -#endif - - // --------------------------------------------------------------------------------------- - // If we are debugging let use save the main window position and size - // TODO: Save position and size on exit - // --------- - IniFile ini; - ini.Load(DEBUGGER_CONFIG_FILE); - - int x, y, w, h; - - ini.Get("MainWindow", "x", &x, 100); - ini.Get("MainWindow", "y", &y, 100); - ini.Get("MainWindow", "w", &w, 600); - ini.Get("MainWindow", "h", &h, 800); - // --------- - if(UseDebugger) - { - main_frame = new CFrame((wxFrame*) NULL, wxID_ANY, wxString::FromAscii(title), - wxPoint(x, y), wxSize(h, w)); - } - else - { - main_frame = new CFrame((wxFrame*) NULL, wxID_ANY, wxString::FromAscii(title), - wxPoint(100, 100), wxSize(800, 600)); - } - // --------- - - // Create debugger - if (UseDebugger) - { - g_pCodeWindow = new CCodeWindow(SConfig::GetInstance().m_LocalCoreStartupParameter, main_frame); - g_pCodeWindow->Show(true); - } - - // First check if we have a elf command line - if (LoadElf && ElfFile != wxEmptyString) - { - BootManager::BootCore(std::string(ElfFile.ToAscii())); - } - /* If we have selected Automatic Start, start the default ISO, or if no default - ISO exists, start the last loaded ISO */ - else if (UseDebugger && g_pCodeWindow->AutomaticStart()) - { - if(!SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDefaultGCM.empty() - && File::Exists(SConfig::GetInstance().m_LocalCoreStartupParameter. - m_strDefaultGCM.c_str())) - { - BootManager::BootCore(SConfig::GetInstance().m_LocalCoreStartupParameter. - m_strDefaultGCM); - } - else if(!SConfig::GetInstance().m_LastFilename.empty() - && File::Exists(SConfig::GetInstance().m_LastFilename.c_str())) - { - BootManager::BootCore(SConfig::GetInstance().m_LastFilename); - } - } - - SetTopWindow(main_frame); - return true; -} - - -void DolphinApp::OnEndSession() -{ - SConfig::GetInstance().SaveSettings(); -} - - -bool wxPanicAlert(const char* text, bool /*yes_no*/) -{ - wxMessageBox(wxString::FromAscii(text), _T("PANIC ALERT")); - return(true); -} - - -void Host_BootingStarted() -{ - wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_BOOTING_STARTED); - wxPostEvent(main_frame, event); - - if (g_pCodeWindow) - { - wxPostEvent(g_pCodeWindow, event); - } -} - - -void Host_BootingEnded() -{ - wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_BOOTING_ENDED); - wxPostEvent(main_frame, event); - - if (g_pCodeWindow) - { - wxPostEvent(g_pCodeWindow, event); - } -} - - -// OK, this thread boundary is DANGEROUS on linux -// wxPostEvent / wxAddPendingEvent is the solution. -void Host_NotifyMapLoaded() -{ - wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_NOTIFYMAPLOADED); - wxPostEvent(main_frame, event); - - if (g_pCodeWindow) - { - wxPostEvent(g_pCodeWindow, event); - } -} - - -void Host_UpdateLogDisplay() -{ - wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATELOGDISPLAY); - wxPostEvent(main_frame, event); - - if (g_pCodeWindow) - { - wxPostEvent(g_pCodeWindow, event); - } -} - - -void Host_UpdateDisasmDialog() -{ - wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATEDISASMDIALOG); - wxPostEvent(main_frame, event); - if (g_pCodeWindow) - { - wxPostEvent(g_pCodeWindow, event); - } -} - - -void Host_UpdateMainFrame() -{ - wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATEGUI); - wxPostEvent(main_frame, event); - - if (g_pCodeWindow) - { - wxPostEvent(g_pCodeWindow, event); - } -} - -void Host_UpdateBreakPointView() -{ - wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATEBREAKPOINTS); - wxPostEvent(main_frame, event); - - if (g_pCodeWindow) - { - wxPostEvent(g_pCodeWindow, event); - } -} - - -void Host_UpdateMemoryView() -{} - - -void Host_SetDebugMode(bool) -{} - - -void Host_SetWaitCursor(bool enable) -{ -#ifdef _WIN32 - if (enable) - { - SetCursor(LoadCursor(NULL, IDC_WAIT)); - } - else - { - SetCursor(LoadCursor(NULL, IDC_ARROW)); - } -#endif - -} - -void Host_UpdateStatusBar(const char* _pText) -{ - wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATESTATUSBAR); - event.SetString(wxString::FromAscii(_pText)); - event.SetInt(0); - - wxPostEvent(main_frame, event); -} - -void Host_SysMessage(const char *fmt, ...) -{ - va_list list; - char msg[512]; - - va_start(list, fmt); - vsprintf(msg, fmt, list); - va_end(list); - - if (msg[strlen(msg)-1] == '\n') msg[strlen(msg)-1] = 0; - wxMessageBox(wxString::FromAscii(msg)); -} - -void Host_SetWiiMoteConnectionState(int _State) -{ - static int currentState = -1; - if (_State == currentState) - return; - currentState = _State; - - wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATESTATUSBAR); - - switch(_State) - { - case 0: event.SetString(wxString::FromAscii("Not connected")); break; - case 1: event.SetString(wxString::FromAscii("Connecting...")); break; - case 2: event.SetString(wxString::FromAscii("Wiimote Connected")); break; - } - - event.SetInt(1); - - wxPostEvent(main_frame, event); -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include +#include "svnrev.h" + +#ifdef WIN32 +#include +#endif + +#include "Globals.h" // Core +#include "Host.h" + +#include "Common.h" // Common +#include "CPUDetect.h" +#include "IniFile.h" +#include "FileUtil.h" + +#include "Main.h" // Local +#include "Frame.h" +#include "Config.h" +#include "CodeWindow.h" +#include "ExtendedTrace.h" +#include "BootManager.h" + +IMPLEMENT_APP(DolphinApp) + +CFrame* main_frame = NULL; +CCodeWindow* g_pCodeWindow = NULL; + +#ifdef WIN32 +//Has no error handling. +//I think that if an error occurs here there's no way to handle it anyway. +LONG WINAPI MyUnhandledExceptionFilter(LPEXCEPTION_POINTERS e) { + //EnterCriticalSection(&g_uefcs); + + FILE* file=NULL; + fopen_s(&file, "exceptioninfo.txt", "a"); + fseek(file, 0, SEEK_END); + etfprint(file, "\n"); + //etfprint(file, g_buildtime); + //etfprint(file, "\n"); + //dumpCurrentDate(file); + etfprintf(file, "Unhandled Exception\n Code: 0x%08X\n", + e->ExceptionRecord->ExceptionCode); +#ifndef _M_X64 + STACKTRACE2(file, e->ContextRecord->Eip, e->ContextRecord->Esp, e->ContextRecord->Ebp); +#else + STACKTRACE2(file, e->ContextRecord->Rip, e->ContextRecord->Rsp, e->ContextRecord->Rbp); +#endif + fclose(file); + _flushall(); + + //LeaveCriticalSection(&g_uefcs); + return EXCEPTION_CONTINUE_SEARCH; +} +#endif + +// The `main program' equivalent, creating the windows and returning the +// main frame +bool DolphinApp::OnInit() +{ + DetectCPU(); + +#ifdef _DEBUG + int tmpflag = _CrtSetDbgFlag(_CRTDBG_REPORT_FLAG); + tmpflag |= _CRTDBG_DELAY_FREE_MEM_DF; + _CrtSetDbgFlag(tmpflag); +#endif + +#ifndef _WIN32 +// RegisterPanicAlertHandler(&wxPanicAlert); +#endif + +#ifdef _WIN32 + EXTENDEDTRACEINITIALIZE("."); + SetUnhandledExceptionFilter(&MyUnhandledExceptionFilter); + + // TODO: if First Boot + if (!cpu_info.bSSE2) + { + MessageBox(0, _T("Hi,\n\nDolphin requires that your CPU has support for SSE2 extensions.\n" + "Unfortunately your CPU does not support them, so Dolphin will not run.\n\n" + "Sayonara!\n"), "Dolphin", MB_ICONINFORMATION); + return false; + } +#else + if (!cpu_info.bSSE2) + { + printf("%s", "Hi,\n\nDolphin requires that your CPU has support for SSE2 extensions.\n" + "Unfortunately your CPU does not support them, so Dolphin will not run.\n\n" + "Sayonara!\n"); + exit(0); + } +#endif + + // ============ + // Check for debugger + bool UseDebugger = false; + bool LoadElf = false; wxString ElfFile; + +#if wxUSE_CMDLINE_PARSER + wxCmdLineEntryDesc cmdLineDesc[] = + { + { + wxCMD_LINE_SWITCH, _T("h"), _T("help"), _T("Show this help message"), + wxCMD_LINE_VAL_NONE, wxCMD_LINE_OPTION_HELP + }, + { + wxCMD_LINE_SWITCH, _T("d"), _T("debugger"), _T("Opens the debugger") + }, + { + wxCMD_LINE_OPTION, _T("e"), _T("elf"), _T("Loads an elf file"), + wxCMD_LINE_VAL_STRING, wxCMD_LINE_PARAM_OPTIONAL + }, + { + wxCMD_LINE_NONE + } + }; + +#if defined(__APPLE__) + // HACK: Get rid of bogous osx param + if (argc > 1 && wxString(argv[argc - 1]).StartsWith(_("-psn_"))) { + delete argv[argc-1]; + argv[argc-1] = NULL; + argc--; + } +#endif + + // Gets the passed media files from command line + wxCmdLineParser parser(cmdLineDesc, argc, argv); + + // Get filenames from the command line + if (parser.Parse() != 0) + { + return false; + } + + UseDebugger = parser.Found(_T("debugger")); + LoadElf = parser.Found(_T("elf"), &ElfFile); + + if( LoadElf && ElfFile == wxEmptyString ) + PanicAlert("You did not specify a file name"); + + // ============ +#endif + + SConfig::GetInstance().LoadSettings(); + wxInitAllImageHandlers(); + // Create the main frame window + +#ifdef _DEBUG + const char *title = "Dolphin Debug SVN R " SVN_REV_STR; +#else + const char *title = "Dolphin SVN R " SVN_REV_STR; +#endif + + // --------------------------------------------------------------------------------------- + // If we are debugging let use save the main window position and size + // TODO: Save position and size on exit + // --------- + IniFile ini; + ini.Load(DEBUGGER_CONFIG_FILE); + + int x, y, w, h; + + ini.Get("MainWindow", "x", &x, 100); + ini.Get("MainWindow", "y", &y, 100); + ini.Get("MainWindow", "w", &w, 600); + ini.Get("MainWindow", "h", &h, 800); + // --------- + if(UseDebugger) + { + main_frame = new CFrame((wxFrame*) NULL, wxID_ANY, wxString::FromAscii(title), + wxPoint(x, y), wxSize(h, w)); + } + else + { + main_frame = new CFrame((wxFrame*) NULL, wxID_ANY, wxString::FromAscii(title), + wxPoint(100, 100), wxSize(800, 600)); + } + // --------- + + // Create debugger + if (UseDebugger) + { + g_pCodeWindow = new CCodeWindow(SConfig::GetInstance().m_LocalCoreStartupParameter, main_frame); + g_pCodeWindow->Show(true); + } + + // First check if we have a elf command line + if (LoadElf && ElfFile != wxEmptyString) + { + BootManager::BootCore(std::string(ElfFile.ToAscii())); + } + /* If we have selected Automatic Start, start the default ISO, or if no default + ISO exists, start the last loaded ISO */ + else if (UseDebugger && g_pCodeWindow->AutomaticStart()) + { + if(!SConfig::GetInstance().m_LocalCoreStartupParameter.m_strDefaultGCM.empty() + && File::Exists(SConfig::GetInstance().m_LocalCoreStartupParameter. + m_strDefaultGCM.c_str())) + { + BootManager::BootCore(SConfig::GetInstance().m_LocalCoreStartupParameter. + m_strDefaultGCM); + } + else if(!SConfig::GetInstance().m_LastFilename.empty() + && File::Exists(SConfig::GetInstance().m_LastFilename.c_str())) + { + BootManager::BootCore(SConfig::GetInstance().m_LastFilename); + } + } + + SetTopWindow(main_frame); + return true; +} + + +void DolphinApp::OnEndSession() +{ + SConfig::GetInstance().SaveSettings(); +} + + +bool wxPanicAlert(const char* text, bool /*yes_no*/) +{ + wxMessageBox(wxString::FromAscii(text), _T("PANIC ALERT")); + return(true); +} + + +void Host_BootingStarted() +{ + wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_BOOTING_STARTED); + wxPostEvent(main_frame, event); + + if (g_pCodeWindow) + { + wxPostEvent(g_pCodeWindow, event); + } +} + + +void Host_BootingEnded() +{ + wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_BOOTING_ENDED); + wxPostEvent(main_frame, event); + + if (g_pCodeWindow) + { + wxPostEvent(g_pCodeWindow, event); + } +} + + +// OK, this thread boundary is DANGEROUS on linux +// wxPostEvent / wxAddPendingEvent is the solution. +void Host_NotifyMapLoaded() +{ + wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_NOTIFYMAPLOADED); + wxPostEvent(main_frame, event); + + if (g_pCodeWindow) + { + wxPostEvent(g_pCodeWindow, event); + } +} + + +void Host_UpdateLogDisplay() +{ + wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATELOGDISPLAY); + wxPostEvent(main_frame, event); + + if (g_pCodeWindow) + { + wxPostEvent(g_pCodeWindow, event); + } +} + + +void Host_UpdateDisasmDialog() +{ + wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATEDISASMDIALOG); + wxPostEvent(main_frame, event); + if (g_pCodeWindow) + { + wxPostEvent(g_pCodeWindow, event); + } +} + + +void Host_UpdateMainFrame() +{ + wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATEGUI); + wxPostEvent(main_frame, event); + + if (g_pCodeWindow) + { + wxPostEvent(g_pCodeWindow, event); + } +} + +void Host_UpdateBreakPointView() +{ + wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATEBREAKPOINTS); + wxPostEvent(main_frame, event); + + if (g_pCodeWindow) + { + wxPostEvent(g_pCodeWindow, event); + } +} + + +void Host_UpdateMemoryView() +{} + + +void Host_SetDebugMode(bool) +{} + + +void Host_SetWaitCursor(bool enable) +{ +#ifdef _WIN32 + if (enable) + { + SetCursor(LoadCursor(NULL, IDC_WAIT)); + } + else + { + SetCursor(LoadCursor(NULL, IDC_ARROW)); + } +#endif + +} + +void Host_UpdateStatusBar(const char* _pText) +{ + wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATESTATUSBAR); + event.SetString(wxString::FromAscii(_pText)); + event.SetInt(0); + + wxPostEvent(main_frame, event); +} + +void Host_SysMessage(const char *fmt, ...) +{ + va_list list; + char msg[512]; + + va_start(list, fmt); + vsprintf(msg, fmt, list); + va_end(list); + + if (msg[strlen(msg)-1] == '\n') msg[strlen(msg)-1] = 0; + wxMessageBox(wxString::FromAscii(msg)); +} + +void Host_SetWiiMoteConnectionState(int _State) +{ + static int currentState = -1; + if (_State == currentState) + return; + currentState = _State; + + wxCommandEvent event(wxEVT_HOST_COMMAND, IDM_UPDATESTATUSBAR); + + switch(_State) + { + case 0: event.SetString(wxString::FromAscii("Not connected")); break; + case 1: event.SetString(wxString::FromAscii("Connecting...")); break; + case 2: event.SetString(wxString::FromAscii("Wiimote Connected")); break; + } + + event.SetInt(1); + + wxPostEvent(main_frame, event); +} diff --git a/Source/Core/DolphinWX/Src/MemcardManager.cpp b/Source/Core/DolphinWX/Src/MemcardManager.cpp index cbed087842..393cf7a077 100644 --- a/Source/Core/DolphinWX/Src/MemcardManager.cpp +++ b/Source/Core/DolphinWX/Src/MemcardManager.cpp @@ -1,742 +1,742 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Globals.h" -#include "MemcardManager.h" - -#include "wx/mstream.h" - -const u8 hdr[] = { -0x42,0x4D, -0x38,0x30,0x00,0x00, -0x00,0x00,0x00,0x00, -0x36,0x00,0x00,0x00, -0x28,0x00,0x00,0x00, -0x20,0x00,0x00,0x00, //W -0x20,0x00,0x00,0x00, //H -0x01,0x00, -0x20,0x00, -0x00,0x00,0x00,0x00, -0x02,0x30,0x00,0x00, //data size -0x12,0x0B,0x00,0x00, -0x12,0x0B,0x00,0x00, -0x00,0x00,0x00,0x00, -0x00,0x00,0x00,0x00 -}; - -wxBitmap wxBitmapFromMemoryRGBA(const unsigned char* data, int width, int height) -{ - int stride = (4*width); - - int bytes = (stride*height) + sizeof(hdr); - - bytes = (bytes+3)&(~3); - - u8 *pdata = new u8[bytes]; - - memset(pdata,0,bytes); - memcpy(pdata,hdr,sizeof(hdr)); - - u8 *pixelData = pdata + sizeof(hdr); - - for (int y=0;yCopy->"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - - m_FixChecksumLeft = new wxButton(this, ID_FIXCHECKSUMLEFT, wxT("<-Fix Checksum"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - m_FixChecksumRight = new wxButton(this, ID_FIXCHECKSUMRIGHT, wxT("Fix Checksum->"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - - m_SaveImportLeft = new wxButton(this, ID_SAVEIMPORTLEFT, wxT("<-Import GCI"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - m_SaveImportRight = new wxButton(this, ID_SAVEIMPORTRIGHT, wxT("Import GCI->"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - - m_SaveExportLeft = new wxButton(this, ID_SAVEEXPORTLEFT, wxT("<-Export GCI"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - m_SaveExportRight = new wxButton(this, ID_SAVEEXPORTRIGHT, wxT("Export GCI->"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - - m_ConvertToGci = new wxButton(this, ID_CONVERTTOGCI, wxT("Convert to GCI"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - - m_DeleteLeft = new wxButton(this, ID_DELETELEFT, wxT("<-Delete"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - m_DeleteRight = new wxButton(this, ID_DELETERIGHT, wxT("Delete->"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - - m_Memcard1PrevPage = new wxButton(this, ID_MEMCARD1PREVPAGE, wxT("Prev Page"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - m_Memcard2PrevPage = new wxButton(this, ID_MEMCARD2PREVPAGE, wxT("Prev Page"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - - m_Memcard1NextPage = new wxButton(this, ID_MEMCARD1NEXTPAGE, wxT("Next Page"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - m_Memcard2NextPage = new wxButton(this, ID_MEMCARD2NEXTPAGE, wxT("Next Page"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - - // Sizers that double as wxStaticBoxes - sMemcard1 = new wxStaticBoxSizer(wxVERTICAL, this, wxT("Memory Card 1")); - sMemcard2 = new wxStaticBoxSizer(wxVERTICAL, this, wxT("Memory Card 2")); - - // Create the controls for both memcards - // Loading invalid .raw files should no longer crash the app - m_Memcard1Path = new wxFilePickerCtrl(this, ID_MEMCARD1PATH, wxEmptyString, wxT("Choose a memory card:"), - wxT("Raw memcards (*.raw)|*.raw"), wxDefaultPosition, wxDefaultSize, wxFLP_USE_TEXTCTRL|wxFLP_FILE_MUST_EXIST|wxFLP_OPEN); - m_Memcard2Path = new wxFilePickerCtrl(this, ID_MEMCARD2PATH, wxEmptyString, wxT("Choose a memory card:"), - wxT("Raw memcards (*.raw)|*.raw"), wxDefaultPosition, wxDefaultSize, wxFLP_USE_TEXTCTRL|wxFLP_FILE_MUST_EXIST|wxFLP_OPEN); - - m_MemcardList[0] = new wxListCtrl(this, ID_MEMCARD1LIST, wxDefaultPosition, wxSize(350,400), - wxLC_REPORT | wxSUNKEN_BORDER | wxLC_ALIGN_LEFT | wxLC_SINGLE_SEL); - m_MemcardList[1] = new wxListCtrl(this, ID_MEMCARD2LIST, wxDefaultPosition, wxSize(350,400), - wxLC_REPORT | wxSUNKEN_BORDER | wxLC_ALIGN_LEFT | wxLC_SINGLE_SEL); - - m_MemcardList[0]->AssignImageList(new wxImageList(96,32),wxIMAGE_LIST_SMALL); - m_MemcardList[1]->AssignImageList(new wxImageList(96,32),wxIMAGE_LIST_SMALL); - - // mmmm sizer goodness - wxBoxSizer* sButtons; - sButtons = new wxBoxSizer(wxVERTICAL); - sButtons->AddStretchSpacer(2); - sButtons->Add(m_CopyToLeft, 0, wxEXPAND, 5); - sButtons->Add(m_CopyToRight, 0, wxEXPAND, 5); - sButtons->AddStretchSpacer(1); - sButtons->Add(m_FixChecksumLeft, 0, wxEXPAND, 5); - sButtons->Add(m_FixChecksumRight, 0, wxEXPAND, 5); - sButtons->AddStretchSpacer(1); - sButtons->Add(m_SaveImportLeft, 0, wxEXPAND, 5); - sButtons->Add(m_SaveExportLeft, 0, wxEXPAND, 5); - sButtons->AddStretchSpacer(1); - sButtons->Add(m_ConvertToGci, 0, wxEXPAND, 5); - sButtons->AddStretchSpacer(1); - sButtons->Add(m_SaveImportRight, 0, wxEXPAND, 5); - sButtons->Add(m_SaveExportRight, 0, wxEXPAND, 5); - sButtons->AddStretchSpacer(1); - sButtons->Add(m_DeleteLeft, 0, wxEXPAND, 5); - sButtons->Add(m_DeleteRight, 0, wxEXPAND, 5); - sButtons->AddStretchSpacer(1); - - sPagesLeft = new wxBoxSizer(wxHORIZONTAL); - sPagesRight = new wxBoxSizer(wxHORIZONTAL); - - sPagesLeft->Add(m_Memcard1PrevPage, 0, wxEXPAND|wxALL, 1); - sPagesLeft->Add(t_StatusLeft,0, wxEXPAND|wxALL, 5); - sPagesLeft->Add(0, 0, 1, wxEXPAND|wxALL, 0); - sPagesLeft->Add(m_Memcard1NextPage, 0, wxEXPAND|wxALL, 1); - sPagesRight->Add(m_Memcard2PrevPage, 0, wxEXPAND|wxALL, 1); - sPagesRight->Add(t_StatusRight, 0, wxEXPAND|wxALL, 5); - sPagesRight->Add(0, 0, 1, wxEXPAND|wxALL, 0); - sPagesRight->Add(m_Memcard2NextPage, 0, wxEXPAND|wxALL, 1); - - sMemcard1->Add(m_Memcard1Path, 0, wxEXPAND|wxALL, 5); - sMemcard1->Add(m_MemcardList[0], 1, wxEXPAND|wxALL, 5); - sMemcard1->Add(sPagesLeft, 0, wxEXPAND|wxALL, 1); - sMemcard2->Add(m_Memcard2Path, 0, wxEXPAND|wxALL, 5); - sMemcard2->Add(m_MemcardList[1], 1, wxEXPAND|wxALL, 5); - sMemcard2->Add(sPagesRight, 0, wxEXPAND|wxALL, 1); - - sMain = new wxBoxSizer(wxHORIZONTAL); - sMain->Add(sMemcard1, 1, wxEXPAND|wxALL, 5); - sMain->Add(sButtons, 0, wxEXPAND, 0); - sMain->Add(sMemcard2, 1, wxEXPAND|wxALL, 5); - - this->SetSizer(sMain); - sMain->SetSizeHints(this); - Fit(); - m_Memcard1PrevPage->Disable(); - m_Memcard1NextPage->Disable(); - m_Memcard2PrevPage->Disable(); - m_Memcard2NextPage->Disable(); - m_CopyToLeft->Disable(); - m_CopyToRight->Disable(); - m_FixChecksumLeft->Disable(); - m_FixChecksumRight->Disable(); - m_SaveImportLeft->Disable(); - m_SaveExportLeft->Disable(); - m_SaveImportRight->Disable(); - m_SaveExportRight->Disable(); - m_DeleteLeft->Disable(); - m_DeleteRight->Disable(); -} - -void CMemcardManager::OnClose(wxCloseEvent& WXUNUSED (event)) -{ - Destroy(); -} - -void CMemcardManager::OnPathChange(wxFileDirPickerEvent& event) -{ - switch (event.GetId()) - { - case ID_MEMCARD1PATH: - page0 = 0; - if (m_Memcard1PrevPage->IsEnabled()) m_Memcard1PrevPage->Disable(); - if (!strcasecmp(m_Memcard1Path->GetPath().mb_str(),m_Memcard2Path->GetPath().mb_str())) - { - wxMessageBox(wxT("Memcard already opened"), wxT("Error"), wxOK|wxICON_ERROR); - m_Memcard1Path->SetPath(wxEmptyString); - m_MemcardList[0]->ClearAll(); - t_StatusLeft->SetLabel(wxEmptyString); - } - else if (ReloadMemcard(event.GetPath().mb_str(), 0, page0)) - { - m_FixChecksumLeft->Enable(); - m_SaveImportLeft->Enable(); - m_SaveExportLeft->Enable(); - m_DeleteLeft->Enable(); - break; - } - m_Memcard1Path->SetPath(wxEmptyString); - m_MemcardList[0]->ClearAll(); - t_StatusLeft->SetLabel(wxEmptyString); - m_FixChecksumLeft->Disable(); - m_SaveImportLeft->Disable(); - m_SaveExportLeft->Disable(); - m_DeleteLeft->Disable(); - m_Memcard1PrevPage->Disable(); - m_Memcard1NextPage->Disable(); - break; - case ID_MEMCARD2PATH: - page1 = 0; - if (m_Memcard2PrevPage->IsEnabled()) m_Memcard2PrevPage->Disable(); - if (!strcasecmp(m_Memcard1Path->GetPath().mb_str(),m_Memcard2Path->GetPath().mb_str())) - { - wxMessageBox(wxT("Memcard already opened"), wxT("Error"), wxOK|wxICON_ERROR); - } - else if (ReloadMemcard(event.GetPath().mb_str(), 1, page1)) - { - m_FixChecksumRight->Enable(); - m_SaveImportRight->Enable(); - m_SaveExportRight->Enable(); - m_DeleteRight->Enable(); - break; - } - m_Memcard2Path->SetPath(wxEmptyString); - m_MemcardList[1]->ClearAll(); - t_StatusRight->SetLabel(wxEmptyString); - m_FixChecksumRight->Disable(); - m_SaveImportRight->Disable(); - m_SaveExportRight->Disable(); - m_DeleteRight->Disable(); - m_Memcard2PrevPage->Disable(); - m_Memcard2NextPage->Disable(); - break; - } - if (m_DeleteRight->IsEnabled() && m_DeleteLeft->IsEnabled()) - { - m_CopyToLeft->Enable(); - m_CopyToRight->Enable(); - } - else - { - m_CopyToLeft->Disable(); - m_CopyToRight->Disable(); - } -} - -void CMemcardManager::OnPageChange(wxCommandEvent& event) -{ - switch (event.GetId()) - { - case ID_MEMCARD1NEXTPAGE: - if (!m_Memcard1PrevPage->IsEnabled()) m_Memcard1PrevPage->Enable(); - if (!m_Memcard1NextPage->IsEnabled()) m_Memcard1NextPage->Enable(); - page0++; - if (page0 == MAXPAGES) m_Memcard1NextPage->Disable(); - ReloadMemcard(m_Memcard1Path->GetPath().mb_str(), 0, page0); - break; - case ID_MEMCARD2NEXTPAGE: - if (!m_Memcard2PrevPage->IsEnabled()) m_Memcard2PrevPage->Enable(); - if (!m_Memcard2NextPage->IsEnabled()) m_Memcard2NextPage->Enable(); - page1++; - if (page1 == MAXPAGES) m_Memcard2NextPage->Disable(); - ReloadMemcard(m_Memcard2Path->GetPath().mb_str(), 1, page1); - break; - case ID_MEMCARD1PREVPAGE: - if (!m_Memcard1NextPage->IsEnabled()) m_Memcard1NextPage->Enable(); - page0--; - if (!page0) m_Memcard1PrevPage->Disable(); - ReloadMemcard(m_Memcard1Path->GetPath().mb_str(), 0, page0); - break; - case ID_MEMCARD2PREVPAGE: - if (!m_Memcard2NextPage->IsEnabled()) m_Memcard2NextPage->Enable(); - page1--; - if (!page1) m_Memcard2PrevPage->Disable(); - ReloadMemcard(m_Memcard2Path->GetPath().mb_str(), 1, page1); - break; - } -} - -void CMemcardManager::CopyDeleteClick(wxCommandEvent& event) -{ - int index0 = m_MemcardList[0]->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); - int index1 = m_MemcardList[1]->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); - int slot = 1; - int index2 = index1; - std::string fileName2(""); - wxString blocksOpen; - - if (index0 != -1 && page0) index0 += ITEMSPERPAGE * page0; - if (index1 != -1 && page1) index1 += ITEMSPERPAGE * page1; - - switch (event.GetId()) - { - case ID_COPYTOLEFT: - if ((index1 != -1) && (memoryCard[0] != NULL)) - { - switch (memoryCard[0]->CopyFrom(*memoryCard[1], index1)) - { - case FAIL: - wxMessageBox(wxT("Invalid bat.map or dir entry"), wxT("Failure"), wxOK); - break; - case NOMEMCARD: - wxMessageBox(wxT("File is not recognized as a memcard"), wxT("Failure"), wxOK); - break; - case SUCCESS: - memoryCard[0]->Save(); - ReloadMemcard(m_Memcard1Path->GetPath().mb_str(), 0, 0); - break; - } - - } - else - { - wxMessageBox(wxT("You have not selected a save to copy"), wxT("Error"), wxOK|wxICON_ERROR); - } - break; - case ID_COPYTORIGHT: - if ((index0 != -1) && (memoryCard[1] != NULL)) - { - switch (memoryCard[1]->CopyFrom(*memoryCard[0], index0)) - { - case FAIL: - wxMessageBox(wxT("Invalid bat.map or dir entry"), - wxT("Error"), wxOK|wxICON_ERROR); - break; - case NOMEMCARD: - wxMessageBox(wxT("File is not recognized as a memcard"), - wxT("Error"), wxOK|wxICON_ERROR); - break; - case SUCCESS: - memoryCard[1]->Save(); - ReloadMemcard(m_Memcard2Path->GetPath().mb_str(), 1, 0); - break; - } - } - else - { - wxMessageBox(wxT("You have not selected a save to copy"), wxT("Error"), wxOK|wxICON_ERROR); - } - break; - case ID_FIXCHECKSUMLEFT: - slot = 0; - case ID_FIXCHECKSUMRIGHT: - if (memoryCard[slot] != NULL) - { - // Fix checksums and save the changes - memoryCard[slot]->FixChecksums() ? wxMessageBox(wxT("The checksum was successfully fixed"), wxT("Success"), wxOK) - : wxMessageBox(wxT("The checksum could not be successfully fixed"), wxT("Error"), wxOK|wxICON_ERROR); - memoryCard[slot]->Save(); - } - else - { - wxMessageBox(wxT("memorycard is not loaded"), wxT("Error"), wxOK|wxICON_ERROR); - } - break; - case ID_CONVERTTOGCI: - fileName2 = "convert"; - - case ID_SAVEIMPORTLEFT: - slot = 0; - case ID_SAVEIMPORTRIGHT: - if (memoryCard[slot] != NULL || !fileName2.empty()) - { - wxString temp = wxFileSelector(_T("Select a save file to import"), - wxEmptyString, wxEmptyString, wxEmptyString,wxString::Format - ( - _T("Gamecube save files(*.gci,*.gcs,*.sav)|*.gci;*.gcs;*.sav|" - "Native GCI files (*.gci)|*.gci|" - "MadCatz Gameshark files(*.gcs)|*.gcs|" - "Datel MaxDrive/Pro files(*.sav)|*.sav"), - wxFileSelectorDefaultWildcardStr, - wxFileSelectorDefaultWildcardStr - ), - wxFD_OPEN | wxFD_FILE_MUST_EXIST); - const char * fileName = temp.ToAscii(); - if (!temp.empty() && !fileName2.empty()) - { - wxString temp2 = wxFileSelector(_T("Save GCI as.."), - wxEmptyString, wxEmptyString, _T(".gci"), wxString::Format - ( - _T("GCI File(*.gci)|*.gci"), - wxFileSelectorDefaultWildcardStr, - wxFileSelectorDefaultWildcardStr - ), - wxFD_OVERWRITE_PROMPT|wxFD_SAVE); - if (temp2.empty()) break; - fileName2 = temp2.mb_str(); - } - if (temp.length() > 0) - { - switch(memoryCard[slot]->ImportGci(fileName, fileName2)) - { - case LENGTHFAIL: - wxMessageBox(wxT("Imported file has invalid length"), - wxT("Error"), wxOK|wxICON_ERROR); - break; - case GCSFAIL: - wxMessageBox(wxT("Imported file has gsc extension\nbut" - " does not have a correct header"), - wxT("Error"), wxOK|wxICON_ERROR); - break; - case SAVFAIL: - wxMessageBox(wxT("Imported file has sav extension\nbut" - " does not have a correct header"), - wxT("Error"), wxOK|wxICON_ERROR); - break; - case OPENFAIL: - wxMessageBox(wxT("Imported file could not be opened\nor" - " does not have a valid extension"), - wxT("Error"), wxOK|wxICON_ERROR); - break; - case GCS: - wxMessageBox(wxT("File converted to .gci"), - wxT("Success"), wxOK); - break; - case OUTOFBLOCKS: - blocksOpen.Printf(wxT("Only %d blocks available"), memoryCard[slot]->GetFreeBlocks()); - wxMessageBox(blocksOpen, wxT("Error"), wxOK|wxICON_ERROR); - break; - case OUTOFDIRENTRIES: - wxMessageBox(wxT("No free dir index entries"), - wxT("Error"), wxOK|wxICON_ERROR); - break; - case NOMEMCARD: - wxMessageBox(wxT("File is not recognized as a memcard"), - wxT("Error"), wxOK|wxICON_ERROR); - break; - case TITLEPRESENT: - wxMessageBox(wxT("Memcard already has a save for this title"), - wxT("Error"), wxOK|wxICON_ERROR); - break; - case FAIL: - wxMessageBox(wxT("Invalid bat.map or dir entry"), - wxT("Error"), wxOK|wxICON_ERROR); - break; - default: - memoryCard[slot]->Save(); - slot == 1 ? ReloadMemcard(m_Memcard2Path->GetPath().mb_str(), 1, 0) - : ReloadMemcard(m_Memcard1Path->GetPath().mb_str(), 0, 0); - break; - } - } - } - else - { - wxMessageBox(wxT("Memory card is not loaded"), wxT("Error"), wxOK|wxICON_ERROR); - } - break; - case ID_SAVEEXPORTLEFT: - slot=0; - index2 = index0; - case ID_SAVEEXPORTRIGHT: - if (index2 != -1) - { - wxString temp = wxFileSelector(_T("Save GCI as.."), - wxEmptyString, wxEmptyString, _T(".gci"), wxString::Format - ( - _T("GCI File(*.gci)|*.gci"), - wxFileSelectorDefaultWildcardStr, - wxFileSelectorDefaultWildcardStr - ), - wxFD_OVERWRITE_PROMPT|wxFD_SAVE); - const char * fileName = temp.ToAscii(); - - if (temp.length() > 0) - { - switch (memoryCard[slot]->ExportGci(index2, fileName)) - { - case NOMEMCARD: - wxMessageBox(wxT("File is not recognized as a memcard"), - wxT("Error"), wxOK|wxICON_ERROR); - break; - case NOFILE: - wxMessageBox(wxT("Could not open gci for writing"), - wxT("Error"), wxOK|wxICON_ERROR); - break; - case FAIL: - //TODO: delete file if fails - wxMessageBox(wxT("Invalid bat.map or dir entry"), - wxT("Error"), wxOK|wxICON_ERROR); - break; - default: - break; - } - } - } - else - { - wxMessageBox(wxT("You have not selected a save to export"), wxT("Error"), wxOK|wxICON_ERROR); - } - break; - case ID_DELETELEFT: - slot=0; - index2 = index0; - case ID_DELETERIGHT: - if (index2 != -1) - { - switch (memoryCard[slot]->RemoveFile(index2)) - { - case NOMEMCARD: - wxMessageBox(wxT("File is not recognized as a memcard"), - wxT("Error"), wxOK|wxICON_ERROR); - break; - case FAIL: - wxMessageBox(wxT("Invalid bat.map or dir entry"), - wxT("Error"), wxOK|wxICON_ERROR); - break; - case SUCCESS: - memoryCard[slot]->Save(); - slot == 1 ? ReloadMemcard(m_Memcard2Path->GetPath().mb_str(), 1, 0) - : ReloadMemcard(m_Memcard1Path->GetPath().mb_str(), 0, 0); - break; - } - } - else - { - wxMessageBox(wxT("You have not selected a save to delete"), wxT("Error"), wxOK|wxICON_ERROR); - } - break; - } -} - -bool CMemcardManager::ReloadMemcard(const char *fileName, int card, int page) -{ - wxString wxBlock; - wxString wxFirstBlock; - wxString wxLabel; - int j; - - if (memoryCard[card]) delete memoryCard[card]; - - // TODO: add error checking and animate icons - memoryCard[card] = new GCMemcard(fileName); - - if (ReadError(memoryCard[card])) return false; - - m_MemcardList[card]->Hide(); - m_MemcardList[card]->ClearAll(); - m_MemcardList[card]->InsertColumn(COLUMN_BANNER, _T("Banner")); - m_MemcardList[card]->InsertColumn(COLUMN_TITLE, _T("Title")); - m_MemcardList[card]->InsertColumn(COLUMN_COMMENT, _T("Comment")); - m_MemcardList[card]->InsertColumn(COLUMN_ICON, _T("Icon")); - m_MemcardList[card]->InsertColumn(COLUMN_BLOCKS, _T("Blocks")); - m_MemcardList[card]->InsertColumn(COLUMN_FIRSTBLOCK, _T("First Block")); - - wxImageList *list = m_MemcardList[card]->GetImageList(wxIMAGE_LIST_SMALL); - list->RemoveAll(); - - int nFiles = memoryCard[card]->GetNumFiles(); - - int *images = new int[nFiles*2]; - for (int i = 0;i < nFiles;i++) - { - static u32 pxdata[96*32]; - static u8 animDelay[8]; - static u32 animData[32*32*8]; - - int numFrames = memoryCard[card]->ReadAnimRGBA8(i,animData,animDelay); - - if (!memoryCard[card]->ReadBannerRGBA8(i,pxdata)) - { - memset(pxdata,0,96*32*4); - - if (numFrames>0) // Just use the first one - { - u32 *icdata = animData; - - for (int y=0;y<32;y++) - { - for (int x=0;x<32;x++) - { - pxdata[y*96+x+32] = icdata[y*32+x] /* | 0xFF000000 */; - } - } - } - } - - wxBitmap map = wxBitmapFromMemoryRGBA((u8*)pxdata,96,32); - images[i*2] = list->Add(map); - - if (numFrames>0) - { - memset(pxdata,0,96*32*4); - int frames=3; - if (numFramesAdd(icon); - } - } - for (j = page * 16;(j < nFiles) && (j < (page + 1)* 16);j++) - { - char title[32]; - char comment[32]; - u16 blocks; - u16 firstblock; - - if (!memoryCard[card]->GetComment1(j, title)) title[0]=0; - if (!memoryCard[card]->GetComment2(j, comment)) comment[0]=0; - int index = m_MemcardList[card]->InsertItem(j, wxT("row")); - m_MemcardList[card]->SetItem(index, COLUMN_BANNER, wxEmptyString); - m_MemcardList[card]->SetItem(index, COLUMN_TITLE, wxString::FromAscii(title)); - m_MemcardList[card]->SetItem(index, COLUMN_COMMENT, wxString::FromAscii(comment)); - blocks = memoryCard[card]->GetFileSize(j); - if (blocks == 0xFFFF) blocks = 0; - wxBlock.Printf(wxT("%10d"), blocks); - firstblock = memoryCard[card]->GetFirstBlock(j); - if (firstblock == 0xFFFF) firstblock = 3; // to make firstblock -1 - wxFirstBlock.Printf(wxT("%10d"), firstblock-4); - m_MemcardList[card]->SetItem(index,COLUMN_BLOCKS, wxBlock); - m_MemcardList[card]->SetItem(index,COLUMN_FIRSTBLOCK, wxFirstBlock); - m_MemcardList[card]->SetItem(index, COLUMN_ICON, wxEmptyString); - - if (images[j] >= 0) - { - m_MemcardList[card]->SetItemImage(index, images[j*2]); - m_MemcardList[card]->SetItemColumnImage(index, COLUMN_ICON, images[j*2+1]); - } - } - - if (j == nFiles) - { - card ? m_Memcard2NextPage->Disable() : m_Memcard1NextPage->Disable(); - } - else - { - card ? m_Memcard2NextPage->Enable() : m_Memcard1NextPage->Enable(); - } - - delete[] images; - // Automatic column width and then show the list - for (int i = 0; i < m_MemcardList[card]->GetColumnCount(); i++) - { - m_MemcardList[card]->SetColumnWidth(i, wxLIST_AUTOSIZE); - } - m_MemcardList[card]->Show(); - wxLabel.Printf(wxT("%d Free Blocks; %d Free Dir Entries"), - memoryCard[card]->GetFreeBlocks(), 127 - nFiles); - card ? t_StatusRight->SetLabel(wxLabel) : t_StatusLeft->SetLabel(wxLabel); - - return true; -} - -bool CMemcardManager::ReadError(GCMemcard *memcard) -{ - if (!memcard->fail[0]) return false; - wxString wxBlock; - if (memcard->fail[HDR_READ_ERROR]) wxMessageBox(wxT("Failed to read header correctly\n(0x0000-0x1FFF)"), - wxT("Error"), wxOK|wxICON_ERROR); - if (memcard->fail[DIR_READ_ERROR]) wxMessageBox(wxT("Failed to read directory correctly\n(0x2000-0x3FFF)"), - wxT("Error"), wxOK|wxICON_ERROR); - if (memcard->fail[DIR_BAK_READ_ERROR]) wxMessageBox(wxT("Failed to read directory backup correctly\n(0x4000-0x5FFF)"), - wxT("Error"), wxOK|wxICON_ERROR); - if (memcard->fail[BAT_READ_ERROR]) wxMessageBox(wxT("Failed to read block allocation table correctly\n(0x6000-0x7FFF)"), - wxT("Error"), wxOK|wxICON_ERROR); - if (memcard->fail[BAT_BAK_READ_ERROR]) wxMessageBox(wxT("Failed to read block allocation table backup correctly\n(0x8000-0x9FFF)"), - wxT("Error"), wxOK|wxICON_ERROR); - if (memcard->fail[HDR_CSUM_FAIL]) wxMessageBox(wxT("Header checksum failed"), - wxT("Error"), wxOK|wxICON_ERROR); - if (memcard->fail[DIR_CSUM_FAIL]) - { - wxMessageBox(wxT("Directory checksum failed"), - wxT("Error"), wxOK|wxICON_ERROR); - wxMessageBox(wxT("Directory backup checksum failed"), - wxT("Error"), wxOK|wxICON_ERROR); - } - if (memcard->fail[BAT_CSUM_FAIL]) wxMessageBox(wxT("Block Allocation Table checksum failed"), - wxT("Error"), wxOK|wxICON_ERROR); - if (memcard->fail[DATA_READ_FAIL]) wxMessageBox(wxT("Failed to read save data\n(0xA000-)\nMemcard may be truncated"), - wxT("Error"), wxOK|wxICON_ERROR); - if (memcard->fail[HDR_SIZE_FFFF]) wxMessageBox(wxT("Memcard failed to load\n Card size is invalid"), - wxT("Error"), wxOK|wxICON_ERROR); - if (memcard->fail[NOTRAWORGCP]) wxMessageBox(wxT("File does not have a valid extension (.raw/.gcp)"), - wxT("Error"), wxOK|wxICON_ERROR); - return true; -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Globals.h" +#include "MemcardManager.h" + +#include "wx/mstream.h" + +const u8 hdr[] = { +0x42,0x4D, +0x38,0x30,0x00,0x00, +0x00,0x00,0x00,0x00, +0x36,0x00,0x00,0x00, +0x28,0x00,0x00,0x00, +0x20,0x00,0x00,0x00, //W +0x20,0x00,0x00,0x00, //H +0x01,0x00, +0x20,0x00, +0x00,0x00,0x00,0x00, +0x02,0x30,0x00,0x00, //data size +0x12,0x0B,0x00,0x00, +0x12,0x0B,0x00,0x00, +0x00,0x00,0x00,0x00, +0x00,0x00,0x00,0x00 +}; + +wxBitmap wxBitmapFromMemoryRGBA(const unsigned char* data, int width, int height) +{ + int stride = (4*width); + + int bytes = (stride*height) + sizeof(hdr); + + bytes = (bytes+3)&(~3); + + u8 *pdata = new u8[bytes]; + + memset(pdata,0,bytes); + memcpy(pdata,hdr,sizeof(hdr)); + + u8 *pixelData = pdata + sizeof(hdr); + + for (int y=0;yCopy->"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + + m_FixChecksumLeft = new wxButton(this, ID_FIXCHECKSUMLEFT, wxT("<-Fix Checksum"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + m_FixChecksumRight = new wxButton(this, ID_FIXCHECKSUMRIGHT, wxT("Fix Checksum->"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + + m_SaveImportLeft = new wxButton(this, ID_SAVEIMPORTLEFT, wxT("<-Import GCI"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + m_SaveImportRight = new wxButton(this, ID_SAVEIMPORTRIGHT, wxT("Import GCI->"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + + m_SaveExportLeft = new wxButton(this, ID_SAVEEXPORTLEFT, wxT("<-Export GCI"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + m_SaveExportRight = new wxButton(this, ID_SAVEEXPORTRIGHT, wxT("Export GCI->"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + + m_ConvertToGci = new wxButton(this, ID_CONVERTTOGCI, wxT("Convert to GCI"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + + m_DeleteLeft = new wxButton(this, ID_DELETELEFT, wxT("<-Delete"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + m_DeleteRight = new wxButton(this, ID_DELETERIGHT, wxT("Delete->"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + + m_Memcard1PrevPage = new wxButton(this, ID_MEMCARD1PREVPAGE, wxT("Prev Page"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + m_Memcard2PrevPage = new wxButton(this, ID_MEMCARD2PREVPAGE, wxT("Prev Page"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + + m_Memcard1NextPage = new wxButton(this, ID_MEMCARD1NEXTPAGE, wxT("Next Page"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + m_Memcard2NextPage = new wxButton(this, ID_MEMCARD2NEXTPAGE, wxT("Next Page"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + + // Sizers that double as wxStaticBoxes + sMemcard1 = new wxStaticBoxSizer(wxVERTICAL, this, wxT("Memory Card 1")); + sMemcard2 = new wxStaticBoxSizer(wxVERTICAL, this, wxT("Memory Card 2")); + + // Create the controls for both memcards + // Loading invalid .raw files should no longer crash the app + m_Memcard1Path = new wxFilePickerCtrl(this, ID_MEMCARD1PATH, wxEmptyString, wxT("Choose a memory card:"), + wxT("Raw memcards (*.raw)|*.raw"), wxDefaultPosition, wxDefaultSize, wxFLP_USE_TEXTCTRL|wxFLP_FILE_MUST_EXIST|wxFLP_OPEN); + m_Memcard2Path = new wxFilePickerCtrl(this, ID_MEMCARD2PATH, wxEmptyString, wxT("Choose a memory card:"), + wxT("Raw memcards (*.raw)|*.raw"), wxDefaultPosition, wxDefaultSize, wxFLP_USE_TEXTCTRL|wxFLP_FILE_MUST_EXIST|wxFLP_OPEN); + + m_MemcardList[0] = new wxListCtrl(this, ID_MEMCARD1LIST, wxDefaultPosition, wxSize(350,400), + wxLC_REPORT | wxSUNKEN_BORDER | wxLC_ALIGN_LEFT | wxLC_SINGLE_SEL); + m_MemcardList[1] = new wxListCtrl(this, ID_MEMCARD2LIST, wxDefaultPosition, wxSize(350,400), + wxLC_REPORT | wxSUNKEN_BORDER | wxLC_ALIGN_LEFT | wxLC_SINGLE_SEL); + + m_MemcardList[0]->AssignImageList(new wxImageList(96,32),wxIMAGE_LIST_SMALL); + m_MemcardList[1]->AssignImageList(new wxImageList(96,32),wxIMAGE_LIST_SMALL); + + // mmmm sizer goodness + wxBoxSizer* sButtons; + sButtons = new wxBoxSizer(wxVERTICAL); + sButtons->AddStretchSpacer(2); + sButtons->Add(m_CopyToLeft, 0, wxEXPAND, 5); + sButtons->Add(m_CopyToRight, 0, wxEXPAND, 5); + sButtons->AddStretchSpacer(1); + sButtons->Add(m_FixChecksumLeft, 0, wxEXPAND, 5); + sButtons->Add(m_FixChecksumRight, 0, wxEXPAND, 5); + sButtons->AddStretchSpacer(1); + sButtons->Add(m_SaveImportLeft, 0, wxEXPAND, 5); + sButtons->Add(m_SaveExportLeft, 0, wxEXPAND, 5); + sButtons->AddStretchSpacer(1); + sButtons->Add(m_ConvertToGci, 0, wxEXPAND, 5); + sButtons->AddStretchSpacer(1); + sButtons->Add(m_SaveImportRight, 0, wxEXPAND, 5); + sButtons->Add(m_SaveExportRight, 0, wxEXPAND, 5); + sButtons->AddStretchSpacer(1); + sButtons->Add(m_DeleteLeft, 0, wxEXPAND, 5); + sButtons->Add(m_DeleteRight, 0, wxEXPAND, 5); + sButtons->AddStretchSpacer(1); + + sPagesLeft = new wxBoxSizer(wxHORIZONTAL); + sPagesRight = new wxBoxSizer(wxHORIZONTAL); + + sPagesLeft->Add(m_Memcard1PrevPage, 0, wxEXPAND|wxALL, 1); + sPagesLeft->Add(t_StatusLeft,0, wxEXPAND|wxALL, 5); + sPagesLeft->Add(0, 0, 1, wxEXPAND|wxALL, 0); + sPagesLeft->Add(m_Memcard1NextPage, 0, wxEXPAND|wxALL, 1); + sPagesRight->Add(m_Memcard2PrevPage, 0, wxEXPAND|wxALL, 1); + sPagesRight->Add(t_StatusRight, 0, wxEXPAND|wxALL, 5); + sPagesRight->Add(0, 0, 1, wxEXPAND|wxALL, 0); + sPagesRight->Add(m_Memcard2NextPage, 0, wxEXPAND|wxALL, 1); + + sMemcard1->Add(m_Memcard1Path, 0, wxEXPAND|wxALL, 5); + sMemcard1->Add(m_MemcardList[0], 1, wxEXPAND|wxALL, 5); + sMemcard1->Add(sPagesLeft, 0, wxEXPAND|wxALL, 1); + sMemcard2->Add(m_Memcard2Path, 0, wxEXPAND|wxALL, 5); + sMemcard2->Add(m_MemcardList[1], 1, wxEXPAND|wxALL, 5); + sMemcard2->Add(sPagesRight, 0, wxEXPAND|wxALL, 1); + + sMain = new wxBoxSizer(wxHORIZONTAL); + sMain->Add(sMemcard1, 1, wxEXPAND|wxALL, 5); + sMain->Add(sButtons, 0, wxEXPAND, 0); + sMain->Add(sMemcard2, 1, wxEXPAND|wxALL, 5); + + this->SetSizer(sMain); + sMain->SetSizeHints(this); + Fit(); + m_Memcard1PrevPage->Disable(); + m_Memcard1NextPage->Disable(); + m_Memcard2PrevPage->Disable(); + m_Memcard2NextPage->Disable(); + m_CopyToLeft->Disable(); + m_CopyToRight->Disable(); + m_FixChecksumLeft->Disable(); + m_FixChecksumRight->Disable(); + m_SaveImportLeft->Disable(); + m_SaveExportLeft->Disable(); + m_SaveImportRight->Disable(); + m_SaveExportRight->Disable(); + m_DeleteLeft->Disable(); + m_DeleteRight->Disable(); +} + +void CMemcardManager::OnClose(wxCloseEvent& WXUNUSED (event)) +{ + Destroy(); +} + +void CMemcardManager::OnPathChange(wxFileDirPickerEvent& event) +{ + switch (event.GetId()) + { + case ID_MEMCARD1PATH: + page0 = 0; + if (m_Memcard1PrevPage->IsEnabled()) m_Memcard1PrevPage->Disable(); + if (!strcasecmp(m_Memcard1Path->GetPath().mb_str(),m_Memcard2Path->GetPath().mb_str())) + { + wxMessageBox(wxT("Memcard already opened"), wxT("Error"), wxOK|wxICON_ERROR); + m_Memcard1Path->SetPath(wxEmptyString); + m_MemcardList[0]->ClearAll(); + t_StatusLeft->SetLabel(wxEmptyString); + } + else if (ReloadMemcard(event.GetPath().mb_str(), 0, page0)) + { + m_FixChecksumLeft->Enable(); + m_SaveImportLeft->Enable(); + m_SaveExportLeft->Enable(); + m_DeleteLeft->Enable(); + break; + } + m_Memcard1Path->SetPath(wxEmptyString); + m_MemcardList[0]->ClearAll(); + t_StatusLeft->SetLabel(wxEmptyString); + m_FixChecksumLeft->Disable(); + m_SaveImportLeft->Disable(); + m_SaveExportLeft->Disable(); + m_DeleteLeft->Disable(); + m_Memcard1PrevPage->Disable(); + m_Memcard1NextPage->Disable(); + break; + case ID_MEMCARD2PATH: + page1 = 0; + if (m_Memcard2PrevPage->IsEnabled()) m_Memcard2PrevPage->Disable(); + if (!strcasecmp(m_Memcard1Path->GetPath().mb_str(),m_Memcard2Path->GetPath().mb_str())) + { + wxMessageBox(wxT("Memcard already opened"), wxT("Error"), wxOK|wxICON_ERROR); + } + else if (ReloadMemcard(event.GetPath().mb_str(), 1, page1)) + { + m_FixChecksumRight->Enable(); + m_SaveImportRight->Enable(); + m_SaveExportRight->Enable(); + m_DeleteRight->Enable(); + break; + } + m_Memcard2Path->SetPath(wxEmptyString); + m_MemcardList[1]->ClearAll(); + t_StatusRight->SetLabel(wxEmptyString); + m_FixChecksumRight->Disable(); + m_SaveImportRight->Disable(); + m_SaveExportRight->Disable(); + m_DeleteRight->Disable(); + m_Memcard2PrevPage->Disable(); + m_Memcard2NextPage->Disable(); + break; + } + if (m_DeleteRight->IsEnabled() && m_DeleteLeft->IsEnabled()) + { + m_CopyToLeft->Enable(); + m_CopyToRight->Enable(); + } + else + { + m_CopyToLeft->Disable(); + m_CopyToRight->Disable(); + } +} + +void CMemcardManager::OnPageChange(wxCommandEvent& event) +{ + switch (event.GetId()) + { + case ID_MEMCARD1NEXTPAGE: + if (!m_Memcard1PrevPage->IsEnabled()) m_Memcard1PrevPage->Enable(); + if (!m_Memcard1NextPage->IsEnabled()) m_Memcard1NextPage->Enable(); + page0++; + if (page0 == MAXPAGES) m_Memcard1NextPage->Disable(); + ReloadMemcard(m_Memcard1Path->GetPath().mb_str(), 0, page0); + break; + case ID_MEMCARD2NEXTPAGE: + if (!m_Memcard2PrevPage->IsEnabled()) m_Memcard2PrevPage->Enable(); + if (!m_Memcard2NextPage->IsEnabled()) m_Memcard2NextPage->Enable(); + page1++; + if (page1 == MAXPAGES) m_Memcard2NextPage->Disable(); + ReloadMemcard(m_Memcard2Path->GetPath().mb_str(), 1, page1); + break; + case ID_MEMCARD1PREVPAGE: + if (!m_Memcard1NextPage->IsEnabled()) m_Memcard1NextPage->Enable(); + page0--; + if (!page0) m_Memcard1PrevPage->Disable(); + ReloadMemcard(m_Memcard1Path->GetPath().mb_str(), 0, page0); + break; + case ID_MEMCARD2PREVPAGE: + if (!m_Memcard2NextPage->IsEnabled()) m_Memcard2NextPage->Enable(); + page1--; + if (!page1) m_Memcard2PrevPage->Disable(); + ReloadMemcard(m_Memcard2Path->GetPath().mb_str(), 1, page1); + break; + } +} + +void CMemcardManager::CopyDeleteClick(wxCommandEvent& event) +{ + int index0 = m_MemcardList[0]->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + int index1 = m_MemcardList[1]->GetNextItem(-1, wxLIST_NEXT_ALL, wxLIST_STATE_SELECTED); + int slot = 1; + int index2 = index1; + std::string fileName2(""); + wxString blocksOpen; + + if (index0 != -1 && page0) index0 += ITEMSPERPAGE * page0; + if (index1 != -1 && page1) index1 += ITEMSPERPAGE * page1; + + switch (event.GetId()) + { + case ID_COPYTOLEFT: + if ((index1 != -1) && (memoryCard[0] != NULL)) + { + switch (memoryCard[0]->CopyFrom(*memoryCard[1], index1)) + { + case FAIL: + wxMessageBox(wxT("Invalid bat.map or dir entry"), wxT("Failure"), wxOK); + break; + case NOMEMCARD: + wxMessageBox(wxT("File is not recognized as a memcard"), wxT("Failure"), wxOK); + break; + case SUCCESS: + memoryCard[0]->Save(); + ReloadMemcard(m_Memcard1Path->GetPath().mb_str(), 0, 0); + break; + } + + } + else + { + wxMessageBox(wxT("You have not selected a save to copy"), wxT("Error"), wxOK|wxICON_ERROR); + } + break; + case ID_COPYTORIGHT: + if ((index0 != -1) && (memoryCard[1] != NULL)) + { + switch (memoryCard[1]->CopyFrom(*memoryCard[0], index0)) + { + case FAIL: + wxMessageBox(wxT("Invalid bat.map or dir entry"), + wxT("Error"), wxOK|wxICON_ERROR); + break; + case NOMEMCARD: + wxMessageBox(wxT("File is not recognized as a memcard"), + wxT("Error"), wxOK|wxICON_ERROR); + break; + case SUCCESS: + memoryCard[1]->Save(); + ReloadMemcard(m_Memcard2Path->GetPath().mb_str(), 1, 0); + break; + } + } + else + { + wxMessageBox(wxT("You have not selected a save to copy"), wxT("Error"), wxOK|wxICON_ERROR); + } + break; + case ID_FIXCHECKSUMLEFT: + slot = 0; + case ID_FIXCHECKSUMRIGHT: + if (memoryCard[slot] != NULL) + { + // Fix checksums and save the changes + memoryCard[slot]->FixChecksums() ? wxMessageBox(wxT("The checksum was successfully fixed"), wxT("Success"), wxOK) + : wxMessageBox(wxT("The checksum could not be successfully fixed"), wxT("Error"), wxOK|wxICON_ERROR); + memoryCard[slot]->Save(); + } + else + { + wxMessageBox(wxT("memorycard is not loaded"), wxT("Error"), wxOK|wxICON_ERROR); + } + break; + case ID_CONVERTTOGCI: + fileName2 = "convert"; + + case ID_SAVEIMPORTLEFT: + slot = 0; + case ID_SAVEIMPORTRIGHT: + if (memoryCard[slot] != NULL || !fileName2.empty()) + { + wxString temp = wxFileSelector(_T("Select a save file to import"), + wxEmptyString, wxEmptyString, wxEmptyString,wxString::Format + ( + _T("Gamecube save files(*.gci,*.gcs,*.sav)|*.gci;*.gcs;*.sav|" + "Native GCI files (*.gci)|*.gci|" + "MadCatz Gameshark files(*.gcs)|*.gcs|" + "Datel MaxDrive/Pro files(*.sav)|*.sav"), + wxFileSelectorDefaultWildcardStr, + wxFileSelectorDefaultWildcardStr + ), + wxFD_OPEN | wxFD_FILE_MUST_EXIST); + const char * fileName = temp.ToAscii(); + if (!temp.empty() && !fileName2.empty()) + { + wxString temp2 = wxFileSelector(_T("Save GCI as.."), + wxEmptyString, wxEmptyString, _T(".gci"), wxString::Format + ( + _T("GCI File(*.gci)|*.gci"), + wxFileSelectorDefaultWildcardStr, + wxFileSelectorDefaultWildcardStr + ), + wxFD_OVERWRITE_PROMPT|wxFD_SAVE); + if (temp2.empty()) break; + fileName2 = temp2.mb_str(); + } + if (temp.length() > 0) + { + switch(memoryCard[slot]->ImportGci(fileName, fileName2)) + { + case LENGTHFAIL: + wxMessageBox(wxT("Imported file has invalid length"), + wxT("Error"), wxOK|wxICON_ERROR); + break; + case GCSFAIL: + wxMessageBox(wxT("Imported file has gsc extension\nbut" + " does not have a correct header"), + wxT("Error"), wxOK|wxICON_ERROR); + break; + case SAVFAIL: + wxMessageBox(wxT("Imported file has sav extension\nbut" + " does not have a correct header"), + wxT("Error"), wxOK|wxICON_ERROR); + break; + case OPENFAIL: + wxMessageBox(wxT("Imported file could not be opened\nor" + " does not have a valid extension"), + wxT("Error"), wxOK|wxICON_ERROR); + break; + case GCS: + wxMessageBox(wxT("File converted to .gci"), + wxT("Success"), wxOK); + break; + case OUTOFBLOCKS: + blocksOpen.Printf(wxT("Only %d blocks available"), memoryCard[slot]->GetFreeBlocks()); + wxMessageBox(blocksOpen, wxT("Error"), wxOK|wxICON_ERROR); + break; + case OUTOFDIRENTRIES: + wxMessageBox(wxT("No free dir index entries"), + wxT("Error"), wxOK|wxICON_ERROR); + break; + case NOMEMCARD: + wxMessageBox(wxT("File is not recognized as a memcard"), + wxT("Error"), wxOK|wxICON_ERROR); + break; + case TITLEPRESENT: + wxMessageBox(wxT("Memcard already has a save for this title"), + wxT("Error"), wxOK|wxICON_ERROR); + break; + case FAIL: + wxMessageBox(wxT("Invalid bat.map or dir entry"), + wxT("Error"), wxOK|wxICON_ERROR); + break; + default: + memoryCard[slot]->Save(); + slot == 1 ? ReloadMemcard(m_Memcard2Path->GetPath().mb_str(), 1, 0) + : ReloadMemcard(m_Memcard1Path->GetPath().mb_str(), 0, 0); + break; + } + } + } + else + { + wxMessageBox(wxT("Memory card is not loaded"), wxT("Error"), wxOK|wxICON_ERROR); + } + break; + case ID_SAVEEXPORTLEFT: + slot=0; + index2 = index0; + case ID_SAVEEXPORTRIGHT: + if (index2 != -1) + { + wxString temp = wxFileSelector(_T("Save GCI as.."), + wxEmptyString, wxEmptyString, _T(".gci"), wxString::Format + ( + _T("GCI File(*.gci)|*.gci"), + wxFileSelectorDefaultWildcardStr, + wxFileSelectorDefaultWildcardStr + ), + wxFD_OVERWRITE_PROMPT|wxFD_SAVE); + const char * fileName = temp.ToAscii(); + + if (temp.length() > 0) + { + switch (memoryCard[slot]->ExportGci(index2, fileName)) + { + case NOMEMCARD: + wxMessageBox(wxT("File is not recognized as a memcard"), + wxT("Error"), wxOK|wxICON_ERROR); + break; + case NOFILE: + wxMessageBox(wxT("Could not open gci for writing"), + wxT("Error"), wxOK|wxICON_ERROR); + break; + case FAIL: + //TODO: delete file if fails + wxMessageBox(wxT("Invalid bat.map or dir entry"), + wxT("Error"), wxOK|wxICON_ERROR); + break; + default: + break; + } + } + } + else + { + wxMessageBox(wxT("You have not selected a save to export"), wxT("Error"), wxOK|wxICON_ERROR); + } + break; + case ID_DELETELEFT: + slot=0; + index2 = index0; + case ID_DELETERIGHT: + if (index2 != -1) + { + switch (memoryCard[slot]->RemoveFile(index2)) + { + case NOMEMCARD: + wxMessageBox(wxT("File is not recognized as a memcard"), + wxT("Error"), wxOK|wxICON_ERROR); + break; + case FAIL: + wxMessageBox(wxT("Invalid bat.map or dir entry"), + wxT("Error"), wxOK|wxICON_ERROR); + break; + case SUCCESS: + memoryCard[slot]->Save(); + slot == 1 ? ReloadMemcard(m_Memcard2Path->GetPath().mb_str(), 1, 0) + : ReloadMemcard(m_Memcard1Path->GetPath().mb_str(), 0, 0); + break; + } + } + else + { + wxMessageBox(wxT("You have not selected a save to delete"), wxT("Error"), wxOK|wxICON_ERROR); + } + break; + } +} + +bool CMemcardManager::ReloadMemcard(const char *fileName, int card, int page) +{ + wxString wxBlock; + wxString wxFirstBlock; + wxString wxLabel; + int j; + + if (memoryCard[card]) delete memoryCard[card]; + + // TODO: add error checking and animate icons + memoryCard[card] = new GCMemcard(fileName); + + if (ReadError(memoryCard[card])) return false; + + m_MemcardList[card]->Hide(); + m_MemcardList[card]->ClearAll(); + m_MemcardList[card]->InsertColumn(COLUMN_BANNER, _T("Banner")); + m_MemcardList[card]->InsertColumn(COLUMN_TITLE, _T("Title")); + m_MemcardList[card]->InsertColumn(COLUMN_COMMENT, _T("Comment")); + m_MemcardList[card]->InsertColumn(COLUMN_ICON, _T("Icon")); + m_MemcardList[card]->InsertColumn(COLUMN_BLOCKS, _T("Blocks")); + m_MemcardList[card]->InsertColumn(COLUMN_FIRSTBLOCK, _T("First Block")); + + wxImageList *list = m_MemcardList[card]->GetImageList(wxIMAGE_LIST_SMALL); + list->RemoveAll(); + + int nFiles = memoryCard[card]->GetNumFiles(); + + int *images = new int[nFiles*2]; + for (int i = 0;i < nFiles;i++) + { + static u32 pxdata[96*32]; + static u8 animDelay[8]; + static u32 animData[32*32*8]; + + int numFrames = memoryCard[card]->ReadAnimRGBA8(i,animData,animDelay); + + if (!memoryCard[card]->ReadBannerRGBA8(i,pxdata)) + { + memset(pxdata,0,96*32*4); + + if (numFrames>0) // Just use the first one + { + u32 *icdata = animData; + + for (int y=0;y<32;y++) + { + for (int x=0;x<32;x++) + { + pxdata[y*96+x+32] = icdata[y*32+x] /* | 0xFF000000 */; + } + } + } + } + + wxBitmap map = wxBitmapFromMemoryRGBA((u8*)pxdata,96,32); + images[i*2] = list->Add(map); + + if (numFrames>0) + { + memset(pxdata,0,96*32*4); + int frames=3; + if (numFramesAdd(icon); + } + } + for (j = page * 16;(j < nFiles) && (j < (page + 1)* 16);j++) + { + char title[32]; + char comment[32]; + u16 blocks; + u16 firstblock; + + if (!memoryCard[card]->GetComment1(j, title)) title[0]=0; + if (!memoryCard[card]->GetComment2(j, comment)) comment[0]=0; + int index = m_MemcardList[card]->InsertItem(j, wxT("row")); + m_MemcardList[card]->SetItem(index, COLUMN_BANNER, wxEmptyString); + m_MemcardList[card]->SetItem(index, COLUMN_TITLE, wxString::FromAscii(title)); + m_MemcardList[card]->SetItem(index, COLUMN_COMMENT, wxString::FromAscii(comment)); + blocks = memoryCard[card]->GetFileSize(j); + if (blocks == 0xFFFF) blocks = 0; + wxBlock.Printf(wxT("%10d"), blocks); + firstblock = memoryCard[card]->GetFirstBlock(j); + if (firstblock == 0xFFFF) firstblock = 3; // to make firstblock -1 + wxFirstBlock.Printf(wxT("%10d"), firstblock-4); + m_MemcardList[card]->SetItem(index,COLUMN_BLOCKS, wxBlock); + m_MemcardList[card]->SetItem(index,COLUMN_FIRSTBLOCK, wxFirstBlock); + m_MemcardList[card]->SetItem(index, COLUMN_ICON, wxEmptyString); + + if (images[j] >= 0) + { + m_MemcardList[card]->SetItemImage(index, images[j*2]); + m_MemcardList[card]->SetItemColumnImage(index, COLUMN_ICON, images[j*2+1]); + } + } + + if (j == nFiles) + { + card ? m_Memcard2NextPage->Disable() : m_Memcard1NextPage->Disable(); + } + else + { + card ? m_Memcard2NextPage->Enable() : m_Memcard1NextPage->Enable(); + } + + delete[] images; + // Automatic column width and then show the list + for (int i = 0; i < m_MemcardList[card]->GetColumnCount(); i++) + { + m_MemcardList[card]->SetColumnWidth(i, wxLIST_AUTOSIZE); + } + m_MemcardList[card]->Show(); + wxLabel.Printf(wxT("%d Free Blocks; %d Free Dir Entries"), + memoryCard[card]->GetFreeBlocks(), 127 - nFiles); + card ? t_StatusRight->SetLabel(wxLabel) : t_StatusLeft->SetLabel(wxLabel); + + return true; +} + +bool CMemcardManager::ReadError(GCMemcard *memcard) +{ + if (!memcard->fail[0]) return false; + wxString wxBlock; + if (memcard->fail[HDR_READ_ERROR]) wxMessageBox(wxT("Failed to read header correctly\n(0x0000-0x1FFF)"), + wxT("Error"), wxOK|wxICON_ERROR); + if (memcard->fail[DIR_READ_ERROR]) wxMessageBox(wxT("Failed to read directory correctly\n(0x2000-0x3FFF)"), + wxT("Error"), wxOK|wxICON_ERROR); + if (memcard->fail[DIR_BAK_READ_ERROR]) wxMessageBox(wxT("Failed to read directory backup correctly\n(0x4000-0x5FFF)"), + wxT("Error"), wxOK|wxICON_ERROR); + if (memcard->fail[BAT_READ_ERROR]) wxMessageBox(wxT("Failed to read block allocation table correctly\n(0x6000-0x7FFF)"), + wxT("Error"), wxOK|wxICON_ERROR); + if (memcard->fail[BAT_BAK_READ_ERROR]) wxMessageBox(wxT("Failed to read block allocation table backup correctly\n(0x8000-0x9FFF)"), + wxT("Error"), wxOK|wxICON_ERROR); + if (memcard->fail[HDR_CSUM_FAIL]) wxMessageBox(wxT("Header checksum failed"), + wxT("Error"), wxOK|wxICON_ERROR); + if (memcard->fail[DIR_CSUM_FAIL]) + { + wxMessageBox(wxT("Directory checksum failed"), + wxT("Error"), wxOK|wxICON_ERROR); + wxMessageBox(wxT("Directory backup checksum failed"), + wxT("Error"), wxOK|wxICON_ERROR); + } + if (memcard->fail[BAT_CSUM_FAIL]) wxMessageBox(wxT("Block Allocation Table checksum failed"), + wxT("Error"), wxOK|wxICON_ERROR); + if (memcard->fail[DATA_READ_FAIL]) wxMessageBox(wxT("Failed to read save data\n(0xA000-)\nMemcard may be truncated"), + wxT("Error"), wxOK|wxICON_ERROR); + if (memcard->fail[HDR_SIZE_FFFF]) wxMessageBox(wxT("Memcard failed to load\n Card size is invalid"), + wxT("Error"), wxOK|wxICON_ERROR); + if (memcard->fail[NOTRAWORGCP]) wxMessageBox(wxT("File does not have a valid extension (.raw/.gcp)"), + wxT("Error"), wxOK|wxICON_ERROR); + return true; +} diff --git a/Source/Core/DolphinWX/Src/MemoryCards/GCMemcard.cpp b/Source/Core/DolphinWX/Src/MemoryCards/GCMemcard.cpp index 18d5932436..e41e964a3e 100644 --- a/Source/Core/DolphinWX/Src/MemoryCards/GCMemcard.cpp +++ b/Source/Core/DolphinWX/Src/MemoryCards/GCMemcard.cpp @@ -1,1002 +1,1002 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#ifdef _WIN32 -#include "stdafx.h" -#endif -#include -#include - -#include "GCMemcard.h" - - -// i think there is support for this stuff in the common lib... if not there should be support -#define BE16(x) ((u16((x)[0])<<8) | u16((x)[1])) -#define BE32(x) ((u32((x)[0])<<24) | (u32((x)[1])<<16) | (u32((x)[2])<<8) | u32((x)[3])) -#define ArrayByteSwap(a) (ByteSwap(a, a+sizeof(u8))); - -// undefined functions... prolly it means something like that -void ByteSwap(u8 *valueA, u8 *valueB) -{ - u8 tmp = *valueA; - *valueA = *valueB; - *valueB = tmp; -} - -u16 __inline bswap16(u16 s) -{ - return (s>>8) | (s<<8); -} - -u32 __inline bswap32(u32 s) -{ - return (u32)bswap16((u16)(s>>16)) | ((u32)bswap16((u16)s)<<16); -} - - - - - -void GCMemcard::calc_checksumsBE(u16 *buf, u32 num, u16 *c1, u16 *c2) -{ - *c1 = 0;*c2 = 0; - for (u32 i = 0; i < num; ++i) - { - //weird warnings here - *c1 += bswap16(buf[i]); - *c2 += bswap16((u16)(buf[i] ^ 0xffff)); - } - if (*c1 == 0xffff) - { - *c1 = 0; - } - if (*c2 == 0xffff) - { - *c2 = 0; - } -} - -u16 GCMemcard::GetFreeBlocks() -{ - if (!mcdFile) return 0; - return BE16(bat.FreeBlocks); -} - -u32 GCMemcard::GetNumFiles() -{ - if (!mcdFile) return 0; - int j = 0; - for (int i = 0; i < 127; i++) - { - if (BE32(dir.Dir[i].Gamecode)!= 0xFFFFFFFF) - j++; - } - return j; -} - -bool GCMemcard::TitlePresent(DEntry d) -{ - //TODO: Clean up this function - bool equal = false; - if (!mcdFile) return false; - - for (int i = 0; i < 127; i++) - { - if (BE32(dir.Dir[i].Gamecode) == BE32(d.Gamecode)) - { - for ( int j = 0; j < 32; j++) - { - if (dir.Dir[i].Filename[j] != d.Filename[j]) - { - equal = false; - break; - } - else - { - equal = true; - } - } - if (equal) - { - return true; - } - } - } - return false; -} - -u16 GCMemcard::GetFirstBlock(u32 index) -{ - if (!mcdFile) return 0xFFFF; - u16 block = BE16(dir.Dir[index].FirstBlock); - if (block > MAXBLOCK) return 0xFFFF; - return block; -} - -u32 GCMemcard::RemoveFile(u32 index) //index in the directory array -{ - if (!mcdFile) return NOMEMCARD; - - //backup the directory and bat (not really needed here but meh :P - dir_backup = dir; - bat_backup = bat; - - //free the blocks - int blocks_left = BE16(dir.Dir[index].BlockCount); - int block = BE16(dir.Dir[index].FirstBlock) - 1; - bat.LastAllocated[0] = (u8)(block >> 8); - bat.LastAllocated[1] = (u8)block; - - int i = index + 1; - memset(&(dir.Dir[index]), 0xFF, 0x40); - - while (i < 127) - { - DEntry * d = new DEntry; - GetFileInfo(i, *d); - u8 *t = NULL; - //Only get file data if it is a valid dir entry - if (BE16(d->FirstBlock) != 0xFFFF) - { - u16 freeBlock= BE16(bat.FreeBlocks) - BE16(d->BlockCount); - bat.FreeBlocks[0] = u8(freeBlock >> 8); - bat.FreeBlocks[1] = u8(freeBlock); - - u16 size = GetFileSize(i); - if (size != 0xFFFF) - { - t = new u8[size * 0x2000]; - switch (GetFileData(i, t, true)) - { - case NOMEMCARD: - delete[] t; - break; - case FAIL: - delete[] t; - return FAIL; - break; - } - } - } - memset(&(dir.Dir[i]), 0xFF, 0x40); - //Only call import file if Get File Data returns true - if (t) - { - ImportFile(*d, t, blocks_left); - delete[] t; - } - delete d; - i++; - - } - //Added to clean up if removing last file - if (BE16(bat.LastAllocated) == (u16)4) - { - for (int j = 0; j < blocks_left; j++) - { - bat.Map[j] = 0x0000; - } - } - // increment update counter - int updateCtr = BE16(dir.UpdateCounter) + 1; - dir.UpdateCounter[0] = u8(updateCtr >> 8); - dir.UpdateCounter[1] = u8(updateCtr); - - - FixChecksums(); - Save(); - - return SUCCESS; -} - -u32 GCMemcard::ImportFile(DEntry& direntry, u8* contents, int remove) -{ - if (!mcdFile) return NOMEMCARD; - - if (GetNumFiles() >= 127) - { - return OUTOFDIRENTRIES; - } - if (BE16(bat.FreeBlocks) < BE16(direntry.BlockCount)) - { - return OUTOFBLOCKS; - } - if (!remove && TitlePresent(direntry)) return TITLEPRESENT; - - // find first free data block -- assume no freespace fragmentation - int totalspace = (((u32)BE16(hdr.Size) * 16) - 5); - - int firstFree1 = BE16(bat.LastAllocated) + 1; - int firstFree2 = 0; - for (int i = 0; i < 126; i++) - { - if (BE32(dir.Dir[i].Gamecode) == 0xFFFFFFFF) - { - break; - } - else - { - firstFree2 = max(firstFree2, - (int)(BE16(dir.Dir[i].FirstBlock) + BE16(dir.Dir[i].BlockCount))); - } - } - firstFree1 = max(firstFree1, firstFree2); - - // find first free dir entry - int index = -1; - for (int i=0; i < 127; i++) - { - if (BE32(dir.Dir[i].Gamecode) == 0xFFFFFFFF) - { - index = i; - dir.Dir[i] = direntry; - dir.Dir[i].FirstBlock[0] = u8(firstFree1 >> 8); - dir.Dir[i].FirstBlock[1] = u8(firstFree1); - if (!remove) - { - dir.Dir[i].CopyCounter = dir.Dir[i].CopyCounter+1; - } - dir_backup = dir; - break; - } - } - - // keep assuming no freespace fragmentation, and copy over all the data - u8*destination = mc_data + (firstFree1 - 5) * 0x2000; - - int fileBlocks = BE16(direntry.BlockCount); - memcpy(destination, contents, 0x2000 * fileBlocks); - bat_backup = bat; - u16 last = BE16(bat_backup.LastAllocated); - u16 i = (last - 4); - int j = 2; - while(j < BE16(direntry.BlockCount) + 1) - { - bat_backup.Map[i] = bswap16(last + (u16)j); - i++; - j++; - } - bat_backup.Map[i++] = 0xFFFF; - //Set bat.map to 0 for each block that was removed - for (int k = 0; k < remove; k++) - { - bat_backup.Map[i++] = 0x0000; - } - - //update last allocated block - int lastallocated = BE16(bat_backup.LastAllocated) + j - 1; - bat_backup.LastAllocated[0] = u8(lastallocated >> 8); - bat_backup.LastAllocated[1] = u8(lastallocated); - - //update freespace counter - int freespace1 = totalspace - firstFree1 - fileBlocks + 5; - bat_backup.FreeBlocks[0] = u8(freespace1 >> 8); - bat_backup.FreeBlocks[1] = u8(freespace1); - - - if (!remove) - { // ... and dir update counter - int updateCtr = BE16(dir_backup.UpdateCounter) + 1; - dir_backup.UpdateCounter[0] = u8(updateCtr>>8); - dir_backup.UpdateCounter[1] = u8(updateCtr); - // ... and bat update counter - updateCtr = BE16(bat_backup.UpdateCounter) + 1; - bat_backup.UpdateCounter[0] = u8(updateCtr>>8); - bat_backup.UpdateCounter[1] = u8(updateCtr); - } - bat = bat_backup; - - if (!remove) - { - FixChecksums(); - Save(); - } - return fileBlocks; -} - -u32 GCMemcard::GetFileData(u32 index, u8* dest, bool old) //index in the directory array -{ - if (!mcdFile) return NOMEMCARD; - - u16 block = BE16(dir.Dir[index].FirstBlock); - u16 saveLength = BE16(dir.Dir[index].BlockCount); - u16 memcardSize = BE16(hdr.Size) * 0x0010; - - if ((block == 0xFFFF) || (saveLength == 0xFFFF) - || (block + saveLength > memcardSize)) - { - return FAIL; - } - - if (!old) - { - memcpy(dest,mc_data + 0x2000*(block-5), saveLength*0x2000); - } - else - { - assert(block > 0); - while (block != 0xffff) - { - memcpy(dest,mc_data + 0x2000 * (block - 5), 0x2000); - dest+=0x2000; - - u16 nextblock = bswap16(bat.Map[block - 5]); - if (block + saveLength != memcardSize && nextblock > 0) - {//Fixes for older memcards that were not initialized with FF - block = nextblock; - } - else break; - } - } - return SUCCESS; -} - -u16 GCMemcard::GetFileSize(u32 index) //index in the directory array -{ - if (!mcdFile) return 0xFFFF; - - u16 blocks = BE16(dir.Dir[index].BlockCount); - if (blocks > (u16) MAXBLOCK) return 0xFFFF; - return blocks; -} - -bool GCMemcard::GetFileInfo(u32 index, GCMemcard::DEntry& info) //index in the directory array -{ - if (!mcdFile) return false; - - info = dir.Dir[index]; - return true; -} - -bool GCMemcard::GetFileName(u32 index, char *fn) //index in the directory array -{ - if (!mcdFile) return false; - - memcpy (fn, (const char*)dir.Dir[index].Filename, 32); - fn[31] = 0; - return true; -} - -bool GCMemcard::GetComment1(u32 index, char *fn) //index in the directory array -{ - if (!mcdFile) return false; - - u32 Comment1 = BE32(dir.Dir[index].CommentsAddr); - u32 DataBlock = BE16(dir.Dir[index].FirstBlock) - 5; - if ((DataBlock > MAXBLOCK) || (Comment1 == 0xFFFFFFFF)) - { - fn[0] = 0; - return false; - } - memcpy(fn, mc_data + (DataBlock * 0x2000) + Comment1, 32); - fn[31] = 0; - return true; -} - -bool GCMemcard::GetComment2(u32 index, char *fn) //index in the directory array -{ - if (!mcdFile) return false; - - u32 Comment1 = BE32(dir.Dir[index].CommentsAddr); - u32 Comment2 = Comment1 + 32; - u32 DataBlock = BE16(dir.Dir[index].FirstBlock) - 5; - if ((DataBlock > MAXBLOCK) || (Comment1 == 0xFFFFFFFF)) - { - fn[0] = 0; - return false; - } - memcpy(fn, mc_data + (DataBlock * 0x2000) + Comment2, 32); - fn[31] = 0; - return true; -} - -u32 decode5A3(u16 val) -{ - const int lut5to8[] = { 0x00,0x08,0x10,0x18,0x20,0x29,0x31,0x39, - 0x41,0x4A,0x52,0x5A,0x62,0x6A,0x73,0x7B, - 0x83,0x8B,0x94,0x9C,0xA4,0xAC,0xB4,0xBD, - 0xC5,0xCD,0xD5,0xDE,0xE6,0xEE,0xF6,0xFF}; - const int lut4to8[] = { 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77, - 0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF}; - const int lut3to8[] = { 0x00,0x24,0x48,0x6D,0x91,0xB6,0xDA,0xFF}; - - int r,g,b,a; - if ((val&0x8000)) - { - r=lut5to8[(val>>10) & 0x1f]; - g=lut5to8[(val>>5 ) & 0x1f]; - b=lut5to8[(val ) & 0x1f]; - a=0xFF; - } - else - { - a=lut3to8[(val>>12) & 0x7]; - r=lut4to8[(val>>8 ) & 0xf]; - g=lut4to8[(val>>4 ) & 0xf]; - b=lut4to8[(val ) & 0xf]; - } - return (a<<24) | (r<<16) | (g<<8) | b; -} - -void decode5A3image(u32* dst, u16* src, int width, int height) -{ - for (int y = 0; y < height; y += 4) - { - for (int x = 0; x < width; x += 4) - { - for (int iy = 0; iy < 4; iy++, src += 4) - { - for (int ix = 0; ix < 4; ix++) - { - u32 RGBA = decode5A3(bswap16(src[ix])); - dst[(y + iy) * width + (x + ix)] = RGBA; - } - } - } - } -} - -void decodeCI8image(u32* dst, u8* src, u16* pal, int width, int height) -{ - for (int y = 0; y < height; y += 4) - { - for (int x = 0; x < width; x += 8) - { - for (int iy = 0; iy < 4; iy++, src += 8) - { - u32 *tdst = dst+(y+iy)*width+x; - for (int ix = 0; ix < 8; ix++) - { - tdst[ix] = decode5A3(bswap16(pal[src[ix]])); - } - } - } - } -} - -bool GCMemcard::ReadBannerRGBA8(u32 index, u32* buffer) -{ - if (!mcdFile) return false; - - int flags = dir.Dir[index].BIFlags; - - int bnrFormat = (flags&3); - - if (bnrFormat == 0) - return false; - - u32 DataOffset = BE32(dir.Dir[index].ImageOffset); - u32 DataBlock = BE16(dir.Dir[index].FirstBlock) - 5; - - if ((DataBlock > MAXBLOCK) || (DataOffset == 0xFFFFFFFF)) - { - return false; - } - - const int pixels = 96*32; - - if (bnrFormat&1) - { - u8 *pxdata = (u8* )(mc_data +(DataBlock*0x2000) + DataOffset); - u16 *paldata = (u16*)(mc_data +(DataBlock*0x2000) + DataOffset + pixels); - - decodeCI8image(buffer, pxdata, paldata, 96, 32); - } - else - { - u16 *pxdata = (u16*)(mc_data +(DataBlock*0x2000) + DataOffset); - - decode5A3image(buffer, pxdata, 96, 32); - } - return true; -} - -u32 GCMemcard::ReadAnimRGBA8(u32 index, u32* buffer, u8 *delays) -{ - if (!mcdFile) return 0; - - int formats = BE16(dir.Dir[index].IconFmt); - int fdelays = BE16(dir.Dir[index].AnimSpeed); - - int flags = dir.Dir[index].BIFlags; - - int bnrFormat = (flags&3); - - u32 DataOffset = BE32(dir.Dir[index].ImageOffset); - u32 DataBlock = BE16(dir.Dir[index].FirstBlock) - 5; - - if ((DataBlock > MAXBLOCK) || (DataOffset == 0xFFFFFFFF)) - { - return 0; - } - - u8* animData = (u8*)(mc_data +(DataBlock*0x2000) + DataOffset); - - switch (bnrFormat) - { - case 1: - case 3: - animData += 96*32 + 2*256; // image+palette - break; - case 2: - animData += 96*32*2; - break; - } - - int fmts[8]; - u8* data[8]; - int frames = 0; - - - for (int i = 0; i < 8; i++) - { - fmts[i] = (formats >> (2*i))&3; - delays[i] = ((fdelays >> (2*i))&3) << 2; - data[i] = animData; - - switch (fmts[i]) - { - case 1: // CI8 with shared palette - animData += 32*32; - frames++; - break; - case 2: // RGB5A3 - animData += 32*32*2; - frames++; - break; - case 3: // CI8 with own palette - animData += 32*32 + 2*256; - frames++; - break; - } - } - - u16* sharedPal = (u16*)(animData); - - for (int i = 0; i < 8; i++) - { - switch (fmts[i]) - { - case 1: // CI8 with shared palette - decodeCI8image(buffer,data[i],sharedPal,32,32); - buffer += 32*32; - break; - case 2: // RGB5A3 - decode5A3image(buffer, (u16*)(data[i]), 32, 32); - break; - case 3: // CI8 with own palette - u16 *paldata = (u16*)(data[i] + 32*32); - decodeCI8image(buffer, data[i], paldata, 32, 32); - buffer += 32*32; - break; - } - } - - return frames; -} - -u32 GCMemcard::TestChecksums() -{ - if (!mcdFile) return 0xFFFFFFFF; - - u16 csum1=0, - csum2=0; - - u32 results = 0; - - calc_checksumsBE((u16*)&hdr, 0xFE , &csum1, &csum2); - if (BE16(hdr.CheckSum1) != csum1) results |= 1; - if (BE16(hdr.CheckSum2) != csum2) results |= 1; - - calc_checksumsBE((u16*)&dir, 0xFFE, &csum1, &csum2); - if (BE16(dir.CheckSum1) != csum1) results |= 2; - if (BE16(dir.CheckSum2) != csum2) results |= 2; - - calc_checksumsBE((u16*)&dir_backup, 0xFFE, &csum1, &csum2); - if (BE16(dir_backup.CheckSum1) != csum1) results |= 4; - if (BE16(dir_backup.CheckSum2) != csum2) results |= 4; - - calc_checksumsBE((u16*)(((u8*)&bat)+4), 0xFFE, &csum1, &csum2); - if (BE16(bat.CheckSum1) != csum1) results |= 8; - if (BE16(bat.CheckSum2) != csum2) results |= 8; - - calc_checksumsBE((u16*)(((u8*)&bat_backup)+4), 0xFFE, &csum1, &csum2); - if (BE16(bat_backup.CheckSum1) != csum1) results |= 16; - if (BE16(bat_backup.CheckSum2) != csum2) results |= 16; - - return 0; -} - -bool GCMemcard::FixChecksums() -{ - if (!mcdFile) return false; - - u16 csum1=0, - csum2=0; - - calc_checksumsBE((u16*)&hdr, 0xFE, &csum1, &csum2); - hdr.CheckSum1[0] = u8(csum1 >> 8); - hdr.CheckSum1[1] = u8(csum1); - hdr.CheckSum2[0] = u8(csum2 >> 8); - hdr.CheckSum2[1] = u8(csum2); - - calc_checksumsBE((u16*)&dir, 0xFFE, &csum1, &csum2); - dir.CheckSum1[0] = u8(csum1 >> 8); - dir.CheckSum1[1] = u8(csum1); - dir.CheckSum2[0] = u8(csum2 >> 8); - dir.CheckSum2[1] = u8(csum2); - - calc_checksumsBE((u16*)&dir_backup, 0xFFE, &csum1, &csum2); - dir_backup.CheckSum1[0] = u8(csum1 >> 8); - dir_backup.CheckSum1[1] = u8(csum1); - dir_backup.CheckSum2[0] = u8(csum2 >> 8); - dir_backup.CheckSum2[1] = u8(csum2); - - calc_checksumsBE((u16*)(((u8*)&bat)+4), 0xFFE, &csum1, &csum2); - bat.CheckSum1[0] = u8(csum1 >> 8); - bat.CheckSum1[1] = u8(csum1); - bat.CheckSum2[0] = u8(csum2 >> 8); - bat.CheckSum2[1] = u8(csum2); - - calc_checksumsBE((u16*)(((u8*)&bat_backup)+4), 0xFFE, &csum1, &csum2); - bat_backup.CheckSum1[0] = u8(csum1 >> 8); - bat_backup.CheckSum1[1] = u8(csum1); - bat_backup.CheckSum2[0] = u8(csum2 >> 8); - bat_backup.CheckSum2[1] = u8(csum2); - - return true; -} -u32 GCMemcard::CopyFrom(GCMemcard& source, u32 index) -{ - if (!mcdFile) return NOMEMCARD; - - DEntry d; - if (!source.GetFileInfo(index, d)) return 0; - - u32 size = source.GetFileSize(index); - if (size == 0xFFFF) return 0; - u8 *t = new u8[size * 0x2000]; - - switch (source.GetFileData(index, t, true)) - { - case FAIL: - delete[] t; - return FAIL; - case NOMEMCARD: - delete[] t; - return NOMEMCARD; - default: - break; - } - - u32 ret = ImportFile(d,t,0); - - delete[] t; - - return ret; -} - -s32 GCMemcard::ImportGci(const char *fileName, std::string fileName2) -{ - if (fileName2.empty() && !mcdFile) return OPENFAIL; - - FILE *gci = fopen(fileName, "rb"); - if (!gci) return OPENFAIL; - - int offset; - char * tmp = new char[0xD]; - std::string fileType; - SplitPath(fileName, NULL, NULL, &fileType); - - if( !strcasecmp(fileType.c_str(), ".gci")) - offset = GCI; - else - { - fread(tmp, 1, 0xD, gci); - if (!strcasecmp(fileType.c_str(), ".gcs")) - { - if (!memcmp(tmp, "GCSAVE", 6)) // Header must be uppercase - offset = GCS; - else - { - return GCSFAIL; - } - } - else{ - if (!strcasecmp(fileType.c_str(), ".sav")) - { - if (!memcmp(tmp, "DATELGC_SAVE", 0xC)) // Header must be uppercase - offset = SAV; - else - { - return SAVFAIL; - } - } - else - { - return OPENFAIL; - } - } - } - fseek(gci, offset, SEEK_SET); - - DEntry *d = new DEntry; - fread(d, 1, 0x40, gci); - int fStart = (int) ftell(gci); - fseek(gci, 0, SEEK_END); - int length = (int) ftell(gci) - fStart; - fseek(gci, offset + 0x40, SEEK_SET); - - switch(offset){ - case GCS: - { // field containing the Block count as displayed within - // the GameSaves software is not stored in the GCS file. - // It is stored only within the corresponding GSV file. - // If the GCS file is added without using the GameSaves software, - // the value stored is always "1" - int blockCount = length / 0x2000; - d->BlockCount[0] = u8(blockCount >> 8); - d->BlockCount[1] = u8(blockCount); - } - break; - case SAV: - // swap byte pairs - // 0x2C and 0x2D, 0x2E and 0x2F, 0x30 and 0x31, 0x32 and 0x33, - // 0x34 and 0x35, 0x36 and 0x37, 0x38 and 0x39, 0x3A and 0x3B, - // 0x3C and 0x3D,0x3E and 0x3F. - ArrayByteSwap((d->ImageOffset)); - ArrayByteSwap(&(d->ImageOffset[2])); - ArrayByteSwap((d->IconFmt)); - ArrayByteSwap((d->AnimSpeed)); - ByteSwap(&d->Permissions, &d->CopyCounter); - ArrayByteSwap((d->FirstBlock)); - ArrayByteSwap((d->BlockCount)); - ArrayByteSwap((d->Unused2)); - ArrayByteSwap((d->CommentsAddr)); - ArrayByteSwap(&(d->CommentsAddr[2])); - break; - default: - break; - } - if (length != BE16(d->BlockCount) * 0x2000) - { - return LENGTHFAIL; - } - if (ftell(gci) != offset + 0x40) // Verify correct file position - { - return OPENFAIL; - } - u32 size = BE16((d->BlockCount)) * 0x2000; - u8 *t = new u8[size]; - fread(t, 1, size, gci); - fclose(gci); - u32 ret; - if(!fileName2.empty()) - { - FILE * gci2 = fopen(fileName2.c_str(), "wb"); - if (!gci2) return OPENFAIL; - fseek(gci2, 0, SEEK_SET); - assert(fwrite(d, 1, 0x40, gci2)==0x40); - int fileBlocks = BE16(d->BlockCount); - fseek(gci2, 0x40, SEEK_SET); - - assert(fwrite(t, 1, 0x2000 * fileBlocks, gci2)==(unsigned) (0x2000*fileBlocks)); - fclose(gci2); - ret = GCS; - } - else ret= ImportFile(*d, t,0); - - delete []t; - delete []tmp; - delete d; - return ret; -} - -u32 GCMemcard::ExportGci(u32 index, const char *fileName) -{ - FILE *gci = fopen(fileName, "wb"); - if (!gci) return NOFILE; - fseek(gci, 0, SEEK_SET); - - DEntry d; - if (!GetFileInfo(index, d)) return NOMEMCARD; - assert(fwrite(&d, 1, 0x40, gci) == 0x40); - - u32 size = GetFileSize(index); - if (size == 0xFFFF)return FAIL; - u8 *t = new u8[size * 0x2000]; - - switch(GetFileData(index, t, true)) - { - case FAIL: - fclose(gci); - delete []t; - return FAIL; - case NOMEMCARD: - fclose(gci); - delete []t; - return NOMEMCARD; - default: - break; - } - - fseek(gci, 0x40, SEEK_SET); - assert(fwrite(t, 1, 0x2000 * BE16(d.BlockCount), gci)== (unsigned) (0x2000 * BE16(d.BlockCount))); - fclose(gci); - delete []t; - return SUCCESS; -} - -bool GCMemcard::Save() -{ - if (!mcdFile) return false; - - FILE *mcd=(FILE*)mcdFile; - fseek(mcd, 0, SEEK_SET); - assert(fwrite(&hdr, 1, 0x2000, mcd) == 0x2000); - assert(fwrite(&dir, 1, 0x2000, mcd) == 0x2000); - assert(fwrite(&dir_backup, 1, 0x2000, mcd) == 0x2000); - assert(fwrite(&bat, 1, 0x2000 ,mcd) == 0x2000); - assert(fwrite(&bat_backup, 1, 0x2000, mcd) == 0x2000); - assert(fwrite(mc_data, 1, mc_data_size, mcd) == mc_data_size); - return true; -} - -bool GCMemcard::IsOpen() -{ - return (mcdFile!=NULL); -} - -GCMemcard::GCMemcard(const char *filename) -{ - FILE *mcd = fopen(filename,"r+b"); - mcdFile = mcd; - fail[0] = true; - if (!mcd) return; - - for(int i=0;i<12;i++)fail[i]=false; - - //This function can be removed once more about hdr is known and we can check for a valid header - std::string fileType; - SplitPath(filename, NULL, NULL, &fileType); - if (strcasecmp(fileType.c_str(), ".raw") && strcasecmp(fileType.c_str(), ".gcp")) - { - fail[0] = true; - fail[NOTRAWORGCP] = true; - return; - } - - fseek(mcd, 0x0000, SEEK_SET); - if (fread(&hdr, 1, 0x2000, mcd) != 0x2000) - { - fail[0] = true; - fail[HDR_READ_ERROR] = true; - return; - } - if (fread(&dir, 1, 0x2000, mcd) != 0x2000) - { - fail[0] = true; - fail[DIR_READ_ERROR] = true; - return; - } - if (fread(&dir_backup, 1, 0x2000, mcd) != 0x2000) - { - fail[0] = true; - fail[DIR_BAK_READ_ERROR] = true; - return; - } - if (fread(&bat, 1, 0x2000, mcd) != 0x2000) - { - fail[0] = true; - fail[BAT_READ_ERROR] = true; - return; - } - if (fread(&bat_backup, 1, 0x2000, mcd) != 0x2000) - { - fail[0] = true; - fail[BAT_BAK_READ_ERROR] = true; - return; - } - - u32 csums = TestChecksums(); - - if (csums&1) - { - // header checksum error! - // invalid files do not always get here - fail[0] = true; - fail[HDR_CSUM_FAIL] = true; - return; - } - - if (csums&2) // directory checksum error! - { - if (csums&4) - { - // backup is also wrong! - fail[0] = true; - fail[DIR_CSUM_FAIL] = true; - return; - } - else - { - // backup is correct, restore - dir = dir_backup; - bat = bat_backup; - - // update checksums - csums = TestChecksums(); - } - } - - if (csums&8) // BAT checksum error! - { - if (csums&16) - { - // backup is also wrong! - fail[0] = true; - fail[BAT_CSUM_FAIL] = true; - return; - } - else - { - // backup is correct, restore - dir = dir_backup; - bat = bat_backup; - - // update checksums - csums = TestChecksums(); - } -// It seems that the backup having a larger counter doesn't necessarily mean -// the backup should be copied? -// } -// -// if(BE16(dir_backup.UpdateCounter) > BE16(dir.UpdateCounter)) //check if the backup is newer -// { -// dir = dir_backup; -// bat = bat_backup; // needed? - } - - fseek(mcd, 0xa000, SEEK_SET); - - u16 size = BE16(hdr.Size); - if ((size== 0x0080) || (size == 0x0040) || - (size == 0x0020) || (size == 0x0010) || - (size == 0x0008) || (size == 0x0004)) - { - mc_data_size = (((u32)size * 16) - 5) * 0x2000; - mc_data = new u8[mc_data_size]; - - size_t read = fread(mc_data, 1, mc_data_size, mcd); - if (mc_data_size != read) - { - fail[0] = true; - fail[DATA_READ_FAIL] = true; - } - } - else - { - fail[0] = true; - fail[HDR_SIZE_FFFF] = true; - } -} - -GCMemcard::~GCMemcard() -{ - if (!mcdFile) return; - fclose((FILE*)mcdFile); -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#ifdef _WIN32 +#include "stdafx.h" +#endif +#include +#include + +#include "GCMemcard.h" + + +// i think there is support for this stuff in the common lib... if not there should be support +#define BE16(x) ((u16((x)[0])<<8) | u16((x)[1])) +#define BE32(x) ((u32((x)[0])<<24) | (u32((x)[1])<<16) | (u32((x)[2])<<8) | u32((x)[3])) +#define ArrayByteSwap(a) (ByteSwap(a, a+sizeof(u8))); + +// undefined functions... prolly it means something like that +void ByteSwap(u8 *valueA, u8 *valueB) +{ + u8 tmp = *valueA; + *valueA = *valueB; + *valueB = tmp; +} + +u16 __inline bswap16(u16 s) +{ + return (s>>8) | (s<<8); +} + +u32 __inline bswap32(u32 s) +{ + return (u32)bswap16((u16)(s>>16)) | ((u32)bswap16((u16)s)<<16); +} + + + + + +void GCMemcard::calc_checksumsBE(u16 *buf, u32 num, u16 *c1, u16 *c2) +{ + *c1 = 0;*c2 = 0; + for (u32 i = 0; i < num; ++i) + { + //weird warnings here + *c1 += bswap16(buf[i]); + *c2 += bswap16((u16)(buf[i] ^ 0xffff)); + } + if (*c1 == 0xffff) + { + *c1 = 0; + } + if (*c2 == 0xffff) + { + *c2 = 0; + } +} + +u16 GCMemcard::GetFreeBlocks() +{ + if (!mcdFile) return 0; + return BE16(bat.FreeBlocks); +} + +u32 GCMemcard::GetNumFiles() +{ + if (!mcdFile) return 0; + int j = 0; + for (int i = 0; i < 127; i++) + { + if (BE32(dir.Dir[i].Gamecode)!= 0xFFFFFFFF) + j++; + } + return j; +} + +bool GCMemcard::TitlePresent(DEntry d) +{ + //TODO: Clean up this function + bool equal = false; + if (!mcdFile) return false; + + for (int i = 0; i < 127; i++) + { + if (BE32(dir.Dir[i].Gamecode) == BE32(d.Gamecode)) + { + for ( int j = 0; j < 32; j++) + { + if (dir.Dir[i].Filename[j] != d.Filename[j]) + { + equal = false; + break; + } + else + { + equal = true; + } + } + if (equal) + { + return true; + } + } + } + return false; +} + +u16 GCMemcard::GetFirstBlock(u32 index) +{ + if (!mcdFile) return 0xFFFF; + u16 block = BE16(dir.Dir[index].FirstBlock); + if (block > MAXBLOCK) return 0xFFFF; + return block; +} + +u32 GCMemcard::RemoveFile(u32 index) //index in the directory array +{ + if (!mcdFile) return NOMEMCARD; + + //backup the directory and bat (not really needed here but meh :P + dir_backup = dir; + bat_backup = bat; + + //free the blocks + int blocks_left = BE16(dir.Dir[index].BlockCount); + int block = BE16(dir.Dir[index].FirstBlock) - 1; + bat.LastAllocated[0] = (u8)(block >> 8); + bat.LastAllocated[1] = (u8)block; + + int i = index + 1; + memset(&(dir.Dir[index]), 0xFF, 0x40); + + while (i < 127) + { + DEntry * d = new DEntry; + GetFileInfo(i, *d); + u8 *t = NULL; + //Only get file data if it is a valid dir entry + if (BE16(d->FirstBlock) != 0xFFFF) + { + u16 freeBlock= BE16(bat.FreeBlocks) - BE16(d->BlockCount); + bat.FreeBlocks[0] = u8(freeBlock >> 8); + bat.FreeBlocks[1] = u8(freeBlock); + + u16 size = GetFileSize(i); + if (size != 0xFFFF) + { + t = new u8[size * 0x2000]; + switch (GetFileData(i, t, true)) + { + case NOMEMCARD: + delete[] t; + break; + case FAIL: + delete[] t; + return FAIL; + break; + } + } + } + memset(&(dir.Dir[i]), 0xFF, 0x40); + //Only call import file if Get File Data returns true + if (t) + { + ImportFile(*d, t, blocks_left); + delete[] t; + } + delete d; + i++; + + } + //Added to clean up if removing last file + if (BE16(bat.LastAllocated) == (u16)4) + { + for (int j = 0; j < blocks_left; j++) + { + bat.Map[j] = 0x0000; + } + } + // increment update counter + int updateCtr = BE16(dir.UpdateCounter) + 1; + dir.UpdateCounter[0] = u8(updateCtr >> 8); + dir.UpdateCounter[1] = u8(updateCtr); + + + FixChecksums(); + Save(); + + return SUCCESS; +} + +u32 GCMemcard::ImportFile(DEntry& direntry, u8* contents, int remove) +{ + if (!mcdFile) return NOMEMCARD; + + if (GetNumFiles() >= 127) + { + return OUTOFDIRENTRIES; + } + if (BE16(bat.FreeBlocks) < BE16(direntry.BlockCount)) + { + return OUTOFBLOCKS; + } + if (!remove && TitlePresent(direntry)) return TITLEPRESENT; + + // find first free data block -- assume no freespace fragmentation + int totalspace = (((u32)BE16(hdr.Size) * 16) - 5); + + int firstFree1 = BE16(bat.LastAllocated) + 1; + int firstFree2 = 0; + for (int i = 0; i < 126; i++) + { + if (BE32(dir.Dir[i].Gamecode) == 0xFFFFFFFF) + { + break; + } + else + { + firstFree2 = max(firstFree2, + (int)(BE16(dir.Dir[i].FirstBlock) + BE16(dir.Dir[i].BlockCount))); + } + } + firstFree1 = max(firstFree1, firstFree2); + + // find first free dir entry + int index = -1; + for (int i=0; i < 127; i++) + { + if (BE32(dir.Dir[i].Gamecode) == 0xFFFFFFFF) + { + index = i; + dir.Dir[i] = direntry; + dir.Dir[i].FirstBlock[0] = u8(firstFree1 >> 8); + dir.Dir[i].FirstBlock[1] = u8(firstFree1); + if (!remove) + { + dir.Dir[i].CopyCounter = dir.Dir[i].CopyCounter+1; + } + dir_backup = dir; + break; + } + } + + // keep assuming no freespace fragmentation, and copy over all the data + u8*destination = mc_data + (firstFree1 - 5) * 0x2000; + + int fileBlocks = BE16(direntry.BlockCount); + memcpy(destination, contents, 0x2000 * fileBlocks); + bat_backup = bat; + u16 last = BE16(bat_backup.LastAllocated); + u16 i = (last - 4); + int j = 2; + while(j < BE16(direntry.BlockCount) + 1) + { + bat_backup.Map[i] = bswap16(last + (u16)j); + i++; + j++; + } + bat_backup.Map[i++] = 0xFFFF; + //Set bat.map to 0 for each block that was removed + for (int k = 0; k < remove; k++) + { + bat_backup.Map[i++] = 0x0000; + } + + //update last allocated block + int lastallocated = BE16(bat_backup.LastAllocated) + j - 1; + bat_backup.LastAllocated[0] = u8(lastallocated >> 8); + bat_backup.LastAllocated[1] = u8(lastallocated); + + //update freespace counter + int freespace1 = totalspace - firstFree1 - fileBlocks + 5; + bat_backup.FreeBlocks[0] = u8(freespace1 >> 8); + bat_backup.FreeBlocks[1] = u8(freespace1); + + + if (!remove) + { // ... and dir update counter + int updateCtr = BE16(dir_backup.UpdateCounter) + 1; + dir_backup.UpdateCounter[0] = u8(updateCtr>>8); + dir_backup.UpdateCounter[1] = u8(updateCtr); + // ... and bat update counter + updateCtr = BE16(bat_backup.UpdateCounter) + 1; + bat_backup.UpdateCounter[0] = u8(updateCtr>>8); + bat_backup.UpdateCounter[1] = u8(updateCtr); + } + bat = bat_backup; + + if (!remove) + { + FixChecksums(); + Save(); + } + return fileBlocks; +} + +u32 GCMemcard::GetFileData(u32 index, u8* dest, bool old) //index in the directory array +{ + if (!mcdFile) return NOMEMCARD; + + u16 block = BE16(dir.Dir[index].FirstBlock); + u16 saveLength = BE16(dir.Dir[index].BlockCount); + u16 memcardSize = BE16(hdr.Size) * 0x0010; + + if ((block == 0xFFFF) || (saveLength == 0xFFFF) + || (block + saveLength > memcardSize)) + { + return FAIL; + } + + if (!old) + { + memcpy(dest,mc_data + 0x2000*(block-5), saveLength*0x2000); + } + else + { + assert(block > 0); + while (block != 0xffff) + { + memcpy(dest,mc_data + 0x2000 * (block - 5), 0x2000); + dest+=0x2000; + + u16 nextblock = bswap16(bat.Map[block - 5]); + if (block + saveLength != memcardSize && nextblock > 0) + {//Fixes for older memcards that were not initialized with FF + block = nextblock; + } + else break; + } + } + return SUCCESS; +} + +u16 GCMemcard::GetFileSize(u32 index) //index in the directory array +{ + if (!mcdFile) return 0xFFFF; + + u16 blocks = BE16(dir.Dir[index].BlockCount); + if (blocks > (u16) MAXBLOCK) return 0xFFFF; + return blocks; +} + +bool GCMemcard::GetFileInfo(u32 index, GCMemcard::DEntry& info) //index in the directory array +{ + if (!mcdFile) return false; + + info = dir.Dir[index]; + return true; +} + +bool GCMemcard::GetFileName(u32 index, char *fn) //index in the directory array +{ + if (!mcdFile) return false; + + memcpy (fn, (const char*)dir.Dir[index].Filename, 32); + fn[31] = 0; + return true; +} + +bool GCMemcard::GetComment1(u32 index, char *fn) //index in the directory array +{ + if (!mcdFile) return false; + + u32 Comment1 = BE32(dir.Dir[index].CommentsAddr); + u32 DataBlock = BE16(dir.Dir[index].FirstBlock) - 5; + if ((DataBlock > MAXBLOCK) || (Comment1 == 0xFFFFFFFF)) + { + fn[0] = 0; + return false; + } + memcpy(fn, mc_data + (DataBlock * 0x2000) + Comment1, 32); + fn[31] = 0; + return true; +} + +bool GCMemcard::GetComment2(u32 index, char *fn) //index in the directory array +{ + if (!mcdFile) return false; + + u32 Comment1 = BE32(dir.Dir[index].CommentsAddr); + u32 Comment2 = Comment1 + 32; + u32 DataBlock = BE16(dir.Dir[index].FirstBlock) - 5; + if ((DataBlock > MAXBLOCK) || (Comment1 == 0xFFFFFFFF)) + { + fn[0] = 0; + return false; + } + memcpy(fn, mc_data + (DataBlock * 0x2000) + Comment2, 32); + fn[31] = 0; + return true; +} + +u32 decode5A3(u16 val) +{ + const int lut5to8[] = { 0x00,0x08,0x10,0x18,0x20,0x29,0x31,0x39, + 0x41,0x4A,0x52,0x5A,0x62,0x6A,0x73,0x7B, + 0x83,0x8B,0x94,0x9C,0xA4,0xAC,0xB4,0xBD, + 0xC5,0xCD,0xD5,0xDE,0xE6,0xEE,0xF6,0xFF}; + const int lut4to8[] = { 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77, + 0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF}; + const int lut3to8[] = { 0x00,0x24,0x48,0x6D,0x91,0xB6,0xDA,0xFF}; + + int r,g,b,a; + if ((val&0x8000)) + { + r=lut5to8[(val>>10) & 0x1f]; + g=lut5to8[(val>>5 ) & 0x1f]; + b=lut5to8[(val ) & 0x1f]; + a=0xFF; + } + else + { + a=lut3to8[(val>>12) & 0x7]; + r=lut4to8[(val>>8 ) & 0xf]; + g=lut4to8[(val>>4 ) & 0xf]; + b=lut4to8[(val ) & 0xf]; + } + return (a<<24) | (r<<16) | (g<<8) | b; +} + +void decode5A3image(u32* dst, u16* src, int width, int height) +{ + for (int y = 0; y < height; y += 4) + { + for (int x = 0; x < width; x += 4) + { + for (int iy = 0; iy < 4; iy++, src += 4) + { + for (int ix = 0; ix < 4; ix++) + { + u32 RGBA = decode5A3(bswap16(src[ix])); + dst[(y + iy) * width + (x + ix)] = RGBA; + } + } + } + } +} + +void decodeCI8image(u32* dst, u8* src, u16* pal, int width, int height) +{ + for (int y = 0; y < height; y += 4) + { + for (int x = 0; x < width; x += 8) + { + for (int iy = 0; iy < 4; iy++, src += 8) + { + u32 *tdst = dst+(y+iy)*width+x; + for (int ix = 0; ix < 8; ix++) + { + tdst[ix] = decode5A3(bswap16(pal[src[ix]])); + } + } + } + } +} + +bool GCMemcard::ReadBannerRGBA8(u32 index, u32* buffer) +{ + if (!mcdFile) return false; + + int flags = dir.Dir[index].BIFlags; + + int bnrFormat = (flags&3); + + if (bnrFormat == 0) + return false; + + u32 DataOffset = BE32(dir.Dir[index].ImageOffset); + u32 DataBlock = BE16(dir.Dir[index].FirstBlock) - 5; + + if ((DataBlock > MAXBLOCK) || (DataOffset == 0xFFFFFFFF)) + { + return false; + } + + const int pixels = 96*32; + + if (bnrFormat&1) + { + u8 *pxdata = (u8* )(mc_data +(DataBlock*0x2000) + DataOffset); + u16 *paldata = (u16*)(mc_data +(DataBlock*0x2000) + DataOffset + pixels); + + decodeCI8image(buffer, pxdata, paldata, 96, 32); + } + else + { + u16 *pxdata = (u16*)(mc_data +(DataBlock*0x2000) + DataOffset); + + decode5A3image(buffer, pxdata, 96, 32); + } + return true; +} + +u32 GCMemcard::ReadAnimRGBA8(u32 index, u32* buffer, u8 *delays) +{ + if (!mcdFile) return 0; + + int formats = BE16(dir.Dir[index].IconFmt); + int fdelays = BE16(dir.Dir[index].AnimSpeed); + + int flags = dir.Dir[index].BIFlags; + + int bnrFormat = (flags&3); + + u32 DataOffset = BE32(dir.Dir[index].ImageOffset); + u32 DataBlock = BE16(dir.Dir[index].FirstBlock) - 5; + + if ((DataBlock > MAXBLOCK) || (DataOffset == 0xFFFFFFFF)) + { + return 0; + } + + u8* animData = (u8*)(mc_data +(DataBlock*0x2000) + DataOffset); + + switch (bnrFormat) + { + case 1: + case 3: + animData += 96*32 + 2*256; // image+palette + break; + case 2: + animData += 96*32*2; + break; + } + + int fmts[8]; + u8* data[8]; + int frames = 0; + + + for (int i = 0; i < 8; i++) + { + fmts[i] = (formats >> (2*i))&3; + delays[i] = ((fdelays >> (2*i))&3) << 2; + data[i] = animData; + + switch (fmts[i]) + { + case 1: // CI8 with shared palette + animData += 32*32; + frames++; + break; + case 2: // RGB5A3 + animData += 32*32*2; + frames++; + break; + case 3: // CI8 with own palette + animData += 32*32 + 2*256; + frames++; + break; + } + } + + u16* sharedPal = (u16*)(animData); + + for (int i = 0; i < 8; i++) + { + switch (fmts[i]) + { + case 1: // CI8 with shared palette + decodeCI8image(buffer,data[i],sharedPal,32,32); + buffer += 32*32; + break; + case 2: // RGB5A3 + decode5A3image(buffer, (u16*)(data[i]), 32, 32); + break; + case 3: // CI8 with own palette + u16 *paldata = (u16*)(data[i] + 32*32); + decodeCI8image(buffer, data[i], paldata, 32, 32); + buffer += 32*32; + break; + } + } + + return frames; +} + +u32 GCMemcard::TestChecksums() +{ + if (!mcdFile) return 0xFFFFFFFF; + + u16 csum1=0, + csum2=0; + + u32 results = 0; + + calc_checksumsBE((u16*)&hdr, 0xFE , &csum1, &csum2); + if (BE16(hdr.CheckSum1) != csum1) results |= 1; + if (BE16(hdr.CheckSum2) != csum2) results |= 1; + + calc_checksumsBE((u16*)&dir, 0xFFE, &csum1, &csum2); + if (BE16(dir.CheckSum1) != csum1) results |= 2; + if (BE16(dir.CheckSum2) != csum2) results |= 2; + + calc_checksumsBE((u16*)&dir_backup, 0xFFE, &csum1, &csum2); + if (BE16(dir_backup.CheckSum1) != csum1) results |= 4; + if (BE16(dir_backup.CheckSum2) != csum2) results |= 4; + + calc_checksumsBE((u16*)(((u8*)&bat)+4), 0xFFE, &csum1, &csum2); + if (BE16(bat.CheckSum1) != csum1) results |= 8; + if (BE16(bat.CheckSum2) != csum2) results |= 8; + + calc_checksumsBE((u16*)(((u8*)&bat_backup)+4), 0xFFE, &csum1, &csum2); + if (BE16(bat_backup.CheckSum1) != csum1) results |= 16; + if (BE16(bat_backup.CheckSum2) != csum2) results |= 16; + + return 0; +} + +bool GCMemcard::FixChecksums() +{ + if (!mcdFile) return false; + + u16 csum1=0, + csum2=0; + + calc_checksumsBE((u16*)&hdr, 0xFE, &csum1, &csum2); + hdr.CheckSum1[0] = u8(csum1 >> 8); + hdr.CheckSum1[1] = u8(csum1); + hdr.CheckSum2[0] = u8(csum2 >> 8); + hdr.CheckSum2[1] = u8(csum2); + + calc_checksumsBE((u16*)&dir, 0xFFE, &csum1, &csum2); + dir.CheckSum1[0] = u8(csum1 >> 8); + dir.CheckSum1[1] = u8(csum1); + dir.CheckSum2[0] = u8(csum2 >> 8); + dir.CheckSum2[1] = u8(csum2); + + calc_checksumsBE((u16*)&dir_backup, 0xFFE, &csum1, &csum2); + dir_backup.CheckSum1[0] = u8(csum1 >> 8); + dir_backup.CheckSum1[1] = u8(csum1); + dir_backup.CheckSum2[0] = u8(csum2 >> 8); + dir_backup.CheckSum2[1] = u8(csum2); + + calc_checksumsBE((u16*)(((u8*)&bat)+4), 0xFFE, &csum1, &csum2); + bat.CheckSum1[0] = u8(csum1 >> 8); + bat.CheckSum1[1] = u8(csum1); + bat.CheckSum2[0] = u8(csum2 >> 8); + bat.CheckSum2[1] = u8(csum2); + + calc_checksumsBE((u16*)(((u8*)&bat_backup)+4), 0xFFE, &csum1, &csum2); + bat_backup.CheckSum1[0] = u8(csum1 >> 8); + bat_backup.CheckSum1[1] = u8(csum1); + bat_backup.CheckSum2[0] = u8(csum2 >> 8); + bat_backup.CheckSum2[1] = u8(csum2); + + return true; +} +u32 GCMemcard::CopyFrom(GCMemcard& source, u32 index) +{ + if (!mcdFile) return NOMEMCARD; + + DEntry d; + if (!source.GetFileInfo(index, d)) return 0; + + u32 size = source.GetFileSize(index); + if (size == 0xFFFF) return 0; + u8 *t = new u8[size * 0x2000]; + + switch (source.GetFileData(index, t, true)) + { + case FAIL: + delete[] t; + return FAIL; + case NOMEMCARD: + delete[] t; + return NOMEMCARD; + default: + break; + } + + u32 ret = ImportFile(d,t,0); + + delete[] t; + + return ret; +} + +s32 GCMemcard::ImportGci(const char *fileName, std::string fileName2) +{ + if (fileName2.empty() && !mcdFile) return OPENFAIL; + + FILE *gci = fopen(fileName, "rb"); + if (!gci) return OPENFAIL; + + int offset; + char * tmp = new char[0xD]; + std::string fileType; + SplitPath(fileName, NULL, NULL, &fileType); + + if( !strcasecmp(fileType.c_str(), ".gci")) + offset = GCI; + else + { + fread(tmp, 1, 0xD, gci); + if (!strcasecmp(fileType.c_str(), ".gcs")) + { + if (!memcmp(tmp, "GCSAVE", 6)) // Header must be uppercase + offset = GCS; + else + { + return GCSFAIL; + } + } + else{ + if (!strcasecmp(fileType.c_str(), ".sav")) + { + if (!memcmp(tmp, "DATELGC_SAVE", 0xC)) // Header must be uppercase + offset = SAV; + else + { + return SAVFAIL; + } + } + else + { + return OPENFAIL; + } + } + } + fseek(gci, offset, SEEK_SET); + + DEntry *d = new DEntry; + fread(d, 1, 0x40, gci); + int fStart = (int) ftell(gci); + fseek(gci, 0, SEEK_END); + int length = (int) ftell(gci) - fStart; + fseek(gci, offset + 0x40, SEEK_SET); + + switch(offset){ + case GCS: + { // field containing the Block count as displayed within + // the GameSaves software is not stored in the GCS file. + // It is stored only within the corresponding GSV file. + // If the GCS file is added without using the GameSaves software, + // the value stored is always "1" + int blockCount = length / 0x2000; + d->BlockCount[0] = u8(blockCount >> 8); + d->BlockCount[1] = u8(blockCount); + } + break; + case SAV: + // swap byte pairs + // 0x2C and 0x2D, 0x2E and 0x2F, 0x30 and 0x31, 0x32 and 0x33, + // 0x34 and 0x35, 0x36 and 0x37, 0x38 and 0x39, 0x3A and 0x3B, + // 0x3C and 0x3D,0x3E and 0x3F. + ArrayByteSwap((d->ImageOffset)); + ArrayByteSwap(&(d->ImageOffset[2])); + ArrayByteSwap((d->IconFmt)); + ArrayByteSwap((d->AnimSpeed)); + ByteSwap(&d->Permissions, &d->CopyCounter); + ArrayByteSwap((d->FirstBlock)); + ArrayByteSwap((d->BlockCount)); + ArrayByteSwap((d->Unused2)); + ArrayByteSwap((d->CommentsAddr)); + ArrayByteSwap(&(d->CommentsAddr[2])); + break; + default: + break; + } + if (length != BE16(d->BlockCount) * 0x2000) + { + return LENGTHFAIL; + } + if (ftell(gci) != offset + 0x40) // Verify correct file position + { + return OPENFAIL; + } + u32 size = BE16((d->BlockCount)) * 0x2000; + u8 *t = new u8[size]; + fread(t, 1, size, gci); + fclose(gci); + u32 ret; + if(!fileName2.empty()) + { + FILE * gci2 = fopen(fileName2.c_str(), "wb"); + if (!gci2) return OPENFAIL; + fseek(gci2, 0, SEEK_SET); + assert(fwrite(d, 1, 0x40, gci2)==0x40); + int fileBlocks = BE16(d->BlockCount); + fseek(gci2, 0x40, SEEK_SET); + + assert(fwrite(t, 1, 0x2000 * fileBlocks, gci2)==(unsigned) (0x2000*fileBlocks)); + fclose(gci2); + ret = GCS; + } + else ret= ImportFile(*d, t,0); + + delete []t; + delete []tmp; + delete d; + return ret; +} + +u32 GCMemcard::ExportGci(u32 index, const char *fileName) +{ + FILE *gci = fopen(fileName, "wb"); + if (!gci) return NOFILE; + fseek(gci, 0, SEEK_SET); + + DEntry d; + if (!GetFileInfo(index, d)) return NOMEMCARD; + assert(fwrite(&d, 1, 0x40, gci) == 0x40); + + u32 size = GetFileSize(index); + if (size == 0xFFFF)return FAIL; + u8 *t = new u8[size * 0x2000]; + + switch(GetFileData(index, t, true)) + { + case FAIL: + fclose(gci); + delete []t; + return FAIL; + case NOMEMCARD: + fclose(gci); + delete []t; + return NOMEMCARD; + default: + break; + } + + fseek(gci, 0x40, SEEK_SET); + assert(fwrite(t, 1, 0x2000 * BE16(d.BlockCount), gci)== (unsigned) (0x2000 * BE16(d.BlockCount))); + fclose(gci); + delete []t; + return SUCCESS; +} + +bool GCMemcard::Save() +{ + if (!mcdFile) return false; + + FILE *mcd=(FILE*)mcdFile; + fseek(mcd, 0, SEEK_SET); + assert(fwrite(&hdr, 1, 0x2000, mcd) == 0x2000); + assert(fwrite(&dir, 1, 0x2000, mcd) == 0x2000); + assert(fwrite(&dir_backup, 1, 0x2000, mcd) == 0x2000); + assert(fwrite(&bat, 1, 0x2000 ,mcd) == 0x2000); + assert(fwrite(&bat_backup, 1, 0x2000, mcd) == 0x2000); + assert(fwrite(mc_data, 1, mc_data_size, mcd) == mc_data_size); + return true; +} + +bool GCMemcard::IsOpen() +{ + return (mcdFile!=NULL); +} + +GCMemcard::GCMemcard(const char *filename) +{ + FILE *mcd = fopen(filename,"r+b"); + mcdFile = mcd; + fail[0] = true; + if (!mcd) return; + + for(int i=0;i<12;i++)fail[i]=false; + + //This function can be removed once more about hdr is known and we can check for a valid header + std::string fileType; + SplitPath(filename, NULL, NULL, &fileType); + if (strcasecmp(fileType.c_str(), ".raw") && strcasecmp(fileType.c_str(), ".gcp")) + { + fail[0] = true; + fail[NOTRAWORGCP] = true; + return; + } + + fseek(mcd, 0x0000, SEEK_SET); + if (fread(&hdr, 1, 0x2000, mcd) != 0x2000) + { + fail[0] = true; + fail[HDR_READ_ERROR] = true; + return; + } + if (fread(&dir, 1, 0x2000, mcd) != 0x2000) + { + fail[0] = true; + fail[DIR_READ_ERROR] = true; + return; + } + if (fread(&dir_backup, 1, 0x2000, mcd) != 0x2000) + { + fail[0] = true; + fail[DIR_BAK_READ_ERROR] = true; + return; + } + if (fread(&bat, 1, 0x2000, mcd) != 0x2000) + { + fail[0] = true; + fail[BAT_READ_ERROR] = true; + return; + } + if (fread(&bat_backup, 1, 0x2000, mcd) != 0x2000) + { + fail[0] = true; + fail[BAT_BAK_READ_ERROR] = true; + return; + } + + u32 csums = TestChecksums(); + + if (csums&1) + { + // header checksum error! + // invalid files do not always get here + fail[0] = true; + fail[HDR_CSUM_FAIL] = true; + return; + } + + if (csums&2) // directory checksum error! + { + if (csums&4) + { + // backup is also wrong! + fail[0] = true; + fail[DIR_CSUM_FAIL] = true; + return; + } + else + { + // backup is correct, restore + dir = dir_backup; + bat = bat_backup; + + // update checksums + csums = TestChecksums(); + } + } + + if (csums&8) // BAT checksum error! + { + if (csums&16) + { + // backup is also wrong! + fail[0] = true; + fail[BAT_CSUM_FAIL] = true; + return; + } + else + { + // backup is correct, restore + dir = dir_backup; + bat = bat_backup; + + // update checksums + csums = TestChecksums(); + } +// It seems that the backup having a larger counter doesn't necessarily mean +// the backup should be copied? +// } +// +// if(BE16(dir_backup.UpdateCounter) > BE16(dir.UpdateCounter)) //check if the backup is newer +// { +// dir = dir_backup; +// bat = bat_backup; // needed? + } + + fseek(mcd, 0xa000, SEEK_SET); + + u16 size = BE16(hdr.Size); + if ((size== 0x0080) || (size == 0x0040) || + (size == 0x0020) || (size == 0x0010) || + (size == 0x0008) || (size == 0x0004)) + { + mc_data_size = (((u32)size * 16) - 5) * 0x2000; + mc_data = new u8[mc_data_size]; + + size_t read = fread(mc_data, 1, mc_data_size, mcd); + if (mc_data_size != read) + { + fail[0] = true; + fail[DATA_READ_FAIL] = true; + } + } + else + { + fail[0] = true; + fail[HDR_SIZE_FFFF] = true; + } +} + +GCMemcard::~GCMemcard() +{ + if (!mcdFile) return; + fclose((FILE*)mcdFile); +} diff --git a/Source/Core/DolphinWX/Src/PatchAddEdit.cpp b/Source/Core/DolphinWX/Src/PatchAddEdit.cpp index 3bab617774..e1328f6a7f 100644 --- a/Source/Core/DolphinWX/Src/PatchAddEdit.cpp +++ b/Source/Core/DolphinWX/Src/PatchAddEdit.cpp @@ -1,109 +1,109 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "PatchAddEdit.h" - -extern std::vector onFrame; - -BEGIN_EVENT_TABLE(CPatchAddEdit, wxDialog) - EVT_CLOSE(CPatchAddEdit::OnClose) - EVT_SPIN(ID_ENTRY_SELECT, CPatchAddEdit::ChangeEntry) -END_EVENT_TABLE() - -CPatchAddEdit::CPatchAddEdit(int _selection, wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& position, const wxSize& size, long style) - : wxDialog(parent, id, title, position, size, style) -{ - selection = _selection; - CreateGUIControls(selection); -} - -CPatchAddEdit::~CPatchAddEdit() -{ -} - -void CPatchAddEdit::CreateGUIControls(int _selection) -{ - std::string currentName = onFrame.at(_selection).name; - std::vector currentEntries = onFrame.at(_selection).entries; - - wxBoxSizer* sEditPatch = new wxBoxSizer(wxVERTICAL); - wxStaticText* EditPatchNameText = new wxStaticText(this, ID_EDITPATCH_NAME_TEXT, _("Name:"), wxDefaultPosition, wxDefaultSize); - EditPatchName = new wxTextCtrl(this, ID_EDITPATCH_NAME, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - EditPatchName->SetValue(wxString::FromAscii(currentName.c_str())); - wxStaticText* EditPatchOffsetText = new wxStaticText(this, ID_EDITPATCH_OFFSET_TEXT, _("Offset:"), wxDefaultPosition, wxDefaultSize); - EditPatchOffset = new wxTextCtrl(this, ID_EDITPATCH_OFFSET, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - EditPatchOffset->SetValue(wxString::Format(wxT("%08X"), currentEntries.at(0).address)); - EntrySelection = new wxSpinButton(this, ID_ENTRY_SELECT, wxDefaultPosition, wxDefaultSize, wxVERTICAL); - EntrySelection->SetRange(0, (int)currentEntries.size()-1); - wxArrayString wxArrayStringFor_EditPatchType; - for (int i = 0; i < 3; ++i) - wxArrayStringFor_EditPatchType.Add(wxString::FromAscii(PatchEngine::PatchTypeStrings[i])); - EditPatchType = new wxRadioBox(this, ID_EDITPATCH_TYPE, _("Type"), wxDefaultPosition, wxDefaultSize, wxArrayStringFor_EditPatchType, 3, wxRA_SPECIFY_COLS, wxDefaultValidator); - EditPatchType->SetSelection((int)currentEntries.at(0).type); - wxStaticText* EditPatchValueText = new wxStaticText(this, ID_EDITPATCH_VALUE_TEXT, _("Value:"), wxDefaultPosition, wxDefaultSize); - EditPatchValue = new wxTextCtrl(this, ID_EDITPATCH_VALUE, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); - EditPatchValue->SetValue(wxString::Format(wxT("%08X"), currentEntries.at(0).value)); - - wxBoxSizer* sEditPatchName = new wxBoxSizer(wxHORIZONTAL); - sEditPatchName->Add(EditPatchNameText, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); - sEditPatchName->Add(EditPatchName, 1, wxEXPAND|wxALL, 5); - sEditPatch->Add(sEditPatchName, 0, wxEXPAND); - wxStaticBoxSizer* sbEntry = new wxStaticBoxSizer(wxVERTICAL, this, _("Entry")); - wxGridBagSizer* sgEntry = new wxGridBagSizer(0, 0); - sgEntry->AddGrowableCol(1); - sgEntry->Add(EditPatchType, wxGBPosition(0, 0), wxGBSpan(1, 2), wxEXPAND|wxALL, 5); - sgEntry->Add(EditPatchOffsetText, wxGBPosition(1, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); - sgEntry->Add(EditPatchOffset, wxGBPosition(1, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); - sgEntry->Add(EditPatchValueText, wxGBPosition(2, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); - sgEntry->Add(EditPatchValue, wxGBPosition(2, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); - sgEntry->Add(EntrySelection, wxGBPosition(0, 2), wxGBSpan(3, 1), wxEXPAND|wxALL, 5); - sbEntry->Add(sgEntry, 0, wxEXPAND); - sEditPatch->Add(sbEntry, 0, wxEXPAND|wxALL, 5); - wxBoxSizer* sEditPatchButtons = new wxBoxSizer(wxHORIZONTAL); - wxButton* bOK = new wxButton(this, wxID_OK, _("OK"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - wxButton* bCancel = new wxButton(this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); - sEditPatchButtons->Add(0, 0, 1, wxEXPAND, 5); - sEditPatchButtons->Add(bOK, 0, wxALL, 5); - sEditPatchButtons->Add(bCancel, 0, wxALL, 5); - sEditPatch->Add(sEditPatchButtons, 0, wxEXPAND, 5); - this->SetSizer(sEditPatch); - sEditPatch->Layout(); - - /*if (this->ShowModal() == wxID_OK) - { - onFrame.at(selection).name = std::string(EditPatchName->GetValue().mb_str()); - unsigned long value; - if (EditPatchOffset->GetValue().ToULong(&value, 16)) - onFrame.at(selection).entries.at(0).address = value; - onFrame.at(selection).entries.at(0).type = (PatchEngine::PatchType)EditPatchType->GetSelection(); - if (EditPatchValue->GetValue().ToULong(&value, 16)) - onFrame.at(selection).entries.at(0).value = value; - }*/ -} - -void CPatchAddEdit::OnClose(wxCloseEvent& WXUNUSED (event)) -{ - Destroy(); -} - -void CPatchAddEdit::ChangeEntry(wxSpinEvent& event) -{ - PatchEngine::PatchEntry pE = onFrame.at(selection).entries.at(event.GetPosition()); - EditPatchOffset->SetValue(wxString::Format(wxT("%08X"), pE.address)); - EditPatchType->SetSelection(pE.type); - EditPatchValue->SetValue(wxString::Format(wxT("%08X"), pE.value)); -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "PatchAddEdit.h" + +extern std::vector onFrame; + +BEGIN_EVENT_TABLE(CPatchAddEdit, wxDialog) + EVT_CLOSE(CPatchAddEdit::OnClose) + EVT_SPIN(ID_ENTRY_SELECT, CPatchAddEdit::ChangeEntry) +END_EVENT_TABLE() + +CPatchAddEdit::CPatchAddEdit(int _selection, wxWindow* parent, wxWindowID id, const wxString& title, const wxPoint& position, const wxSize& size, long style) + : wxDialog(parent, id, title, position, size, style) +{ + selection = _selection; + CreateGUIControls(selection); +} + +CPatchAddEdit::~CPatchAddEdit() +{ +} + +void CPatchAddEdit::CreateGUIControls(int _selection) +{ + std::string currentName = onFrame.at(_selection).name; + std::vector currentEntries = onFrame.at(_selection).entries; + + wxBoxSizer* sEditPatch = new wxBoxSizer(wxVERTICAL); + wxStaticText* EditPatchNameText = new wxStaticText(this, ID_EDITPATCH_NAME_TEXT, _("Name:"), wxDefaultPosition, wxDefaultSize); + EditPatchName = new wxTextCtrl(this, ID_EDITPATCH_NAME, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + EditPatchName->SetValue(wxString::FromAscii(currentName.c_str())); + wxStaticText* EditPatchOffsetText = new wxStaticText(this, ID_EDITPATCH_OFFSET_TEXT, _("Offset:"), wxDefaultPosition, wxDefaultSize); + EditPatchOffset = new wxTextCtrl(this, ID_EDITPATCH_OFFSET, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + EditPatchOffset->SetValue(wxString::Format(wxT("%08X"), currentEntries.at(0).address)); + EntrySelection = new wxSpinButton(this, ID_ENTRY_SELECT, wxDefaultPosition, wxDefaultSize, wxVERTICAL); + EntrySelection->SetRange(0, (int)currentEntries.size()-1); + wxArrayString wxArrayStringFor_EditPatchType; + for (int i = 0; i < 3; ++i) + wxArrayStringFor_EditPatchType.Add(wxString::FromAscii(PatchEngine::PatchTypeStrings[i])); + EditPatchType = new wxRadioBox(this, ID_EDITPATCH_TYPE, _("Type"), wxDefaultPosition, wxDefaultSize, wxArrayStringFor_EditPatchType, 3, wxRA_SPECIFY_COLS, wxDefaultValidator); + EditPatchType->SetSelection((int)currentEntries.at(0).type); + wxStaticText* EditPatchValueText = new wxStaticText(this, ID_EDITPATCH_VALUE_TEXT, _("Value:"), wxDefaultPosition, wxDefaultSize); + EditPatchValue = new wxTextCtrl(this, ID_EDITPATCH_VALUE, wxEmptyString, wxDefaultPosition, wxDefaultSize, 0); + EditPatchValue->SetValue(wxString::Format(wxT("%08X"), currentEntries.at(0).value)); + + wxBoxSizer* sEditPatchName = new wxBoxSizer(wxHORIZONTAL); + sEditPatchName->Add(EditPatchNameText, 0, wxALIGN_CENTER_VERTICAL|wxALL, 5); + sEditPatchName->Add(EditPatchName, 1, wxEXPAND|wxALL, 5); + sEditPatch->Add(sEditPatchName, 0, wxEXPAND); + wxStaticBoxSizer* sbEntry = new wxStaticBoxSizer(wxVERTICAL, this, _("Entry")); + wxGridBagSizer* sgEntry = new wxGridBagSizer(0, 0); + sgEntry->AddGrowableCol(1); + sgEntry->Add(EditPatchType, wxGBPosition(0, 0), wxGBSpan(1, 2), wxEXPAND|wxALL, 5); + sgEntry->Add(EditPatchOffsetText, wxGBPosition(1, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); + sgEntry->Add(EditPatchOffset, wxGBPosition(1, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); + sgEntry->Add(EditPatchValueText, wxGBPosition(2, 0), wxGBSpan(1, 1), wxALIGN_CENTER_VERTICAL|wxALL, 5); + sgEntry->Add(EditPatchValue, wxGBPosition(2, 1), wxGBSpan(1, 1), wxEXPAND|wxALL, 5); + sgEntry->Add(EntrySelection, wxGBPosition(0, 2), wxGBSpan(3, 1), wxEXPAND|wxALL, 5); + sbEntry->Add(sgEntry, 0, wxEXPAND); + sEditPatch->Add(sbEntry, 0, wxEXPAND|wxALL, 5); + wxBoxSizer* sEditPatchButtons = new wxBoxSizer(wxHORIZONTAL); + wxButton* bOK = new wxButton(this, wxID_OK, _("OK"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + wxButton* bCancel = new wxButton(this, wxID_CANCEL, _("Cancel"), wxDefaultPosition, wxDefaultSize, 0, wxDefaultValidator); + sEditPatchButtons->Add(0, 0, 1, wxEXPAND, 5); + sEditPatchButtons->Add(bOK, 0, wxALL, 5); + sEditPatchButtons->Add(bCancel, 0, wxALL, 5); + sEditPatch->Add(sEditPatchButtons, 0, wxEXPAND, 5); + this->SetSizer(sEditPatch); + sEditPatch->Layout(); + + /*if (this->ShowModal() == wxID_OK) + { + onFrame.at(selection).name = std::string(EditPatchName->GetValue().mb_str()); + unsigned long value; + if (EditPatchOffset->GetValue().ToULong(&value, 16)) + onFrame.at(selection).entries.at(0).address = value; + onFrame.at(selection).entries.at(0).type = (PatchEngine::PatchType)EditPatchType->GetSelection(); + if (EditPatchValue->GetValue().ToULong(&value, 16)) + onFrame.at(selection).entries.at(0).value = value; + }*/ +} + +void CPatchAddEdit::OnClose(wxCloseEvent& WXUNUSED (event)) +{ + Destroy(); +} + +void CPatchAddEdit::ChangeEntry(wxSpinEvent& event) +{ + PatchEngine::PatchEntry pE = onFrame.at(selection).entries.at(event.GetPosition()); + EditPatchOffset->SetValue(wxString::Format(wxT("%08X"), pE.address)); + EditPatchType->SetSelection(pE.type); + EditPatchValue->SetValue(wxString::Format(wxT("%08X"), pE.value)); +} diff --git a/Source/Core/DolphinWX/Src/PluginManager.cpp b/Source/Core/DolphinWX/Src/PluginManager.cpp index c8e802fff7..fd235905cc 100644 --- a/Source/Core/DolphinWX/Src/PluginManager.cpp +++ b/Source/Core/DolphinWX/Src/PluginManager.cpp @@ -1,192 +1,192 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include -#include - -#include "Globals.h" -#include "FileSearch.h" -#include "FileUtil.h" -#include "PluginManager.h" -#include "StringUtil.h" - -/* Why does it crash if we try to open the debugger in the same instance like this? */ -namespace PluginVideo -{ - extern DynamicLibrary plugin; - extern bool IsLoaded(); - extern bool LoadPlugin(const char *_Filename); - extern void Debug(HWND _hwnd, bool Show); -} - - -namespace PluginDSP -{ - extern DynamicLibrary plugin; - extern bool IsLoaded(); - extern bool LoadPlugin(const char *_Filename); - extern void Debug(HWND _hwnd, bool Show); -} - - -//void(__cdecl * m_DllDebugger) (HWND _hParent) = 0; - - -CPluginManager CPluginManager::m_Instance; - - -CPluginManager::CPluginManager() -{} - - -CPluginManager::~CPluginManager() -{} - - -// ---------------------------------------- -// Create list of avaliable plugins -// ------------- -void CPluginManager::ScanForPlugins(wxWindow* _wxWindow) -{ - m_PluginInfos.clear(); - - CFileSearch::XStringVector Directories; - Directories.push_back(std::string(PLUGINS_DIR)); - - CFileSearch::XStringVector Extensions; - Extensions.push_back("*" PLUGIN_SUFFIX); - - CFileSearch FileSearch(Extensions, Directories); - const CFileSearch::XStringVector& rFilenames = FileSearch.GetFileNames(); - - if (rFilenames.size() > 0) - { - /* - wxProgressDialog dialog(_T("Scanning for Plugins"), - _T("Scanning..."), - (int)rFilenames.size(), // range - _wxWindow, // parent - wxPD_CAN_ABORT | - wxPD_APP_MODAL | - // wxPD_AUTO_HIDE | -- try this as well - wxPD_ELAPSED_TIME | - wxPD_ESTIMATED_TIME | - wxPD_REMAINING_TIME | - wxPD_SMOOTH // - makes indeterminate mode bar on WinXP very small - ); - dialog.CenterOnParent(); - */ - - for (size_t i = 0; i < rFilenames.size(); i++) - { - std::string orig_name = rFilenames[i]; - std::string FileName; - - if (!SplitPath(rFilenames[i], NULL, &FileName, NULL)) - { - printf("Bad Path %s\n", rFilenames[i].c_str()); - return; - } - - /* - wxString msg; - char temp[128]; - sprintf(temp,"Scanning %s", FileName.c_str()); - msg = wxString::FromAscii(temp); - bool Cont = dialog.Update((int)i, msg); - - if (!Cont) - { - break; - } - */ - CPluginInfo PluginInfo(orig_name.c_str()); - if (PluginInfo.IsValid()) - { - m_PluginInfos.push_back(PluginInfo); - } - } - } -} - - -// ---------------------------------------- -// Open config window. _rFilename = plugin filename ,ret = the dll slot number -// ------------- -void CPluginManager::OpenConfig(void* _Parent, const char *_rFilename) -{ - Common::CPlugin::Load(_rFilename); - - Common::CPlugin::Config((HWND)_Parent); - Common::CPlugin::Release(); -} - -// ---------------------------------------- -// Open debugging window. Type = Video or DSP. Show = Show or hide window. -// ------------- -void CPluginManager::OpenDebug(void* _Parent, const char *_rFilename, bool Type, bool Show) -{ - //int ret = 1; - //int ret = Common::CPlugin::Load(_rFilename, true); - //int ret = PluginVideo::LoadPlugin(_rFilename); - //int ret = PluginDSP::LoadPlugin(_rFilename); - - if(Type) - { - //Common::CPlugin::Debug((HWND)_Parent); - if(!PluginVideo::IsLoaded()) PluginVideo::LoadPlugin(_rFilename); - PluginVideo::Debug((HWND)_Parent, Show); - } - else - { - if(!PluginDSP::IsLoaded()) PluginDSP::LoadPlugin(_rFilename); - PluginDSP::Debug((HWND)_Parent, Show); - } - //Common::CPlugin::Release(); // this is only if the wx dialog is called with ShowModal() - - //m_DllDebugger = (void (__cdecl*)(HWND))PluginVideo::plugin.Get("DllDebugger"); - //m_DllDebugger(NULL); -} - - -// ---------------------------------------- -// Get dll info -// ------------- -CPluginInfo::CPluginInfo(const char *_rFileName) - : m_FileName(_rFileName) - , m_Valid(false) -{ - if (Common::CPlugin::Load(_rFileName)) - { - if (Common::CPlugin::GetInfo(m_PluginInfo)) - m_Valid = true; - else - PanicAlert("Could not get info about plugin %s", _rFileName); - - Common::CPlugin::Release(); - } - else - { - if (!File::Exists(_rFileName)) { - PanicAlert("Could not load plugin %s - file does not exist", _rFileName); - } else { - PanicAlert("Failed to load plugin %s - unknown error.\n", _rFileName); - } - } -} - - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include + +#include "Globals.h" +#include "FileSearch.h" +#include "FileUtil.h" +#include "PluginManager.h" +#include "StringUtil.h" + +/* Why does it crash if we try to open the debugger in the same instance like this? */ +namespace PluginVideo +{ + extern DynamicLibrary plugin; + extern bool IsLoaded(); + extern bool LoadPlugin(const char *_Filename); + extern void Debug(HWND _hwnd, bool Show); +} + + +namespace PluginDSP +{ + extern DynamicLibrary plugin; + extern bool IsLoaded(); + extern bool LoadPlugin(const char *_Filename); + extern void Debug(HWND _hwnd, bool Show); +} + + +//void(__cdecl * m_DllDebugger) (HWND _hParent) = 0; + + +CPluginManager CPluginManager::m_Instance; + + +CPluginManager::CPluginManager() +{} + + +CPluginManager::~CPluginManager() +{} + + +// ---------------------------------------- +// Create list of avaliable plugins +// ------------- +void CPluginManager::ScanForPlugins(wxWindow* _wxWindow) +{ + m_PluginInfos.clear(); + + CFileSearch::XStringVector Directories; + Directories.push_back(std::string(PLUGINS_DIR)); + + CFileSearch::XStringVector Extensions; + Extensions.push_back("*" PLUGIN_SUFFIX); + + CFileSearch FileSearch(Extensions, Directories); + const CFileSearch::XStringVector& rFilenames = FileSearch.GetFileNames(); + + if (rFilenames.size() > 0) + { + /* + wxProgressDialog dialog(_T("Scanning for Plugins"), + _T("Scanning..."), + (int)rFilenames.size(), // range + _wxWindow, // parent + wxPD_CAN_ABORT | + wxPD_APP_MODAL | + // wxPD_AUTO_HIDE | -- try this as well + wxPD_ELAPSED_TIME | + wxPD_ESTIMATED_TIME | + wxPD_REMAINING_TIME | + wxPD_SMOOTH // - makes indeterminate mode bar on WinXP very small + ); + dialog.CenterOnParent(); + */ + + for (size_t i = 0; i < rFilenames.size(); i++) + { + std::string orig_name = rFilenames[i]; + std::string FileName; + + if (!SplitPath(rFilenames[i], NULL, &FileName, NULL)) + { + printf("Bad Path %s\n", rFilenames[i].c_str()); + return; + } + + /* + wxString msg; + char temp[128]; + sprintf(temp,"Scanning %s", FileName.c_str()); + msg = wxString::FromAscii(temp); + bool Cont = dialog.Update((int)i, msg); + + if (!Cont) + { + break; + } + */ + CPluginInfo PluginInfo(orig_name.c_str()); + if (PluginInfo.IsValid()) + { + m_PluginInfos.push_back(PluginInfo); + } + } + } +} + + +// ---------------------------------------- +// Open config window. _rFilename = plugin filename ,ret = the dll slot number +// ------------- +void CPluginManager::OpenConfig(void* _Parent, const char *_rFilename) +{ + Common::CPlugin::Load(_rFilename); + + Common::CPlugin::Config((HWND)_Parent); + Common::CPlugin::Release(); +} + +// ---------------------------------------- +// Open debugging window. Type = Video or DSP. Show = Show or hide window. +// ------------- +void CPluginManager::OpenDebug(void* _Parent, const char *_rFilename, bool Type, bool Show) +{ + //int ret = 1; + //int ret = Common::CPlugin::Load(_rFilename, true); + //int ret = PluginVideo::LoadPlugin(_rFilename); + //int ret = PluginDSP::LoadPlugin(_rFilename); + + if(Type) + { + //Common::CPlugin::Debug((HWND)_Parent); + if(!PluginVideo::IsLoaded()) PluginVideo::LoadPlugin(_rFilename); + PluginVideo::Debug((HWND)_Parent, Show); + } + else + { + if(!PluginDSP::IsLoaded()) PluginDSP::LoadPlugin(_rFilename); + PluginDSP::Debug((HWND)_Parent, Show); + } + //Common::CPlugin::Release(); // this is only if the wx dialog is called with ShowModal() + + //m_DllDebugger = (void (__cdecl*)(HWND))PluginVideo::plugin.Get("DllDebugger"); + //m_DllDebugger(NULL); +} + + +// ---------------------------------------- +// Get dll info +// ------------- +CPluginInfo::CPluginInfo(const char *_rFileName) + : m_FileName(_rFileName) + , m_Valid(false) +{ + if (Common::CPlugin::Load(_rFileName)) + { + if (Common::CPlugin::GetInfo(m_PluginInfo)) + m_Valid = true; + else + PanicAlert("Could not get info about plugin %s", _rFileName); + + Common::CPlugin::Release(); + } + else + { + if (!File::Exists(_rFileName)) { + PanicAlert("Could not load plugin %s - file does not exist", _rFileName); + } else { + PanicAlert("Failed to load plugin %s - unknown error.\n", _rFileName); + } + } +} + + diff --git a/Source/Core/VideoCommon/Src/BPMemory.cpp b/Source/Core/VideoCommon/Src/BPMemory.cpp index da7083da4c..4fdbf25306 100644 --- a/Source/Core/VideoCommon/Src/BPMemory.cpp +++ b/Source/Core/VideoCommon/Src/BPMemory.cpp @@ -1,24 +1,24 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" - -#include "BPMemory.h" - -//BP state -// STATE_TO_SAVE +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" + +#include "BPMemory.h" + +//BP state +// STATE_TO_SAVE BPMemory bpmem; diff --git a/Source/Core/VideoCommon/Src/CPMemory.cpp b/Source/Core/VideoCommon/Src/CPMemory.cpp index 837dde70ac..32cd4f0b32 100644 --- a/Source/Core/VideoCommon/Src/CPMemory.cpp +++ b/Source/Core/VideoCommon/Src/CPMemory.cpp @@ -1,29 +1,29 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" -#include "CPMemory.h" - -// CP state -// STATE_TO_SAVE -u32 arraybases[16]; -u32 arraystrides[16]; -TMatrixIndexA MatrixIndexA; -TMatrixIndexB MatrixIndexB; -TVtxDesc g_VtxDesc; -// Most games only use the first VtxAttr and simply reconfigure it all the time as needed. -VAT g_VtxAttr[8]; +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "Common.h" +#include "CPMemory.h" + +// CP state +// STATE_TO_SAVE +u32 arraybases[16]; +u32 arraystrides[16]; +TMatrixIndexA MatrixIndexA; +TMatrixIndexB MatrixIndexB; +TVtxDesc g_VtxDesc; +// Most games only use the first VtxAttr and simply reconfigure it all the time as needed. +VAT g_VtxAttr[8]; diff --git a/Source/Core/VideoCommon/Src/Fifo.cpp b/Source/Core/VideoCommon/Src/Fifo.cpp index c1a59e4918..8ebe2dc720 100644 --- a/Source/Core/VideoCommon/Src/Fifo.cpp +++ b/Source/Core/VideoCommon/Src/Fifo.cpp @@ -1,167 +1,167 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include - -#include "MemoryUtil.h" -#include "Thread.h" -#include "OpcodeDecoding.h" - -#include "Fifo.h" - -extern u8* g_pVideoData; - -bool fifoStateRun = true; - -// STATE_TO_SAVE -static u8 *videoBuffer; -static int size = 0; - -void Fifo_DoState(PointerWrap &p) -{ - p.DoArray(videoBuffer, FIFO_SIZE); - p.Do(size); - int pos = (int)(g_pVideoData-videoBuffer); // get offset - p.Do(pos); // read or write offset (depends on the mode afaik) - g_pVideoData = &videoBuffer[pos]; // overwrite g_pVideoData -> expected no change when load ss and change when save ss -} - -void Fifo_Init() -{ - videoBuffer = (u8*)AllocateMemoryPages(FIFO_SIZE); - fifoStateRun = true; -} - -void Fifo_Shutdown() -{ - FreeMemoryPages(videoBuffer, FIFO_SIZE); - fifoStateRun = false; -} - -void Fifo_Stop() -{ - fifoStateRun = false; -} - -u8* FAKE_GetFifoStartPtr() -{ - return videoBuffer; -} - -u8* FAKE_GetFifoEndPtr() -{ - return &videoBuffer[size]; -} - -void Video_SendFifoData(u8* _uData, u32 len) -{ - if (size + len >= FIFO_SIZE) - { - int pos = (int)(g_pVideoData-videoBuffer); - if (size-pos > pos) - { - PanicAlert("FIFO out of bounds (sz = %i, at %08x)", size, pos); - } - memmove(&videoBuffer[0], &videoBuffer[pos], size - pos ); - size -= pos; - g_pVideoData = FAKE_GetFifoStartPtr(); - } - memcpy(videoBuffer + size, _uData, len); - size += len; - OpcodeDecoder_Run(); -} - -void Fifo_EnterLoop(const SVideoInitialize &video_initialize) -{ - SCPFifoStruct &_fifo = *video_initialize.pCPFifo; - u32 distToSend; - -#ifdef _WIN32 - // TODO(ector): Don't peek so often! - while (video_initialize.pPeekMessages()) -#else - while (fifoStateRun) -#endif - { - if (_fifo.CPReadWriteDistance < _fifo.CPLoWatermark) - Common::SleepCurrentThread(1); - //etc... - - // check if we are able to run this buffer - if ((_fifo.bFF_GPReadEnable) && _fifo.CPReadWriteDistance && !(_fifo.bFF_BPEnable && _fifo.bFF_Breakpoint)) - { - Common::SyncInterlockedExchange((LONG*)&_fifo.CPReadIdle, 0); - //video_initialize.pLog("RUN...........................",FALSE); - int peek_counter = 0; - while (_fifo.bFF_GPReadEnable && (_fifo.CPReadWriteDistance > 0)) - { - peek_counter++; - if (peek_counter == 50) { - video_initialize.pPeekMessages(); - peek_counter = 0; - } - // read the data and send it to the VideoPlugin - u32 readPtr = _fifo.CPReadPointer; - u8 *uData = video_initialize.pGetMemoryPointer(readPtr); - - // if we are on BP mode we must send 32B chunks to Video plugin for BP checking - // TODO (mb2): test & check if MP1/MP2 realy need this now. - if (_fifo.bFF_BPEnable) - { - if (readPtr == _fifo.CPBreakpoint) - { - video_initialize.pLog("!!! BP irq raised",FALSE); - Common::SyncInterlockedExchange((LONG*)&_fifo.bFF_Breakpoint, 1); - - video_initialize.pUpdateInterrupts(); - break; - } - distToSend = 32; - readPtr += 32; - if ( readPtr >= _fifo.CPEnd) - readPtr = _fifo.CPBase; - } - else - { -#if 0 // ugly random GP slowdown for testing DC robustness... TODO: remove when completly sure DC is ok - int r=rand();if ((r&0xF)==r) Common::SleepCurrentThread(r); - distToSend = 32; - readPtr += 32; - if ( readPtr >= _fifo.CPEnd) - readPtr = _fifo.CPBase; -#else - // sending the whole CPReadWriteDistance - distToSend = _fifo.CPReadWriteDistance; - if ( (distToSend+readPtr) >= _fifo.CPEnd) // TODO: better? - { - distToSend =_fifo.CPEnd - readPtr; - readPtr = _fifo.CPBase; - } - else - readPtr += distToSend; -#endif - } - Video_SendFifoData(uData, distToSend); - Common::SyncInterlockedExchange((LONG*)&_fifo.CPReadPointer, readPtr); - Common::SyncInterlockedExchangeAdd((LONG*)&_fifo.CPReadWriteDistance, -distToSend); - } - //video_initialize.pLog("..........................IDLE",FALSE); - Common::SyncInterlockedExchange((LONG*)&_fifo.CPReadIdle, 1); - } - } -} - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "MemoryUtil.h" +#include "Thread.h" +#include "OpcodeDecoding.h" + +#include "Fifo.h" + +extern u8* g_pVideoData; + +bool fifoStateRun = true; + +// STATE_TO_SAVE +static u8 *videoBuffer; +static int size = 0; + +void Fifo_DoState(PointerWrap &p) +{ + p.DoArray(videoBuffer, FIFO_SIZE); + p.Do(size); + int pos = (int)(g_pVideoData-videoBuffer); // get offset + p.Do(pos); // read or write offset (depends on the mode afaik) + g_pVideoData = &videoBuffer[pos]; // overwrite g_pVideoData -> expected no change when load ss and change when save ss +} + +void Fifo_Init() +{ + videoBuffer = (u8*)AllocateMemoryPages(FIFO_SIZE); + fifoStateRun = true; +} + +void Fifo_Shutdown() +{ + FreeMemoryPages(videoBuffer, FIFO_SIZE); + fifoStateRun = false; +} + +void Fifo_Stop() +{ + fifoStateRun = false; +} + +u8* FAKE_GetFifoStartPtr() +{ + return videoBuffer; +} + +u8* FAKE_GetFifoEndPtr() +{ + return &videoBuffer[size]; +} + +void Video_SendFifoData(u8* _uData, u32 len) +{ + if (size + len >= FIFO_SIZE) + { + int pos = (int)(g_pVideoData-videoBuffer); + if (size-pos > pos) + { + PanicAlert("FIFO out of bounds (sz = %i, at %08x)", size, pos); + } + memmove(&videoBuffer[0], &videoBuffer[pos], size - pos ); + size -= pos; + g_pVideoData = FAKE_GetFifoStartPtr(); + } + memcpy(videoBuffer + size, _uData, len); + size += len; + OpcodeDecoder_Run(); +} + +void Fifo_EnterLoop(const SVideoInitialize &video_initialize) +{ + SCPFifoStruct &_fifo = *video_initialize.pCPFifo; + u32 distToSend; + +#ifdef _WIN32 + // TODO(ector): Don't peek so often! + while (video_initialize.pPeekMessages()) +#else + while (fifoStateRun) +#endif + { + if (_fifo.CPReadWriteDistance < _fifo.CPLoWatermark) + Common::SleepCurrentThread(1); + //etc... + + // check if we are able to run this buffer + if ((_fifo.bFF_GPReadEnable) && _fifo.CPReadWriteDistance && !(_fifo.bFF_BPEnable && _fifo.bFF_Breakpoint)) + { + Common::SyncInterlockedExchange((LONG*)&_fifo.CPReadIdle, 0); + //video_initialize.pLog("RUN...........................",FALSE); + int peek_counter = 0; + while (_fifo.bFF_GPReadEnable && (_fifo.CPReadWriteDistance > 0)) + { + peek_counter++; + if (peek_counter == 50) { + video_initialize.pPeekMessages(); + peek_counter = 0; + } + // read the data and send it to the VideoPlugin + u32 readPtr = _fifo.CPReadPointer; + u8 *uData = video_initialize.pGetMemoryPointer(readPtr); + + // if we are on BP mode we must send 32B chunks to Video plugin for BP checking + // TODO (mb2): test & check if MP1/MP2 realy need this now. + if (_fifo.bFF_BPEnable) + { + if (readPtr == _fifo.CPBreakpoint) + { + video_initialize.pLog("!!! BP irq raised",FALSE); + Common::SyncInterlockedExchange((LONG*)&_fifo.bFF_Breakpoint, 1); + + video_initialize.pUpdateInterrupts(); + break; + } + distToSend = 32; + readPtr += 32; + if ( readPtr >= _fifo.CPEnd) + readPtr = _fifo.CPBase; + } + else + { +#if 0 // ugly random GP slowdown for testing DC robustness... TODO: remove when completly sure DC is ok + int r=rand();if ((r&0xF)==r) Common::SleepCurrentThread(r); + distToSend = 32; + readPtr += 32; + if ( readPtr >= _fifo.CPEnd) + readPtr = _fifo.CPBase; +#else + // sending the whole CPReadWriteDistance + distToSend = _fifo.CPReadWriteDistance; + if ( (distToSend+readPtr) >= _fifo.CPEnd) // TODO: better? + { + distToSend =_fifo.CPEnd - readPtr; + readPtr = _fifo.CPBase; + } + else + readPtr += distToSend; +#endif + } + Video_SendFifoData(uData, distToSend); + Common::SyncInterlockedExchange((LONG*)&_fifo.CPReadPointer, readPtr); + Common::SyncInterlockedExchangeAdd((LONG*)&_fifo.CPReadWriteDistance, -distToSend); + } + //video_initialize.pLog("..........................IDLE",FALSE); + Common::SyncInterlockedExchange((LONG*)&_fifo.CPReadIdle, 1); + } + } +} + diff --git a/Source/Core/VideoCommon/Src/LookUpTables.cpp b/Source/Core/VideoCommon/Src/LookUpTables.cpp index 99453f0ffc..3bb7cc5695 100644 --- a/Source/Core/VideoCommon/Src/LookUpTables.cpp +++ b/Source/Core/VideoCommon/Src/LookUpTables.cpp @@ -1,45 +1,45 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "LookUpTables.h" - -const int lut3to8[] = { 0x00,0x24,0x48,0x6D,0x91,0xB6,0xDA,0xFF}; -const int lut4to8[] = { 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77, - 0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF}; -const int lut5to8[] = { 0x00,0x08,0x10,0x18,0x20,0x29,0x31,0x39, - 0x41,0x4A,0x52,0x5A,0x62,0x6A,0x73,0x7B, - 0x83,0x8B,0x94,0x9C,0xA4,0xAC,0xB4,0xBD, - 0xC5,0xCD,0xD5,0xDE,0xE6,0xEE,0xF6,0xFF}; -int lut6to8[64]; -float lutu8tosfloat[256]; -float lutu8toufloat[256]; -float luts8tosfloat[256]; -float shiftLookup[32]; - -void InitLUTs() -{ - for (int i = 0; i < 32; i++) - shiftLookup[i] = 1.0f / float(1 << i); - for (int i = 0; i < 64; i++) - lut6to8[i] = (i*255) / 63; - for (int i = 0; i < 256; i++) - { - lutu8tosfloat[i] = (float)(i - 128) / 127.0f; - lutu8toufloat[i] = (float)(i) / 255.0f; - luts8tosfloat[i] = ((float)(signed char)(char)i) / 127.0f; - } -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "LookUpTables.h" + +const int lut3to8[] = { 0x00,0x24,0x48,0x6D,0x91,0xB6,0xDA,0xFF}; +const int lut4to8[] = { 0x00,0x11,0x22,0x33,0x44,0x55,0x66,0x77, + 0x88,0x99,0xAA,0xBB,0xCC,0xDD,0xEE,0xFF}; +const int lut5to8[] = { 0x00,0x08,0x10,0x18,0x20,0x29,0x31,0x39, + 0x41,0x4A,0x52,0x5A,0x62,0x6A,0x73,0x7B, + 0x83,0x8B,0x94,0x9C,0xA4,0xAC,0xB4,0xBD, + 0xC5,0xCD,0xD5,0xDE,0xE6,0xEE,0xF6,0xFF}; +int lut6to8[64]; +float lutu8tosfloat[256]; +float lutu8toufloat[256]; +float luts8tosfloat[256]; +float shiftLookup[32]; + +void InitLUTs() +{ + for (int i = 0; i < 32; i++) + shiftLookup[i] = 1.0f / float(1 << i); + for (int i = 0; i < 64; i++) + lut6to8[i] = (i*255) / 63; + for (int i = 0; i < 256; i++) + { + lutu8tosfloat[i] = (float)(i - 128) / 127.0f; + lutu8toufloat[i] = (float)(i) / 255.0f; + luts8tosfloat[i] = ((float)(signed char)(char)i) / 127.0f; + } +} diff --git a/Source/Core/VideoCommon/Src/PixelShader.cpp b/Source/Core/VideoCommon/Src/PixelShader.cpp index 799335735d..c036ef7a83 100644 --- a/Source/Core/VideoCommon/Src/PixelShader.cpp +++ b/Source/Core/VideoCommon/Src/PixelShader.cpp @@ -1,846 +1,846 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include -#include -#include - -#include "Profiler.h" -#include "PixelShader.h" -#include "XFMemory.h" // for texture projection mode -#include "BPMemory.h" - -// old tev->pixelshader notes -// -// color for this stage (alpha, color) is given by bpmem.tevorders[0].colorchan0 -// konstant for this stage (alpha, color) is given by bpmem.tevksel -// inputs are given by bpmem.combiners[0].colorC.a/b/c/d << could be current chan color -// according to GXTevColorArg table above -// output is given by .outreg -// tevtemp is set according to swapmodetables and - -static void WriteStage(char *&p, int n, u32 texture_mask); -static void WrapNonPow2Tex(char* &p, const char* var, int texmap, u32 texture_mask); -static void WriteAlphaCompare(char *&p, int num, int comp); -static bool WriteAlphaTest(char *&p); - -const float epsilon8bit = 1.0f / 255.0f; - -static const char *tevKSelTableC[] = // KCSEL -{ - "1.0f,1.0f,1.0f", //1 = 0x00 - "0.875,0.875,0.875",//7_8 = 0x01 - "0.75,0.75,0.75", //3_4 = 0x02 - "0.625,0.625,0.625",//5_8 = 0x03 - "0.5,0.5,0.5", //1_2 = 0x04 - "0.375,0.375,0.375",//3_8 = 0x05 - "0.25,0.25,0.25", //1_4 = 0x06 - "0.125,0.125,0.125",//1_8 = 0x07 - "ERROR", //0x08 - "ERROR", //0x09 - "ERROR", //0x0a - "ERROR", //0x0b - I_KCOLORS"[0].rgb",//K0 = 0x0C - I_KCOLORS"[1].rgb",//K1 = 0x0D - I_KCOLORS"[2].rgb",//K2 = 0x0E - I_KCOLORS"[3].rgb",//K3 = 0x0F - I_KCOLORS"[0].rrr",//K0_R = 0x10 - I_KCOLORS"[1].rrr",//K1_R = 0x11 - I_KCOLORS"[2].rrr",//K2_R = 0x12 - I_KCOLORS"[3].rrr",//K3_R = 0x13 - I_KCOLORS"[0].ggg",//K0_G = 0x14 - I_KCOLORS"[1].ggg",//K1_G = 0x15 - I_KCOLORS"[2].ggg",//K2_G = 0x16 - I_KCOLORS"[3].ggg",//K3_G = 0x17 - I_KCOLORS"[0].bbb",//K0_B = 0x18 - I_KCOLORS"[1].bbb",//K1_B = 0x19 - I_KCOLORS"[2].bbb",//K2_B = 0x1A - I_KCOLORS"[3].bbb",//K3_B = 0x1B - I_KCOLORS"[0].aaa",//K0_A = 0x1C - I_KCOLORS"[1].aaa",//K1_A = 0x1D - I_KCOLORS"[2].aaa",//K2_A = 0x1E - I_KCOLORS"[3].aaa",//K3_A = 0x1F -}; - -static const char *tevKSelTableA[] = // KASEL -{ - "1.0f", //1 = 0x00 - "0.875f",//7_8 = 0x01 - "0.75f", //3_4 = 0x02 - "0.625f",//5_8 = 0x03 - "0.5f", //1_2 = 0x04 - "0.375f",//3_8 = 0x05 - "0.25f", //1_4 = 0x06 - "0.125f",//1_8 = 0x07 - "ERROR", //0x08 - "ERROR", //0x09 - "ERROR", //0x0a - "ERROR", //0x0b - "ERROR", //0x0c - "ERROR", //0x0d - "ERROR", //0x0e - "ERROR", //0x0f - I_KCOLORS"[0].r",//K0_R = 0x10 - I_KCOLORS"[1].r",//K1_R = 0x11 - I_KCOLORS"[2].r",//K2_R = 0x12 - I_KCOLORS"[3].r",//K3_R = 0x13 - I_KCOLORS"[0].g",//K0_G = 0x14 - I_KCOLORS"[1].g",//K1_G = 0x15 - I_KCOLORS"[2].g",//K2_G = 0x16 - I_KCOLORS"[3].g",//K3_G = 0x17 - I_KCOLORS"[0].b",//K0_B = 0x18 - I_KCOLORS"[1].b",//K1_B = 0x19 - I_KCOLORS"[2].b",//K2_B = 0x1A - I_KCOLORS"[3].b",//K3_B = 0x1B - I_KCOLORS"[0].a",//K0_A = 0x1C - I_KCOLORS"[1].a",//K1_A = 0x1D - I_KCOLORS"[2].a",//K2_A = 0x1E - I_KCOLORS"[3].a",//K3_A = 0x1F -}; - -static const char *tevScaleTable[] = // CS -{ - "1.0f", //SCALE_1 - "2.0f", //SCALE_2 - "4.0f", //SCALE_4 - "0.5f",//DIVIDE_2 -}; - -static const char *tevBiasTable[] = // TB -{ - "", //ZERO, - "+0.5f", //ADDHALF, - "-0.5f", //SUBHALF, - "", -}; - -static const char *tevOpTable[] = { // TEV - "+", //TEVOP_ADD = 0, - "-", //TEVOP_SUB = 1, -}; - -//static const char *tevCompOpTable[] = { ">", "==" }; - -#define TEVCMP_R8 0 -#define TEVCMP_GR16 1 -#define TEVCMP_BGR24 2 -#define TEVCMP_RGB8 3 - -static const char *tevCInputTable[] = // CC -{ - "prev.rgb", //CPREV, - "prev.aaa", //APREV, - "c0.rgb", //C0, - "c0.aaa", //A0, - "c1.rgb", //C1, - "c1.aaa", //A1, - "c2.rgb", //C2, - "c2.aaa", //A2, - "textemp.rgb", //TEXC, - "textemp.aaa", //TEXA, - "rastemp.rgb", //RASC, - "rastemp.aaa", //RASA, - "float3(1.0f,1.0f,1.0f)", //ONE, - "float3(.5f,.5f,.5f)", //HALF, - "konsttemp.rgb", //KONST, - "float3(0.0f,0.0f,0.0f)", //ZERO - "PADERROR", "PADERROR", "PADERROR", "PADERROR", - "PADERROR", "PADERROR", "PADERROR", "PADERROR", - "PADERROR", "PADERROR", "PADERROR", "PADERROR", - "PADERROR", "PADERROR", "PADERROR", "PADERROR", - "PADERROR", "PADERROR", "PADERROR", "PADERROR", -}; - -static const char *tevCInputTable2[] = // CC -{ - "prev", //CPREV, - "(prev.aaa)", //APREV, - "c0", //C0, - "(c0.aaa)", //A0, - "c1", //C1, - "(c1.aaa)", //A1, - "c2", //C2, - "(c2.aaa)", //A2, - "textemp", //TEXC, - "(textemp.aaa)", //TEXA, - "rastemp", //RASC, - "(rastemp.aaa)", //RASA, - "float3(1.0f,1.0f,1.0f)", //ONE, - "float3(.5f,.5f,.5f)", //HALF, - "konsttemp", //"konsttemp.rgb", //KONST, - "float3(0.0f,0.0f,0.0f)", //ZERO - "PADERROR", "PADERROR", "PADERROR", "PADERROR", - "PADERROR", "PADERROR", "PADERROR", "PADERROR", - "PADERROR", "PADERROR", "PADERROR", "PADERROR", - "PADERROR", "PADERROR", "PADERROR", "PADERROR", - "PADERROR", "PADERROR", "PADERROR", "PADERROR", -}; - -static const char *tevAInputTable[] = // CA -{ - "prev.a", //APREV, - "c0.a", //A0, - "c1.a", //A1, - "c2.a", //A2, - "textemp.a", //TEXA, - "rastemp.a", //RASA, - "konsttemp.a", //KONST - "0.0", //ZERO - "PADERROR", "PADERROR", "PADERROR", "PADERROR", - "PADERROR", "PADERROR", "PADERROR", "PADERROR", - "PADERROR", "PADERROR", "PADERROR", "PADERROR", - "PADERROR", "PADERROR", "PADERROR", -}; - -static const char *tevAInputTable2[] = // CA -{ - "prev", //APREV, - "c0", //A0, - "c1", //A1, - "c2", //A2, - "textemp", //TEXA, - "rastemp", //RASA, - "konsttemp", //KONST, (hw1 had quarter) - "float4(0,0,0,0)", //ZERO - "PADERROR", "PADERROR", "PADERROR", "PADERROR", - "PADERROR", "PADERROR", "PADERROR", "PADERROR", - "PADERROR", "PADERROR", "PADERROR", "PADERROR", - "PADERROR", "PADERROR", "PADERROR", "PADERROR", -}; - -static const char *tevRasTable[] = -{ - "colors[0]", - "colors[1]", - "ERROR", //2 - "ERROR", //3 - "ERROR", //4 - "alphabump", // use bump alpha - "(alphabump*(255.0f/248.0f))", //normalized - "float4(0,0,0,0)", // zero -}; - -static const char *alphaRef[2] = -{ - I_ALPHA"[0].x", - I_ALPHA"[0].y" -}; - -//static const char *tevTexFunc[] = { "tex2D", "texRECT" }; - -static const char *tevCOutputTable[] = { "prev.rgb", "c0.rgb", "c1.rgb", "c2.rgb" }; -static const char *tevAOutputTable[] = { "prev.a", "c0.a", "c1.a", "c2.a" }; -static const char* tevIndAlphaSel[] = {"", "x", "y", "z"}; -static const char* tevIndAlphaScale[] = {"", "*32","*16","*8"}; -static const char* tevIndBiasField[] = {"", "x", "y", "xy", "z", "xz", "yz", "xyz"}; // indexed by bias -static const char* tevIndBiasAdd[] = {"-128.0f", "1.0f", "1.0f", "1.0f" }; // indexed by fmt -static const char* tevIndWrapStart[] = {"0", "256", "128", "64", "32", "16", "0.001" }; -static const char* tevIndFmtScale[] = {"255.0f", "31.0f", "15.0f", "8.0f" }; - -#define WRITE p+=sprintf - -static const char *swapColors = "rgba"; -static char swapModeTable[4][5]; - -static char text[16384]; - -static void BuildSwapModeTable() -{ - //bpmem.tevregs[0]. - for (int i = 0; i < 4; i++) - { - swapModeTable[i][0] = swapColors[bpmem.tevksel[i*2].swap1]; - swapModeTable[i][1] = swapColors[bpmem.tevksel[i*2].swap2]; - swapModeTable[i][2] = swapColors[bpmem.tevksel[i*2+1].swap1]; - swapModeTable[i][3] = swapColors[bpmem.tevksel[i*2+1].swap2]; - swapModeTable[i][4] = 0; - } -} - -char *GeneratePixelShader(u32 texture_mask, bool has_zbuffer_target, bool bRenderZToCol0) -{ - text[sizeof(text) - 1] = 0x7C; // canary - DVSTARTPROFILE(); - - BuildSwapModeTable(); - int numStages = bpmem.genMode.numtevstages + 1; - int numTexgen = bpmem.genMode.numtexgens; - - char *p = text; - WRITE(p, "//Pixel Shader for TEV stages\n"); - WRITE(p, "//%i TEV stages, %i texgens, %i IND stages\n", - numStages, numTexgen, bpmem.genMode.numindstages); - - bool bRenderZ = has_zbuffer_target && bpmem.zmode.updateenable; - bool bOutputZ = bpmem.ztex2.op != ZTEXTURE_DISABLE; - bool bInputZ = bpmem.ztex2.op==ZTEXTURE_ADD || bRenderZ; - - // bool bRenderZToCol0 = ; // output z and alpha to color0 - assert( !bRenderZToCol0 || bRenderZ ); - - int ztexcoord = -1; - if (bInputZ) - ztexcoord = numTexgen == 0 ? 0 : numTexgen-1; - - int nIndirectStagesUsed = 0; - if (bpmem.genMode.numindstages > 0) { - for (int i = 0; i < numStages; ++i) { - if (bpmem.tevind[i].IsActive() && bpmem.tevind[i].bt < bpmem.genMode.numindstages) { - nIndirectStagesUsed |= 1<= 1.0f )\n", bpmem.tevind[n].bt, - tevIndAlphaSel[bpmem.tevind[n].bs]); - WRITE(p, " alphabump = 1.0f;\n"); - WRITE(p, "else\n"); - WRITE(p, " alphabump = fract ( indtex%d.%s %s );\n", bpmem.tevind[n].bt, - tevIndAlphaSel[bpmem.tevind[n].bs], tevIndAlphaScale[bpmem.tevind[n].fmt]); - } - } - - // bias - WRITE(p, "float3 indtevcrd%d = indtex%d;\n", n, bpmem.tevind[n].bt); - WRITE(p, "indtevcrd%d.xyz *= %s;\n", n, tevIndFmtScale[bpmem.tevind[n].fmt]); - if (bpmem.tevind[n].bias != ITB_NONE ) - WRITE(p, "indtevcrd%d.%s += %s;\n", n, tevIndBiasField[bpmem.tevind[n].bias], tevIndBiasAdd[bpmem.tevind[n].fmt]); - - // multiply by offset matrix and scale - if (bpmem.tevind[n].mid != 0) { - if (bpmem.tevind[n].mid <= 3) { - int mtxidx = 2*(bpmem.tevind[n].mid-1); - WRITE(p, "float2 indtevtrans%d = float2(dot("I_INDTEXMTX"[%d].xyz, indtevcrd%d), dot("I_INDTEXMTX"[%d].xyz, indtevcrd%d));\n", - n, mtxidx, n, mtxidx+1, n); - } - else if (bpmem.tevind[n].mid <= 5) { // s matrix - int mtxidx = 2*(bpmem.tevind[n].mid-5); - WRITE(p, "float2 indtevtrans%d = "I_INDTEXMTX"[%d].ww * uv%d.xy * indtevcrd%d.xx;\n", n, mtxidx, texcoord, n); - } - else if (bpmem.tevind[n].mid <= 9) { // t matrix - int mtxidx = 2*(bpmem.tevind[n].mid-9); - WRITE(p, "float2 indtevtrans%d = "I_INDTEXMTX"[%d].ww * uv%d.xy * indtevcrd%d.yy;\n", n, mtxidx, texcoord, n); - } - else { - // TODO: I removed a superfluous argument, please check that the resulting expression is correct. (mthuurne 2008-08-27) - WRITE(p, "float2 indtevtrans%d = 0;\n", n); //, n - } - } - else { - // TODO: I removed a superfluous argument, please check that the resulting expression is correct. (mthuurne 2008-08-27) - WRITE(p, "float2 indtevtrans%d = 0;\n", n); //, n - } - - // wrapping - if (!bpmem.tevorders[n/2].getEnable(n&1) || (texture_mask & (1< %s.%s) ? %s : float3(0.0f,0.0f,0.0f));\n", - tevCInputTable[cc.d], tevCInputTable2[cc.a], cmp==TEVCMP_R8_GT?"r":"rgb", tevCInputTable2[cc.b], cmp==TEVCMP_R8_GT?"r":"rgb", tevCInputTable[cc.c]); - break; - case TEVCMP_R8_EQ: - case TEVCMP_RGB8_EQ: - WRITE(p, " %s + (abs(%s.r - %s.r)<%f ? %s : float3(0.0f,0.0f,0.0f));\n", - tevCInputTable[cc.d], tevCInputTable2[cc.a], tevCInputTable2[cc.b], epsilon8bit, tevCInputTable[cc.c]); - break; - - case TEVCMP_GR16_GT: // 16 bit compares: 255*g+r (probably used for ztextures, so make sure in ztextures, g is the most significant byte) - case TEVCMP_BGR24_GT: // 24 bit compares: 255*255*b+255*g+r - WRITE(p, " %s + (( dot(%s.rgb-%s.rgb, comp%s) > 0) ? %s : float3(0.0f,0.0f,0.0f));\n", - tevCInputTable[cc.d], tevCInputTable2[cc.a], tevCInputTable2[cc.b], cmp==TEVCMP_GR16_GT?"16":"24", tevCInputTable[cc.c]); - break; - case TEVCMP_GR16_EQ: - case TEVCMP_BGR24_EQ: - WRITE(p, " %s + (abs(dot(%s.rgb - %s.rgb, comp%s))<%f ? %s : float3(0.0f,0.0f,0.0f));\n", - tevCInputTable[cc.d], tevCInputTable2[cc.a], tevCInputTable2[cc.b], cmp==TEVCMP_GR16_GT?"16":"24", epsilon8bit, tevCInputTable[cc.c]); - break; - default: - WRITE(p, "float3(0.0f,0.0f,0.0f);\n"); - break; - } - } - - if (cc.clamp) - WRITE(p, "%s = clamp(%s,0.0f,1.0f);\n", tevCOutputTable[cc.dest],tevCOutputTable[cc.dest]); - - // combine the alpha channel - WRITE(p, "%s= ", tevAOutputTable[ac.dest]); - - if (ac.bias != 3) { // if not compare - //normal alpha combiner goes here - WRITE(p, " %s*(%s%s",tevScaleTable[ac.shift],tevAInputTable[ac.d],tevOpTable[ac.op]); - WRITE(p, "lerp(%s,%s,%s) %s)\n", - tevAInputTable[ac.a],tevAInputTable[ac.b], - tevAInputTable[ac.c],tevBiasTable[ac.bias]); - } - else { - //compare alpha combiner goes here - int cmp = (ac.shift<<1)|ac.op|8; // comparemode stored here - switch(cmp) { - case TEVCMP_R8_GT: - case TEVCMP_A8_GT: - WRITE(p, " %s + ((%s.%s > %s.%s) ? %s : 0)\n", - tevAInputTable[ac.d],tevAInputTable2[ac.a], cmp==TEVCMP_R8_GT?"r":"a", tevAInputTable2[ac.b], cmp==TEVCMP_R8_GT?"r":"a", tevAInputTable[ac.c]); - break; - case TEVCMP_R8_EQ: - case TEVCMP_A8_EQ: - WRITE(p, " %s + (abs(%s.r - %s.r)<%f ? %s : 0)\n", - tevAInputTable[ac.d],tevAInputTable2[ac.a], tevAInputTable2[ac.b],epsilon8bit,tevAInputTable[ac.c]); - break; - - case TEVCMP_GR16_GT: // 16 bit compares: 255*g+r (probably used for ztextures, so make sure in ztextures, g is the most significant byte) - case TEVCMP_BGR24_GT: // 24 bit compares: 255*255*b+255*g+r - WRITE(p, " %s + (( dot(%s.rgb-%s.rgb, comp%s) > 0) ? %s : 0)\n", - tevAInputTable[ac.d],tevAInputTable2[ac.a], tevAInputTable2[ac.b], cmp==TEVCMP_GR16_GT?"16":"24", tevAInputTable[ac.c]); - break; - case TEVCMP_GR16_EQ: - case TEVCMP_BGR24_EQ: - WRITE(p, " %s + (abs(dot(%s.rgb - %s.rgb, comp%s))<%f ? %s : 0)\n", - tevAInputTable[ac.d],tevAInputTable2[ac.a], tevAInputTable2[ac.b],cmp==TEVCMP_GR16_GT?"16":"24",epsilon8bit,tevAInputTable[ac.c]); - break; - default: - WRITE(p, "0)\n"); - break; - } - } - - WRITE(p, ";\n"); - - if (ac.clamp) - WRITE(p, "%s = clamp(%s,0.0f,1.0f);\n", tevAOutputTable[ac.dest],tevAOutputTable[ac.dest]); - WRITE(p, "\n"); -} - -void WrapNonPow2Tex(char* &p, const char* var, int texmap, u32 texture_mask) -{ - _assert_(texture_mask & (1< %s)",alphaRef[num]); break; - case ALPHACMP_LESS: WRITE(p, "(prev.a >= %s - %f)",alphaRef[num],epsilon8bit*0.5f);break; - case ALPHACMP_GEQUAL: WRITE(p, "(prev.a < %s)",alphaRef[num]); break; - case ALPHACMP_GREATER: WRITE(p, "(prev.a <= %s + %f)",alphaRef[num],epsilon8bit*0.5f);break; - case ALPHACMP_EQUAL: WRITE(p, "(abs(prev.a-%s)>%f)",alphaRef[num],epsilon8bit*2); break; - case ALPHACMP_NEQUAL: WRITE(p, "(abs(prev.a-%s)<%f)",alphaRef[num],epsilon8bit*2); break; - } -} - -static bool WriteAlphaTest(char *&p) -{ - u32 op = bpmem.alphaFunc.logic; - u32 comp[2] = {bpmem.alphaFunc.comp0,bpmem.alphaFunc.comp1}; - - //first kill all the simple cases - switch(op) { - case 0: // and - if (comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_ALWAYS) return true; - if (comp[0] == ALPHACMP_NEVER || comp[1] == ALPHACMP_NEVER) { - WRITE(p, "discard;\n"); - return false; - } - break; - case 1: // or - if (comp[0] == ALPHACMP_ALWAYS || comp[1] == ALPHACMP_ALWAYS) return true; - if (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_NEVER) { - WRITE(p, "discard;\n"); - return false; - } - break; - case 2: // xor - if ( (comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_NEVER) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_ALWAYS) ) return true; - if ( (comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_ALWAYS) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_NEVER)) { - WRITE(p, "discard;\n"); - return false; - } - break; - case 3: // xnor - if ( (comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_NEVER) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_ALWAYS)) { - WRITE(p, "discard;\n"); - return false; - } - if ( (comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_ALWAYS) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_NEVER) ) - return true; - break; - } - - WRITE(p, "discard( "); - WriteAlphaCompare(p, 0, bpmem.alphaFunc.comp0); - - // negated because testing the inverse condition - switch(bpmem.alphaFunc.logic) { - case 0: WRITE(p, " || "); break; // and - case 1: WRITE(p, " && "); break; // or - case 2: WRITE(p, " == "); break; // xor - case 3: WRITE(p, " != "); break; // xnor - } - WriteAlphaCompare(p, 1, bpmem.alphaFunc.comp1); - WRITE(p, ");\n"); - return true; -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include +#include +#include + +#include "Profiler.h" +#include "PixelShader.h" +#include "XFMemory.h" // for texture projection mode +#include "BPMemory.h" + +// old tev->pixelshader notes +// +// color for this stage (alpha, color) is given by bpmem.tevorders[0].colorchan0 +// konstant for this stage (alpha, color) is given by bpmem.tevksel +// inputs are given by bpmem.combiners[0].colorC.a/b/c/d << could be current chan color +// according to GXTevColorArg table above +// output is given by .outreg +// tevtemp is set according to swapmodetables and + +static void WriteStage(char *&p, int n, u32 texture_mask); +static void WrapNonPow2Tex(char* &p, const char* var, int texmap, u32 texture_mask); +static void WriteAlphaCompare(char *&p, int num, int comp); +static bool WriteAlphaTest(char *&p); + +const float epsilon8bit = 1.0f / 255.0f; + +static const char *tevKSelTableC[] = // KCSEL +{ + "1.0f,1.0f,1.0f", //1 = 0x00 + "0.875,0.875,0.875",//7_8 = 0x01 + "0.75,0.75,0.75", //3_4 = 0x02 + "0.625,0.625,0.625",//5_8 = 0x03 + "0.5,0.5,0.5", //1_2 = 0x04 + "0.375,0.375,0.375",//3_8 = 0x05 + "0.25,0.25,0.25", //1_4 = 0x06 + "0.125,0.125,0.125",//1_8 = 0x07 + "ERROR", //0x08 + "ERROR", //0x09 + "ERROR", //0x0a + "ERROR", //0x0b + I_KCOLORS"[0].rgb",//K0 = 0x0C + I_KCOLORS"[1].rgb",//K1 = 0x0D + I_KCOLORS"[2].rgb",//K2 = 0x0E + I_KCOLORS"[3].rgb",//K3 = 0x0F + I_KCOLORS"[0].rrr",//K0_R = 0x10 + I_KCOLORS"[1].rrr",//K1_R = 0x11 + I_KCOLORS"[2].rrr",//K2_R = 0x12 + I_KCOLORS"[3].rrr",//K3_R = 0x13 + I_KCOLORS"[0].ggg",//K0_G = 0x14 + I_KCOLORS"[1].ggg",//K1_G = 0x15 + I_KCOLORS"[2].ggg",//K2_G = 0x16 + I_KCOLORS"[3].ggg",//K3_G = 0x17 + I_KCOLORS"[0].bbb",//K0_B = 0x18 + I_KCOLORS"[1].bbb",//K1_B = 0x19 + I_KCOLORS"[2].bbb",//K2_B = 0x1A + I_KCOLORS"[3].bbb",//K3_B = 0x1B + I_KCOLORS"[0].aaa",//K0_A = 0x1C + I_KCOLORS"[1].aaa",//K1_A = 0x1D + I_KCOLORS"[2].aaa",//K2_A = 0x1E + I_KCOLORS"[3].aaa",//K3_A = 0x1F +}; + +static const char *tevKSelTableA[] = // KASEL +{ + "1.0f", //1 = 0x00 + "0.875f",//7_8 = 0x01 + "0.75f", //3_4 = 0x02 + "0.625f",//5_8 = 0x03 + "0.5f", //1_2 = 0x04 + "0.375f",//3_8 = 0x05 + "0.25f", //1_4 = 0x06 + "0.125f",//1_8 = 0x07 + "ERROR", //0x08 + "ERROR", //0x09 + "ERROR", //0x0a + "ERROR", //0x0b + "ERROR", //0x0c + "ERROR", //0x0d + "ERROR", //0x0e + "ERROR", //0x0f + I_KCOLORS"[0].r",//K0_R = 0x10 + I_KCOLORS"[1].r",//K1_R = 0x11 + I_KCOLORS"[2].r",//K2_R = 0x12 + I_KCOLORS"[3].r",//K3_R = 0x13 + I_KCOLORS"[0].g",//K0_G = 0x14 + I_KCOLORS"[1].g",//K1_G = 0x15 + I_KCOLORS"[2].g",//K2_G = 0x16 + I_KCOLORS"[3].g",//K3_G = 0x17 + I_KCOLORS"[0].b",//K0_B = 0x18 + I_KCOLORS"[1].b",//K1_B = 0x19 + I_KCOLORS"[2].b",//K2_B = 0x1A + I_KCOLORS"[3].b",//K3_B = 0x1B + I_KCOLORS"[0].a",//K0_A = 0x1C + I_KCOLORS"[1].a",//K1_A = 0x1D + I_KCOLORS"[2].a",//K2_A = 0x1E + I_KCOLORS"[3].a",//K3_A = 0x1F +}; + +static const char *tevScaleTable[] = // CS +{ + "1.0f", //SCALE_1 + "2.0f", //SCALE_2 + "4.0f", //SCALE_4 + "0.5f",//DIVIDE_2 +}; + +static const char *tevBiasTable[] = // TB +{ + "", //ZERO, + "+0.5f", //ADDHALF, + "-0.5f", //SUBHALF, + "", +}; + +static const char *tevOpTable[] = { // TEV + "+", //TEVOP_ADD = 0, + "-", //TEVOP_SUB = 1, +}; + +//static const char *tevCompOpTable[] = { ">", "==" }; + +#define TEVCMP_R8 0 +#define TEVCMP_GR16 1 +#define TEVCMP_BGR24 2 +#define TEVCMP_RGB8 3 + +static const char *tevCInputTable[] = // CC +{ + "prev.rgb", //CPREV, + "prev.aaa", //APREV, + "c0.rgb", //C0, + "c0.aaa", //A0, + "c1.rgb", //C1, + "c1.aaa", //A1, + "c2.rgb", //C2, + "c2.aaa", //A2, + "textemp.rgb", //TEXC, + "textemp.aaa", //TEXA, + "rastemp.rgb", //RASC, + "rastemp.aaa", //RASA, + "float3(1.0f,1.0f,1.0f)", //ONE, + "float3(.5f,.5f,.5f)", //HALF, + "konsttemp.rgb", //KONST, + "float3(0.0f,0.0f,0.0f)", //ZERO + "PADERROR", "PADERROR", "PADERROR", "PADERROR", + "PADERROR", "PADERROR", "PADERROR", "PADERROR", + "PADERROR", "PADERROR", "PADERROR", "PADERROR", + "PADERROR", "PADERROR", "PADERROR", "PADERROR", + "PADERROR", "PADERROR", "PADERROR", "PADERROR", +}; + +static const char *tevCInputTable2[] = // CC +{ + "prev", //CPREV, + "(prev.aaa)", //APREV, + "c0", //C0, + "(c0.aaa)", //A0, + "c1", //C1, + "(c1.aaa)", //A1, + "c2", //C2, + "(c2.aaa)", //A2, + "textemp", //TEXC, + "(textemp.aaa)", //TEXA, + "rastemp", //RASC, + "(rastemp.aaa)", //RASA, + "float3(1.0f,1.0f,1.0f)", //ONE, + "float3(.5f,.5f,.5f)", //HALF, + "konsttemp", //"konsttemp.rgb", //KONST, + "float3(0.0f,0.0f,0.0f)", //ZERO + "PADERROR", "PADERROR", "PADERROR", "PADERROR", + "PADERROR", "PADERROR", "PADERROR", "PADERROR", + "PADERROR", "PADERROR", "PADERROR", "PADERROR", + "PADERROR", "PADERROR", "PADERROR", "PADERROR", + "PADERROR", "PADERROR", "PADERROR", "PADERROR", +}; + +static const char *tevAInputTable[] = // CA +{ + "prev.a", //APREV, + "c0.a", //A0, + "c1.a", //A1, + "c2.a", //A2, + "textemp.a", //TEXA, + "rastemp.a", //RASA, + "konsttemp.a", //KONST + "0.0", //ZERO + "PADERROR", "PADERROR", "PADERROR", "PADERROR", + "PADERROR", "PADERROR", "PADERROR", "PADERROR", + "PADERROR", "PADERROR", "PADERROR", "PADERROR", + "PADERROR", "PADERROR", "PADERROR", +}; + +static const char *tevAInputTable2[] = // CA +{ + "prev", //APREV, + "c0", //A0, + "c1", //A1, + "c2", //A2, + "textemp", //TEXA, + "rastemp", //RASA, + "konsttemp", //KONST, (hw1 had quarter) + "float4(0,0,0,0)", //ZERO + "PADERROR", "PADERROR", "PADERROR", "PADERROR", + "PADERROR", "PADERROR", "PADERROR", "PADERROR", + "PADERROR", "PADERROR", "PADERROR", "PADERROR", + "PADERROR", "PADERROR", "PADERROR", "PADERROR", +}; + +static const char *tevRasTable[] = +{ + "colors[0]", + "colors[1]", + "ERROR", //2 + "ERROR", //3 + "ERROR", //4 + "alphabump", // use bump alpha + "(alphabump*(255.0f/248.0f))", //normalized + "float4(0,0,0,0)", // zero +}; + +static const char *alphaRef[2] = +{ + I_ALPHA"[0].x", + I_ALPHA"[0].y" +}; + +//static const char *tevTexFunc[] = { "tex2D", "texRECT" }; + +static const char *tevCOutputTable[] = { "prev.rgb", "c0.rgb", "c1.rgb", "c2.rgb" }; +static const char *tevAOutputTable[] = { "prev.a", "c0.a", "c1.a", "c2.a" }; +static const char* tevIndAlphaSel[] = {"", "x", "y", "z"}; +static const char* tevIndAlphaScale[] = {"", "*32","*16","*8"}; +static const char* tevIndBiasField[] = {"", "x", "y", "xy", "z", "xz", "yz", "xyz"}; // indexed by bias +static const char* tevIndBiasAdd[] = {"-128.0f", "1.0f", "1.0f", "1.0f" }; // indexed by fmt +static const char* tevIndWrapStart[] = {"0", "256", "128", "64", "32", "16", "0.001" }; +static const char* tevIndFmtScale[] = {"255.0f", "31.0f", "15.0f", "8.0f" }; + +#define WRITE p+=sprintf + +static const char *swapColors = "rgba"; +static char swapModeTable[4][5]; + +static char text[16384]; + +static void BuildSwapModeTable() +{ + //bpmem.tevregs[0]. + for (int i = 0; i < 4; i++) + { + swapModeTable[i][0] = swapColors[bpmem.tevksel[i*2].swap1]; + swapModeTable[i][1] = swapColors[bpmem.tevksel[i*2].swap2]; + swapModeTable[i][2] = swapColors[bpmem.tevksel[i*2+1].swap1]; + swapModeTable[i][3] = swapColors[bpmem.tevksel[i*2+1].swap2]; + swapModeTable[i][4] = 0; + } +} + +char *GeneratePixelShader(u32 texture_mask, bool has_zbuffer_target, bool bRenderZToCol0) +{ + text[sizeof(text) - 1] = 0x7C; // canary + DVSTARTPROFILE(); + + BuildSwapModeTable(); + int numStages = bpmem.genMode.numtevstages + 1; + int numTexgen = bpmem.genMode.numtexgens; + + char *p = text; + WRITE(p, "//Pixel Shader for TEV stages\n"); + WRITE(p, "//%i TEV stages, %i texgens, %i IND stages\n", + numStages, numTexgen, bpmem.genMode.numindstages); + + bool bRenderZ = has_zbuffer_target && bpmem.zmode.updateenable; + bool bOutputZ = bpmem.ztex2.op != ZTEXTURE_DISABLE; + bool bInputZ = bpmem.ztex2.op==ZTEXTURE_ADD || bRenderZ; + + // bool bRenderZToCol0 = ; // output z and alpha to color0 + assert( !bRenderZToCol0 || bRenderZ ); + + int ztexcoord = -1; + if (bInputZ) + ztexcoord = numTexgen == 0 ? 0 : numTexgen-1; + + int nIndirectStagesUsed = 0; + if (bpmem.genMode.numindstages > 0) { + for (int i = 0; i < numStages; ++i) { + if (bpmem.tevind[i].IsActive() && bpmem.tevind[i].bt < bpmem.genMode.numindstages) { + nIndirectStagesUsed |= 1<= 1.0f )\n", bpmem.tevind[n].bt, + tevIndAlphaSel[bpmem.tevind[n].bs]); + WRITE(p, " alphabump = 1.0f;\n"); + WRITE(p, "else\n"); + WRITE(p, " alphabump = fract ( indtex%d.%s %s );\n", bpmem.tevind[n].bt, + tevIndAlphaSel[bpmem.tevind[n].bs], tevIndAlphaScale[bpmem.tevind[n].fmt]); + } + } + + // bias + WRITE(p, "float3 indtevcrd%d = indtex%d;\n", n, bpmem.tevind[n].bt); + WRITE(p, "indtevcrd%d.xyz *= %s;\n", n, tevIndFmtScale[bpmem.tevind[n].fmt]); + if (bpmem.tevind[n].bias != ITB_NONE ) + WRITE(p, "indtevcrd%d.%s += %s;\n", n, tevIndBiasField[bpmem.tevind[n].bias], tevIndBiasAdd[bpmem.tevind[n].fmt]); + + // multiply by offset matrix and scale + if (bpmem.tevind[n].mid != 0) { + if (bpmem.tevind[n].mid <= 3) { + int mtxidx = 2*(bpmem.tevind[n].mid-1); + WRITE(p, "float2 indtevtrans%d = float2(dot("I_INDTEXMTX"[%d].xyz, indtevcrd%d), dot("I_INDTEXMTX"[%d].xyz, indtevcrd%d));\n", + n, mtxidx, n, mtxidx+1, n); + } + else if (bpmem.tevind[n].mid <= 5) { // s matrix + int mtxidx = 2*(bpmem.tevind[n].mid-5); + WRITE(p, "float2 indtevtrans%d = "I_INDTEXMTX"[%d].ww * uv%d.xy * indtevcrd%d.xx;\n", n, mtxidx, texcoord, n); + } + else if (bpmem.tevind[n].mid <= 9) { // t matrix + int mtxidx = 2*(bpmem.tevind[n].mid-9); + WRITE(p, "float2 indtevtrans%d = "I_INDTEXMTX"[%d].ww * uv%d.xy * indtevcrd%d.yy;\n", n, mtxidx, texcoord, n); + } + else { + // TODO: I removed a superfluous argument, please check that the resulting expression is correct. (mthuurne 2008-08-27) + WRITE(p, "float2 indtevtrans%d = 0;\n", n); //, n + } + } + else { + // TODO: I removed a superfluous argument, please check that the resulting expression is correct. (mthuurne 2008-08-27) + WRITE(p, "float2 indtevtrans%d = 0;\n", n); //, n + } + + // wrapping + if (!bpmem.tevorders[n/2].getEnable(n&1) || (texture_mask & (1< %s.%s) ? %s : float3(0.0f,0.0f,0.0f));\n", + tevCInputTable[cc.d], tevCInputTable2[cc.a], cmp==TEVCMP_R8_GT?"r":"rgb", tevCInputTable2[cc.b], cmp==TEVCMP_R8_GT?"r":"rgb", tevCInputTable[cc.c]); + break; + case TEVCMP_R8_EQ: + case TEVCMP_RGB8_EQ: + WRITE(p, " %s + (abs(%s.r - %s.r)<%f ? %s : float3(0.0f,0.0f,0.0f));\n", + tevCInputTable[cc.d], tevCInputTable2[cc.a], tevCInputTable2[cc.b], epsilon8bit, tevCInputTable[cc.c]); + break; + + case TEVCMP_GR16_GT: // 16 bit compares: 255*g+r (probably used for ztextures, so make sure in ztextures, g is the most significant byte) + case TEVCMP_BGR24_GT: // 24 bit compares: 255*255*b+255*g+r + WRITE(p, " %s + (( dot(%s.rgb-%s.rgb, comp%s) > 0) ? %s : float3(0.0f,0.0f,0.0f));\n", + tevCInputTable[cc.d], tevCInputTable2[cc.a], tevCInputTable2[cc.b], cmp==TEVCMP_GR16_GT?"16":"24", tevCInputTable[cc.c]); + break; + case TEVCMP_GR16_EQ: + case TEVCMP_BGR24_EQ: + WRITE(p, " %s + (abs(dot(%s.rgb - %s.rgb, comp%s))<%f ? %s : float3(0.0f,0.0f,0.0f));\n", + tevCInputTable[cc.d], tevCInputTable2[cc.a], tevCInputTable2[cc.b], cmp==TEVCMP_GR16_GT?"16":"24", epsilon8bit, tevCInputTable[cc.c]); + break; + default: + WRITE(p, "float3(0.0f,0.0f,0.0f);\n"); + break; + } + } + + if (cc.clamp) + WRITE(p, "%s = clamp(%s,0.0f,1.0f);\n", tevCOutputTable[cc.dest],tevCOutputTable[cc.dest]); + + // combine the alpha channel + WRITE(p, "%s= ", tevAOutputTable[ac.dest]); + + if (ac.bias != 3) { // if not compare + //normal alpha combiner goes here + WRITE(p, " %s*(%s%s",tevScaleTable[ac.shift],tevAInputTable[ac.d],tevOpTable[ac.op]); + WRITE(p, "lerp(%s,%s,%s) %s)\n", + tevAInputTable[ac.a],tevAInputTable[ac.b], + tevAInputTable[ac.c],tevBiasTable[ac.bias]); + } + else { + //compare alpha combiner goes here + int cmp = (ac.shift<<1)|ac.op|8; // comparemode stored here + switch(cmp) { + case TEVCMP_R8_GT: + case TEVCMP_A8_GT: + WRITE(p, " %s + ((%s.%s > %s.%s) ? %s : 0)\n", + tevAInputTable[ac.d],tevAInputTable2[ac.a], cmp==TEVCMP_R8_GT?"r":"a", tevAInputTable2[ac.b], cmp==TEVCMP_R8_GT?"r":"a", tevAInputTable[ac.c]); + break; + case TEVCMP_R8_EQ: + case TEVCMP_A8_EQ: + WRITE(p, " %s + (abs(%s.r - %s.r)<%f ? %s : 0)\n", + tevAInputTable[ac.d],tevAInputTable2[ac.a], tevAInputTable2[ac.b],epsilon8bit,tevAInputTable[ac.c]); + break; + + case TEVCMP_GR16_GT: // 16 bit compares: 255*g+r (probably used for ztextures, so make sure in ztextures, g is the most significant byte) + case TEVCMP_BGR24_GT: // 24 bit compares: 255*255*b+255*g+r + WRITE(p, " %s + (( dot(%s.rgb-%s.rgb, comp%s) > 0) ? %s : 0)\n", + tevAInputTable[ac.d],tevAInputTable2[ac.a], tevAInputTable2[ac.b], cmp==TEVCMP_GR16_GT?"16":"24", tevAInputTable[ac.c]); + break; + case TEVCMP_GR16_EQ: + case TEVCMP_BGR24_EQ: + WRITE(p, " %s + (abs(dot(%s.rgb - %s.rgb, comp%s))<%f ? %s : 0)\n", + tevAInputTable[ac.d],tevAInputTable2[ac.a], tevAInputTable2[ac.b],cmp==TEVCMP_GR16_GT?"16":"24",epsilon8bit,tevAInputTable[ac.c]); + break; + default: + WRITE(p, "0)\n"); + break; + } + } + + WRITE(p, ";\n"); + + if (ac.clamp) + WRITE(p, "%s = clamp(%s,0.0f,1.0f);\n", tevAOutputTable[ac.dest],tevAOutputTable[ac.dest]); + WRITE(p, "\n"); +} + +void WrapNonPow2Tex(char* &p, const char* var, int texmap, u32 texture_mask) +{ + _assert_(texture_mask & (1< %s)",alphaRef[num]); break; + case ALPHACMP_LESS: WRITE(p, "(prev.a >= %s - %f)",alphaRef[num],epsilon8bit*0.5f);break; + case ALPHACMP_GEQUAL: WRITE(p, "(prev.a < %s)",alphaRef[num]); break; + case ALPHACMP_GREATER: WRITE(p, "(prev.a <= %s + %f)",alphaRef[num],epsilon8bit*0.5f);break; + case ALPHACMP_EQUAL: WRITE(p, "(abs(prev.a-%s)>%f)",alphaRef[num],epsilon8bit*2); break; + case ALPHACMP_NEQUAL: WRITE(p, "(abs(prev.a-%s)<%f)",alphaRef[num],epsilon8bit*2); break; + } +} + +static bool WriteAlphaTest(char *&p) +{ + u32 op = bpmem.alphaFunc.logic; + u32 comp[2] = {bpmem.alphaFunc.comp0,bpmem.alphaFunc.comp1}; + + //first kill all the simple cases + switch(op) { + case 0: // and + if (comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_ALWAYS) return true; + if (comp[0] == ALPHACMP_NEVER || comp[1] == ALPHACMP_NEVER) { + WRITE(p, "discard;\n"); + return false; + } + break; + case 1: // or + if (comp[0] == ALPHACMP_ALWAYS || comp[1] == ALPHACMP_ALWAYS) return true; + if (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_NEVER) { + WRITE(p, "discard;\n"); + return false; + } + break; + case 2: // xor + if ( (comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_NEVER) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_ALWAYS) ) return true; + if ( (comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_ALWAYS) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_NEVER)) { + WRITE(p, "discard;\n"); + return false; + } + break; + case 3: // xnor + if ( (comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_NEVER) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_ALWAYS)) { + WRITE(p, "discard;\n"); + return false; + } + if ( (comp[0] == ALPHACMP_ALWAYS && comp[1] == ALPHACMP_ALWAYS) || (comp[0] == ALPHACMP_NEVER && comp[1] == ALPHACMP_NEVER) ) + return true; + break; + } + + WRITE(p, "discard( "); + WriteAlphaCompare(p, 0, bpmem.alphaFunc.comp0); + + // negated because testing the inverse condition + switch(bpmem.alphaFunc.logic) { + case 0: WRITE(p, " || "); break; // and + case 1: WRITE(p, " && "); break; // or + case 2: WRITE(p, " == "); break; // xor + case 3: WRITE(p, " != "); break; // xnor + } + WriteAlphaCompare(p, 1, bpmem.alphaFunc.comp1); + WRITE(p, ");\n"); + return true; +} diff --git a/Source/Core/VideoCommon/Src/Profiler.cpp b/Source/Core/VideoCommon/Src/Profiler.cpp index b66a4d32d2..d18c6a0ba9 100644 --- a/Source/Core/VideoCommon/Src/Profiler.cpp +++ b/Source/Core/VideoCommon/Src/Profiler.cpp @@ -1,291 +1,291 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -// Simple profiler - -#include "Common.h" -#include "Profiler.h" - -#include -#include -#include - -#ifdef _WIN32 - -#if defined (_MSC_VER) && _MSC_VER >= 1400 -#define _interlockedbittestandset workaround_ms_header_bug_platform_sdk6_set -#define _interlockedbittestandreset workaround_ms_header_bug_platform_sdk6_reset -#define _interlockedbittestandset64 workaround_ms_header_bug_platform_sdk6_set64 -#define _interlockedbittestandreset64 workaround_ms_header_bug_platform_sdk6_reset64 -#include -#undef _interlockedbittestandset -#undef _interlockedbittestandreset -#undef _interlockedbittestandset64 -#undef _interlockedbittestandreset64 -#pragma intrinsic(__rdtsc) -#endif - -// Globals -static u64 luPerfFreq = 0; -#ifdef DVPROFILE -int g_bWriteProfile = 1; -#else -int g_bWriteProfile = 1; -#endif - -inline u64 GET_PROFILE_TIME() -{ -#if defined (_MSC_VER) && _MSC_VER >= 1400 - return __rdtsc(); -#else - LARGE_INTEGER lu; - QueryPerformanceCounter(&lu); - return lu.QuadPart; -#endif -} -#else -static u64 luPerfFreq = 1000000; -#define GET_PROFILE_TIME() //GetCpuTick() -#endif - -struct DVPROFSTRUCT; - -struct DVPROFSTRUCT -{ - struct DATA - { - DATA(u64 time, u32 user = 0) : dwTime(time), dwUserData(user) {} - DATA() : dwTime(0), dwUserData(0) {} - - u64 dwTime; - u32 dwUserData; - }; - - ~DVPROFSTRUCT() { - std::list::iterator it = listpChild.begin(); - while (it != listpChild.end()) { - delete *it; - *it = NULL; - ++it; - } - } - - // before DVProfEnd is called, contains the global time it started - // after DVProfEnd is called, contains the time it lasted - // the list contains all the tracked times - std::list listTimes; - - char pname[256]; - std::list listpChild; // other profilers called during this profiler period -}; - -struct DVPROFTRACK -{ - u32 dwUserData; - DVPROFSTRUCT::DATA* pdwTime; - DVPROFSTRUCT* pprof; -}; - -// the current profiling functions, the back element is the -// one that will first get popped off the list when DVProfEnd is called -// the pointer is an element in DVPROFSTRUCT::listTimes -static std::list g_listCurTracking; - -// the current profilers, note that these are the parents -// any profiler started during the time of another is held in -// DVPROFSTRUCT::listpChild -static std::list g_listProfilers; - -// ignores the hierarchy, pointer to elements in g_listProfilers -static std::list g_listAllProfilers; - - -void DVProfRegister(const char *pname) -{ - if (!g_bWriteProfile) - return; - -#ifdef _WIN32 - if (luPerfFreq <= 1) { -#if defined (_MSC_VER) && _MSC_VER >= 1400 - luPerfFreq = 1000000; -#else - LARGE_INTEGER temp; - QueryPerformanceFrequency(&temp); - luPerfFreq = temp.QuadPart; -#endif - } -#endif - - std::list::iterator it = g_listAllProfilers.begin(); - -// while(it != g_listAllProfilers.end() ) { -// -// if( _tcscmp(pname, (*it)->pname) == 0 ) { -// (*it)->listTimes.push_back(timeGetTime()); -// DVPROFTRACK dvtrack; -// dvtrack.pdwTime = &(*it)->listTimes.back(); -// dvtrack.pprof = *it; -// g_listCurTracking.push_back(dvtrack); -// return; -// } -// -// ++it; -// } - - // else add in a new profiler to the appropriate parent profiler - DVPROFSTRUCT* pprof = NULL; - - if (g_listCurTracking.size() > 0) { - _assert_( g_listCurTracking.back().pprof != NULL ); - g_listCurTracking.back().pprof->listpChild.push_back(new DVPROFSTRUCT()); - pprof = g_listCurTracking.back().pprof->listpChild.back(); - } - else { - g_listProfilers.push_back(DVPROFSTRUCT()); - pprof = &g_listProfilers.back(); - } - - strncpy(pprof->pname, pname, 256); - - // setup the profiler for tracking - pprof->listTimes.push_back(DVPROFSTRUCT::DATA(GET_PROFILE_TIME())); - - DVPROFTRACK dvtrack; - dvtrack.pdwTime = &pprof->listTimes.back(); - dvtrack.pprof = pprof; - dvtrack.dwUserData = 0; - - g_listCurTracking.push_back(dvtrack); - - // add to all profiler list - g_listAllProfilers.push_back(pprof); -} - -void DVProfEnd(u32 dwUserData) -{ - if (!g_bWriteProfile) - return; - if (g_listCurTracking.size() == 0) - return; - - DVPROFTRACK dvtrack = g_listCurTracking.back(); - - _assert_( dvtrack.pdwTime != NULL && dvtrack.pprof != NULL ); - - dvtrack.pdwTime->dwTime = GET_PROFILE_TIME()- dvtrack.pdwTime->dwTime; - dvtrack.pdwTime->dwUserData= dwUserData; - - g_listCurTracking.pop_back(); -} - -struct DVTIMEINFO -{ - DVTIMEINFO() : uInclusive(0), uExclusive(0) {} - u64 uInclusive, uExclusive; -}; - -std::map mapAggregateTimes; - -u64 DVProfWriteStruct(FILE* f, const DVPROFSTRUCT* p, int ident) -{ - fprintf(f, "%*s%s - ", ident, "", p->pname); - std::list::const_iterator ittime = p->listTimes.begin(); - u64 utime = 0; - while (ittime != p->listTimes.end()) { - utime += ittime->dwTime; - if (ittime->dwUserData) - fprintf(f, "time: %d, user: 0x%8.8x", (u32)ittime->dwTime, ittime->dwUserData); - else - fprintf(f, "time: %d", (u32)ittime->dwTime); - ++ittime; - } - - // yes this is necessary, maps have problems with constructors on their type - std::map::iterator ittimes = mapAggregateTimes.find(p->pname); - if (ittimes == mapAggregateTimes.end()) { - ittimes = mapAggregateTimes.insert(std::map::value_type(p->pname, DVTIMEINFO())).first; - ittimes->second.uExclusive = 0; - ittimes->second.uInclusive = 0; - } - - ittimes->second.uInclusive += utime; - - fprintf(f, "\n"); - - std::list::const_iterator itprof = p->listpChild.begin(); - - u64 uex = utime; - while (itprof != p->listpChild.end()) { - uex -= DVProfWriteStruct(f, *itprof, ident+4); - ++itprof; - } - - if (uex > utime) { - uex = 0; - } - - ittimes->second.uExclusive += uex; - return utime; -} - -void DVProfWrite(const char* pfilename, u32 frames) -{ - _assert_( pfilename != NULL ); - FILE* f = fopen(pfilename, "w"); - - // pop back any unused - mapAggregateTimes.clear(); - std::list::iterator it = g_listProfilers.begin(); - - while (it != g_listProfilers.end() ) { - DVProfWriteStruct(f, &(*it), 0); - ++it; - } - - std::map::const_iterator iter; - fprintf(f, "\n\n-------------------------------------------------------------------\n\n"); - - u64 uTotal[2] = {0}; - double fiTotalTime[2]; - - for (iter = mapAggregateTimes.begin(); iter != mapAggregateTimes.end(); ++iter) { - uTotal[0] += iter->second.uExclusive; - uTotal[1] += iter->second.uInclusive; - } - - fprintf(f, "total times (%d): ex: %Lu ", frames, 1000000 * uTotal[0] / (luPerfFreq*(u64)frames)); - fprintf(f, "inc: %Lu\n", 1000000 * uTotal[1]/(luPerfFreq*(u64)frames)); - - fiTotalTime[0] = 1.0 / (double)uTotal[0]; - fiTotalTime[1] = 1.0 / (double)uTotal[1]; - - // output the combined times - for (iter = mapAggregateTimes.begin(); iter != mapAggregateTimes.end(); ++iter) { - fprintf(f, "%s - ex: %f inc: %f\n", iter->first.c_str(), (float)((double)iter->second.uExclusive * fiTotalTime[0]), - (float)((double)iter->second.uInclusive * fiTotalTime[1])); - } - - fclose(f); -} - -void DVProfClear() -{ - g_listCurTracking.clear(); - g_listProfilers.clear(); - g_listAllProfilers.clear(); -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// Simple profiler + +#include "Common.h" +#include "Profiler.h" + +#include +#include +#include + +#ifdef _WIN32 + +#if defined (_MSC_VER) && _MSC_VER >= 1400 +#define _interlockedbittestandset workaround_ms_header_bug_platform_sdk6_set +#define _interlockedbittestandreset workaround_ms_header_bug_platform_sdk6_reset +#define _interlockedbittestandset64 workaround_ms_header_bug_platform_sdk6_set64 +#define _interlockedbittestandreset64 workaround_ms_header_bug_platform_sdk6_reset64 +#include +#undef _interlockedbittestandset +#undef _interlockedbittestandreset +#undef _interlockedbittestandset64 +#undef _interlockedbittestandreset64 +#pragma intrinsic(__rdtsc) +#endif + +// Globals +static u64 luPerfFreq = 0; +#ifdef DVPROFILE +int g_bWriteProfile = 1; +#else +int g_bWriteProfile = 1; +#endif + +inline u64 GET_PROFILE_TIME() +{ +#if defined (_MSC_VER) && _MSC_VER >= 1400 + return __rdtsc(); +#else + LARGE_INTEGER lu; + QueryPerformanceCounter(&lu); + return lu.QuadPart; +#endif +} +#else +static u64 luPerfFreq = 1000000; +#define GET_PROFILE_TIME() //GetCpuTick() +#endif + +struct DVPROFSTRUCT; + +struct DVPROFSTRUCT +{ + struct DATA + { + DATA(u64 time, u32 user = 0) : dwTime(time), dwUserData(user) {} + DATA() : dwTime(0), dwUserData(0) {} + + u64 dwTime; + u32 dwUserData; + }; + + ~DVPROFSTRUCT() { + std::list::iterator it = listpChild.begin(); + while (it != listpChild.end()) { + delete *it; + *it = NULL; + ++it; + } + } + + // before DVProfEnd is called, contains the global time it started + // after DVProfEnd is called, contains the time it lasted + // the list contains all the tracked times + std::list listTimes; + + char pname[256]; + std::list listpChild; // other profilers called during this profiler period +}; + +struct DVPROFTRACK +{ + u32 dwUserData; + DVPROFSTRUCT::DATA* pdwTime; + DVPROFSTRUCT* pprof; +}; + +// the current profiling functions, the back element is the +// one that will first get popped off the list when DVProfEnd is called +// the pointer is an element in DVPROFSTRUCT::listTimes +static std::list g_listCurTracking; + +// the current profilers, note that these are the parents +// any profiler started during the time of another is held in +// DVPROFSTRUCT::listpChild +static std::list g_listProfilers; + +// ignores the hierarchy, pointer to elements in g_listProfilers +static std::list g_listAllProfilers; + + +void DVProfRegister(const char *pname) +{ + if (!g_bWriteProfile) + return; + +#ifdef _WIN32 + if (luPerfFreq <= 1) { +#if defined (_MSC_VER) && _MSC_VER >= 1400 + luPerfFreq = 1000000; +#else + LARGE_INTEGER temp; + QueryPerformanceFrequency(&temp); + luPerfFreq = temp.QuadPart; +#endif + } +#endif + + std::list::iterator it = g_listAllProfilers.begin(); + +// while(it != g_listAllProfilers.end() ) { +// +// if( _tcscmp(pname, (*it)->pname) == 0 ) { +// (*it)->listTimes.push_back(timeGetTime()); +// DVPROFTRACK dvtrack; +// dvtrack.pdwTime = &(*it)->listTimes.back(); +// dvtrack.pprof = *it; +// g_listCurTracking.push_back(dvtrack); +// return; +// } +// +// ++it; +// } + + // else add in a new profiler to the appropriate parent profiler + DVPROFSTRUCT* pprof = NULL; + + if (g_listCurTracking.size() > 0) { + _assert_( g_listCurTracking.back().pprof != NULL ); + g_listCurTracking.back().pprof->listpChild.push_back(new DVPROFSTRUCT()); + pprof = g_listCurTracking.back().pprof->listpChild.back(); + } + else { + g_listProfilers.push_back(DVPROFSTRUCT()); + pprof = &g_listProfilers.back(); + } + + strncpy(pprof->pname, pname, 256); + + // setup the profiler for tracking + pprof->listTimes.push_back(DVPROFSTRUCT::DATA(GET_PROFILE_TIME())); + + DVPROFTRACK dvtrack; + dvtrack.pdwTime = &pprof->listTimes.back(); + dvtrack.pprof = pprof; + dvtrack.dwUserData = 0; + + g_listCurTracking.push_back(dvtrack); + + // add to all profiler list + g_listAllProfilers.push_back(pprof); +} + +void DVProfEnd(u32 dwUserData) +{ + if (!g_bWriteProfile) + return; + if (g_listCurTracking.size() == 0) + return; + + DVPROFTRACK dvtrack = g_listCurTracking.back(); + + _assert_( dvtrack.pdwTime != NULL && dvtrack.pprof != NULL ); + + dvtrack.pdwTime->dwTime = GET_PROFILE_TIME()- dvtrack.pdwTime->dwTime; + dvtrack.pdwTime->dwUserData= dwUserData; + + g_listCurTracking.pop_back(); +} + +struct DVTIMEINFO +{ + DVTIMEINFO() : uInclusive(0), uExclusive(0) {} + u64 uInclusive, uExclusive; +}; + +std::map mapAggregateTimes; + +u64 DVProfWriteStruct(FILE* f, const DVPROFSTRUCT* p, int ident) +{ + fprintf(f, "%*s%s - ", ident, "", p->pname); + std::list::const_iterator ittime = p->listTimes.begin(); + u64 utime = 0; + while (ittime != p->listTimes.end()) { + utime += ittime->dwTime; + if (ittime->dwUserData) + fprintf(f, "time: %d, user: 0x%8.8x", (u32)ittime->dwTime, ittime->dwUserData); + else + fprintf(f, "time: %d", (u32)ittime->dwTime); + ++ittime; + } + + // yes this is necessary, maps have problems with constructors on their type + std::map::iterator ittimes = mapAggregateTimes.find(p->pname); + if (ittimes == mapAggregateTimes.end()) { + ittimes = mapAggregateTimes.insert(std::map::value_type(p->pname, DVTIMEINFO())).first; + ittimes->second.uExclusive = 0; + ittimes->second.uInclusive = 0; + } + + ittimes->second.uInclusive += utime; + + fprintf(f, "\n"); + + std::list::const_iterator itprof = p->listpChild.begin(); + + u64 uex = utime; + while (itprof != p->listpChild.end()) { + uex -= DVProfWriteStruct(f, *itprof, ident+4); + ++itprof; + } + + if (uex > utime) { + uex = 0; + } + + ittimes->second.uExclusive += uex; + return utime; +} + +void DVProfWrite(const char* pfilename, u32 frames) +{ + _assert_( pfilename != NULL ); + FILE* f = fopen(pfilename, "w"); + + // pop back any unused + mapAggregateTimes.clear(); + std::list::iterator it = g_listProfilers.begin(); + + while (it != g_listProfilers.end() ) { + DVProfWriteStruct(f, &(*it), 0); + ++it; + } + + std::map::const_iterator iter; + fprintf(f, "\n\n-------------------------------------------------------------------\n\n"); + + u64 uTotal[2] = {0}; + double fiTotalTime[2]; + + for (iter = mapAggregateTimes.begin(); iter != mapAggregateTimes.end(); ++iter) { + uTotal[0] += iter->second.uExclusive; + uTotal[1] += iter->second.uInclusive; + } + + fprintf(f, "total times (%d): ex: %Lu ", frames, 1000000 * uTotal[0] / (luPerfFreq*(u64)frames)); + fprintf(f, "inc: %Lu\n", 1000000 * uTotal[1]/(luPerfFreq*(u64)frames)); + + fiTotalTime[0] = 1.0 / (double)uTotal[0]; + fiTotalTime[1] = 1.0 / (double)uTotal[1]; + + // output the combined times + for (iter = mapAggregateTimes.begin(); iter != mapAggregateTimes.end(); ++iter) { + fprintf(f, "%s - ex: %f inc: %f\n", iter->first.c_str(), (float)((double)iter->second.uExclusive * fiTotalTime[0]), + (float)((double)iter->second.uInclusive * fiTotalTime[1])); + } + + fclose(f); +} + +void DVProfClear() +{ + g_listCurTracking.clear(); + g_listProfilers.clear(); + g_listAllProfilers.clear(); +} diff --git a/Source/Core/VideoCommon/Src/Statistics.cpp b/Source/Core/VideoCommon/Src/Statistics.cpp index df0dbec877..b686195e39 100644 --- a/Source/Core/VideoCommon/Src/Statistics.cpp +++ b/Source/Core/VideoCommon/Src/Statistics.cpp @@ -1,43 +1,43 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include - -#include "Statistics.h" - -Statistics stats; - -template -void Xchg(T& a, T&b) -{ - T c = a; - a = b; - b = c; -} - -void Statistics::ResetFrame() -{ - memset(&thisFrame, 0, sizeof(ThisFrame)); -} - -void Statistics::SwapDL() -{ - Xchg(stats.thisFrame.numDLPrims, stats.thisFrame.numPrims); - Xchg(stats.thisFrame.numXFLoadsInDL, stats.thisFrame.numXFLoads); - Xchg(stats.thisFrame.numCPLoadsInDL, stats.thisFrame.numCPLoads); - Xchg(stats.thisFrame.numBPLoadsInDL, stats.thisFrame.numBPLoads); -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "Statistics.h" + +Statistics stats; + +template +void Xchg(T& a, T&b) +{ + T c = a; + a = b; + b = c; +} + +void Statistics::ResetFrame() +{ + memset(&thisFrame, 0, sizeof(ThisFrame)); +} + +void Statistics::SwapDL() +{ + Xchg(stats.thisFrame.numDLPrims, stats.thisFrame.numPrims); + Xchg(stats.thisFrame.numXFLoadsInDL, stats.thisFrame.numXFLoads); + Xchg(stats.thisFrame.numCPLoadsInDL, stats.thisFrame.numCPLoads); + Xchg(stats.thisFrame.numBPLoadsInDL, stats.thisFrame.numBPLoads); +} diff --git a/Source/Core/VideoCommon/Src/TextureDecoder.cpp b/Source/Core/VideoCommon/Src/TextureDecoder.cpp index 9f2b7c00bd..e20b5368d6 100644 --- a/Source/Core/VideoCommon/Src/TextureDecoder.cpp +++ b/Source/Core/VideoCommon/Src/TextureDecoder.cpp @@ -1,1380 +1,1380 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "Common.h" - -#include "TextureDecoder.h" -#include "LookUpTables.h" -#include - -//Uncomment this to enable Texture Format ID overlays -#define OVERLAY_TEXFMT - -#ifdef OVERLAY_TEXFMT -bool TexFmt_Overlay_Enable=false; -bool TexFmt_Overlay_Center=false; -#endif - -// TRAM -// STATE_TO_SAVE -u8 texMem[TMEM_SIZE]; - -////////////////////////////////////////////////////////////////////////// -// Gamecube/Wii texture decoder -////////////////////////////////////////////////////////////////////////// -// Decodes all known Gamecube/Wii texture formats. -// by ector -////////////////////////////////////////////////////////////////////////// -int TexDecoder_GetTexelSizeInNibbles(int format) -{ - switch (format & 0x3f) { - case GX_TF_I4: return 1; - case GX_TF_I8: return 2; - case GX_TF_IA4: return 2; - case GX_TF_IA8: return 4; - case GX_TF_RGB565: return 4; - case GX_TF_RGB5A3: return 4; - case GX_TF_RGBA8: return 8; - case GX_TF_C4: return 1; - case GX_TF_C8: return 2; - case GX_TF_C14X2: return 4; - case GX_TF_CMPR: return 1; - default: return 1; - } -} - -int TexDecoder_GetTextureSizeInBytes(int width, int height, int format) -{ - return (width * height * TexDecoder_GetTexelSizeInNibbles(format)) / 2; -} - -u32 TexDecoder_GetTlutHash(const u16 *src, int len) -{ - u32 hash = 0xbeefbabe; - for (int i = 0; i < len / 2; i += 2) { - hash = _rotl(hash, 17) ^ ((u32 *)src)[i]; - } - return hash; -} - -u32 TexDecoder_GetSafeTextureHash(const u8 *src, int width, int height, int texformat, u32 seed) -{ - int sz = TexDecoder_GetTextureSizeInBytes(width, height, texformat); - - u32 hash = seed ? seed : 0x1337c0de; - - if (sz < 2048) { - for (int i = 0; i < sz / 4; i += 13) { - hash = _rotl(hash, 17) ^ ((u32 *)src)[i]; - } - return hash; - } else { - int step = sz / 13 / 4; - for (int i = 0; i < sz / 4; i += step) { - hash = _rotl(hash, 17) ^ ((u32 *)src)[i]; - } - } - return hash; -} - -int TexDecoder_GetBlockWidthInTexels(int format) -{ - switch (format) { - case GX_TF_I4: return 8; - case GX_TF_I8: return 8; - case GX_TF_IA4: return 8; - case GX_TF_IA8: return 4; - case GX_TF_RGB565: return 4; - case GX_TF_RGB5A3: return 4; - case GX_TF_RGBA8: return 4; - case GX_TF_C4: return 8; - case GX_TF_C8: return 8; - case GX_TF_C14X2: return 4; - case GX_TF_CMPR: return 8; - default: return 8; - } -} - -//returns bytes -int TexDecoder_GetPaletteSize(int format) -{ - switch (format) { - case GX_TF_C4: return 16*2; - case GX_TF_C8: return 256*2; - case GX_TF_C14X2: return 16384*2; - default: - return 0; - } -} - -inline u32 decode565(u16 val) -{ - int r,g,b,a; - r=lut5to8[(val>>11) & 0x1f]; - g=lut6to8[(val>>5 ) & 0x3f]; - b=lut5to8[(val ) & 0x1f]; - a=0xFF; - return (a<<24) | (r<<16) | (g<<8) | b; -} - -inline u32 decodeIA8(u16 val) -{ - int a=val>>8; - int i=val&0xFF; - return (a<<24) | (i<<16) | (i<<8) | i; -} - -inline u32 decode5A3(u16 val) -{ - int r,g,b,a; - if ((val&0x8000)) - { - r=lut5to8[(val>>10) & 0x1f]; - g=lut5to8[(val>>5 ) & 0x1f]; - b=lut5to8[(val ) & 0x1f]; - a=0xFF; - } - else - { - a=lut3to8[(val>>12) & 0x7]; - r=lut4to8[(val>>8 ) & 0xf]; - g=lut4to8[(val>>4 ) & 0xf]; - b=lut4to8[(val ) & 0xf]; - } - return (a<<24) | (r<<16) | (g<<8) | b; -} - - - -struct DXTBlock -{ - u16 color1; - u16 color2; - u8 lines[4]; -}; - -inline int expand8888(const int j) -{ - int i = j | (j<<8); - return i | (i<<16); -} - -//inline void decodebytesI4(u32 *dst, const u8 *src, int numbytes) -inline void decodebytesI4(u32 *dst, const u8 *src) -{ - for (int x = 0; x < 4; x++) - { - int val = src[x]; - *dst++ = expand8888(lut4to8[val>>4]); - *dst++ = expand8888(lut4to8[val&15]); - } -} - -inline void decodebytesI8_8(u32 *dst, const u8 *src) -{ - for (int x = 0; x < 8; x++) - dst[x] = src[x] * 0x01010101; //expand8888(src[x]); *0x... may or may not be faster. not sure. Should be faster on P4 at least. -} - -//inline void decodebytesC4(u32 *dst, const u8 *src, int numbytes, int tlutaddr, int tlutfmt) -inline void decodebytesC4(u32 *dst, const u8 *src, int tlutaddr, int tlutfmt) -{ - u16 *tlut = (u16*)(texMem + tlutaddr); - for (int x = 0; x < 4; x++) - { - int val = src[x]; - switch (tlutfmt) { - case 0: - *dst++ = decodeIA8(Common::swap16(tlut[val >> 4])); - *dst++ = decodeIA8(Common::swap16(tlut[val & 15])); - break; - case 1: - *dst++ = decode565(Common::swap16(tlut[val >> 4])); - *dst++ = decode565(Common::swap16(tlut[val & 15])); - break; - case 2: - *dst++ = decode5A3(Common::swap16(tlut[val >> 4])); - *dst++ = decode5A3(Common::swap16(tlut[val & 15])); - break; - case 3: //ERROR - *dst++ = 0xFFFF00FF; - *dst++ = 0xFFFF00FF; - break; - } - } -} - -//inline void decodebytesC8(u32 *dst, const u8 *src, int numbytes, int tlutaddr, int tlutfmt) -inline void decodebytesC8(u32 *dst, const u8 *src, int tlutaddr, int tlutfmt) -{ - u16 *tlut = (u16*)(texMem+tlutaddr); - for (int x = 0; x < 8; x++) - { - int val = src[x]; - switch (tlutfmt) { - case 0: - *dst++ = decodeIA8(Common::swap16(tlut[val])); - break; - case 1: - *dst++ = decode565(Common::swap16(tlut[val])); - break; - case 2: - *dst++ = decode5A3(Common::swap16(tlut[val])); - break; - case 3: //ERROR - *dst++ = 0xFFFF00FF; - break; - } - } -} - - -//inline void decodebytesC14X2(u32 *dst, const u16 *src, int numpixels, int tlutaddr, int tlutfmt) -inline void decodebytesC14X2(u32 *dst, const u16 *src, int tlutaddr, int tlutfmt) -{ - u16 *tlut = (u16*)(texMem+tlutaddr); - for (int x = 0; x < 4; x++) - { - int val = Common::swap16(src[x]); - switch (tlutfmt) { - case 0: - *dst++ = decodeIA8(Common::swap16(tlut[(val&0x3FFF)])); - break; - case 1: - *dst++ = decode565(Common::swap16(tlut[(val&0x3FFF)])); - break; - case 2: - *dst++ = decode5A3(Common::swap16(tlut[(val&0x3FFF)])); - break; - case 3: //ERROR - *dst++ = 0xFFFF00FF; - break; - } - } -} - -//inline void decodebytesRGB565(u32 *dst, const u16 *src, int numpixels) -inline void decodebytesRGB565(u32 *dst, const u16 *src) -{ - for (int x = 0; x < 4; x++) - *dst++ = decode565(Common::swap16(src[x])); -} - -//inline void decodebytesIA4(u32 *dst, const u8 *src, int numbytes) -inline void decodebytesIA4(u32 *dst, const u8 *src) -{ - for (int x = 0; x < 8; x++) - { - int val = src[x]; - int a = lut4to8[val>>4]; - int r = lut4to8[val&15]; - dst[x] = (a<<24) | (r<<16) | (r<<8) | r; - } -} - -//inline void decodebytesIA8(u32 *dst, const u16 *src, int numpixels) -inline void decodebytesIA8(u32 *dst, const u16 *src) -{ - for (int x = 0; x < 4; x++) - dst[x] = decodeIA8(Common::swap16(src[x])); -} - -//inline void decodebytesRGB5A3(u32 *dst, const u16 *src, int numpixels) -inline void decodebytesRGB5A3(u32 *dst, const u16 *src) -{ - for (int x = 0; x < 4; x++) - dst[x] = decode5A3(Common::swap16(src[x])); -} - -// This one is used by many video formats. It'd therefore be good if it was fast. -inline void decodebytesARGB8_4(u32 *dst, const u16 *src, const u16 *src2) -{ - for (int x = 0; x < 4; x++) { - dst[x] = Common::swap32((src2[x] << 16) | src[x]); - } - - // This can probably be done in a few SSE pack/unpack instructions + pshufb - // some unpack instruction x2: - // ABABABABABABABAB 1212121212121212 -> - // AB12AB12AB12AB12 AB12AB12AB12AB12 - // 2x pshufb-> - // 21BA21BA21BA21BA 21BA21BA21BA21BA - // and we are done. -} - -inline u32 makecol(int r, int g, int b, int a) -{ - return (a<<24)|(r<<16)|(g<<8)|b; -} - -void decodeDXTBlock(u32 *dst, const DXTBlock *src, int pitch) -{ - u16 c1 = Common::swap16(src->color1); - u16 c2 = Common::swap16(src->color2); - int blue1 = lut5to8[c1 & 0x1F]; - int blue2 = lut5to8[c2 & 0x1F]; - int green1 = lut6to8[(c1>>5) & 0x3F]; - int green2 = lut6to8[(c2>>5) & 0x3F]; - int red1 = lut5to8[(c1>>11) & 0x1F]; - int red2 = lut5to8[(c2>>11) & 0x1F]; - - int colors[4]; - - if (c1 > c2) - { - colors[0] = makecol(red1, green1, blue1, 255); - colors[1] = makecol(red2, green2, blue2, 255); - colors[2] = makecol(red1+(red2-red1)/3, green1+(green2-green1)/3, blue1+(blue2-blue1)/3, 255); - colors[3] = makecol(red2+(red1-red2)/3, green2+(green1-green2)/3, blue2+(blue1-blue2)/3, 255); - } - else - { - colors[0] = makecol(red1, green1, blue1, 255); - colors[1] = makecol(red2, green2, blue2, 255); - colors[2] = makecol((red1+red2)/2, (green1+green2)/2, (blue1+blue2)/2, 255); - colors[3] = makecol(0,0,0,0); //transparent - } - - for (int y = 0; y < 4; y++) - { - int val = src->lines[y]; - for (int x = 0; x < 4; x++) - { - dst[x] = colors[(val >> 6) & 3]; - val <<= 2; - } - dst += pitch; - } -} - - -//switch endianness, unswizzle -//TODO: to save memory, don't blindly convert everything to argb8888 -//also ARGB order needs to be swapped later, to accommodate modern hardware better -//need to add DXT support too -#ifdef OVERLAY_TEXFMT -PC_TexFormat TexDecoder_Decode_real(u8 *dst, const u8 *src, int width, int height, int texformat, int tlutaddr, int tlutfmt) -#else -PC_TexFormat TexDecoder_Decode(u8 *dst, const u8 *src, int width, int height, int texformat, int tlutaddr, int tlutfmt) -#endif -{ - switch (texformat) - { - case GX_TF_C4: - { - for (int y = 0; y < height; y += 8) - for (int x = 0; x < width; x += 8) - for (int iy = 0; iy < 8; iy++, src += 4) - //decodebytesC4((u32*)dst+(y+iy)*width+x, src, 4, tlutaddr, tlutfmt); - decodebytesC4((u32*)dst+(y+iy)*width+x, src, tlutaddr, tlutfmt); - } - return PC_TEX_FMT_BGRA32; - case GX_TF_I4: - { - // TODO: SSSE3 variant (pshufb), THP videos use this format. - // SSSE3 variant could bring even more speed -#if (defined(_WIN32) || (defined (_M_X64) && !defined(_WIN32))) - __m128i Lmask = _mm_set1_epi8 (0x0F); - __m128i Hmask = _mm_set1_epi8 (0xF0); - __m128i* sseSrc = (__m128i *)src; - __m128i* sseDst = (__m128i *)dst; - for (int y = 0; y < height; y += 8) - for (int x = 0; x < width; x += 8) - for (int iy = 0; iy < 8; iy++, sseSrc++) { - // TODO (mb2): func and don't let the optimizer perform all the clean up by itself - __m128i s = _mm_load_si128 (sseSrc); // ab cd ef gh ... - __m128i sl = _mm_and_si128 (s, Lmask); // 0b 0d 0f 0h ... - __m128i sls = _mm_slli_epi16 (sl, 4); // b0 d0 f0 h0 ... - __m128i sl_ = _mm_or_si128 (sl, sls); // bb dd ff ff ... - - __m128i sh = _mm_and_si128 (s, Hmask); // a0 c0 e0 g0 ... - __m128i shs = _mm_srli_epi16 (sh, 4); // 0a 0c 0e g0 ... - __m128i sh_ = _mm_or_si128 (sh, shs); // aa cc ee gg ... - __m128i rl = _mm_unpacklo_epi8 (sh_, sl_); // bb aa dd cc ... - __m128i rh = _mm_unpackhi_epi8 (sh_, sl_); // - - __m128i ral = _mm_unpacklo_epi8 (rl, rl); // bb bb aa aa ... - __m128i rah = _mm_unpackhi_epi8 (rl, rl); // - // result part a - __m128i rall = _mm_unpacklo_epi16 (ral, ral); // bb bb bb bb ... -> done - __m128i ralh = _mm_unpackhi_epi16 (ral, ral); // -> done - __m128i rahl = _mm_unpacklo_epi16 (rah, rah); // -> done - __m128i rahh = _mm_unpackhi_epi16 (rah, rah); // -> done - - __m128i rbl = _mm_unpacklo_epi8 (rh, rh); // - __m128i rbh = _mm_unpackhi_epi8 (rh, rh); // - // result part b - __m128i rbll = _mm_unpacklo_epi16 (rbl, rbl); // -> done - __m128i rblh = _mm_unpackhi_epi16 (rbl, rbl); // -> done - __m128i rbhl = _mm_unpacklo_epi16 (rbh, rbh); // -> done - __m128i rbhh = _mm_unpackhi_epi16 (rbh, rbh); // -> done - // store - sseDst = (__m128i*)(dst+((y+iy)*width+x)*4); // that sucks... too lazy - _mm_store_si128 (sseDst++, rall); - _mm_store_si128 (sseDst, ralh); - iy++; - sseDst = (__m128i*)(dst+((y+iy)*width+x)*4); - _mm_store_si128 (sseDst++, rahl); - _mm_store_si128 (sseDst, rahh); - iy++; - sseDst = (__m128i*)(dst+((y+iy)*width+x)*4); - _mm_store_si128 (sseDst++, rbll); - _mm_store_si128 (sseDst, rblh); - iy++; - sseDst = (__m128i*)(dst+((y+iy)*width+x)*4); - _mm_store_si128 (sseDst++, rbhl); - _mm_store_si128 (sseDst, rbhh); - } -#else - for (int y = 0; y < height; y += 8) - for (int x = 0; x < width; x += 8) - for (int iy = 0; iy < 8; iy++, src += 4) - //decodebytesI4((u32*)dst+(y+iy)*width+x, src, 4); - decodebytesI4((u32*)dst+(y+iy)*width+x, src); -#endif - } - return PC_TEX_FMT_BGRA32; - case GX_TF_C8: - { - for (int y = 0; y < height; y += 4) - for (int x = 0; x < width; x += 8) - for (int iy = 0; iy < 4; iy++, src += 8) - //decodebytesC8((u32*)dst+(y+iy)*width+x, src, 8, tlutaddr, tlutfmt); - decodebytesC8((u32*)dst+(y+iy)*width+x, src, tlutaddr, tlutfmt); - } - return PC_TEX_FMT_BGRA32; - case GX_TF_I8: // speed critical - { -#if (defined(_WIN32) || (defined (_M_X64) && !defined(_WIN32))) - __m128i *sseSrc = (__m128i *)src; - __m128i *sseDst = (__m128i *)dst; - for (int y = 0; y < height; y += 4) - for (int x = 0; x < width; x += 8) - for (int iy = 0; iy < 4; iy++, sseSrc++) { - // TODO (mb2): func and don't let the optimizer perform all the clean up by itself - __m128i s = _mm_load_si128 (sseSrc); // ab cd ef gh ... - __m128i rl = _mm_unpacklo_epi8 (s, s); // ab ab cd cd ... - __m128i rh = _mm_unpackhi_epi8 (s, s); // - // result - __m128i rll = _mm_unpacklo_epi8 (rl, rl); // ab ab ab ab - __m128i rlh = _mm_unpackhi_epi8 (rl, rl); - __m128i rhl = _mm_unpacklo_epi8 (rh, rh); - __m128i rhh = _mm_unpackhi_epi8 (rh, rh); - // store - sseDst = (__m128i*)(dst+((y+iy)*width+x)*4); - _mm_store_si128 (sseDst++, rll); - _mm_store_si128 (sseDst, rlh); - iy++; - sseDst = (__m128i*)(dst+((y+iy)*width+x)*4); - _mm_store_si128 (sseDst++, rhl); - _mm_store_si128 (sseDst, rhh); - } -#else - for (int y = 0; y < height; y += 4) - for (int x = 0; x < width; x += 8) - for (int iy = 0; iy < 4; iy++, src += 8) - decodebytesI8_8((u32*)dst+(y+iy)*width+x, src); -#endif - } - return PC_TEX_FMT_BGRA32; - case GX_TF_IA4: - { - for (int y = 0; y < height; y += 4) - for (int x = 0; x < width; x += 8) - for (int iy = 0; iy < 4; iy++, src += 8) - //decodebytesIA4((u32*)dst+(y+iy)*width+x, src, 8); - decodebytesIA4((u32*)dst+(y+iy)*width+x, src); - } - return PC_TEX_FMT_BGRA32; - case GX_TF_IA8: - { - for (int y = 0; y < height; y += 4) - for (int x = 0; x < width; x += 4) - for (int iy = 0; iy < 4; iy++, src += 8) - //decodebytesIA8((u32*)dst+(y+iy)*width+x, (u16*)src, 4); - decodebytesIA8((u32*)dst+(y+iy)*width+x, (u16*)src); - } - return PC_TEX_FMT_BGRA32; - case GX_TF_C14X2: - { - for (int y = 0; y < height; y += 4) - for (int x = 0; x < width; x += 4) - for (int iy = 0; iy < 4; iy++, src += 8) - //decodebytesC14X2((u32*)dst+(y+iy)*width+x, (u16*)src, 4, tlutaddr, tlutfmt); - decodebytesC14X2((u32*)dst+(y+iy)*width+x, (u16*)src, tlutaddr, tlutfmt); - } - return PC_TEX_FMT_BGRA32; - case GX_TF_RGB565: - { - for (int y = 0; y < height; y += 4) - for (int x = 0; x < width; x += 4) - for (int iy = 0; iy < 4; iy++, src += 8) - //decodebytesRGB565((u32*)dst+(y+iy)*width+x, (u16*)src, 4); - decodebytesRGB565((u32*)dst+(y+iy)*width+x, (u16*)src); - } - return PC_TEX_FMT_BGRA32; - case GX_TF_RGB5A3: - { - for (int y = 0; y < height; y += 4) - for (int x = 0; x < width; x += 4) - for (int iy = 0; iy < 4; iy++, src += 8) - //decodebytesRGB5A3((u32*)dst+(y+iy)*width+x, (u16*)src, 4); - decodebytesRGB5A3((u32*)dst+(y+iy)*width+x, (u16*)src); - } - return PC_TEX_FMT_BGRA32; - case GX_TF_RGBA8: // speed critical - { - for (int y = 0; y < height; y += 4) { - for (int x = 0; x < width; x += 4) - { - for (int iy = 0; iy < 4; iy++) { - decodebytesARGB8_4((u32*)dst + (y+iy)*width + x, (u16*)src + 4 * iy, (u16*)src + 4 * iy + 16); - } - src += 64; - } - } - } - return PC_TEX_FMT_BGRA32; - case GX_TF_CMPR: // speed critical - { - // TODO: Shuffle to PC S3TC (DXTC) format instead of converting - // 11111111 22222222 55555555 66666666 - // 33333333 44444444 77777777 88888888 - // The metroid games use this format almost exclusively. - for (int y = 0; y < height; y += 8) - for (int x = 0; x < width; x += 8) - { - decodeDXTBlock((u32*)dst+y*width+x, (DXTBlock*)src, width); - src += sizeof(DXTBlock); - decodeDXTBlock((u32*)dst+y*width+x+4, (DXTBlock*)src, width); - src += sizeof(DXTBlock); - decodeDXTBlock((u32*)dst+(y+4)*width+x, (DXTBlock*)src, width); - src += sizeof(DXTBlock); - decodeDXTBlock((u32*)dst+(y+4)*width+x+4, (DXTBlock*)src, width); - src += sizeof(DXTBlock); - } - } - return PC_TEX_FMT_BGRA32; - } - - // The "copy" texture formats, too? - return PC_TEX_FMT_NONE; -} - -void TexDecoder_SetTexFmtOverlayOptions(bool enable, bool center) -{ -#ifdef OVERLAY_TEXFMT - TexFmt_Overlay_Enable = enable; - TexFmt_Overlay_Center = center; -#endif -} - -#ifdef OVERLAY_TEXFMT -extern const char* texfmt[]; -extern const unsigned char sfont_map[]; -extern const unsigned char sfont_raw[][9*10]; - -PC_TexFormat TexDecoder_Decode(u8 *dst, const u8 *src, int width, int height, int texformat, int tlutaddr, int tlutfmt) -{ - PC_TexFormat retval = TexDecoder_Decode_real(dst,src,width,height,texformat,tlutaddr,tlutfmt); - - if((!TexFmt_Overlay_Enable)||(retval==PC_TEX_FMT_NONE)) - return retval; - - // assume ABGR/ARGB (32bit) - int *dtp = (int*)dst; - - int w = min(width,40); - int h = min(height,10); - - int xoff = (width-w)>>1; - int yoff = (height-h)>>1; - - if(!TexFmt_Overlay_Center) - { - xoff=0; - yoff=0; - } - - const char* fmt = texfmt[texformat&15]; - while(*fmt) - { - int xcnt = 0; - int nchar = sfont_map[(int)*fmt]; - - const unsigned char *ptr = sfont_raw[nchar]; // each char is up to 9x10 - - for(int x=0;x<9;x++) - { - if(ptr[x]==0x78) - break; - xcnt++; - } - for(int y=0;y<10;y++) - { - for(int x=0;x + +//Uncomment this to enable Texture Format ID overlays +#define OVERLAY_TEXFMT + +#ifdef OVERLAY_TEXFMT +bool TexFmt_Overlay_Enable=false; +bool TexFmt_Overlay_Center=false; +#endif + +// TRAM +// STATE_TO_SAVE +u8 texMem[TMEM_SIZE]; + +////////////////////////////////////////////////////////////////////////// +// Gamecube/Wii texture decoder +////////////////////////////////////////////////////////////////////////// +// Decodes all known Gamecube/Wii texture formats. +// by ector +////////////////////////////////////////////////////////////////////////// +int TexDecoder_GetTexelSizeInNibbles(int format) +{ + switch (format & 0x3f) { + case GX_TF_I4: return 1; + case GX_TF_I8: return 2; + case GX_TF_IA4: return 2; + case GX_TF_IA8: return 4; + case GX_TF_RGB565: return 4; + case GX_TF_RGB5A3: return 4; + case GX_TF_RGBA8: return 8; + case GX_TF_C4: return 1; + case GX_TF_C8: return 2; + case GX_TF_C14X2: return 4; + case GX_TF_CMPR: return 1; + default: return 1; + } +} + +int TexDecoder_GetTextureSizeInBytes(int width, int height, int format) +{ + return (width * height * TexDecoder_GetTexelSizeInNibbles(format)) / 2; +} + +u32 TexDecoder_GetTlutHash(const u16 *src, int len) +{ + u32 hash = 0xbeefbabe; + for (int i = 0; i < len / 2; i += 2) { + hash = _rotl(hash, 17) ^ ((u32 *)src)[i]; + } + return hash; +} + +u32 TexDecoder_GetSafeTextureHash(const u8 *src, int width, int height, int texformat, u32 seed) +{ + int sz = TexDecoder_GetTextureSizeInBytes(width, height, texformat); + + u32 hash = seed ? seed : 0x1337c0de; + + if (sz < 2048) { + for (int i = 0; i < sz / 4; i += 13) { + hash = _rotl(hash, 17) ^ ((u32 *)src)[i]; + } + return hash; + } else { + int step = sz / 13 / 4; + for (int i = 0; i < sz / 4; i += step) { + hash = _rotl(hash, 17) ^ ((u32 *)src)[i]; + } + } + return hash; +} + +int TexDecoder_GetBlockWidthInTexels(int format) +{ + switch (format) { + case GX_TF_I4: return 8; + case GX_TF_I8: return 8; + case GX_TF_IA4: return 8; + case GX_TF_IA8: return 4; + case GX_TF_RGB565: return 4; + case GX_TF_RGB5A3: return 4; + case GX_TF_RGBA8: return 4; + case GX_TF_C4: return 8; + case GX_TF_C8: return 8; + case GX_TF_C14X2: return 4; + case GX_TF_CMPR: return 8; + default: return 8; + } +} + +//returns bytes +int TexDecoder_GetPaletteSize(int format) +{ + switch (format) { + case GX_TF_C4: return 16*2; + case GX_TF_C8: return 256*2; + case GX_TF_C14X2: return 16384*2; + default: + return 0; + } +} + +inline u32 decode565(u16 val) +{ + int r,g,b,a; + r=lut5to8[(val>>11) & 0x1f]; + g=lut6to8[(val>>5 ) & 0x3f]; + b=lut5to8[(val ) & 0x1f]; + a=0xFF; + return (a<<24) | (r<<16) | (g<<8) | b; +} + +inline u32 decodeIA8(u16 val) +{ + int a=val>>8; + int i=val&0xFF; + return (a<<24) | (i<<16) | (i<<8) | i; +} + +inline u32 decode5A3(u16 val) +{ + int r,g,b,a; + if ((val&0x8000)) + { + r=lut5to8[(val>>10) & 0x1f]; + g=lut5to8[(val>>5 ) & 0x1f]; + b=lut5to8[(val ) & 0x1f]; + a=0xFF; + } + else + { + a=lut3to8[(val>>12) & 0x7]; + r=lut4to8[(val>>8 ) & 0xf]; + g=lut4to8[(val>>4 ) & 0xf]; + b=lut4to8[(val ) & 0xf]; + } + return (a<<24) | (r<<16) | (g<<8) | b; +} + + + +struct DXTBlock +{ + u16 color1; + u16 color2; + u8 lines[4]; +}; + +inline int expand8888(const int j) +{ + int i = j | (j<<8); + return i | (i<<16); +} + +//inline void decodebytesI4(u32 *dst, const u8 *src, int numbytes) +inline void decodebytesI4(u32 *dst, const u8 *src) +{ + for (int x = 0; x < 4; x++) + { + int val = src[x]; + *dst++ = expand8888(lut4to8[val>>4]); + *dst++ = expand8888(lut4to8[val&15]); + } +} + +inline void decodebytesI8_8(u32 *dst, const u8 *src) +{ + for (int x = 0; x < 8; x++) + dst[x] = src[x] * 0x01010101; //expand8888(src[x]); *0x... may or may not be faster. not sure. Should be faster on P4 at least. +} + +//inline void decodebytesC4(u32 *dst, const u8 *src, int numbytes, int tlutaddr, int tlutfmt) +inline void decodebytesC4(u32 *dst, const u8 *src, int tlutaddr, int tlutfmt) +{ + u16 *tlut = (u16*)(texMem + tlutaddr); + for (int x = 0; x < 4; x++) + { + int val = src[x]; + switch (tlutfmt) { + case 0: + *dst++ = decodeIA8(Common::swap16(tlut[val >> 4])); + *dst++ = decodeIA8(Common::swap16(tlut[val & 15])); + break; + case 1: + *dst++ = decode565(Common::swap16(tlut[val >> 4])); + *dst++ = decode565(Common::swap16(tlut[val & 15])); + break; + case 2: + *dst++ = decode5A3(Common::swap16(tlut[val >> 4])); + *dst++ = decode5A3(Common::swap16(tlut[val & 15])); + break; + case 3: //ERROR + *dst++ = 0xFFFF00FF; + *dst++ = 0xFFFF00FF; + break; + } + } +} + +//inline void decodebytesC8(u32 *dst, const u8 *src, int numbytes, int tlutaddr, int tlutfmt) +inline void decodebytesC8(u32 *dst, const u8 *src, int tlutaddr, int tlutfmt) +{ + u16 *tlut = (u16*)(texMem+tlutaddr); + for (int x = 0; x < 8; x++) + { + int val = src[x]; + switch (tlutfmt) { + case 0: + *dst++ = decodeIA8(Common::swap16(tlut[val])); + break; + case 1: + *dst++ = decode565(Common::swap16(tlut[val])); + break; + case 2: + *dst++ = decode5A3(Common::swap16(tlut[val])); + break; + case 3: //ERROR + *dst++ = 0xFFFF00FF; + break; + } + } +} + + +//inline void decodebytesC14X2(u32 *dst, const u16 *src, int numpixels, int tlutaddr, int tlutfmt) +inline void decodebytesC14X2(u32 *dst, const u16 *src, int tlutaddr, int tlutfmt) +{ + u16 *tlut = (u16*)(texMem+tlutaddr); + for (int x = 0; x < 4; x++) + { + int val = Common::swap16(src[x]); + switch (tlutfmt) { + case 0: + *dst++ = decodeIA8(Common::swap16(tlut[(val&0x3FFF)])); + break; + case 1: + *dst++ = decode565(Common::swap16(tlut[(val&0x3FFF)])); + break; + case 2: + *dst++ = decode5A3(Common::swap16(tlut[(val&0x3FFF)])); + break; + case 3: //ERROR + *dst++ = 0xFFFF00FF; + break; + } + } +} + +//inline void decodebytesRGB565(u32 *dst, const u16 *src, int numpixels) +inline void decodebytesRGB565(u32 *dst, const u16 *src) +{ + for (int x = 0; x < 4; x++) + *dst++ = decode565(Common::swap16(src[x])); +} + +//inline void decodebytesIA4(u32 *dst, const u8 *src, int numbytes) +inline void decodebytesIA4(u32 *dst, const u8 *src) +{ + for (int x = 0; x < 8; x++) + { + int val = src[x]; + int a = lut4to8[val>>4]; + int r = lut4to8[val&15]; + dst[x] = (a<<24) | (r<<16) | (r<<8) | r; + } +} + +//inline void decodebytesIA8(u32 *dst, const u16 *src, int numpixels) +inline void decodebytesIA8(u32 *dst, const u16 *src) +{ + for (int x = 0; x < 4; x++) + dst[x] = decodeIA8(Common::swap16(src[x])); +} + +//inline void decodebytesRGB5A3(u32 *dst, const u16 *src, int numpixels) +inline void decodebytesRGB5A3(u32 *dst, const u16 *src) +{ + for (int x = 0; x < 4; x++) + dst[x] = decode5A3(Common::swap16(src[x])); +} + +// This one is used by many video formats. It'd therefore be good if it was fast. +inline void decodebytesARGB8_4(u32 *dst, const u16 *src, const u16 *src2) +{ + for (int x = 0; x < 4; x++) { + dst[x] = Common::swap32((src2[x] << 16) | src[x]); + } + + // This can probably be done in a few SSE pack/unpack instructions + pshufb + // some unpack instruction x2: + // ABABABABABABABAB 1212121212121212 -> + // AB12AB12AB12AB12 AB12AB12AB12AB12 + // 2x pshufb-> + // 21BA21BA21BA21BA 21BA21BA21BA21BA + // and we are done. +} + +inline u32 makecol(int r, int g, int b, int a) +{ + return (a<<24)|(r<<16)|(g<<8)|b; +} + +void decodeDXTBlock(u32 *dst, const DXTBlock *src, int pitch) +{ + u16 c1 = Common::swap16(src->color1); + u16 c2 = Common::swap16(src->color2); + int blue1 = lut5to8[c1 & 0x1F]; + int blue2 = lut5to8[c2 & 0x1F]; + int green1 = lut6to8[(c1>>5) & 0x3F]; + int green2 = lut6to8[(c2>>5) & 0x3F]; + int red1 = lut5to8[(c1>>11) & 0x1F]; + int red2 = lut5to8[(c2>>11) & 0x1F]; + + int colors[4]; + + if (c1 > c2) + { + colors[0] = makecol(red1, green1, blue1, 255); + colors[1] = makecol(red2, green2, blue2, 255); + colors[2] = makecol(red1+(red2-red1)/3, green1+(green2-green1)/3, blue1+(blue2-blue1)/3, 255); + colors[3] = makecol(red2+(red1-red2)/3, green2+(green1-green2)/3, blue2+(blue1-blue2)/3, 255); + } + else + { + colors[0] = makecol(red1, green1, blue1, 255); + colors[1] = makecol(red2, green2, blue2, 255); + colors[2] = makecol((red1+red2)/2, (green1+green2)/2, (blue1+blue2)/2, 255); + colors[3] = makecol(0,0,0,0); //transparent + } + + for (int y = 0; y < 4; y++) + { + int val = src->lines[y]; + for (int x = 0; x < 4; x++) + { + dst[x] = colors[(val >> 6) & 3]; + val <<= 2; + } + dst += pitch; + } +} + + +//switch endianness, unswizzle +//TODO: to save memory, don't blindly convert everything to argb8888 +//also ARGB order needs to be swapped later, to accommodate modern hardware better +//need to add DXT support too +#ifdef OVERLAY_TEXFMT +PC_TexFormat TexDecoder_Decode_real(u8 *dst, const u8 *src, int width, int height, int texformat, int tlutaddr, int tlutfmt) +#else +PC_TexFormat TexDecoder_Decode(u8 *dst, const u8 *src, int width, int height, int texformat, int tlutaddr, int tlutfmt) +#endif +{ + switch (texformat) + { + case GX_TF_C4: + { + for (int y = 0; y < height; y += 8) + for (int x = 0; x < width; x += 8) + for (int iy = 0; iy < 8; iy++, src += 4) + //decodebytesC4((u32*)dst+(y+iy)*width+x, src, 4, tlutaddr, tlutfmt); + decodebytesC4((u32*)dst+(y+iy)*width+x, src, tlutaddr, tlutfmt); + } + return PC_TEX_FMT_BGRA32; + case GX_TF_I4: + { + // TODO: SSSE3 variant (pshufb), THP videos use this format. + // SSSE3 variant could bring even more speed +#if (defined(_WIN32) || (defined (_M_X64) && !defined(_WIN32))) + __m128i Lmask = _mm_set1_epi8 (0x0F); + __m128i Hmask = _mm_set1_epi8 (0xF0); + __m128i* sseSrc = (__m128i *)src; + __m128i* sseDst = (__m128i *)dst; + for (int y = 0; y < height; y += 8) + for (int x = 0; x < width; x += 8) + for (int iy = 0; iy < 8; iy++, sseSrc++) { + // TODO (mb2): func and don't let the optimizer perform all the clean up by itself + __m128i s = _mm_load_si128 (sseSrc); // ab cd ef gh ... + __m128i sl = _mm_and_si128 (s, Lmask); // 0b 0d 0f 0h ... + __m128i sls = _mm_slli_epi16 (sl, 4); // b0 d0 f0 h0 ... + __m128i sl_ = _mm_or_si128 (sl, sls); // bb dd ff ff ... + + __m128i sh = _mm_and_si128 (s, Hmask); // a0 c0 e0 g0 ... + __m128i shs = _mm_srli_epi16 (sh, 4); // 0a 0c 0e g0 ... + __m128i sh_ = _mm_or_si128 (sh, shs); // aa cc ee gg ... + __m128i rl = _mm_unpacklo_epi8 (sh_, sl_); // bb aa dd cc ... + __m128i rh = _mm_unpackhi_epi8 (sh_, sl_); // + + __m128i ral = _mm_unpacklo_epi8 (rl, rl); // bb bb aa aa ... + __m128i rah = _mm_unpackhi_epi8 (rl, rl); // + // result part a + __m128i rall = _mm_unpacklo_epi16 (ral, ral); // bb bb bb bb ... -> done + __m128i ralh = _mm_unpackhi_epi16 (ral, ral); // -> done + __m128i rahl = _mm_unpacklo_epi16 (rah, rah); // -> done + __m128i rahh = _mm_unpackhi_epi16 (rah, rah); // -> done + + __m128i rbl = _mm_unpacklo_epi8 (rh, rh); // + __m128i rbh = _mm_unpackhi_epi8 (rh, rh); // + // result part b + __m128i rbll = _mm_unpacklo_epi16 (rbl, rbl); // -> done + __m128i rblh = _mm_unpackhi_epi16 (rbl, rbl); // -> done + __m128i rbhl = _mm_unpacklo_epi16 (rbh, rbh); // -> done + __m128i rbhh = _mm_unpackhi_epi16 (rbh, rbh); // -> done + // store + sseDst = (__m128i*)(dst+((y+iy)*width+x)*4); // that sucks... too lazy + _mm_store_si128 (sseDst++, rall); + _mm_store_si128 (sseDst, ralh); + iy++; + sseDst = (__m128i*)(dst+((y+iy)*width+x)*4); + _mm_store_si128 (sseDst++, rahl); + _mm_store_si128 (sseDst, rahh); + iy++; + sseDst = (__m128i*)(dst+((y+iy)*width+x)*4); + _mm_store_si128 (sseDst++, rbll); + _mm_store_si128 (sseDst, rblh); + iy++; + sseDst = (__m128i*)(dst+((y+iy)*width+x)*4); + _mm_store_si128 (sseDst++, rbhl); + _mm_store_si128 (sseDst, rbhh); + } +#else + for (int y = 0; y < height; y += 8) + for (int x = 0; x < width; x += 8) + for (int iy = 0; iy < 8; iy++, src += 4) + //decodebytesI4((u32*)dst+(y+iy)*width+x, src, 4); + decodebytesI4((u32*)dst+(y+iy)*width+x, src); +#endif + } + return PC_TEX_FMT_BGRA32; + case GX_TF_C8: + { + for (int y = 0; y < height; y += 4) + for (int x = 0; x < width; x += 8) + for (int iy = 0; iy < 4; iy++, src += 8) + //decodebytesC8((u32*)dst+(y+iy)*width+x, src, 8, tlutaddr, tlutfmt); + decodebytesC8((u32*)dst+(y+iy)*width+x, src, tlutaddr, tlutfmt); + } + return PC_TEX_FMT_BGRA32; + case GX_TF_I8: // speed critical + { +#if (defined(_WIN32) || (defined (_M_X64) && !defined(_WIN32))) + __m128i *sseSrc = (__m128i *)src; + __m128i *sseDst = (__m128i *)dst; + for (int y = 0; y < height; y += 4) + for (int x = 0; x < width; x += 8) + for (int iy = 0; iy < 4; iy++, sseSrc++) { + // TODO (mb2): func and don't let the optimizer perform all the clean up by itself + __m128i s = _mm_load_si128 (sseSrc); // ab cd ef gh ... + __m128i rl = _mm_unpacklo_epi8 (s, s); // ab ab cd cd ... + __m128i rh = _mm_unpackhi_epi8 (s, s); // + // result + __m128i rll = _mm_unpacklo_epi8 (rl, rl); // ab ab ab ab + __m128i rlh = _mm_unpackhi_epi8 (rl, rl); + __m128i rhl = _mm_unpacklo_epi8 (rh, rh); + __m128i rhh = _mm_unpackhi_epi8 (rh, rh); + // store + sseDst = (__m128i*)(dst+((y+iy)*width+x)*4); + _mm_store_si128 (sseDst++, rll); + _mm_store_si128 (sseDst, rlh); + iy++; + sseDst = (__m128i*)(dst+((y+iy)*width+x)*4); + _mm_store_si128 (sseDst++, rhl); + _mm_store_si128 (sseDst, rhh); + } +#else + for (int y = 0; y < height; y += 4) + for (int x = 0; x < width; x += 8) + for (int iy = 0; iy < 4; iy++, src += 8) + decodebytesI8_8((u32*)dst+(y+iy)*width+x, src); +#endif + } + return PC_TEX_FMT_BGRA32; + case GX_TF_IA4: + { + for (int y = 0; y < height; y += 4) + for (int x = 0; x < width; x += 8) + for (int iy = 0; iy < 4; iy++, src += 8) + //decodebytesIA4((u32*)dst+(y+iy)*width+x, src, 8); + decodebytesIA4((u32*)dst+(y+iy)*width+x, src); + } + return PC_TEX_FMT_BGRA32; + case GX_TF_IA8: + { + for (int y = 0; y < height; y += 4) + for (int x = 0; x < width; x += 4) + for (int iy = 0; iy < 4; iy++, src += 8) + //decodebytesIA8((u32*)dst+(y+iy)*width+x, (u16*)src, 4); + decodebytesIA8((u32*)dst+(y+iy)*width+x, (u16*)src); + } + return PC_TEX_FMT_BGRA32; + case GX_TF_C14X2: + { + for (int y = 0; y < height; y += 4) + for (int x = 0; x < width; x += 4) + for (int iy = 0; iy < 4; iy++, src += 8) + //decodebytesC14X2((u32*)dst+(y+iy)*width+x, (u16*)src, 4, tlutaddr, tlutfmt); + decodebytesC14X2((u32*)dst+(y+iy)*width+x, (u16*)src, tlutaddr, tlutfmt); + } + return PC_TEX_FMT_BGRA32; + case GX_TF_RGB565: + { + for (int y = 0; y < height; y += 4) + for (int x = 0; x < width; x += 4) + for (int iy = 0; iy < 4; iy++, src += 8) + //decodebytesRGB565((u32*)dst+(y+iy)*width+x, (u16*)src, 4); + decodebytesRGB565((u32*)dst+(y+iy)*width+x, (u16*)src); + } + return PC_TEX_FMT_BGRA32; + case GX_TF_RGB5A3: + { + for (int y = 0; y < height; y += 4) + for (int x = 0; x < width; x += 4) + for (int iy = 0; iy < 4; iy++, src += 8) + //decodebytesRGB5A3((u32*)dst+(y+iy)*width+x, (u16*)src, 4); + decodebytesRGB5A3((u32*)dst+(y+iy)*width+x, (u16*)src); + } + return PC_TEX_FMT_BGRA32; + case GX_TF_RGBA8: // speed critical + { + for (int y = 0; y < height; y += 4) { + for (int x = 0; x < width; x += 4) + { + for (int iy = 0; iy < 4; iy++) { + decodebytesARGB8_4((u32*)dst + (y+iy)*width + x, (u16*)src + 4 * iy, (u16*)src + 4 * iy + 16); + } + src += 64; + } + } + } + return PC_TEX_FMT_BGRA32; + case GX_TF_CMPR: // speed critical + { + // TODO: Shuffle to PC S3TC (DXTC) format instead of converting + // 11111111 22222222 55555555 66666666 + // 33333333 44444444 77777777 88888888 + // The metroid games use this format almost exclusively. + for (int y = 0; y < height; y += 8) + for (int x = 0; x < width; x += 8) + { + decodeDXTBlock((u32*)dst+y*width+x, (DXTBlock*)src, width); + src += sizeof(DXTBlock); + decodeDXTBlock((u32*)dst+y*width+x+4, (DXTBlock*)src, width); + src += sizeof(DXTBlock); + decodeDXTBlock((u32*)dst+(y+4)*width+x, (DXTBlock*)src, width); + src += sizeof(DXTBlock); + decodeDXTBlock((u32*)dst+(y+4)*width+x+4, (DXTBlock*)src, width); + src += sizeof(DXTBlock); + } + } + return PC_TEX_FMT_BGRA32; + } + + // The "copy" texture formats, too? + return PC_TEX_FMT_NONE; +} + +void TexDecoder_SetTexFmtOverlayOptions(bool enable, bool center) +{ +#ifdef OVERLAY_TEXFMT + TexFmt_Overlay_Enable = enable; + TexFmt_Overlay_Center = center; +#endif +} + +#ifdef OVERLAY_TEXFMT +extern const char* texfmt[]; +extern const unsigned char sfont_map[]; +extern const unsigned char sfont_raw[][9*10]; + +PC_TexFormat TexDecoder_Decode(u8 *dst, const u8 *src, int width, int height, int texformat, int tlutaddr, int tlutfmt) +{ + PC_TexFormat retval = TexDecoder_Decode_real(dst,src,width,height,texformat,tlutaddr,tlutfmt); + + if((!TexFmt_Overlay_Enable)||(retval==PC_TEX_FMT_NONE)) + return retval; + + // assume ABGR/ARGB (32bit) + int *dtp = (int*)dst; + + int w = min(width,40); + int h = min(height,10); + + int xoff = (width-w)>>1; + int yoff = (height-h)>>1; + + if(!TexFmt_Overlay_Center) + { + xoff=0; + yoff=0; + } + + const char* fmt = texfmt[texformat&15]; + while(*fmt) + { + int xcnt = 0; + int nchar = sfont_map[(int)*fmt]; + + const unsigned char *ptr = sfont_raw[nchar]; // each char is up to 9x10 + + for(int x=0;x<9;x++) + { + if(ptr[x]==0x78) + break; + xcnt++; + } + for(int y=0;y<10;y++) + { + for(int x=0;x - -#include "Profiler.h" -#include "NativeVertexFormat.h" - -#include "BPMemory.h" -#include "VertexShader.h" - -static char text[16384]; - -#define WRITE p+=sprintf - -#define LIGHTS_POS "" - -char *GenerateLightShader(char* p, int index, const LitChannel& chan, const char* dest, int coloralpha); - -char *GenerateVertexShader(u32 components, bool has_zbuffer_target) -{ - text[sizeof(text) - 1] = 0x7C; // canary - DVSTARTPROFILE(); - - _assert_( bpmem.genMode.numtexgens == xfregs.numTexGens); - _assert_( bpmem.genMode.numcolchans == xfregs.nNumChans); - - u32 lightMask = 0; - if (xfregs.nNumChans > 0) - lightMask |= xfregs.colChans[0].color.GetFullLightMask() | xfregs.colChans[0].alpha.GetFullLightMask(); - if (xfregs.nNumChans > 1) - lightMask |= xfregs.colChans[1].color.GetFullLightMask() | xfregs.colChans[1].alpha.GetFullLightMask(); - - bool bOutputZ = bpmem.ztex2.op==ZTEXTURE_ADD || has_zbuffer_target; - int ztexcoord = -1; - - char *p = text; - WRITE(p, "//Vertex Shader: comp:%x, \n", components); - WRITE(p, "typedef struct {\n" - " float4 T0, T1, T2;\n" - " float4 N0, N1, N2;\n" - "} s_"I_POSNORMALMATRIX";\n\n" - "typedef struct {\n" - " float4 t;\n" - "} FLT4;\n" - "typedef struct {\n" - " FLT4 T[24];\n" - "} s_"I_TEXMATRICES";\n\n" - "typedef struct {\n" - " FLT4 T[64];\n" - "} s_"I_TRANSFORMMATRICES";\n\n" - "typedef struct {\n" - " FLT4 T[32];\n" - "} s_"I_NORMALMATRICES";\n\n" - "typedef struct {\n" - " FLT4 T[64];\n" - "} s_"I_POSTTRANSFORMMATRICES";\n\n" - "typedef struct {\n" - " float4 col;\n" - " float4 cosatt;\n" - " float4 distatt;\n" - " float4 pos;\n" - " float4 dir;\n" - "} Light;\n\n" - "typedef struct {\n" - " Light lights[8];\n" - "} s_"I_LIGHTS";\n\n" - "typedef struct {\n" - " float4 C0, C1, C2, C3;\n" - "} s_"I_MATERIALS";\n\n" - "typedef struct {\n" - " float4 T0,T1,T2,T3;\n" - "} s_"I_PROJECTION";\n" - "typedef struct {\n" - " float4 params;\n" // a, b, c, b_shift - "} s_"I_FOGPARAMS";\n\n"); - - WRITE(p, "struct VS_OUTPUT {\n"); - WRITE(p, " float4 pos : POSITION;\n"); - WRITE(p, " float4 colors[2] : COLOR0;\n"); - - // if outputting Z, embed the Z coordinate in the w component of a texture coordinate - // if number of tex gens occupies all the texture coordinates, use the last tex coord - // otherwise use the next available tex coord - for (int i = 0; i < xfregs.numTexGens; ++i) { - WRITE(p, " float%d tex%d : TEXCOORD%d;\n", (i==(xfregs.numTexGens-1)&&bOutputZ)?4:3, i, i); - } - if (bOutputZ && xfregs.numTexGens == 0) { - ztexcoord = 0; - WRITE(p, " float4 tex%d : TEXCOORD%d;\n", ztexcoord, ztexcoord); - } - else if (bOutputZ) - ztexcoord = xfregs.numTexGens - 1; - - WRITE(p, "};\n"); - WRITE(p, "\n"); - - // uniforms - - // bool bTexMtx = ((components & VB_HAS_TEXMTXIDXALL)<= 32 ? (posmtx-32) : posmtx;\n"); - WRITE(p, "float3 N0 = "I_NORMALMATRICES".T[normidx].t.xyz, N1 = "I_NORMALMATRICES".T[normidx+1].t.xyz, N2 = "I_NORMALMATRICES".T[normidx+2].t.xyz;\n"); - } - - if (components & VB_HAS_NRM0) - WRITE(p, "half3 _norm0 = half3(dot(N0, rawnorm0), dot(N1, rawnorm0), dot(N2, rawnorm0));\n" - "half3 norm0 = normalize(_norm0);\n"); - if (components & VB_HAS_NRM1) - WRITE(p, "half3 _norm1 = half3(dot(N0, rawnorm1), dot(N1, rawnorm1), dot(N2, rawnorm1));\n"); - //"half3 norm1 = normalize(_norm1);\n"); - if (components & VB_HAS_NRM2) - WRITE(p, "half3 _norm2 = half3(dot(N0, rawnorm2), dot(N1, rawnorm2), dot(N2, rawnorm2));\n"); - //"half3 norm2 = normalize(_norm2);\n"); - } - else { - WRITE(p, "float4 pos = float4(dot("I_POSNORMALMATRIX".T0, rawpos), dot("I_POSNORMALMATRIX".T1, rawpos), dot("I_POSNORMALMATRIX".T2, rawpos), 1);\n"); - if (components & VB_HAS_NRM0) - WRITE(p, "half3 _norm0 = half3(dot("I_POSNORMALMATRIX".N0.xyz, rawnorm0), dot("I_POSNORMALMATRIX".N1.xyz, rawnorm0), dot("I_POSNORMALMATRIX".N2.xyz, rawnorm0));\n" - "half3 norm0 = normalize(_norm0);\n"); - if (components & VB_HAS_NRM1) - WRITE(p, "half3 _norm1 = half3(dot("I_POSNORMALMATRIX".N0.xyz, rawnorm1), dot("I_POSNORMALMATRIX".N1.xyz, rawnorm1), dot("I_POSNORMALMATRIX".N2.xyz, rawnorm1));\n"); - //"half3 norm1 = normalize(_norm1);\n"); - if (components & VB_HAS_NRM2) - WRITE(p, "half3 _norm2 = half3(dot("I_POSNORMALMATRIX".N0.xyz, rawnorm2), dot("I_POSNORMALMATRIX".N1.xyz, rawnorm2), dot("I_POSNORMALMATRIX".N2.xyz, rawnorm2));\n"); - //"half3 norm2 = normalize(_norm2);\n"); - } - - if (!(components & VB_HAS_NRM0)) - WRITE(p, "half3 _norm0 = half3(0,0,0), norm0= half3(0,0,0);\n"); - - WRITE(p, "o.pos = float4(dot("I_PROJECTION".T0, pos), dot("I_PROJECTION".T1, pos), dot("I_PROJECTION".T2, pos), dot("I_PROJECTION".T3, pos));\n"); - - WRITE(p, "half4 mat, lacc;\n" - "half3 ldir, h;\n" - "half dist, dist2, attn;\n"); - - // lights/colors - for (int j = 0; j < xfregs.nNumChans; j++) { - - // bool bColorAlphaSame = xfregs.colChans[j].color.hex == xfregs.colChans[j].alpha.hex; unused - const LitChannel& color = xfregs.colChans[j].color; - const LitChannel& alpha = xfregs.colChans[j].alpha; - - WRITE(p, "{\n"); - - if (color.matsource) {// from vertex - if (components & (VB_HAS_COL0<= 0 ) - WRITE(p, "o.tex%d.w = o.pos.z/o.pos.w;\n", ztexcoord); - -// if (bpmem.fog.c_proj_fsel.fsel != 0) { -// switch (bpmem.fog.c_proj_fsel.fsel) { -// case 1: // linear -// break; -// case 4: // exp -// break; -// case 5: // exp2 -// break; -// case 6: // backward exp -// break; -// case 7: // backward exp2 -// break; -// } -// -// WRITE(p, "o.fog = o.pos.z/o.pos.w;\n"); -// } - - WRITE(p, "return o;\n}\n"); - - if (text[sizeof(text) - 1] != 0x7C) - PanicAlert("VertexShader generator - buffer too small, canary has been eaten!"); - return text; -} - -// coloralpha - 1 if color, 2 if alpha -char* GenerateLightShader(char* p, int index, const LitChannel& chan, const char* dest, int coloralpha) -{ - const char* swizzle = "xyzw"; - if (coloralpha == 1 ) swizzle = "xyz"; - else if (coloralpha == 2 ) swizzle = "w"; - - if (!(chan.attnfunc & 1)) { - // atten disabled - switch (chan.diffusefunc) { - case LIGHTDIF_NONE: - WRITE(p, "%s.%s += "I_LIGHTS".lights[%d].col.%s;\n", dest, swizzle, index, swizzle); - break; - case LIGHTDIF_SIGN: - case LIGHTDIF_CLAMP: - WRITE(p, "ldir = normalize("I_LIGHTS".lights[%d].pos.xyz - pos.xyz);\n", index); - WRITE(p, "%s.%s += %sdot(ldir, norm0)) * "I_LIGHTS".lights[%d].col.%s;\n", - dest, swizzle, chan.diffusefunc != LIGHTDIF_SIGN ? "max(0.0f," :"(", index, swizzle); - break; - default: _assert_(0); - } - } - else { // spec and spot - WRITE(p, "ldir = "I_LIGHTS".lights[%d].pos.xyz - pos.xyz;\n", index); - - if (chan.attnfunc == 3) { // spot - WRITE(p, "dist2 = dot(ldir, ldir);\n" - "dist = sqrt(dist2);\n" - "ldir = ldir / dist;\n" - "attn = max(0.0f, dot(ldir, "I_LIGHTS".lights[%d].dir.xyz));\n",index); - WRITE(p, "attn = max(0.0f, dot("I_LIGHTS".lights[%d].cosatt.xyz, half3(1, attn, attn*attn))) / dot("I_LIGHTS".lights[%d].distatt.xyz, half3(1,dist,dist2));\n", index, index); - } - else if (chan.attnfunc == 1) { // specular - WRITE(p, "attn = dot(norm0, "I_LIGHTS".lights[%d].pos.xyz) > 0 ? max(0.0f, dot(norm0, "I_LIGHTS".lights[%d].dir.xyz)) : 0;\n", index, index); - WRITE(p, "ldir = half3(1,attn,attn*attn);\n"); - WRITE(p, "attn = max(0.0f, dot("I_LIGHTS".lights[%d].cosatt.xyz, ldir)) / dot("I_LIGHTS".lights[%d].distatt.xyz, ldir);\n", index, index); - } - - switch (chan.diffusefunc) { - case LIGHTDIF_NONE: - WRITE(p, "%s.%s += attn * "I_LIGHTS".lights[%d].col.%s;\n", dest, swizzle, index, swizzle); - break; - case LIGHTDIF_SIGN: - case LIGHTDIF_CLAMP: - WRITE(p, "%s.%s += attn * %sdot(ldir, norm0)) * "I_LIGHTS".lights[%d].col.%s;\n", - dest, swizzle, chan.diffusefunc != LIGHTDIF_SIGN ? "max(0.0f," :"(", index, swizzle); - break; - default: _assert_(0); - } - } - WRITE(p, "\n"); - - return p; -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include + +#include "Profiler.h" +#include "NativeVertexFormat.h" + +#include "BPMemory.h" +#include "VertexShader.h" + +static char text[16384]; + +#define WRITE p+=sprintf + +#define LIGHTS_POS "" + +char *GenerateLightShader(char* p, int index, const LitChannel& chan, const char* dest, int coloralpha); + +char *GenerateVertexShader(u32 components, bool has_zbuffer_target) +{ + text[sizeof(text) - 1] = 0x7C; // canary + DVSTARTPROFILE(); + + _assert_( bpmem.genMode.numtexgens == xfregs.numTexGens); + _assert_( bpmem.genMode.numcolchans == xfregs.nNumChans); + + u32 lightMask = 0; + if (xfregs.nNumChans > 0) + lightMask |= xfregs.colChans[0].color.GetFullLightMask() | xfregs.colChans[0].alpha.GetFullLightMask(); + if (xfregs.nNumChans > 1) + lightMask |= xfregs.colChans[1].color.GetFullLightMask() | xfregs.colChans[1].alpha.GetFullLightMask(); + + bool bOutputZ = bpmem.ztex2.op==ZTEXTURE_ADD || has_zbuffer_target; + int ztexcoord = -1; + + char *p = text; + WRITE(p, "//Vertex Shader: comp:%x, \n", components); + WRITE(p, "typedef struct {\n" + " float4 T0, T1, T2;\n" + " float4 N0, N1, N2;\n" + "} s_"I_POSNORMALMATRIX";\n\n" + "typedef struct {\n" + " float4 t;\n" + "} FLT4;\n" + "typedef struct {\n" + " FLT4 T[24];\n" + "} s_"I_TEXMATRICES";\n\n" + "typedef struct {\n" + " FLT4 T[64];\n" + "} s_"I_TRANSFORMMATRICES";\n\n" + "typedef struct {\n" + " FLT4 T[32];\n" + "} s_"I_NORMALMATRICES";\n\n" + "typedef struct {\n" + " FLT4 T[64];\n" + "} s_"I_POSTTRANSFORMMATRICES";\n\n" + "typedef struct {\n" + " float4 col;\n" + " float4 cosatt;\n" + " float4 distatt;\n" + " float4 pos;\n" + " float4 dir;\n" + "} Light;\n\n" + "typedef struct {\n" + " Light lights[8];\n" + "} s_"I_LIGHTS";\n\n" + "typedef struct {\n" + " float4 C0, C1, C2, C3;\n" + "} s_"I_MATERIALS";\n\n" + "typedef struct {\n" + " float4 T0,T1,T2,T3;\n" + "} s_"I_PROJECTION";\n" + "typedef struct {\n" + " float4 params;\n" // a, b, c, b_shift + "} s_"I_FOGPARAMS";\n\n"); + + WRITE(p, "struct VS_OUTPUT {\n"); + WRITE(p, " float4 pos : POSITION;\n"); + WRITE(p, " float4 colors[2] : COLOR0;\n"); + + // if outputting Z, embed the Z coordinate in the w component of a texture coordinate + // if number of tex gens occupies all the texture coordinates, use the last tex coord + // otherwise use the next available tex coord + for (int i = 0; i < xfregs.numTexGens; ++i) { + WRITE(p, " float%d tex%d : TEXCOORD%d;\n", (i==(xfregs.numTexGens-1)&&bOutputZ)?4:3, i, i); + } + if (bOutputZ && xfregs.numTexGens == 0) { + ztexcoord = 0; + WRITE(p, " float4 tex%d : TEXCOORD%d;\n", ztexcoord, ztexcoord); + } + else if (bOutputZ) + ztexcoord = xfregs.numTexGens - 1; + + WRITE(p, "};\n"); + WRITE(p, "\n"); + + // uniforms + + // bool bTexMtx = ((components & VB_HAS_TEXMTXIDXALL)<= 32 ? (posmtx-32) : posmtx;\n"); + WRITE(p, "float3 N0 = "I_NORMALMATRICES".T[normidx].t.xyz, N1 = "I_NORMALMATRICES".T[normidx+1].t.xyz, N2 = "I_NORMALMATRICES".T[normidx+2].t.xyz;\n"); + } + + if (components & VB_HAS_NRM0) + WRITE(p, "half3 _norm0 = half3(dot(N0, rawnorm0), dot(N1, rawnorm0), dot(N2, rawnorm0));\n" + "half3 norm0 = normalize(_norm0);\n"); + if (components & VB_HAS_NRM1) + WRITE(p, "half3 _norm1 = half3(dot(N0, rawnorm1), dot(N1, rawnorm1), dot(N2, rawnorm1));\n"); + //"half3 norm1 = normalize(_norm1);\n"); + if (components & VB_HAS_NRM2) + WRITE(p, "half3 _norm2 = half3(dot(N0, rawnorm2), dot(N1, rawnorm2), dot(N2, rawnorm2));\n"); + //"half3 norm2 = normalize(_norm2);\n"); + } + else { + WRITE(p, "float4 pos = float4(dot("I_POSNORMALMATRIX".T0, rawpos), dot("I_POSNORMALMATRIX".T1, rawpos), dot("I_POSNORMALMATRIX".T2, rawpos), 1);\n"); + if (components & VB_HAS_NRM0) + WRITE(p, "half3 _norm0 = half3(dot("I_POSNORMALMATRIX".N0.xyz, rawnorm0), dot("I_POSNORMALMATRIX".N1.xyz, rawnorm0), dot("I_POSNORMALMATRIX".N2.xyz, rawnorm0));\n" + "half3 norm0 = normalize(_norm0);\n"); + if (components & VB_HAS_NRM1) + WRITE(p, "half3 _norm1 = half3(dot("I_POSNORMALMATRIX".N0.xyz, rawnorm1), dot("I_POSNORMALMATRIX".N1.xyz, rawnorm1), dot("I_POSNORMALMATRIX".N2.xyz, rawnorm1));\n"); + //"half3 norm1 = normalize(_norm1);\n"); + if (components & VB_HAS_NRM2) + WRITE(p, "half3 _norm2 = half3(dot("I_POSNORMALMATRIX".N0.xyz, rawnorm2), dot("I_POSNORMALMATRIX".N1.xyz, rawnorm2), dot("I_POSNORMALMATRIX".N2.xyz, rawnorm2));\n"); + //"half3 norm2 = normalize(_norm2);\n"); + } + + if (!(components & VB_HAS_NRM0)) + WRITE(p, "half3 _norm0 = half3(0,0,0), norm0= half3(0,0,0);\n"); + + WRITE(p, "o.pos = float4(dot("I_PROJECTION".T0, pos), dot("I_PROJECTION".T1, pos), dot("I_PROJECTION".T2, pos), dot("I_PROJECTION".T3, pos));\n"); + + WRITE(p, "half4 mat, lacc;\n" + "half3 ldir, h;\n" + "half dist, dist2, attn;\n"); + + // lights/colors + for (int j = 0; j < xfregs.nNumChans; j++) { + + // bool bColorAlphaSame = xfregs.colChans[j].color.hex == xfregs.colChans[j].alpha.hex; unused + const LitChannel& color = xfregs.colChans[j].color; + const LitChannel& alpha = xfregs.colChans[j].alpha; + + WRITE(p, "{\n"); + + if (color.matsource) {// from vertex + if (components & (VB_HAS_COL0<= 0 ) + WRITE(p, "o.tex%d.w = o.pos.z/o.pos.w;\n", ztexcoord); + +// if (bpmem.fog.c_proj_fsel.fsel != 0) { +// switch (bpmem.fog.c_proj_fsel.fsel) { +// case 1: // linear +// break; +// case 4: // exp +// break; +// case 5: // exp2 +// break; +// case 6: // backward exp +// break; +// case 7: // backward exp2 +// break; +// } +// +// WRITE(p, "o.fog = o.pos.z/o.pos.w;\n"); +// } + + WRITE(p, "return o;\n}\n"); + + if (text[sizeof(text) - 1] != 0x7C) + PanicAlert("VertexShader generator - buffer too small, canary has been eaten!"); + return text; +} + +// coloralpha - 1 if color, 2 if alpha +char* GenerateLightShader(char* p, int index, const LitChannel& chan, const char* dest, int coloralpha) +{ + const char* swizzle = "xyzw"; + if (coloralpha == 1 ) swizzle = "xyz"; + else if (coloralpha == 2 ) swizzle = "w"; + + if (!(chan.attnfunc & 1)) { + // atten disabled + switch (chan.diffusefunc) { + case LIGHTDIF_NONE: + WRITE(p, "%s.%s += "I_LIGHTS".lights[%d].col.%s;\n", dest, swizzle, index, swizzle); + break; + case LIGHTDIF_SIGN: + case LIGHTDIF_CLAMP: + WRITE(p, "ldir = normalize("I_LIGHTS".lights[%d].pos.xyz - pos.xyz);\n", index); + WRITE(p, "%s.%s += %sdot(ldir, norm0)) * "I_LIGHTS".lights[%d].col.%s;\n", + dest, swizzle, chan.diffusefunc != LIGHTDIF_SIGN ? "max(0.0f," :"(", index, swizzle); + break; + default: _assert_(0); + } + } + else { // spec and spot + WRITE(p, "ldir = "I_LIGHTS".lights[%d].pos.xyz - pos.xyz;\n", index); + + if (chan.attnfunc == 3) { // spot + WRITE(p, "dist2 = dot(ldir, ldir);\n" + "dist = sqrt(dist2);\n" + "ldir = ldir / dist;\n" + "attn = max(0.0f, dot(ldir, "I_LIGHTS".lights[%d].dir.xyz));\n",index); + WRITE(p, "attn = max(0.0f, dot("I_LIGHTS".lights[%d].cosatt.xyz, half3(1, attn, attn*attn))) / dot("I_LIGHTS".lights[%d].distatt.xyz, half3(1,dist,dist2));\n", index, index); + } + else if (chan.attnfunc == 1) { // specular + WRITE(p, "attn = dot(norm0, "I_LIGHTS".lights[%d].pos.xyz) > 0 ? max(0.0f, dot(norm0, "I_LIGHTS".lights[%d].dir.xyz)) : 0;\n", index, index); + WRITE(p, "ldir = half3(1,attn,attn*attn);\n"); + WRITE(p, "attn = max(0.0f, dot("I_LIGHTS".lights[%d].cosatt.xyz, ldir)) / dot("I_LIGHTS".lights[%d].distatt.xyz, ldir);\n", index, index); + } + + switch (chan.diffusefunc) { + case LIGHTDIF_NONE: + WRITE(p, "%s.%s += attn * "I_LIGHTS".lights[%d].col.%s;\n", dest, swizzle, index, swizzle); + break; + case LIGHTDIF_SIGN: + case LIGHTDIF_CLAMP: + WRITE(p, "%s.%s += attn * %sdot(ldir, norm0)) * "I_LIGHTS".lights[%d].col.%s;\n", + dest, swizzle, chan.diffusefunc != LIGHTDIF_SIGN ? "max(0.0f," :"(", index, swizzle); + break; + default: _assert_(0); + } + } + WRITE(p, "\n"); + + return p; +} diff --git a/Source/Core/VideoCommon/Src/VideoState.cpp b/Source/Core/VideoCommon/Src/VideoState.cpp index f1149be7f3..66f3611b9c 100644 --- a/Source/Core/VideoCommon/Src/VideoState.cpp +++ b/Source/Core/VideoCommon/Src/VideoState.cpp @@ -1,51 +1,51 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "VideoState.h" - -#include "BPMemory.h" -#include "CPMemory.h" -#include "XFMemory.h" -#include "TextureDecoder.h" -#include "Fifo.h" - -static void DoState(PointerWrap &p) -{ - // BP Memory - p.Do(bpmem); - // CP Memory - p.DoArray(arraybases, 16); - p.DoArray(arraystrides, 16); - p.Do(MatrixIndexA); - p.Do(MatrixIndexB); - p.Do(g_VtxDesc.Hex); - p.DoArray(g_VtxAttr, 8); - - // XF Memory - p.Do(xfregs); - p.DoArray(xfmem, XFMEM_SIZE); - // Texture decoder - p.DoArray(texMem, TMEM_SIZE); - - // FIFO - Fifo_DoState(p); -} - -void VideoCommon_DoState(PointerWrap &p) { - DoState(p); - //TODO: search for more data that should be saved and add it here -} +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "VideoState.h" + +#include "BPMemory.h" +#include "CPMemory.h" +#include "XFMemory.h" +#include "TextureDecoder.h" +#include "Fifo.h" + +static void DoState(PointerWrap &p) +{ + // BP Memory + p.Do(bpmem); + // CP Memory + p.DoArray(arraybases, 16); + p.DoArray(arraystrides, 16); + p.Do(MatrixIndexA); + p.Do(MatrixIndexB); + p.Do(g_VtxDesc.Hex); + p.DoArray(g_VtxAttr, 8); + + // XF Memory + p.Do(xfregs); + p.DoArray(xfmem, XFMEM_SIZE); + // Texture decoder + p.DoArray(texMem, TMEM_SIZE); + + // FIFO + Fifo_DoState(p); +} + +void VideoCommon_DoState(PointerWrap &p) { + DoState(p); + //TODO: search for more data that should be saved and add it here +} diff --git a/Source/Core/VideoCommon/Src/XFBConvert.cpp b/Source/Core/VideoCommon/Src/XFBConvert.cpp index 3f20ab421d..2aff13c15e 100644 --- a/Source/Core/VideoCommon/Src/XFBConvert.cpp +++ b/Source/Core/VideoCommon/Src/XFBConvert.cpp @@ -1,131 +1,131 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#if _WIN32 -#include -#endif - -#include - -#include "XFBConvert.h" -#include "Common.h" - -namespace { - -const __m128i _bias1 = _mm_set_epi32(128/2 << 16, 0, 128/2 << 16, 16 << 16); -const __m128i _bias2 = _mm_set_epi32(128/2 << 16, 16 << 16, 128/2 << 16, 0); - -__m128i _y[256]; -__m128i _u[256]; -__m128i _v[256]; - -__m128i _r1[256]; -__m128i _r2[256]; -__m128i _g1[256]; -__m128i _g2[256]; -__m128i _b1[256]; -__m128i _b2[256]; - -} // namespace - -void InitXFBConvTables() -{ - for (int i = 0; i < 256; i++) - { - _y[i] = _mm_set_epi32(0xFFFFFFF, 76283*(i - 16), 76283*(i - 16), 76283*(i - 16)); - _u[i] = _mm_set_epi32( 0, 0, -25624 * (i - 128), 132252 * (i - 128)); - _v[i] = _mm_set_epi32( 0, 104595 * (i - 128), -53281 * (i - 128), 0); - - _r1[i] = _mm_add_epi32(_mm_set_epi32( 28770 * i / 2, 0, -9699 * i / 2, 16843 * i), - _bias1); - _g1[i] = _mm_set_epi32(-24117 * i / 2, 0, -19071 * i / 2, 33030 * i); - _b1[i] = _mm_set_epi32( -4653 * i / 2, 0, 28770 * i / 2, 6423 * i); - - _r2[i] = _mm_add_epi32(_mm_set_epi32( 28770 * i / 2, 16843 * i, -9699 * i / 2, 0), - _bias2); - _g2[i] = _mm_set_epi32(-24117 * i / 2, 33030 * i, -19071 * i / 2, 0); - _b2[i] = _mm_set_epi32( -4653 * i / 2, 6423 * i, 28770 * i / 2, 0); - } -} - -void ConvertFromXFB(u32 *dst, const u8* _pXFB, int width, int height) -{ - if (((size_t)dst & 0xF) != 0) { - PanicAlert("ConvertFromXFB - unaligned destination"); - } - const unsigned char *src = _pXFB; - u32 numBlocks = ((width * height) / 2) / 2; - for (u32 i = 0; i < numBlocks; i++) - { - __m128i y1 = _y[src[0]]; - __m128i u = _u[src[1]]; - __m128i y2 = _y[src[2]]; - __m128i v = _v[src[3]]; - __m128i y1_2 = _y[src[4+0]]; - __m128i u_2 = _u[src[4+1]]; - __m128i y2_2 = _y[src[4+2]]; - __m128i v_2 = _v[src[4+3]]; - - __m128i c1 = _mm_srai_epi32(_mm_add_epi32(y1, _mm_add_epi32(u, v)), 16); - __m128i c2 = _mm_srai_epi32(_mm_add_epi32(y2, _mm_add_epi32(u, v)), 16); - __m128i c3 = _mm_srai_epi32(_mm_add_epi32(y1_2, _mm_add_epi32(u_2, v_2)), 16); - __m128i c4 = _mm_srai_epi32(_mm_add_epi32(y2_2, _mm_add_epi32(u_2, v_2)), 16); - - __m128i four_dest = _mm_packus_epi16(_mm_packs_epi32(c1, c2), _mm_packs_epi32(c3, c4)); - _mm_store_si128((__m128i *)dst, four_dest); - dst += 4; - src += 8; - } -} - -void ConvertToXFB(u32 *dst, const u8* _pEFB, int width, int height) -{ - const unsigned char *src = _pEFB; - - u32 numBlocks = ((width * height) / 2) / 4; - if (((size_t)dst & 0xF) != 0) { - PanicAlert("ConvertToXFB - unaligned XFB"); - } - - for (u32 i = 0; i < numBlocks; i++) - { - __m128i yuyv0 = _mm_srai_epi32( - _mm_add_epi32( - _mm_add_epi32(_r1[src[0]], _mm_add_epi32(_g1[src[1]], _b1[src[2]])), - _mm_add_epi32(_r2[src[4]], _mm_add_epi32(_g2[src[5]], _b2[src[6]]))), 16); - src += 8; - __m128i yuyv1 = _mm_srai_epi32( - _mm_add_epi32( - _mm_add_epi32(_r1[src[0]], _mm_add_epi32(_g1[src[1]], _b1[src[2]])), - _mm_add_epi32(_r2[src[4]], _mm_add_epi32(_g2[src[5]], _b2[src[6]]))), 16); - src += 8; - __m128i yuyv2 = _mm_srai_epi32( - _mm_add_epi32( - _mm_add_epi32(_r1[src[0]], _mm_add_epi32(_g1[src[1]], _b1[src[2]])), - _mm_add_epi32(_r2[src[4]], _mm_add_epi32(_g2[src[5]], _b2[src[6]]))), 16); - src += 8; - __m128i yuyv3 = _mm_srai_epi32( - _mm_add_epi32( - _mm_add_epi32(_r1[src[0]], _mm_add_epi32(_g1[src[1]], _b1[src[2]])), - _mm_add_epi32(_r2[src[4]], _mm_add_epi32(_g2[src[5]], _b2[src[6]]))), 16); - src += 8; - __m128i four_dest = _mm_packus_epi16(_mm_packs_epi32(yuyv0, yuyv1), _mm_packs_epi32(yuyv2, yuyv3)); - _mm_store_si128((__m128i *)dst, four_dest); - dst += 4; - } -} - +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#if _WIN32 +#include +#endif + +#include + +#include "XFBConvert.h" +#include "Common.h" + +namespace { + +const __m128i _bias1 = _mm_set_epi32(128/2 << 16, 0, 128/2 << 16, 16 << 16); +const __m128i _bias2 = _mm_set_epi32(128/2 << 16, 16 << 16, 128/2 << 16, 0); + +__m128i _y[256]; +__m128i _u[256]; +__m128i _v[256]; + +__m128i _r1[256]; +__m128i _r2[256]; +__m128i _g1[256]; +__m128i _g2[256]; +__m128i _b1[256]; +__m128i _b2[256]; + +} // namespace + +void InitXFBConvTables() +{ + for (int i = 0; i < 256; i++) + { + _y[i] = _mm_set_epi32(0xFFFFFFF, 76283*(i - 16), 76283*(i - 16), 76283*(i - 16)); + _u[i] = _mm_set_epi32( 0, 0, -25624 * (i - 128), 132252 * (i - 128)); + _v[i] = _mm_set_epi32( 0, 104595 * (i - 128), -53281 * (i - 128), 0); + + _r1[i] = _mm_add_epi32(_mm_set_epi32( 28770 * i / 2, 0, -9699 * i / 2, 16843 * i), + _bias1); + _g1[i] = _mm_set_epi32(-24117 * i / 2, 0, -19071 * i / 2, 33030 * i); + _b1[i] = _mm_set_epi32( -4653 * i / 2, 0, 28770 * i / 2, 6423 * i); + + _r2[i] = _mm_add_epi32(_mm_set_epi32( 28770 * i / 2, 16843 * i, -9699 * i / 2, 0), + _bias2); + _g2[i] = _mm_set_epi32(-24117 * i / 2, 33030 * i, -19071 * i / 2, 0); + _b2[i] = _mm_set_epi32( -4653 * i / 2, 6423 * i, 28770 * i / 2, 0); + } +} + +void ConvertFromXFB(u32 *dst, const u8* _pXFB, int width, int height) +{ + if (((size_t)dst & 0xF) != 0) { + PanicAlert("ConvertFromXFB - unaligned destination"); + } + const unsigned char *src = _pXFB; + u32 numBlocks = ((width * height) / 2) / 2; + for (u32 i = 0; i < numBlocks; i++) + { + __m128i y1 = _y[src[0]]; + __m128i u = _u[src[1]]; + __m128i y2 = _y[src[2]]; + __m128i v = _v[src[3]]; + __m128i y1_2 = _y[src[4+0]]; + __m128i u_2 = _u[src[4+1]]; + __m128i y2_2 = _y[src[4+2]]; + __m128i v_2 = _v[src[4+3]]; + + __m128i c1 = _mm_srai_epi32(_mm_add_epi32(y1, _mm_add_epi32(u, v)), 16); + __m128i c2 = _mm_srai_epi32(_mm_add_epi32(y2, _mm_add_epi32(u, v)), 16); + __m128i c3 = _mm_srai_epi32(_mm_add_epi32(y1_2, _mm_add_epi32(u_2, v_2)), 16); + __m128i c4 = _mm_srai_epi32(_mm_add_epi32(y2_2, _mm_add_epi32(u_2, v_2)), 16); + + __m128i four_dest = _mm_packus_epi16(_mm_packs_epi32(c1, c2), _mm_packs_epi32(c3, c4)); + _mm_store_si128((__m128i *)dst, four_dest); + dst += 4; + src += 8; + } +} + +void ConvertToXFB(u32 *dst, const u8* _pEFB, int width, int height) +{ + const unsigned char *src = _pEFB; + + u32 numBlocks = ((width * height) / 2) / 4; + if (((size_t)dst & 0xF) != 0) { + PanicAlert("ConvertToXFB - unaligned XFB"); + } + + for (u32 i = 0; i < numBlocks; i++) + { + __m128i yuyv0 = _mm_srai_epi32( + _mm_add_epi32( + _mm_add_epi32(_r1[src[0]], _mm_add_epi32(_g1[src[1]], _b1[src[2]])), + _mm_add_epi32(_r2[src[4]], _mm_add_epi32(_g2[src[5]], _b2[src[6]]))), 16); + src += 8; + __m128i yuyv1 = _mm_srai_epi32( + _mm_add_epi32( + _mm_add_epi32(_r1[src[0]], _mm_add_epi32(_g1[src[1]], _b1[src[2]])), + _mm_add_epi32(_r2[src[4]], _mm_add_epi32(_g2[src[5]], _b2[src[6]]))), 16); + src += 8; + __m128i yuyv2 = _mm_srai_epi32( + _mm_add_epi32( + _mm_add_epi32(_r1[src[0]], _mm_add_epi32(_g1[src[1]], _b1[src[2]])), + _mm_add_epi32(_r2[src[4]], _mm_add_epi32(_g2[src[5]], _b2[src[6]]))), 16); + src += 8; + __m128i yuyv3 = _mm_srai_epi32( + _mm_add_epi32( + _mm_add_epi32(_r1[src[0]], _mm_add_epi32(_g1[src[1]], _b1[src[2]])), + _mm_add_epi32(_r2[src[4]], _mm_add_epi32(_g2[src[5]], _b2[src[6]]))), 16); + src += 8; + __m128i four_dest = _mm_packus_epi16(_mm_packs_epi32(yuyv0, yuyv1), _mm_packs_epi32(yuyv2, yuyv3)); + _mm_store_si128((__m128i *)dst, four_dest); + dst += 4; + } +} + diff --git a/Source/Core/VideoCommon/Src/XFMemory.cpp b/Source/Core/VideoCommon/Src/XFMemory.cpp index 44000613c6..60c9c8811f 100644 --- a/Source/Core/VideoCommon/Src/XFMemory.cpp +++ b/Source/Core/VideoCommon/Src/XFMemory.cpp @@ -1,22 +1,22 @@ -// Copyright (C) 2003-2008 Dolphin Project. - -// This program is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, version 2.0. - -// This program is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License 2.0 for more details. - -// A copy of the GPL 2.0 should have been included with the program. -// If not, see http://www.gnu.org/licenses/ - -// Official SVN repository and contact information can be found at -// http://code.google.com/p/dolphin-emu/ - -#include "XFMemory.h" - -// STATE_TO_SAVE -XFRegisters xfregs; -u32 xfmem[XFMEM_SIZE]; +// Copyright (C) 2003-2008 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +#include "XFMemory.h" + +// STATE_TO_SAVE +XFRegisters xfregs; +u32 xfmem[XFMEM_SIZE]; diff --git a/Source/TestSuite/Audio/Test1/source/Test1.cpp b/Source/TestSuite/Audio/Test1/source/Test1.cpp index d82d3ab850..15f5e4376b 100644 --- a/Source/TestSuite/Audio/Test1/source/Test1.cpp +++ b/Source/TestSuite/Audio/Test1/source/Test1.cpp @@ -1,53 +1,53 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -static void *xfb = NULL; -static GXRModeObj *rmode = NULL; - -void *Initialise(); -int main(int argc, char **argv) { - - xfb = Initialise(); - AUDIO_Init(NULL); - AUDIO_SetStreamVolLeft(0xFF); // Max - AUDIO_SetStreamVolRight(0xFF); // Max - AUDIO_SetStreamSampleRate(AI_SAMPLERATE_48KHZ); - while(1) { - PAD_ScanPads(); - VIDEO_ClearFrameBuffer(rmode, xfb, 0); - - - VIDEO_WaitVSync(); - } - - return 0; -} - -void * Initialise() { - - void *framebuffer; - - VIDEO_Init(); - PAD_Init(); - - rmode = VIDEO_GetPreferredMode(NULL); - - framebuffer = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); - console_init(framebuffer,20,20,rmode->fbWidth,rmode->xfbHeight,rmode->fbWidth*VI_DISPLAY_PIX_SZ); - - VIDEO_Configure(rmode); - VIDEO_SetNextFramebuffer(framebuffer); - VIDEO_SetBlack(FALSE); - VIDEO_Flush(); - VIDEO_WaitVSync(); - if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync(); - - return framebuffer; - -} +#include +#include +#include +#include +#include +#include +#include +#include + +static void *xfb = NULL; +static GXRModeObj *rmode = NULL; + +void *Initialise(); +int main(int argc, char **argv) { + + xfb = Initialise(); + AUDIO_Init(NULL); + AUDIO_SetStreamVolLeft(0xFF); // Max + AUDIO_SetStreamVolRight(0xFF); // Max + AUDIO_SetStreamSampleRate(AI_SAMPLERATE_48KHZ); + while(1) { + PAD_ScanPads(); + VIDEO_ClearFrameBuffer(rmode, xfb, 0); + + + VIDEO_WaitVSync(); + } + + return 0; +} + +void * Initialise() { + + void *framebuffer; + + VIDEO_Init(); + PAD_Init(); + + rmode = VIDEO_GetPreferredMode(NULL); + + framebuffer = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); + console_init(framebuffer,20,20,rmode->fbWidth,rmode->xfbHeight,rmode->fbWidth*VI_DISPLAY_PIX_SZ); + + VIDEO_Configure(rmode); + VIDEO_SetNextFramebuffer(framebuffer); + VIDEO_SetBlack(FALSE); + VIDEO_Flush(); + VIDEO_WaitVSync(); + if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync(); + + return framebuffer; + +} diff --git a/Source/TestSuite/Misc/Aram/source/Aram.cpp b/Source/TestSuite/Misc/Aram/source/Aram.cpp index 7a2d4c3523..7933168998 100644 --- a/Source/TestSuite/Misc/Aram/source/Aram.cpp +++ b/Source/TestSuite/Misc/Aram/source/Aram.cpp @@ -1,56 +1,56 @@ -#include -#include -#include -#include -#include -#include -#include -#include -#include - -static void *xfb = NULL; -static GXRModeObj *rmode = NULL; - -void *Initialise(); -#define NUM_BLOCKS 10 -u32 aram_blocks[NUM_BLOCKS]; -u32 start_addr = -1; -int main(int argc, char **argv) { - - xfb = Initialise(); - start_addr = AR_Init(aram_blocks, NUM_BLOCKS); - while(1) { - PAD_ScanPads(); - VIDEO_ClearFrameBuffer(rmode, xfb, 0); - - std::cout << "Aram is " << (AR_GetDMAStatus() ? "In Progress" : "Idle") << std::endl; - std::cout << "Aram Start is 0x" << std::setbase(16) << start_addr << ", Allocated 0x" << AR_GetSize() << " Of memory" << std::setbase(10) << std::endl; - std::cout << "Internal Size is 0x" << std::setbase(16) << AR_GetInternalSize() << std::setbase(10) << std::endl; - VIDEO_WaitVSync(); - } - - return 0; -} - -void * Initialise() { - - void *framebuffer; - - VIDEO_Init(); - PAD_Init(); - - rmode = VIDEO_GetPreferredMode(NULL); - - framebuffer = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); - console_init(framebuffer,20,20,rmode->fbWidth,rmode->xfbHeight,rmode->fbWidth*VI_DISPLAY_PIX_SZ); - - VIDEO_Configure(rmode); - VIDEO_SetNextFramebuffer(framebuffer); - VIDEO_SetBlack(FALSE); - VIDEO_Flush(); - VIDEO_WaitVSync(); - if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync(); - - return framebuffer; - -} +#include +#include +#include +#include +#include +#include +#include +#include +#include + +static void *xfb = NULL; +static GXRModeObj *rmode = NULL; + +void *Initialise(); +#define NUM_BLOCKS 10 +u32 aram_blocks[NUM_BLOCKS]; +u32 start_addr = -1; +int main(int argc, char **argv) { + + xfb = Initialise(); + start_addr = AR_Init(aram_blocks, NUM_BLOCKS); + while(1) { + PAD_ScanPads(); + VIDEO_ClearFrameBuffer(rmode, xfb, 0); + + std::cout << "Aram is " << (AR_GetDMAStatus() ? "In Progress" : "Idle") << std::endl; + std::cout << "Aram Start is 0x" << std::setbase(16) << start_addr << ", Allocated 0x" << AR_GetSize() << " Of memory" << std::setbase(10) << std::endl; + std::cout << "Internal Size is 0x" << std::setbase(16) << AR_GetInternalSize() << std::setbase(10) << std::endl; + VIDEO_WaitVSync(); + } + + return 0; +} + +void * Initialise() { + + void *framebuffer; + + VIDEO_Init(); + PAD_Init(); + + rmode = VIDEO_GetPreferredMode(NULL); + + framebuffer = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); + console_init(framebuffer,20,20,rmode->fbWidth,rmode->xfbHeight,rmode->fbWidth*VI_DISPLAY_PIX_SZ); + + VIDEO_Configure(rmode); + VIDEO_SetNextFramebuffer(framebuffer); + VIDEO_SetBlack(FALSE); + VIDEO_Flush(); + VIDEO_WaitVSync(); + if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync(); + + return framebuffer; + +} diff --git a/Source/TestSuite/SI/Test1/source/Test1.cpp b/Source/TestSuite/SI/Test1/source/Test1.cpp index 4692878123..bf8b1ce9f3 100644 --- a/Source/TestSuite/SI/Test1/source/Test1.cpp +++ b/Source/TestSuite/SI/Test1/source/Test1.cpp @@ -1,55 +1,55 @@ -#include -#include -#include -#include -#include -#include -#include -#include - -static void *xfb = NULL; -static GXRModeObj *rmode = NULL; - -void *Initialise(); -int main(int argc, char **argv) { - - xfb = Initialise(); - - while(1) { - PAD_ScanPads(); - VIDEO_ClearFrameBuffer(rmode, xfb, 0); - for(int Chan = 0; Chan < 4; Chan++) - { - std::cout << "Chan " << Chan << std::endl; - std::cout << "Status is " << SI_GetStatus(Chan) << std::endl; - std::cout << "Type is 0x" << std::setbase(16) << SI_GetType(Chan) << std::setbase(10) << std::endl << std::endl; - } - - VIDEO_WaitVSync(); - } - - return 0; -} - -void * Initialise() { - - void *framebuffer; - - VIDEO_Init(); - PAD_Init(); - - rmode = VIDEO_GetPreferredMode(NULL); - - framebuffer = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); - console_init(framebuffer,20,20,rmode->fbWidth,rmode->xfbHeight,rmode->fbWidth*VI_DISPLAY_PIX_SZ); - - VIDEO_Configure(rmode); - VIDEO_SetNextFramebuffer(framebuffer); - VIDEO_SetBlack(FALSE); - VIDEO_Flush(); - VIDEO_WaitVSync(); - if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync(); - - return framebuffer; - -} +#include +#include +#include +#include +#include +#include +#include +#include + +static void *xfb = NULL; +static GXRModeObj *rmode = NULL; + +void *Initialise(); +int main(int argc, char **argv) { + + xfb = Initialise(); + + while(1) { + PAD_ScanPads(); + VIDEO_ClearFrameBuffer(rmode, xfb, 0); + for(int Chan = 0; Chan < 4; Chan++) + { + std::cout << "Chan " << Chan << std::endl; + std::cout << "Status is " << SI_GetStatus(Chan) << std::endl; + std::cout << "Type is 0x" << std::setbase(16) << SI_GetType(Chan) << std::setbase(10) << std::endl << std::endl; + } + + VIDEO_WaitVSync(); + } + + return 0; +} + +void * Initialise() { + + void *framebuffer; + + VIDEO_Init(); + PAD_Init(); + + rmode = VIDEO_GetPreferredMode(NULL); + + framebuffer = MEM_K0_TO_K1(SYS_AllocateFramebuffer(rmode)); + console_init(framebuffer,20,20,rmode->fbWidth,rmode->xfbHeight,rmode->fbWidth*VI_DISPLAY_PIX_SZ); + + VIDEO_Configure(rmode); + VIDEO_SetNextFramebuffer(framebuffer); + VIDEO_SetBlack(FALSE); + VIDEO_Flush(); + VIDEO_WaitVSync(); + if(rmode->viTVMode&VI_NON_INTERLACE) VIDEO_WaitVSync(); + + return framebuffer; + +}