forked from dolphin-emu/dolphin
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:
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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>
|
||||
|
||||
|
||||
@@ -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);
|
||||
}*/
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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 = §ions[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 = §ions[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;
|
||||
}
|
||||
*/
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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();
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -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(§ion, spincount);
|
||||
}
|
||||
else
|
||||
{
|
||||
InitializeCriticalSection(§ion);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CriticalSection::~CriticalSection()
|
||||
{
|
||||
DeleteCriticalSection(§ion);
|
||||
}
|
||||
|
||||
|
||||
void CriticalSection::Enter()
|
||||
{
|
||||
EnterCriticalSection(§ion);
|
||||
}
|
||||
|
||||
|
||||
bool CriticalSection::TryEnter()
|
||||
{
|
||||
return(TryEnterCriticalSection(§ion) ? true : false);
|
||||
}
|
||||
|
||||
|
||||
void CriticalSection::Leave()
|
||||
{
|
||||
LeaveCriticalSection(§ion);
|
||||
}
|
||||
|
||||
|
||||
Thread::Thread(ThreadFunc function, void* arg)
|
||||
: m_hThread(NULL), m_threadId(0)
|
||||
{
|
||||
m_hThread = CreateThread(
|
||||
0, // Security attributes
|
||||
0, // Stack size
|
||||
function,
|
||||
arg,
|
||||
0,
|
||||
&m_threadId);
|
||||
}
|
||||
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
WaitForDeath();
|
||||
}
|
||||
|
||||
|
||||
void Thread::WaitForDeath()
|
||||
{
|
||||
if (m_hThread)
|
||||
{
|
||||
WaitForSingleObject(m_hThread, INFINITE);
|
||||
CloseHandle(m_hThread);
|
||||
m_hThread = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Thread::SetAffinity(int mask)
|
||||
{
|
||||
SetThreadAffinityMask(m_hThread, mask);
|
||||
}
|
||||
|
||||
|
||||
void Thread::SetCurrentThreadAffinity(int mask)
|
||||
{
|
||||
SetThreadAffinityMask(GetCurrentThread(), mask);
|
||||
}
|
||||
|
||||
|
||||
Event::Event()
|
||||
{
|
||||
m_hEvent = 0;
|
||||
}
|
||||
|
||||
|
||||
void Event::Init()
|
||||
{
|
||||
m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
}
|
||||
|
||||
|
||||
void Event::Shutdown()
|
||||
{
|
||||
CloseHandle(m_hEvent);
|
||||
m_hEvent = 0;
|
||||
}
|
||||
|
||||
|
||||
void Event::Set()
|
||||
{
|
||||
SetEvent(m_hEvent);
|
||||
}
|
||||
|
||||
|
||||
void Event::Wait()
|
||||
{
|
||||
WaitForSingleObject(m_hEvent, INFINITE);
|
||||
}
|
||||
|
||||
|
||||
void SleepCurrentThread(int ms)
|
||||
{
|
||||
Sleep(ms);
|
||||
}
|
||||
|
||||
|
||||
typedef struct tagTHREADNAME_INFO
|
||||
{
|
||||
DWORD dwType; // must be 0x1000
|
||||
LPCSTR szName; // pointer to name (in user addr space)
|
||||
DWORD dwThreadID; // thread ID (-1=caller thread)
|
||||
DWORD dwFlags; // reserved for future use, must be zero
|
||||
} THREADNAME_INFO;
|
||||
// Usage: SetThreadName (-1, "MainThread");
|
||||
//
|
||||
// Sets the debugger-visible name of the current thread.
|
||||
// Uses undocumented (actually, it is now documented) trick.
|
||||
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp
|
||||
|
||||
void SetCurrentThreadName(const TCHAR* szThreadName)
|
||||
{
|
||||
THREADNAME_INFO info;
|
||||
info.dwType = 0x1000;
|
||||
#ifdef UNICODE
|
||||
//TODO: Find the proper way to do this.
|
||||
char tname[256];
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < _tcslen(szThreadName); i++)
|
||||
{
|
||||
tname[i] = (char)szThreadName[i]; //poor man's unicode->ansi, TODO: fix
|
||||
}
|
||||
|
||||
tname[i] = 0;
|
||||
info.szName = tname;
|
||||
#else
|
||||
info.szName = szThreadName;
|
||||
#endif
|
||||
|
||||
info.dwThreadID = -1; //dwThreadID;
|
||||
info.dwFlags = 0;
|
||||
__try
|
||||
{
|
||||
RaiseException(0x406D1388, 0, sizeof(info) / sizeof(DWORD), (ULONG_PTR*)&info);
|
||||
}
|
||||
__except(EXCEPTION_CONTINUE_EXECUTION)
|
||||
{}
|
||||
}
|
||||
// TODO: check if ever inline
|
||||
LONG SyncInterlockedIncrement(LONG *Dest)
|
||||
{
|
||||
return InterlockedIncrement(Dest);
|
||||
}
|
||||
|
||||
LONG SyncInterlockedExchangeAdd(LONG *Dest, LONG Val)
|
||||
{
|
||||
return InterlockedExchangeAdd(Dest, Val);
|
||||
}
|
||||
|
||||
LONG SyncInterlockedExchange(LONG *Dest, LONG Val)
|
||||
{
|
||||
return InterlockedExchange(Dest, Val);
|
||||
}
|
||||
|
||||
#elif __GNUC__
|
||||
CriticalSection::CriticalSection(int spincount_unused)
|
||||
{
|
||||
pthread_mutex_init(&mutex, 0);
|
||||
}
|
||||
|
||||
|
||||
CriticalSection::~CriticalSection()
|
||||
{
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
|
||||
|
||||
void CriticalSection::Enter()
|
||||
{
|
||||
pthread_mutex_lock(&mutex);
|
||||
}
|
||||
|
||||
|
||||
bool CriticalSection::TryEnter()
|
||||
{
|
||||
return(!pthread_mutex_trylock(&mutex));
|
||||
}
|
||||
|
||||
|
||||
void CriticalSection::Leave()
|
||||
{
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
|
||||
Thread::Thread(ThreadFunc function, void* arg)
|
||||
: thread_id(0)
|
||||
{
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setstacksize(&attr, 1024 * 1024);
|
||||
pthread_create(&thread_id, &attr, function, arg);
|
||||
}
|
||||
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
WaitForDeath();
|
||||
}
|
||||
|
||||
|
||||
void Thread::WaitForDeath()
|
||||
{
|
||||
if (thread_id)
|
||||
{
|
||||
void* exit_status;
|
||||
pthread_join(thread_id, &exit_status);
|
||||
if (exit_status)
|
||||
fprintf(stderr, "error %d joining thread\n", *(int *)exit_status);
|
||||
thread_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Thread::SetAffinity(int mask)
|
||||
{
|
||||
// This is non-standard
|
||||
#ifdef __linux__
|
||||
cpu_set_t cpu_set;
|
||||
CPU_ZERO(&cpu_set);
|
||||
|
||||
for (unsigned int i = 0; i < sizeof(mask) * 8; i++)
|
||||
{
|
||||
if ((mask >> i) & 1){CPU_SET(i, &cpu_set);}
|
||||
}
|
||||
|
||||
pthread_setaffinity_np(thread_id, sizeof(cpu_set), &cpu_set);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Thread::SetCurrentThreadAffinity(int mask)
|
||||
{
|
||||
#ifdef __linux__
|
||||
cpu_set_t cpu_set;
|
||||
CPU_ZERO(&cpu_set);
|
||||
|
||||
for (size_t i = 0; i < sizeof(mask) * 8; i++)
|
||||
{
|
||||
if ((mask >> i) & 1){CPU_SET(i, &cpu_set);}
|
||||
}
|
||||
|
||||
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void SleepCurrentThread(int ms)
|
||||
{
|
||||
usleep(1000 * ms);
|
||||
}
|
||||
|
||||
|
||||
void SetCurrentThreadName(const TCHAR* szThreadName)
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
|
||||
Event::Event()
|
||||
{
|
||||
is_set_ = false;
|
||||
}
|
||||
|
||||
|
||||
void Event::Init()
|
||||
{
|
||||
pthread_cond_init(&event_, 0);
|
||||
pthread_mutex_init(&mutex_, 0);
|
||||
}
|
||||
|
||||
|
||||
void Event::Shutdown()
|
||||
{
|
||||
pthread_mutex_destroy(&mutex_);
|
||||
pthread_cond_destroy(&event_);
|
||||
}
|
||||
|
||||
|
||||
void Event::Set()
|
||||
{
|
||||
pthread_mutex_lock(&mutex_);
|
||||
|
||||
if (!is_set_)
|
||||
{
|
||||
is_set_ = true;
|
||||
pthread_cond_signal(&event_);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&mutex_);
|
||||
}
|
||||
|
||||
|
||||
void Event::Wait()
|
||||
{
|
||||
pthread_mutex_lock(&mutex_);
|
||||
|
||||
while (!is_set_)
|
||||
{
|
||||
pthread_cond_wait(&event_, &mutex_);
|
||||
}
|
||||
|
||||
is_set_ = false;
|
||||
pthread_mutex_unlock(&mutex_);
|
||||
}
|
||||
|
||||
LONG SyncInterlockedIncrement(LONG *Dest)
|
||||
{
|
||||
#if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__))
|
||||
return __sync_add_and_fetch(Dest, 1);
|
||||
#else
|
||||
register int result;
|
||||
__asm__ __volatile__("lock; xadd %0,%1"
|
||||
: "=r" (result), "=m" (*Dest)
|
||||
: "0" (1), "m" (*Dest)
|
||||
: "memory");
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
LONG SyncInterlockedExchangeAdd(LONG *Dest, LONG Val)
|
||||
{
|
||||
#if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__))
|
||||
return __sync_add_and_fetch(Dest, Val);
|
||||
#else
|
||||
register int result;
|
||||
__asm__ __volatile__("lock; xadd %0,%1"
|
||||
: "=r" (result), "=m" (*Dest)
|
||||
: "0" (Val), "m" (*Dest)
|
||||
: "memory");
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
LONG SyncInterlockedExchange(LONG *Dest, LONG Val)
|
||||
{
|
||||
#if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__))
|
||||
return __sync_lock_test_and_set(Dest, Val);
|
||||
#else
|
||||
register int result;
|
||||
__asm__ __volatile__("lock; xchg %0,%1"
|
||||
: "=r" (result), "=m" (*Dest)
|
||||
: "0" (Val), "m" (*Dest)
|
||||
: "memory");
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // end of namespace Common
|
||||
// Copyright (C) 2003-2008 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#include "Common.h"
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <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(§ion, spincount);
|
||||
}
|
||||
else
|
||||
{
|
||||
InitializeCriticalSection(§ion);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
CriticalSection::~CriticalSection()
|
||||
{
|
||||
DeleteCriticalSection(§ion);
|
||||
}
|
||||
|
||||
|
||||
void CriticalSection::Enter()
|
||||
{
|
||||
EnterCriticalSection(§ion);
|
||||
}
|
||||
|
||||
|
||||
bool CriticalSection::TryEnter()
|
||||
{
|
||||
return(TryEnterCriticalSection(§ion) ? true : false);
|
||||
}
|
||||
|
||||
|
||||
void CriticalSection::Leave()
|
||||
{
|
||||
LeaveCriticalSection(§ion);
|
||||
}
|
||||
|
||||
|
||||
Thread::Thread(ThreadFunc function, void* arg)
|
||||
: m_hThread(NULL), m_threadId(0)
|
||||
{
|
||||
m_hThread = CreateThread(
|
||||
0, // Security attributes
|
||||
0, // Stack size
|
||||
function,
|
||||
arg,
|
||||
0,
|
||||
&m_threadId);
|
||||
}
|
||||
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
WaitForDeath();
|
||||
}
|
||||
|
||||
|
||||
void Thread::WaitForDeath()
|
||||
{
|
||||
if (m_hThread)
|
||||
{
|
||||
WaitForSingleObject(m_hThread, INFINITE);
|
||||
CloseHandle(m_hThread);
|
||||
m_hThread = NULL;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Thread::SetAffinity(int mask)
|
||||
{
|
||||
SetThreadAffinityMask(m_hThread, mask);
|
||||
}
|
||||
|
||||
|
||||
void Thread::SetCurrentThreadAffinity(int mask)
|
||||
{
|
||||
SetThreadAffinityMask(GetCurrentThread(), mask);
|
||||
}
|
||||
|
||||
|
||||
Event::Event()
|
||||
{
|
||||
m_hEvent = 0;
|
||||
}
|
||||
|
||||
|
||||
void Event::Init()
|
||||
{
|
||||
m_hEvent = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
}
|
||||
|
||||
|
||||
void Event::Shutdown()
|
||||
{
|
||||
CloseHandle(m_hEvent);
|
||||
m_hEvent = 0;
|
||||
}
|
||||
|
||||
|
||||
void Event::Set()
|
||||
{
|
||||
SetEvent(m_hEvent);
|
||||
}
|
||||
|
||||
|
||||
void Event::Wait()
|
||||
{
|
||||
WaitForSingleObject(m_hEvent, INFINITE);
|
||||
}
|
||||
|
||||
|
||||
void SleepCurrentThread(int ms)
|
||||
{
|
||||
Sleep(ms);
|
||||
}
|
||||
|
||||
|
||||
typedef struct tagTHREADNAME_INFO
|
||||
{
|
||||
DWORD dwType; // must be 0x1000
|
||||
LPCSTR szName; // pointer to name (in user addr space)
|
||||
DWORD dwThreadID; // thread ID (-1=caller thread)
|
||||
DWORD dwFlags; // reserved for future use, must be zero
|
||||
} THREADNAME_INFO;
|
||||
// Usage: SetThreadName (-1, "MainThread");
|
||||
//
|
||||
// Sets the debugger-visible name of the current thread.
|
||||
// Uses undocumented (actually, it is now documented) trick.
|
||||
// http://msdn.microsoft.com/library/default.asp?url=/library/en-us/vsdebug/html/vxtsksettingthreadname.asp
|
||||
|
||||
void SetCurrentThreadName(const TCHAR* szThreadName)
|
||||
{
|
||||
THREADNAME_INFO info;
|
||||
info.dwType = 0x1000;
|
||||
#ifdef UNICODE
|
||||
//TODO: Find the proper way to do this.
|
||||
char tname[256];
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < _tcslen(szThreadName); i++)
|
||||
{
|
||||
tname[i] = (char)szThreadName[i]; //poor man's unicode->ansi, TODO: fix
|
||||
}
|
||||
|
||||
tname[i] = 0;
|
||||
info.szName = tname;
|
||||
#else
|
||||
info.szName = szThreadName;
|
||||
#endif
|
||||
|
||||
info.dwThreadID = -1; //dwThreadID;
|
||||
info.dwFlags = 0;
|
||||
__try
|
||||
{
|
||||
RaiseException(0x406D1388, 0, sizeof(info) / sizeof(DWORD), (ULONG_PTR*)&info);
|
||||
}
|
||||
__except(EXCEPTION_CONTINUE_EXECUTION)
|
||||
{}
|
||||
}
|
||||
// TODO: check if ever inline
|
||||
LONG SyncInterlockedIncrement(LONG *Dest)
|
||||
{
|
||||
return InterlockedIncrement(Dest);
|
||||
}
|
||||
|
||||
LONG SyncInterlockedExchangeAdd(LONG *Dest, LONG Val)
|
||||
{
|
||||
return InterlockedExchangeAdd(Dest, Val);
|
||||
}
|
||||
|
||||
LONG SyncInterlockedExchange(LONG *Dest, LONG Val)
|
||||
{
|
||||
return InterlockedExchange(Dest, Val);
|
||||
}
|
||||
|
||||
#elif __GNUC__
|
||||
CriticalSection::CriticalSection(int spincount_unused)
|
||||
{
|
||||
pthread_mutex_init(&mutex, 0);
|
||||
}
|
||||
|
||||
|
||||
CriticalSection::~CriticalSection()
|
||||
{
|
||||
pthread_mutex_destroy(&mutex);
|
||||
}
|
||||
|
||||
|
||||
void CriticalSection::Enter()
|
||||
{
|
||||
pthread_mutex_lock(&mutex);
|
||||
}
|
||||
|
||||
|
||||
bool CriticalSection::TryEnter()
|
||||
{
|
||||
return(!pthread_mutex_trylock(&mutex));
|
||||
}
|
||||
|
||||
|
||||
void CriticalSection::Leave()
|
||||
{
|
||||
pthread_mutex_unlock(&mutex);
|
||||
}
|
||||
|
||||
|
||||
Thread::Thread(ThreadFunc function, void* arg)
|
||||
: thread_id(0)
|
||||
{
|
||||
pthread_attr_t attr;
|
||||
pthread_attr_init(&attr);
|
||||
pthread_attr_setstacksize(&attr, 1024 * 1024);
|
||||
pthread_create(&thread_id, &attr, function, arg);
|
||||
}
|
||||
|
||||
|
||||
Thread::~Thread()
|
||||
{
|
||||
WaitForDeath();
|
||||
}
|
||||
|
||||
|
||||
void Thread::WaitForDeath()
|
||||
{
|
||||
if (thread_id)
|
||||
{
|
||||
void* exit_status;
|
||||
pthread_join(thread_id, &exit_status);
|
||||
if (exit_status)
|
||||
fprintf(stderr, "error %d joining thread\n", *(int *)exit_status);
|
||||
thread_id = 0;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void Thread::SetAffinity(int mask)
|
||||
{
|
||||
// This is non-standard
|
||||
#ifdef __linux__
|
||||
cpu_set_t cpu_set;
|
||||
CPU_ZERO(&cpu_set);
|
||||
|
||||
for (unsigned int i = 0; i < sizeof(mask) * 8; i++)
|
||||
{
|
||||
if ((mask >> i) & 1){CPU_SET(i, &cpu_set);}
|
||||
}
|
||||
|
||||
pthread_setaffinity_np(thread_id, sizeof(cpu_set), &cpu_set);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void Thread::SetCurrentThreadAffinity(int mask)
|
||||
{
|
||||
#ifdef __linux__
|
||||
cpu_set_t cpu_set;
|
||||
CPU_ZERO(&cpu_set);
|
||||
|
||||
for (size_t i = 0; i < sizeof(mask) * 8; i++)
|
||||
{
|
||||
if ((mask >> i) & 1){CPU_SET(i, &cpu_set);}
|
||||
}
|
||||
|
||||
pthread_setaffinity_np(pthread_self(), sizeof(cpu_set), &cpu_set);
|
||||
#endif
|
||||
}
|
||||
|
||||
|
||||
void SleepCurrentThread(int ms)
|
||||
{
|
||||
usleep(1000 * ms);
|
||||
}
|
||||
|
||||
|
||||
void SetCurrentThreadName(const TCHAR* szThreadName)
|
||||
{
|
||||
// noop
|
||||
}
|
||||
|
||||
|
||||
Event::Event()
|
||||
{
|
||||
is_set_ = false;
|
||||
}
|
||||
|
||||
|
||||
void Event::Init()
|
||||
{
|
||||
pthread_cond_init(&event_, 0);
|
||||
pthread_mutex_init(&mutex_, 0);
|
||||
}
|
||||
|
||||
|
||||
void Event::Shutdown()
|
||||
{
|
||||
pthread_mutex_destroy(&mutex_);
|
||||
pthread_cond_destroy(&event_);
|
||||
}
|
||||
|
||||
|
||||
void Event::Set()
|
||||
{
|
||||
pthread_mutex_lock(&mutex_);
|
||||
|
||||
if (!is_set_)
|
||||
{
|
||||
is_set_ = true;
|
||||
pthread_cond_signal(&event_);
|
||||
}
|
||||
|
||||
pthread_mutex_unlock(&mutex_);
|
||||
}
|
||||
|
||||
|
||||
void Event::Wait()
|
||||
{
|
||||
pthread_mutex_lock(&mutex_);
|
||||
|
||||
while (!is_set_)
|
||||
{
|
||||
pthread_cond_wait(&event_, &mutex_);
|
||||
}
|
||||
|
||||
is_set_ = false;
|
||||
pthread_mutex_unlock(&mutex_);
|
||||
}
|
||||
|
||||
LONG SyncInterlockedIncrement(LONG *Dest)
|
||||
{
|
||||
#if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__))
|
||||
return __sync_add_and_fetch(Dest, 1);
|
||||
#else
|
||||
register int result;
|
||||
__asm__ __volatile__("lock; xadd %0,%1"
|
||||
: "=r" (result), "=m" (*Dest)
|
||||
: "0" (1), "m" (*Dest)
|
||||
: "memory");
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
LONG SyncInterlockedExchangeAdd(LONG *Dest, LONG Val)
|
||||
{
|
||||
#if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__))
|
||||
return __sync_add_and_fetch(Dest, Val);
|
||||
#else
|
||||
register int result;
|
||||
__asm__ __volatile__("lock; xadd %0,%1"
|
||||
: "=r" (result), "=m" (*Dest)
|
||||
: "0" (Val), "m" (*Dest)
|
||||
: "memory");
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
LONG SyncInterlockedExchange(LONG *Dest, LONG Val)
|
||||
{
|
||||
#if defined(__GNUC__) && defined (__GNUC_MINOR__) && ((4 < __GNUC__) || (4 == __GNUC__ && 1 <= __GNUC_MINOR__))
|
||||
return __sync_lock_test_and_set(Dest, Val);
|
||||
#else
|
||||
register int result;
|
||||
__asm__ __volatile__("lock; xchg %0,%1"
|
||||
: "=r" (result), "=m" (*Dest)
|
||||
: "0" (Val), "m" (*Dest)
|
||||
: "memory");
|
||||
return result;
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
} // end of namespace Common
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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(<ime);
|
||||
return((u64)ltime);
|
||||
}
|
||||
} // end of namespace Common
|
||||
// Copyright (C) 2003-2008 Dolphin Project.
|
||||
|
||||
// This program is free software: you can redistribute it and/or modify
|
||||
// it under the terms of the GNU General Public License as published by
|
||||
// the Free Software Foundation, version 2.0.
|
||||
|
||||
// This program is distributed in the hope that it will be useful,
|
||||
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
// GNU General Public License 2.0 for more details.
|
||||
|
||||
// A copy of the GPL 2.0 should have been included with the program.
|
||||
// If not, see http://www.gnu.org/licenses/
|
||||
|
||||
// Official SVN repository and contact information can be found at
|
||||
// http://code.google.com/p/dolphin-emu/
|
||||
|
||||
#ifdef _WIN32
|
||||
#include <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(<ime);
|
||||
return((u64)ltime);
|
||||
}
|
||||
} // end of namespace Common
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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 = §ions[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 = §ions[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;
|
||||
}
|
||||
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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()
|
||||
{
|
||||
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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++;
|
||||
}
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
|
||||
@@ -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);
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
@@ -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
@@ -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)
|
||||
{
|
||||
}
|
||||
|
||||
|
||||
@@ -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 );
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,2 +1,2 @@
|
||||
#include "Host.h"
|
||||
|
||||
#include "Host.h"
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
@@ -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
@@ -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;
|
||||
}
|
||||
@@ -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
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
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
@@ -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=======
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
Reference in New Issue
Block a user