set svn:eol-style=native for **.cpp

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@1442 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
bushing 2008-12-08 05:30:24 +00:00
parent 901fe7c00f
commit 49cfded60b
177 changed files with 53968 additions and 53968 deletions

View File

@ -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

View File

@ -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 <memory.h>
#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 <intrin.h>
#undef _interlockedbittestandset
#undef _interlockedbittestandreset
#undef _interlockedbittestandset64
#undef _interlockedbittestandreset64
#else
//#include <config/i386/cpuid.h>
#include <xmmintrin.h>
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 <memory.h>
#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 <intrin.h>
#undef _interlockedbittestandset
#undef _interlockedbittestandreset
#undef _interlockedbittestandset64
#undef _interlockedbittestandreset64
#else
//#include <config/i386/cpuid.h>
#include <xmmintrin.h>
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;
}

View File

@ -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 <stdio.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 "Common.h"
#include "ChunkFile.h"
#include <stdio.h>

View File

@ -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 <stdio.h>
#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 <stdio.h>
#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);
}*/

View File

@ -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 <windows.h>
#include <winioctl.h>
#endif
void GetAllRemovableDrives(std::vector<std::string> *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 <windows.h>
#include <winioctl.h>
#endif
void GetAllRemovableDrives(std::vector<std::string> *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
}

View File

@ -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 <string.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#include <stdio.h>
#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 <string.h>
#ifdef _WIN32
#include <windows.h>
#else
#include <dlfcn.h>
#include <stdio.h>
#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;
}

View File

@ -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 <windows.h>
#include <stdio.h>
#include "ExtendedTrace.h"
using namespace std;
#include <tchar.h>
#include <ImageHlp.h>
#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 <windows.h>
#include <stdio.h>
#include "ExtendedTrace.h"
using namespace std;
#include <tchar.h>
#include <ImageHlp.h>
#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

View File

@ -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 <sys/types.h>
#include <dirent.h>
#else
#include <windows.h>
#endif
#include <string>
#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 <sys/types.h>
#include <dirent.h>
#else
#include <windows.h>
#endif
#include <string>
#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);
}

View File

@ -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 <windows.h>
#include <shlobj.h> // for SHGetFolderPath
#include <shellapi.h>
#include <commdlg.h> // for GetSaveFileName
#include <io.h>
#include <direct.h> // getcwd
#else
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <stdlib.h>
#endif
#include <fstream>
#include <sys/stat.h>
#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 <windows.h>
#include <shlobj.h> // for SHGetFolderPath
#include <shellapi.h>
#include <commdlg.h> // for GetSaveFileName
#include <io.h>
#include <direct.h> // getcwd
#else
#include <sys/types.h>
#include <dirent.h>
#include <errno.h>
#include <stdlib.h>
#endif
#include <fstream>
#include <sys/stat.h>
#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

View File

@ -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);
}

View File

@ -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 <stdlib.h>
#include <stdio.h>
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <algorithm>
#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<Section>::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<Section>::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 = &sections[sections.size() - 1];
}
return(section);
}
bool IniFile::DeleteSection(const char* sectionName)
{
Section* s = GetSection(sectionName);
if (!s)
{
return false;
}
for (std::vector<Section>::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<std::string>::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<std::string> &lines)
{
Section* section = GetOrCreateSection(sectionName);
section->lines.clear();
for (std::vector<std::string>::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<std::string>::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<Section>::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<std::string>::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<std::string>& keys) const
{
const Section* section = GetSection(sectionName);
if (!section)
{
return false;
}
keys.clear();
for (std::vector<std::string>::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<std::string>& lines) const
{
const Section* section = GetSection(sectionName);
if (!section)
return false;
lines.clear();
for (std::vector<std::string>::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 <stdlib.h>
#include <stdio.h>
#include <string>
#include <vector>
#include <iostream>
#include <fstream>
#include <algorithm>
#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<Section>::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<Section>::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 = &sections[sections.size() - 1];
}
return(section);
}
bool IniFile::DeleteSection(const char* sectionName)
{
Section* s = GetSection(sectionName);
if (!s)
{
return false;
}
for (std::vector<Section>::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<std::string>::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<std::string> &lines)
{
Section* section = GetOrCreateSection(sectionName);
section->lines.clear();
for (std::vector<std::string>::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<std::string>::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<Section>::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<std::string>::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<std::string>& keys) const
{
const Section* section = GetSection(sectionName);
if (!section)
{
return false;
}
keys.clear();
for (std::vector<std::string>::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<std::string>& lines) const
{
const Section* section = GetSection(sectionName);
if (!section)
return false;
lines.clear();
for (std::vector<std::string>::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;
}
*/

View File

@ -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 <windows.h>
#else
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#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::map<u8*, u8*>Lockmap;
Lockmap lockMap;
#ifdef _WIN32
HANDLE hFile;
HANDLE hFileMapping;
#elif POSIX
int fd;
typedef std::map<u8*, size_t>Sizemap;
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 <windows.h>
#else
#include <unistd.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#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::map<u8*, u8*>Lockmap;
Lockmap lockMap;
#ifdef _WIN32
HANDLE hFile;
HANDLE hFileMapping;
#elif POSIX
int fd;
typedef std::map<u8*, size_t>Sizemap;
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

View File

@ -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 <xmmintrin.h>
#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 <xmmintrin.h>
#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();
}

View File

@ -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 <windows.h>
#else
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#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<u8*>(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<u8*>(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 <windows.h>
#else
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <cerrno>
#include <cstring>
#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<u8*>(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<u8*>(base);
#endif
#endif
}

View File

@ -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 <windows.h>
#elif __GNUC__
#include <sys/mman.h>
#include <errno.h>
#include <stdio.h>
#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 <windows.h>
#elif __GNUC__
#include <sys/mman.h>
#include <errno.h>
#include <stdio.h>
#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
}

View File

@ -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

View File

@ -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 <stdlib.h>
#include <stdio.h>
#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<std::string>& 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 <stdlib.h>
#include <stdio.h>
#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<std::string>& 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;
}

View File

@ -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);
}

View File

@ -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 <windows.h>
#elif __GNUC__
#include <unistd.h>
#include <pthread.h>
#else
#error unsupported platform
#endif
#include "Thread.h"
namespace Common
{
#ifdef _WIN32
CriticalSection::CriticalSection(int spincount)
{
if (spincount)
{
InitializeCriticalSectionAndSpinCount(&section, spincount);
}
else
{
InitializeCriticalSection(&section);
}
}
CriticalSection::~CriticalSection()
{
DeleteCriticalSection(&section);
}
void CriticalSection::Enter()
{
EnterCriticalSection(&section);
}
bool CriticalSection::TryEnter()
{
return(TryEnterCriticalSection(&section) ? true : false);
}
void CriticalSection::Leave()
{
LeaveCriticalSection(&section);
}
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 <windows.h>
#elif __GNUC__
#include <unistd.h>
#include <pthread.h>
#else
#error unsupported platform
#endif
#include "Thread.h"
namespace Common
{
#ifdef _WIN32
CriticalSection::CriticalSection(int spincount)
{
if (spincount)
{
InitializeCriticalSectionAndSpinCount(&section, spincount);
}
else
{
InitializeCriticalSection(&section);
}
}
CriticalSection::~CriticalSection()
{
DeleteCriticalSection(&section);
}
void CriticalSection::Enter()
{
EnterCriticalSection(&section);
}
bool CriticalSection::TryEnter()
{
return(TryEnterCriticalSection(&section) ? true : false);
}
void CriticalSection::Leave()
{
LeaveCriticalSection(&section);
}
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

View File

@ -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 <map>
#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<void *, const u8 *> 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<void *, const u8 *>::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 <map>
#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<void *, const u8 *> 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<void *, const u8 *>::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;
}

View File

@ -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 <windows.h>
#include <mmsystem.h>
#endif
#include <time.h>
#include "Common.h"
#include "Timer.h"
#ifdef __GNUC__
#include <sys/timeb.h>
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(&ltime);
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 <windows.h>
#include <mmsystem.h>
#endif
#include <time.h>
#include "Common.h"
#include "Timer.h"
#ifdef __GNUC__
#include <sys/timeb.h>
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(&ltime);
return((u64)ltime);
}
} // end of namespace Common

View File

@ -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;
}

View File

@ -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"

View File

@ -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;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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 <string>
#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; i<GetNumSections(); i++)
{
Elf32_Shdr *s = &sections[i];
const char *name = GetSectionName(i);
u32 writeAddr = s->sh_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 <string>
#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; i<GetNumSections(); i++)
{
Elf32_Shdr *s = &sections[i];
const char *name = GetSectionName(i);
u32 writeAddr = s->sh_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;
}

View File

@ -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 <iostream>
#include <cstring>
#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<end; i++)
{
u8 b = Memory::ReadUnchecked_U8(i);
fputc(b,f);
}
fclose(f);
LOG(CONSOLE, "Dumped from %08x to %08x to %s",start,end,filename);
}
CASE("disa")
{
u32 start;
u32 end;
TCHAR temp[256];
sscanf(cmd, "%s %08x %08x", temp, &start, &end);
for (u32 addr = start; addr <= end; addr += 4) {
u32 data = Memory::ReadUnchecked_U32(addr);
printf("%08x: %08x: %s\n", addr, data, DisassembleGekko(data, addr));
}
}
CASE("help")
{
LOG(CONSOLE, "Dolphin Console Command List");
LOG(CONSOLE, "scan ADDR - will find functions that are called by this function");
LOG(CONSOLE, "call ADDR - will find functions that call this function");
LOG(CONSOLE, "dump START_A END_A FILENAME - will dump memory between START_A and END_A");
LOG(CONSOLE, "help - guess what this does :P");
LOG(CONSOLE, "lisd - list signature database");
LOG(CONSOLE, "lisf - list functions");
LOG(CONSOLE, "trans ADDR - translate address");
}
CASE("lisd")
{
// PPCAnalyst::ListDB();
}
CASE("ipro")
{
PPCTables::PrintInstructionRunCounts();
}
CASE("lisf")
{
g_symbolDB.List();
}
else {
printf("blach\n");
LOG(CONSOLE, "Invalid command");
}
}
// Copyright (C) 2003-2008 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, see http://www.gnu.org/licenses/
// Official SVN repository and contact information can be found at
// http://code.google.com/p/dolphin-emu/
#include <iostream>
#include <cstring>
#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<end; i++)
{
u8 b = Memory::ReadUnchecked_U8(i);
fputc(b,f);
}
fclose(f);
LOG(CONSOLE, "Dumped from %08x to %08x to %s",start,end,filename);
}
CASE("disa")
{
u32 start;
u32 end;
TCHAR temp[256];
sscanf(cmd, "%s %08x %08x", temp, &start, &end);
for (u32 addr = start; addr <= end; addr += 4) {
u32 data = Memory::ReadUnchecked_U32(addr);
printf("%08x: %08x: %s\n", addr, data, DisassembleGekko(data, addr));
}
}
CASE("help")
{
LOG(CONSOLE, "Dolphin Console Command List");
LOG(CONSOLE, "scan ADDR - will find functions that are called by this function");
LOG(CONSOLE, "call ADDR - will find functions that call this function");
LOG(CONSOLE, "dump START_A END_A FILENAME - will dump memory between START_A and END_A");
LOG(CONSOLE, "help - guess what this does :P");
LOG(CONSOLE, "lisd - list signature database");
LOG(CONSOLE, "lisf - list functions");
LOG(CONSOLE, "trans ADDR - translate address");
}
CASE("lisd")
{
// PPCAnalyst::ListDB();
}
CASE("ipro")
{
PPCTables::PrintInstructionRunCounts();
}
CASE("lisf")
{
g_symbolDB.List();
}
else {
printf("blach\n");
LOG(CONSOLE, "Invalid command");
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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;
}

