Add mini unit testing framework to Dolphin itself - use it to find bugs and verify the portable powerpc fp number classifier. also random cleanup.

git-svn-id: https://dolphin-emu.googlecode.com/svn/trunk@3432 8ced0084-cf51-0410-be5f-012b33b47a6e
This commit is contained in:
hrydgard 2009-06-13 22:08:01 +00:00
parent df7b29da32
commit 31f7020b2d
18 changed files with 1159 additions and 620 deletions

View File

@ -19,6 +19,7 @@
#define _SI_DEVICEGCCONTROLLER_H
#include "../PluginManager.h"
#include "SI_Device.h"
//////////////////////////////////////////////////////////////////////////
// standard gamecube controller

View File

@ -405,25 +405,45 @@ void faddsx(UGeckoInstruction _inst)
void fdivx(UGeckoInstruction _inst)
{
rPS0(_inst.FD) = rPS0(_inst.FA) / rPS0(_inst.FB);
double a = rPS0(_inst.FA);
double b = rPS0(_inst.FB);
if (a == 0.0f && b == 0.0f)
rPS0(_inst.FD) = rPS1(_inst.FD) = 0.0; // NAN?
else
rPS0(_inst.FD) = rPS1(_inst.FD) = a / b;
if (fabs(rPS0(_inst.FB)) == 0.0) {
if (!FPSCR.ZX)
FPSCR.FX = 1;
FPSCR.ZX = 1;
FPSCR.XX = 1;
}
if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD));
}
void fdivsx(UGeckoInstruction _inst)
{
rPS0(_inst.FD) = rPS1(_inst.FD) = static_cast<float>(rPS0(_inst.FA) / rPS0(_inst.FB));
if (fabs(rPS0(_inst.FB)) == 0.0) {
float a = rPS0(_inst.FA);
float b = rPS0(_inst.FB);
if (a != a || b != b)
rPS0(_inst.FD) = rPS1(_inst.FD) = 0.0; // NAN?
else
rPS0(_inst.FD) = rPS1(_inst.FD) = a / b;
if (b == 0.0) {
if (!FPSCR.ZX)
FPSCR.FX = 1;
FPSCR.ZX = 1;
FPSCR.XX = 1;
}
if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD));
}
void fresx(UGeckoInstruction _inst)
{
rPS0(_inst.FD) = rPS1(_inst.FD) = static_cast<float>(1.0f / rPS0(_inst.FB));
double b = rPS0(_inst.FB);
rPS0(_inst.FD) = rPS1(_inst.FD) = 1.0 / b;
if (fabs(rPS0(_inst.FB)) == 0.0) {
if (!FPSCR.ZX)
FPSCR.FX = 1;
FPSCR.ZX = 1;
FPSCR.XX = 1;
}
if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD));
}
@ -480,16 +500,24 @@ void fsubsx(UGeckoInstruction _inst)
if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD));
}
void frsqrtex(UGeckoInstruction _inst)
{
rPS0(_inst.FD) = 1.0f / (sqrt(rPS0(_inst.FB)));
double b = rPS0(_inst.FB);
if (b <= 0.0)
rPS0(_inst.FD) = 0.0;
else
rPS0(_inst.FD) = 1.0f / (sqrt(b));
if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD));
}
void fsqrtx(UGeckoInstruction _inst)
{
rPS0(_inst.FD) = sqrt(rPS0(_inst.FB));
double b = rPS0(_inst.FB);
if (b < 0.0)
{
FPSCR.VXSQRT = 1;
}
rPS0(_inst.FD) = sqrt(b);
if (_inst.Rc) Helper_UpdateCR1(rPS0(_inst.FD));
}

View File

