From a0086f6412cb5ccb6372213f8ce2528dae15dfe8 Mon Sep 17 00:00:00 2001 From: Sacha Date: Thu, 13 Nov 2014 00:09:51 +1000 Subject: [PATCH] Introduce a Fake JIT for generic builds. --- Common/FakeEmitter.h | 475 ++++++++++++++++++++++++++ Core/MIPS/JitCommon/JitBlockCache.cpp | 9 +- Core/MIPS/JitCommon/JitBlockCache.h | 6 +- Core/MIPS/JitCommon/JitCommon.h | 4 +- Core/MIPS/MIPS.cpp | 10 +- Core/MIPS/fake/FakeJit.cpp | 243 +++++++++++++ Core/MIPS/fake/FakeJit.h | 258 ++++++++++++++ Qt/Core.pro | 6 +- Qt/GPU.pro | 2 +- 9 files changed, 998 insertions(+), 15 deletions(-) create mode 100644 Common/FakeEmitter.h create mode 100644 Core/MIPS/fake/FakeJit.cpp create mode 100644 Core/MIPS/fake/FakeJit.h diff --git a/Common/FakeEmitter.h b/Common/FakeEmitter.h new file mode 100644 index 0000000000..a1660b9536 --- /dev/null +++ b/Common/FakeEmitter.h @@ -0,0 +1,475 @@ +// Copyright (C) 2003 Dolphin Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official SVN repository and contact information can be found at +// http://code.google.com/p/dolphin-emu/ + +// WARNING - THIS LIBRARY IS NOT THREAD SAFE!!! + +#ifndef _DOLPHIN_FAKE_CODEGEN_ +#define _DOLPHIN_FAKE_CODEGEN_ + +#include +#include + +#include "Common.h" +#include "MsgHandler.h" + +// TODO: Check if Pandora still needs signal.h/kill here. Symbian doesn't. + +// VCVT flags +#define TO_FLOAT 0 +#define TO_INT 1 << 0 +#define IS_SIGNED 1 << 1 +#define ROUND_TO_ZERO 1 << 2 + +namespace FakeGen +{ +enum FakeReg +{ + // GPRs + R0 = 0, R1, R2, R3, R4, R5, + R6, R7, R8, R9, R10, R11, + + // SPRs + // R13 - R15 are SP, LR, and PC. + // Almost always referred to by name instead of register number + R12 = 12, R13 = 13, R14 = 14, R15 = 15, + R_IP = 12, R_SP = 13, R_LR = 14, R_PC = 15, + + + // VFP single precision registers + S0, S1, S2, S3, S4, S5, S6, + S7, S8, S9, S10, S11, S12, S13, + S14, S15, S16, S17, S18, S19, S20, + S21, S22, S23, S24, S25, S26, S27, + S28, S29, S30, S31, + + // VFP Double Precision registers + D0, D1, D2, D3, D4, D5, D6, D7, + D8, D9, D10, D11, D12, D13, D14, D15, + D16, D17, D18, D19, D20, D21, D22, D23, + D24, D25, D26, D27, D28, D29, D30, D31, + + // ASIMD Quad-Word registers + Q0, Q1, Q2, Q3, Q4, Q5, Q6, Q7, + Q8, Q9, Q10, Q11, Q12, Q13, Q14, Q15, + + // for NEON VLD/VST instructions + REG_UPDATE = R13, + INVALID_REG = 0xFFFFFFFF +}; + +enum CCFlags +{ + CC_EQ = 0, // Equal + CC_NEQ, // Not equal + CC_CS, // Carry Set + CC_CC, // Carry Clear + CC_MI, // Minus (Negative) + CC_PL, // Plus + CC_VS, // Overflow + CC_VC, // No Overflow + CC_HI, // Unsigned higher + CC_LS, // Unsigned lower or same + CC_GE, // Signed greater than or equal + CC_LT, // Signed less than + CC_GT, // Signed greater than + CC_LE, // Signed less than or equal + CC_AL, // Always (unconditional) 14 + CC_HS = CC_CS, // Alias of CC_CS Unsigned higher or same + CC_LO = CC_CC, // Alias of CC_CC Unsigned lower +}; +const u32 NO_COND = 0xE0000000; + +enum ShiftType +{ + ST_LSL = 0, + ST_ASL = 0, + ST_LSR = 1, + ST_ASR = 2, + ST_ROR = 3, + ST_RRX = 4 +}; +enum IntegerSize +{ + I_I8 = 0, + I_I16, + I_I32, + I_I64 +}; + +enum +{ + NUMGPRs = 13, +}; + +class FakeXEmitter; + +enum OpType +{ + TYPE_IMM = 0, + TYPE_REG, + TYPE_IMMSREG, + TYPE_RSR, + TYPE_MEM +}; + +// This is no longer a proper operand2 class. Need to split up. +class Operand2 +{ + friend class FakeXEmitter; +protected: + u32 Value; + +private: + OpType Type; + + // IMM types + u8 Rotation; // Only for u8 values + + // Register types + u8 IndexOrShift; + ShiftType Shift; +public: + OpType GetType() + { + return Type; + } + Operand2() {} + Operand2(u32 imm, OpType type = TYPE_IMM) + { + Type = type; + Value = imm; + Rotation = 0; + } + + Operand2(FakeReg Reg) + { + Type = TYPE_REG; + Value = Reg; + Rotation = 0; + } + Operand2(u8 imm, u8 rotation) + { + Type = TYPE_IMM; + Value = imm; + Rotation = rotation; + } + Operand2(FakeReg base, ShiftType type, FakeReg shift) // RSR + { + Type = TYPE_RSR; + _assert_msg_(JIT, type != ST_RRX, "Invalid Operand2: RRX does not take a register shift amount"); + IndexOrShift = shift; + Shift = type; + Value = base; + } + + Operand2(FakeReg base, ShiftType type, u8 shift)// For IMM shifted register + { + if(shift == 32) shift = 0; + switch (type) + { + case ST_LSL: + _assert_msg_(JIT, shift < 32, "Invalid Operand2: LSL %u", shift); + break; + case ST_LSR: + _assert_msg_(JIT, shift <= 32, "Invalid Operand2: LSR %u", shift); + if (!shift) + type = ST_LSL; + if (shift == 32) + shift = 0; + break; + case ST_ASR: + _assert_msg_(JIT, shift < 32, "Invalid Operand2: ASR %u", shift); + if (!shift) + type = ST_LSL; + if (shift == 32) + shift = 0; + break; + case ST_ROR: + _assert_msg_(JIT, shift < 32, "Invalid Operand2: ROR %u", shift); + if (!shift) + type = ST_LSL; + break; + case ST_RRX: + _assert_msg_(JIT, shift == 0, "Invalid Operand2: RRX does not take an immediate shift amount"); + type = ST_ROR; + break; + } + IndexOrShift = shift; + Shift = type; + Value = base; + Type = TYPE_IMMSREG; + } + u32 GetData() + { + switch(Type) + { + case TYPE_IMM: + return Imm12Mod(); // This'll need to be changed later + case TYPE_REG: + return Rm(); + case TYPE_IMMSREG: + return IMMSR(); + case TYPE_RSR: + return RSR(); + default: + _assert_msg_(JIT, false, "GetData with Invalid Type"); + return 0; + } + } + u32 IMMSR() // IMM shifted register + { + _assert_msg_(JIT, Type == TYPE_IMMSREG, "IMMSR must be imm shifted register"); + return ((IndexOrShift & 0x1f) << 7 | (Shift << 5) | Value); + } + u32 RSR() // Register shifted register + { + _assert_msg_(JIT, Type == TYPE_RSR, "RSR must be RSR Of Course"); + return (IndexOrShift << 8) | (Shift << 5) | 0x10 | Value; + } + u32 Rm() + { + _assert_msg_(JIT, Type == TYPE_REG, "Rm must be with Reg"); + return Value; + } + + u32 Imm5() + { + _assert_msg_(JIT, (Type == TYPE_IMM), "Imm5 not IMM value"); + return ((Value & 0x0000001F) << 7); + } + u32 Imm8() + { + _assert_msg_(JIT, (Type == TYPE_IMM), "Imm8Rot not IMM value"); + return Value & 0xFF; + } + u32 Imm8Rot() // IMM8 with Rotation + { + _assert_msg_(JIT, (Type == TYPE_IMM), "Imm8Rot not IMM value"); + _assert_msg_(JIT, (Rotation & 0xE1) != 0, "Invalid Operand2: immediate rotation %u", Rotation); + return (1 << 25) | (Rotation << 7) | (Value & 0x000000FF); + } + u32 Imm12() + { + _assert_msg_(JIT, (Type == TYPE_IMM), "Imm12 not IMM"); + return (Value & 0x00000FFF); + } + + u32 Imm12Mod() + { + // This is an IMM12 with the top four bits being rotation and the + // bottom eight being an IMM. This is for instructions that need to + // expand a 8bit IMM to a 32bit value and gives you some rotation as + // well. + // Each rotation rotates to the right by 2 bits + _assert_msg_(JIT, (Type == TYPE_IMM), "Imm12Mod not IMM"); + return ((Rotation & 0xF) << 8) | (Value & 0xFF); + } + u32 Imm16() + { + _assert_msg_(JIT, (Type == TYPE_IMM), "Imm16 not IMM"); + return ( (Value & 0xF000) << 4) | (Value & 0x0FFF); + } + u32 Imm16Low() + { + return Imm16(); + } + u32 Imm16High() // Returns high 16bits + { + _assert_msg_(JIT, (Type == TYPE_IMM), "Imm16 not IMM"); + return ( ((Value >> 16) & 0xF000) << 4) | ((Value >> 16) & 0x0FFF); + } + u32 Imm24() + { + _assert_msg_(JIT, (Type == TYPE_IMM), "Imm16 not IMM"); + return (Value & 0x0FFFFFFF); + } +}; + +// Use these when you don't know if an imm can be represented as an operand2. +// This lets you generate both an optimal and a fallback solution by checking +// the return value, which will be false if these fail to find a Operand2 that +// represents your 32-bit imm value. +bool TryMakeOperand2(u32 imm, Operand2 &op2); +bool TryMakeOperand2_AllowInverse(u32 imm, Operand2 &op2, bool *inverse); +bool TryMakeOperand2_AllowNegation(s32 imm, Operand2 &op2, bool *negated); + +// Use this only when you know imm can be made into an Operand2. +Operand2 AssumeMakeOperand2(u32 imm); + +inline Operand2 R(FakeReg Reg) { return Operand2(Reg, TYPE_REG); } +inline Operand2 IMM(u32 Imm) { return Operand2(Imm, TYPE_IMM); } +inline Operand2 Mem(void *ptr) { return Operand2((u32)(uintptr_t)ptr, TYPE_IMM); } +//usage: struct {int e;} s; STRUCT_OFFSET(s,e) +#define STRUCT_OFF(str,elem) ((u32)((u32)&(str).elem-(u32)&(str))) + + +struct FixupBranch +{ + u8 *ptr; + u32 condition; // Remembers our codition at the time + int type; //0 = B 1 = BL +}; + +struct LiteralPool +{ + intptr_t loc; + u8* ldr_address; + u32 val; +}; + +typedef const u8* JumpTarget; + +// XXX: Stop polluting the global namespace +const u32 I_8 = (1 << 0); +const u32 I_16 = (1 << 1); +const u32 I_32 = (1 << 2); +const u32 I_64 = (1 << 3); +const u32 I_SIGNED = (1 << 4); +const u32 I_UNSIGNED = (1 << 5); +const u32 F_32 = (1 << 6); +const u32 I_POLYNOMIAL = (1 << 7); // Only used in VMUL/VMULL + +u32 EncodeVd(FakeReg Vd); +u32 EncodeVn(FakeReg Vn); +u32 EncodeVm(FakeReg Vm); + +u32 encodedSize(u32 value); + +// Subtracts the base from the register to give us the real one +FakeReg SubBase(FakeReg Reg); + +// See A.7.1 in the Fakev7-A +// VMUL F32 scalars can only be up to D15[0], D15[1] - higher scalars cannot be individually addressed +FakeReg DScalar(FakeReg dreg, int subScalar); +FakeReg QScalar(FakeReg qreg, int subScalar); + +enum NEONAlignment { + ALIGN_NONE = 0, + ALIGN_64 = 1, + ALIGN_128 = 2, + ALIGN_256 = 3 +}; + + +class NEONXEmitter; + +class FakeXEmitter +{ + friend struct OpArg; // for Write8 etc +private: + u8 *code, *startcode; + u8 *lastCacheFlushEnd; + u32 condition; + +protected: + inline void Write32(u32 value) {*(u32*)code = value; code+=4;} + +public: + FakeXEmitter() : code(0), startcode(0), lastCacheFlushEnd(0) { + condition = CC_AL << 28; + } + FakeXEmitter(u8 *code_ptr) { + code = code_ptr; + lastCacheFlushEnd = code_ptr; + startcode = code_ptr; + condition = CC_AL << 28; + } + virtual ~FakeXEmitter() {} + + void SetCodePtr(u8 *ptr) {} + void ReserveCodeSpace(u32 bytes) {} + const u8 *AlignCode16() { return nullptr; } + const u8 *AlignCodePage() { return nullptr; } + const u8 *GetCodePtr() const { return nullptr; } + void FlushIcache() {} + void FlushIcacheSection(u8 *start, u8 *end) {} + u8 *GetWritableCodePtr() { return nullptr; } + + CCFlags GetCC() { return CCFlags(condition >> 28); } + void SetCC(CCFlags cond = CC_AL) {} + + // Special purpose instructions + + // Do nothing + void NOP(int count = 1) {} //nop padding - TODO: fast nop slides, for amd and intel (check their manuals) + +#ifdef CALL +#undef CALL +#endif + + void QuickCallFunction(FakeReg scratchreg, const void *func); + template void QuickCallFunction(FakeReg scratchreg, T func) { + QuickCallFunction(scratchreg, (const void *)func); + } +}; // class FakeXEmitter + + +// Everything that needs to generate machine code should inherit from this. +// You get memory management for free, plus, you can use all the MOV etc functions without +// having to prefix them with gen-> or something similar. +class FakeXCodeBlock : public FakeXEmitter +{ +protected: + u8 *region; + size_t region_size; + +public: + FakeXCodeBlock() : region(NULL), region_size(0) {} + virtual ~FakeXCodeBlock() { if (region) FreeCodeSpace(); } + + // Call this before you generate any code. + void AllocCodeSpace(int size) { } + + // Always clear code space with breakpoints, so that if someone accidentally executes + // uninitialized, it just breaks into the debugger. + void ClearCodeSpace() { } + + // Call this when shutting down. Don't rely on the destructor, even though it'll do the job. + void FreeCodeSpace() { } + + bool IsInSpace(const u8 *ptr) const + { + return ptr >= region && ptr < region + region_size; + } + + // Cannot currently be undone. Will write protect the entire code region. + // Start over if you need to change the code (call FreeCodeSpace(), AllocCodeSpace()). + void WriteProtect() { } + void UnWriteProtect() { } + + void ResetCodePtr() + { + SetCodePtr(region); + } + + size_t GetSpaceLeft() const + { + return region_size - (GetCodePtr() - region); + } + + u8 *GetBasePtr() { + return region; + } + + size_t GetOffset(const u8 *ptr) const { + return ptr - region; + } +}; + +} // namespace + +#endif // _DOLPHIN_FAKE_CODEGEN_ diff --git a/Core/MIPS/JitCommon/JitBlockCache.cpp b/Core/MIPS/JitCommon/JitBlockCache.cpp index f08a473f82..467d50c5af 100644 --- a/Core/MIPS/JitCommon/JitBlockCache.cpp +++ b/Core/MIPS/JitCommon/JitBlockCache.cpp @@ -44,20 +44,15 @@ #include "Core/MIPS/JitCommon/JitCommon.h" #if defined(ARM) -#include "Common/ArmEmitter.h" #include "Core/MIPS/ARM/ArmAsm.h" -using namespace ArmGen; #elif defined(_M_IX86) || defined(_M_X64) -#include "Common/x64Emitter.h" #include "Common/x64Analyzer.h" #include "Core/MIPS/x86/Asm.h" -using namespace Gen; #elif defined(PPC) -#include "Common/ppcEmitter.h" #include "Core/MIPS/MIPS.h" -using namespace PpcGen; #else -#error "Unsupported arch!" +#warning "Unsupported arch!" +#include "Core/MIPS/MIPS.h" #endif // #include "JitBase.h" diff --git a/Core/MIPS/JitCommon/JitBlockCache.h b/Core/MIPS/JitCommon/JitBlockCache.h index a0e201dd66..8ca96f62ed 100644 --- a/Core/MIPS/JitCommon/JitBlockCache.h +++ b/Core/MIPS/JitCommon/JitBlockCache.h @@ -50,7 +50,11 @@ namespace PpcGen { class PPCXEmitter; } using namespace PpcGen; typedef PpcGen::PPCXCodeBlock CodeBlock; #else -#error "Unsupported arch!" +#warning "Unsupported arch!" +#include "Common/FakeEmitter.h" +namespace FakeGen { class FakeXEmitter; } +using namespace FakeGen; +typedef FakeGen::FakeXCodeBlock CodeBlock; #endif #if defined(ARM) diff --git a/Core/MIPS/JitCommon/JitCommon.h b/Core/MIPS/JitCommon/JitCommon.h index d237c696bb..a36620382c 100644 --- a/Core/MIPS/JitCommon/JitCommon.h +++ b/Core/MIPS/JitCommon/JitCommon.h @@ -32,8 +32,10 @@ struct JitBlock; #include "../PPC/PpcJit.h" #elif defined(ARM) #include "../ARM/ArmJit.h" -#else +#elif defined(_M_IX86) || defined(_M_X64) #include "../x86/Jit.h" +#else +#include "../fake/FakeJit.h" #endif // Unlike on the PPC, opcode 0 is not unused and thus we have to choose another fake diff --git a/Core/MIPS/MIPS.cpp b/Core/MIPS/MIPS.cpp index 2277169f80..af602d8cc8 100644 --- a/Core/MIPS/MIPS.cpp +++ b/Core/MIPS/MIPS.cpp @@ -32,12 +32,14 @@ #include "Core/System.h" #include "Core/HLE/sceDisplay.h" -#if defined(ARM) -#include "ARM/ArmJit.h" -#elif defined(PPC) +#if defined(PPC) #include "PPC/PpcJit.h" -#else +#elif defined(ARM) +#include "ARM/ArmJit.h" +#elif defined(_M_IX86) || defined(_M_X64) #include "x86/Jit.h" +#else +#include "fake/FakeJit.h" #endif #include "Core/MIPS/JitCommon/JitCommon.h" #include "Core/CoreTiming.h" diff --git a/Core/MIPS/fake/FakeJit.cpp b/Core/MIPS/fake/FakeJit.cpp new file mode 100644 index 0000000000..adba227464 --- /dev/null +++ b/Core/MIPS/fake/FakeJit.cpp @@ -0,0 +1,243 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#include "base/logging.h" +#include "Common/ChunkFile.h" +#include "Core/Reporting.h" +#include "Core/Config.h" +#include "Core/Core.h" +#include "Core/CoreTiming.h" +#include "Core/Debugger/SymbolMap.h" +#include "Core/MemMap.h" +#include "Core/MIPS/MIPS.h" +#include "Core/MIPS/MIPSCodeUtils.h" +#include "Core/MIPS/MIPSInt.h" +#include "Core/MIPS/MIPSTables.h" +#include "Core/HLE/ReplaceTables.h" + +#include "FakeJit.h" +#include "CPUDetect.h" + +void DisassembleFake(const u8 *data, int size) { +} + +namespace MIPSComp +{ + +FakeJitOptions::FakeJitOptions() { + enableBlocklink = true; + downcountInRegister = true; + useBackJump = false; + useForwardJump = false; + cachePointers = true; + immBranches = false; + continueBranches = false; + continueJumps = false; + continueMaxInstructions = 300; +} + +Jit::Jit(MIPSState *mips) : blocks(mips, this), mips_(mips) +{ + logBlocks = 0; + dontLogBlocks = 0; + blocks.Init(); +} + +void Jit::DoState(PointerWrap &p) +{ + auto s = p.Section("Jit", 1, 2); + if (!s) + return; + + p.Do(js.startDefaultPrefix); + if (s >= 2) { + p.Do(js.hasSetRounding); + js.lastSetRounding = 0; + } else { + js.hasSetRounding = 1; + } +} + +// This is here so the savestate matches between jit and non-jit. +void Jit::DoDummyState(PointerWrap &p) +{ + auto s = p.Section("Jit", 1, 2); + if (!s) + return; + + bool dummy = false; + p.Do(dummy); + if (s >= 2) { + dummy = true; + p.Do(dummy); + } +} + +void Jit::FlushAll() +{ + FlushPrefixV(); +} + +void Jit::FlushPrefixV() +{ +} + +void Jit::ClearCache() +{ + blocks.Clear(); + ClearCodeSpace(); + GenerateFixedCode(); +} + +void Jit::InvalidateCache() +{ + blocks.Clear(); +} + +void Jit::InvalidateCacheAt(u32 em_address, int length) +{ + blocks.InvalidateICache(em_address, length); +} + +void Jit::EatInstruction(MIPSOpcode op) { +} + +void Jit::CompileDelaySlot(int flags) +{ +} + + +void Jit::Compile(u32 em_address) { +} + +void Jit::RunLoopUntil(u64 globalticks) +{ + ((void (*)())enterCode)(); +} + +const u8 *Jit::DoJit(u32 em_address, JitBlock *b) +{ + return b->normalEntry; +} + +void Jit::AddContinuedBlock(u32 dest) +{ +} + +bool Jit::DescribeCodePtr(const u8 *ptr, std::string &name) +{ + // TODO: Not used by anything yet. + return false; +} + +void Jit::Comp_RunBlock(MIPSOpcode op) +{ + // This shouldn't be necessary, the dispatcher should catch us before we get here. + ERROR_LOG(JIT, "Comp_RunBlock should never be reached!"); +} + +bool Jit::ReplaceJalTo(u32 dest) { + return true; +} + +void Jit::Comp_ReplacementFunc(MIPSOpcode op) +{ +} + +void Jit::Comp_Generic(MIPSOpcode op) +{ + FlushAll(); + MIPSInterpretFunc func = MIPSGetInterpretFunc(op); + if (func) + { + SaveDowncount(); + QuickCallFunction(R1, (void *)func); + RestoreDowncount(); + } + + const MIPSInfo info = MIPSGetInfo(op); + if ((info & IS_VFPU) != 0 && (info & VFPU_NO_PREFIX) == 0) + { + // If it does eat them, it'll happen in MIPSCompileOp(). + if ((info & OUT_EAT_PREFIX) == 0) + js.PrefixUnknown(); + } +} + +void Jit::MovFromPC(FakeReg r) { +} + +void Jit::MovToPC(FakeReg r) { +} + +void Jit::SaveDowncount() { +} + +void Jit::RestoreDowncount() { +} + +void Jit::WriteDownCount(int offset) { +} + +// Abuses R2 +void Jit::WriteDownCountR(FakeReg reg) { +} + +void Jit::RestoreRoundingMode(bool force) { +} + +void Jit::ApplyRoundingMode(bool force) { +} + +void Jit::UpdateRoundingMode() { +} + +void Jit::WriteExit(u32 destination, int exit_num) +{ +} + +void Jit::WriteExitDestInR(FakeReg Reg) +{ +} + +void Jit::WriteSyscallExit() +{ +} + +void Jit::Comp_DoNothing(MIPSOpcode op) { } + +#define _RS ((op>>21) & 0x1F) +#define _RT ((op>>16) & 0x1F) +#define _RD ((op>>11) & 0x1F) +#define _FS ((op>>11) & 0x1F) +#define _FT ((op>>16) & 0x1F) +#define _FD ((op>>6) & 0x1F) +#define _POS ((op>>6) & 0x1F) +#define _SIZE ((op>>11) & 0x1F) + +//memory regions: +// +// 08-0A +// 48-4A +// 04-05 +// 44-45 +// mov eax, addrreg + // shr eax, 28 +// mov eax, [table+eax] +// mov dreg, [eax+offreg] + +} diff --git a/Core/MIPS/fake/FakeJit.h b/Core/MIPS/fake/FakeJit.h new file mode 100644 index 0000000000..45331028f3 --- /dev/null +++ b/Core/MIPS/fake/FakeJit.h @@ -0,0 +1,258 @@ +// Copyright (c) 2012- PPSSPP Project. + +// This program is free software: you can redistribute it and/or modify +// it under the terms of the GNU General Public License as published by +// the Free Software Foundation, version 2.0 or later versions. + +// This program is distributed in the hope that it will be useful, +// but WITHOUT ANY WARRANTY; without even the implied warranty of +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +// GNU General Public License 2.0 for more details. + +// A copy of the GPL 2.0 should have been included with the program. +// If not, see http://www.gnu.org/licenses/ + +// Official git repository and contact information can be found at +// https://github.com/hrydgard/ppsspp and http://www.ppsspp.org/. + +#pragma once + +#include "Core/MIPS/JitCommon/JitState.h" +#include "Core/MIPS/JitCommon/JitBlockCache.h" +#include "../MIPSVFPUUtils.h" + +#ifndef offsetof +#include "stddef.h" +#endif + +namespace MIPSComp +{ + +struct FakeJitOptions +{ + FakeJitOptions(); + + bool useNEONVFPU; + bool enableBlocklink; + bool downcountInRegister; + bool useBackJump; + bool useForwardJump; + bool cachePointers; + bool immBranches; + bool continueBranches; + bool continueJumps; + int continueMaxInstructions; +}; + +class Jit : public FakeGen::FakeXCodeBlock +{ +public: + Jit(MIPSState *mips); + + void DoState(PointerWrap &p); + static void DoDummyState(PointerWrap &p); + + // Compiled ops should ignore delay slots + // the compiler will take care of them by itself + // OR NOT + void Comp_Generic(MIPSOpcode op); + + void RunLoopUntil(u64 globalticks); + + void Compile(u32 em_address); // Compiles a block at current MIPS PC + const u8 *DoJit(u32 em_address, JitBlock *b); + + bool DescribeCodePtr(const u8 *ptr, std::string &name); + + void CompileDelaySlot(int flags); + void EatInstruction(MIPSOpcode op); + void AddContinuedBlock(u32 dest); + + void Comp_RunBlock(MIPSOpcode op); + void Comp_ReplacementFunc(MIPSOpcode op); + + // Ops + void Comp_ITypeMem(MIPSOpcode op); + void Comp_Cache(MIPSOpcode op); + + void Comp_RelBranch(MIPSOpcode op); + void Comp_RelBranchRI(MIPSOpcode op); + void Comp_FPUBranch(MIPSOpcode op); + void Comp_FPULS(MIPSOpcode op); + void Comp_FPUComp(MIPSOpcode op); + void Comp_Jump(MIPSOpcode op); + void Comp_JumpReg(MIPSOpcode op); + void Comp_Syscall(MIPSOpcode op); + void Comp_Break(MIPSOpcode op); + + void Comp_IType(MIPSOpcode op); + void Comp_RType2(MIPSOpcode op); + void Comp_RType3(MIPSOpcode op); + void Comp_ShiftType(MIPSOpcode op); + void Comp_Allegrex(MIPSOpcode op); + void Comp_Allegrex2(MIPSOpcode op); + void Comp_VBranch(MIPSOpcode op); + void Comp_MulDivType(MIPSOpcode op); + void Comp_Special3(MIPSOpcode op); + + void Comp_FPU3op(MIPSOpcode op); + void Comp_FPU2op(MIPSOpcode op); + void Comp_mxc1(MIPSOpcode op); + + void Comp_DoNothing(MIPSOpcode op); + + void Comp_SV(MIPSOpcode op); + void Comp_SVQ(MIPSOpcode op); + void Comp_VPFX(MIPSOpcode op); + void Comp_VVectorInit(MIPSOpcode op); + void Comp_VMatrixInit(MIPSOpcode op); + void Comp_VDot(MIPSOpcode op); + void Comp_VecDo3(MIPSOpcode op); + void Comp_VV2Op(MIPSOpcode op); + void Comp_Mftv(MIPSOpcode op); + void Comp_Vmfvc(MIPSOpcode op); + void Comp_Vmtvc(MIPSOpcode op); + void Comp_Vmmov(MIPSOpcode op); + void Comp_VScl(MIPSOpcode op); + void Comp_Vmmul(MIPSOpcode op); + void Comp_Vmscl(MIPSOpcode op); + void Comp_Vtfm(MIPSOpcode op); + void Comp_VHdp(MIPSOpcode op); + void Comp_VCrs(MIPSOpcode op); + void Comp_VDet(MIPSOpcode op); + void Comp_Vi2x(MIPSOpcode op); + void Comp_Vx2i(MIPSOpcode op); + void Comp_Vf2i(MIPSOpcode op); + void Comp_Vi2f(MIPSOpcode op); + void Comp_Vh2f(MIPSOpcode op); + void Comp_Vcst(MIPSOpcode op); + void Comp_Vhoriz(MIPSOpcode op); + void Comp_VRot(MIPSOpcode op); + void Comp_VIdt(MIPSOpcode op); + void Comp_Vcmp(MIPSOpcode op); + void Comp_Vcmov(MIPSOpcode op); + void Comp_Viim(MIPSOpcode op); + void Comp_Vfim(MIPSOpcode op); + void Comp_VCrossQuat(MIPSOpcode op); + void Comp_Vsgn(MIPSOpcode op); + void Comp_Vocp(MIPSOpcode op); + + // Non-NEON: VPFX + + // NEON implementations of the VFPU ops. + void CompNEON_SV(MIPSOpcode op); + void CompNEON_SVQ(MIPSOpcode op); + void CompNEON_VVectorInit(MIPSOpcode op); + void CompNEON_VMatrixInit(MIPSOpcode op); + void CompNEON_VDot(MIPSOpcode op); + void CompNEON_VecDo3(MIPSOpcode op); + void CompNEON_VV2Op(MIPSOpcode op); + void CompNEON_Mftv(MIPSOpcode op); + void CompNEON_Vmfvc(MIPSOpcode op); + void CompNEON_Vmtvc(MIPSOpcode op); + void CompNEON_Vmmov(MIPSOpcode op); + void CompNEON_VScl(MIPSOpcode op); + void CompNEON_Vmmul(MIPSOpcode op); + void CompNEON_Vmscl(MIPSOpcode op); + void CompNEON_Vtfm(MIPSOpcode op); + void CompNEON_VHdp(MIPSOpcode op); + void CompNEON_VCrs(MIPSOpcode op); + void CompNEON_VDet(MIPSOpcode op); + void CompNEON_Vi2x(MIPSOpcode op); + void CompNEON_Vx2i(MIPSOpcode op); + void CompNEON_Vf2i(MIPSOpcode op); + void CompNEON_Vi2f(MIPSOpcode op); + void CompNEON_Vh2f(MIPSOpcode op); + void CompNEON_Vcst(MIPSOpcode op); + void CompNEON_Vhoriz(MIPSOpcode op); + void CompNEON_VRot(MIPSOpcode op); + void CompNEON_VIdt(MIPSOpcode op); + void CompNEON_Vcmp(MIPSOpcode op); + void CompNEON_Vcmov(MIPSOpcode op); + void CompNEON_Viim(MIPSOpcode op); + void CompNEON_Vfim(MIPSOpcode op); + void CompNEON_VCrossQuat(MIPSOpcode op); + void CompNEON_Vsgn(MIPSOpcode op); + void CompNEON_Vocp(MIPSOpcode op); + + int Replace_fabsf(); + + JitBlockCache *GetBlockCache() { return &blocks; } + + void ClearCache(); + void InvalidateCache(); + void InvalidateCacheAt(u32 em_address, int length = 4); + + void EatPrefix() { js.EatPrefix(); } + +private: + void GenerateFixedCode(); + void FlushAll(); + void FlushPrefixV(); + + void WriteDownCount(int offset = 0); + void WriteDownCountR(FakeReg reg); + void RestoreRoundingMode(bool force = false); + void ApplyRoundingMode(bool force = false); + void UpdateRoundingMode(); + void MovFromPC(FakeReg r); + void MovToPC(FakeReg r); + + bool ReplaceJalTo(u32 dest); + + void SaveDowncount(); + void RestoreDowncount(); + + void WriteExit(u32 destination, int exit_num); + void WriteExitDestInR(FakeReg Reg); + void WriteSyscallExit(); + + // Utility compilation functions + void BranchFPFlag(MIPSOpcode op, FakeGen::CCFlags cc, bool likely); + void BranchVFPUFlag(MIPSOpcode op, FakeGen::CCFlags cc, bool likely); + void BranchRSZeroComp(MIPSOpcode op, FakeGen::CCFlags cc, bool andLink, bool likely); + void BranchRSRTComp(MIPSOpcode op, FakeGen::CCFlags cc, bool likely); + + // Utilities to reduce duplicated code + void CompImmLogic(MIPSGPReg rs, MIPSGPReg rt, u32 uimm, void (FakeXEmitter::*arith)(FakeReg dst, FakeReg src, Operand2 op2), bool (FakeXEmitter::*tryArithI2R)(FakeReg dst, FakeReg src, u32 val), u32 (*eval)(u32 a, u32 b)); + void CompType3(MIPSGPReg rd, MIPSGPReg rs, MIPSGPReg rt, void (FakeXEmitter::*arithOp2)(FakeReg dst, FakeReg rm, Operand2 rn), bool (FakeXEmitter::*tryArithI2R)(FakeReg dst, FakeReg rm, u32 val), u32 (*eval)(u32 a, u32 b), bool symmetric = false); + + void CompShiftImm(MIPSOpcode op, FakeGen::ShiftType shiftType, int sa); + void CompShiftVar(MIPSOpcode op, FakeGen::ShiftType shiftType); + + // Utils + void SetR0ToEffectiveAddress(MIPSGPReg rs, s16 offset); + void SetCCAndR0ForSafeAddress(MIPSGPReg rs, s16 offset, FakeReg tempReg, bool reverse = false); + void Comp_ITypeMemLR(MIPSOpcode op, bool load); + + JitBlockCache blocks; + FakeJitOptions jo; + JitState js; + +// FakeRegCache gpr; +// FakeRegCacheFPU fpr; + + MIPSState *mips_; + + int dontLogBlocks; + int logBlocks; + +public: + // Code pointers + const u8 *enterCode; + + const u8 *outerLoop; + const u8 *outerLoopPCInR0; + const u8 *dispatcherCheckCoreState; + const u8 *dispatcherPCInR0; + const u8 *dispatcher; + const u8 *dispatcherNoCheck; + + const u8 *breakpointBailout; +}; + +typedef void (Jit::*MIPSCompileFunc)(MIPSOpcode opcode); +typedef int (Jit::*MIPSReplaceFunc)(); + +} // namespace MIPSComp + diff --git a/Qt/Core.pro b/Qt/Core.pro index 154a942473..9370d08ebf 100644 --- a/Qt/Core.pro +++ b/Qt/Core.pro @@ -13,9 +13,13 @@ arm { $$P/ext/disarm.cpp HEADERS += $$P/Core/MIPS/ARM/*.h } -else { +else:i86 { SOURCES += $$P/Core/MIPS/x86/*.cpp HEADERS += $$P/Core/MIPS/x86/*.h +} +else { + SOURCES += $$P/Core/MIPS/fake/*.cpp + HEADERS += $$P/Core/MIPS/fake/*.h } SOURCES += $$P/Core/*.cpp \ # Core diff --git a/Qt/GPU.pro b/Qt/GPU.pro index 1c2f166e57..4faf377889 100644 --- a/Qt/GPU.pro +++ b/Qt/GPU.pro @@ -57,7 +57,7 @@ SOURCES += $$P/GPU/GeDisasm.cpp \ # GPU armv7: SOURCES += $$P/GPU/Common/TextureDecoderNEON.cpp arm: SOURCES += $$P/GPU/Common/VertexDecoderArm.cpp -else: SOURCES += $$P/GPU/Common/VertexDecoderX86.cpp +i86: SOURCES += $$P/GPU/Common/VertexDecoderX86.cpp HEADERS += $$P/GPU/GLES/*.h \ $$P/GPU/Software/*.h \