View File

@ -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 <vector>
#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<EventType> event_types;
struct BaseEvent
{
s64 time;
u64 userdata;
int type;
// Event *next;
};
typedef LinkedListItem<BaseEvent> 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 <vector>
#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<EventType> event_types;
struct BaseEvent
{
s64 time;
u64 userdata;
int type;
// Event *next;
};
typedef LinkedListItem<BaseEvent> 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

View File

@ -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<TBreakPoint>::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<TBreakPoint>::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<TMemCheck>::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<TBreakPoint>::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<TBreakPoint>::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<TMemCheck>::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<TBreakPoint>::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<TBreakPoint>::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<TMemCheck>::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<TBreakPoint>::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<TBreakPoint>::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<TMemCheck>::iterator iter;
for (iter = m_MemChecks.begin(); iter != m_MemChecks.end(); ++iter)
{
if ((*iter).StartAddress == _Address)
{
m_MemChecks.erase(iter);
Host_UpdateBreakPointView();
return;
}
}
}

View File

@ -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<CallstackEntry> &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<CallstackEntry> &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

View File

@ -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 <stdio.h>
#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 <stdio.h>
#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;
}

View File

@ -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[] = "<unknown>";
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[] = "<unknown>";
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[] = "<unknown>";
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[] = "<unknown>";
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()
{
}

View File

@ -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);
}
}

View File

@ -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 <cmath>
#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 <cmath>
#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;
}
}

View File

@ -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 <string>
#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 <string>
#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);
}
}

View File

@ -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<u32>(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<u32>(Diff / g_CPUCyclesPerSample);
g_LastCPUTime += Samples * g_CPUCyclesPerSample;
IncreaseSampleCount(Samples);
}
}
}
} // end of namespace AudioInterface

View File

@ -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);
}

View File

@ -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 <windows.h>
#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 <windows.h>
#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

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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<NUM_CHANNELS; i++)
{
if(g_Channels[i].IsCausingInterrupt())
{
CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_EXI, true);
return;
}
}
CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_EXI, false);
}
} // end of namespace ExpansionInterface
// Copyright (C) 2003-2008 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, 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<NUM_CHANNELS; i++)
{
if(g_Channels[i].IsCausingInterrupt())
{
CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_EXI, true);
return;
}
}
CPeripheralInterface::SetInterrupt(CPeripheralInterface::INT_CAUSE_EXI, false);
}
} // end of namespace ExpansionInterface

View File

@ -1,284 +1,284 @@
// Copyright (C) 2003-2008 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, 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;
}
}
// Copyright (C) 2003-2008 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, 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;
}
}

View File

@ -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;
}

View File

@ -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++;
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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

View File

@ -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);
}
}

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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 <stdio.h>
#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 <stdio.h>
#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;
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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 <stdio.h>
#include <stdlib.h>
#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<EBufferCommands>(_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<u8*>(&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<u8*>(&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<u32*>(_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 <stdio.h>
#include <stdlib.h>
#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<EBufferCommands>(_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<u8*>(&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<u8*>(&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<u32*>(_pBuffer)[0] = 0x00000000; // no device
return 4;
}
bool CSIDevice_Dummy::GetData(u32& _Hi, u32& _Low)
{
return false;
}
void CSIDevice_Dummy::SendCommand(u32 _Cmd)
{
}

View File

@ -1,61 +1,61 @@
/*********************************************************************
Nintendo GameCube ADPCM Decoder Core Class
Author: Shinji Chiba <ch3@mail.goo.ne.jp>
*********************************************************************/
#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 <ch3@mail.goo.ne.jp>
*********************************************************************/
#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 );
}
}

View File

@ -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

File diff suppressed because it is too large Load Diff

View File

@ -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

View File

@ -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 <map>
#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 <map>
#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

View File

@ -1,2 +1,2 @@
#include "Host.h"
#include "Host.h"

View File