@ -161,6 +161,23 @@ ps_adds1
*/
//#define NAN_CHECK
static void CheckForNans()
{
static bool lastNan[32];
for (int i = 0; i < 32; i++) {
double v = rPS0(i);
if (v != v) {
if (!lastNan[i]) {
lastNan[i] = true;
PanicAlert("PC = %08x Got NAN in R%i", PC, i);
}
} else {
lastNan[i] = false;
}
}
}
Jit64 jit;
@ -171,376 +188,382 @@ namespace CPUCompare
extern u32 m_BlockStart;
}
void Jit(u32 em_address)
{
jit.Jit(em_address);
}
void Jit(u32 em_address)
{
jit.Jit(em_address);
}
void Jit64::Init()
{
asm_routines.compareEnabled = ::Core::g_CoreStartupParameter.bRunCompareClient;
void Jit64::Init()
{
asm_routines.compareEnabled = ::Core::g_CoreStartupParameter.bRunCompareClient;
jo.optimizeStack = true;
/* This will enable block linking in JitBlockCache::FinalizeBlock(), it gives faster execution but may not
be as stable as the alternative (to not link the blocks). However, I have not heard about any good examples
where this cause problems, so I'm enabling this by default, since I seem to get perhaps as much as 20% more
fps with this option enabled. If you suspect that this option cause problems you can also disable it from the
debugging window. */
jo.enableBlocklink = true;
jo.optimizeStack = true;
/* This will enable block linking in JitBlockCache::FinalizeBlock(), it gives faster execution but may not
be as stable as the alternative (to not link the blocks). However, I have not heard about any good examples
where this cause problems, so I'm enabling this by default, since I seem to get perhaps as much as 20% more
fps with this option enabled. If you suspect that this option cause problems you can also disable it from the
debugging window. */
jo.enableBlocklink = true;
#ifdef _M_X64
jo.enableFastMem = Core::GetStartupParameter().bUseFastMem;
jo.enableFastMem = Core::GetStartupParameter().bUseFastMem;
#else
jo.enableFastMem = false;
jo.enableFastMem = false;
#endif
jo.assumeFPLoadFromMem = true;
jo.fpAccurateFlags = true;
jo.optimizeGatherPipe = true;
jo.fastInterrupts = false;
jo.accurateSinglePrecision = true;
jo.assumeFPLoadFromMem = true;
jo.fpAccurateFlags = true;
jo.optimizeGatherPipe = true;
jo.fastInterrupts = false;
jo.accurateSinglePrecision = true;
gpr.SetEmitter(this);
fpr.SetEmitter(this);
gpr.SetEmitter(this);
fpr.SetEmitter(this);
// Custom settings
if (Core::g_CoreStartupParameter.bJITUnlimitedCache)
CODE_SIZE = 1024*1024*8*8;
if (Core::g_CoreStartupParameter.bJITBlockLinking)
{ jo.enableBlocklink = false; SuccessAlert("Your game was started without JIT Block Linking"); }
// Custom settings
if (Core::g_CoreStartupParameter.bJITUnlimitedCache)
CODE_SIZE = 1024*1024*8*8;
if (Core::g_CoreStartupParameter.bJITBlockLinking)
{ jo.enableBlocklink = false; SuccessAlert("Your game was started without JIT Block Linking"); }
trampolines.Init();
AllocCodeSpace(CODE_SIZE);
trampolines.Init();
AllocCodeSpace(CODE_SIZE);
blocks.Init();
asm_routines.Init();
}
blocks.Init();
asm_routines.Init();
}
void Jit64::ClearCache()
void Jit64::ClearCache()
{
blocks.Clear();
trampolines.ClearCodeSpace();
ClearCodeSpace();
}
void Jit64::Shutdown()
{
FreeCodeSpace();
blocks.Shutdown();
trampolines.Shutdown();
asm_routines.Shutdown();
}
// This is only called by Default() in this file. It will execute an instruction with the interpreter functions.
void Jit64::WriteCallInterpreter(UGeckoInstruction inst)
{
gpr.Flush(FLUSH_ALL);
fpr.Flush(FLUSH_ALL);
if (js.isLastInstruction)
{
blocks.Clear();
trampolines.ClearCodeSpace();
ClearCodeSpace();
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 Jit64::Shutdown()
if (js.isLastInstruction && SConfig::GetInstance().m_EnableRE0Fix )
{
FreeCodeSpace();
blocks.Shutdown();
trampolines.Shutdown();
asm_routines.Shutdown();
}
// This is only called by Default() in this file. It will execute an instruction with the interpreter functions.
void Jit64::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);
if (js.isLastInstruction && SConfig::GetInstance().m_EnableRE0Fix )
{
SConfig::GetInstance().LoadSettingsHLE();//Make sure the settings are up to date
MOV(32, R(EAX), M(&NPC));
WriteRfiExitDestInEAX();
}
}
void Jit64::unknown_instruction(UGeckoInstruction inst)
{
// CCPU::Break();
PanicAlert("unknown_instruction %08x - Fix me ;)", inst.hex);
}
void Jit64::Default(UGeckoInstruction _inst)
{
WriteCallInterpreter(_inst.hex);
}
void Jit64::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 Jit64::DoNothing(UGeckoInstruction _inst)
{
// Yup, just don't do anything.
}
void Jit64::NotifyBreakpoint(u32 em_address, bool set)
{
int block_num = blocks.GetBlockNumberFromStartAddress(em_address);
if (block_num >= 0)
{
blocks.DestroyBlock(block_num, false);
}
}
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;
}
DEBUG_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 Jit64::Cleanup()
{
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock > 0)
ABI_CallFunction((void *)&GPFifo::CheckGatherPipe);
}
void Jit64::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 = blocks.GetBlockNumberFromStartAddress(destination);
if (block >= 0 && jo.enableBlocklink)
{
// It exists! Joy of joy!
JMP(blocks.GetBlock(block)->checkedEntry, true);
b->linkStatus[exit_num] = true;
}
else
{
MOV(32, M(&PC), Imm32(destination));
JMP(asm_routines.dispatcher, true);
}
SConfig::GetInstance().LoadSettingsHLE();//Make sure the settings are up to date
MOV(32, R(EAX), M(&NPC));
WriteRfiExitDestInEAX();
}
}
void Jit64::WriteExitDestInEAX(int exit_num)
void Jit64::unknown_instruction(UGeckoInstruction inst)
{
// CCPU::Break();
PanicAlert("unknown_instruction %08x - Fix me ;)", inst.hex);
}
void Jit64::Default(UGeckoInstruction _inst)
{
WriteCallInterpreter(_inst.hex);
}
void Jit64::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 Jit64::DoNothing(UGeckoInstruction _inst)
{
// Yup, just don't do anything.
}
void Jit64::NotifyBreakpoint(u32 em_address, bool set)
{
int block_num = blocks.GetBlockNumberFromStartAddress(em_address);
if (block_num >= 0)
{
MOV(32, M(&PC), R(EAX));
Cleanup();
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
blocks.DestroyBlock(block_num, false);
}
}
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;
}
DEBUG_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 Jit64::Cleanup()
{
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock > 0)
ABI_CallFunction((void *)&GPFifo::CheckGatherPipe);
if (GetAsyncKeyState(VK_LSHIFT))
ABI_CallFunction(thunks.ProtectFunction((void *)&CheckForNans, 0));
}
void Jit64::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 = blocks.GetBlockNumberFromStartAddress(destination);
if (block >= 0 && jo.enableBlocklink)
{
// It exists! Joy of joy!
JMP(blocks.GetBlock(block)->checkedEntry, true);
b->linkStatus[exit_num] = true;
}
else
{
MOV(32, M(&PC), Imm32(destination));
JMP(asm_routines.dispatcher, true);
}
}
void Jit64::WriteRfiExitDestInEAX()
{
MOV(32, M(&PC), R(EAX));
Cleanup();
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
JMP(asm_routines.testExceptions, true);
}
void Jit64::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_routines.dispatcher, true);
}
void Jit64::WriteExceptionExit(u32 exception)
void Jit64::WriteRfiExitDestInEAX()
{
MOV(32, M(&PC), R(EAX));
Cleanup();
SUB(32, M(&CoreTiming::downcount), js.downcountAmount > 127 ? Imm32(js.downcountAmount) : Imm8(js.downcountAmount));
JMP(asm_routines.testExceptions, true);
}
void Jit64::WriteExceptionExit(u32 exception)
{
Cleanup();
OR(32, M(&PowerPC::ppcState.Exceptions), Imm32(exception));
MOV(32, M(&PC), Imm32(js.compilerPC + 4));
JMP(asm_routines.testExceptions, true);
}
void STACKALIGN Jit64::Run()
{
CompiledCode pExecAddr = (CompiledCode)asm_routines.enterCode;
pExecAddr();
//Will return when PowerPC::state changes
}
void Jit64::SingleStep()
{
// NOT USED, NOT TESTED, PROBABLY NOT WORKING YET
// PanicAlert("Single");
/*
JitBlock temp_block;
PPCAnalyst::CodeBuffer temp_codebuffer(1); // Only room for one instruction! Single step!
const u8 *code = DoJit(PowerPC::ppcState.pc, &temp_codebuffer, &temp_block);
CompiledCode pExecAddr = (CompiledCode)code;
pExecAddr();*/
}
void STACKALIGN Jit64::Jit(u32 em_address)
{
if (GetSpaceLeft() < 0x10000 || blocks.IsFull())
{
Cleanup();
OR(32, M(&PowerPC::ppcState.Exceptions), Imm32(exception));
MOV(32, M(&PC), Imm32(js.compilerPC + 4));
JMP(asm_routines.testExceptions, true);
WARN_LOG(DYNA_REC, "JIT cache full - clearing.")
if (Core::g_CoreStartupParameter.bJITUnlimitedCache)
{
ERROR_LOG(DYNA_REC, "What? JIT cache still full - clearing.");
PanicAlert("What? JIT cache still full - clearing.");
}
ClearCache();
}
int block_num = blocks.AllocateBlock(em_address);
JitBlock *b = blocks.GetBlock(block_num);
blocks.FinalizeBlock(block_num, jo.enableBlocklink, DoJit(em_address, &code_buffer, b));
}
const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buffer, JitBlock *b)
{
if (em_address == 0)
PanicAlert("ERROR : Trying to compile at 0. LR=%08x", LR);
int size;
js.isLastInstruction = false;
js.blockStart = em_address;
js.fifoBytesThisBlock = 0;
js.curBlock = b;
js.blockSetsQuantizers = false;
js.block_flags = 0;
js.cancel = false;
//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::Flatten(em_address, &size, &js.st, &js.gpa, &js.fpa, code_buffer);
PPCAnalyst::CodeOp *ops = code_buffer->codebuffer;
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_routines.doTiming, true); // downcount hit zero - go doTiming.
SetJumpTarget(skip);
const u8 *normalEntry = GetCodePtr();
if (ImHereDebug)
ABI_CallFunction((void *)&ImHere); //Used to get a trace of the last few blocks before a crash, sometimes VERY useful
void STACKALIGN Jit64::Run()
if (js.fpa.any)
{
CompiledCode pExecAddr = (CompiledCode)asm_routines.enterCode;
pExecAddr();
//Will return when PowerPC::state changes
}
void Jit64::SingleStep()
{
// NOT USED, NOT TESTED, PROBABLY NOT WORKING YET
// PanicAlert("Single");
/*
JitBlock temp_block;
PPCAnalyst::CodeBuffer temp_codebuffer(1); // Only room for one instruction! Single step!
const u8 *code = DoJit(PowerPC::ppcState.pc, &temp_codebuffer, &temp_block);
CompiledCode pExecAddr = (CompiledCode)code;
pExecAddr();*/
}
void STACKALIGN Jit64::Jit(u32 em_address)
{
if (GetSpaceLeft() < 0x10000 || blocks.IsFull())
{
WARN_LOG(DYNA_REC, "JIT cache full - clearing.")
if (Core::g_CoreStartupParameter.bJITUnlimitedCache)
{
ERROR_LOG(DYNA_REC, "What? JIT cache still full - clearing.");
PanicAlert("What? JIT cache still full - clearing.");
}
ClearCache();
}
int block_num = blocks.AllocateBlock(em_address);
JitBlock *b = blocks.GetBlock(block_num);
blocks.FinalizeBlock(block_num, jo.enableBlocklink, DoJit(em_address, &code_buffer, b));
}
const u8* Jit64::DoJit(u32 em_address, PPCAnalyst::CodeBuffer *code_buffer, JitBlock *b)
{
if (em_address == 0)
PanicAlert("ERROR : Trying to compile at 0. LR=%08x", LR);
int size;
js.isLastInstruction = false;
js.blockStart = em_address;
js.fifoBytesThisBlock = 0;
js.curBlock = b;
js.blockSetsQuantizers = false;
js.block_flags = 0;
js.cancel = false;
//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::Flatten(em_address, &size, &js.st, &js.gpa, &js.fpa, code_buffer);
PPCAnalyst::CodeOp *ops = code_buffer->codebuffer;
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);
//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_routines.doTiming, true); // downcount hit zero - go doTiming.
SetJumpTarget(skip);
JMP(asm_routines.fpException, true);
SetJumpTarget(b1);
}
const u8 *normalEntry = GetCodePtr();
if (ImHereDebug)
ABI_CallFunction((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_routines.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_routines.testExceptions, 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_routines.testExceptions, true);
SetJumpTarget(b1);
}
// Conditionally add profiling code.
if (Profiler::g_ProfileBlocks) {
ADD(32, M(&b->runCount), Imm8(1));
// 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;
b->ticCounter.QuadPart = 0;
b->ticStart.QuadPart = 0;
b->ticStop.QuadPart = 0;
#else
//TODO
#endif
// get start tic
PROFILER_QUERY_PERFORMACE_COUNTER(&b->ticStart);
}
#if defined(_DEBUG) || defined(DEBUGFAST)
// should help logged stacktraces become more accurate
MOV(32, M(&PC), Imm32(js.blockStart));
#endif
//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(em_address);
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)
{
// WARNING - cmp->branch merging will screw this up.
js.isLastInstruction = true;
js.next_inst = 0;
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;
}
}
else
{
// help peephole optimizations
js.next_inst = ops[i + 1].inst;
js.next_compilerPC = ops[i + 1].address;
}
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock >= 32)
{
js.fifoBytesThisBlock -= 32;
ABI_CallFunction(thunks.ProtectFunction((void *)&GPFifo::CheckGatherPipe, 0));
}
// If starting from the breakpointed instruction, we don't break.
if (em_address != ops[i].address && BreakPoints::IsAddressBreakPoint(ops[i].address))
{
}
if (!ops[i].skip)
PPCTables::CompileInstruction(ops[i].inst);
gpr.SanityCheck();
fpr.SanityCheck();
if (js.cancel)
break;
}
b->flags = js.block_flags;
b->codeSize = (u32)(GetCodePtr() - normalEntry);
b->originalSize = size;
return normalEntry;
// get start tic
PROFILER_QUERY_PERFORMACE_COUNTER(&b->ticStart);
}
//#if defined(_DEBUG) || defined(DEBUGFAST)
// should help logged stacktraces become more accurate
MOV(32, M(&PC), Imm32(js.blockStart));
//#endif
// if (em_address == 0x801e4188)
// INT3();
//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(em_address);
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)
{
// WARNING - cmp->branch merging will screw this up.
js.isLastInstruction = true;
js.next_inst = 0;
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;
}
}
else
{
// help peephole optimizations
js.next_inst = ops[i + 1].inst;
js.next_compilerPC = ops[i + 1].address;
}
if (jo.optimizeGatherPipe && js.fifoBytesThisBlock >= 32)
{
js.fifoBytesThisBlock -= 32;
ABI_CallFunction(thunks.ProtectFunction((void *)&GPFifo::CheckGatherPipe, 0));
}
// If starting from the breakpointed instruction, we don't break.
if (em_address != ops[i].address && BreakPoints::IsAddressBreakPoint(ops[i].address))
{
}
if (!ops[i].skip)
PPCTables::CompileInstruction(ops[i].inst);
gpr.SanityCheck();
fpr.SanityCheck();
if (js.cancel)
break;
}
b->flags = js.block_flags;
b->codeSize = (u32)(GetCodePtr() - normalEntry);
b->originalSize = size;
return normalEntry;
}