@ -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 <map>
#include <string>
#include <list>
#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<u32, IWII_IPC_HLE_Device*> TDeviceMap;
TDeviceMap g_DeviceMap;
// STATE_TO_SAVE
u32 g_LastDeviceID = 0x13370000;
std::list<u32> g_Ack;
std::queue<std::pair<u32,std::string> > 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<u32>::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<ECommandType>(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<u32, std::string>(_Address, pDevice->GetDeviceName()));
} else {
LOG(WII_IPC_HLE, "IOP: Reply to unknown device ID (DeviceID=%i)", DeviceID);
g_ReplyQueue.push(std::pair<u32, std::string>(_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<u32, std::string>(_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<u32, std::string>(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 <map>
#include <string>
#include <list>
#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<u32, IWII_IPC_HLE_Device*> TDeviceMap;
TDeviceMap g_DeviceMap;
// STATE_TO_SAVE
u32 g_LastDeviceID = 0x13370000;
std::list<u32> g_Ack;
std::queue<std::pair<u32,std::string> > 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<u32>::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<ECommandType>(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<u32, std::string>(_Address, pDevice->GetDeviceName()));
} else {
LOG(WII_IPC_HLE, "IOP: Reply to unknown device ID (DeviceID=%i)", DeviceID);
g_ReplyQueue.push(std::pair<u32, std::string>(_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<u32, std::string>(_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<u32, std::string>(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

View File

@ -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;
}

View File

@ -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;
}

View File

@ -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<FileSearch.GetFileNames().size(); i++)
{
if (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<FileSearch.GetFileNames().size(); i++)
{
overAllSize += File::GetSize(FileSearch.GetFileNames()[i].c_str());
}
fsBlock = (u32)(overAllSize / (16 * 1024)); // one bock is 16kb
iNodes = (u32)(FileSearch.GetFileNames().size());
ReturnValue = FS_RESULT_OK;
LOGV(WII_IPC_FILEIO, 1, " fsBlock: %i, iNodes: %i", fsBlock, iNodes);
}
else
{
fsBlock = 0;
iNodes = 0;
ReturnValue = FS_RESULT_OK;
// PanicAlert("IOCTL_GETUSAGE - unk dir %s", Filename.c_str());
LOGV(WII_IPC_FILEIO, 1, " error: not executed on a valid directoy: %s", Filename.c_str());
}
Memory::Write_U32(fsBlock, CommandBuffer.PayloadBuffer[0].m_Address);
Memory::Write_U32(iNodes, CommandBuffer.PayloadBuffer[1].m_Address);
}
break;
default:
PanicAlert("CWII_IPC_HLE_Device_fs::IOCtlV: %i", CommandBuffer.Parameter);
break;
}
Memory::Write_U32(ReturnValue, _CommandAddress+4);
return true;
}
// =======================================================
// IOCtl calls begin here
// -------------
bool CWII_IPC_HLE_Device_fs::IOCtl(u32 _CommandAddress)
{
//u32 DeviceID = Memory::Read_U32(_CommandAddress + 8);
//LOG(WII_IPC_FILEIO, "FS: IOCtl (Device=%s, DeviceID=%08x)", GetDeviceName().c_str(), DeviceID);
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);
/* Prepare the out buffer(s) with zeroes as a safety precaution
to avoid returning bad values. */
//LOG(WII_IPC_FILEIO, "Cleared %u bytes of the out buffer", _BufferOutSize);
Memory::Memset(BufferOut, 0, BufferOutSize);
u32 ReturnValue = ExecuteCommand(Parameter, BufferIn, BufferInSize, BufferOut, BufferOutSize);
Memory::Write_U32(ReturnValue, _CommandAddress + 4);
return true;
}
// =======================================================
// Execute IOCtl commands
// -------------
s32 CWII_IPC_HLE_Device_fs::ExecuteCommand(u32 _Parameter, u32 _BufferIn, u32 _BufferInSize, u32 _BufferOut, u32 _BufferOutSize)
{
switch(_Parameter)
{
case GET_STATS:
{
_dbg_assert_(WII_IPC_FILEIO, _BufferOutSize == 28);
LOG(WII_IPC_FILEIO, "FS: GET STATS - no idea what we have to return here, prolly the free memory etc:)");
LOG(WII_IPC_FILEIO, " InBufferSize: %i OutBufferSize: %i", _BufferInSize, _BufferOutSize);
PanicAlert("GET_STATS");
/* Memory::Write_U32(Addr, a); Addr += 4;
Memory::Write_U32(Addr, b); Addr += 4;
Memory::Write_U32(Addr, c); Addr += 4;
Memory::Write_U32(Addr, d); Addr += 4;
Memory::Write_U32(Addr, e); Addr += 4;
Memory::Write_U32(Addr, f); Addr += 4;
Memory::Write_U32(Addr, g); Addr += 4;
*/
return FS_RESULT_OK;
}
break;
case CREATE_DIR:
{
_dbg_assert_(WII_IPC_FILEIO, _BufferOutSize == 0);
u32 Addr = _BufferIn;
u32 OwnerID = Memory::Read_U32(Addr); Addr += 4;
u16 GroupID = Memory::Read_U16(Addr); Addr += 2;
std::string DirName(HLE_IPC_BuildFilename((const char*)Memory::GetPointer(Addr), 64)); Addr += 64;
Addr += 9; // owner attribs, permission
u8 Attribs = Memory::Read_U8(Addr);
LOG(WII_IPC_FILEIO, "FS: CREATE_DIR %s", DirName.c_str());
DirName += DIR_SEP;
File::CreateDirectoryStructure(DirName );
_dbg_assert_msg_(WII_IPC_FILEIO, File::IsDirectory(DirName.c_str()), "FS: CREATE_DIR %s failed", DirName.c_str());
return FS_RESULT_OK;
}
break;
case SET_ATTR:
{
u32 Addr = _BufferIn;
u32 OwnerID = Memory::Read_U32(Addr); Addr += 4;
u16 GroupID = Memory::Read_U16(Addr); Addr += 2;
std::string Filename = HLE_IPC_BuildFilename((const char*)Memory::GetPointer(_BufferIn), 64); Addr += 64;
u8 OwnerPerm = Memory::Read_U8(Addr); Addr += 1;
u8 GroupPerm = Memory::Read_U8(Addr); Addr += 1;
u8 OtherPerm = Memory::Read_U8(Addr); Addr += 1;
u8 Attributes = Memory::Read_U8(Addr); Addr += 1;
LOGV(WII_IPC_FILEIO, 0, "FS: SetAttrib %s", Filename.c_str());
LOG(WII_IPC_FILEIO, " OwnerID: 0x%08x", OwnerID);
LOG(WII_IPC_FILEIO, " GroupID: 0x%04x", GroupID);
LOG(WII_IPC_FILEIO, " OwnerPerm: 0x%02x", OwnerPerm);
LOG(WII_IPC_FILEIO, " GroupPerm: 0x%02x", GroupPerm);
LOG(WII_IPC_FILEIO, " OtherPerm: 0x%02x", OtherPerm);
LOG(WII_IPC_FILEIO, " Attributes: 0x%02x", Attributes);
return FS_RESULT_OK;
}
break;
case GET_ATTR:
{
_dbg_assert_msg_(WII_IPC_FILEIO, _BufferOutSize == 76,
" GET_ATTR needs an 76 bytes large output buffer but it is %i bytes large",
_BufferOutSize);
u32 OwnerID = 0;
u16 GroupID = 0;
std::string Filename = HLE_IPC_BuildFilename((const char*)Memory::GetPointer(_BufferIn), 64);
u8 OwnerPerm = 0x3; // read/write
u8 GroupPerm = 0x3; // read/write
u8 OtherPerm = 0x3; // read/write
u8 Attributes = 0x00; // no attributes
if (File::IsDirectory(Filename.c_str()))
{
LOG(WII_IPC_FILEIO, "FS: GET_ATTR Directory %s - all permission flags are set", Filename.c_str());
}
else
{
if (File::Exists(Filename.c_str()))
{
LOG(WII_IPC_FILEIO, "FS: GET_ATTR %s - all permission flags are set", Filename.c_str());
}
else
{
LOG(WII_IPC_FILEIO, "FS: GET_ATTR unknown %s", Filename.c_str());
return FS_FILE_NOT_EXIST;
}
}
// write answer to buffer
if (_BufferOutSize == 76)
{
u32 Addr = _BufferOut;
Memory::Write_U32(OwnerID, Addr); Addr += 4;
Memory::Write_U16(GroupID, Addr); Addr += 2;
memcpy(Memory::GetPointer(Addr), Filename.c_str(), Filename.size()); Addr += 64;
Memory::Write_U8(OwnerPerm, Addr); Addr += 1;
Memory::Write_U8(GroupPerm, Addr); Addr += 1;
Memory::Write_U8(OtherPerm, Addr); Addr += 1;
Memory::Write_U8(Attributes, Addr); Addr += 1;
}
return FS_RESULT_OK;
}
break;
case DELETE_FILE:
{
_dbg_assert_(WII_IPC_FILEIO, _BufferOutSize == 0);
int Offset = 0;
std::string Filename = HLE_IPC_BuildFilename((const char*)Memory::GetPointer(_BufferIn+Offset), 64);
Offset += 64;
if (File::Delete(Filename.c_str()))
{
LOG(WII_IPC_FILEIO, "FS: DeleteFile %s", Filename.c_str());
}
else if (File::DeleteDir(Filename.c_str()))
{
LOG(WII_IPC_FILEIO, "FS: DeleteDir %s", Filename.c_str());
}
else
{
LOG(WII_IPC_FILEIO, "FS: DeleteFile %s - failed!!!", Filename.c_str());
}
return FS_RESULT_OK;
}
break;
case RENAME_FILE:
{
_dbg_assert_(WII_IPC_FILEIO, _BufferOutSize == 0);
int Offset = 0;
std::string Filename = HLE_IPC_BuildFilename((const char*)Memory::GetPointer(_BufferIn+Offset), 64);
Offset += 64;
std::string FilenameRename = HLE_IPC_BuildFilename((const char*)Memory::GetPointer(_BufferIn+Offset), 64);
Offset += 64;
// try to make the basis directory
File::CreateDirectoryStructure(FilenameRename);
// if there is already a filedelete it
if (File::Exists(FilenameRename.c_str()))
{
File::Delete(FilenameRename.c_str());
}
// finally try to rename the file
if (File::Rename(Filename.c_str(), FilenameRename.c_str()))
{
LOG(WII_IPC_FILEIO, "FS: Rename %s to %s", Filename.c_str(), FilenameRename.c_str());
}
else
{
LOG(WII_IPC_FILEIO, "FS: Rename %s to %s - failed", Filename.c_str(), FilenameRename.c_str());
PanicAlert("CWII_IPC_HLE_Device_fs: rename %s to %s failed", Filename.c_str(), FilenameRename.c_str());
}
return FS_RESULT_OK;
}
break;
case CREATE_FILE:
{
_dbg_assert_(WII_IPC_FILEIO, _BufferOutSize == 0);
u32 Addr = _BufferIn;
u32 OwnerID = Memory::Read_U32(Addr); Addr += 4;
u16 GroupID = Memory::Read_U16(Addr); Addr += 2;
std::string Filename(HLE_IPC_BuildFilename((const char*)Memory::GetPointer(Addr), 64)); Addr += 64;
u8 OwnerPerm = Memory::Read_U8(Addr); Addr++;
u8 GroupPerm = Memory::Read_U8(Addr); Addr++;
u8 OtherPerm = Memory::Read_U8(Addr); Addr++;
u8 Attributes = Memory::Read_U8(Addr); Addr++;
LOGV(WII_IPC_FILEIO, 0, "FS: CreateFile %s", Filename.c_str());
LOG(WII_IPC_FILEIO, " OwnerID: 0x%08x", OwnerID);
LOG(WII_IPC_FILEIO, " GroupID: 0x%04x", GroupID);
LOG(WII_IPC_FILEIO, " OwnerPerm: 0x%02x", OwnerPerm);
LOG(WII_IPC_FILEIO, " GroupPerm: 0x%02x", GroupPerm);
LOG(WII_IPC_FILEIO, " OtherPerm: 0x%02x", OtherPerm);
LOG(WII_IPC_FILEIO, " Attributes: 0x%02x", Attributes);
// check if the file already exist
if (File::Exists(Filename.c_str()))
{
LOG(WII_IPC_FILEIO, " result = FS_RESULT_EXISTS", Filename.c_str());
return FS_FILE_EXIST;
}
// create the file
File::CreateDirectoryStructure(Filename); // just to be sure
bool Result = File::CreateEmptyFile(Filename.c_str());
if (!Result)
{
PanicAlert("CWII_IPC_HLE_Device_fs: couldn't create new file");
return FS_RESULT_FATAL;
}
LOG(WII_IPC_FILEIO, " result = FS_RESULT_OK", Filename.c_str());
return FS_RESULT_OK;
}
break;
default:
PanicAlert("CWII_IPC_HLE_Device_fs::IOCtl: ni 0x%x", _Parameter);
break;
}
return FS_RESULT_FATAL;
}
// Copyright (C) 2003-2008 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, 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<FileSearch.GetFileNames().size(); i++)
{
if (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<FileSearch.GetFileNames().size(); i++)
{
overAllSize += File::GetSize(FileSearch.GetFileNames()[i].c_str());
}
fsBlock = (u32)(overAllSize / (16 * 1024)); // one bock is 16kb
iNodes = (u32)(FileSearch.GetFileNames().size());
ReturnValue = FS_RESULT_OK;
LOGV(WII_IPC_FILEIO, 1, " fsBlock: %i, iNodes: %i", fsBlock, iNodes);
}
else
{
fsBlock = 0;
iNodes = 0;
ReturnValue = FS_RESULT_OK;
// PanicAlert("IOCTL_GETUSAGE - unk dir %s", Filename.c_str());
LOGV(WII_IPC_FILEIO, 1, " error: not executed on a valid directoy: %s", Filename.c_str());
}
Memory::Write_U32(fsBlock, CommandBuffer.PayloadBuffer[0].m_Address);
Memory::Write_U32(iNodes, CommandBuffer.PayloadBuffer[1].m_Address);
}
break;
default:
PanicAlert("CWII_IPC_HLE_Device_fs::IOCtlV: %i", CommandBuffer.Parameter);
break;
}
Memory::Write_U32(ReturnValue, _CommandAddress+4);
return true;
}
// =======================================================
// IOCtl calls begin here
// -------------
bool CWII_IPC_HLE_Device_fs::IOCtl(u32 _CommandAddress)
{
//u32 DeviceID = Memory::Read_U32(_CommandAddress + 8);
//LOG(WII_IPC_FILEIO, "FS: IOCtl (Device=%s, DeviceID=%08x)", GetDeviceName().c_str(), DeviceID);
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);
/* Prepare the out buffer(s) with zeroes as a safety precaution
to avoid returning bad values. */
//LOG(WII_IPC_FILEIO, "Cleared %u bytes of the out buffer", _BufferOutSize);
Memory::Memset(BufferOut, 0, BufferOutSize);
u32 ReturnValue = ExecuteCommand(Parameter, BufferIn, BufferInSize, BufferOut, BufferOutSize);
Memory::Write_U32(ReturnValue, _CommandAddress + 4);
return true;
}
// =======================================================
// Execute IOCtl commands
// -------------
s32 CWII_IPC_HLE_Device_fs::ExecuteCommand(u32 _Parameter, u32 _BufferIn, u32 _BufferInSize, u32 _BufferOut, u32 _BufferOutSize)
{
switch(_Parameter)
{
case GET_STATS:
{
_dbg_assert_(WII_IPC_FILEIO, _BufferOutSize == 28);
LOG(WII_IPC_FILEIO, "FS: GET STATS - no idea what we have to return here, prolly the free memory etc:)");
LOG(WII_IPC_FILEIO, " InBufferSize: %i OutBufferSize: %i", _BufferInSize, _BufferOutSize);
PanicAlert("GET_STATS");
/* Memory::Write_U32(Addr, a); Addr += 4;
Memory::Write_U32(Addr, b); Addr += 4;
Memory::Write_U32(Addr, c); Addr += 4;
Memory::Write_U32(Addr, d); Addr += 4;
Memory::Write_U32(Addr, e); Addr += 4;
Memory::Write_U32(Addr, f); Addr += 4;
Memory::Write_U32(Addr, g); Addr += 4;
*/
return FS_RESULT_OK;
}
break;
case CREATE_DIR:
{
_dbg_assert_(WII_IPC_FILEIO, _BufferOutSize == 0);
u32 Addr = _BufferIn;
u32 OwnerID = Memory::Read_U32(Addr); Addr += 4;
u16 GroupID = Memory::Read_U16(Addr); Addr += 2;
std::string DirName(HLE_IPC_BuildFilename((const char*)Memory::GetPointer(Addr), 64)); Addr += 64;
Addr += 9; // owner attribs, permission
u8 Attribs = Memory::Read_U8(Addr);
LOG(WII_IPC_FILEIO, "FS: CREATE_DIR %s", DirName.c_str());
DirName += DIR_SEP;
File::CreateDirectoryStructure(DirName );
_dbg_assert_msg_(WII_IPC_FILEIO, File::IsDirectory(DirName.c_str()), "FS: CREATE_DIR %s failed", DirName.c_str());
return FS_RESULT_OK;
}
break;
case SET_ATTR:
{
u32 Addr = _BufferIn;
u32 OwnerID = Memory::Read_U32(Addr); Addr += 4;
u16 GroupID = Memory::Read_U16(Addr); Addr += 2;
std::string Filename = HLE_IPC_BuildFilename((const char*)Memory::GetPointer(_BufferIn), 64); Addr += 64;
u8 OwnerPerm = Memory::Read_U8(Addr); Addr += 1;
u8 GroupPerm = Memory::Read_U8(Addr); Addr += 1;
u8 OtherPerm = Memory::Read_U8(Addr); Addr += 1;
u8 Attributes = Memory::Read_U8(Addr); Addr += 1;
LOGV(WII_IPC_FILEIO, 0, "FS: SetAttrib %s", Filename.c_str());
LOG(WII_IPC_FILEIO, " OwnerID: 0x%08x", OwnerID);
LOG(WII_IPC_FILEIO, " GroupID: 0x%04x", GroupID);
LOG(WII_IPC_FILEIO, " OwnerPerm: 0x%02x", OwnerPerm);
LOG(WII_IPC_FILEIO, " GroupPerm: 0x%02x", GroupPerm);
LOG(WII_IPC_FILEIO, " OtherPerm: 0x%02x", OtherPerm);
LOG(WII_IPC_FILEIO, " Attributes: 0x%02x", Attributes);
return FS_RESULT_OK;
}
break;
case GET_ATTR:
{
_dbg_assert_msg_(WII_IPC_FILEIO, _BufferOutSize == 76,
" GET_ATTR needs an 76 bytes large output buffer but it is %i bytes large",
_BufferOutSize);
u32 OwnerID = 0;
u16 GroupID = 0;
std::string Filename = HLE_IPC_BuildFilename((const char*)Memory::GetPointer(_BufferIn), 64);
u8 OwnerPerm = 0x3; // read/write
u8 GroupPerm = 0x3; // read/write
u8 OtherPerm = 0x3; // read/write
u8 Attributes = 0x00; // no attributes
if (File::IsDirectory(Filename.c_str()))
{
LOG(WII_IPC_FILEIO, "FS: GET_ATTR Directory %s - all permission flags are set", Filename.c_str());
}
else
{
if (File::Exists(Filename.c_str()))
{
LOG(WII_IPC_FILEIO, "FS: GET_ATTR %s - all permission flags are set", Filename.c_str());
}
else
{
LOG(WII_IPC_FILEIO, "FS: GET_ATTR unknown %s", Filename.c_str());
return FS_FILE_NOT_EXIST;
}
}
// write answer to buffer
if (_BufferOutSize == 76)
{
u32 Addr = _BufferOut;
Memory::Write_U32(OwnerID, Addr); Addr += 4;
Memory::Write_U16(GroupID, Addr); Addr += 2;
memcpy(Memory::GetPointer(Addr), Filename.c_str(), Filename.size()); Addr += 64;
Memory::Write_U8(OwnerPerm, Addr); Addr += 1;
Memory::Write_U8(GroupPerm, Addr); Addr += 1;
Memory::Write_U8(OtherPerm, Addr); Addr += 1;
Memory::Write_U8(Attributes, Addr); Addr += 1;
}
return FS_RESULT_OK;
}
break;
case DELETE_FILE:
{
_dbg_assert_(WII_IPC_FILEIO, _BufferOutSize == 0);
int Offset = 0;
std::string Filename = HLE_IPC_BuildFilename((const char*)Memory::GetPointer(_BufferIn+Offset), 64);
Offset += 64;
if (File::Delete(Filename.c_str()))
{
LOG(WII_IPC_FILEIO, "FS: DeleteFile %s", Filename.c_str());
}
else if (File::DeleteDir(Filename.c_str()))
{
LOG(WII_IPC_FILEIO, "FS: DeleteDir %s", Filename.c_str());
}
else
{
LOG(WII_IPC_FILEIO, "FS: DeleteFile %s - failed!!!", Filename.c_str());
}
return FS_RESULT_OK;
}
break;
case RENAME_FILE:
{
_dbg_assert_(WII_IPC_FILEIO, _BufferOutSize == 0);
int Offset = 0;
std::string Filename = HLE_IPC_BuildFilename((const char*)Memory::GetPointer(_BufferIn+Offset), 64);
Offset += 64;
std::string FilenameRename = HLE_IPC_BuildFilename((const char*)Memory::GetPointer(_BufferIn+Offset), 64);
Offset += 64;
// try to make the basis directory
File::CreateDirectoryStructure(FilenameRename);
// if there is already a filedelete it
if (File::Exists(FilenameRename.c_str()))
{
File::Delete(FilenameRename.c_str());
}
// finally try to rename the file
if (File::Rename(Filename.c_str(), FilenameRename.c_str()))
{
LOG(WII_IPC_FILEIO, "FS: Rename %s to %s", Filename.c_str(), FilenameRename.c_str());
}
else
{
LOG(WII_IPC_FILEIO, "FS: Rename %s to %s - failed", Filename.c_str(), FilenameRename.c_str());
PanicAlert("CWII_IPC_HLE_Device_fs: rename %s to %s failed", Filename.c_str(), FilenameRename.c_str());
}
return FS_RESULT_OK;
}
break;
case CREATE_FILE:
{
_dbg_assert_(WII_IPC_FILEIO, _BufferOutSize == 0);
u32 Addr = _BufferIn;
u32 OwnerID = Memory::Read_U32(Addr); Addr += 4;
u16 GroupID = Memory::Read_U16(Addr); Addr += 2;
std::string Filename(HLE_IPC_BuildFilename((const char*)Memory::GetPointer(Addr), 64)); Addr += 64;
u8 OwnerPerm = Memory::Read_U8(Addr); Addr++;
u8 GroupPerm = Memory::Read_U8(Addr); Addr++;
u8 OtherPerm = Memory::Read_U8(Addr); Addr++;
u8 Attributes = Memory::Read_U8(Addr); Addr++;
LOGV(WII_IPC_FILEIO, 0, "FS: CreateFile %s", Filename.c_str());
LOG(WII_IPC_FILEIO, " OwnerID: 0x%08x", OwnerID);
LOG(WII_IPC_FILEIO, " GroupID: 0x%04x", GroupID);
LOG(WII_IPC_FILEIO, " OwnerPerm: 0x%02x", OwnerPerm);
LOG(WII_IPC_FILEIO, " GroupPerm: 0x%02x", GroupPerm);
LOG(WII_IPC_FILEIO, " OtherPerm: 0x%02x", OtherPerm);
LOG(WII_IPC_FILEIO, " Attributes: 0x%02x", Attributes);
// check if the file already exist
if (File::Exists(Filename.c_str()))
{
LOG(WII_IPC_FILEIO, " result = FS_RESULT_EXISTS", Filename.c_str());
return FS_FILE_EXIST;
}
// create the file
File::CreateDirectoryStructure(Filename); // just to be sure
bool Result = File::CreateEmptyFile(Filename.c_str());
if (!Result)
{
PanicAlert("CWII_IPC_HLE_Device_fs: couldn't create new file");
return FS_RESULT_FATAL;
}
LOG(WII_IPC_FILEIO, " result = FS_RESULT_OK", Filename.c_str());
return FS_RESULT_OK;
}
break;
default:
PanicAlert("CWII_IPC_HLE_Device_fs::IOCtl: ni 0x%x", _Parameter);
break;
}
return FS_RESULT_FATAL;
}

View File

@ -1,171 +1,171 @@
// Copyright (C) 2003-2008 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If 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
// -------------
/* Here we handle /dev/net and /dev/net/ncd/manage requests.
// -----------------------
The /dev/net/kd/request requests are part of what is called WiiConnect24,
it's used by for example SSBB, Mario Kart, Metroid Prime 3
0x01 SuspendScheduler (Input: none, Output: 32 bytes)
0x02 ExecTrySuspendScheduler (Input: 32 bytes, Output: 32 bytes) // Sounds like it will
check if it should suspend the updates scheduler or not. If I returned
(OutBuffer: 0, Ret: -1) to Metroid Prime 3 it got stuck in an endless loops of
requests, probably harmless but I changed it to (OutBuffer: 1, Ret: 0) to stop
the calls. However then it also calls 0x3 and then changes its error message
to a Wii Memory error message from just a general Error message.
0x03 ? (Input: none, Output: 32 bytes) // This is only called if 0x02
does not return -1
0x0f NWC24iRequestGenerateUserId (Input: none, Output: 32 bytes)
Requests are made in this order by these games
Mario Kart: 2, 1, f, 3
SSBB: 2, 3
For Mario Kart I had to return -1 from at least 2, f and 3 to convince it that the network
was unavaliable and prevent if from looking for shared2/wc24 files (and do a PPCHalt when
it failed)
// -------
*/
// =============
#include "WII_IPC_HLE_Device_net.h"
// **********************************************************************************
// Handle /dev/net/kd/request requests
CWII_IPC_HLE_Device_net_kd_request::CWII_IPC_HLE_Device_net_kd_request(u32 _DeviceID, const std::string& _rDeviceName)
: IWII_IPC_HLE_Device(_DeviceID, _rDeviceName)
{
}
CWII_IPC_HLE_Device_net_kd_request::~CWII_IPC_HLE_Device_net_kd_request()
{
}
bool CWII_IPC_HLE_Device_net_kd_request::Open(u32 _CommandAddress, u32 _Mode)
{
LOG(WII_IPC_NET, "NET_KD_REQ: Open");
Memory::Write_U32(GetDeviceID(), _CommandAddress + 4);
return true;
}
bool CWII_IPC_HLE_Device_net_kd_request::Close(u32 _CommandAddress)
{
LOG(WII_IPC_NET, "NET_KD_REQ: Close");
Memory::Write_U32(0, _CommandAddress + 4);
return true;
}
bool CWII_IPC_HLE_Device_net_kd_request::IOCtl(u32 _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);
u32 ReturnValue = ExecuteCommand(Parameter, BufferIn, BufferInSize, BufferOut, BufferOutSize);
LOG(WII_IPC_NET, "NET_KD_REQ: IOCtl (Device=%s) (Parameter: 0x%02x)", GetDeviceName().c_str(), Parameter);
Memory::Write_U32(ReturnValue, _CommandAddress + 4);
return true;
}
s32 CWII_IPC_HLE_Device_net_kd_request::ExecuteCommand(u32 _Parameter, u32 _BufferIn, u32 _BufferInSize, u32 _BufferOut, u32 _BufferOutSize)
{
// Clean the location of the output buffer to zeroes as a safety precaution */
Memory::Memset(_BufferOut, 0, _BufferOutSize);
switch(_Parameter)
{
case 1: // SuspendScheduler (Input: none, Output: 32 bytes)
//Memory::Write_U32(0, _BufferOut);
return -1;
break;
case 2: // ExecTrySuspendScheduler (Input: 32 bytes, Output: 32 bytes).
DumpCommands(_BufferIn, _BufferInSize / 4, LogTypes::WII_IPC_NET);
Memory::Write_U32(1, _BufferOut);
return 0;
break;
case 3: // ? (Input: none, Output: 32 bytes)
//Memory::Write_U32(0, _BufferOut);
return -1;
break;
case 0xf: // NWC24iRequestGenerateUserId (Input: none, Output: 32 bytes)
//Memory::Write_U32(0, _BufferOut);
return -1;
break;
default:
_dbg_assert_msg_(WII_IPC_NET, 0, "/dev/net/kd/request::IOCtl request 0x%x (BufferIn: (%08x, %i), BufferOut: (%08x, %i)",
_Parameter, _BufferIn, _BufferInSize, _BufferOut, _BufferOutSize);
break;
}
// We return a success for any potential unknown requests
return 0;
}
// **********************************************************************************
// Handle /dev/net/ncd/manage requests
CWII_IPC_HLE_Device_net_ncd_manage::CWII_IPC_HLE_Device_net_ncd_manage(u32 _DeviceID, const std::string& _rDeviceName)
: IWII_IPC_HLE_Device(_DeviceID, _rDeviceName)
{}
CWII_IPC_HLE_Device_net_ncd_manage::~CWII_IPC_HLE_Device_net_ncd_manage()
{}
bool CWII_IPC_HLE_Device_net_ncd_manage::Open(u32 _CommandAddress, u32 _Mode)
{
Memory::Write_U32(GetDeviceID(), _CommandAddress+4);
return true;
}
bool CWII_IPC_HLE_Device_net_ncd_manage::IOCtlV(u32 _CommandAddress)
{
u32 ReturnValue = 0;
SIOCtlVBuffer CommandBuffer(_CommandAddress);
switch(CommandBuffer.Parameter)
{
default:
LOG(WII_IPC_NET, "NET_NCD_MANAGE IOCtlV: %i", CommandBuffer.Parameter);
_dbg_assert_msg_(WII_IPC_NET, 0, "NET_NCD_MANAGE IOCtlV: %i", CommandBuffer.Parameter);
break;
}
Memory::Write_U32(ReturnValue, _CommandAddress+4);
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/
// =======================================================
// File description
// -------------
/* Here we handle /dev/net and /dev/net/ncd/manage requests.
// -----------------------
The /dev/net/kd/request requests are part of what is called WiiConnect24,
it's used by for example SSBB, Mario Kart, Metroid Prime 3
0x01 SuspendScheduler (Input: none, Output: 32 bytes)
0x02 ExecTrySuspendScheduler (Input: 32 bytes, Output: 32 bytes) // Sounds like it will
check if it should suspend the updates scheduler or not. If I returned
(OutBuffer: 0, Ret: -1) to Metroid Prime 3 it got stuck in an endless loops of
requests, probably harmless but I changed it to (OutBuffer: 1, Ret: 0) to stop
the calls. However then it also calls 0x3 and then changes its error message
to a Wii Memory error message from just a general Error message.
0x03 ? (Input: none, Output: 32 bytes) // This is only called if 0x02
does not return -1
0x0f NWC24iRequestGenerateUserId (Input: none, Output: 32 bytes)
Requests are made in this order by these games
Mario Kart: 2, 1, f, 3
SSBB: 2, 3
For Mario Kart I had to return -1 from at least 2, f and 3 to convince it that the network
was unavaliable and prevent if from looking for shared2/wc24 files (and do a PPCHalt when
it failed)
// -------
*/
// =============
#include "WII_IPC_HLE_Device_net.h"
// **********************************************************************************
// Handle /dev/net/kd/request requests
CWII_IPC_HLE_Device_net_kd_request::CWII_IPC_HLE_Device_net_kd_request(u32 _DeviceID, const std::string& _rDeviceName)
: IWII_IPC_HLE_Device(_DeviceID, _rDeviceName)
{
}
CWII_IPC_HLE_Device_net_kd_request::~CWII_IPC_HLE_Device_net_kd_request()
{
}
bool CWII_IPC_HLE_Device_net_kd_request::Open(u32 _CommandAddress, u32 _Mode)
{
LOG(WII_IPC_NET, "NET_KD_REQ: Open");
Memory::Write_U32(GetDeviceID(), _CommandAddress + 4);
return true;
}
bool CWII_IPC_HLE_Device_net_kd_request::Close(u32 _CommandAddress)
{
LOG(WII_IPC_NET, "NET_KD_REQ: Close");
Memory::Write_U32(0, _CommandAddress + 4);
return true;
}
bool CWII_IPC_HLE_Device_net_kd_request::IOCtl(u32 _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);
u32 ReturnValue = ExecuteCommand(Parameter, BufferIn, BufferInSize, BufferOut, BufferOutSize);
LOG(WII_IPC_NET, "NET_KD_REQ: IOCtl (Device=%s) (Parameter: 0x%02x)", GetDeviceName().c_str(), Parameter);
Memory::Write_U32(ReturnValue, _CommandAddress + 4);
return true;
}
s32 CWII_IPC_HLE_Device_net_kd_request::ExecuteCommand(u32 _Parameter, u32 _BufferIn, u32 _BufferInSize, u32 _BufferOut, u32 _BufferOutSize)
{
// Clean the location of the output buffer to zeroes as a safety precaution */
Memory::Memset(_BufferOut, 0, _BufferOutSize);
switch(_Parameter)
{
case 1: // SuspendScheduler (Input: none, Output: 32 bytes)
//Memory::Write_U32(0, _BufferOut);
return -1;
break;
case 2: // ExecTrySuspendScheduler (Input: 32 bytes, Output: 32 bytes).
DumpCommands(_BufferIn, _BufferInSize / 4, LogTypes::WII_IPC_NET);
Memory::Write_U32(1, _BufferOut);
return 0;
break;
case 3: // ? (Input: none, Output: 32 bytes)
//Memory::Write_U32(0, _BufferOut);
return -1;
break;
case 0xf: // NWC24iRequestGenerateUserId (Input: none, Output: 32 bytes)
//Memory::Write_U32(0, _BufferOut);
return -1;
break;
default:
_dbg_assert_msg_(WII_IPC_NET, 0, "/dev/net/kd/request::IOCtl request 0x%x (BufferIn: (%08x, %i), BufferOut: (%08x, %i)",
_Parameter, _BufferIn, _BufferInSize, _BufferOut, _BufferOutSize);
break;
}
// We return a success for any potential unknown requests
return 0;
}
// **********************************************************************************
// Handle /dev/net/ncd/manage requests
CWII_IPC_HLE_Device_net_ncd_manage::CWII_IPC_HLE_Device_net_ncd_manage(u32 _DeviceID, const std::string& _rDeviceName)
: IWII_IPC_HLE_Device(_DeviceID, _rDeviceName)
{}
CWII_IPC_HLE_Device_net_ncd_manage::~CWII_IPC_HLE_Device_net_ncd_manage()
{}
bool CWII_IPC_HLE_Device_net_ncd_manage::Open(u32 _CommandAddress, u32 _Mode)
{
Memory::Write_U32(GetDeviceID(), _CommandAddress+4);
return true;
}
bool CWII_IPC_HLE_Device_net_ncd_manage::IOCtlV(u32 _CommandAddress)
{
u32 ReturnValue = 0;
SIOCtlVBuffer CommandBuffer(_CommandAddress);
switch(CommandBuffer.Parameter)
{
default:
LOG(WII_IPC_NET, "NET_NCD_MANAGE IOCtlV: %i", CommandBuffer.Parameter);
_dbg_assert_msg_(WII_IPC_NET, 0, "NET_NCD_MANAGE IOCtlV: %i", CommandBuffer.Parameter);
break;
}
Memory::Write_U32(ReturnValue, _CommandAddress+4);
return true;
}