View File

@ -40,250 +40,250 @@
using namespace Gen;
void Jit64::sc(UGeckoInstruction inst)
void Jit64::sc(UGeckoInstruction inst)
{
if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITBranchOff)
{Default(inst); return;} // turn off from debugger
INSTRUCTION_START;
gpr.Flush(FLUSH_ALL);
fpr.Flush(FLUSH_ALL);
WriteExceptionExit(EXCEPTION_SYSCALL);
}
void Jit64::rfi(UGeckoInstruction inst)
{
if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITBranchOff)
{Default(inst); return;} // turn off from debugger
INSTRUCTION_START;
gpr.Flush(FLUSH_ALL);
fpr.Flush(FLUSH_ALL);
// See Interpreter rfi for details
const u32 mask = 0x87C0FFFF;
// MSR = (MSR & ~mask) | (SRR1 & mask);
MOV(32, R(EAX), M(&MSR));
MOV(32, R(ECX), M(&SRR1));
AND(32, R(EAX), Imm32(~mask));
AND(32, R(ECX), Imm32(mask));
OR(32, R(EAX), R(ECX));
// MSR &= 0xFFFDFFFF; //TODO: VERIFY
AND(32, R(EAX), Imm32(0xFFFDFFFF));
MOV(32, M(&MSR), R(EAX));
// NPC = SRR0;
MOV(32, R(EAX), M(&SRR0));
WriteRfiExitDestInEAX();
}
void Jit64::bx(UGeckoInstruction inst)
{
if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITBranchOff)
{Default(inst); return;} // turn off from debugger
INSTRUCTION_START;
if (inst.LK)
MOV(32, M(&LR), Imm32(js.compilerPC + 4));
gpr.Flush(FLUSH_ALL);
fpr.Flush(FLUSH_ALL);
if (js.isLastInstruction)
{
if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITBranchOff)
{Default(inst); return;} // turn off from debugger
INSTRUCTION_START;
gpr.Flush(FLUSH_ALL);
fpr.Flush(FLUSH_ALL);
WriteExceptionExit(EXCEPTION_SYSCALL);
}
void Jit64::rfi(UGeckoInstruction inst)
{
if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITBranchOff)
{Default(inst); return;} // turn off from debugger
INSTRUCTION_START;
gpr.Flush(FLUSH_ALL);
fpr.Flush(FLUSH_ALL);
// See Interpreter rfi for details
const u32 mask = 0x87C0FFFF;
// MSR = (MSR & ~mask) | (SRR1 & mask);
MOV(32, R(EAX), M(&MSR));
MOV(32, R(ECX), M(&SRR1));
AND(32, R(EAX), Imm32(~mask));
AND(32, R(ECX), Imm32(mask));
OR(32, R(EAX), R(ECX));
// MSR &= 0xFFFDFFFF; //TODO: VERIFY
AND(32, R(EAX), Imm32(0xFFFDFFFF));
MOV(32, M(&MSR), R(EAX));
// NPC = SRR0;
MOV(32, R(EAX), M(&SRR0));
WriteRfiExitDestInEAX();
}
void Jit64::bx(UGeckoInstruction inst)
{
if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITBranchOff)
{Default(inst); return;} // turn off from debugger
INSTRUCTION_START;
if (inst.LK)
MOV(32, M(&LR), Imm32(js.compilerPC + 4));
gpr.Flush(FLUSH_ALL);
fpr.Flush(FLUSH_ALL);
if (js.isLastInstruction)
{
u32 destination;
if (inst.AA)
destination = SignExt26(inst.LI << 2);
else
destination = js.compilerPC + SignExt26(inst.LI << 2);
#ifdef ACID_TEST
if (inst.LK)
AND(32, M(&CR), Imm32(~(0xFF000000)));
#endif
if (destination == js.compilerPC)
{
//PanicAlert("Idle loop detected at %08x", destination);
// CALL(ProtectFunction(&CoreTiming::Idle, 0));
// JMP(Asm::testExceptions, true);
// make idle loops go faster
js.downcountAmount += 8;
}
WriteExit(destination, 0);
}
else {
// TODO: investigate the good old method of merging blocks here.
PanicAlert("bx not last instruction of block"); // this should not happen
}
}
// TODO - optimize to hell and beyond
// TODO - make nice easy to optimize special cases for the most common
// variants of this instruction.
void Jit64::bcx(UGeckoInstruction inst)
{
if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITBranchOff)
{Default(inst); return;} // turn off from debugger
INSTRUCTION_START;
// USES_CR
_assert_msg_(DYNA_REC, js.isLastInstruction, "bcx not last instruction of block");
gpr.Flush(FLUSH_ALL);
fpr.Flush(FLUSH_ALL);
CCFlags branch = CC_Z;
//const bool only_counter_check = (inst.BO & 16) ? true : false;
//const bool only_condition_check = (inst.BO & 4) ? true : false;
//if (only_condition_check && only_counter_check)
// PanicAlert("Bizarre bcx encountered. Likely bad or corrupt code.");
bool doFullTest = (inst.BO & 16) == 0 && (inst.BO & 4) == 0;
bool ctrDecremented = false;
if ((inst.BO & 16) == 0) // Test a CR bit
{
TEST(8, M(&PowerPC::ppcState.cr_fast[inst.BI >> 2]), Imm8(8 >> (inst.BI & 3)));
if (inst.BO & 8) // Conditional branch
branch = CC_NZ;
else
branch = CC_Z;
if (doFullTest)
SETcc(branch, R(EAX));
}
else
{
if (doFullTest)
MOV(32, R(EAX), Imm32(1));
}
if ((inst.BO & 4) == 0) // Decrement and test CTR
{
// Decrement CTR
SUB(32, M(&CTR), Imm8(1));
ctrDecremented = true;
// Test whether to branch if CTR is zero or not
if (inst.BO & 2)
branch = CC_Z;
else
branch = CC_NZ;
if (doFullTest)
SETcc(branch, R(ECX));
}
else
{
if (doFullTest)
MOV(32, R(ECX), Imm32(1));
}
if (doFullTest)
{
TEST(32, R(EAX), R(ECX));
branch = CC_Z;
}
else
{
if (branch == CC_Z)
branch = CC_NZ;
else
branch = CC_Z;
}
if (!ctrDecremented && (inst.BO & BO_DONT_DECREMENT_FLAG) == 0)
{
SUB(32, M(&CTR), Imm8(1));
}
FixupBranch skip;
if (inst.BO != 20)
{
skip = J_CC(branch);
}
u32 destination;
if (inst.LK)
MOV(32, M(&LR), Imm32(js.compilerPC + 4));
if(inst.AA)
destination = SignExt16(inst.BD << 2);
if (inst.AA)
destination = SignExt26(inst.LI << 2);
else
destination = js.compilerPC + SignExt16(inst.BD << 2);
WriteExit(destination, 0);
if (inst.BO != 20)
{
SetJumpTarget(skip);
WriteExit(js.compilerPC + 4, 1);
}
}
void Jit64::bcctrx(UGeckoInstruction inst)
{
if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITBranchOff)
{Default(inst); return;} // turn off from debugger
INSTRUCTION_START;
gpr.Flush(FLUSH_ALL);
fpr.Flush(FLUSH_ALL);
// bool fastway = true;
if ((inst.BO & 16) == 0)
{
PanicAlert("Bizarro bcctrx %08x, not supported.", inst.hex);
_assert_msg_(DYNA_REC, 0, "Bizarro bcctrx");
/*
fastway = false;
MOV(32, M(&PC), Imm32(js.compilerPC+4));
MOV(32, R(EAX), M(&CR));
XOR(32, R(ECX), R(ECX));
AND(32, R(EAX), Imm32(0x80000000 >> inst.BI));
CCFlags branch;
if(inst.BO & 8)
branch = CC_NZ;
else
branch = CC_Z;
*/
// TODO(ector): Why is this commented out?
//SETcc(branch, R(ECX));
// check for EBX
//TEST(32, R(ECX), R(ECX));
//linkEnd = J_CC(branch);
}
// NPC = CTR & 0xfffffffc;
MOV(32, R(EAX), M(&CTR));
if (inst.LK)
MOV(32, M(&LR), Imm32(js.compilerPC + 4)); // LR = PC + 4;
AND(32, R(EAX), Imm32(0xFFFFFFFC));
WriteExitDestInEAX(0);
}
void Jit64::bclrx(UGeckoInstruction inst)
{
if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITBranchOff)
{Default(inst); return;} // turn off from debugger
INSTRUCTION_START;
gpr.Flush(FLUSH_ALL);
fpr.Flush(FLUSH_ALL);
//Special case BLR
if (inst.hex == 0x4e800020)
{
//CDynaRegCache::Flush();
// This below line can be used to prove that blr "eats flags" in practice.
// This observation will let us do a lot of fun observations.
destination = js.compilerPC + SignExt26(inst.LI << 2);
#ifdef ACID_TEST
if (inst.LK)
AND(32, M(&CR), Imm32(~(0xFF000000)));
#endif
MOV(32, R(EAX), M(&LR));
MOV(32, M(&PC), R(EAX));
WriteExitDestInEAX(0);
return;
if (destination == js.compilerPC)
{
//PanicAlert("Idle loop detected at %08x", destination);
// CALL(ProtectFunction(&CoreTiming::Idle, 0));
// JMP(Asm::testExceptions, true);
// make idle loops go faster
js.downcountAmount += 8;
}
// Call interpreter
Default(inst);
MOV(32, R(EAX), M(&NPC));
WriteExitDestInEAX(0);
WriteExit(destination, 0);
}
else {
// TODO: investigate the good old method of merging blocks here.
PanicAlert("bx not last instruction of block"); // this should not happen
}
}
// TODO - optimize to hell and beyond
// TODO - make nice easy to optimize special cases for the most common
// variants of this instruction.
void Jit64::bcx(UGeckoInstruction inst)
{
if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITBranchOff)
{Default(inst); return;} // turn off from debugger
INSTRUCTION_START;
// USES_CR
_assert_msg_(DYNA_REC, js.isLastInstruction, "bcx not last instruction of block");
gpr.Flush(FLUSH_ALL);
fpr.Flush(FLUSH_ALL);
CCFlags branch = CC_Z;
//const bool only_counter_check = (inst.BO & 16) ? true : false;
//const bool only_condition_check = (inst.BO & 4) ? true : false;
//if (only_condition_check && only_counter_check)
// PanicAlert("Bizarre bcx encountered. Likely bad or corrupt code.");
bool doFullTest = (inst.BO & 16) == 0 && (inst.BO & 4) == 0;
bool ctrDecremented = false;
if ((inst.BO & 16) == 0) // Test a CR bit
{
TEST(8, M(&PowerPC::ppcState.cr_fast[inst.BI >> 2]), Imm8(8 >> (inst.BI & 3)));
if (inst.BO & 8) // Conditional branch
branch = CC_NZ;
else
branch = CC_Z;
if (doFullTest)
SETcc(branch, R(EAX));
}
else
{
if (doFullTest)
MOV(32, R(EAX), Imm32(1));
}
if ((inst.BO & 4) == 0) // Decrement and test CTR
{
// Decrement CTR
SUB(32, M(&CTR), Imm8(1));
ctrDecremented = true;
// Test whether to branch if CTR is zero or not
if (inst.BO & 2)
branch = CC_Z;
else
branch = CC_NZ;
if (doFullTest)
SETcc(branch, R(ECX));
}
else
{
if (doFullTest)
MOV(32, R(ECX), Imm32(1));
}
if (doFullTest)
{
TEST(32, R(EAX), R(ECX));
branch = CC_Z;
}
else
{
if (branch == CC_Z)
branch = CC_NZ;
else
branch = CC_Z;
}
if (!ctrDecremented && (inst.BO & BO_DONT_DECREMENT_FLAG) == 0)
{
SUB(32, M(&CTR), Imm8(1));
}
FixupBranch skip;
if (inst.BO != 20)
{
skip = J_CC(branch);
}
u32 destination;
if (inst.LK)
MOV(32, M(&LR), Imm32(js.compilerPC + 4));
if(inst.AA)
destination = SignExt16(inst.BD << 2);
else
destination = js.compilerPC + SignExt16(inst.BD << 2);
WriteExit(destination, 0);
if (inst.BO != 20)
{
SetJumpTarget(skip);
WriteExit(js.compilerPC + 4, 1);
}
}
void Jit64::bcctrx(UGeckoInstruction inst)
{
if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITBranchOff)
{Default(inst); return;} // turn off from debugger
INSTRUCTION_START;
gpr.Flush(FLUSH_ALL);
fpr.Flush(FLUSH_ALL);
// bool fastway = true;
if ((inst.BO & 16) == 0)
{
PanicAlert("Bizarro bcctrx %08x, not supported.", inst.hex);
_assert_msg_(DYNA_REC, 0, "Bizarro bcctrx");
/*
fastway = false;
MOV(32, M(&PC), Imm32(js.compilerPC+4));
MOV(32, R(EAX), M(&CR));
XOR(32, R(ECX), R(ECX));
AND(32, R(EAX), Imm32(0x80000000 >> inst.BI));
CCFlags branch;
if(inst.BO & 8)
branch = CC_NZ;
else
branch = CC_Z;
*/
// TODO(ector): Why is this commented out?
//SETcc(branch, R(ECX));
// check for EBX
//TEST(32, R(ECX), R(ECX));
//linkEnd = J_CC(branch);
}
// NPC = CTR & 0xfffffffc;
MOV(32, R(EAX), M(&CTR));
if (inst.LK)
MOV(32, M(&LR), Imm32(js.compilerPC + 4)); // LR = PC + 4;
AND(32, R(EAX), Imm32(0xFFFFFFFC));
WriteExitDestInEAX(0);
}
void Jit64::bclrx(UGeckoInstruction inst)
{
if(Core::g_CoreStartupParameter.bJITOff || Core::g_CoreStartupParameter.bJITBranchOff)
{Default(inst); return;} // turn off from debugger
INSTRUCTION_START;
gpr.Flush(FLUSH_ALL);
fpr.Flush(FLUSH_ALL);
//Special case BLR
if (inst.hex == 0x4e800020)
{
//CDynaRegCache::Flush();
// This below line can be used to prove that blr "eats flags" in practice.
// This observation will let us do a lot of fun observations.
#ifdef ACID_TEST
AND(32, M(&CR), Imm32(~(0xFF000000)));
#endif
MOV(32, R(EAX), M(&LR));
MOV(32, M(&PC), R(EAX));
WriteExitDestInEAX(0);
return;
}
// Call interpreter
Default(inst);
MOV(32, R(EAX), M(&NPC));
WriteExitDestInEAX(0);
}