View File

@ -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 "Common.h"
#include "WII_IPC_HLE_Device_sdio_slot0.h"
#include "../HW/CPU.h"
#include "../HW/Memmap.h"
#include "../Core.h"
// __________________________________________________________________________________________________
//
CWII_IPC_HLE_Device_sdio_slot0::CWII_IPC_HLE_Device_sdio_slot0(u32 _DeviceID, const std::string& _rDeviceName )
: IWII_IPC_HLE_Device(_DeviceID, _rDeviceName)
{
}
// __________________________________________________________________________________________________
//
CWII_IPC_HLE_Device_sdio_slot0::~CWII_IPC_HLE_Device_sdio_slot0()
{
}
// __________________________________________________________________________________________________
//
bool
CWII_IPC_HLE_Device_sdio_slot0::Open(u32 _CommandAddress, u32 _Mode)
{
LOG(WII_IPC_SD, "SD: Open");
Memory::Write_U32(GetDeviceID(), _CommandAddress + 0x4);
return true;
}
// __________________________________________________________________________________________________
// The front SD slot
bool CWII_IPC_HLE_Device_sdio_slot0::IOCtl(u32 _CommandAddress)
{
//LOG(WII_IPC_FILEIO, "*************************************");
//LOG(WII_IPC_FILEIO, "CWII_IPC_HLE_Device_sdio_slot0::IOCtl");
//LOG(WII_IPC_FILEIO, "*************************************");
// DumpCommands(_CommandAddress);
u32 Cmd = Memory::Read_U32(_CommandAddress + 0xC);
// TODO: Use Cmd for something?
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);
//LOG(WII_IPC_SD, "%s Cmd 0x%x - BufferIn(0x%08x, 0x%x) BufferOut(0x%08x, 0x%x)",
// GetDeviceName().c_str(), Cmd, BufferIn, BufferInSize, BufferOut, BufferOutSize);
/* As a safety precaution we fill the out buffer with zeroes to avoid
returning nonsense values */
Memory::Memset(BufferOut, 0, BufferOutSize);
u32 ReturnValue = 0;
switch (Cmd) {
case 1: // set_hc_reg
LOGV(WII_IPC_SD, 0, "SD: set_hc_reg");
break;
case 2: // get_hc_reg
LOGV(WII_IPC_SD, 0, "SD: get_hc_reg");
break;
case 4: // reset, do nothing ?
LOGV(WII_IPC_SD, 0, "SD: reset");
break;
case 6: // sd_clock
LOGV(WII_IPC_SD, 0, "SD: sd_clock");
break;
case 7: // Send cmd (Input: 24 bytes, Output: 10 bytes)
LOGV(WII_IPC_SD, 0, "SD: sendcmd");
ReturnValue = ExecuteCommand(BufferIn, BufferInSize, BufferOut, BufferOutSize);
break;
case 11: // sd_get_status
LOGV(WII_IPC_SD, 0, "SD: sd_get_status. Answer: SD card is not inserted", BufferOut);
Memory::Write_U32(2, BufferOut); // SD card is not inserted
break;
default:
PanicAlert("Unknown SD command (0x%08x)", Cmd);
break;
}
//DumpCommands(_CommandAddress);
//LOG(WII_IPC_SD, "InBuffer");
//DumpCommands(BufferIn, BufferInSize / 4, LogTypes::WII_IPC_SD);
//LOG(WII_IPC_SD, "OutBuffer");
//DumpCommands(BufferOut, BufferOutSize);
Memory::Write_U32(ReturnValue, _CommandAddress + 0x4);
return true;
}
// __________________________________________________________________________________________________
//
bool CWII_IPC_HLE_Device_sdio_slot0::IOCtlV(u32 _CommandAddress)
{
PanicAlert("CWII_IPC_HLE_Device_sdio_slot0::IOCtlV() unknown");
// SD_Read uses this
DumpCommands(_CommandAddress);
return true;
}
// __________________________________________________________________________________________________
//
u32 CWII_IPC_HLE_Device_sdio_slot0::ExecuteCommand(u32 _BufferIn, u32 _BufferInSize, u32 _BufferOut, u32 _BufferOutSize)
{
/* The game will send us a SendCMD with this information. To be able to read and write
to a file we need to prepare a 10 byte output buffer as response. */
struct Request {
u32 command;
u32 type;
u32 resp;
u32 arg;
u32 blocks;
u32 bsize;
u32 addr;
} req;
req.command = Memory::Read_U32(_BufferIn + 0);
req.type = Memory::Read_U32(_BufferIn + 4);
req.resp = Memory::Read_U32(_BufferIn + 8);
req.arg = Memory::Read_U32(_BufferIn + 12);
req.blocks = Memory::Read_U32(_BufferIn + 16);
req.bsize = Memory::Read_U32(_BufferIn + 20);
req.addr = Memory::Read_U32(_BufferIn + 24);
//switch (req.command)
{
}
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/
#include "Common.h"
#include "WII_IPC_HLE_Device_sdio_slot0.h"
#include "../HW/CPU.h"
#include "../HW/Memmap.h"
#include "../Core.h"
// __________________________________________________________________________________________________
//
CWII_IPC_HLE_Device_sdio_slot0::CWII_IPC_HLE_Device_sdio_slot0(u32 _DeviceID, const std::string& _rDeviceName )
: IWII_IPC_HLE_Device(_DeviceID, _rDeviceName)
{
}
// __________________________________________________________________________________________________
//
CWII_IPC_HLE_Device_sdio_slot0::~CWII_IPC_HLE_Device_sdio_slot0()
{
}
// __________________________________________________________________________________________________
//
bool
CWII_IPC_HLE_Device_sdio_slot0::Open(u32 _CommandAddress, u32 _Mode)
{
LOG(WII_IPC_SD, "SD: Open");
Memory::Write_U32(GetDeviceID(), _CommandAddress + 0x4);
return true;
}
// __________________________________________________________________________________________________
// The front SD slot
bool CWII_IPC_HLE_Device_sdio_slot0::IOCtl(u32 _CommandAddress)
{
//LOG(WII_IPC_FILEIO, "*************************************");
//LOG(WII_IPC_FILEIO, "CWII_IPC_HLE_Device_sdio_slot0::IOCtl");
//LOG(WII_IPC_FILEIO, "*************************************");
// DumpCommands(_CommandAddress);
u32 Cmd = Memory::Read_U32(_CommandAddress + 0xC);
// TODO: Use Cmd for something?
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);
//LOG(WII_IPC_SD, "%s Cmd 0x%x - BufferIn(0x%08x, 0x%x) BufferOut(0x%08x, 0x%x)",
// GetDeviceName().c_str(), Cmd, BufferIn, BufferInSize, BufferOut, BufferOutSize);
/* As a safety precaution we fill the out buffer with zeroes to avoid
returning nonsense values */
Memory::Memset(BufferOut, 0, BufferOutSize);
u32 ReturnValue = 0;
switch (Cmd) {
case 1: // set_hc_reg
LOGV(WII_IPC_SD, 0, "SD: set_hc_reg");
break;
case 2: // get_hc_reg
LOGV(WII_IPC_SD, 0, "SD: get_hc_reg");
break;
case 4: // reset, do nothing ?
LOGV(WII_IPC_SD, 0, "SD: reset");
break;
case 6: // sd_clock
LOGV(WII_IPC_SD, 0, "SD: sd_clock");
break;
case 7: // Send cmd (Input: 24 bytes, Output: 10 bytes)
LOGV(WII_IPC_SD, 0, "SD: sendcmd");
ReturnValue = ExecuteCommand(BufferIn, BufferInSize, BufferOut, BufferOutSize);
break;
case 11: // sd_get_status
LOGV(WII_IPC_SD, 0, "SD: sd_get_status. Answer: SD card is not inserted", BufferOut);
Memory::Write_U32(2, BufferOut); // SD card is not inserted
break;
default:
PanicAlert("Unknown SD command (0x%08x)", Cmd);
break;
}
//DumpCommands(_CommandAddress);
//LOG(WII_IPC_SD, "InBuffer");
//DumpCommands(BufferIn, BufferInSize / 4, LogTypes::WII_IPC_SD);
//LOG(WII_IPC_SD, "OutBuffer");
//DumpCommands(BufferOut, BufferOutSize);
Memory::Write_U32(ReturnValue, _CommandAddress + 0x4);
return true;
}
// __________________________________________________________________________________________________
//
bool CWII_IPC_HLE_Device_sdio_slot0::IOCtlV(u32 _CommandAddress)
{
PanicAlert("CWII_IPC_HLE_Device_sdio_slot0::IOCtlV() unknown");
// SD_Read uses this
DumpCommands(_CommandAddress);
return true;
}
// __________________________________________________________________________________________________
//
u32 CWII_IPC_HLE_Device_sdio_slot0::ExecuteCommand(u32 _BufferIn, u32 _BufferInSize, u32 _BufferOut, u32 _BufferOutSize)
{
/* The game will send us a SendCMD with this information. To be able to read and write
to a file we need to prepare a 10 byte output buffer as response. */
struct Request {
u32 command;
u32 type;
u32 resp;
u32 arg;
u32 blocks;
u32 bsize;
u32 addr;
} req;
req.command = Memory::Read_U32(_BufferIn + 0);
req.type = Memory::Read_U32(_BufferIn + 4);
req.resp = Memory::Read_U32(_BufferIn + 8);
req.arg = Memory::Read_U32(_BufferIn + 12);
req.blocks = Memory::Read_U32(_BufferIn + 16);
req.bsize = Memory::Read_U32(_BufferIn + 20);
req.addr = Memory::Read_U32(_BufferIn + 24);
//switch (req.command)
{
}
return 0;
}

File diff suppressed because it is too large Load Diff

File diff suppressed because it is too large Load Diff

View File

@ -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 <vector>
#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 <vector>
#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;
}

View File

@ -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 <stdio.h>
#include "Common.h"
#if defined(HAVE_WX) && HAVE_WX
#include <wx/datetime.h> // 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; i<LogTypes::NUMBER_OF_LOGS; i++)
{
if (m_Log[i] != NULL)
{
m_Log[i]->Shutdown();
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 <stdio.h>
#include "Common.h"
#if defined(HAVE_WX) && HAVE_WX
#include <wx/datetime.h> // 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; i<LogTypes::NUMBER_OF_LOGS; i++)
{
if (m_Log[i] != NULL)
{
m_Log[i]->Shutdown();
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
}

View File

@ -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 <windows.h>
#include <vector>
#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 <windows.h>
#include <vector>
#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

View File

@ -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] <Knuckles-> 0x802904b4 in US released
// [Tue Aug 21 2007] [18:30:53] <Knuckles-> 0x80294d54 in EUR Demo version
// [Tue Aug 21 2007] [18:31:10] <Knuckles-> we just patch a blr on it (0x4E800020)
// [OnLoad]
// 0x80020394=dword,0x4e800020
#include <string>
#include <vector>
#include <map>
#include "StringUtil.h"
#include "PatchEngine.h"
#include "HW/Memmap.h"
#include "ActionReplay.h"
using namespace Common;
namespace PatchEngine
{
std::vector<Patch> onFrame;
std::map<u32, int> speedHacks;
void LoadPatchSection(const char *section, std::vector<Patch> &patches, IniFile &ini)
{
std::vector<std::string> lines;
if (!ini.GetLines(section, lines))
return;
Patch currentPatch;
for (std::vector<std::string>::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<std::string> 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<u32, int> &hacks, IniFile &ini) {
std::vector<std::string> keys;
ini.GetKeys(section, keys);
for (std::vector<std::string>::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<u32, int>::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<Patch> &patches)
{
for (std::vector<Patch>::const_iterator iter = patches.begin(); iter != patches.end(); ++iter)
{
if (iter->active)
{
for (std::vector<PatchEntry>::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] <Knuckles-> 0x802904b4 in US released
// [Tue Aug 21 2007] [18:30:53] <Knuckles-> 0x80294d54 in EUR Demo version
// [Tue Aug 21 2007] [18:31:10] <Knuckles-> we just patch a blr on it (0x4E800020)
// [OnLoad]
// 0x80020394=dword,0x4e800020
#include <string>
#include <vector>
#include <map>
#include "StringUtil.h"
#include "PatchEngine.h"
#include "HW/Memmap.h"
#include "ActionReplay.h"
using namespace Common;
namespace PatchEngine
{
std::vector<Patch> onFrame;
std::map<u32, int> speedHacks;
void LoadPatchSection(const char *section, std::vector<Patch> &patches, IniFile &ini)
{
std::vector<std::string> lines;
if (!ini.GetLines(section, lines))
return;
Patch currentPatch;
for (std::vector<std::string>::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<std::string> 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<u32, int> &hacks, IniFile &ini) {
std::vector<std::string> keys;
ini.GetKeys(section, keys);
for (std::vector<std::string>::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<u32, int>::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<Patch> &patches)
{
for (std::vector<Patch>::const_iterator iter = patches.begin(); iter != patches.end(); ++iter)
{
if (iter->active)
{
for (std::vector<PatchEntry>::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

View File

@ -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<TGetDllInfo> (plugin.Get("GetDllInfo"));
DllConfig = reinterpret_cast<TDllConfig> (plugin.Get("DllConfig"));
DllDebugger = reinterpret_cast<TDllDebugger> (plugin.Get("DllDebugger"));
DSP_Initialize = reinterpret_cast<TDSP_Initialize> (plugin.Get("DSP_Initialize"));
DSP_Shutdown = reinterpret_cast<TDSP_Shutdown> (plugin.Get("DSP_Shutdown"));
DSP_ReadMailboxHigh = reinterpret_cast<TDSP_ReadMailBox> (plugin.Get("DSP_ReadMailboxHigh"));
DSP_ReadMailboxLow = reinterpret_cast<TDSP_ReadMailBox> (plugin.Get("DSP_ReadMailboxLow"));
DSP_WriteMailboxHigh = reinterpret_cast<TDSP_WriteMailBox> (plugin.Get("DSP_WriteMailboxHigh"));
DSP_WriteMailboxLow = reinterpret_cast<TDSP_WriteMailBox> (plugin.Get("DSP_WriteMailboxLow"));
DSP_ReadControlRegister = reinterpret_cast<TDSP_ReadControlRegister> (plugin.Get("DSP_ReadControlRegister"));
DSP_WriteControlRegister = reinterpret_cast<TDSP_WriteControlRegister> (plugin.Get("DSP_WriteControlRegister"));
DSP_Update = reinterpret_cast<TDSP_Update> (plugin.Get("DSP_Update"));
DSP_SendAIBuffer = reinterpret_cast<TDSP_SendAIBuffer> (plugin.Get("DSP_SendAIBuffer"));
DSP_DoState = reinterpret_cast<TDSP_DoState> (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<TGetDllInfo> (plugin.Get("GetDllInfo"));
DllConfig = reinterpret_cast<TDllConfig> (plugin.Get("DllConfig"));
DllDebugger = reinterpret_cast<TDllDebugger> (plugin.Get("DllDebugger"));
DSP_Initialize = reinterpret_cast<TDSP_Initialize> (plugin.Get("DSP_Initialize"));
DSP_Shutdown = reinterpret_cast<TDSP_Shutdown> (plugin.Get("DSP_Shutdown"));
DSP_ReadMailboxHigh = reinterpret_cast<TDSP_ReadMailBox> (plugin.Get("DSP_ReadMailboxHigh"));
DSP_ReadMailboxLow = reinterpret_cast<TDSP_ReadMailBox> (plugin.Get("DSP_ReadMailboxLow"));
DSP_WriteMailboxHigh = reinterpret_cast<TDSP_WriteMailBox> (plugin.Get("DSP_WriteMailboxHigh"));
DSP_WriteMailboxLow = reinterpret_cast<TDSP_WriteMailBox> (plugin.Get("DSP_WriteMailboxLow"));
DSP_ReadControlRegister = reinterpret_cast<TDSP_ReadControlRegister> (plugin.Get("DSP_ReadControlRegister"));
DSP_WriteControlRegister = reinterpret_cast<TDSP_WriteControlRegister> (plugin.Get("DSP_WriteControlRegister"));
DSP_Update = reinterpret_cast<TDSP_Update> (plugin.Get("DSP_Update"));
DSP_SendAIBuffer = reinterpret_cast<TDSP_SendAIBuffer> (plugin.Get("DSP_SendAIBuffer"));
DSP_DoState = reinterpret_cast<TDSP_DoState> (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

View File

@ -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<TGetDllInfo> (GetProcAddress(g_hLibraryInstance, "GetDllInfo"));
g_DllConfig = reinterpret_cast<TDllConfig> (GetProcAddress(g_hLibraryInstance, "DllConfig"));
g_DVD_Initialize = reinterpret_cast<TDVD_Initialize> (GetProcAddress(g_hLibraryInstance, "DVD_Initialize"));
g_DVD_Shutdown = reinterpret_cast<TDVD_Shutdown> (GetProcAddress(g_hLibraryInstance, "DVD_Shutdown"));
g_DVD_SetISOFile = reinterpret_cast<TDVD_SetISOFile> (GetProcAddress(g_hLibraryInstance, "DVD_SetISOFile"));
g_DVD_GetISOName = reinterpret_cast<TDVD_GetISOName> (GetProcAddress(g_hLibraryInstance, "DVD_GetISOName"));
g_DVD_ReadToPtr = reinterpret_cast<TDVD_ReadToPtr> (GetProcAddress(g_hLibraryInstance, "DVD_ReadToPtr"));
g_DVD_Read32 = reinterpret_cast<TDVD_Read32> (GetProcAddress(g_hLibraryInstance, "DVD_Read32"));
g_DVD_IsValid = reinterpret_cast<TDVD_IsValid> (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<TGetDllInfo> (GetProcAddress(g_hLibraryInstance, "GetDllInfo"));
g_DllConfig = reinterpret_cast<TDllConfig> (GetProcAddress(g_hLibraryInstance, "DllConfig"));
g_DVD_Initialize = reinterpret_cast<TDVD_Initialize> (GetProcAddress(g_hLibraryInstance, "DVD_Initialize"));
g_DVD_Shutdown = reinterpret_cast<TDVD_Shutdown> (GetProcAddress(g_hLibraryInstance, "DVD_Shutdown"));
g_DVD_SetISOFile = reinterpret_cast<TDVD_SetISOFile> (GetProcAddress(g_hLibraryInstance, "DVD_SetISOFile"));
g_DVD_GetISOName = reinterpret_cast<TDVD_GetISOName> (GetProcAddress(g_hLibraryInstance, "DVD_GetISOName"));
g_DVD_ReadToPtr = reinterpret_cast<TDVD_ReadToPtr> (GetProcAddress(g_hLibraryInstance, "DVD_ReadToPtr"));
g_DVD_Read32 = reinterpret_cast<TDVD_Read32> (GetProcAddress(g_hLibraryInstance, "DVD_Read32"));
g_DVD_IsValid = reinterpret_cast<TDVD_IsValid> (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

View File

@ -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<TGetDllInfo> (plugin.Get("GetDllInfo"));
DllConfig = reinterpret_cast<TDllConfig> (plugin.Get("DllConfig"));
PAD_Initialize = reinterpret_cast<TPAD_Initialize> (plugin.Get("PAD_Initialize"));
PAD_Shutdown = reinterpret_cast<TPAD_Shutdown> (plugin.Get("PAD_Shutdown"));
PAD_GetStatus = reinterpret_cast<TPAD_GetStatus> (plugin.Get("PAD_GetStatus"));
PAD_Rumble = reinterpret_cast<TPAD_Rumble> (plugin.Get("PAD_Rumble"));
PAD_GetAttachedPads = reinterpret_cast<TPAD_GetAttachedPads>(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<TGetDllInfo> (plugin.Get("GetDllInfo"));
DllConfig = reinterpret_cast<TDllConfig> (plugin.Get("DllConfig"));
PAD_Initialize = reinterpret_cast<TPAD_Initialize> (plugin.Get("PAD_Initialize"));
PAD_Shutdown = reinterpret_cast<TPAD_Shutdown> (plugin.Get("PAD_Shutdown"));
PAD_GetStatus = reinterpret_cast<TPAD_GetStatus> (plugin.Get("PAD_GetStatus"));
PAD_Rumble = reinterpret_cast<TPAD_Rumble> (plugin.Get("PAD_Rumble"));
PAD_GetAttachedPads = reinterpret_cast<TPAD_GetAttachedPads>(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

View File

@ -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<TGetDllInfo> (plugin.Get("GetDllInfo"));
DllConfig = reinterpret_cast<TDllConfig> (plugin.Get("DllConfig"));
DllDebugger = reinterpret_cast<TDllDebugger> (plugin.Get("DllDebugger"));
Video_Initialize = reinterpret_cast<TVideo_Initialize> (plugin.Get("Video_Initialize"));
Video_Prepare = reinterpret_cast<TVideo_Prepare> (plugin.Get("Video_Prepare"));
Video_Shutdown = reinterpret_cast<TVideo_Shutdown> (plugin.Get("Video_Shutdown"));
Video_SendFifoData = reinterpret_cast<TVideo_SendFifoData> (plugin.Get("Video_SendFifoData"));
Video_UpdateXFB = reinterpret_cast<TVideo_UpdateXFB> (plugin.Get("Video_UpdateXFB"));
Video_Screenshot = reinterpret_cast<TVideo_Screenshot> (plugin.Get("Video_Screenshot"));
Video_EnterLoop = reinterpret_cast<TVideo_EnterLoop> (plugin.Get("Video_EnterLoop"));
Video_AddMessage = reinterpret_cast<TVideo_AddMessage> (plugin.Get("Video_AddMessage"));
Video_DoState = reinterpret_cast<TVideo_DoState> (plugin.Get("Video_DoState"));
Video_Stop = reinterpret_cast<TVideo_Stop> (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<TGetDllInfo> (plugin.Get("GetDllInfo"));
DllConfig = reinterpret_cast<TDllConfig> (plugin.Get("DllConfig"));
DllDebugger = reinterpret_cast<TDllDebugger> (plugin.Get("DllDebugger"));
Video_Initialize = reinterpret_cast<TVideo_Initialize> (plugin.Get("Video_Initialize"));
Video_Prepare = reinterpret_cast<TVideo_Prepare> (plugin.Get("Video_Prepare"));
Video_Shutdown = reinterpret_cast<TVideo_Shutdown> (plugin.Get("Video_Shutdown"));
Video_SendFifoData = reinterpret_cast<TVideo_SendFifoData> (plugin.Get("Video_SendFifoData"));
Video_UpdateXFB = reinterpret_cast<TVideo_UpdateXFB> (plugin.Get("Video_UpdateXFB"));
Video_Screenshot = reinterpret_cast<TVideo_Screenshot> (plugin.Get("Video_Screenshot"));
Video_EnterLoop = reinterpret_cast<TVideo_EnterLoop> (plugin.Get("Video_EnterLoop"));
Video_AddMessage = reinterpret_cast<TVideo_AddMessage> (plugin.Get("Video_AddMessage"));
Video_DoState = reinterpret_cast<TVideo_DoState> (plugin.Get("Video_DoState"));
Video_Stop = reinterpret_cast<TVideo_Stop> (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

View File

@ -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<TGetDllInfo> (plugin.Get("GetDllInfo"));
DllConfig = reinterpret_cast<TDllConfig> (plugin.Get("DllConfig"));
Wiimote_Initialize = reinterpret_cast<TWiimote_Initialize> (plugin.Get("Wiimote_Initialize"));
Wiimote_Shutdown = reinterpret_cast<TWiimote_Shutdown> (plugin.Get("Wiimote_Shutdown"));
Wiimote_ControlChannel = reinterpret_cast<TWiimote_Output> (plugin.Get("Wiimote_ControlChannel"));
Wiimote_InterruptChannel = reinterpret_cast<TWiimote_Input> (plugin.Get("Wiimote_InterruptChannel"));
Wiimote_Update = reinterpret_cast<TWiimote_Update> (plugin.Get("Wiimote_Update"));
Wiimote_GetAttachedControllers = reinterpret_cast<TWiimote_GetAttachedControllers> (plugin.Get("Wiimote_GetAttachedControllers"));
Wiimote_DoState = reinterpret_cast<TWiimote_DoState> (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<TGetDllInfo> (plugin.Get("GetDllInfo"));
DllConfig = reinterpret_cast<TDllConfig> (plugin.Get("DllConfig"));
Wiimote_Initialize = reinterpret_cast<TWiimote_Initialize> (plugin.Get("Wiimote_Initialize"));
Wiimote_Shutdown = reinterpret_cast<TWiimote_Shutdown> (plugin.Get("Wiimote_Shutdown"));
Wiimote_ControlChannel = reinterpret_cast<TWiimote_Output> (plugin.Get("Wiimote_ControlChannel"));
Wiimote_InterruptChannel = reinterpret_cast<TWiimote_Input> (plugin.Get("Wiimote_InterruptChannel"));
Wiimote_Update = reinterpret_cast<TWiimote_Update> (plugin.Get("Wiimote_Update"));
Wiimote_GetAttachedControllers = reinterpret_cast<TWiimote_GetAttachedControllers> (plugin.Get("Wiimote_GetAttachedControllers"));
Wiimote_DoState = reinterpret_cast<TWiimote_DoState> (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

View File

@ -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

View File

@ -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<31>23, 25<32>27, 30<33>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<31>23, 25<32>27, 30<33>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

View File

@ -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 <math.h>
#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 <class T>
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<float>(Memory::Read_U8(_Addr)) * m_dequantizeTable[_uScale];
break;
case QUANTIZE_U16:
fResult = static_cast<float>(Memory::Read_U16(_Addr)) * m_dequantizeTable[_uScale];
break;
case QUANTIZE_S8:
fResult = static_cast<float>((s8)Memory::Read_U8(_Addr)) * m_dequantizeTable[_uScale];
break;
// used for THP player
case QUANTIZE_S16:
fResult = static_cast<float>((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<EQuantizeType>(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<EQuantizeType>(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<EQuantizeType>(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<EQuantizeType>(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<EQuantizeType>(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<EQuantizeType>(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<EQuantizeType>(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<EQuantizeType>(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 <math.h>
#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 <class T>
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<float>(Memory::Read_U8(_Addr)) * m_dequantizeTable[_uScale];
break;
case QUANTIZE_U16:
fResult = static_cast<float>(Memory::Read_U16(_Addr)) * m_dequantizeTable[_uScale];
break;
case QUANTIZE_S8:
fResult = static_cast<float>((s8)Memory::Read_U8(_Addr)) * m_dequantizeTable[_uScale];
break;
// used for THP player
case QUANTIZE_S16:
fResult = static_cast<float>((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<EQuantizeType>(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<EQuantizeType>(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<EQuantizeType>(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<EQuantizeType>(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<EQuantizeType>(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<EQuantizeType>(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<EQuantizeType>(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<EQuantizeType>(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=======
}

View File

@ -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 <math.h>
#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<float>((rPS0(_inst.FA) >= -0.0) ? rPS0(_inst.FC) : rPS0(_inst.FB));
rPS1(_inst.FD) = static_cast<float>((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<float>(rPS0(_inst.FA) / rPS0(_inst.FB));
rPS1(_inst.FD) = static_cast<float>(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<float>(rPS0(_inst.FA) - rPS0(_inst.FB));
rPS1(_inst.FD) = static_cast<float>(rPS1(_inst.FA) - rPS1(_inst.FB));
}
void ps_add(UGeckoInstruction _inst)
{
rPS0(_inst.FD) = static_cast<float>(rPS0(_inst.FA) + rPS0(_inst.FB));
rPS1(_inst.FD) = static_cast<float>(rPS1(_inst.FA) + rPS1(_inst.FB));
}
void ps_res(UGeckoInstruction _inst)
{
rPS0(_inst.FD) = 1.0f / static_cast<float>(rPS0(_inst.FB));
rPS1(_inst.FD) = 1.0f / static_cast<float>(rPS1(_inst.FB));
}
void ps_mul(UGeckoInstruction _inst)
{
rPS0(_inst.FD) = static_cast<float>(rPS0(_inst.FA) * rPS0(_inst.FC));
rPS1(_inst.FD) = static_cast<float>(rPS1(_inst.FA) * rPS1(_inst.FC));
}
void ps_rsqrte(UGeckoInstruction _inst)
{
rPS0(_inst.FD) = static_cast<double>(1.0f / sqrtf((float)rPS0(_inst.FB)));
rPS1(_inst.FD) = static_cast<double>(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<float>((rPS0(_inst.FA) * rPS0(_inst.FC)) - rPS0(_inst.FB));
rPS1(_inst.FD) = static_cast<float>((rPS1(_inst.FA) * rPS1(_inst.FC)) - rPS1(_inst.FB));
}
void ps_madd(UGeckoInstruction _inst)
{
rPS0(_inst.FD) = static_cast<float>((rPS0(_inst.FA) * rPS0(_inst.FC)) + rPS0(_inst.FB));
rPS1(_inst.FD) = static_cast<float>((rPS1(_inst.FA) * rPS1(_inst.FC)) + rPS1(_inst.FB));
}
void ps_nmsub(UGeckoInstruction _inst)
{
rPS0(_inst.FD) = static_cast<float>(-(rPS0(_inst.FA) * rPS0(_inst.FC) - rPS0(_inst.FB)));
rPS1(_inst.FD) = static_cast<float>(-(rPS1(_inst.FA) * rPS1(_inst.FC) - rPS1(_inst.FB)));
}
void ps_nmadd(UGeckoInstruction _inst)
{
rPS0(_inst.FD) = static_cast<float>(-(rPS0(_inst.FA) * rPS0(_inst.FC) + rPS0(_inst.FB)));
rPS1(_inst.FD) = static_cast<float>(-(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<blockEnd; i+=4)
Memory::Write_U32(0,i);
*/
}
} // 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 <math.h>
#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<float>((rPS0(_inst.FA) >= -0.0) ? rPS0(_inst.FC) : rPS0(_inst.FB));
rPS1(_inst.FD) = static_cast<float>((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<float>(rPS0(_inst.FA) / rPS0(_inst.FB));
rPS1(_inst.FD) = static_cast<float>(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<float>(rPS0(_inst.FA) - rPS0(_inst.FB));
rPS1(_inst.FD) = static_cast<float>(rPS1(_inst.FA) - rPS1(_inst.FB));
}
void ps_add(UGeckoInstruction _inst)
{
rPS0(_inst.FD) = static_cast<float>(rPS0(_inst.FA) + rPS0(_inst.FB));
rPS1(_inst.FD) = static_cast<float>(rPS1(_inst.FA) + rPS1(_inst.FB));
}
void ps_res(UGeckoInstruction _inst)
{
rPS0(_inst.FD) = 1.0f / static_cast<float>(rPS0(_inst.FB));
rPS1(_inst.FD) = 1.0f / static_cast<float>(rPS1(_inst.FB));
}
void ps_mul(UGeckoInstruction _inst)
{
rPS0(_inst.FD) = static_cast<float>(rPS0(_inst.FA) * rPS0(_inst.FC));
rPS1(_inst.FD) = static_cast<float>(rPS1(_inst.FA) * rPS1(_inst.FC));
}
void ps_rsqrte(UGeckoInstruction _inst)
{
rPS0(_inst.FD) = static_cast<double>(1.0f / sqrtf((float)rPS0(_inst.FB)));
rPS1(_inst.FD) = static_cast<double>(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<float>((rPS0(_inst.FA) * rPS0(_inst.FC)) - rPS0(_inst.FB));
rPS1(_inst.FD) = static_cast<float>((rPS1(_inst.FA) * rPS1(_inst.FC)) - rPS1(_inst.FB));
}
void ps_madd(UGeckoInstruction _inst)
{
rPS0(_inst.FD) = static_cast<float>((rPS0(_inst.FA) * rPS0(_inst.FC)) + rPS0(_inst.FB));
rPS1(_inst.FD) = static_cast<float>((rPS1(_inst.FA) * rPS1(_inst.FC)) + rPS1(_inst.FB));
}
void ps_nmsub(UGeckoInstruction _inst)
{
rPS0(_inst.FD) = static_cast<float>(-(rPS0(_inst.FA) * rPS0(_inst.FC) - rPS0(_inst.FB)));
rPS1(_inst.FD) = static_cast<float>(-(rPS1(_inst.FA) * rPS1(_inst.FC) - rPS1(_inst.FB)));
}
void ps_nmadd(UGeckoInstruction _inst)
{
rPS0(_inst.FD) = static_cast<float>(-(rPS0(_inst.FA) * rPS0(_inst.FC) + rPS0(_inst.FB)));
rPS1(_inst.FD) = static_cast<float>(-(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<blockEnd; i+=4)
Memory::Write_U32(0,i);
*/
}
} // namespace

View File

@ -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 <map>
#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<u32, int> 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 <map>
#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<u32, int> 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;
}
}

View File

@ -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<void *>(&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<void *>(&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<void *>(&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<void *>(&PowerPC::CheckExceptions));
MOV(32, R(EAX), M(&NPC));
MOV(32, M(&PC), R(EAX));
JMP(dispatcher);
SetJumpTarget(bail);
doTiming = GetCodePtr();
CALL(reinterpret_cast<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(reinterpret_cast<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);
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<void *>(&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<void *>(&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<void *>(&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<void *>(&PowerPC::CheckExceptions));
MOV(32, R(EAX), M(&NPC));
MOV(32, M(&PC), R(EAX));
JMP(dispatcher);
SetJumpTarget(bail);
doTiming = GetCodePtr();
CALL(reinterpret_cast<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(reinterpret_cast<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);
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

View File

@ -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 <string>
#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 <string>
#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

View File

@ -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 <map>
#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 <opagent.h>
#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<u32, int> 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<u32, int>(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<u32, int>::iterator iter;
pair<multimap<u32, int>::iterator, multimap<u32, int>::iterator> ppp;
// equal_range(b) returns pair<iterator,iterator> representing the range
// of element with key b
ppp = links_to.equal_range(b.originalAddress);
if (ppp.first == ppp.second)
return;
for (multimap<u32, int>::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 <map>
#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 <opagent.h>
#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<u32, int> 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<u32, int>(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<u32, int>::iterator iter;
pair<multimap<u32, int>::iterator, multimap<u32, int>::iterator> ppp;
// equal_range(b) returns pair<iterator,iterator> representing the range
// of element with key b
ppp = links_to.equal_range(b.originalAddress);
if (ppp.first == ppp.second)
return;
for (multimap<u32, int>::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

View File

@ -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

View File

@ -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);
}
}
}
}
}

Some files were not shown because too many files have changed in this diff Show More