View File

@ -196,6 +196,8 @@
MOV(8, M(&PowerPC::ppcState.cr_fast[crf]), Imm8(0x8)); // _x86Reg < 0
SetJumpTarget(continue1);
SetJumpTarget(continue2);
// TODO: If we ever care about SO, borrow a trick from
// http://maws.mameworld.info/maws/mamesrc/src/emu/cpu/powerpc/drc_ops.c : bt, adc
} else {
int test_bit = 8 >> (js.next_inst.BI & 3);
bool condition = (js.next_inst.BO & 8) ? false : true;

View File

@ -215,25 +215,39 @@ const float m_65535 = 65535.0f;
#define QUANTIZE_OVERFLOW_SAFE
// according to Intel Docs CVTPS2DQ writes 0x80000000 if the source floating point value is out of int32 range
// while it's OK for large negatives, it isn't for positives
// I don't know whether the overflow actually happens in any games
// but it potentially can cause problems, so we need some clamping
// TODO(ector): Improve 64-bit version
static void WriteDual32(u64 value, u32 address)
{
Memory::Write_U32((u32)(value >> 32), address);
Memory::Write_U32((u32)value, address + 4);
}
void AsmRoutineManager::GenQuantizedStores() {
const u8* storePairedIllegal = AlignCode4();
UD2();
const u8* storePairedFloat = AlignCode4();
// IN: value = XMM0, two singles in bottom. PPC address = ECX.
#ifdef _M_X64
MOVQ_xmm(R(RAX), XMM0);
ROL(64, R(RAX), Imm8(32));
// INT3();
MOVQ_xmm(M(&psTemp[0]), XMM0);
MOV(64, R(RAX), M(&psTemp[0]));
//INT3();
//MOVQ_xmm(R(RAX), XMM0);
//INT3();
ROL(64, R(RAX), Imm8(32)); // Swap the two - the big BSWAP will unswap.
TEST(32, R(ECX), Imm32(0x0C000000));
FixupBranch argh = J_CC(CC_NZ);
BSWAP(64, RAX);
MOV(64, MComplex(RBX, RCX, 1, 0), R(RAX));
MOV(64, MComplex(RBX, RCX, SCALE_1, 0), R(RAX));
FixupBranch arg2 = J();
SetJumpTarget(argh);
ABI_CallFunctionRR(thunks.ProtectFunction((void *)&Memory::Write_U64, 2), RAX, RCX);
ABI_CallFunctionRR(thunks.ProtectFunction((void *)&WriteDual32, 2), RAX, RCX);
SetJumpTarget(arg2);
#else
MOVQ_xmm(M(&psTemp[0]), XMM0);
@ -258,11 +272,12 @@ void AsmRoutineManager::GenQuantizedStores() {
RET();
const u8* storePairedU8 = AlignCode4();
INT3();
SHR(32, R(EAX), Imm8(6));
MOVSS(XMM1, MDisp(EAX, (u32)(u64)m_quantizeTableS));
PUNPCKLDQ(XMM1, R(XMM1));
MULPS(XMM0, R(XMM1));
#ifdef QUANTIZE_OVERFLOW_SAFE
#ifdef QUANTIZE_OVERFLOW_SAFE
MOVSS(XMM1, M((void *)&m_65535));
PUNPCKLDQ(XMM1, R(XMM1));
MINPS(XMM0, R(XMM1));
@ -280,6 +295,7 @@ void AsmRoutineManager::GenQuantizedStores() {
RET();
const u8* storePairedS8 = AlignCode4();
INT3();
SHR(32, R(EAX), Imm8(6));
MOVSS(XMM1, MDisp(EAX, (u32)(u64)m_quantizeTableS));
PUNPCKLDQ(XMM1, R(XMM1));
@ -302,6 +318,7 @@ void AsmRoutineManager::GenQuantizedStores() {
RET();
const u8* storePairedU16 = AlignCode4();
INT3();
SHR(32, R(EAX), Imm8(6));
MOVSS(XMM1, MDisp(EAX, (u32)(u64)m_quantizeTableS));
PUNPCKLDQ(XMM1, R(XMM1));
@ -333,6 +350,7 @@ void AsmRoutineManager::GenQuantizedStores() {
RET();
const u8* storePairedS16 = AlignCode4();
INT3();
SHR(32, R(EAX), Imm8(6));
MOVSS(XMM1, MDisp(EAX, (u32)(u64)m_quantizeTableS));
PUNPCKLDQ(XMM1, R(XMM1));

View File

@ -444,7 +444,7 @@ static GekkoOPTemplate table31_2[] =
static GekkoOPTemplate table59[] =
{
{18, Interpreter::fdivsx, &Jit64::fp_arith_s, {"fdivsx", OPTYPE_FPU, FL_RC_BIT_F, 16}},
{18, Interpreter::fdivsx, &Jit64::Default, /*TODO*/ {"fdivsx", OPTYPE_FPU, FL_RC_BIT_F, 16}},
{20, Interpreter::fsubsx, &Jit64::fp_arith_s, {"fsubsx", OPTYPE_FPU, FL_RC_BIT_F}},
{21, Interpreter::faddsx, &Jit64::fp_arith_s, {"faddsx", OPTYPE_FPU, FL_RC_BIT_F}},
// {22, Interpreter::fsqrtsx, &Jit64::Default, {"fsqrtsx", OPTYPE_FPU, FL_RC_BIT_F}}, // Not implemented on gekko
@ -687,7 +687,7 @@ void InitTables()
}
#define OPLOG
#define OP_TO_LOG "mcrfs"
#define OP_TO_LOG "mffs"
#ifdef OPLOG
namespace {

View File

@ -357,7 +357,7 @@ void OnIdleIL()
int PPCFPClass(double dvalue)
{
#ifdef _WIN32
/* // win32-only reference implementation, to compare to:
switch (_fpclass(dvalue))
{
case _FPCLASS_SNAN:
@ -371,9 +371,9 @@ int PPCFPClass(double dvalue)
case _FPCLASS_PN: return 0x4;
case _FPCLASS_PINF: return 0x5;
default: return 0x4;
}
#else
// TODO: Make sure the below is equivalent to the above - then switch win32 implementation to it.
}*/
// TODO: Optimize the below to be as fast as possible.
union {
double d;
u64 i;
@ -395,7 +395,7 @@ int PPCFPClass(double dvalue)
return 0x9;
} else {
// OK let's dissect this thing.
int sign = (int)(value.i & 0x8000000000000000ULL) ? 1 : 0;
int sign = value.i >> 63;
int exp = (int)((value.i >> 52) & 0x7FF);
if (exp >= 1 && exp <= 2046) {
// Nice normalized number.
@ -419,12 +419,22 @@ int PPCFPClass(double dvalue)
}
return 0x4;
#endif
}
} // namespace
// FPSCR update functions
void UpdateFPRF(double dvalue)
{
FPSCR.FPRF = PowerPC::PPCFPClass(dvalue);
}
void UpdateFEX() {
FPSCR.FEX = (FPSCR.XX & FPSCR.XE) |
(FPSCR.ZX & FPSCR.ZE) |
(FPSCR.UX & FPSCR.UE) |
(FPSCR.OX & FPSCR.OE) |
(FPSCR.VX & FPSCR.VE);
}

View File

@ -91,6 +91,8 @@ volatile CPUState *GetStatePtr(); // this oddity is here instead of an extern d
void CompactCR();
void ExpandCR();
int PPCFPClass(double dvalue);
void OnIdle(u32 _uThreadAddr);
void OnIdleIL();

View File

@ -96,7 +96,7 @@
AdditionalLibraryDirectories="..\..\..\Externals\wxWidgets\lib;..\..\..\Externals\LZO\win32\$(ConfigurationName)"
IgnoreAllDefaultLibraries="false"
IgnoreDefaultLibraryNames="msvcrt"
GenerateDebugInformation="false"
GenerateDebugInformation="true"
ProgramDatabaseFile="$(PlatformName)\$(ConfigurationName)\$(TargetName).pdb"
GenerateMapFile="false"
MapFileName="$(TargetDir)linkermap.map"
@ -768,7 +768,7 @@
AdditionalLibraryDirectories="..\..\..\Externals\wxWidgets\lib;..\..\..\Externals\LZO\win32\$(ConfigurationName)"
IgnoreAllDefaultLibraries="false"
IgnoreDefaultLibraryNames="msvcrt"
GenerateDebugInformation="false"
GenerateDebugInformation="true"
ProgramDatabaseFile="$(PlatformName)\$(ConfigurationName)\$(TargetName).pdb"
GenerateMapFile="false"
MapFileName="$(TargetDir)linkermap.map"

View File

@ -20,7 +20,7 @@ EventHandler::~EventHandler() {
}
EventHandler *EventHandler::GetInstance() {
fprintf(stderr, "handler instance %p\n", m_Instance);
// fprintf(stderr, "handler instance %p\n", m_Instance);
if (! m_Instance)
m_Instance = new EventHandler();
@ -30,13 +30,13 @@ EventHandler *EventHandler::GetInstance() {
void EventHandler::Destroy() {
if (m_Instance)
delete m_Instance;
fprintf(stderr, "deleting instance %p\n", m_Instance);
// fprintf(stderr, "deleting instance %p\n", m_Instance);
m_Instance = 0;
}
bool EventHandler::RegisterEventListener(listenFuncPtr func, Keys key) {
if (key.inputType == KeyboardInput) {
fprintf(stderr, "Registering %d:%d %p %p \n", key.keyCode, key.mods, func, this);
// fprintf(stderr, "Registering %d:%d %p %p \n", key.keyCode, key.mods, func, this);
if (key.keyCode == sf::Key::Count || key.mods >= NUMMODS ||
key.keyCode >= NUMKEYS)
return false;

View File

@ -3,7 +3,9 @@ Microsoft Visual Studio Solution File, Format Version 10.00
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "Core", "Core\Core\Core.vcproj", "{F0B874CB-4476-4199-9315-8343D05AE684}"
ProjectSection(ProjectDependencies) = postProject
{C7E5D50A-2916-464B-86A7-E10B3CC88ADA} = {C7E5D50A-2916-464B-86A7-E10B3CC88ADA}
{33546D62-7F34-4EA6-A88E-D538B36E16BF} = {33546D62-7F34-4EA6-A88E-D538B36E16BF}
{11F55366-12EC-4C44-A8CB-1D4E315D61ED} = {11F55366-12EC-4C44-A8CB-1D4E315D61ED}
{3E03C179-8251-46E4-81F4-466F114BAC63} = {3E03C179-8251-46E4-81F4-466F114BAC63}
{0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E} = {0E231FB1-F3C9-4724-ACCB-DE8BCB3C089E}
{29C2ABC1-ADA5-42CD-A5FC-96022D52A510} = {29C2ABC1-ADA5-42CD-A5FC-96022D52A510}
{1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE} = {1C8436C9-DBAF-42BE-83BC-CF3EC9175ABE}
@ -185,6 +187,12 @@ Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SOIL", "..\Externals\SOIL\S
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "SFML_Network", "..\Externals\SFML\build\vc2008\sfml-network.vcproj", "{823DDC98-42D5-4A38-88CF-9DC06C788AE4}"
EndProject
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "UnitTests", "UnitTests\UnitTests.vcproj", "{40C636FA-B5BF-4D67-ABC8-376B524A7551}"
ProjectSection(ProjectDependencies) = postProject
{F0B874CB-4476-4199-9315-8343D05AE684} = {F0B874CB-4476-4199-9315-8343D05AE684}
{C573CAF7-EE6A-458E-8049-16C0BF34C2E9} = {C573CAF7-EE6A-458E-8049-16C0BF34C2E9}
EndProjectSection
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Win32 = Debug|Win32
@ -623,6 +631,19 @@ Global
{823DDC98-42D5-4A38-88CF-9DC06C788AE4}.Release|Win32.Build.0 = Release|Win32
{823DDC98-42D5-4A38-88CF-9DC06C788AE4}.Release|x64.ActiveCfg = Release|x64
{823DDC98-42D5-4A38-88CF-9DC06C788AE4}.Release|x64.Build.0 = Release|x64
{40C636FA-B5BF-4D67-ABC8-376B524A7551}.Debug|Win32.ActiveCfg = Debug|Win32
{40C636FA-B5BF-4D67-ABC8-376B524A7551}.Debug|Win32.Build.0 = Debug|Win32
{40C636FA-B5BF-4D67-ABC8-376B524A7551}.Debug|x64.ActiveCfg = Debug|Win32
{40C636FA-B5BF-4D67-ABC8-376B524A7551}.DebugFast|Win32.ActiveCfg = Debug|Win32
{40C636FA-B5BF-4D67-ABC8-376B524A7551}.DebugFast|Win32.Build.0 = Debug|Win32
{40C636FA-B5BF-4D67-ABC8-376B524A7551}.DebugFast|x64.ActiveCfg = Debug|Win32
{40C636FA-B5BF-4D67-ABC8-376B524A7551}.Release_JITIL|Win32.ActiveCfg = Release|Win32
{40C636FA-B5BF-4D67-ABC8-376B524A7551}.Release_JITIL|Win32.Build.0 = Release|Win32
{40C636FA-B5BF-4D67-ABC8-376B524A7551}.Release_JITIL|x64.ActiveCfg = Release|Win32
{40C636FA-B5BF-4D67-ABC8-376B524A7551}.Release|Win32.ActiveCfg = Release|Win32
{40C636FA-B5BF-4D67-ABC8-376B524A7551}.Release|Win32.Build.0 = Release|Win32
{40C636FA-B5BF-4D67-ABC8-376B524A7551}.Release|x64.ActiveCfg = Release|x64
{40C636FA-B5BF-4D67-ABC8-376B524A7551}.Release|x64.Build.0 = Release|x64
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE

View File

@ -144,7 +144,7 @@ bool OpenGL_ReportFBOError(const char *function, const char *file, int line);
#if defined(_DEBUG) || defined(DEBUGFAST)
#define GL_REPORT_ERRORD() OpenGL_ReportGLError(__FUNCTION__, __FILE__, __LINE__)
#else
#define GL_REPORT_ERRORD() GL_NO_ERROR
#define GL_REPORT_ERRORD()
#endif
#endif // GLTEST ??

View File

@ -81,8 +81,13 @@ void DrawMessages()
}
}
GL_REPORT_ERRORD();
if (enabled)
glEnable(GL_BLEND);
GL_REPORT_ERRORD();
}
} // namespace

View File

@ -1122,6 +1122,7 @@ void Renderer::SwapBuffers()
fpscount = 0;
}
// ---------------------------------------------------------------------
GL_REPORT_ERRORD();
for (int i = 0; i < 8; i++) {
glActiveTexture(GL_TEXTURE0 + i);
@ -1132,8 +1133,12 @@ void Renderer::SwapBuffers()
DrawDebugText();
GL_REPORT_ERRORD();
OSD::DrawMessages();
GL_REPORT_ERRORD();
#if defined(DVPROFILE)
if (g_bWriteProfile) {
//g_bWriteProfile = 0;
@ -1148,6 +1153,9 @@ void Renderer::SwapBuffers()
#endif
// Copy the rendered frame to the real window
OpenGL_SwapBuffers();
GL_REPORT_ERRORD();
// Clear framebuffer
glClearColor(0, 0, 0, 0);
glClear(GL_COLOR_BUFFER_BIT);
@ -1359,6 +1367,7 @@ void Renderer::RenderText(const char* pstr, int left, int top, u32 color)
left * 2.0f / (float)nBackbufferWidth - 1,
1 - top * 2.0f / (float)nBackbufferHeight,
0, nBackbufferWidth, nBackbufferHeight);
GL_REPORT_ERRORD();
}

View File

@ -16,20 +16,7 @@
// http://code.google.com/p/dolphin-emu/
#include "Globals.h"
#ifdef _WIN32
#include <windows.h>
#endif
#if defined(__APPLE__)
#include <OpenGL/gl.h>
#else
#include <GL/gl.h>
#endif
#include "GLUtil.h"
#include <string.h>
@ -154,13 +141,28 @@ RasterFont::~RasterFont()
void RasterFont::printString(const char *s, double x, double y, double z)
{
int length = strlen(s);
if (!length)
return;
// Sanitize string to avoid GL errors.
char *s2 = new char[length + 1];
strcpy(s2, s);
for (int i = 0; i < length; i++) {
if (s2[i] < 32 || s2[i] > 126)
s2[i] = '!';
}
// go to the right spot
glRasterPos3d(x, y, z);
GL_REPORT_ERRORD();
glPushAttrib (GL_LIST_BIT);
glListBase(fontOffset);
glCallLists((GLsizei)strlen(s), GL_UNSIGNED_BYTE, (GLubyte *) s);
glPopAttrib ();
glCallLists((GLsizei)strlen(s2), GL_UNSIGNED_BYTE, (GLubyte *) s2);
GL_REPORT_ERRORD();
glPopAttrib();
GL_REPORT_ERRORD();
}
void RasterFont::printCenteredString(const char *s, double y, int screen_width, double z)

View File

@ -0,0 +1,89 @@
// Copyright (C) 2003-2009 Dolphin Project.
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, version 2.0.
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License 2.0 for more details.
// A copy of the GPL 2.0 should have been included with the program.
// If not, 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 <iostream>
#include "PowerPC/PowerPC.h"
#include "HW/SI_DeviceGCController.h"
using namespace std;
int fail_count = 0;
#define EXPECT_EQ(a, b) \
if ((a) != (b)) { \
cout << "FAIL: " << #a << " %s is not equal to " << #b << endl; \
cout << "Actual: " << a << endl << "Expected: " << b << endl; \
fail_count++; \
}
void CoreTests()
{
// Tests that our fp classifier is correct.
EXPECT_EQ(PowerPC::PPCFPClass(1.0), 0x4);
EXPECT_EQ(PowerPC::PPCFPClass(-1.0), 0x8);
EXPECT_EQ(PowerPC::PPCFPClass(1235223.0), 0x4);
EXPECT_EQ(PowerPC::PPCFPClass(-126323521.0), 0x8);
EXPECT_EQ(PowerPC::PPCFPClass(1.0E-308), 0x14);
EXPECT_EQ(PowerPC::PPCFPClass(-1.0E-308), 0x18);
EXPECT_EQ(PowerPC::PPCFPClass(0.0), 0x2);
EXPECT_EQ(PowerPC::PPCFPClass(-0.0), 0x12);
EXPECT_EQ(PowerPC::PPCFPClass(HUGE_VAL), 0x5); // weird #define for infinity
EXPECT_EQ(PowerPC::PPCFPClass(-HUGE_VAL), 0x9);
EXPECT_EQ(PowerPC::PPCFPClass(sqrt(-1.0)), 0x11); // SNAN
}
int main(int argc, _TCHAR* argv[])
{
CoreTests();
if (fail_count == 0)
{
printf("All tests passed.\n");
}
return 0;
}
// Pretend that we are a host so we can link to core.... urgh.
//==============================================================
void Host_UpdateMainFrame(){}
void Host_UpdateDisasmDialog(){}
void Host_UpdateLogDisplay(){}
void Host_UpdateMemoryView(){}
void Host_NotifyMapLoaded(){}
void Host_UpdateBreakPointView(){}
void Host_SetDebugMode(bool enable){}
void Host_SetWaitCursor(bool enable){}
void Host_UpdateStatusBar(const char* _pText, int Filed = 0){}
#ifdef SETUP_TIMER_WAITING
void Host_UpdateGUI(){}
#endif
void Host_SysMessage(const char *fmt, ...){}
void Host_SetWiiMoteConnectionState(int _State){}
void Host_UpdateLeds(int bits){}
void Host_UpdateSpeakerStatus(int index, int bits){}
void Host_UpdateStatus(){}
int CSIDevice_GCController::GetNetInput(u8 numPAD, SPADStatus PadStatus, u32 *PADStatus)
{
return 0;
}

View File

@ -0,0 +1,329 @@
<?xml version="1.0" encoding="Windows-1252"?>
<VisualStudioProject
ProjectType="Visual C++"
Version="9.00"
Name="UnitTests"
ProjectGUID="{40C636FA-B5BF-4D67-ABC8-376B524A7551}"
RootNamespace="UnitTests"
Keyword="Win32Proj"
TargetFrameworkVersion="196613"
>
<Platforms>
<Platform
Name="Win32"
/>
<Platform
Name="x64"
/>
</Platforms>
<ToolFiles>
</ToolFiles>
<Configurations>
<Configuration
Name="Debug|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="../Core/Core/Src;../Core/Common/Src;../PluginSpecs;../Core/InputCommon/Src"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL=0"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="0"
WarningLevel="3"
DebugInformationFormat="4"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|Win32"
OutputDirectory="$(SolutionDir)$(ConfigurationName)"
IntermediateDirectory="$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
AdditionalIncludeDirectories="../Core/Core/Src;../Core/Common/Src;../PluginSpecs;../Core/InputCommon/Src"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL=0"
RuntimeLibrary="0"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="0"
WarningLevel="3"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="1"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Debug|x64"
OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)"
IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
TargetEnvironment="3"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="0"
AdditionalIncludeDirectories="../Core/Core/Src;../Core/Common/Src;../PluginSpecs;../Core/InputCommon/Src"
PreprocessorDefinitions="WIN32;_DEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL=0"
MinimalRebuild="true"
BasicRuntimeChecks="3"
RuntimeLibrary="1"
UsePrecompiledHeader="0"
WarningLevel="3"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="2"
GenerateDebugInformation="true"
SubSystem="1"
TargetMachine="17"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
<Configuration
Name="Release|x64"
OutputDirectory="$(SolutionDir)$(PlatformName)\$(ConfigurationName)"
IntermediateDirectory="$(PlatformName)\$(ConfigurationName)"
ConfigurationType="1"
CharacterSet="1"
WholeProgramOptimization="1"
>
<Tool
Name="VCPreBuildEventTool"
/>
<Tool
Name="VCCustomBuildTool"
/>
<Tool
Name="VCXMLDataGeneratorTool"
/>
<Tool
Name="VCWebServiceProxyGeneratorTool"
/>
<Tool
Name="VCMIDLTool"
TargetEnvironment="3"
/>
<Tool
Name="VCCLCompilerTool"
Optimization="2"
EnableIntrinsicFunctions="true"
AdditionalIncludeDirectories="../Core/Core/Src;../Core/Common/Src;../PluginSpecs;../Core/InputCommon/Src"
PreprocessorDefinitions="WIN32;NDEBUG;_CONSOLE;_CRT_SECURE_NO_DEPRECATE;_SECURE_SCL=0"
RuntimeLibrary="0"
EnableFunctionLevelLinking="true"
UsePrecompiledHeader="0"
WarningLevel="3"
DebugInformationFormat="3"
/>
<Tool
Name="VCManagedResourceCompilerTool"
/>
<Tool
Name="VCResourceCompilerTool"
/>
<Tool
Name="VCPreLinkEventTool"
/>
<Tool
Name="VCLinkerTool"
LinkIncremental="1"
GenerateDebugInformation="true"
SubSystem="1"
OptimizeReferences="2"
EnableCOMDATFolding="2"
TargetMachine="17"
/>
<Tool
Name="VCALinkTool"
/>
<Tool
Name="VCManifestTool"
/>
<Tool
Name="VCXDCMakeTool"
/>
<Tool
Name="VCBscMakeTool"
/>
<Tool
Name="VCFxCopTool"
/>
<Tool
Name="VCAppVerifierTool"
/>
<Tool
Name="VCPostBuildEventTool"
/>
</Configuration>
</Configurations>
<References>
</References>
<Files>
<File
RelativePath=".\UnitTests.cpp"
>
</File>
</Files>
<Globals>
</Globals>
</VisualStudioProject>